@remnic/core 9.4.2 → 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +6 -6
- package/dist/access-admin-ops-surface.js +28 -28
- package/dist/access-boundary.d.ts +27 -8
- package/dist/access-boundary.js +33 -29
- package/dist/access-cli.js +83 -81
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +52 -6
- package/dist/access-http.js +40 -38
- package/dist/access-identity-continuity-surface.d.ts +5 -5
- package/dist/access-identity-continuity-surface.js +28 -28
- package/dist/access-lcm-surface.d.ts +6 -6
- package/dist/access-lcm-surface.js +28 -28
- package/dist/access-mcp.d.ts +16 -6
- package/dist/access-mcp.js +37 -35
- package/dist/access-observe-write-surface.d.ts +6 -6
- package/dist/access-observe-write-surface.js +28 -28
- package/dist/access-operations-batch.js +33 -32
- package/dist/access-operations.d.ts +9 -9
- package/dist/access-operations.js +35 -34
- package/dist/access-recall-surface.d.ts +6 -6
- package/dist/access-recall-surface.js +28 -28
- package/dist/access-schema.d.ts +4 -4
- package/dist/access-schema.js +4 -4
- package/dist/{access-service-m-2VO_Ig.d.ts → access-service-CXJaql3A.d.ts} +4 -4
- package/dist/access-service.d.ts +6 -6
- package/dist/access-service.js +28 -28
- package/dist/access-surface-catalog.d.ts +6 -6
- package/dist/access-surface-catalog.js +1 -0
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-token-capabilities.d.ts +174 -0
- package/dist/access-token-capabilities.js +33 -0
- 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 +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-HMHQHUAG.js → auto-sync-4A5YBWYO.js} +4 -4
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +5 -5
- 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.d.ts +2 -2
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-RAARDY3M.js} +2 -2
- package/dist/capsule-crypto-RAARDY3M.js.map +1 -0
- package/dist/{catalog-BxWokdC2.d.ts → catalog-C9cu67Ig.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-behavior.js +5 -5
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +13 -13
- package/dist/causal-retrieval.js +3 -3
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-6ZIETXZ2.js → chunk-23JFRB73.js} +2 -2
- package/dist/{chunk-IC6QX5TC.js → chunk-3XIY7MDQ.js} +6 -6
- package/dist/{chunk-FZGGZAAU.js → chunk-4GNEDXDI.js} +146 -2
- package/dist/chunk-4GNEDXDI.js.map +1 -0
- package/dist/{chunk-LOR6ZM5R.js → chunk-4LV23CHK.js} +2 -2
- package/dist/{chunk-E2SPGGUI.js → chunk-4OKLXWDZ.js} +13 -6
- package/dist/chunk-4OKLXWDZ.js.map +1 -0
- package/dist/{chunk-AER6MT24.js → chunk-6SXVCD7W.js} +2 -2
- package/dist/{chunk-OHDJ34CC.js → chunk-72WG5QKN.js} +2 -2
- package/dist/{chunk-CZAZX7JP.js → chunk-7MW3CVLD.js} +4 -4
- package/dist/{chunk-CXLNZXG5.js → chunk-7XGSIUP5.js} +1 -1
- package/dist/{chunk-CZAA5N5S.js → chunk-AYZJID4S.js} +2 -2
- package/dist/{chunk-M7XQSUBB.js → chunk-B3ABVQ36.js} +116 -9
- package/dist/chunk-B3ABVQ36.js.map +1 -0
- package/dist/{chunk-6BISDEN6.js → chunk-BEM4D2QG.js} +14 -14
- package/dist/{chunk-ZPOROUTX.js → chunk-CYWA2CXR.js} +5 -5
- package/dist/{chunk-M5QKGHCR.js → chunk-CYWCKFCL.js} +5 -5
- package/dist/chunk-D7FOKIY5.js +173 -0
- package/dist/chunk-D7FOKIY5.js.map +1 -0
- package/dist/{chunk-4BFRBP45.js → chunk-DEG5ULFJ.js} +2 -2
- package/dist/{chunk-JLR3JSTL.js → chunk-DS7MM2ES.js} +3 -3
- package/dist/{chunk-2BFLDH5F.js → chunk-EEIROWEG.js} +2 -2
- package/dist/{chunk-5QZDMSSN.js → chunk-FKCRGLRD.js} +237 -82
- package/dist/{chunk-5QZDMSSN.js.map → chunk-FKCRGLRD.js.map} +1 -1
- package/dist/{chunk-X4A62ET7.js → chunk-H2DXDQCQ.js} +17 -17
- package/dist/{chunk-27FLVMDC.js → chunk-HLN7RROI.js} +7 -7
- package/dist/chunk-IC2TCZJ2.js +54 -0
- package/dist/chunk-IC2TCZJ2.js.map +1 -0
- package/dist/{chunk-2U2A5HF4.js → chunk-J3XHPFEU.js} +6 -6
- package/dist/{chunk-AKUCU7CI.js → chunk-JI5AVXYO.js} +5 -5
- package/dist/{chunk-KCJ5H6XQ.js → chunk-JSNVWNGX.js} +2 -2
- package/dist/{chunk-7Z2DZNWN.js → chunk-KL4UAXUY.js} +43 -2
- package/dist/{chunk-7Z2DZNWN.js.map → chunk-KL4UAXUY.js.map} +1 -1
- package/dist/{chunk-FLVL4L3L.js → chunk-KTULX4YO.js} +4 -4
- package/dist/{chunk-ILFJ7AVS.js → chunk-LADFWYHY.js} +35 -21
- package/dist/chunk-LADFWYHY.js.map +1 -0
- package/dist/{chunk-437S3G37.js → chunk-LNQUAJNC.js} +1 -1
- package/dist/{chunk-EPGWWCZF.js → chunk-LR5DZ56F.js} +6 -6
- package/dist/{chunk-YDSWRD33.js → chunk-N3JDIRBW.js} +4 -4
- package/dist/{chunk-OUMTBFSQ.js → chunk-NKCPUHCI.js} +5 -5
- package/dist/{chunk-7QWMTOIF.js → chunk-P454KXD6.js} +112 -34
- package/dist/chunk-P454KXD6.js.map +1 -0
- package/dist/{chunk-DR6PEJU3.js → chunk-PCLIWBGD.js} +44 -59
- package/dist/chunk-PCLIWBGD.js.map +1 -0
- package/dist/{chunk-BGTAL46Y.js → chunk-PNLFN2PZ.js} +2 -2
- package/dist/{chunk-P6SYWE4G.js → chunk-PVUJ6WVV.js} +10 -10
- package/dist/{chunk-YPEMNNNY.js → chunk-RGK27ELN.js} +6 -6
- package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/{chunk-66FLU5BT.js → chunk-SIRPLF54.js} +6 -6
- package/dist/{chunk-M7GGMGMZ.js → chunk-SVOHGUMI.js} +1202 -126
- package/dist/chunk-SVOHGUMI.js.map +1 -0
- package/dist/{chunk-HRCACVHL.js → chunk-TLUDFPZL.js} +2 -2
- package/dist/{chunk-2FOD2YY3.js → chunk-UFUKHGZD.js} +236 -103
- package/dist/chunk-UFUKHGZD.js.map +1 -0
- package/dist/{chunk-2MR3MFQB.js → chunk-UPSAT4PY.js} +2 -2
- package/dist/{chunk-4P4626RY.js → chunk-UTRJFDP2.js} +4 -4
- package/dist/{chunk-CGS7UKWM.js → chunk-VCW74PNH.js} +2 -2
- package/dist/{chunk-5GPPACXK.js → chunk-VSMRDBFT.js} +2 -1
- package/dist/{chunk-NG7YBCJO.js → chunk-XCL57M6Q.js} +4 -4
- package/dist/{chunk-CLX5VDNY.js → chunk-Y4RKAQZ7.js} +4 -4
- package/dist/{chunk-DGYCWCQ7.js → chunk-Y7UNNAZD.js} +2 -2
- package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
- package/dist/{chunk-XN3TUUX2.js → chunk-YQB32INI.js} +2 -2
- package/dist/{cli-EiBBOt3r.d.ts → cli-CqdPA6z9.d.ts} +3 -3
- package/dist/cli.d.ts +7 -7
- package/dist/cli.js +61 -59
- 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 +13 -11
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/contradiction/index.js +4 -4
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/dashboard-runtime.js +2 -2
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +9 -9
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +5 -5
- 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.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-DY6YCRC2.js → first-start-migration-VBJQL6OI.js} +5 -5
- 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-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +271 -17
- package/dist/index.js +163 -99
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +2 -2
- 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/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +10 -10
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +9 -9
- package/dist/maintenance/rebuild-memory-projection.js +11 -11
- package/dist/mcp-memory-inspector-app.d.ts +6 -6
- package/dist/mcp-read-only-tools.d.ts +16 -0
- package/dist/mcp-read-only-tools.js +8 -0
- package/dist/mcp-read-only-tools.js.map +1 -0
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +19 -19
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +10 -10
- 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/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +25 -25
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +11 -11
- package/dist/{orchestrator-B2Y28Z9t.d.ts → orchestrator-CWFrT8AF.d.ts} +91 -60
- package/dist/orchestrator.d.ts +5 -5
- package/dist/orchestrator.js +85 -83
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{qmd-Db9mOkzB.d.ts → qmd--fnPMK60.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-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.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/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/schemas.d.ts +22 -22
- package/dist/search/document-scanner.js +2 -2
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +9 -9
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +13 -13
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +3 -3
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +3 -3
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +3 -3
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-BDVIbLRw.d.ts → semantic-consolidation-O5lMZ4LG.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +11 -11
- package/dist/semantic-rule-promotion.js +9 -9
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +10 -10
- 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/storage-C1maCNbc.d.ts +1591 -0
- package/dist/storage.d.ts +5 -1239
- package/dist/storage.js +8 -8
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +2 -2
- 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/{tier-stats-TG6GUZ6N.js → tier-stats-5XF4UBRQ.js} +4 -4
- package/dist/tokens.d.ts +17 -2
- package/dist/tokens.js +3 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +3 -3
- package/dist/transfer/capsule-import.js +3 -3
- package/dist/transfer/types.d.ts +12 -12
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-ljkd82rn.d.ts → types-CGgcNWCG.d.ts} +31 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +10 -10
- package/package.json +2 -2
- package/src/access-boundary.test.ts +62 -0
- package/src/access-boundary.ts +165 -126
- package/src/access-http.test.ts +781 -0
- package/src/access-http.ts +334 -108
- package/src/access-mcp.test.ts +264 -0
- package/src/access-mcp.ts +45 -70
- package/src/access-operations-batch.ts +35 -12
- package/src/access-surface-catalog.test.ts +22 -14
- package/src/access-surface-catalog.ts +6 -3
- package/src/access-token-capabilities.test.ts +844 -0
- package/src/access-token-capabilities.ts +391 -0
- package/src/chat/chat-mcp.test.ts +232 -0
- package/src/chat/chat-session.ts +34 -0
- package/src/cli-wearables-fuse.test.ts +197 -0
- package/src/cli.ts +25 -0
- package/src/index.ts +14 -0
- package/src/mcp-read-only-tools.ts +61 -0
- package/src/storage.ts +16 -0
- package/src/tokens.ts +32 -3
- package/src/wearables/cli.test.ts +102 -0
- package/src/wearables/cli.ts +65 -0
- package/src/wearables/config.test.ts +32 -0
- package/src/wearables/config.ts +52 -0
- package/src/wearables/day-store.test.ts +246 -0
- package/src/wearables/day-store.ts +272 -3
- package/src/wearables/fusion/cluster.ts +230 -0
- package/src/wearables/fusion/fuse.ts +218 -0
- package/src/wearables/fusion/fusion.test.ts +2575 -0
- package/src/wearables/fusion/index.ts +50 -0
- package/src/wearables/fusion/reconcile.ts +831 -0
- package/src/wearables/fusion/reconstruct.ts +251 -0
- package/src/wearables/fusion/store.ts +480 -0
- package/src/wearables/fusion/types.ts +228 -0
- package/src/wearables/index.ts +41 -0
- package/src/wearables/service.test.ts +1030 -2
- package/src/wearables/service.ts +258 -16
- package/src/wearables/speakers.test.ts +128 -0
- package/src/wearables/speakers.ts +58 -4
- package/src/wearables/storage-io.test.ts +146 -0
- package/src/wearables/types.ts +31 -0
- package/dist/chunk-2FOD2YY3.js.map +0 -1
- package/dist/chunk-7QWMTOIF.js.map +0 -1
- package/dist/chunk-DR6PEJU3.js.map +0 -1
- package/dist/chunk-E2SPGGUI.js.map +0 -1
- package/dist/chunk-FZGGZAAU.js.map +0 -1
- package/dist/chunk-ILFJ7AVS.js.map +0 -1
- package/dist/chunk-M7GGMGMZ.js.map +0 -1
- package/dist/chunk-M7XQSUBB.js.map +0 -1
- /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-token-capabilities.js.map} +0 -0
- /package/dist/{auto-sync-HMHQHUAG.js.map → auto-sync-4A5YBWYO.js.map} +0 -0
- /package/dist/{chunk-6ZIETXZ2.js.map → chunk-23JFRB73.js.map} +0 -0
- /package/dist/{chunk-IC6QX5TC.js.map → chunk-3XIY7MDQ.js.map} +0 -0
- /package/dist/{chunk-LOR6ZM5R.js.map → chunk-4LV23CHK.js.map} +0 -0
- /package/dist/{chunk-AER6MT24.js.map → chunk-6SXVCD7W.js.map} +0 -0
- /package/dist/{chunk-OHDJ34CC.js.map → chunk-72WG5QKN.js.map} +0 -0
- /package/dist/{chunk-CZAZX7JP.js.map → chunk-7MW3CVLD.js.map} +0 -0
- /package/dist/{chunk-CXLNZXG5.js.map → chunk-7XGSIUP5.js.map} +0 -0
- /package/dist/{chunk-CZAA5N5S.js.map → chunk-AYZJID4S.js.map} +0 -0
- /package/dist/{chunk-6BISDEN6.js.map → chunk-BEM4D2QG.js.map} +0 -0
- /package/dist/{chunk-ZPOROUTX.js.map → chunk-CYWA2CXR.js.map} +0 -0
- /package/dist/{chunk-M5QKGHCR.js.map → chunk-CYWCKFCL.js.map} +0 -0
- /package/dist/{chunk-4BFRBP45.js.map → chunk-DEG5ULFJ.js.map} +0 -0
- /package/dist/{chunk-JLR3JSTL.js.map → chunk-DS7MM2ES.js.map} +0 -0
- /package/dist/{chunk-2BFLDH5F.js.map → chunk-EEIROWEG.js.map} +0 -0
- /package/dist/{chunk-X4A62ET7.js.map → chunk-H2DXDQCQ.js.map} +0 -0
- /package/dist/{chunk-27FLVMDC.js.map → chunk-HLN7RROI.js.map} +0 -0
- /package/dist/{chunk-2U2A5HF4.js.map → chunk-J3XHPFEU.js.map} +0 -0
- /package/dist/{chunk-AKUCU7CI.js.map → chunk-JI5AVXYO.js.map} +0 -0
- /package/dist/{chunk-KCJ5H6XQ.js.map → chunk-JSNVWNGX.js.map} +0 -0
- /package/dist/{chunk-FLVL4L3L.js.map → chunk-KTULX4YO.js.map} +0 -0
- /package/dist/{chunk-437S3G37.js.map → chunk-LNQUAJNC.js.map} +0 -0
- /package/dist/{chunk-EPGWWCZF.js.map → chunk-LR5DZ56F.js.map} +0 -0
- /package/dist/{chunk-YDSWRD33.js.map → chunk-N3JDIRBW.js.map} +0 -0
- /package/dist/{chunk-OUMTBFSQ.js.map → chunk-NKCPUHCI.js.map} +0 -0
- /package/dist/{chunk-BGTAL46Y.js.map → chunk-PNLFN2PZ.js.map} +0 -0
- /package/dist/{chunk-P6SYWE4G.js.map → chunk-PVUJ6WVV.js.map} +0 -0
- /package/dist/{chunk-YPEMNNNY.js.map → chunk-RGK27ELN.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-66FLU5BT.js.map → chunk-SIRPLF54.js.map} +0 -0
- /package/dist/{chunk-HRCACVHL.js.map → chunk-TLUDFPZL.js.map} +0 -0
- /package/dist/{chunk-2MR3MFQB.js.map → chunk-UPSAT4PY.js.map} +0 -0
- /package/dist/{chunk-4P4626RY.js.map → chunk-UTRJFDP2.js.map} +0 -0
- /package/dist/{chunk-CGS7UKWM.js.map → chunk-VCW74PNH.js.map} +0 -0
- /package/dist/{chunk-5GPPACXK.js.map → chunk-VSMRDBFT.js.map} +0 -0
- /package/dist/{chunk-NG7YBCJO.js.map → chunk-XCL57M6Q.js.map} +0 -0
- /package/dist/{chunk-CLX5VDNY.js.map → chunk-Y4RKAQZ7.js.map} +0 -0
- /package/dist/{chunk-DGYCWCQ7.js.map → chunk-Y7UNNAZD.js.map} +0 -0
- /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
- /package/dist/{chunk-XN3TUUX2.js.map → chunk-YQB32INI.js.map} +0 -0
- /package/dist/{first-start-migration-DY6YCRC2.js.map → first-start-migration-VBJQL6OI.js.map} +0 -0
- /package/dist/{tier-stats-TG6GUZ6N.js.map → tier-stats-5XF4UBRQ.js.map} +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
registerCli,
|
|
6
|
+
type CliApi,
|
|
7
|
+
type CliCommand,
|
|
8
|
+
type CliProgram,
|
|
9
|
+
} from "./cli.js";
|
|
10
|
+
import type { WearablesService } from "./wearables/service.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Host-CLI registration test for the wearables `fuse`/`fused` subcommands.
|
|
14
|
+
*
|
|
15
|
+
* The shared runner (`runWearablesCliCommand`) already handles fuse/fused, but
|
|
16
|
+
* the host CLI must ALSO register forwarders under `wearablesCmd` — otherwise
|
|
17
|
+
* commander rejects `remnic wearables fuse <date>` / `remnic wearables fused
|
|
18
|
+
* <date>` as unknown subcommands before the runner is ever reached (issue
|
|
19
|
+
* #1849). This drives `registerCli` with a recording program mock plus a stub
|
|
20
|
+
* orchestrator and asserts both that the subcommands are registered and that
|
|
21
|
+
* their actions dispatch into the runner (which calls the wearables service).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** Recording commander stand-in: records the command tree + action callbacks. */
|
|
25
|
+
class RecCmd implements CliCommand {
|
|
26
|
+
readonly children = new Map<string, RecCmd>();
|
|
27
|
+
readonly options: string[] = [];
|
|
28
|
+
readonly args: string[] = [];
|
|
29
|
+
actionFn?: (...args: unknown[]) => unknown;
|
|
30
|
+
constructor(readonly rawName: string) {}
|
|
31
|
+
description(): CliCommand {
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
option(flags: string): CliCommand {
|
|
35
|
+
this.options.push(flags);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
requiredOption(flags: string): CliCommand {
|
|
39
|
+
this.options.push(flags);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
argument(name: string): CliCommand {
|
|
43
|
+
this.args.push(name);
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
action(fn: (...args: unknown[]) => Promise<void> | void): CliCommand {
|
|
47
|
+
this.actionFn = fn;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
command(name: string): CliCommand {
|
|
51
|
+
const key = name.split(/\s+/)[0]!;
|
|
52
|
+
const child = new RecCmd(name);
|
|
53
|
+
this.children.set(key, child);
|
|
54
|
+
return child;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class RecProgram implements CliProgram {
|
|
59
|
+
readonly root = new RecCmd("__program__");
|
|
60
|
+
command(name: string): CliCommand {
|
|
61
|
+
return this.root.command(name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Deterministic orchestrator stub. registerCli only reads `config.*` while
|
|
67
|
+
* building the command tree (feature flags gate subcommand registration;
|
|
68
|
+
* memoryDir is an option default), so every accessed field returns a real
|
|
69
|
+
* primitive — no Proxy ever reaches a string/number call site. The wearables
|
|
70
|
+
* forwarder is the only action that touches the stub, via getWearablesService.
|
|
71
|
+
*/
|
|
72
|
+
function makeOrchestratorStub(
|
|
73
|
+
service: WearablesService,
|
|
74
|
+
): Parameters<typeof registerCli>[1] {
|
|
75
|
+
const config = new Proxy(
|
|
76
|
+
{ memoryDir: "/tmp/cli-wearables-fuse-test" } as Record<string, unknown>,
|
|
77
|
+
{
|
|
78
|
+
get(target, prop) {
|
|
79
|
+
if (typeof prop === "symbol") return undefined;
|
|
80
|
+
if (prop in target) return target[prop as string];
|
|
81
|
+
const key = prop as string;
|
|
82
|
+
if (
|
|
83
|
+
key.endsWith("WindowMs") ||
|
|
84
|
+
key.endsWith("SoftLimit") ||
|
|
85
|
+
key.endsWith("HardLimit")
|
|
86
|
+
) {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
return true; // feature flags -> enabled (every subcommand registers)
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
return { config, getWearablesService: () => service } as unknown as Parameters<
|
|
94
|
+
typeof registerCli
|
|
95
|
+
>[1];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Spy service recording fuseDay/fusedConversations dispatch. */
|
|
99
|
+
function spyService(): WearablesService & {
|
|
100
|
+
calls: Array<[string, string]>;
|
|
101
|
+
} {
|
|
102
|
+
const calls: Array<[string, string]> = [];
|
|
103
|
+
const svc = {
|
|
104
|
+
fuseDay: async (date: string) => {
|
|
105
|
+
calls.push(["fuseDay", date]);
|
|
106
|
+
return {
|
|
107
|
+
date,
|
|
108
|
+
sources: ["limitless", "bee"],
|
|
109
|
+
conversationCount: 2,
|
|
110
|
+
contentHash: "h",
|
|
111
|
+
written: true,
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
fusedConversations: async (date: string) => {
|
|
115
|
+
calls.push(["fusedConversations", date]);
|
|
116
|
+
return [];
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
return { ...svc, calls } as unknown as WearablesService & {
|
|
120
|
+
calls: Array<[string, string]>;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function buildRegisteredTree(): {
|
|
125
|
+
program: RecProgram;
|
|
126
|
+
wearables: RecCmd;
|
|
127
|
+
service: WearablesService & { calls: Array<[string, string]> };
|
|
128
|
+
} {
|
|
129
|
+
const program = new RecProgram();
|
|
130
|
+
const service = spyService();
|
|
131
|
+
const api: CliApi = {
|
|
132
|
+
registerCli: (handler) => handler({ program }),
|
|
133
|
+
};
|
|
134
|
+
registerCli(api, makeOrchestratorStub(service));
|
|
135
|
+
const wearables = program.root.children.get("engram")?.children.get("wearables");
|
|
136
|
+
assert.ok(wearables, "wearables command registered under engram");
|
|
137
|
+
return { program, wearables: wearables!, service };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
test("wearablesCmd registers fuse and fused subcommands (#1849)", () => {
|
|
141
|
+
const { wearables } = buildRegisteredTree();
|
|
142
|
+
// fuse/fused must be present alongside the previously-registered forwarders.
|
|
143
|
+
const subcommands = [...wearables.children.keys()];
|
|
144
|
+
for (const expected of [
|
|
145
|
+
"status",
|
|
146
|
+
"check",
|
|
147
|
+
"sync",
|
|
148
|
+
"transcript",
|
|
149
|
+
"search",
|
|
150
|
+
"memories",
|
|
151
|
+
"speakers",
|
|
152
|
+
"corrections",
|
|
153
|
+
"fuse",
|
|
154
|
+
"fused",
|
|
155
|
+
]) {
|
|
156
|
+
assert.ok(
|
|
157
|
+
subcommands.includes(expected),
|
|
158
|
+
`wearables ${expected} subcommand registered`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const fuse = wearables.children.get("fuse")!;
|
|
163
|
+
assert.ok(
|
|
164
|
+
fuse.rawName.includes("<date>"),
|
|
165
|
+
"fuse declares a <date> argument",
|
|
166
|
+
);
|
|
167
|
+
assert.ok(fuse.options.includes("--json"), "fuse forwards --json");
|
|
168
|
+
|
|
169
|
+
const fused = wearables.children.get("fused")!;
|
|
170
|
+
assert.ok(
|
|
171
|
+
fused.rawName.includes("<date>"),
|
|
172
|
+
"fused declares a <date> argument",
|
|
173
|
+
);
|
|
174
|
+
assert.ok(fused.options.includes("--json"), "fused forwards --json");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("wearables fuse/fused forwarders dispatch to the runner, not rejected (#1849)", async () => {
|
|
178
|
+
const { wearables, service } = buildRegisteredTree();
|
|
179
|
+
const fuse = wearables.children.get("fuse")!;
|
|
180
|
+
const fused = wearables.children.get("fused")!;
|
|
181
|
+
|
|
182
|
+
// commander passes the <date> positional as arg[0] and the options object
|
|
183
|
+
// as the trailing arg — the same shape the `search <query>` forwarder uses.
|
|
184
|
+
await fuse.actionFn!("2026-06-10", { json: false });
|
|
185
|
+
assert.deepEqual(
|
|
186
|
+
service.calls[0],
|
|
187
|
+
["fuseDay", "2026-06-10"],
|
|
188
|
+
"wearables fuse <date> dispatches into the runner -> service.fuseDay",
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
await fused.actionFn!("2026-06-10", { json: true });
|
|
192
|
+
assert.deepEqual(
|
|
193
|
+
service.calls[1],
|
|
194
|
+
["fusedConversations", "2026-06-10"],
|
|
195
|
+
"wearables fused <date> dispatches into the runner -> service.fusedConversations",
|
|
196
|
+
);
|
|
197
|
+
});
|
package/src/cli.ts
CHANGED
|
@@ -5376,6 +5376,31 @@ export function registerCli(
|
|
|
5376
5376
|
.action(async (...args: unknown[]) =>
|
|
5377
5377
|
forwardWearables(["corrections", "remove", String(args[0] ?? "")]),
|
|
5378
5378
|
);
|
|
5379
|
+
wearablesCmd
|
|
5380
|
+
.command("fuse <date>")
|
|
5381
|
+
.description("Fuse (cross-source merge) wearable transcripts for a day")
|
|
5382
|
+
.option("--json", "JSON output")
|
|
5383
|
+
.action(async (...args: unknown[]) => {
|
|
5384
|
+
const options = (args[1] ?? {}) as Record<string, unknown>;
|
|
5385
|
+
await forwardWearables([
|
|
5386
|
+
"fuse",
|
|
5387
|
+
String(args[0] ?? ""),
|
|
5388
|
+
...(options.json === true ? ["--json"] : []),
|
|
5389
|
+
]);
|
|
5390
|
+
});
|
|
5391
|
+
|
|
5392
|
+
wearablesCmd
|
|
5393
|
+
.command("fused <date>")
|
|
5394
|
+
.description("List fused conversations for a day")
|
|
5395
|
+
.option("--json", "JSON output")
|
|
5396
|
+
.action(async (...args: unknown[]) => {
|
|
5397
|
+
const options = (args[1] ?? {}) as Record<string, unknown>;
|
|
5398
|
+
await forwardWearables([
|
|
5399
|
+
"fused",
|
|
5400
|
+
String(args[0] ?? ""),
|
|
5401
|
+
...(options.json === true ? ["--json"] : []),
|
|
5402
|
+
]);
|
|
5403
|
+
});
|
|
5379
5404
|
}
|
|
5380
5405
|
|
|
5381
5406
|
cmd
|
package/src/index.ts
CHANGED
|
@@ -992,6 +992,20 @@ export {
|
|
|
992
992
|
type TokenStore,
|
|
993
993
|
} from "./tokens.js";
|
|
994
994
|
|
|
995
|
+
export {
|
|
996
|
+
TOKEN_CAPABILITIES_VERSION,
|
|
997
|
+
capabilityAllowsNamespace,
|
|
998
|
+
capabilityAllowsOp,
|
|
999
|
+
isCapabilityRestricted,
|
|
1000
|
+
isValidNamespaceValue,
|
|
1001
|
+
normalizeCapabilities,
|
|
1002
|
+
tokenCapabilityStore,
|
|
1003
|
+
validateCapabilitiesForMint,
|
|
1004
|
+
type TokenCapabilities,
|
|
1005
|
+
} from "./access-token-capabilities.js";
|
|
1006
|
+
|
|
1007
|
+
export { OPERATION_NAMES, type OperationName } from "./access-boundary.js";
|
|
1008
|
+
|
|
995
1009
|
// ---------------------------------------------------------------------------
|
|
996
1010
|
// Codex materializer (#378)
|
|
997
1011
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conservative allowlist of canonical MCP tool suffixes that are
|
|
3
|
+
* unambiguously read-only. Tools in this set are tagged with
|
|
4
|
+
* `annotations: { readOnlyHint: true }` so ChatGPT (and other MCP
|
|
5
|
+
* clients that honor the hint) can skip per-call confirmation.
|
|
6
|
+
*
|
|
7
|
+
* The list is suffix-based so it covers both the `remnic.*` and
|
|
8
|
+
* `engram.*` naming forms. Anything not on it stays unannotated:
|
|
9
|
+
* uncertainty is resolved as "might mutate".
|
|
10
|
+
*
|
|
11
|
+
* Excluded by construction: anything that writes, runs a pipeline,
|
|
12
|
+
* flushes, applies, records, imports, or destructively deletes.
|
|
13
|
+
*/
|
|
14
|
+
export const MCP_READ_ONLY_TOOL_SUFFIXES: Readonly<Record<string, true>> = {
|
|
15
|
+
recall: true,
|
|
16
|
+
recall_explain: true,
|
|
17
|
+
recall_tier_explain: true,
|
|
18
|
+
recall_xray: true,
|
|
19
|
+
briefing: true,
|
|
20
|
+
wearables_status: true,
|
|
21
|
+
transcript_day: true,
|
|
22
|
+
transcript_search: true,
|
|
23
|
+
transcript_memories: true,
|
|
24
|
+
action_confidence: true,
|
|
25
|
+
capsule_list: true,
|
|
26
|
+
procedural_stats: true,
|
|
27
|
+
memory_get: true,
|
|
28
|
+
memory_timeline: true,
|
|
29
|
+
entity_get: true,
|
|
30
|
+
review_queue_list: true,
|
|
31
|
+
lcm_search: true,
|
|
32
|
+
continuity_incident_list: true,
|
|
33
|
+
identity_anchor_get: true,
|
|
34
|
+
memory_identity: true,
|
|
35
|
+
memory_search: true,
|
|
36
|
+
memory_profile: true,
|
|
37
|
+
memory_entities_list: true,
|
|
38
|
+
memory_questions: true,
|
|
39
|
+
memory_last_recall: true,
|
|
40
|
+
memory_intent_debug: true,
|
|
41
|
+
memory_qmd_debug: true,
|
|
42
|
+
memory_graph_explain: true,
|
|
43
|
+
graph_snapshot: true,
|
|
44
|
+
review_list: true,
|
|
45
|
+
profiling_report: true,
|
|
46
|
+
peer_list: true,
|
|
47
|
+
peer_get: true,
|
|
48
|
+
peer_profile_get: true,
|
|
49
|
+
console_state: true,
|
|
50
|
+
dreams_status: true,
|
|
51
|
+
codegraph_list_projects: true,
|
|
52
|
+
codegraph_index_status: true,
|
|
53
|
+
codegraph_search_graph: true,
|
|
54
|
+
codegraph_trace_path: true,
|
|
55
|
+
codegraph_detect_changes: true,
|
|
56
|
+
codegraph_query_graph: true,
|
|
57
|
+
codegraph_get_schema: true,
|
|
58
|
+
codegraph_get_snippet: true,
|
|
59
|
+
codegraph_get_architecture: true,
|
|
60
|
+
codegraph_search_code: true,
|
|
61
|
+
};
|
package/src/storage.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { serializeProvenanceFields, parseProvenanceSources, parseProvenanceTag,
|
|
|
23
23
|
import { serializeFaithfulnessFields, parseFaithfulnessField } from "./extraction-faithfulness.js";
|
|
24
24
|
import { createVersion as createPageVersion, type VersioningConfig, type VersionTrigger } from "./page-versioning.js";
|
|
25
25
|
import { isValidTranscriptDate, WEARABLES_DIR_NAME } from "./wearables/day-store.js";
|
|
26
|
+
import { FusionArtifactStore } from "./wearables/fusion/index.js";
|
|
26
27
|
import {
|
|
27
28
|
SecureStoreLockedError,
|
|
28
29
|
MAGIC_HEADER_SIZE,
|
|
@@ -2709,6 +2710,21 @@ export class StorageManager {
|
|
|
2709
2710
|
);
|
|
2710
2711
|
}
|
|
2711
2712
|
|
|
2713
|
+
private _fusionStore?: FusionArtifactStore;
|
|
2714
|
+
|
|
2715
|
+
/** Derived fusion-day IO; file IO lives in wearables/fusion (#1810). */
|
|
2716
|
+
fusionArtifactStore(): FusionArtifactStore {
|
|
2717
|
+
if (this._fusionStore) return this._fusionStore;
|
|
2718
|
+
return (this._fusionStore = new FusionArtifactStore(this.wearablesDir, this.baseDir, {
|
|
2719
|
+
writeFile: (p, c) => this.writeStorageSecureFile(p, c),
|
|
2720
|
+
readFile: (p) => readMaybeEncryptedFile(p, this._secureStoreKey, this.baseDir),
|
|
2721
|
+
readDir: (d) => readdir(d),
|
|
2722
|
+
deleteFile: (p) => unlink(p),
|
|
2723
|
+
realpath: (p) => realpath(p),
|
|
2724
|
+
lstat: (p) => lstat(p).then((st) => ({ isSymbolicLink: st.isSymbolicLink() })),
|
|
2725
|
+
}));
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2712
2728
|
/**
|
|
2713
2729
|
* Locate a wearable-sourced memory by exact (trimmed) content,
|
|
2714
2730
|
* ignoring the "[Attributes: ...]" suffix writeMemory appends for
|
package/src/tokens.ts
CHANGED
|
@@ -9,11 +9,27 @@ import fs from "node:fs";
|
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
11
11
|
import { resolveHomeDir } from "./runtime/env.js";
|
|
12
|
+
import {
|
|
13
|
+
TOKEN_CAPABILITIES_VERSION,
|
|
14
|
+
normalizeCapabilities,
|
|
15
|
+
type TokenCapabilities,
|
|
16
|
+
} from "./access-token-capabilities.js";
|
|
12
17
|
|
|
13
18
|
export interface TokenEntry {
|
|
14
19
|
token: string;
|
|
15
20
|
connector: string;
|
|
16
21
|
createdAt: string;
|
|
22
|
+
/**
|
|
23
|
+
* Versioned capabilities record (issue #1837). PRESENT ⇒ the token was
|
|
24
|
+
* minted by the capability-aware path; its ops/namespaces axes are
|
|
25
|
+
* enforced (absent axis ⇒ unrestricted on that axis; empty array ⇒ deny
|
|
26
|
+
* all; non-empty ⇒ allow-list). ABSENT ⇒ a pre-feature legacy entry ⇒
|
|
27
|
+
* unrestricted (full access), purely for backward compatibility — this is
|
|
28
|
+
* the ONLY path by which full access is granted by omission. Every new
|
|
29
|
+
* token carries an explicit record (even `{ version: 1 }` for a deliberate
|
|
30
|
+
* unrestricted mint), so a new token can never silently gain full access.
|
|
31
|
+
*/
|
|
32
|
+
capabilities?: TokenCapabilities;
|
|
17
33
|
}
|
|
18
34
|
|
|
19
35
|
export interface TokenStore {
|
|
@@ -75,6 +91,9 @@ function validateTokenEntry(raw: unknown, index: number): TokenEntry {
|
|
|
75
91
|
if (typeof entry.connector !== "string" || entry.connector.length === 0) {
|
|
76
92
|
throw new Error(`invalid token entry at index ${index}: connector must be a non-empty string`);
|
|
77
93
|
}
|
|
94
|
+
const capabilities = entry.capabilities !== undefined
|
|
95
|
+
? normalizeCapabilities(entry.capabilities)
|
|
96
|
+
: undefined;
|
|
78
97
|
return {
|
|
79
98
|
token: entry.token,
|
|
80
99
|
connector: entry.connector,
|
|
@@ -82,6 +101,7 @@ function validateTokenEntry(raw: unknown, index: number): TokenEntry {
|
|
|
82
101
|
typeof entry.createdAt === "string" && entry.createdAt.length > 0
|
|
83
102
|
? entry.createdAt
|
|
84
103
|
: new Date(0).toISOString(),
|
|
104
|
+
...(capabilities ? { capabilities } : {}),
|
|
85
105
|
};
|
|
86
106
|
}
|
|
87
107
|
|
|
@@ -201,13 +221,18 @@ export function saveTokenStore(store: TokenStore, tokensPath?: string): void {
|
|
|
201
221
|
* dependent write (e.g. Hermes config.yaml) succeeds — see
|
|
202
222
|
* commitTokenEntry() to persist the candidate.
|
|
203
223
|
*/
|
|
204
|
-
export function buildTokenEntry(connector: string): TokenEntry {
|
|
224
|
+
export function buildTokenEntry(connector: string, capabilities?: TokenCapabilities): TokenEntry {
|
|
205
225
|
const prefix = TOKEN_PREFIXES[connector] ?? "remnic_xx_";
|
|
206
226
|
const token = prefix + randomBytes(24).toString("hex");
|
|
227
|
+
// Every newly-built entry carries an EXPLICIT capabilities record (issue
|
|
228
|
+
// #1837). When none is requested, record a deliberate unrestricted
|
|
229
|
+
// `{ version: 1 }` — never an omitted field — so "full access via omission"
|
|
230
|
+
// is reachable only by pre-feature legacy entries lacking the marker.
|
|
207
231
|
return {
|
|
208
232
|
token,
|
|
209
233
|
connector,
|
|
210
234
|
createdAt: new Date().toISOString(),
|
|
235
|
+
capabilities: capabilities ?? { version: TOKEN_CAPABILITIES_VERSION },
|
|
211
236
|
};
|
|
212
237
|
}
|
|
213
238
|
|
|
@@ -230,13 +255,17 @@ export function commitTokenEntry(entry: TokenEntry, tokensPath?: string): void {
|
|
|
230
255
|
saveTokenStore(store, tokensPath);
|
|
231
256
|
}
|
|
232
257
|
|
|
233
|
-
export function generateToken(
|
|
258
|
+
export function generateToken(
|
|
259
|
+
connector: string,
|
|
260
|
+
tokensPath?: string,
|
|
261
|
+
capabilities?: TokenCapabilities,
|
|
262
|
+
): TokenEntry {
|
|
234
263
|
const store = loadTokenStore(tokensPath);
|
|
235
264
|
|
|
236
265
|
// Remove existing token for this connector
|
|
237
266
|
store.tokens = store.tokens.filter((t) => t.connector !== connector);
|
|
238
267
|
|
|
239
|
-
const entry = buildTokenEntry(connector);
|
|
268
|
+
const entry = buildTokenEntry(connector, capabilities);
|
|
240
269
|
store.tokens.push(entry);
|
|
241
270
|
saveTokenStore(store, tokensPath);
|
|
242
271
|
return entry;
|
|
@@ -198,3 +198,105 @@ test("backend faults propagate instead of being swallowed as exit codes", async
|
|
|
198
198
|
/disk exploded/,
|
|
199
199
|
);
|
|
200
200
|
});
|
|
201
|
+
|
|
202
|
+
test("fuse command requires a date and renders the fuseDay result", async () => {
|
|
203
|
+
const { io, out, err } = makeIo();
|
|
204
|
+
const service = stubService({
|
|
205
|
+
fuseDay: async () => ({
|
|
206
|
+
date: "2026-06-10",
|
|
207
|
+
sources: ["bee", "limitless"],
|
|
208
|
+
conversationCount: 2,
|
|
209
|
+
contentHash: "abc123",
|
|
210
|
+
written: true,
|
|
211
|
+
}),
|
|
212
|
+
});
|
|
213
|
+
assert.equal(await runWearablesCliCommand(service, ["fuse"], io), 1);
|
|
214
|
+
assert.match(err.join(""), /fuse requires a date/);
|
|
215
|
+
|
|
216
|
+
// Success path needs its own captured stdout.
|
|
217
|
+
const okIo = makeIo();
|
|
218
|
+
assert.equal(
|
|
219
|
+
await runWearablesCliCommand(service, ["fuse", "2026-06-10"], okIo.io),
|
|
220
|
+
0,
|
|
221
|
+
);
|
|
222
|
+
assert.match(
|
|
223
|
+
okIo.out.join(""),
|
|
224
|
+
/Fused 2026-06-10: 2 conversations from 2 sources/,
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("fuse command prints a distinct skip line when fusion is skipped, not '(unchanged)'", async () => {
|
|
229
|
+
// written:false has two meanings (issue #1849): a skip (e.g. conflicting
|
|
230
|
+
// source timezones, which also clears any existing fused artifact) must
|
|
231
|
+
// NOT be mislabeled as an idempotent "(unchanged)" no-op.
|
|
232
|
+
const { io, out } = makeIo();
|
|
233
|
+
const service = stubService({
|
|
234
|
+
fuseDay: async () => ({
|
|
235
|
+
date: "2026-06-10",
|
|
236
|
+
sources: [],
|
|
237
|
+
conversationCount: 0,
|
|
238
|
+
contentHash: "",
|
|
239
|
+
written: false,
|
|
240
|
+
skipped: { reason: "conflicting-timezones" },
|
|
241
|
+
}),
|
|
242
|
+
});
|
|
243
|
+
assert.equal(await runWearablesCliCommand(service, ["fuse", "2026-06-10"], io), 0);
|
|
244
|
+
const text = out.join("");
|
|
245
|
+
assert.match(text, /Skipped fusing 2026-06-10: conflicting-timezones/);
|
|
246
|
+
assert.match(text, /cleared/);
|
|
247
|
+
assert.doesNotMatch(text, /unchanged/, "a skip must not look idempotent");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("fuse command keeps '(unchanged)' for written:false without a skip reason", async () => {
|
|
251
|
+
const { io, out } = makeIo();
|
|
252
|
+
const service = stubService({
|
|
253
|
+
fuseDay: async () => ({
|
|
254
|
+
date: "2026-06-10",
|
|
255
|
+
sources: ["bee", "limitless"],
|
|
256
|
+
conversationCount: 2,
|
|
257
|
+
contentHash: "abc123",
|
|
258
|
+
written: false,
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
261
|
+
assert.equal(await runWearablesCliCommand(service, ["fuse", "2026-06-10"], io), 0);
|
|
262
|
+
assert.match(out.join(""), /\(unchanged\)/);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("fused command lists conversations and surfaces disagreements (--json)", async () => {
|
|
266
|
+
const { io, out } = makeIo();
|
|
267
|
+
const service = stubService({
|
|
268
|
+
fusedConversations: async () => [
|
|
269
|
+
{
|
|
270
|
+
id: "fusion-1",
|
|
271
|
+
date: "2026-06-10",
|
|
272
|
+
startIso: "2026-06-10T09:00:00.000Z",
|
|
273
|
+
sources: ["bee", "limitless"],
|
|
274
|
+
speakers: [],
|
|
275
|
+
segments: [
|
|
276
|
+
{ speaker: "Me (you)", isSelf: true, text: "hi", confidence: 0.8, provenance: { source: "limitless", conversationId: "c1", sourceTrust: 0.8, reason: "only-source", alternatives: [] } },
|
|
277
|
+
],
|
|
278
|
+
disagreements: [
|
|
279
|
+
{ kind: "asr-text", subject: "2026-06-10T09:00:00.000Z", candidates: [{ source: "bee", value: "x" }, { source: "limitless", value: "y" }] },
|
|
280
|
+
],
|
|
281
|
+
provenance: { contributions: [], proximityGapMs: 300000, windowToleranceMs: 30000, method: "time-proximity" },
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
});
|
|
285
|
+
assert.equal(await runWearablesCliCommand(service, ["fused", "2026-06-10", "--json"], io), 0);
|
|
286
|
+
const parsed = JSON.parse(out.join(""));
|
|
287
|
+
assert.equal(parsed.date, "2026-06-10");
|
|
288
|
+
assert.equal(parsed.conversations.length, 1);
|
|
289
|
+
assert.equal(parsed.conversations[0].disagreements.length, 1);
|
|
290
|
+
|
|
291
|
+
// Plain-text path surfaces the disagreement count.
|
|
292
|
+
const text = makeIo();
|
|
293
|
+
assert.equal(await runWearablesCliCommand(service, ["fused", "2026-06-10"], text.io), 0);
|
|
294
|
+
assert.match(text.out.join(""), /disagreement \[asr-text\]/);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test("fused command reports nothing-fused gracefully", async () => {
|
|
298
|
+
const { io, out } = makeIo();
|
|
299
|
+
const service = stubService({ fusedConversations: async () => [] });
|
|
300
|
+
assert.equal(await runWearablesCliCommand(service, ["fused", "2026-06-10"], io), 0);
|
|
301
|
+
assert.match(out.join(""), /No fused conversations for 2026-06-10/);
|
|
302
|
+
});
|
package/src/wearables/cli.ts
CHANGED
|
@@ -33,6 +33,8 @@ Commands:
|
|
|
33
33
|
--source <id> --from <date> --to <date> --limit <n>
|
|
34
34
|
memories [options] List memories created from transcripts
|
|
35
35
|
--source <id> --date <date> --limit <n>
|
|
36
|
+
fuse <YYYY-MM-DD> Fuse enabled sources' transcripts for a day
|
|
37
|
+
fused <YYYY-MM-DD> List fused conversations for a day
|
|
36
38
|
speakers list Show the speaker registry
|
|
37
39
|
speakers self <name> Set the wearer's display name
|
|
38
40
|
speakers set <source> <key> <name> [--self]
|
|
@@ -419,6 +421,69 @@ export async function runWearablesCliCommand(
|
|
|
419
421
|
`unknown corrections action '${action}' — expected list, add, or remove`,
|
|
420
422
|
);
|
|
421
423
|
}
|
|
424
|
+
case "fuse": {
|
|
425
|
+
const parsed = parseFlags(rest);
|
|
426
|
+
const date = flagString(parsed, "--date") ?? parsed.positional[0];
|
|
427
|
+
if (!date) {
|
|
428
|
+
throw new WearablesInputError(
|
|
429
|
+
"fuse requires a date (e.g. wearables fuse 2026-06-10)",
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
const result = await service.fuseDay(date);
|
|
433
|
+
if (parsed.flags.has("--json")) {
|
|
434
|
+
io.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
435
|
+
return 0;
|
|
436
|
+
}
|
|
437
|
+
// `written:false` has two distinct meanings (issue #1849): a genuine
|
|
438
|
+
// idempotent no-op (inputs unchanged) OR a deliberate SKIP — e.g.
|
|
439
|
+
// mixed source timezones — where fusion was refused and any
|
|
440
|
+
// previously-fused artifact for the day was cleared. The JSON path
|
|
441
|
+
// carries `skipped`, but the text path collapsed both into
|
|
442
|
+
// "(unchanged)", hiding a skip (and a possible artifact deletion)
|
|
443
|
+
// behind an idempotent-looking label. Check `skipped` first and emit
|
|
444
|
+
// a distinct skip line; reserve "(unchanged)" for written:false
|
|
445
|
+
// WITHOUT a skip reason. Mirrors the `skipped` field the JSON path
|
|
446
|
+
// serializes.
|
|
447
|
+
if (result.skipped) {
|
|
448
|
+
io.stdout.write(
|
|
449
|
+
`Skipped fusing ${date}: ${result.skipped.reason} — no fused artifact written; any existing fused artifact for the day was cleared.\n`,
|
|
450
|
+
);
|
|
451
|
+
} else {
|
|
452
|
+
io.stdout.write(
|
|
453
|
+
`Fused ${date}: ${result.conversationCount} conversation${result.conversationCount === 1 ? "" : "s"} from ${result.sources.length} source${result.sources.length === 1 ? "" : "s"}.${result.written ? "" : " (unchanged)"}\n`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
return 0;
|
|
457
|
+
}
|
|
458
|
+
case "fused": {
|
|
459
|
+
const parsed = parseFlags(rest);
|
|
460
|
+
const date = flagString(parsed, "--date") ?? parsed.positional[0];
|
|
461
|
+
if (!date) {
|
|
462
|
+
throw new WearablesInputError(
|
|
463
|
+
"fused requires a date (e.g. wearables fused 2026-06-10)",
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
const conversations = await service.fusedConversations(date);
|
|
467
|
+
if (parsed.flags.has("--json")) {
|
|
468
|
+
io.stdout.write(`${JSON.stringify({ date, conversations }, null, 2)}\n`);
|
|
469
|
+
return 0;
|
|
470
|
+
}
|
|
471
|
+
if (conversations.length === 0) {
|
|
472
|
+
io.stdout.write(`No fused conversations for ${date}.\n`);
|
|
473
|
+
return 0;
|
|
474
|
+
}
|
|
475
|
+
for (const conv of conversations) {
|
|
476
|
+
io.stdout.write(
|
|
477
|
+
`${conv.id} ${conv.startIso}${conv.endIso ? ` \u2013 ${conv.endIso}` : ""} (${conv.sources.join(", ")}, ${conv.segments.length} segment${conv.segments.length === 1 ? "" : "s"})\n`,
|
|
478
|
+
);
|
|
479
|
+
for (const disagreement of conv.disagreements) {
|
|
480
|
+
io.stdout.write(
|
|
481
|
+
` disagreement [${disagreement.kind}] @ ${disagreement.subject}: ${disagreement.candidates.length} candidate${disagreement.candidates.length === 1 ? "" : "s"}\n`,
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return 0;
|
|
486
|
+
}
|
|
422
487
|
default:
|
|
423
488
|
throw new WearablesInputError(
|
|
424
489
|
`unknown wearables command '${command}'\n\n${USAGE}`,
|
|
@@ -245,3 +245,35 @@ test("a deep window narrower than the tick window is rejected, 0 disables", () =
|
|
|
245
245
|
5,
|
|
246
246
|
);
|
|
247
247
|
});
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
test("fusion defaults to disabled with documented knobs", () => {
|
|
251
|
+
const parsed = parseWearablesConfig({});
|
|
252
|
+
assert.equal(parsed.fusion.enabled, false);
|
|
253
|
+
assert.equal(parsed.fusion.proximityGapMs, 300_000);
|
|
254
|
+
assert.equal(parsed.fusion.windowToleranceMs, 30_000);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("fusion enabled flag and knobs parse and validate", () => {
|
|
258
|
+
const parsed = parseWearablesConfig({
|
|
259
|
+
fusion: { enabled: true, proximityGapMs: 120_000, windowToleranceMs: 15_000 },
|
|
260
|
+
});
|
|
261
|
+
assert.equal(parsed.fusion.enabled, true);
|
|
262
|
+
assert.equal(parsed.fusion.proximityGapMs, 120_000);
|
|
263
|
+
assert.equal(parsed.fusion.windowToleranceMs, 15_000);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("fusion knobs reject non-positive integers", () => {
|
|
267
|
+
assert.throws(
|
|
268
|
+
() => parseWearablesConfig({ fusion: { proximityGapMs: 0 } }),
|
|
269
|
+
/proximityGapMs must be a positive integer/,
|
|
270
|
+
);
|
|
271
|
+
assert.throws(
|
|
272
|
+
() => parseWearablesConfig({ fusion: { windowToleranceMs: -5 } }),
|
|
273
|
+
/windowToleranceMs must be a positive integer/,
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("fusion block must be an object when present", () => {
|
|
278
|
+
assert.throws(() => parseWearablesConfig({ fusion: "on" }), /must be an object/);
|
|
279
|
+
});
|