@remnic/core 9.45.4 → 9.46.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 +10 -10
- package/dist/access-admin-ops-surface.js +30 -29
- package/dist/access-authorization-probe.d.ts +10 -10
- package/dist/access-authorization-probe.js +31 -30
- package/dist/access-boundary.d.ts +10 -10
- package/dist/access-boundary.js +30 -29
- package/dist/access-cli.js +91 -87
- 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 +10 -10
- package/dist/access-extraction-force-flush.js +30 -29
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +10 -10
- package/dist/access-http-lifecycle-flush.d.ts +10 -10
- package/dist/access-http-offline-stream.d.ts +10 -10
- package/dist/access-http-offline-stream.js +1 -1
- package/dist/access-http-query.js +30 -29
- package/dist/access-http.d.ts +10 -11
- package/dist/access-http.js +42 -41
- package/dist/access-identity-continuity-surface.d.ts +8 -8
- package/dist/access-identity-continuity-surface.js +30 -29
- package/dist/access-lcm-surface.d.ts +10 -10
- package/dist/access-lcm-surface.js +30 -29
- package/dist/access-mcp.d.ts +10 -10
- package/dist/access-mcp.js +36 -35
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-CWUDwB4M.d.ts → access-namespace-preflight--H6HwN85.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +30 -29
- package/dist/access-observe-write-surface.d.ts +10 -10
- package/dist/access-observe-write-surface.js +30 -29
- package/dist/access-offline-manifest.d.ts +8 -8
- package/dist/access-operations-batch.js +31 -30
- package/dist/access-operations.d.ts +13 -13
- package/dist/access-operations.js +35 -34
- package/dist/access-recall-concurrency.d.ts +10 -10
- package/dist/access-recall-concurrency.js +30 -29
- package/dist/access-recall-response.d.ts +10 -10
- package/dist/access-recall-response.js +30 -29
- package/dist/access-recall-surface.d.ts +10 -10
- package/dist/access-recall-surface.js +30 -29
- package/dist/access-schema.d.ts +84 -84
- package/dist/access-service-helpers.d.ts +10 -10
- package/dist/{access-service-CsU-ljFD.d.ts → access-service-loJadYd7.d.ts} +7 -7
- package/dist/access-service.d.ts +10 -10
- package/dist/access-service.js +30 -29
- package/dist/access-surface-catalog.d.ts +10 -10
- 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/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/ambient-provenance.d.ts +109 -0
- package/dist/ambient-provenance.js +22 -0
- package/dist/ambient-provenance.js.map +1 -0
- package/dist/{auto-sync-UPTUVE4X.js → auto-sync-7O6RP2RT.js} +3 -2
- package/dist/{auto-sync-UPTUVE4X.js.map → auto-sync-7O6RP2RT.js.map} +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +8 -8
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +6 -5
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer-turn-helpers.js +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +5 -5
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-bctMURhN.d.ts → catalog-Iwbk9h5E.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 +7 -6
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-7RZJWZ46.js → chunk-227UFSEQ.js} +5 -5
- package/dist/{chunk-ER7W6H4T.js → chunk-2AD5SXO4.js} +5 -2
- package/dist/chunk-2AD5SXO4.js.map +1 -0
- package/dist/{chunk-XSFHWBQO.js → chunk-2DSUT7DZ.js} +10 -64
- package/dist/{chunk-XSFHWBQO.js.map → chunk-2DSUT7DZ.js.map} +1 -1
- package/dist/{chunk-DZPFMQZ7.js → chunk-3S63QIFQ.js} +1 -1
- package/dist/chunk-3S63QIFQ.js.map +1 -0
- package/dist/{chunk-UQWIKLQK.js → chunk-47J4EDGR.js} +9 -9
- package/dist/{chunk-5SNZSWWR.js → chunk-5LRDM4R7.js} +42 -42
- package/dist/{chunk-7YERSTN7.js → chunk-5R6S4DJZ.js} +7 -7
- package/dist/{chunk-C26TE27X.js → chunk-5VQPZ4GV.js} +3 -3
- package/dist/{chunk-U2VLVBBH.js → chunk-64BHFFKZ.js} +3 -3
- package/dist/{chunk-AJQ57VV3.js → chunk-6H744ZBH.js} +4 -4
- package/dist/chunk-6H744ZBH.js.map +1 -0
- package/dist/{chunk-7CITSYSH.js → chunk-7T4TOGMS.js} +2 -2
- package/dist/{chunk-ZMAYKGPN.js → chunk-7YQ6ESEA.js} +12 -41
- package/dist/chunk-7YQ6ESEA.js.map +1 -0
- package/dist/{chunk-YMAQDHQM.js → chunk-BDNL34CT.js} +2 -2
- package/dist/{chunk-GQL52GQ5.js → chunk-CDKHKZL5.js} +15 -12
- package/dist/chunk-CDKHKZL5.js.map +1 -0
- package/dist/{chunk-WGP57SR3.js → chunk-CK5KOVHW.js} +3 -2
- package/dist/{chunk-WGP57SR3.js.map → chunk-CK5KOVHW.js.map} +1 -1
- package/dist/{chunk-JH6BPLOP.js → chunk-E4FOAENZ.js} +4 -4
- package/dist/{chunk-ZFGZ6PZ2.js → chunk-FULKGK7P.js} +2 -2
- package/dist/{chunk-BGHFOD5A.js → chunk-HJ3SVHUW.js} +3 -3
- package/dist/{chunk-TYISDI6B.js → chunk-HXXTXLKA.js} +2 -2
- package/dist/{chunk-FZP32336.js → chunk-IDE5AR5W.js} +53 -31
- package/dist/chunk-IDE5AR5W.js.map +1 -0
- package/dist/{chunk-7SHJHPTY.js → chunk-ILBLZ5Y5.js} +2 -2
- package/dist/chunk-J7T4MX7P.js +131 -0
- package/dist/chunk-J7T4MX7P.js.map +1 -0
- package/dist/{chunk-DSUAAZZX.js → chunk-JFO5QZ7N.js} +2 -2
- package/dist/{chunk-EJHEWCGQ.js → chunk-KA47C7BF.js} +2 -2
- package/dist/{chunk-NUEQKLNB.js → chunk-KMWYZTIE.js} +2 -2
- package/dist/{chunk-TQ4GD737.js → chunk-L7G72WY4.js} +2 -2
- package/dist/{chunk-CLB32OHM.js → chunk-LI7OE7G6.js} +2 -2
- package/dist/{chunk-TN76AKO3.js → chunk-M2INOSTA.js} +2 -2
- package/dist/{chunk-DYVMXPCF.js → chunk-M32URQXV.js} +665 -614
- package/dist/chunk-M32URQXV.js.map +1 -0
- package/dist/{chunk-WQIHRA5F.js → chunk-N3LL5NEU.js} +2 -2
- package/dist/{chunk-HFPASSFV.js → chunk-P2OTGMTR.js} +2 -2
- package/dist/{chunk-Y7HRYCNW.js → chunk-PFJXPOD4.js} +2 -2
- package/dist/{chunk-MDISBSYA.js → chunk-PKCLQ7KM.js} +43 -143
- package/dist/chunk-PKCLQ7KM.js.map +1 -0
- package/dist/chunk-QW27DOHG.js +84 -0
- package/dist/chunk-QW27DOHG.js.map +1 -0
- package/dist/{chunk-ZI32MKDW.js → chunk-RCU25Z66.js} +32 -35
- package/dist/chunk-RCU25Z66.js.map +1 -0
- package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/{chunk-QCECHYZL.js → chunk-RVO6TCTU.js} +6 -6
- package/dist/{chunk-D3I2MCN7.js → chunk-RVPTS4U5.js} +3 -3
- package/dist/{chunk-W3IC7QOY.js → chunk-RVYVMQK4.js} +3 -3
- package/dist/chunk-S2HLHAZ4.js +16 -0
- package/dist/chunk-S2HLHAZ4.js.map +1 -0
- package/dist/{chunk-2MRHJNWL.js → chunk-ST5IEK3H.js} +2 -2
- package/dist/{chunk-O4KMGRXE.js → chunk-SXWHN7GC.js} +2 -2
- package/dist/{chunk-KXEFRMCF.js → chunk-T6JRMVOV.js} +2 -2
- package/dist/{chunk-R5RFK3CB.js → chunk-U6JGQ37L.js} +2 -2
- package/dist/{chunk-UV72DEZD.js → chunk-UYUYZ4WY.js} +36 -10
- package/dist/chunk-UYUYZ4WY.js.map +1 -0
- package/dist/{chunk-EITZHSX6.js → chunk-V5FE3UAI.js} +2 -2
- package/dist/chunk-VL35ZFSZ.js +102 -0
- package/dist/chunk-VL35ZFSZ.js.map +1 -0
- package/dist/{chunk-NTOIP2E6.js → chunk-W2ZSNIBP.js} +2 -2
- package/dist/{chunk-QVSBTCVP.js → chunk-WHWDBTCJ.js} +2 -2
- package/dist/{chunk-5CEECY6P.js → chunk-WOC6DXP5.js} +3 -3
- package/dist/{chunk-XTAUXTDO.js → chunk-XBMJ35IP.js} +2 -2
- package/dist/{chunk-VOUOLGIP.js → chunk-YRFAYOFQ.js} +11 -13
- package/dist/chunk-YRFAYOFQ.js.map +1 -0
- package/dist/{chunk-P7NGM24P.js → chunk-YWCOJOUI.js} +2 -2
- package/dist/{chunk-6NNNURLK.js → chunk-Z54EM5YZ.js} +4 -4
- package/dist/{chunk-ULVR5KG7.js → chunk-ZDAYWJV6.js} +2 -2
- package/dist/chunk-ZDAYWJV6.js.map +1 -0
- package/dist/{cli-BBHDtG36.d.ts → cli-D_RcqXfZ.d.ts} +5 -5
- package/dist/cli.d.ts +12 -12
- package/dist/cli.js +61 -60
- package/dist/compat/checks.js +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +6 -5
- 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/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +6 -5
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +6 -5
- 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/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +8 -7
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +17 -16
- 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 +6 -5
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +8 -8
- package/dist/external-wiki-access.d.ts +10 -10
- package/dist/external-wiki-access.js +33 -32
- package/dist/external-wiki-cli.js +4 -90
- package/dist/external-wiki-cli.js.map +1 -1
- 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 +10 -10
- package/dist/external-wiki-search.js +2 -2
- package/dist/external-wiki.d.ts +1 -5
- package/dist/external-wiki.js +1 -3
- 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-prompt.d.ts +36 -0
- package/dist/extraction-prompt.js +16 -0
- package/dist/extraction-prompt.js.map +1 -0
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +6 -14
- package/dist/extraction.js +13 -10
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-F2QJD66E.js → first-start-migration-ZSHEFEI3.js} +10 -12
- package/dist/{first-start-migration-F2QJD66E.js.map → first-start-migration-ZSHEFEI3.js.map} +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-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +701 -697
- package/dist/index.js +133 -128
- 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 +6 -5
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +6 -5
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +7 -6
- package/dist/{maintenance-D2mveyCd.d.ts → maintenance-DQrL93Uu.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +10 -10
- 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-snapshot.d.ts +10 -0
- package/dist/memory-snapshot.js +8 -0
- package/dist/memory-snapshot.js.map +1 -0
- 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 +16 -15
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +6 -5
- 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 +18 -17
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +22 -21
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +11 -10
- package/dist/{orchestrator-DJYlwiUv.d.ts → orchestrator-LPGegii3.d.ts} +8 -8
- package/dist/orchestrator.d.ts +8 -8
- package/dist/orchestrator.js +93 -89
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-CNjY4wk3.d.ts → pipeline-1h2GpiHP.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/{qmd-C0UDZidt.d.ts → qmd-UdwoF71D.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-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/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 +76 -76
- package/dist/{scope-profiles-BJzp2F5A.d.ts → scope-profiles-APjhSe-w.d.ts} +1 -1
- package/dist/sdk-compat.js.map +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +14 -14
- 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-D4KAZO2G.d.ts → semantic-consolidation-CrySDbk3.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +7 -6
- package/dist/semantic-rule-promotion.js +6 -5
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +6 -5
- package/dist/{service-BeDxSqE-.d.ts → service-DJ0LDyuN.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 +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +4 -1
- package/dist/source-agent-qualifier.js +10 -9
- package/dist/{storage-DfjxusTE.d.ts → storage-y340tYKb.d.ts} +5 -2
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +5 -4
- 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-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-migration.js +1 -1
- 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/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-yX_FwERf.d.ts → types-D_Au9qJO.d.ts} +13 -2
- 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 +6 -5
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -12
- package/src/access-http-offline-stream.ts +1 -0
- package/src/access-http.ts +0 -57
- package/src/access-operations-batch.ts +1 -1
- package/src/access-service-observe-lcm-parity.test.ts +45 -20
- package/src/access-service.ts +3 -4
- package/src/ambient-provenance.test.ts +510 -0
- package/src/ambient-provenance.ts +229 -0
- package/src/buffer-turn-helpers.ts +17 -1
- package/src/compat/checks.test.ts +45 -1
- package/src/compat/checks.ts +16 -16
- package/src/external-wiki-cli.ts +28 -20
- package/src/external-wiki-search.test.ts +68 -2
- package/src/external-wiki.ts +12 -38
- package/src/extraction-prompt.ts +142 -0
- package/src/extraction.ts +37 -148
- package/src/index.ts +1 -5
- package/src/maintenance/first-start-migration.ts +11 -11
- package/src/meetings/memory-gen.test.ts +51 -0
- package/src/meetings/memory-gen.ts +36 -8
- package/src/meetings/summary-extractor.ts +4 -2
- package/src/memory-snapshot.ts +15 -0
- package/src/orchestration/recall-internal.ts +5 -148
- package/src/orchestration/recall-profile-storage.ts +194 -0
- package/src/orchestration/recall-search-pipeline.ts +13 -182
- package/src/orchestration/recall-search-prefilter.ts +254 -0
- package/src/orchestration/session-context.ts +1 -1
- package/src/orchestrator.ts +1 -1
- package/src/sdk-compat.ts +2 -4
- package/src/source-agent-qualifier.ts +3 -0
- package/src/storage-contract/cache-coherence.test.ts +99 -0
- package/src/storage-fact-hash-index-defer.test.ts +14 -7
- package/src/storage.ts +47 -48
- package/src/testing/subjects/first-start-tier-migration.test.ts +75 -0
- package/src/testing/subjects/recall-profile-storage.test.ts +362 -0
- package/src/testing/subjects/recall-search-prefilter.test.ts +365 -0
- package/src/tier-migration.ts +10 -12
- package/src/types.ts +2 -2
- package/src/wearables/memory-gen.test.ts +82 -0
- package/src/wearables/memory-gen.ts +54 -5
- package/src/wearables/trust.ts +19 -2
- package/dist/chunk-AJQ57VV3.js.map +0 -1
- package/dist/chunk-DYVMXPCF.js.map +0 -1
- package/dist/chunk-DZPFMQZ7.js.map +0 -1
- package/dist/chunk-ER7W6H4T.js.map +0 -1
- package/dist/chunk-FZP32336.js.map +0 -1
- package/dist/chunk-GQL52GQ5.js.map +0 -1
- package/dist/chunk-MDISBSYA.js.map +0 -1
- package/dist/chunk-ULVR5KG7.js.map +0 -1
- package/dist/chunk-UV72DEZD.js.map +0 -1
- package/dist/chunk-VOUOLGIP.js.map +0 -1
- package/dist/chunk-ZI32MKDW.js.map +0 -1
- package/dist/chunk-ZMAYKGPN.js.map +0 -1
- package/dist/legacy-hook-compat.d.ts +0 -3
- package/dist/legacy-hook-compat.js +0 -36
- package/dist/legacy-hook-compat.js.map +0 -1
- package/src/legacy-hook-compat.test.ts +0 -20
- package/src/legacy-hook-compat.ts +0 -45
- /package/dist/{chunk-7RZJWZ46.js.map → chunk-227UFSEQ.js.map} +0 -0
- /package/dist/{chunk-UQWIKLQK.js.map → chunk-47J4EDGR.js.map} +0 -0
- /package/dist/{chunk-5SNZSWWR.js.map → chunk-5LRDM4R7.js.map} +0 -0
- /package/dist/{chunk-7YERSTN7.js.map → chunk-5R6S4DJZ.js.map} +0 -0
- /package/dist/{chunk-C26TE27X.js.map → chunk-5VQPZ4GV.js.map} +0 -0
- /package/dist/{chunk-U2VLVBBH.js.map → chunk-64BHFFKZ.js.map} +0 -0
- /package/dist/{chunk-7CITSYSH.js.map → chunk-7T4TOGMS.js.map} +0 -0
- /package/dist/{chunk-YMAQDHQM.js.map → chunk-BDNL34CT.js.map} +0 -0
- /package/dist/{chunk-JH6BPLOP.js.map → chunk-E4FOAENZ.js.map} +0 -0
- /package/dist/{chunk-ZFGZ6PZ2.js.map → chunk-FULKGK7P.js.map} +0 -0
- /package/dist/{chunk-BGHFOD5A.js.map → chunk-HJ3SVHUW.js.map} +0 -0
- /package/dist/{chunk-TYISDI6B.js.map → chunk-HXXTXLKA.js.map} +0 -0
- /package/dist/{chunk-7SHJHPTY.js.map → chunk-ILBLZ5Y5.js.map} +0 -0
- /package/dist/{chunk-DSUAAZZX.js.map → chunk-JFO5QZ7N.js.map} +0 -0
- /package/dist/{chunk-EJHEWCGQ.js.map → chunk-KA47C7BF.js.map} +0 -0
- /package/dist/{chunk-NUEQKLNB.js.map → chunk-KMWYZTIE.js.map} +0 -0
- /package/dist/{chunk-TQ4GD737.js.map → chunk-L7G72WY4.js.map} +0 -0
- /package/dist/{chunk-CLB32OHM.js.map → chunk-LI7OE7G6.js.map} +0 -0
- /package/dist/{chunk-TN76AKO3.js.map → chunk-M2INOSTA.js.map} +0 -0
- /package/dist/{chunk-WQIHRA5F.js.map → chunk-N3LL5NEU.js.map} +0 -0
- /package/dist/{chunk-HFPASSFV.js.map → chunk-P2OTGMTR.js.map} +0 -0
- /package/dist/{chunk-Y7HRYCNW.js.map → chunk-PFJXPOD4.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-QCECHYZL.js.map → chunk-RVO6TCTU.js.map} +0 -0
- /package/dist/{chunk-D3I2MCN7.js.map → chunk-RVPTS4U5.js.map} +0 -0
- /package/dist/{chunk-W3IC7QOY.js.map → chunk-RVYVMQK4.js.map} +0 -0
- /package/dist/{chunk-2MRHJNWL.js.map → chunk-ST5IEK3H.js.map} +0 -0
- /package/dist/{chunk-O4KMGRXE.js.map → chunk-SXWHN7GC.js.map} +0 -0
- /package/dist/{chunk-KXEFRMCF.js.map → chunk-T6JRMVOV.js.map} +0 -0
- /package/dist/{chunk-R5RFK3CB.js.map → chunk-U6JGQ37L.js.map} +0 -0
- /package/dist/{chunk-EITZHSX6.js.map → chunk-V5FE3UAI.js.map} +0 -0
- /package/dist/{chunk-NTOIP2E6.js.map → chunk-W2ZSNIBP.js.map} +0 -0
- /package/dist/{chunk-QVSBTCVP.js.map → chunk-WHWDBTCJ.js.map} +0 -0
- /package/dist/{chunk-5CEECY6P.js.map → chunk-WOC6DXP5.js.map} +0 -0
- /package/dist/{chunk-XTAUXTDO.js.map → chunk-XBMJ35IP.js.map} +0 -0
- /package/dist/{chunk-P7NGM24P.js.map → chunk-YWCOJOUI.js.map} +0 -0
- /package/dist/{chunk-6NNNURLK.js.map → chunk-Z54EM5YZ.js.map} +0 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient-capture provenance for extraction (issue #2294).
|
|
3
|
+
*
|
|
4
|
+
* Always-on capture devices — wearable recorders, room microphones — hand the
|
|
5
|
+
* extraction engine speech the user never authored: television and film
|
|
6
|
+
* dialogue, podcasts, radio, music with spoken segments, and conversations the
|
|
7
|
+
* user merely walked past. The extraction prompt otherwise reads every turn as
|
|
8
|
+
* first-person user input, so a scripted line about a relative's birthday or a
|
|
9
|
+
* medical diagnosis lands as a high-confidence personal fact and auto-promotes
|
|
10
|
+
* into recall. That output is plausible, personally relevant, and wrong, which
|
|
11
|
+
* makes it materially worse than a missed fact.
|
|
12
|
+
*
|
|
13
|
+
* This module owns both halves of the fix:
|
|
14
|
+
*
|
|
15
|
+
* 1. The prompt text that teaches the model to separate the user's own speech
|
|
16
|
+
* from background media, drop high-consequence personal claims it cannot
|
|
17
|
+
* attribute to the user, and score the remainder speculatively. One source
|
|
18
|
+
* of truth, shared by the cloud, direct-client, and local-LLM prompts so the
|
|
19
|
+
* three paths cannot drift.
|
|
20
|
+
* 2. A deterministic backstop — `clampAmbientCaptureConfidence` — for when the
|
|
21
|
+
* model ignores the prompt anyway. It clamps ONLY high-impact personal facts
|
|
22
|
+
* (family, milestones, medical) to the speculative ceiling. Clamping every
|
|
23
|
+
* ambient fact would gut the wearable integration, which is explicitly not
|
|
24
|
+
* the goal; clamping this class costs a birthday landing in the review queue
|
|
25
|
+
* instead of active recall.
|
|
26
|
+
*
|
|
27
|
+
* The provenance signal itself is `BufferTurn.ambientCapture`, set by the
|
|
28
|
+
* ingesting subsystem (the wearable memory pass), never by tool arguments.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import type { ExtractionResult } from "./types.js";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Upper bound of the speculative confidence tier, matching the tier table in
|
|
35
|
+
* the extraction prompt ("Speculative (0.00-0.39)"). A fact at or below this
|
|
36
|
+
* value cannot auto-promote through the wearable trust bands.
|
|
37
|
+
*/
|
|
38
|
+
export const SPECULATIVE_CONFIDENCE_CEILING = 0.39;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Appended to the speculative tier definition when the input is ambient
|
|
42
|
+
* capture, so the tier itself carries the rule rather than relying on the
|
|
43
|
+
* separate section below being obeyed in isolation.
|
|
44
|
+
*/
|
|
45
|
+
export const AMBIENT_SPECULATIVE_TIER_CLAUSE =
|
|
46
|
+
" Speculative also applies to any fact whose only evidence is ambient or background audio from an always-on capture device, even when the content is concrete and specific.";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Full provenance section for the cloud and direct-client extraction prompts.
|
|
50
|
+
*/
|
|
51
|
+
export const AMBIENT_CAPTURE_PROMPT_SECTION = `
|
|
52
|
+
Source provenance — always-on capture audio:
|
|
53
|
+
- This transcript came from an always-on capture device (a wearable recorder or room microphone). It may contain audio the user did not author: television, film, podcasts, radio, advertisements, music with spoken segments, and conversations the user merely overheard or walked past.
|
|
54
|
+
- Separate the user's own speech from background media before extracting anything. Narrative, scripted, performed, or third-person speech — a show's dialogue, a podcast host, an ad read, a lecture playing in the room — is media, not user input. Never attribute it to the user or to anyone the user knows.
|
|
55
|
+
- High-consequence personal claims must be dropped entirely unless the user's own voice is clearly identifiable as their source: family relationships, personal milestones (births, birthdays, anniversaries, weddings, funerals, divorces), and any medical or health detail. Omit them; do not emit them at reduced confidence as a compromise.
|
|
56
|
+
- When you cannot tell whether a statement was spoken by the user or captured passively, skip it or set its confidence in the speculative tier (0.00-0.39). Never resolve that doubt upward.`;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Compact variant for the local-LLM prompt, which is deliberately small to fit
|
|
60
|
+
* 4k-8k context windows.
|
|
61
|
+
*/
|
|
62
|
+
export const AMBIENT_CAPTURE_PROMPT_SECTION_COMPACT = `
|
|
63
|
+
Ambient capture:
|
|
64
|
+
- This transcript came from an always-on capture device and may contain TV, film, podcast, music, or overheard speech the user did not author.
|
|
65
|
+
- Treat narrative, scripted, or third-person speech as media, not user input.
|
|
66
|
+
- Drop family, milestone (birth, birthday, anniversary, wedding, funeral), and medical claims unless the user's own voice is clearly the source.
|
|
67
|
+
- Otherwise, when unsure whether the user said it, set confidence in the speculative tier (0.00-0.39).`;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Single rule for prompts that already have their own rule list and only need
|
|
71
|
+
* the ambient warning (the meeting scribe, whose transcript is the same
|
|
72
|
+
* wearable audio).
|
|
73
|
+
*/
|
|
74
|
+
export const AMBIENT_CAPTURE_PROMPT_RULE =
|
|
75
|
+
"- The transcript may contain ambient audio nobody in the meeting authored: television, podcasts, music, or a passing conversation. Treat scripted, performed, or third-person speech as background media and never record it as a decision, commitment, or question.";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Categories that are personal-claim-shaped by construction, independent of any
|
|
79
|
+
* vocabulary: `moment` is defined as an emotionally significant event or
|
|
80
|
+
* milestone, and on ambient audio a `relationship` claim is a link the model
|
|
81
|
+
* inferred between people it overheard. Non-lexical, so they still catch harm
|
|
82
|
+
* the term lists below miss.
|
|
83
|
+
*/
|
|
84
|
+
const HIGH_IMPACT_CATEGORIES: Record<string, true> = { moment: true, relationship: true };
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Kinship, milestone, and health vocabulary. Deliberately broad: a false
|
|
88
|
+
* positive costs one ambient fact a trip through the review queue, a false
|
|
89
|
+
* negative puts a fabricated medical claim into active recall. The list cannot
|
|
90
|
+
* be exhaustive and is not the primary defense — the prompt is, and the
|
|
91
|
+
* category signal above plus the trust-band cap in the wearable pass catch
|
|
92
|
+
* classes no word list enumerates. Extend it when a miss is observed.
|
|
93
|
+
*
|
|
94
|
+
* Word-bounded alternation only — no nesting, no backtracking risk.
|
|
95
|
+
*/
|
|
96
|
+
const HIGH_IMPACT_TOPIC_PATTERN =
|
|
97
|
+
/\b(?:mother|mothers|mom|moms|mum|father|fathers|dad|dads|sister|sisters|brother|brothers|son|sons|daughter|daughters|wife|wives|husband|husbands|spouse|fiance|fiancee|grandmother|grandfather|grandma|grandpa|grandparent|grandparents|grandson|granddaughter|grandchild|grandchildren|aunt|uncle|cousin|niece|nephew|stepmother|stepfather|stepson|stepdaughter|stepchild|godmother|godfather|birthday|birthdays|anniversary|anniversaries|wedding|weddings|honeymoon|married|marriage|divorce|divorced|widow|widowed|funeral|memorial service|engagement party|baby shower|christening|bar mitzvah|pregnant|pregnancy|miscarriage|miscarried|newborn|gave birth|stillbirth|infertility|ivf|menopause|diagnosis|diagnosed|prognosis|tested positive|cancer|carcinoma|leukemia|tumor|tumour|biopsy|chemotherapy|chemo|radiation therapy|remission|hospice|palliative|terminal illness|surgery|surgeon|hospital|hospitalized|hospitalised|icu|emergency room|ambulance|stroke|heart attack|cardiac|arrhythmia|afib|seizure|epilepsy|asthma|allergy|allergic|diabetes|diabetic|insulin|cholesterol|blood pressure|hypertension|dementia|alzheimer|alzheimers|parkinson|parkinsons|hiv|aids diagnosis|medication|medications|prescription|prescribed|dosage|symptom|symptoms|illness|disease|disorder|syndrome|therapy|therapist|psychiatrist|psychologist|antidepressant|depression|anxiety|bipolar|schizophrenia|adhd|autism|ptsd|overdose|relapse|sobriety|rehab|addiction|alcoholism|concussion|transplant|dialysis|immunocompromised|mental health|covid|covid-19|coronavirus|flu|influenza|pneumonia|bronchitis|infection|infected|fever|migraine|ulcer|hernia|appendicitis|sepsis|sick leave|urgent care|broken leg|broken arm|broken wrist|broken hip|broken rib|broken ribs|sprained|dislocated|torn ligament|torn acl|whiplash|burn unit|chronic pain|disability|wheelchair|hearing aid|blind in|vaccinated|vaccination|immunization|blood test|scan results|mri|ct scan|x-ray|deaf|deafness|blind|blindness|mute|nonverbal|paralyzed|paralysed|paraplegic|quadriplegic|amputee|amputation|prosthetic|multiple sclerosis|lupus|crohn|celiac|fibromyalgia|cystic fibrosis|sickle cell|cerebral palsy|down syndrome|muscular dystrophy|chronic fatigue|chronic illness|terminally ill|life support|coma|special needs|caregiver|nursing home|assisted living|treatment|treatments|in treatment|medical leave|oncologist|cardiologist|neurologist|pediatrician|pediatric|als|copd|epilepsy|seizures|hospitalization|infirm|frail|bedridden|home health|physical therapy|occupational therapy|speech therapy)\b/i;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Bodily-harm and affliction SHAPES rather than nouns: "broke her leg", "tore
|
|
101
|
+
* his ACL", "caught something at the office". A vocabulary of conditions can
|
|
102
|
+
* never be complete, so this matches the sentence form instead — a verb of
|
|
103
|
+
* injury or contagion pointed at a person's POSSESSION. Articles are excluded
|
|
104
|
+
* on purpose: "the build broke the deploy pipeline" is not an injury.
|
|
105
|
+
*/
|
|
106
|
+
const AFFLICTION_SHAPE_PATTERN =
|
|
107
|
+
/\b(?:broke|broken|fractured|sprained|dislocated|tore|torn|bruised|burned|scalded|injured|hurt)\s+(?:his|her|their|my|your|our)\s+\w+|\b(?:caught|contracted|came down with|is recovering from|was rushed to|passed away|died of|died from)\b/i;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Words that are personal only when someone possesses them: "child process",
|
|
111
|
+
* "parent component", "sibling node", and "customer family" are everyday
|
|
112
|
+
* technical speech, while "his child" and "Rachel's parents" are not.
|
|
113
|
+
*/
|
|
114
|
+
const POSSESSED_KINSHIP_PATTERN =
|
|
115
|
+
/(?:\b(?:my|your|his|her|their|our)|'s)\s+(?:child|children|kid|kids|parent|parents|partner|family|sibling|siblings|in-law|in-laws|relatives)\b/i;
|
|
116
|
+
|
|
117
|
+
/** Tags that mark a fact as belonging to the same high-impact classes. */
|
|
118
|
+
const HIGH_IMPACT_TAGS: Record<string, true> = {
|
|
119
|
+
disability: true,
|
|
120
|
+
family: true,
|
|
121
|
+
health: true,
|
|
122
|
+
medical: true,
|
|
123
|
+
medication: true,
|
|
124
|
+
diagnosis: true,
|
|
125
|
+
birthday: true,
|
|
126
|
+
anniversary: true,
|
|
127
|
+
marriage: true,
|
|
128
|
+
pregnancy: true,
|
|
129
|
+
"mental-health": true,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* True when this claim is personal in a way whose fabrication causes real
|
|
134
|
+
* harm — a family relationship, a personal milestone, or a medical detail.
|
|
135
|
+
*
|
|
136
|
+
* Read by every ambient guard: the post-extraction confidence clamp below, the
|
|
137
|
+
* wearable trust-band cap, and the meeting trust-band cap. A fact this flags
|
|
138
|
+
* can never auto-approve, on any path, however the boosts add up.
|
|
139
|
+
*
|
|
140
|
+
* Takes the shape rather than `ExtractedFact` so meeting candidates — which
|
|
141
|
+
* carry no tags — pass without a synthetic empty array.
|
|
142
|
+
*
|
|
143
|
+
* `personRefs` comes from `collectPersonEntityRefs` over the same extraction:
|
|
144
|
+
* pass it whenever the entity list is available, so personhood is read from
|
|
145
|
+
* the extractor's own `type: "person"` metadata instead of inferred from how
|
|
146
|
+
* it happened to spell the ref.
|
|
147
|
+
*/
|
|
148
|
+
export function isHighImpactPersonalFact(
|
|
149
|
+
fact: {
|
|
150
|
+
category: string;
|
|
151
|
+
content: string;
|
|
152
|
+
tags?: readonly string[];
|
|
153
|
+
entityRef?: string;
|
|
154
|
+
},
|
|
155
|
+
personRefs?: ReadonlySet<string>,
|
|
156
|
+
): boolean {
|
|
157
|
+
// The strongest non-lexical signal: a claim ABOUT A PERSON is high-impact
|
|
158
|
+
// whatever condition, relationship, or milestone the sentence names. This is
|
|
159
|
+
// what covers wording no list anticipates ("Dana is deaf").
|
|
160
|
+
if (isPersonRef(fact.entityRef, personRefs)) return true;
|
|
161
|
+
if (HIGH_IMPACT_CATEGORIES[fact.category] === true) return true;
|
|
162
|
+
if (HIGH_IMPACT_TOPIC_PATTERN.test(fact.content)) return true;
|
|
163
|
+
if (AFFLICTION_SHAPE_PATTERN.test(fact.content)) return true;
|
|
164
|
+
if (POSSESSED_KINSHIP_PATTERN.test(fact.content)) return true;
|
|
165
|
+
for (const tag of fact.tags ?? []) {
|
|
166
|
+
if (HIGH_IMPACT_TAGS[tag.trim().toLowerCase()] === true) return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Normalize an entity reference for comparison: casefold, strip a leading
|
|
173
|
+
* type prefix, and collapse separators. `Person-Jane_Doe`, `person:jane-doe`,
|
|
174
|
+
* and `jane doe` all reduce to `jane-doe`.
|
|
175
|
+
*/
|
|
176
|
+
function normalizeEntityRef(ref: string): string {
|
|
177
|
+
return ref
|
|
178
|
+
.trim()
|
|
179
|
+
.toLowerCase()
|
|
180
|
+
.replace(/^(?:person|people)[-:_\s]+/, "")
|
|
181
|
+
.replace(/[\s_]+/g, "-");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Names of every `type: "person"` entity in an extraction, normalized for
|
|
186
|
+
* lookup. Personhood is the extractor's own metadata, not a guess from the
|
|
187
|
+
* shape of `entityRef` — the prompt asks for `person-jane-doe` but the field is
|
|
188
|
+
* optional and models routinely emit a bare `jane-doe`.
|
|
189
|
+
*/
|
|
190
|
+
export function collectPersonEntityRefs(result: {
|
|
191
|
+
entities: ReadonlyArray<{ name: string; type: string }>;
|
|
192
|
+
}): ReadonlySet<string> {
|
|
193
|
+
const refs = new Set<string>();
|
|
194
|
+
for (const entity of result.entities) {
|
|
195
|
+
if (entity.type !== "person") continue;
|
|
196
|
+
const normalized = normalizeEntityRef(entity.name);
|
|
197
|
+
if (normalized.length > 0) refs.add(normalized);
|
|
198
|
+
}
|
|
199
|
+
return refs;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** True when this ref names a person, by metadata or by explicit prefix. */
|
|
203
|
+
function isPersonRef(ref: string | undefined, personRefs?: ReadonlySet<string>): boolean {
|
|
204
|
+
if (ref === undefined || ref.trim().length === 0) return false;
|
|
205
|
+
if (/^(?:person|people)[-:_]/i.test(ref.trim())) return true;
|
|
206
|
+
return personRefs !== undefined && personRefs.has(normalizeEntityRef(ref));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Deterministic backstop applied after extraction on ambient-capture input:
|
|
211
|
+
* hold high-impact personal facts at or below the speculative ceiling so they
|
|
212
|
+
* cannot auto-promote to active recall on a model's unsupported confidence.
|
|
213
|
+
*
|
|
214
|
+
* Returns the input unchanged when nothing needs clamping, so the non-ambient
|
|
215
|
+
* path pays one predicate per fact and allocates nothing.
|
|
216
|
+
*/
|
|
217
|
+
export function clampAmbientCaptureConfidence(
|
|
218
|
+
result: ExtractionResult,
|
|
219
|
+
): ExtractionResult {
|
|
220
|
+
let clampedAny = false;
|
|
221
|
+
const personRefs = collectPersonEntityRefs(result);
|
|
222
|
+
const facts = result.facts.map((fact) => {
|
|
223
|
+
if (fact.confidence <= SPECULATIVE_CONFIDENCE_CEILING) return fact;
|
|
224
|
+
if (!isHighImpactPersonalFact(fact, personRefs)) return fact;
|
|
225
|
+
clampedAny = true;
|
|
226
|
+
return { ...fact, confidence: SPECULATIVE_CONFIDENCE_CEILING };
|
|
227
|
+
});
|
|
228
|
+
return clampedAny ? { ...result, facts } : result;
|
|
229
|
+
}
|
|
@@ -9,6 +9,18 @@ export interface BufferTurnOwner {
|
|
|
9
9
|
sessionOwnerPrincipal?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export interface AmbientCaptureProvenance {
|
|
13
|
+
/**
|
|
14
|
+
* True when this turn's content came from an always-on ambient capture
|
|
15
|
+
* device (a wearable recorder, a room microphone) and may therefore carry
|
|
16
|
+
* speech the user never authored: TV, podcasts, music, overheard
|
|
17
|
+
* conversation (issue #2294). Set by the ingesting subsystem, never from
|
|
18
|
+
* tool arguments — extraction reads it to warn the model about media audio
|
|
19
|
+
* and to clamp high-impact personal facts to the speculative tier.
|
|
20
|
+
*/
|
|
21
|
+
ambientCapture?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
export function describeError(err: unknown): string {
|
|
13
25
|
if (err instanceof Error) return err.message;
|
|
14
26
|
if (err === null) return "null";
|
|
@@ -46,6 +58,9 @@ export function copyBufferTurn(turn: BufferTurn): BufferTurn {
|
|
|
46
58
|
if (typeof turn.sourceConnector === "string") {
|
|
47
59
|
copy.sourceConnector = turn.sourceConnector;
|
|
48
60
|
}
|
|
61
|
+
if (typeof turn.ambientCapture === "boolean") {
|
|
62
|
+
copy.ambientCapture = turn.ambientCapture;
|
|
63
|
+
}
|
|
49
64
|
return copy;
|
|
50
65
|
}
|
|
51
66
|
|
|
@@ -61,7 +76,8 @@ export function bufferTurnsEqual(left: BufferTurn | undefined, right: BufferTurn
|
|
|
61
76
|
left.providerThreadId === right.providerThreadId &&
|
|
62
77
|
left.turnFingerprint === right.turnFingerprint &&
|
|
63
78
|
left.persistProcessedFingerprint === right.persistProcessedFingerprint &&
|
|
64
|
-
left.sourceConnector === right.sourceConnector
|
|
79
|
+
left.sourceConnector === right.sourceConnector &&
|
|
80
|
+
left.ambientCapture === right.ambientCapture
|
|
65
81
|
);
|
|
66
82
|
}
|
|
67
83
|
|
|
@@ -154,7 +154,11 @@ test("memory prompt section registration must be on the plugin api object", asyn
|
|
|
154
154
|
|
|
155
155
|
const hookCheck = checkById(checks, "hook-registration-core");
|
|
156
156
|
assert.equal(hookCheck.level, "error");
|
|
157
|
-
assert.match(hookCheck.message, /before_prompt_build
|
|
157
|
+
assert.match(hookCheck.message, /before_prompt_build/);
|
|
158
|
+
assert.doesNotMatch(
|
|
159
|
+
`${hookCheck.message} ${hookCheck.remediation ?? ""}`,
|
|
160
|
+
/before_agent_start/,
|
|
161
|
+
);
|
|
158
162
|
});
|
|
159
163
|
|
|
160
164
|
test("memory prompt section registration on api satisfies recall hook requirement", async () => {
|
|
@@ -173,6 +177,46 @@ test("memory prompt section registration on api satisfies recall hook requiremen
|
|
|
173
177
|
assert.equal(hookCheck.level, "ok");
|
|
174
178
|
});
|
|
175
179
|
|
|
180
|
+
test("memory prompt section remediation requests only missing required hooks", async () => {
|
|
181
|
+
const checks = await runFixture({
|
|
182
|
+
enginesNode: ">=22.12.0",
|
|
183
|
+
currentNodeVersion: "v22.12.0",
|
|
184
|
+
indexSource: [
|
|
185
|
+
"api.registerService({ start: async () => {} });",
|
|
186
|
+
"api.registerMemoryPromptSection(() => null);",
|
|
187
|
+
"registerCli(api, orchestrator);",
|
|
188
|
+
].join("\n"),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const hookCheck = checkById(checks, "hook-registration-core");
|
|
192
|
+
assert.equal(hookCheck.level, "error");
|
|
193
|
+
assert.match(`${hookCheck.message} ${hookCheck.remediation ?? ""}`, /agent_end/);
|
|
194
|
+
assert.doesNotMatch(
|
|
195
|
+
`${hookCheck.message} ${hookCheck.remediation ?? ""}`,
|
|
196
|
+
/before_prompt_build|before_agent_start|gateway_start/,
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("memory prompt section remediation requests startup only when absent", async () => {
|
|
201
|
+
const checks = await runFixture({
|
|
202
|
+
enginesNode: ">=22.12.0",
|
|
203
|
+
currentNodeVersion: "v22.12.0",
|
|
204
|
+
indexSource: [
|
|
205
|
+
"api.on('agent_end', async () => {});",
|
|
206
|
+
"api.registerMemoryPromptSection(() => null);",
|
|
207
|
+
"registerCli(api, orchestrator);",
|
|
208
|
+
].join("\n"),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const hookCheck = checkById(checks, "hook-registration-core");
|
|
212
|
+
assert.equal(hookCheck.level, "error");
|
|
213
|
+
assert.match(`${hookCheck.message} ${hookCheck.remediation ?? ""}`, /startup wiring/);
|
|
214
|
+
assert.doesNotMatch(
|
|
215
|
+
`${hookCheck.message} ${hookCheck.remediation ?? ""}`,
|
|
216
|
+
/before_prompt_build|before_agent_start/,
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
176
220
|
test("service start detection ignores nested start properties", async () => {
|
|
177
221
|
const checks = await runFixture({
|
|
178
222
|
enginesNode: ">=22.12.0",
|
package/src/compat/checks.ts
CHANGED
|
@@ -4,8 +4,7 @@ import type { CompatCheckOptions, CompatCheckResult, CompatReport, CompatRunner
|
|
|
4
4
|
import { compareVersions } from "../version-utils.js";
|
|
5
5
|
import { launchProcess } from "../runtime/child-process.js";
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const REQUIRED_HOOKS_NEW = ["before_prompt_build", "agent_end"];
|
|
7
|
+
const REQUIRED_HOOKS = ["before_prompt_build", "agent_end"];
|
|
9
8
|
const OPENCLAW_REMNIC_PLUGIN_ID = "openclaw-remnic";
|
|
10
9
|
const OPENCLAW_REMNIC_LEGACY_PLUGIN_ID = "openclaw-engram";
|
|
11
10
|
|
|
@@ -698,22 +697,16 @@ export async function runCompatChecks(options: CompatCheckOptions): Promise<Comp
|
|
|
698
697
|
const indexRaw = await readFile(indexPath, "utf-8");
|
|
699
698
|
const structuralSource = stripCommentsAndStrings(indexRaw);
|
|
700
699
|
const hooks = parseHookRegistrations(indexRaw);
|
|
701
|
-
const
|
|
702
|
-
const missingNew = REQUIRED_HOOKS_NEW.filter((hook) => !hooks.has(hook));
|
|
700
|
+
const missingHooks = REQUIRED_HOOKS.filter((hook) => !hooks.has(hook));
|
|
703
701
|
// registerMemoryPromptSection is a valid alternative to the recall hook only
|
|
704
702
|
// when it is registered on the OpenClaw plugin API object.
|
|
705
703
|
const hasMemoryPromptSection = hasMemoryPromptSectionRegistration(structuralSource);
|
|
706
|
-
const
|
|
707
|
-
?
|
|
708
|
-
:
|
|
709
|
-
const missingNewAdj = hasMemoryPromptSection
|
|
710
|
-
? missingNew.filter((h) => h !== "before_prompt_build")
|
|
711
|
-
: missingNew;
|
|
712
|
-
// Accept whichever hook set has fewer missing entries
|
|
713
|
-
const missingHooks = missingNewAdj.length <= missingLegacyAdj.length ? missingNewAdj : missingLegacyAdj;
|
|
704
|
+
const missingRequiredHooks = hasMemoryPromptSection
|
|
705
|
+
? missingHooks.filter((hook) => hook !== "before_prompt_build")
|
|
706
|
+
: missingHooks;
|
|
714
707
|
const hasGatewayStartHook = hooks.has("gateway_start");
|
|
715
708
|
const hasServiceStart = hasServiceStartRegistration(structuralSource);
|
|
716
|
-
if (
|
|
709
|
+
if (missingRequiredHooks.length === 0 && (hasGatewayStartHook || hasServiceStart)) {
|
|
717
710
|
checks.push({
|
|
718
711
|
id: "hook-registration-core",
|
|
719
712
|
title: "Core hook registration",
|
|
@@ -722,18 +715,25 @@ export async function runCompatChecks(options: CompatCheckOptions): Promise<Comp
|
|
|
722
715
|
});
|
|
723
716
|
} else {
|
|
724
717
|
const missingParts: string[] = [];
|
|
725
|
-
if (
|
|
726
|
-
missingParts.push(`hooks: ${
|
|
718
|
+
if (missingRequiredHooks.length > 0) {
|
|
719
|
+
missingParts.push(`hooks: ${missingRequiredHooks.join(", ")}`);
|
|
727
720
|
}
|
|
728
721
|
if (!hasGatewayStartHook && !hasServiceStart) {
|
|
729
722
|
missingParts.push("startup wiring: gateway_start hook or api.registerService({ start })");
|
|
730
723
|
}
|
|
724
|
+
const remediationParts: string[] = [];
|
|
725
|
+
if (missingRequiredHooks.length > 0) {
|
|
726
|
+
remediationParts.push(`registers ${missingRequiredHooks.join(", ")}`);
|
|
727
|
+
}
|
|
728
|
+
if (!hasGatewayStartHook && !hasServiceStart) {
|
|
729
|
+
remediationParts.push("registers gateway_start or api.registerService({ start })");
|
|
730
|
+
}
|
|
731
731
|
checks.push({
|
|
732
732
|
id: "hook-registration-core",
|
|
733
733
|
title: "Core hook registration",
|
|
734
734
|
level: "error",
|
|
735
735
|
message: `Missing expected registration(s): ${missingParts.join("; ")}`,
|
|
736
|
-
remediation:
|
|
736
|
+
remediation: `Ensure src/index.ts ${remediationParts.join(" and ")}.`,
|
|
737
737
|
});
|
|
738
738
|
}
|
|
739
739
|
|
package/src/external-wiki-cli.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ExternalWikiRoot, searchExternalWikis } from "./external-wiki-search.js";
|
|
1
|
+
import { type ExternalWikiRoot, type ExternalWikiSearchResult, searchExternalWikis } from "./external-wiki-search.js";
|
|
2
2
|
|
|
3
3
|
interface ExternalWikiCliIo {
|
|
4
4
|
readonly stdout: { write(chunk: string): unknown };
|
|
@@ -13,28 +13,25 @@ export async function runExternalWikiCliCommand(
|
|
|
13
13
|
args: readonly string[],
|
|
14
14
|
io: ExternalWikiCliIo
|
|
15
15
|
): Promise<number> {
|
|
16
|
-
if (args
|
|
16
|
+
if (args[0] === "help" || args[0] === "--help" || args[0] === "-h") {
|
|
17
17
|
io.stdout.write(USAGE);
|
|
18
18
|
return 0;
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
io.stderr.write(`external-wiki: unknown command '${args[0]}'\n${USAGE}`);
|
|
22
|
-
return 2;
|
|
23
|
-
}
|
|
20
|
+
const searchArgs = args[0] === "search" ? args.slice(1) : args;
|
|
24
21
|
|
|
25
22
|
let limit: number | undefined;
|
|
26
23
|
let wikiId: string | undefined;
|
|
27
24
|
let maxCharsPerHit: number | undefined;
|
|
28
25
|
let json = false;
|
|
29
26
|
const queryParts: string[] = [];
|
|
30
|
-
for (let index =
|
|
31
|
-
const argument =
|
|
27
|
+
for (let index = 0; index < searchArgs.length; index += 1) {
|
|
28
|
+
const argument = searchArgs[index];
|
|
32
29
|
if (argument === "--json") {
|
|
33
30
|
json = true;
|
|
34
31
|
continue;
|
|
35
32
|
}
|
|
36
33
|
if (argument === "--limit" || argument === "--wiki-id" || argument === "--max-chars-per-hit") {
|
|
37
|
-
const value =
|
|
34
|
+
const value = searchArgs[index + 1];
|
|
38
35
|
if (value === undefined || value.startsWith("--")) {
|
|
39
36
|
io.stderr.write(`external-wiki: ${argument} requires a value\n`);
|
|
40
37
|
return 2;
|
|
@@ -68,25 +65,36 @@ export async function runExternalWikiCliCommand(
|
|
|
68
65
|
|
|
69
66
|
const query = queryParts.join(" ").trim();
|
|
70
67
|
if (query.length === 0) {
|
|
71
|
-
io.stderr.write(
|
|
72
|
-
return
|
|
68
|
+
io.stderr.write("external-wiki: provide a search query\n");
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
const enabled = roots.filter((root) => root.enabled !== false);
|
|
72
|
+
if (enabled.length === 0) {
|
|
73
|
+
io.stderr.write("external-wiki: no enabled wiki roots configured\n");
|
|
74
|
+
return 1;
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
let result: ExternalWikiSearchResult;
|
|
78
|
+
try {
|
|
79
|
+
result = await searchExternalWikis(enabled, { query, limit, wikiId, maxCharsPerHit });
|
|
80
|
+
} catch (error) {
|
|
81
|
+
io.stderr.write(`external-wiki: ${error instanceof Error ? error.message : "search failed"}\n`);
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
76
84
|
if (json) {
|
|
77
85
|
io.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
78
86
|
return 0;
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
io.stdout.write(`External wiki results for "${result.query}" (${result.count})\n`);
|
|
82
|
-
for (const hit of result.hits) {
|
|
83
|
-
const citation = hit.citations[0];
|
|
84
|
-
const location = citation ? `${citation.path}:${citation.lineStart}-${citation.lineEnd}` : hit.path;
|
|
85
|
-
io.stdout.write(`${hit.rank}. [${hit.wikiId}] ${hit.title} (${location})\n`);
|
|
86
|
-
io.stdout.write(` ${hit.snippet.replace(/\s+/g, " ").trim()}\n`);
|
|
87
|
-
}
|
|
88
89
|
if (result.degradedWikiIds.length > 0) {
|
|
89
|
-
io.stderr.write(`
|
|
90
|
+
io.stderr.write(`Warning: degraded roots: ${result.degradedWikiIds.join(", ")}\n`);
|
|
91
|
+
}
|
|
92
|
+
if (result.hits.length === 0) {
|
|
93
|
+
io.stdout.write("No results.\n");
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
for (const hit of result.hits) {
|
|
97
|
+
io.stdout.write(`[${hit.wikiId}] ${hit.path} (score ${hit.score})\n${hit.snippet}\n\n`);
|
|
90
98
|
}
|
|
91
99
|
return 0;
|
|
92
100
|
}
|
|
@@ -257,7 +257,7 @@ test("external wiki search fails open to filesystem candidates when a dedicated
|
|
|
257
257
|
}
|
|
258
258
|
});
|
|
259
259
|
|
|
260
|
-
test("external wiki CLI
|
|
260
|
+
test("external wiki CLI preserves text contracts and supports cited JSON", async () => {
|
|
261
261
|
const root = await createWiki("cli", {
|
|
262
262
|
"INDEX.md": "- [[wiki/agents|Agent Planning]] - planner fan-out with citations\n",
|
|
263
263
|
"wiki/agents.md": "# Agent Planning\n\nPlanner fan-out uses cited retrieval evidence.\n",
|
|
@@ -286,7 +286,7 @@ test("external wiki CLI searches multi-word queries and renders cited JSON or te
|
|
|
286
286
|
stderr: { write: () => undefined },
|
|
287
287
|
});
|
|
288
288
|
assert.equal(textCode, 0);
|
|
289
|
-
assert.match(textOutput.join(""), /\[cli\]
|
|
289
|
+
assert.match(textOutput.join(""), /\[cli\] wiki\/agents\.md \(score [\d.]+\)/);
|
|
290
290
|
|
|
291
291
|
const invalidErrors: string[] = [];
|
|
292
292
|
const invalidCode = await runExternalWikiCliCommand([root], ["search", "--limit", "0", "query"], {
|
|
@@ -295,6 +295,72 @@ test("external wiki CLI searches multi-word queries and renders cited JSON or te
|
|
|
295
295
|
});
|
|
296
296
|
assert.equal(invalidCode, 2);
|
|
297
297
|
assert.match(invalidErrors.join(""), /--limit must be an integer from 1 to 20/);
|
|
298
|
+
|
|
299
|
+
const noQueryErrors: string[] = [];
|
|
300
|
+
const noQueryCode = await runExternalWikiCliCommand([root], ["search"], {
|
|
301
|
+
stdout: { write: () => undefined },
|
|
302
|
+
stderr: { write: (chunk) => noQueryErrors.push(chunk) },
|
|
303
|
+
});
|
|
304
|
+
assert.equal(noQueryCode, 1);
|
|
305
|
+
assert.equal(noQueryErrors.join(""), "external-wiki: provide a search query\n");
|
|
306
|
+
|
|
307
|
+
const disabledErrors: string[] = [];
|
|
308
|
+
const disabledCode = await runExternalWikiCliCommand([{ ...root, enabled: false }], ["search", "planner"], {
|
|
309
|
+
stdout: { write: () => undefined },
|
|
310
|
+
stderr: { write: (chunk) => disabledErrors.push(chunk) },
|
|
311
|
+
});
|
|
312
|
+
assert.equal(disabledCode, 1);
|
|
313
|
+
assert.equal(disabledErrors.join(""), "external-wiki: no enabled wiki roots configured\n");
|
|
314
|
+
|
|
315
|
+
const emptyOutput: string[] = [];
|
|
316
|
+
const emptyCode = await runExternalWikiCliCommand([root], ["search", "unmatched-token"], {
|
|
317
|
+
stdout: { write: (chunk) => emptyOutput.push(chunk) },
|
|
318
|
+
stderr: { write: () => undefined },
|
|
319
|
+
});
|
|
320
|
+
assert.equal(emptyCode, 0);
|
|
321
|
+
assert.equal(emptyOutput.join(""), "No results.\n");
|
|
322
|
+
|
|
323
|
+
const searchErrors: string[] = [];
|
|
324
|
+
const searchErrorCode = await runExternalWikiCliCommand([root], ["search", "planner", "--wiki-id", "missing"], {
|
|
325
|
+
stdout: { write: () => undefined },
|
|
326
|
+
stderr: { write: (chunk) => searchErrors.push(chunk) },
|
|
327
|
+
});
|
|
328
|
+
assert.equal(searchErrorCode, 1);
|
|
329
|
+
assert.equal(searchErrors.join(""), "external-wiki: unknown external wiki: missing\n");
|
|
330
|
+
|
|
331
|
+
const legacyOutput: string[] = [];
|
|
332
|
+
const legacyCode = await runExternalWikiCliCommand([root], ["planner", "fan-out"], {
|
|
333
|
+
stdout: { write: (chunk) => legacyOutput.push(chunk) },
|
|
334
|
+
stderr: { write: () => undefined },
|
|
335
|
+
});
|
|
336
|
+
assert.equal(legacyCode, 0);
|
|
337
|
+
assert.match(legacyOutput.join(""), /^\[cli\] wiki\/agents\.md \(score [\d.]+\)/);
|
|
338
|
+
|
|
339
|
+
const degradedErrors: string[] = [];
|
|
340
|
+
const degradedCode = await runExternalWikiCliCommand(
|
|
341
|
+
[root, { ...root, id: "broken", rootDir: path.join(root.rootDir, "missing") }],
|
|
342
|
+
["search", "planner"],
|
|
343
|
+
{
|
|
344
|
+
stdout: { write: () => undefined },
|
|
345
|
+
stderr: { write: (chunk) => degradedErrors.push(chunk) },
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
assert.equal(degradedCode, 0);
|
|
349
|
+
assert.equal(degradedErrors.join(""), "Warning: degraded roots: broken\n");
|
|
350
|
+
|
|
351
|
+
const allDegradedOutput: string[] = [];
|
|
352
|
+
const allDegradedErrors: string[] = [];
|
|
353
|
+
const allDegradedCode = await runExternalWikiCliCommand(
|
|
354
|
+
[{ ...root, id: "broken", rootDir: path.join(root.rootDir, "missing") }],
|
|
355
|
+
["search", "planner"],
|
|
356
|
+
{
|
|
357
|
+
stdout: { write: (chunk) => allDegradedOutput.push(chunk) },
|
|
358
|
+
stderr: { write: (chunk) => allDegradedErrors.push(chunk) },
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
assert.equal(allDegradedCode, 0);
|
|
362
|
+
assert.equal(allDegradedOutput.join(""), "No results.\n");
|
|
363
|
+
assert.equal(allDegradedErrors.join(""), "Warning: degraded roots: broken\n");
|
|
298
364
|
} finally {
|
|
299
365
|
await removeWikis([root]);
|
|
300
366
|
}
|
package/src/external-wiki.ts
CHANGED
|
@@ -170,9 +170,18 @@ function normalizeCatalogPath(target: string, config: ExternalWikiRoot, base: Ca
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
const pagesDir = path.posix.normalize(config.pagesDir);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
let relative: string;
|
|
174
|
+
if (base === "index") {
|
|
175
|
+
const baseDir = path.posix.dirname(config.indexFile);
|
|
176
|
+
const rootRelativeTarget = path.posix.normalize(path.posix.join(baseDir, withoutFragment));
|
|
177
|
+
relative = path.posix.relative(pagesDir, rootRelativeTarget);
|
|
178
|
+
} else {
|
|
179
|
+
const pagesPrefix = pagesDir === "." ? "" : `${pagesDir}/`;
|
|
180
|
+
const stripped = pagesPrefix.length > 0 && withoutFragment.startsWith(pagesPrefix)
|
|
181
|
+
? withoutFragment.slice(pagesPrefix.length)
|
|
182
|
+
: withoutFragment;
|
|
183
|
+
relative = path.posix.normalize(stripped);
|
|
184
|
+
}
|
|
176
185
|
if (
|
|
177
186
|
relative === ".." ||
|
|
178
187
|
relative.startsWith("../") ||
|
|
@@ -416,38 +425,3 @@ export async function readExternalWikiPage(
|
|
|
416
425
|
bytes: page.bytes,
|
|
417
426
|
};
|
|
418
427
|
}
|
|
419
|
-
|
|
420
|
-
export async function runExternalWikiCliCommand(
|
|
421
|
-
roots: readonly ExternalWikiRoot[],
|
|
422
|
-
rest: readonly string[],
|
|
423
|
-
io: { stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream },
|
|
424
|
-
): Promise<number> {
|
|
425
|
-
const { searchExternalWikis } = await import("./external-wiki-search.js");
|
|
426
|
-
const query = rest.filter((a) => !a.startsWith("-")).join(" ").trim();
|
|
427
|
-
if (!query) {
|
|
428
|
-
io.stderr.write("external-wiki: provide a search query\n");
|
|
429
|
-
return 1;
|
|
430
|
-
}
|
|
431
|
-
const enabled = roots.filter((r) => r.enabled !== false);
|
|
432
|
-
if (enabled.length === 0) {
|
|
433
|
-
io.stderr.write("external-wiki: no enabled wiki roots configured\n");
|
|
434
|
-
return 1;
|
|
435
|
-
}
|
|
436
|
-
try {
|
|
437
|
-
const result = await searchExternalWikis(enabled, { query });
|
|
438
|
-
if (result.hits.length === 0) {
|
|
439
|
-
io.stdout.write("No results.\n");
|
|
440
|
-
return 0;
|
|
441
|
-
}
|
|
442
|
-
for (const hit of result.hits) {
|
|
443
|
-
io.stdout.write(`[${hit.wikiId}] ${hit.path} (score ${hit.score})\n${hit.snippet}\n\n`);
|
|
444
|
-
}
|
|
445
|
-
if (result.degradedWikiIds.length > 0) {
|
|
446
|
-
io.stderr.write(`Warning: degraded roots: ${result.degradedWikiIds.join(", ")}\n`);
|
|
447
|
-
}
|
|
448
|
-
return 0;
|
|
449
|
-
} catch (error) {
|
|
450
|
-
io.stderr.write(`external-wiki: ${error instanceof Error ? error.message : "search failed"}\n`);
|
|
451
|
-
return 1;
|
|
452
|
-
}
|
|
453
|
-
}
|