@remnic/core 9.54.3 → 9.54.5
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 +12 -11
- package/dist/access-admin-ops-surface.js +46 -44
- package/dist/access-authorization-probe.d.ts +12 -11
- package/dist/access-authorization-probe.js +47 -45
- package/dist/access-boundary.d.ts +12 -11
- package/dist/access-boundary.js +46 -44
- package/dist/access-cli.js +92 -90
- 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 +12 -11
- package/dist/access-extraction-force-flush.js +46 -44
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +12 -11
- package/dist/access-http-lifecycle-flush.d.ts +12 -11
- package/dist/access-http-offline-stream.d.ts +12 -11
- package/dist/access-http-query.js +46 -44
- package/dist/access-http.d.ts +12 -11
- package/dist/access-http.js +56 -54
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +46 -44
- package/dist/access-lcm-surface.d.ts +12 -11
- package/dist/access-lcm-surface.js +46 -44
- package/dist/access-mcp.d.ts +12 -11
- package/dist/access-mcp.js +52 -50
- package/dist/access-memory-search-fanout.d.ts +22 -2
- package/dist/access-memory-search-fanout.js +1 -1
- package/dist/{access-namespace-preflight-C7n4tgWC.d.ts → access-namespace-preflight-BdHlGd_T.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +46 -44
- package/dist/access-observe-write-surface.d.ts +12 -11
- package/dist/access-observe-write-surface.js +46 -44
- package/dist/access-offline-manifest.d.ts +10 -9
- package/dist/access-offline-manifest.js +12 -10
- package/dist/access-operations-batch.js +48 -46
- package/dist/access-operations.d.ts +15 -14
- package/dist/access-operations.js +51 -49
- package/dist/access-recall-concurrency.d.ts +12 -11
- package/dist/access-recall-concurrency.js +46 -44
- package/dist/access-recall-response.d.ts +12 -11
- package/dist/access-recall-response.js +46 -44
- package/dist/access-recall-surface.d.ts +12 -11
- package/dist/access-recall-surface.js +46 -44
- package/dist/access-schema.d.ts +90 -90
- package/dist/access-schema.js +3 -2
- package/dist/{access-service-CXvbxLK-.d.ts → access-service-DL7X9hrg.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +12 -11
- package/dist/access-service.d.ts +12 -11
- package/dist/access-service.js +46 -44
- package/dist/access-surface-catalog.d.ts +12 -11
- 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 +13 -1
- package/dist/active-memory-bridge.js +6 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +3 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +30 -9
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +3 -3
- 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-BOOxl-I2.d.ts → catalog-B90is4d8.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 +31 -10
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-6LAPLZOS.js → chunk-2DD6ZHOM.js} +8 -3
- package/dist/chunk-2DD6ZHOM.js.map +1 -0
- package/dist/{chunk-47P3RYZP.js → chunk-2INWPC6E.js} +2 -2
- package/dist/{chunk-3CSX3ETE.js → chunk-2LWQSV6A.js} +23 -11
- package/dist/chunk-2LWQSV6A.js.map +1 -0
- package/dist/{chunk-7BZOBA5J.js → chunk-2PNXOQ4P.js} +69 -11
- package/dist/chunk-2PNXOQ4P.js.map +1 -0
- package/dist/{chunk-WOPA3SGI.js → chunk-3OTSVCPK.js} +8 -8
- package/dist/{chunk-PX2LSHYM.js → chunk-3SXLD5T3.js} +3 -3
- package/dist/{chunk-T2JHZWH6.js → chunk-3WGI7F4V.js} +237 -93
- package/dist/chunk-3WGI7F4V.js.map +1 -0
- package/dist/{chunk-C4ZNGVK3.js → chunk-5JKUSBMX.js} +2 -2
- package/dist/{chunk-S75M5ZRK.js → chunk-6LEIWWAF.js} +16 -1
- package/dist/chunk-6LEIWWAF.js.map +1 -0
- package/dist/{chunk-MAXVH44E.js → chunk-6NNNURLK.js} +4 -4
- package/dist/{chunk-LJS7KUPV.js → chunk-6PZGKGAO.js} +2 -2
- package/dist/{chunk-YN6ASZUG.js → chunk-6QPI2ITJ.js} +5 -5
- package/dist/{chunk-CYF6WVE7.js → chunk-7HG7MGAP.js} +2688 -496
- package/dist/chunk-7HG7MGAP.js.map +1 -0
- package/dist/{chunk-CGVHPCP2.js → chunk-7Q7JWLQ4.js} +2 -2
- package/dist/{chunk-227UFSEQ.js → chunk-7RZJWZ46.js} +5 -5
- package/dist/{chunk-JBPKEARU.js → chunk-AU7Q3LSC.js} +4 -4
- package/dist/{chunk-PDDTI7IC.js → chunk-B4T3FQMT.js} +1 -1
- package/dist/{chunk-UU3G6GUP.js → chunk-BIF4SHMD.js} +1 -1
- package/dist/{chunk-UU3G6GUP.js.map → chunk-BIF4SHMD.js.map} +1 -1
- package/dist/{chunk-APPFNGX6.js → chunk-BSXGTDVJ.js} +2 -2
- package/dist/{chunk-4IIFL7GC.js → chunk-CLKBV3Z3.js} +10 -10
- package/dist/{chunk-ZYLC7NFO.js → chunk-CSY44OKU.js} +4 -4
- package/dist/{chunk-HLYR2TQL.js → chunk-CUTFSLSD.js} +2 -2
- package/dist/{chunk-RDJRFDJY.js → chunk-DBY26JJJ.js} +2 -2
- package/dist/{chunk-QWGE2PUH.js → chunk-DDA55IVB.js} +10 -10
- package/dist/{chunk-VMMQDEN5.js → chunk-DN2IDKI5.js} +2 -2
- package/dist/{chunk-2GFK22OZ.js → chunk-DOOWFDWN.js} +2 -2
- package/dist/{chunk-7FGL6CGA.js → chunk-DVYRD4HY.js} +2 -2
- package/dist/{chunk-YGLY6QFV.js → chunk-F2Y6I74C.js} +2 -2
- package/dist/{chunk-UU7TRFNE.js → chunk-FFEBSFVT.js} +2 -2
- package/dist/chunk-HNDIND7P.js +331 -0
- package/dist/chunk-HNDIND7P.js.map +1 -0
- package/dist/{chunk-NIS256NW.js → chunk-JKW663ZN.js} +2 -2
- package/dist/{chunk-G7A5HLUS.js → chunk-KJH6W2I7.js} +2 -2
- package/dist/{chunk-IXEBUYO4.js → chunk-KM55SMLG.js} +11 -2
- package/dist/{chunk-IXEBUYO4.js.map → chunk-KM55SMLG.js.map} +1 -1
- package/dist/{chunk-G2LUMAUF.js → chunk-L7RTUEDG.js} +12 -5
- package/dist/chunk-L7RTUEDG.js.map +1 -0
- package/dist/chunk-LC2BDILZ.js +57 -0
- package/dist/chunk-LC2BDILZ.js.map +1 -0
- package/dist/{chunk-JV5YUBIZ.js → chunk-LC3FD4BV.js} +52 -46
- package/dist/chunk-LC3FD4BV.js.map +1 -0
- package/dist/{chunk-HWRD23DW.js → chunk-LVKYLMSM.js} +3 -3
- package/dist/{chunk-RUMNY5DT.js → chunk-MRGJZ75M.js} +6 -2
- package/dist/chunk-MRGJZ75M.js.map +1 -0
- package/dist/{chunk-ZMYDYNI4.js → chunk-N7CXQ5TA.js} +4 -4
- package/dist/{chunk-3JLPGJ43.js → chunk-NGT35IGQ.js} +5 -5
- package/dist/{chunk-GUKBW4HL.js → chunk-NQMSEB5D.js} +6 -6
- package/dist/{chunk-WRJVB3C2.js → chunk-PO7NXBG2.js} +2 -2
- package/dist/{chunk-WM65LU67.js → chunk-QOQQMVR2.js} +2 -2
- package/dist/{chunk-QHFV6VNU.js → chunk-QPAQSG3F.js} +2 -2
- package/dist/{chunk-UTRO5UJU.js → chunk-QPW3PAIH.js} +5 -1
- package/dist/chunk-QPW3PAIH.js.map +1 -0
- package/dist/{chunk-6R4G7N4S.js → chunk-QQXWFDRD.js} +128 -74
- package/dist/chunk-QQXWFDRD.js.map +1 -0
- package/dist/{chunk-UES4BHA2.js → chunk-QTO6BMQZ.js} +2 -2
- package/dist/{chunk-THPPCISA.js → chunk-RBII7637.js} +5 -5
- package/dist/{chunk-QT2FRYI4.js → chunk-RG426DJO.js} +2 -2
- package/dist/{chunk-XUAMCLY4.js → chunk-TKFGFDPJ.js} +17 -13
- package/dist/chunk-TKFGFDPJ.js.map +1 -0
- package/dist/{chunk-VKXWK44Q.js → chunk-TY6MKIWP.js} +3 -3
- package/dist/{chunk-AZM4CCAC.js → chunk-UWJNKGBA.js} +5 -5
- package/dist/{chunk-JDZPNCMA.js → chunk-UYOXFFEY.js} +6 -3
- package/dist/chunk-UYOXFFEY.js.map +1 -0
- package/dist/{chunk-ZPJ4QSGP.js → chunk-VTU5IEKY.js} +2 -2
- package/dist/{chunk-RZ4XGKOJ.js → chunk-VYQ4URQB.js} +8 -8
- package/dist/{chunk-WDBX6VER.js → chunk-VZRFGJCH.js} +5 -5
- package/dist/{chunk-IBKR2TFR.js → chunk-WM5LD6AT.js} +2 -2
- package/dist/{chunk-K4QS7LXU.js → chunk-WZWZRZ2V.js} +2 -2
- package/dist/{chunk-G4T7L557.js → chunk-XKAR4RF6.js} +2 -2
- package/dist/{chunk-DZD576ZU.js → chunk-XSV2YDCJ.js} +2 -2
- package/dist/{chunk-G7Z6EENO.js → chunk-YDWPYDML.js} +4 -4
- package/dist/{chunk-6HPHEFXT.js → chunk-YKZLOPTC.js} +2 -2
- package/dist/{chunk-NH5IR2WM.js → chunk-YR65W7BV.js} +6 -2
- package/dist/chunk-YR65W7BV.js.map +1 -0
- package/dist/{chunk-GYPQL6XN.js → chunk-Z4N7SVVF.js} +35 -47
- package/dist/chunk-Z4N7SVVF.js.map +1 -0
- package/dist/{cli-DVUFIa5E.d.ts → cli-jfuZXixq.d.ts} +5 -5
- package/dist/cli.d.ts +14 -13
- package/dist/cli.js +74 -72
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +30 -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 +3 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +30 -9
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +29 -9
- 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 +3 -1
- package/dist/contradiction-scan-KCADHXPE.js +20 -0
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +32 -11
- 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 +21 -20
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-XzF76C9n.d.ts → dependency-propagation-delivery-CTDuAVJc.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 +30 -9
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +6 -4
- package/dist/external-wiki-access.d.ts +12 -11
- package/dist/external-wiki-access.js +47 -45
- 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 +12 -11
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +1 -1
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +29 -13
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-626C5554.js → first-start-migration-LMMZFLKK.js} +13 -5
- package/dist/first-start-migration-LMMZFLKK.js.map +1 -0
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +1439 -716
- package/dist/index.js +207 -121
- 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 +5 -5
- 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 +30 -9
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +30 -9
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +31 -10
- package/dist/{maintenance-BJzNIKBE.d.ts → maintenance-heMZSq7G.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +12 -11
- 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-mutations.js +2 -2
- package/dist/memory-projection-store.d.ts +6 -2
- package/dist/memory-projection-store.js +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.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 +20 -19
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +6 -6
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +30 -9
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-file-io.d.ts +20 -0
- package/dist/offline-sync-file-io.js +18 -0
- package/dist/offline-sync-file-io.js.map +1 -0
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +5 -5
- package/dist/offline-sync.d.ts +8 -6
- package/dist/offline-sync.js +2 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +22 -21
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +34 -32
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +35 -15
- package/dist/{orchestrator-BiXgBJBQ.d.ts → orchestrator-BQQPsSjs.d.ts} +11 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +93 -91
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-sz_dcuLg.d.ts → pipeline-DFfmsnGk.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/{purge-533FZLS4.js → purge-GB7PZHT2.js} +2 -2
- package/dist/{qmd-eAWObK4M.d.ts → qmd-BF5bcVRs.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-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 +2 -1
- package/dist/reconcile/manifest.d.ts +2 -1
- package/dist/reconcile/manifest.js +6 -5
- package/dist/reconcile/plan.d.ts +2 -1
- package/dist/reconcile/plan.js +3 -2
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +4 -3
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/schemas.d.ts +138 -138
- package/dist/{scope-profiles-BBQsXuyj.d.ts → scope-profiles-CbUOsE_E.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +4 -4
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +6 -6
- 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-HD8RkzVA.d.ts → semantic-consolidation-DAB3xdKM.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +31 -10
- package/dist/semantic-rule-promotion.js +30 -9
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +30 -9
- package/dist/{service-DIrtHHEg.d.ts → service-DGKg2O6r.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 +1 -1
- package/dist/source-agent-qualifier.js +28 -12
- package/dist/{storage-DqCzXi56.d.ts → storage-CC1jKcWg.d.ts} +46 -13
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +30 -9
- 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-routing.d.ts +2 -2
- package/dist/tier-routing.js +7 -1
- package/dist/{tier-stats-2JCZYEF7.js → tier-stats-JDN6HRNT.js} +8 -2
- package/dist/{tier-stats-2JCZYEF7.js.map → tier-stats-JDN6HRNT.js.map} +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-d_j4vC6N.d.ts → types-BnQza1y8.d.ts} +2 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +2 -2
- package/dist/verified-recall.js +30 -9
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-http.test.ts +31 -0
- package/src/access-memory-search-fanout.ts +24 -1
- package/src/access-offline-manifest.ts +5 -0
- package/src/access-service-namespace.test.ts +14 -0
- package/src/access-service-observe-scope.test.ts +2 -0
- package/src/access-service-offline-file-content.test.ts +211 -0
- package/src/access-service.ts +20 -43
- package/src/active-memory-bridge.test.ts +151 -0
- package/src/active-memory-bridge.ts +87 -11
- package/src/briefing.test.ts +38 -0
- package/src/briefing.ts +3 -3
- package/src/cli.ts +6 -1
- package/src/connectors/codex-materialize-runner.ts +2 -0
- package/src/contradiction/contradiction-scan.ts +2 -0
- package/src/contradiction/contradiction.test.ts +42 -0
- package/src/correction/correction-access-wiring.ts +8 -2
- package/src/correction/correction-storage-integration.test.ts +7 -0
- package/src/explicit-capture.test.ts +24 -0
- package/src/explicit-capture.ts +2 -0
- package/src/index.ts +3 -0
- package/src/maintenance/first-start-migration.ts +2 -1
- package/src/maintenance/memory-governance.test.ts +25 -0
- package/src/maintenance/memory-governance.ts +4 -1
- package/src/maintenance/pattern-reinforcement.test.ts +20 -0
- package/src/maintenance/pattern-reinforcement.ts +2 -1
- package/src/maintenance/rebuild-memory-projection.ts +8 -8
- package/src/memory-projection-store.ts +25 -27
- package/src/offline-sync-file-io.ts +68 -0
- package/src/offline-sync.ts +43 -57
- package/src/orchestration/consolidation-run.ts +5 -4
- package/src/orchestration/contradiction-linking-coordinator.ts +7 -0
- package/src/orchestration/contradiction-localization.integration.test.ts +115 -0
- package/src/orchestration/lifecycle-policy-coordinator.ts +2 -1
- package/src/orchestration/qmd-result-resolver.test.ts +215 -14
- package/src/orchestration/qmd-result-resolver.ts +142 -82
- package/src/orchestration/recall-search-pipeline.test.ts +41 -0
- package/src/orchestration/recall-search-pipeline.ts +12 -0
- package/src/orchestration/semantic-consolidation-coordinator.ts +2 -1
- package/src/orchestration/tier-migration-coordinator.ts +23 -56
- package/src/orchestration/workspace-ops.test.ts +37 -0
- package/src/orchestration/workspace-ops.ts +5 -29
- package/src/orchestrator.ts +7 -8
- package/src/storage/supersession-audit.ts +7 -1
- package/src/storage/supersession-side-effects.ts +17 -5
- package/src/storage.ts +18 -19
- package/src/support-passport/card-projection.test.ts +129 -0
- package/src/support-passport/card-projection.ts +287 -0
- package/src/support-passport/card-service.test.ts +2462 -0
- package/src/support-passport/card-service.ts +1042 -0
- package/src/support-passport/contracts.test.ts +143 -0
- package/src/support-passport/contracts.ts +136 -0
- package/src/support-passport/errors.ts +24 -0
- package/src/support-passport/grant-contracts.ts +139 -0
- package/src/support-passport/grant-service-cache.test.ts +148 -0
- package/src/support-passport/grant-service-cancellation.test.ts +81 -0
- package/src/support-passport/grant-service.test.ts +2882 -0
- package/src/support-passport/grant-service.ts +364 -0
- package/src/support-passport/grant-store-commit-hook.test.ts +123 -0
- package/src/support-passport/grant-store-order.test.ts +60 -0
- package/src/support-passport/grant-store-path-safety.test.ts +30 -0
- package/src/support-passport/grant-store-recovery.test.ts +448 -0
- package/src/support-passport/grant-store.ts +902 -0
- package/src/support-passport/index.ts +7 -0
- package/src/support-passport/offline-sync-guard.ts +230 -0
- package/src/support-passport/owner-lock.ts +53 -0
- package/src/support-passport/private-file.ts +371 -0
- package/src/tier-routing.ts +28 -17
- package/src/training-export/converter.test.ts +24 -0
- package/src/training-export/converter.ts +2 -0
- package/src/types.ts +1 -1
- package/src/wearables/service.test.ts +13 -0
- package/src/wearables/service.ts +2 -0
- package/dist/chunk-3CSX3ETE.js.map +0 -1
- package/dist/chunk-6LAPLZOS.js.map +0 -1
- package/dist/chunk-6R4G7N4S.js.map +0 -1
- package/dist/chunk-7BZOBA5J.js.map +0 -1
- package/dist/chunk-CYF6WVE7.js.map +0 -1
- package/dist/chunk-G2LUMAUF.js.map +0 -1
- package/dist/chunk-GYPQL6XN.js.map +0 -1
- package/dist/chunk-JDZPNCMA.js.map +0 -1
- package/dist/chunk-JV5YUBIZ.js.map +0 -1
- package/dist/chunk-NH5IR2WM.js.map +0 -1
- package/dist/chunk-RUMNY5DT.js.map +0 -1
- package/dist/chunk-S75M5ZRK.js.map +0 -1
- package/dist/chunk-T2JHZWH6.js.map +0 -1
- package/dist/chunk-UTRO5UJU.js.map +0 -1
- package/dist/chunk-XUAMCLY4.js.map +0 -1
- package/dist/contradiction-scan-HYLIRMJ2.js +0 -14
- package/dist/first-start-migration-626C5554.js.map +0 -1
- /package/dist/{chunk-47P3RYZP.js.map → chunk-2INWPC6E.js.map} +0 -0
- /package/dist/{chunk-WOPA3SGI.js.map → chunk-3OTSVCPK.js.map} +0 -0
- /package/dist/{chunk-PX2LSHYM.js.map → chunk-3SXLD5T3.js.map} +0 -0
- /package/dist/{chunk-C4ZNGVK3.js.map → chunk-5JKUSBMX.js.map} +0 -0
- /package/dist/{chunk-MAXVH44E.js.map → chunk-6NNNURLK.js.map} +0 -0
- /package/dist/{chunk-LJS7KUPV.js.map → chunk-6PZGKGAO.js.map} +0 -0
- /package/dist/{chunk-YN6ASZUG.js.map → chunk-6QPI2ITJ.js.map} +0 -0
- /package/dist/{chunk-CGVHPCP2.js.map → chunk-7Q7JWLQ4.js.map} +0 -0
- /package/dist/{chunk-227UFSEQ.js.map → chunk-7RZJWZ46.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-AU7Q3LSC.js.map} +0 -0
- /package/dist/{chunk-PDDTI7IC.js.map → chunk-B4T3FQMT.js.map} +0 -0
- /package/dist/{chunk-APPFNGX6.js.map → chunk-BSXGTDVJ.js.map} +0 -0
- /package/dist/{chunk-4IIFL7GC.js.map → chunk-CLKBV3Z3.js.map} +0 -0
- /package/dist/{chunk-ZYLC7NFO.js.map → chunk-CSY44OKU.js.map} +0 -0
- /package/dist/{chunk-HLYR2TQL.js.map → chunk-CUTFSLSD.js.map} +0 -0
- /package/dist/{chunk-RDJRFDJY.js.map → chunk-DBY26JJJ.js.map} +0 -0
- /package/dist/{chunk-QWGE2PUH.js.map → chunk-DDA55IVB.js.map} +0 -0
- /package/dist/{chunk-VMMQDEN5.js.map → chunk-DN2IDKI5.js.map} +0 -0
- /package/dist/{chunk-2GFK22OZ.js.map → chunk-DOOWFDWN.js.map} +0 -0
- /package/dist/{chunk-7FGL6CGA.js.map → chunk-DVYRD4HY.js.map} +0 -0
- /package/dist/{chunk-YGLY6QFV.js.map → chunk-F2Y6I74C.js.map} +0 -0
- /package/dist/{chunk-UU7TRFNE.js.map → chunk-FFEBSFVT.js.map} +0 -0
- /package/dist/{chunk-NIS256NW.js.map → chunk-JKW663ZN.js.map} +0 -0
- /package/dist/{chunk-G7A5HLUS.js.map → chunk-KJH6W2I7.js.map} +0 -0
- /package/dist/{chunk-HWRD23DW.js.map → chunk-LVKYLMSM.js.map} +0 -0
- /package/dist/{chunk-ZMYDYNI4.js.map → chunk-N7CXQ5TA.js.map} +0 -0
- /package/dist/{chunk-3JLPGJ43.js.map → chunk-NGT35IGQ.js.map} +0 -0
- /package/dist/{chunk-GUKBW4HL.js.map → chunk-NQMSEB5D.js.map} +0 -0
- /package/dist/{chunk-WRJVB3C2.js.map → chunk-PO7NXBG2.js.map} +0 -0
- /package/dist/{chunk-WM65LU67.js.map → chunk-QOQQMVR2.js.map} +0 -0
- /package/dist/{chunk-QHFV6VNU.js.map → chunk-QPAQSG3F.js.map} +0 -0
- /package/dist/{chunk-UES4BHA2.js.map → chunk-QTO6BMQZ.js.map} +0 -0
- /package/dist/{chunk-THPPCISA.js.map → chunk-RBII7637.js.map} +0 -0
- /package/dist/{chunk-QT2FRYI4.js.map → chunk-RG426DJO.js.map} +0 -0
- /package/dist/{chunk-VKXWK44Q.js.map → chunk-TY6MKIWP.js.map} +0 -0
- /package/dist/{chunk-AZM4CCAC.js.map → chunk-UWJNKGBA.js.map} +0 -0
- /package/dist/{chunk-ZPJ4QSGP.js.map → chunk-VTU5IEKY.js.map} +0 -0
- /package/dist/{chunk-RZ4XGKOJ.js.map → chunk-VYQ4URQB.js.map} +0 -0
- /package/dist/{chunk-WDBX6VER.js.map → chunk-VZRFGJCH.js.map} +0 -0
- /package/dist/{chunk-IBKR2TFR.js.map → chunk-WM5LD6AT.js.map} +0 -0
- /package/dist/{chunk-K4QS7LXU.js.map → chunk-WZWZRZ2V.js.map} +0 -0
- /package/dist/{chunk-G4T7L557.js.map → chunk-XKAR4RF6.js.map} +0 -0
- /package/dist/{chunk-DZD576ZU.js.map → chunk-XSV2YDCJ.js.map} +0 -0
- /package/dist/{chunk-G7Z6EENO.js.map → chunk-YDWPYDML.js.map} +0 -0
- /package/dist/{chunk-6HPHEFXT.js.map → chunk-YKZLOPTC.js.map} +0 -0
- /package/dist/{contradiction-scan-HYLIRMJ2.js.map → contradiction-scan-KCADHXPE.js.map} +0 -0
- /package/dist/{purge-533FZLS4.js.map → purge-GB7PZHT2.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/support-passport/contracts.ts","../src/support-passport/card-projection.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nimport { normalizeNamespaceIdentity } from \"../namespaces/identity.js\";\nimport { parseIsoOffsetTimestamp } from \"../utils/iso-timestamp.js\";\n\nexport const SUPPORT_PASSPORT_CARD_CATEGORIES = [\n \"communication\",\n \"environment\",\n \"transitions\",\n \"sensory\",\n \"regulation\",\n \"interests\",\n \"other\",\n] as const;\n\nexport const SUPPORT_PASSPORT_CARD_STATUSES = [\n \"pending_review\",\n \"active\",\n \"rejected\",\n \"superseded\",\n \"archived\",\n] as const;\n\nexport const SupportPassportCardCategorySchema = z.enum(SUPPORT_PASSPORT_CARD_CATEGORIES);\nexport const SupportPassportCardStatusSchema = z.enum(SUPPORT_PASSPORT_CARD_STATUSES);\nexport const SupportPassportCardTitleSchema = z\n .string()\n .trim()\n .min(1)\n .max(80)\n .refine((title) => !title.includes(\"]\") && !title.includes(\"\\n\") && !title.includes(\"\\r\"), {\n message: \"card titles cannot contain closing brackets or line breaks\",\n });\n\nconst IsoTimestampSchema = z\n .string()\n .datetime({ offset: true })\n .refine((value) => parseIsoOffsetTimestamp(value) !== null, {\n message: \"timestamps must be valid ISO 8601 values\",\n });\nexport const SupportPassportNamespaceSchema = z\n .string()\n .min(1)\n .refine((namespace) => namespace === normalizeNamespaceIdentity(namespace), {\n message: \"namespaces must be canonical\",\n });\nexport const SupportPassportMemoryIdSchema = z\n .string()\n .trim()\n .min(1)\n .max(128)\n .regex(/^[A-Za-z0-9._:-]+$/);\n\nexport const SupportPassportCardSchema = z\n .object({\n cardId: SupportPassportMemoryIdSchema,\n title: SupportPassportCardTitleSchema,\n statement: z.string().trim().min(1).max(500),\n category: SupportPassportCardCategorySchema,\n status: SupportPassportCardStatusSchema,\n updatedAt: IsoTimestampSchema,\n reviewBy: IsoTimestampSchema,\n revision: z.string().regex(/^[a-f0-9]{64}$/),\n })\n .strict();\n\nexport const SupportPassportCardListSchema = z\n .array(SupportPassportCardSchema)\n .max(100)\n .superRefine((cards, ctx) => {\n const seen = new Set<string>();\n for (const [index, card] of cards.entries()) {\n if (seen.has(card.cardId)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"card IDs must be unique\",\n path: [index, \"cardId\"],\n });\n }\n seen.add(card.cardId);\n }\n });\n\nexport const SupportPassportListCardsInputSchema = z\n .object({\n principal: z.string().trim().min(1).max(512),\n })\n .strict();\n\nexport const SupportPassportManualDraftInputSchema = z\n .object({\n principal: z.string().trim().min(1).max(512),\n title: SupportPassportCardTitleSchema,\n statement: z.string().trim().min(1).max(500),\n category: SupportPassportCardCategorySchema,\n reviewBy: IsoTimestampSchema,\n })\n .strict();\n\nexport const SupportPassportCardMutationInputSchema = z\n .object({\n principal: z.string().trim().min(1).max(512),\n cardId: SupportPassportMemoryIdSchema,\n expectedRevision: z.string().regex(/^[a-f0-9]{64}$/),\n })\n .strict();\n\nexport const SupportPassportReplaceCardInputSchema = SupportPassportCardMutationInputSchema.extend({\n title: SupportPassportCardTitleSchema,\n statement: z.string().trim().min(1).max(500),\n category: SupportPassportCardCategorySchema,\n reviewBy: IsoTimestampSchema,\n}).strict();\n\nexport type SupportPassportCard = z.infer<typeof SupportPassportCardSchema>;\nexport type SupportPassportCardCategory = z.infer<typeof SupportPassportCardCategorySchema>;\nexport type SupportPassportCardStatus = z.infer<typeof SupportPassportCardStatusSchema>;\nexport type SupportPassportManualDraftInput = z.input<typeof SupportPassportManualDraftInputSchema>;\nexport type SupportPassportCardMutationInput = z.input<typeof SupportPassportCardMutationInputSchema>;\nexport type SupportPassportReplaceCardInput = z.input<typeof SupportPassportReplaceCardInputSchema>;\n\ntype RevisionFields = Omit<SupportPassportCard, \"revision\">;\n\nexport function computeSupportPassportCardRevision(fields: RevisionFields): string {\n const canonicalFields = {\n cardId: fields.cardId,\n category: fields.category,\n reviewBy: fields.reviewBy,\n statement: fields.statement,\n status: fields.status,\n title: fields.title,\n updatedAt: fields.updatedAt,\n };\n return createHash(\"sha256\").update(JSON.stringify(canonicalFields)).digest(\"hex\");\n}\n","import { createHash } from \"node:crypto\";\nimport { lstat } from \"node:fs/promises\";\n\nimport type { OfflineSyncExcludeFile } from \"../offline-sync-file-io.js\";\nimport { stripAttributesSuffix } from \"../structured-attributes.js\";\nimport type { MemoryFile } from \"../types.js\";\nimport {\n type SupportPassportCard,\n SupportPassportCardCategorySchema,\n SupportPassportCardSchema,\n SupportPassportCardStatusSchema,\n SupportPassportMemoryIdSchema,\n SupportPassportNamespaceSchema,\n computeSupportPassportCardRevision,\n} from \"./contracts.js\";\n\nexport const SUPPORT_PASSPORT_CARD_TAG = \"support-passport-card\";\nexport const SUPPORT_PASSPORT_AUDIT_TAG = \"support-passport-audit\";\n\nexport function isSupportPassportPrivateMemory(memory: {\n frontmatter: {\n tags?: readonly string[];\n structuredAttributes?: Readonly<Record<string, string>>;\n };\n}): boolean {\n const tags = memory.frontmatter.tags;\n if (tags?.includes(SUPPORT_PASSPORT_CARD_TAG) === true || tags?.includes(SUPPORT_PASSPORT_AUDIT_TAG) === true) {\n return true;\n }\n return Object.keys(memory.frontmatter.structuredAttributes ?? {})\n .some((key) => key.startsWith(\"support-passport-\"));\n}\n\nexport function excludeSupportPassportPrivateMemories<T extends Pick<MemoryFile, \"frontmatter\">>(\n memories: readonly T[]\n): T[] {\n return memories.filter((memory) => !isSupportPassportPrivateMemory(memory));\n}\n\nexport function createSupportPassportPrivateFileExclusion(storage: {\n readMemoryByPath(filePath: string): Promise<MemoryFile | null>;\n}): OfflineSyncExcludeFile {\n const classifications = new Map<string, { identity: string; excluded: Promise<boolean> }>();\n return async ({ filePath }) => {\n let file: Awaited<ReturnType<typeof lstat>>;\n try {\n file = await lstat(filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n classifications.delete(filePath);\n return false;\n }\n throw error;\n }\n const identity = `${file.dev}:${file.ino}:${file.size}:${file.mtimeMs}:${file.ctimeMs}`;\n const cached = classifications.get(filePath);\n if (cached?.identity === identity) return await cached.excluded;\n const excluded = storage\n .readMemoryByPath(filePath)\n .then((memory) => (memory ? isSupportPassportPrivateMemory(memory) : false));\n classifications.set(filePath, { identity, excluded });\n try {\n return await excluded;\n } catch (error) {\n if (classifications.get(filePath)?.excluded === excluded) classifications.delete(filePath);\n throw error;\n }\n };\n}\n\nexport const SUPPORT_PASSPORT_ATTRIBUTE_KEYS = Object.freeze({\n namespace: \"support-passport-namespace\",\n namespaceEncoding: \"support-passport-namespace-encoding\",\n owner: \"support-passport-owner\",\n title: \"support-passport-title\",\n category: \"support-passport-category\",\n order: \"support-passport-order\",\n reviewBy: \"support-passport-review-by\",\n sourceMemoryIds: \"support-passport-source-ids\",\n replacesDraftId: \"support-passport-replaces-draft-id\",\n replacedRevision: \"support-passport-replaced-revision\",\n draftReplacementPrepared: \"support-passport-draft-replacement-prepared\",\n replacementComplete: \"support-passport-replacement-complete\",\n});\n\nconst SUPPORT_PASSPORT_NAMESPACE_ENCODING = \"base64url-v1\";\nconst SUPPORT_PASSPORT_NAMESPACE_CHUNK_PREFIX = \"support-passport-namespace-\";\nconst SUPPORT_PASSPORT_NAMESPACE_CHUNK_SIZE = 1_024;\nconst LegacySupportPassportNamespaceSchema = SupportPassportNamespaceSchema.refine(\n (namespace) =>\n !namespace.includes(\"/\") &&\n !namespace.includes(\"\\\\\") &&\n !namespace.includes(\"..\") &&\n !namespace.includes(\"]\") &&\n !namespace.includes(\"\\0\") &&\n !namespace.includes(\"\\n\") &&\n !namespace.includes(\"\\r\")\n);\n\nexport function encodeSupportPassportNamespaceAttributes(namespace: string): Record<string, string> {\n const canonical = SupportPassportNamespaceSchema.parse(namespace);\n const encoded = Buffer.from(canonical, \"utf8\").toString(\"base64url\");\n const chunks = encoded.match(new RegExp(`.{1,${SUPPORT_PASSPORT_NAMESPACE_CHUNK_SIZE}}`, \"g\")) ?? [];\n const digest = createHash(\"sha256\").update(encoded).digest(\"hex\");\n return Object.fromEntries([\n [SUPPORT_PASSPORT_ATTRIBUTE_KEYS.namespace, chunks[0] ?? \"\"],\n [\n SUPPORT_PASSPORT_ATTRIBUTE_KEYS.namespaceEncoding,\n `${SUPPORT_PASSPORT_NAMESPACE_ENCODING}:${chunks.length}:${digest}`,\n ],\n ...chunks.slice(1).map((chunk, index) => [`${SUPPORT_PASSPORT_NAMESPACE_CHUNK_PREFIX}${index + 1}`, chunk]),\n ]);\n}\n\nexport function decodeSupportPassportNamespaceAttributes(attributes: Readonly<Record<string, string>>): string | null {\n const firstChunk = attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.namespace];\n const encoding = attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.namespaceEncoding];\n if (encoding === undefined) {\n const legacy = LegacySupportPassportNamespaceSchema.safeParse(firstChunk);\n return legacy.success ? legacy.data : null;\n }\n const match = /^base64url-v1:([1-9]\\d*):([a-f0-9]{64})$/.exec(encoding);\n if (!match || typeof firstChunk !== \"string\") return null;\n const chunkCount = Number(match[1]);\n if (!Number.isSafeInteger(chunkCount)) return null;\n const chunkIndexes = Object.keys(attributes).flatMap((key) => {\n const chunkMatch = /^support-passport-namespace-(\\d+)$/.exec(key);\n return chunkMatch ? [Number(chunkMatch[1])] : [];\n });\n if (\n chunkIndexes.length !== chunkCount - 1 ||\n chunkIndexes.some((index) => !Number.isSafeInteger(index) || index < 1 || index >= chunkCount)\n )\n return null;\n const chunks = [firstChunk];\n for (let index = 1; index < chunkCount; index += 1) {\n const chunk = attributes[`${SUPPORT_PASSPORT_NAMESPACE_CHUNK_PREFIX}${index}`];\n if (typeof chunk !== \"string\" || chunk.length === 0) return null;\n chunks.push(chunk);\n }\n const encoded = chunks.join(\"\");\n if (createHash(\"sha256\").update(encoded).digest(\"hex\") !== match[2]) return null;\n const decoded = Buffer.from(encoded, \"base64url\").toString(\"utf8\");\n if (Buffer.from(decoded, \"utf8\").toString(\"base64url\") !== encoded) return null;\n const namespace = SupportPassportNamespaceSchema.safeParse(decoded);\n return namespace.success ? namespace.data : null;\n}\n\nexport interface StoredSupportPassportCard {\n card: SupportPassportCard;\n memory: MemoryFile;\n order: number;\n sourceMemoryIds: string[];\n namespace: string;\n owner: string;\n replacesDraftId?: string;\n replacedRevision?: string;\n draftReplacementPrepared: boolean;\n}\n\nexport function computeSupportPassportOwnerKey(principal: string): string {\n return createHash(\"sha256\").update(principal).digest(\"hex\");\n}\n\nfunction parseSourceMemoryIds(value: string | undefined): string[] | null {\n if (value === undefined) return null;\n if (value === \"\") return [];\n const rawIds = value.split(\",\");\n if (rawIds.length > 5) return null;\n const normalized: string[] = [];\n for (const rawId of rawIds) {\n const parsed = SupportPassportMemoryIdSchema.safeParse(rawId);\n if (!parsed.success) return null;\n normalized.push(parsed.data);\n }\n if (new Set(normalized).size !== normalized.length) return null;\n return normalized;\n}\n\ninterface SupportPassportCardMetadata {\n fields: Omit<SupportPassportCard, \"revision\" | \"statement\">;\n order: number;\n sourceMemoryIds: string[];\n namespace: string;\n owner: string;\n replacesDraftId?: string;\n replacedRevision?: string;\n draftReplacementPrepared: boolean;\n}\n\nfunction parseSupportPassportCardMetadata(memory: Pick<MemoryFile, \"frontmatter\">): SupportPassportCardMetadata | null {\n const frontmatter = memory.frontmatter;\n const attributes = frontmatter.structuredAttributes;\n if (frontmatter.category !== \"preference\") return null;\n if (!frontmatter.tags?.includes(SUPPORT_PASSPORT_CARD_TAG)) return null;\n if (!attributes || frontmatter.blockedBy || frontmatter.archivedAt || frontmatter.supersededBy) return null;\n\n const category = SupportPassportCardCategorySchema.safeParse(attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.category]);\n const status = SupportPassportCardStatusSchema.safeParse(frontmatter.status);\n const rawOrder = attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.order];\n const order = Number(rawOrder);\n const sourceMemoryIds = parseSourceMemoryIds(attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.sourceMemoryIds]);\n const namespace = decodeSupportPassportNamespaceAttributes(attributes);\n const owner = attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.owner];\n let replacesDraftId: string | undefined;\n const rawReplacesDraftId = attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.replacesDraftId];\n if (rawReplacesDraftId !== undefined) {\n const parsedReplacesDraftId = SupportPassportMemoryIdSchema.safeParse(rawReplacesDraftId);\n if (!parsedReplacesDraftId.success) return null;\n replacesDraftId = parsedReplacesDraftId.data;\n }\n let replacedRevision: string | undefined;\n const rawReplacedRevision = attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.replacedRevision];\n if (rawReplacedRevision !== undefined) {\n if (!/^[a-f0-9]{64}$/.test(rawReplacedRevision)) return null;\n replacedRevision = rawReplacedRevision;\n }\n if (\n !category.success ||\n !status.success ||\n status.data === \"superseded\" ||\n typeof rawOrder !== \"string\" ||\n !/^(?:0|[1-9]\\d*)$/.test(rawOrder) ||\n !Number.isSafeInteger(order) ||\n namespace === null ||\n typeof owner !== \"string\" ||\n !/^[a-f0-9]{64}$/.test(owner) ||\n !sourceMemoryIds\n )\n return null;\n const fields = SupportPassportCardSchema.omit({ revision: true, statement: true }).safeParse({\n cardId: frontmatter.id,\n title: attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.title],\n category: category.data,\n status: status.data,\n updatedAt: frontmatter.updated,\n reviewBy: attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.reviewBy],\n });\n if (!fields.success) return null;\n return {\n fields: fields.data,\n order,\n sourceMemoryIds,\n namespace,\n owner,\n replacesDraftId,\n replacedRevision,\n draftReplacementPrepared: attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.draftReplacementPrepared] === \"true\",\n };\n}\n\nexport function hasLiveSupportPassportCard(memory: Pick<MemoryFile, \"frontmatter\" | \"content\">): boolean {\n const metadata = parseSupportPassportCardMetadata(memory);\n if (metadata?.fields.status !== \"active\" && metadata?.fields.status !== \"pending_review\") return false;\n return SupportPassportCardSchema.omit({ revision: true }).safeParse({\n ...metadata.fields,\n statement: stripAttributesSuffix(memory.content),\n }).success;\n}\n\nexport function projectSupportPassportCard(memory: MemoryFile): StoredSupportPassportCard | null {\n const metadata = parseSupportPassportCardMetadata(memory);\n if (!metadata) return null;\n\n const fields = {\n ...metadata.fields,\n statement: stripAttributesSuffix(memory.content),\n };\n const normalized = SupportPassportCardSchema.omit({ revision: true }).safeParse(fields);\n if (!normalized.success) return null;\n const parsed = SupportPassportCardSchema.safeParse({\n ...normalized.data,\n revision: computeSupportPassportCardRevision(normalized.data),\n });\n if (!parsed.success) return null;\n return {\n card: parsed.data,\n memory,\n order: metadata.order,\n sourceMemoryIds: metadata.sourceMemoryIds,\n namespace: metadata.namespace,\n owner: metadata.owner,\n replacesDraftId: metadata.replacesDraftId,\n replacedRevision: metadata.replacedRevision,\n draftReplacementPrepared: metadata.draftReplacementPrepared,\n };\n}\n"],"mappings":"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","names":["createHash","createHash"]}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
|
|
14
14
|
OFFLINE_SYNC_MAX_MTIME_MS
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Z4N7SVVF.js";
|
|
16
16
|
import {
|
|
17
17
|
validateArchiveRelativePath
|
|
18
18
|
} from "./chunk-GCGJW34D.js";
|
|
@@ -475,4 +475,4 @@ export {
|
|
|
475
475
|
actionConfidenceRequestSchema,
|
|
476
476
|
validateRequest
|
|
477
477
|
};
|
|
478
|
-
//# sourceMappingURL=chunk-
|
|
478
|
+
//# sourceMappingURL=chunk-JKW663ZN.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-HBVPYRDW.js";
|
|
8
8
|
import {
|
|
9
9
|
OFFLINE_SYNC_MAX_MTIME_MS
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-Z4N7SVVF.js";
|
|
11
11
|
import {
|
|
12
12
|
validateArchiveRelativePath
|
|
13
13
|
} from "./chunk-GCGJW34D.js";
|
|
@@ -446,4 +446,4 @@ export {
|
|
|
446
446
|
summarizeReconcilePlan,
|
|
447
447
|
planReconciliation
|
|
448
448
|
};
|
|
449
|
-
//# sourceMappingURL=chunk-
|
|
449
|
+
//# sourceMappingURL=chunk-KJH6W2I7.js.map
|
|
@@ -108,7 +108,8 @@ async function searchWithGenericExclusion(options) {
|
|
|
108
108
|
let firstPage = true;
|
|
109
109
|
for (; ; ) {
|
|
110
110
|
const raw = await options.search(limit);
|
|
111
|
-
|
|
111
|
+
const eligible = raw.filter((hit) => !options.isExcluded(hit.path));
|
|
112
|
+
results = await options.filterPrivate(eligible);
|
|
112
113
|
if (firstPage) {
|
|
113
114
|
firstPage = false;
|
|
114
115
|
if (!options.sendInitialLimit) target = Math.min(budget, raw.length);
|
|
@@ -129,6 +130,7 @@ async function runScopedMemorySearch(options) {
|
|
|
129
130
|
budget: options.budget,
|
|
130
131
|
sendInitialLimit: options.sendInitialLimit,
|
|
131
132
|
isExcluded: options.isExcluded,
|
|
133
|
+
filterPrivate: options.filterPrivate,
|
|
132
134
|
search: (limit) => options.namespacesEnabled ? options.namespaced(limit) : options.flatCorpus(limit)
|
|
133
135
|
});
|
|
134
136
|
return results.map((hit) => ({
|
|
@@ -146,12 +148,19 @@ async function memorySearchThroughScope(deps, request) {
|
|
|
146
148
|
throw new EngramAccessInputError("collection must be a non-empty string");
|
|
147
149
|
}
|
|
148
150
|
let searchNamespaces = [];
|
|
151
|
+
const privateVisibilityCache = /* @__PURE__ */ new Map();
|
|
149
152
|
const results = await runScopedMemorySearch({
|
|
150
153
|
query,
|
|
151
154
|
collection,
|
|
152
155
|
mode,
|
|
153
156
|
namespacesEnabled: deps.namespacesEnabled,
|
|
154
157
|
isExcluded: deps.isExcluded,
|
|
158
|
+
filterPrivate: (results2) => deps.filterPrivate(
|
|
159
|
+
results2,
|
|
160
|
+
searchNamespaces,
|
|
161
|
+
!deps.namespacesEnabled && collection !== void 0 && collection !== "global" && !deps.memoryCollections.includes(collection),
|
|
162
|
+
privateVisibilityCache
|
|
163
|
+
),
|
|
155
164
|
budget: maxResults ?? deps.defaultBudget,
|
|
156
165
|
sendInitialLimit: maxResults !== void 0,
|
|
157
166
|
authorizeScope: async () => {
|
|
@@ -198,4 +207,4 @@ export {
|
|
|
198
207
|
runScopedMemorySearch,
|
|
199
208
|
memorySearchThroughScope
|
|
200
209
|
};
|
|
201
|
-
//# sourceMappingURL=chunk-
|
|
210
|
+
//# sourceMappingURL=chunk-KM55SMLG.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/access-memory-search-fanout.ts"],"sourcesContent":["import { canReadNamespace, defaultNamespaceForPrincipal } from \"./namespaces/principal.js\";\nimport type { ResolvedScopeProfilePlan } from \"./namespaces/scope-profiles.js\";\nimport { EngramAccessInputError } from \"./access-errors.js\";\nimport { log } from \"./logger.js\";\nimport type { SearchDegradation, SearchExecutionOptions } from \"./search/port.js\";\nimport type { PluginConfig } from \"./types.js\";\n\n/**\n * Issue #2018: `memory_search` has no sessionKey, so — unlike recall — it\n * cannot resolve a coding overlay. With scope profiles active and no overlay,\n * `expandScopeProfileReadNamespaces` derives a per-principal self namespace.\n * On a legacy flat-root deployment the corpus still lives under the configured\n * DEFAULT namespace's base collection, which that derived set excludes, so\n * every query silently returned 0.\n *\n * Returns the configured default namespace to merge into the profile read set\n * when the base collection must stay reachable, or `null` otherwise. ALL of:\n *\n * - the default namespace is THIS principal's own self store — i.e. the\n * principal has no dedicated namespace policy, so\n * `defaultNamespaceForPrincipal` resolves to the configured default. On a\n * multi-tenant scope-profile deployment every principal has its own\n * policy; reaching into the default would read ANOTHER namespace, so the\n * fallback must not fire (#2056 r2 / #1501 privateOnly);\n * - the profile resolved `userGlobal` as a READABLE layer (not just listed\n * it in `readOrder`). `serverShared` maps to `sharedNamespace`, not the\n * default, and an unreadable resolved layer is a deliberate omission —\n * neither may trigger this fallback (#2056 r2);\n * - the profile's resolved self namespace is NOT already the default\n * (otherwise it is already in the set);\n * - the principal is authorized to read the default namespace.\n *\n * The flat-root signal is resolved lazily via {@link defaultAtFlatRootProvider}\n * — the provider is only invoked once the cheap gates (readOrder consent,\n * resolved-layer readability, principal-self, self-resolved-away, ACL) all\n * pass, so a project-only / serverShared-only / unreadable-userGlobal\n * profile never touches the default store (#2056 r6).\n */\nexport async function resolveMemorySearchDefaultFallback(options: {\n profilePlan: ResolvedScopeProfilePlan;\n config: PluginConfig;\n principal: string | undefined;\n /**\n * True only when the configured default namespace's storage root equals\n * `config.memoryDir` (the legacy flat-root layout). Invoked ONLY after the\n * cheap gates pass, so ineligible profiles never probe the default store.\n */\n defaultAtFlatRootProvider: () => Promise<boolean>;\n}): Promise<string | null> {\n const { profilePlan, config, principal } = options;\n // Defense-in-depth: principalSelfIsDefault is only ever true alongside a\n // flat-root default on legacy deployments (a hosted scope-profile principal\n // with its own policy fails this even when default is at the flat root). It\n // is NOT redundant — it keeps a policy-having principal isolated from the\n // flat default corpus.\n const principalSelfIsDefault =\n defaultNamespaceForPrincipal(principal, config) === config.defaultNamespace;\n // readOrder must explicitly intend userGlobal (resolveScopeProfilePlan\n // always materializes a userGlobal layer even when readOrder omits it, so\n // the layer's presence alone is not consent), AND that layer must have\n // resolved readable (a listed-but-unreadable layer is a deliberate\n // omission). serverShared maps to sharedNamespace, not the default.\n const profileIntendsUserGlobal =\n profilePlan.profile.readOrder.includes(\"userGlobal\");\n const userGlobalLayerReadable = profilePlan.layers.some(\n (layer) => layer.id === \"userGlobal\" && layer.readable && Boolean(layer.namespace),\n );\n const selfResolvedAwayFromDefault =\n profilePlan.baseNamespace !== config.defaultNamespace;\n if (\n !(\n profileIntendsUserGlobal &&\n userGlobalLayerReadable &&\n principalSelfIsDefault &&\n selfResolvedAwayFromDefault &&\n canReadNamespace(principal, config.defaultNamespace, config)\n )\n ) {\n // Cheap gates failed — never probe the default store.\n return null;\n }\n const defaultAtFlatRoot = await options.defaultAtFlatRootProvider();\n return defaultAtFlatRoot ? config.defaultNamespace : null;\n}\n\n/**\n * Issue #2018 r4: the explicit legacy-flat-root signal — the configured\n * default namespace's storage root equals `config.memoryDir`. Resolved from\n * storage (not inferred from config/policy shape) so a hosted scope-profile\n * deployment never reaches into the default corpus.\n */\nexport async function defaultNamespaceAtFlatRoot(\n getStorage: (namespace: string) => Promise<{ dir: string }>,\n config: { defaultNamespace: string; memoryDir: string },\n): Promise<boolean> {\n const storage = await getStorage(config.defaultNamespace);\n return storage.dir === config.memoryDir;\n}\n\n/**\n * Merge the {@link resolveMemorySearchDefaultFallback} result into a profile\n * read-namespace set without duplicates. Returns the original set unchanged\n * when no fallback applies.\n */\nexport function mergeMemorySearchDefaultFallback(\n profileNamespaces: string[],\n fallback: string | null,\n): string[] {\n if (!fallback || profileNamespaces.includes(fallback)) return profileNamespaces;\n return [...profileNamespaces, fallback];\n}\n\n\n/**\n * Run a memory_search namespace fanout with the observability required by\n * issue #2018: a silent empty fanout is surfaced as a warning (never silently\n * empty), and per-namespace backend degradations — which memory_search\n * previously swallowed — are collected and warned so a missing collection\n * looks different from a genuine no-matches.\n *\n * `search` is injected so this stays free of the orchestrator type.\n */\nexport async function runMemorySearchFanout<TResult>(options: {\n query: string;\n namespaces: string[];\n maxResults?: number;\n principal?: string;\n requestedNamespace?: string;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n search(params: {\n query: string;\n namespaces: string[];\n maxResults?: number;\n mode: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n execution: SearchExecutionOptions;\n }): Promise<TResult[]>;\n}): Promise<TResult[]> {\n const { query, namespaces, maxResults, principal, requestedNamespace, collection } = options;\n if (namespaces.length === 0) {\n log.warn(\"memory_search resolved zero readable namespaces\", {\n principal: principal ?? null,\n requestedNamespace: requestedNamespace ?? null,\n collection: collection ?? null,\n });\n return [];\n }\n log.debug(\"memory_search fanout\", {\n principal: principal ?? null,\n namespaces,\n collection: collection ?? null,\n });\n const degradations: SearchDegradation[] = [];\n const results = await options.search({\n query,\n namespaces,\n maxResults,\n mode: options.mode ?? \"search\",\n execution: {\n onDegradation: (degradation) => degradations.push(degradation),\n },\n });\n if (degradations.length > 0) {\n log.warn(\"memory_search backend degradation\", {\n principal: principal ?? null,\n degradations,\n });\n }\n return results;\n}\n\n/**\n * Resolve results for a FLAT corpus (namespaces disabled).\n *\n * An explicit ranking mode routes through the namespace-aware search even\n * here, because that is the only path honoring it; the legacy direct-QMD calls\n * stay the default so nothing else moves.\n */\n/**\n * Reject an option combination a flat corpus cannot honor.\n *\n * A `mode` routes through the namespace-aware backend, which cannot target a\n * specific collection, so pairing the two would silently search the default\n * collection and return unrelated results (AGENTS.md pattern 39).\n *\n * Exported as its own step because callers validate BEFORE any budget\n * short-circuit: changing only the requested result count must never make an\n * invalid request succeed.\n */\nexport const MEMORY_SEARCH_MODES = [\"search\", \"hybrid\", \"bm25\", \"vector\"] as const;\n\nexport type MemorySearchMode = (typeof MEMORY_SEARCH_MODES)[number];\n\n/**\n * Reject an unrecognized ranking mode at the service boundary.\n *\n * The HTTP and MCP schemas already validate their own input, but an\n * in-process caller reaches this service untyped. Both namespace backends\n * route an unknown mode through their DEFAULT ordinary-search branch, so a\n * typo would silently rank differently instead of reporting bad input\n * (AGENTS.md §39).\n */\n/**\n * Reject a budget that is not a finite non-negative integer.\n *\n * The HTTP and MCP schemas validate their own input, but an in-process caller\n * reaches this service untyped: a negative would hit the `budget <= 0`\n * short-circuit and return a successful EMPTY page, and a fraction or a\n * non-finite value would flow into the backend limit and top-up arithmetic.\n * Zero keeps its documented meaning — an empty result with no backend call.\n */\nexport function assertMemorySearchLimit(maxResults: unknown): void {\n if (maxResults === undefined) return;\n if (typeof maxResults !== \"number\" || !Number.isInteger(maxResults) || maxResults < 0) {\n throw new EngramAccessInputError(\n `maxResults must be a non-negative integer (got ${JSON.stringify(maxResults)})`,\n );\n }\n}\n\nexport function assertMemorySearchMode(mode: unknown): void {\n if (mode === undefined) return;\n if (typeof mode !== \"string\" || !(MEMORY_SEARCH_MODES as readonly string[]).includes(mode)) {\n throw new EngramAccessInputError(\n `mode must be one of ${MEMORY_SEARCH_MODES.join(\", \")} (got ${JSON.stringify(mode)})`,\n );\n }\n}\n\nexport function assertFlatCorpusOptions(\n mode: string | undefined,\n collection: string | undefined,\n): void {\n assertMemorySearchMode(mode);\n if (mode && collection) {\n throw new EngramAccessInputError(\n `mode is not supported together with collection on a flat corpus (got collection: ${collection})`,\n );\n }\n}\n\nexport async function runFlatCorpusMemorySearch<TResult>(options: {\n query: string;\n maxResults?: number;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n searchAcrossNamespaces(params: {\n query: string;\n maxResults?: number;\n mode: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n }): Promise<TResult[]>;\n searchGlobal(query: string, maxResults?: number): Promise<TResult[]>;\n search(query: string, collection: string | undefined, maxResults?: number): Promise<TResult[]>;\n}): Promise<TResult[]> {\n const { query, maxResults, collection, mode } = options;\n assertFlatCorpusOptions(options.mode, options.collection);\n if (mode) {\n // The mode-aware backend has no collection selector on a flat corpus, so\n // honoring the mode would silently search the default collection instead\n return options.searchAcrossNamespaces({ query, maxResults, mode });\n }\n return collection === \"global\"\n ? options.searchGlobal(query, maxResults)\n : options.search(query, collection, maxResults);\n}\n\n/**\n * How far one generic memory search may page before it stops.\n *\n * A backend-safety bound, NOT a stand-in for corpus exhaustion — which is why\n * it is ABSOLUTE rather than a multiple of the caller's budget. Scaling it to\n * the budget made \"the excluded paths happen to rank first\" indistinguishable\n * from \"there is nothing else\": a 1,000-row request whose first 4,000 hits are\n * artifacts stopped at 4,000 and returned an empty page while valid memories\n * sat at rank 4,001. Pages that come back FULL mean the corpus is not\n * exhausted, so the loop keeps going until a short page proves it is or this\n * cap protects the backend.\n */\nconst MEMORY_SEARCH_CANDIDATE_CAP = 25_000;\n\n\n/**\n * Run a ranked memory search and apply the generic-recall path exclusions\n * BEFORE the user-facing cap.\n *\n * Artifact isolation is a retrieval contract, not a ranking preference:\n * artifacts flow only through the dedicated verbatim path. Filtering after the\n * backend's own cap would let a handful of top-ranked artifacts shrink - or\n * empty - a page that has valid memories right behind them, so the search runs\n * with candidate headroom and tops up until the post-filter budget is met or\n * the corpus is exhausted.\n */\nexport async function searchWithGenericExclusion<TResult extends { path: string }>(options: {\n budget: number;\n /**\n * `false` omits `maxResults` on the FIRST request, so a caller that named no\n * budget keeps the backend's own page size on the wire; the resolved budget\n * is still what the filtered page is measured against.\n */\n sendInitialLimit: boolean;\n search(limit: number | undefined): Promise<TResult[]>;\n isExcluded(memoryPath: string): boolean;\n}): Promise<TResult[]> {\n const { budget } = options;\n if (budget <= 0) return [];\n let results: TResult[] = [];\n let limit: number | undefined = options.sendInitialLimit ? budget : undefined;\n // With no explicit limit the caller asked for the BACKEND's page, so that\n // page's size is the target — not the configured cap. Using the cap would\n // reissue the query whenever the backend's default page is smaller, and\n // return more rows than the same request used to.\n let target = budget;\n let firstPage = true;\n for (;;) {\n const raw = await options.search(limit);\n results = raw.filter((hit) => !options.isExcluded(hit.path));\n if (firstPage) {\n firstPage = false;\n if (!options.sendInitialLimit) target = Math.min(budget, raw.length);\n if (target <= 0) return [];\n }\n if (results.length >= target) break;\n // What the backend actually served this round: its own page size when we\n // named no limit.\n const served = limit ?? raw.length;\n // A short page means the corpus is exhausted - asking for more is wasted\n // work that returns the same rows.\n if (raw.length === 0 || raw.length < served) break;\n // The cap protects the backend from an unbounded walk, but it can never\n // sit at or below what the caller explicitly asked for: a request for N\n // rows needs room BEYOND N to replace the excluded hits among them, or a\n // large search returns a short page for a count the operation deliberately\n // supports.\n const cap = Math.max(MEMORY_SEARCH_CANDIDATE_CAP, target * 2);\n if (served >= cap) break;\n limit = Math.min(served * 2, cap);\n }\n return results.slice(0, target);\n}\n\n/**\n * The whole ranked memory-search path behind `POST /engram/v1/memories/search`\n * and the `memory_search` tool: pick the flat-corpus or namespace-aware\n * backend, then apply generic-recall exclusions before the caller's cap.\n */\nexport async function runScopedMemorySearch(options: {\n query: string;\n budget: number;\n /** `false` when the caller named no `maxResults`; see the helper below. */\n sendInitialLimit: boolean;\n /**\n * Authorize the scope. Runs BEFORE any budget decision so a zero budget - a\n * valid empty search - can never skip the namespace/principal gate and turn\n * an access error into a successful empty result.\n */\n authorizeScope(): Promise<void> | void;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n namespacesEnabled: boolean;\n isExcluded(memoryPath: string): boolean;\n flatCorpus(\n limit: number | undefined,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n namespaced(\n limit: number | undefined,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n}): Promise<Array<{ path: string; score: number; snippet: string }>> {\n await options.authorizeScope();\n const results = await searchWithGenericExclusion({\n budget: options.budget,\n sendInitialLimit: options.sendInitialLimit,\n isExcluded: options.isExcluded,\n search: (limit) =>\n options.namespacesEnabled ? options.namespaced(limit) : options.flatCorpus(limit),\n });\n return results.map((hit) => ({\n path: hit.path,\n score: hit.score,\n snippet: (hit.snippet ?? \"\").slice(0, 800),\n }));\n}\n\n/** Everything the ranked memory-search surface needs from the service. */\nexport interface ScopedMemorySearchDeps {\n namespacesEnabled: boolean;\n defaultBudget: number;\n isExcluded(memoryPath: string): boolean;\n /** Flat-corpus authorization; throws when the namespace is unreadable. */\n authorizeFlatCorpus(namespace: string | undefined, principal: string | undefined): void;\n /** Namespace-aware authorization; throws, else returns the search fan-out. */\n authorizeNamespaces(\n namespace: string | undefined,\n principal: string | undefined,\n collection: string | undefined,\n ): Promise<string[]>;\n searchAcrossNamespaces(params: {\n query: string;\n namespaces?: string[];\n maxResults?: number;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n }): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n searchGlobal(\n query: string,\n maxResults?: number,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n search(\n query: string,\n collection?: string,\n maxResults?: number,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n}\n\n/** The whole `memory_search` surface: validate, authorize, search, shape. */\nexport async function memorySearchThroughScope(\n deps: ScopedMemorySearchDeps,\n request: {\n query: string;\n namespace?: string;\n maxResults?: number;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n principal?: string;\n },\n): Promise<{\n query: string;\n results: Array<{ path: string; score: number; snippet: string }>;\n count: number;\n}> {\n const { query, namespace, maxResults, mode, principal } = request;\n // BOTH branches take the mode, and only the flat one reaches\n // `assertFlatCorpusOptions` — validate here so a namespaced request cannot\n // rank differently on a typo, and before any budget short-circuit.\n assertMemorySearchMode(mode);\n assertMemorySearchLimit(maxResults);\n const collection = request.collection?.trim();\n if (request.collection !== undefined && !collection) {\n throw new EngramAccessInputError(\"collection must be a non-empty string\");\n }\n let searchNamespaces: string[] = [];\n const results = await runScopedMemorySearch({\n query, collection, mode,\n namespacesEnabled: deps.namespacesEnabled,\n isExcluded: deps.isExcluded,\n budget: maxResults ?? deps.defaultBudget,\n sendInitialLimit: maxResults !== undefined,\n authorizeScope: async () => {\n if (!deps.namespacesEnabled) {\n // Validate the option combination here too: a zero budget must not be\n // a way to slip an invalid request past the check.\n assertFlatCorpusOptions(mode, collection);\n return deps.authorizeFlatCorpus(namespace, principal);\n }\n searchNamespaces = await deps.authorizeNamespaces(namespace, principal, collection);\n },\n flatCorpus: (limit) =>\n runFlatCorpusMemorySearch({\n query, maxResults: limit, collection, mode,\n searchAcrossNamespaces: (p) => deps.searchAcrossNamespaces(p),\n searchGlobal: (q, globalLimit) => deps.searchGlobal(q, globalLimit),\n search: (q, coll, searchLimit) => deps.search(q, coll, searchLimit),\n }),\n namespaced: (limit) =>\n runMemorySearchFanout({\n query, maxResults: limit, principal, collection, mode,\n requestedNamespace: namespace,\n namespaces: searchNamespaces,\n search: (p) => deps.searchAcrossNamespaces(p),\n }),\n });\n return { query, results, count: results.length };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/access-memory-search-fanout.ts"],"sourcesContent":["import { canReadNamespace, defaultNamespaceForPrincipal } from \"./namespaces/principal.js\";\nimport type { ResolvedScopeProfilePlan } from \"./namespaces/scope-profiles.js\";\nimport { EngramAccessInputError } from \"./access-errors.js\";\nimport { log } from \"./logger.js\";\nimport type { SearchDegradation, SearchExecutionOptions } from \"./search/port.js\";\nimport type { PluginConfig } from \"./types.js\";\n\n/**\n * Issue #2018: `memory_search` has no sessionKey, so — unlike recall — it\n * cannot resolve a coding overlay. With scope profiles active and no overlay,\n * `expandScopeProfileReadNamespaces` derives a per-principal self namespace.\n * On a legacy flat-root deployment the corpus still lives under the configured\n * DEFAULT namespace's base collection, which that derived set excludes, so\n * every query silently returned 0.\n *\n * Returns the configured default namespace to merge into the profile read set\n * when the base collection must stay reachable, or `null` otherwise. ALL of:\n *\n * - the default namespace is THIS principal's own self store — i.e. the\n * principal has no dedicated namespace policy, so\n * `defaultNamespaceForPrincipal` resolves to the configured default. On a\n * multi-tenant scope-profile deployment every principal has its own\n * policy; reaching into the default would read ANOTHER namespace, so the\n * fallback must not fire (#2056 r2 / #1501 privateOnly);\n * - the profile resolved `userGlobal` as a READABLE layer (not just listed\n * it in `readOrder`). `serverShared` maps to `sharedNamespace`, not the\n * default, and an unreadable resolved layer is a deliberate omission —\n * neither may trigger this fallback (#2056 r2);\n * - the profile's resolved self namespace is NOT already the default\n * (otherwise it is already in the set);\n * - the principal is authorized to read the default namespace.\n *\n * The flat-root signal is resolved lazily via {@link defaultAtFlatRootProvider}\n * — the provider is only invoked once the cheap gates (readOrder consent,\n * resolved-layer readability, principal-self, self-resolved-away, ACL) all\n * pass, so a project-only / serverShared-only / unreadable-userGlobal\n * profile never touches the default store (#2056 r6).\n */\nexport async function resolveMemorySearchDefaultFallback(options: {\n profilePlan: ResolvedScopeProfilePlan;\n config: PluginConfig;\n principal: string | undefined;\n /**\n * True only when the configured default namespace's storage root equals\n * `config.memoryDir` (the legacy flat-root layout). Invoked ONLY after the\n * cheap gates pass, so ineligible profiles never probe the default store.\n */\n defaultAtFlatRootProvider: () => Promise<boolean>;\n}): Promise<string | null> {\n const { profilePlan, config, principal } = options;\n // Defense-in-depth: principalSelfIsDefault is only ever true alongside a\n // flat-root default on legacy deployments (a hosted scope-profile principal\n // with its own policy fails this even when default is at the flat root). It\n // is NOT redundant — it keeps a policy-having principal isolated from the\n // flat default corpus.\n const principalSelfIsDefault =\n defaultNamespaceForPrincipal(principal, config) === config.defaultNamespace;\n // readOrder must explicitly intend userGlobal (resolveScopeProfilePlan\n // always materializes a userGlobal layer even when readOrder omits it, so\n // the layer's presence alone is not consent), AND that layer must have\n // resolved readable (a listed-but-unreadable layer is a deliberate\n // omission). serverShared maps to sharedNamespace, not the default.\n const profileIntendsUserGlobal =\n profilePlan.profile.readOrder.includes(\"userGlobal\");\n const userGlobalLayerReadable = profilePlan.layers.some(\n (layer) => layer.id === \"userGlobal\" && layer.readable && Boolean(layer.namespace),\n );\n const selfResolvedAwayFromDefault =\n profilePlan.baseNamespace !== config.defaultNamespace;\n if (\n !(\n profileIntendsUserGlobal &&\n userGlobalLayerReadable &&\n principalSelfIsDefault &&\n selfResolvedAwayFromDefault &&\n canReadNamespace(principal, config.defaultNamespace, config)\n )\n ) {\n // Cheap gates failed — never probe the default store.\n return null;\n }\n const defaultAtFlatRoot = await options.defaultAtFlatRootProvider();\n return defaultAtFlatRoot ? config.defaultNamespace : null;\n}\n\n/**\n * Issue #2018 r4: the explicit legacy-flat-root signal — the configured\n * default namespace's storage root equals `config.memoryDir`. Resolved from\n * storage (not inferred from config/policy shape) so a hosted scope-profile\n * deployment never reaches into the default corpus.\n */\nexport async function defaultNamespaceAtFlatRoot(\n getStorage: (namespace: string) => Promise<{ dir: string }>,\n config: { defaultNamespace: string; memoryDir: string },\n): Promise<boolean> {\n const storage = await getStorage(config.defaultNamespace);\n return storage.dir === config.memoryDir;\n}\n\n/**\n * Merge the {@link resolveMemorySearchDefaultFallback} result into a profile\n * read-namespace set without duplicates. Returns the original set unchanged\n * when no fallback applies.\n */\nexport function mergeMemorySearchDefaultFallback(\n profileNamespaces: string[],\n fallback: string | null,\n): string[] {\n if (!fallback || profileNamespaces.includes(fallback)) return profileNamespaces;\n return [...profileNamespaces, fallback];\n}\n\n\n/**\n * Run a memory_search namespace fanout with the observability required by\n * issue #2018: a silent empty fanout is surfaced as a warning (never silently\n * empty), and per-namespace backend degradations — which memory_search\n * previously swallowed — are collected and warned so a missing collection\n * looks different from a genuine no-matches.\n *\n * `search` is injected so this stays free of the orchestrator type.\n */\nexport async function runMemorySearchFanout<TResult>(options: {\n query: string;\n namespaces: string[];\n maxResults?: number;\n principal?: string;\n requestedNamespace?: string;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n search(params: {\n query: string;\n namespaces: string[];\n maxResults?: number;\n mode: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n execution: SearchExecutionOptions;\n }): Promise<TResult[]>;\n}): Promise<TResult[]> {\n const { query, namespaces, maxResults, principal, requestedNamespace, collection } = options;\n if (namespaces.length === 0) {\n log.warn(\"memory_search resolved zero readable namespaces\", {\n principal: principal ?? null,\n requestedNamespace: requestedNamespace ?? null,\n collection: collection ?? null,\n });\n return [];\n }\n log.debug(\"memory_search fanout\", {\n principal: principal ?? null,\n namespaces,\n collection: collection ?? null,\n });\n const degradations: SearchDegradation[] = [];\n const results = await options.search({\n query,\n namespaces,\n maxResults,\n mode: options.mode ?? \"search\",\n execution: {\n onDegradation: (degradation) => degradations.push(degradation),\n },\n });\n if (degradations.length > 0) {\n log.warn(\"memory_search backend degradation\", {\n principal: principal ?? null,\n degradations,\n });\n }\n return results;\n}\n\n/**\n * Resolve results for a FLAT corpus (namespaces disabled).\n *\n * An explicit ranking mode routes through the namespace-aware search even\n * here, because that is the only path honoring it; the legacy direct-QMD calls\n * stay the default so nothing else moves.\n */\n/**\n * Reject an option combination a flat corpus cannot honor.\n *\n * A `mode` routes through the namespace-aware backend, which cannot target a\n * specific collection, so pairing the two would silently search the default\n * collection and return unrelated results (AGENTS.md pattern 39).\n *\n * Exported as its own step because callers validate BEFORE any budget\n * short-circuit: changing only the requested result count must never make an\n * invalid request succeed.\n */\nexport const MEMORY_SEARCH_MODES = [\"search\", \"hybrid\", \"bm25\", \"vector\"] as const;\n\nexport type MemorySearchMode = (typeof MEMORY_SEARCH_MODES)[number];\n\n/**\n * Reject an unrecognized ranking mode at the service boundary.\n *\n * The HTTP and MCP schemas already validate their own input, but an\n * in-process caller reaches this service untyped. Both namespace backends\n * route an unknown mode through their DEFAULT ordinary-search branch, so a\n * typo would silently rank differently instead of reporting bad input\n * (AGENTS.md §39).\n */\n/**\n * Reject a budget that is not a finite non-negative integer.\n *\n * The HTTP and MCP schemas validate their own input, but an in-process caller\n * reaches this service untyped: a negative would hit the `budget <= 0`\n * short-circuit and return a successful EMPTY page, and a fraction or a\n * non-finite value would flow into the backend limit and top-up arithmetic.\n * Zero keeps its documented meaning — an empty result with no backend call.\n */\nexport function assertMemorySearchLimit(maxResults: unknown): void {\n if (maxResults === undefined) return;\n if (typeof maxResults !== \"number\" || !Number.isInteger(maxResults) || maxResults < 0) {\n throw new EngramAccessInputError(\n `maxResults must be a non-negative integer (got ${JSON.stringify(maxResults)})`,\n );\n }\n}\n\nexport function assertMemorySearchMode(mode: unknown): void {\n if (mode === undefined) return;\n if (typeof mode !== \"string\" || !(MEMORY_SEARCH_MODES as readonly string[]).includes(mode)) {\n throw new EngramAccessInputError(\n `mode must be one of ${MEMORY_SEARCH_MODES.join(\", \")} (got ${JSON.stringify(mode)})`,\n );\n }\n}\n\nexport function assertFlatCorpusOptions(\n mode: string | undefined,\n collection: string | undefined,\n): void {\n assertMemorySearchMode(mode);\n if (mode && collection) {\n throw new EngramAccessInputError(\n `mode is not supported together with collection on a flat corpus (got collection: ${collection})`,\n );\n }\n}\n\nexport async function runFlatCorpusMemorySearch<TResult>(options: {\n query: string;\n maxResults?: number;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n searchAcrossNamespaces(params: {\n query: string;\n maxResults?: number;\n mode: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n }): Promise<TResult[]>;\n searchGlobal(query: string, maxResults?: number): Promise<TResult[]>;\n search(query: string, collection: string | undefined, maxResults?: number): Promise<TResult[]>;\n}): Promise<TResult[]> {\n const { query, maxResults, collection, mode } = options;\n assertFlatCorpusOptions(options.mode, options.collection);\n if (mode) {\n // The mode-aware backend has no collection selector on a flat corpus, so\n // honoring the mode would silently search the default collection instead\n return options.searchAcrossNamespaces({ query, maxResults, mode });\n }\n return collection === \"global\"\n ? options.searchGlobal(query, maxResults)\n : options.search(query, collection, maxResults);\n}\n\n/**\n * How far one generic memory search may page before it stops.\n *\n * A backend-safety bound, NOT a stand-in for corpus exhaustion — which is why\n * it is ABSOLUTE rather than a multiple of the caller's budget. Scaling it to\n * the budget made \"the excluded paths happen to rank first\" indistinguishable\n * from \"there is nothing else\": a 1,000-row request whose first 4,000 hits are\n * artifacts stopped at 4,000 and returned an empty page while valid memories\n * sat at rank 4,001. Pages that come back FULL mean the corpus is not\n * exhausted, so the loop keeps going until a short page proves it is or this\n * cap protects the backend.\n */\nconst MEMORY_SEARCH_CANDIDATE_CAP = 25_000;\n\n\n/**\n * Run a ranked memory search and apply the generic-recall path exclusions\n * BEFORE the user-facing cap.\n *\n * Artifact isolation is a retrieval contract, not a ranking preference:\n * artifacts flow only through the dedicated verbatim path. Filtering after the\n * backend's own cap would let a handful of top-ranked artifacts shrink - or\n * empty - a page that has valid memories right behind them, so the search runs\n * with candidate headroom and tops up until the post-filter budget is met or\n * the corpus is exhausted.\n */\nexport async function searchWithGenericExclusion<TResult extends { path: string }>(options: {\n budget: number;\n /**\n * `false` omits `maxResults` on the FIRST request, so a caller that named no\n * budget keeps the backend's own page size on the wire; the resolved budget\n * is still what the filtered page is measured against.\n */\n sendInitialLimit: boolean;\n search(limit: number | undefined): Promise<TResult[]>;\n filterPrivate(results: TResult[]): Promise<TResult[]>;\n isExcluded(memoryPath: string): boolean;\n}): Promise<TResult[]> {\n const { budget } = options;\n if (budget <= 0) return [];\n let results: TResult[] = [];\n let limit: number | undefined = options.sendInitialLimit ? budget : undefined;\n // With no explicit limit the caller asked for the BACKEND's page, so that\n // page's size is the target — not the configured cap. Using the cap would\n // reissue the query whenever the backend's default page is smaller, and\n // return more rows than the same request used to.\n let target = budget;\n let firstPage = true;\n for (;;) {\n const raw = await options.search(limit);\n const eligible = raw.filter((hit) => !options.isExcluded(hit.path));\n results = await options.filterPrivate(eligible);\n if (firstPage) {\n firstPage = false;\n if (!options.sendInitialLimit) target = Math.min(budget, raw.length);\n if (target <= 0) return [];\n }\n if (results.length >= target) break;\n // What the backend actually served this round: its own page size when we\n // named no limit.\n const served = limit ?? raw.length;\n // A short page means the corpus is exhausted - asking for more is wasted\n // work that returns the same rows.\n if (raw.length === 0 || raw.length < served) break;\n // The cap protects the backend from an unbounded walk, but it can never\n // sit at or below what the caller explicitly asked for: a request for N\n // rows needs room BEYOND N to replace the excluded hits among them, or a\n // large search returns a short page for a count the operation deliberately\n // supports.\n const cap = Math.max(MEMORY_SEARCH_CANDIDATE_CAP, target * 2);\n if (served >= cap) break;\n limit = Math.min(served * 2, cap);\n }\n return results.slice(0, target);\n}\n\n/**\n * The whole ranked memory-search path behind `POST /engram/v1/memories/search`\n * and the `memory_search` tool: pick the flat-corpus or namespace-aware\n * backend, then apply generic-recall exclusions before the caller's cap.\n */\nexport async function runScopedMemorySearch(options: {\n query: string;\n budget: number;\n /** `false` when the caller named no `maxResults`; see the helper below. */\n sendInitialLimit: boolean;\n /**\n * Authorize the scope. Runs BEFORE any budget decision so a zero budget - a\n * valid empty search - can never skip the namespace/principal gate and turn\n * an access error into a successful empty result.\n */\n authorizeScope(): Promise<void> | void;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n namespacesEnabled: boolean;\n isExcluded(memoryPath: string): boolean;\n filterPrivate(\n results: Array<{ path: string; score: number; snippet?: string }>,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n flatCorpus(\n limit: number | undefined,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n namespaced(\n limit: number | undefined,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n}): Promise<Array<{ path: string; score: number; snippet: string }>> {\n await options.authorizeScope();\n const results = await searchWithGenericExclusion({\n budget: options.budget,\n sendInitialLimit: options.sendInitialLimit,\n isExcluded: options.isExcluded,\n filterPrivate: options.filterPrivate,\n search: (limit) =>\n options.namespacesEnabled ? options.namespaced(limit) : options.flatCorpus(limit),\n });\n return results.map((hit) => ({\n path: hit.path,\n score: hit.score,\n snippet: (hit.snippet ?? \"\").slice(0, 800),\n }));\n}\n\n/** Everything the ranked memory-search surface needs from the service. */\nexport interface ScopedMemorySearchDeps {\n namespacesEnabled: boolean;\n defaultBudget: number;\n memoryCollections: readonly string[];\n isExcluded(memoryPath: string): boolean;\n filterPrivate(\n results: Array<{ path: string; score: number; snippet?: string }>,\n namespaces: readonly string[],\n preserveUnresolved?: boolean,\n visibilityCache?: Map<string, boolean>,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n /** Flat-corpus authorization; throws when the namespace is unreadable. */\n authorizeFlatCorpus(namespace: string | undefined, principal: string | undefined): void;\n /** Namespace-aware authorization; throws, else returns the search fan-out. */\n authorizeNamespaces(\n namespace: string | undefined,\n principal: string | undefined,\n collection: string | undefined,\n ): Promise<string[]>;\n searchAcrossNamespaces(params: {\n query: string;\n namespaces?: string[];\n maxResults?: number;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n }): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n searchGlobal(\n query: string,\n maxResults?: number,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n search(\n query: string,\n collection?: string,\n maxResults?: number,\n ): Promise<Array<{ path: string; score: number; snippet?: string }>>;\n}\n\n/** The whole `memory_search` surface: validate, authorize, search, shape. */\nexport async function memorySearchThroughScope(\n deps: ScopedMemorySearchDeps,\n request: {\n query: string;\n namespace?: string;\n maxResults?: number;\n collection?: string;\n mode?: \"search\" | \"hybrid\" | \"bm25\" | \"vector\";\n principal?: string;\n },\n): Promise<{\n query: string;\n results: Array<{ path: string; score: number; snippet: string }>;\n count: number;\n}> {\n const { query, namespace, maxResults, mode, principal } = request;\n // BOTH branches take the mode, and only the flat one reaches\n // `assertFlatCorpusOptions` — validate here so a namespaced request cannot\n // rank differently on a typo, and before any budget short-circuit.\n assertMemorySearchMode(mode);\n assertMemorySearchLimit(maxResults);\n const collection = request.collection?.trim();\n if (request.collection !== undefined && !collection) {\n throw new EngramAccessInputError(\"collection must be a non-empty string\");\n }\n let searchNamespaces: string[] = [];\n const privateVisibilityCache = new Map<string, boolean>();\n const results = await runScopedMemorySearch({\n query, collection, mode,\n namespacesEnabled: deps.namespacesEnabled,\n isExcluded: deps.isExcluded,\n filterPrivate: (results) => deps.filterPrivate(\n results,\n searchNamespaces,\n !deps.namespacesEnabled &&\n collection !== undefined &&\n collection !== \"global\" &&\n !deps.memoryCollections.includes(collection),\n privateVisibilityCache,\n ),\n budget: maxResults ?? deps.defaultBudget,\n sendInitialLimit: maxResults !== undefined,\n authorizeScope: async () => {\n if (!deps.namespacesEnabled) {\n // Validate the option combination here too: a zero budget must not be\n // a way to slip an invalid request past the check.\n assertFlatCorpusOptions(mode, collection);\n return deps.authorizeFlatCorpus(namespace, principal);\n }\n searchNamespaces = await deps.authorizeNamespaces(namespace, principal, collection);\n },\n flatCorpus: (limit) =>\n runFlatCorpusMemorySearch({\n query, maxResults: limit, collection, mode,\n searchAcrossNamespaces: (p) => deps.searchAcrossNamespaces(p),\n searchGlobal: (q, globalLimit) => deps.searchGlobal(q, globalLimit),\n search: (q, coll, searchLimit) => deps.search(q, coll, searchLimit),\n }),\n namespaced: (limit) =>\n runMemorySearchFanout({\n query, maxResults: limit, principal, collection, mode,\n requestedNamespace: namespace,\n namespaces: searchNamespaces,\n search: (p) => deps.searchAcrossNamespaces(p),\n }),\n });\n return { query, results, count: results.length };\n}\n"],"mappings":"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","names":["results"]}
|
|
@@ -2,13 +2,16 @@ import {
|
|
|
2
2
|
RECONCILE_MANIFEST_FORMAT,
|
|
3
3
|
RECONCILE_MANIFEST_SCHEMA_VERSION,
|
|
4
4
|
buildReconcileManifestFile
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RBII7637.js";
|
|
6
6
|
import {
|
|
7
7
|
offlineSyncStorageForSnapshot
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NQMSEB5D.js";
|
|
9
|
+
import {
|
|
10
|
+
isSupportPassportPrivateMemory
|
|
11
|
+
} from "./chunk-HNDIND7P.js";
|
|
9
12
|
import {
|
|
10
13
|
iterateOfflineSyncSnapshotFileRecords
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-Z4N7SVVF.js";
|
|
12
15
|
|
|
13
16
|
// src/access-offline-manifest.ts
|
|
14
17
|
import * as nodePath from "path";
|
|
@@ -20,7 +23,11 @@ async function createOfflineSyncManifestStream(orchestrator, namespace, userExcl
|
|
|
20
23
|
includeTranscripts: options.includeTranscripts !== false,
|
|
21
24
|
readFileDigest: async ({ filePath }) => storage.digestOfflineSyncFile(filePath),
|
|
22
25
|
signal: options.signal,
|
|
23
|
-
userExcludeRegexps
|
|
26
|
+
userExcludeRegexps,
|
|
27
|
+
excludeFile: async ({ filePath }) => {
|
|
28
|
+
const memory = await storage.readMemoryByPath(filePath);
|
|
29
|
+
return memory ? isSupportPassportPrivateMemory(memory) : false;
|
|
30
|
+
}
|
|
24
31
|
});
|
|
25
32
|
return {
|
|
26
33
|
namespace,
|
|
@@ -42,4 +49,4 @@ async function createOfflineSyncManifestStream(orchestrator, namespace, userExcl
|
|
|
42
49
|
export {
|
|
43
50
|
createOfflineSyncManifestStream
|
|
44
51
|
};
|
|
45
|
-
//# sourceMappingURL=chunk-
|
|
52
|
+
//# sourceMappingURL=chunk-L7RTUEDG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/access-offline-manifest.ts"],"sourcesContent":["import * as nodePath from \"node:path\";\nimport type { Orchestrator } from \"./orchestrator.js\";\nimport { iterateOfflineSyncSnapshotFileRecords } from \"./offline-sync.js\";\nimport { offlineSyncStorageForSnapshot } from \"./offline-sync-impression-drain.js\";\nimport {\n buildReconcileManifestFile,\n RECONCILE_MANIFEST_FORMAT,\n RECONCILE_MANIFEST_SCHEMA_VERSION,\n type ReconcileManifest,\n type ReconcileManifestFile,\n type ReconcileMemoryParser,\n} from \"./reconcile/manifest.js\";\nimport { isSupportPassportPrivateMemory } from \"./support-passport/card-projection.js\";\n\nexport interface OfflineSyncManifestRequest {\n includeTranscripts?: boolean;\n signal?: AbortSignal;\n}\n\nexport interface OfflineSyncManifestStreamResponse extends Omit<ReconcileManifest, \"files\"> {\n namespace: string;\n files: AsyncIterable<ReconcileManifestFile>;\n}\n\nexport async function createOfflineSyncManifestStream(\n orchestrator: Orchestrator,\n namespace: string,\n userExcludeRegexps: RegExp[],\n options: OfflineSyncManifestRequest,\n parseMemory: ReconcileMemoryParser,\n): Promise<OfflineSyncManifestStreamResponse> {\n const storage = await offlineSyncStorageForSnapshot(orchestrator, namespace);\n const snapshotFiles = iterateOfflineSyncSnapshotFileRecords({\n root: storage.dir,\n includeContent: false,\n includeTranscripts: options.includeTranscripts !== false,\n readFileDigest: async ({ filePath }) => storage.digestOfflineSyncFile(filePath),\n signal: options.signal,\n userExcludeRegexps,\n excludeFile: async ({ filePath }) => {\n const memory = await storage.readMemoryByPath(filePath);\n return memory ? isSupportPassportPrivateMemory(memory) : false;\n },\n });\n return {\n namespace,\n format: RECONCILE_MANIFEST_FORMAT,\n schemaVersion: RECONCILE_MANIFEST_SCHEMA_VERSION,\n files: (async function* () {\n for await (const file of snapshotFiles) {\n yield await buildReconcileManifestFile(\n file,\n async ({ path }) => storage.readOfflineSyncFile(nodePath.join(storage.dir, path)),\n parseMemory,\n orchestrator.config.inlineSourceAttributionFormat,\n );\n }\n })(),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,YAAY,cAAc;AAwB1B,eAAsB,gCACpB,cACA,WACA,oBACA,SACA,aAC4C;AAC5C,QAAM,UAAU,MAAM,8BAA8B,cAAc,SAAS;AAC3E,QAAM,gBAAgB,sCAAsC;AAAA,IAC1D,MAAM,QAAQ;AAAA,IACd,gBAAgB;AAAA,IAChB,oBAAoB,QAAQ,uBAAuB;AAAA,IACnD,gBAAgB,OAAO,EAAE,SAAS,MAAM,QAAQ,sBAAsB,QAAQ;AAAA,IAC9E,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,aAAa,OAAO,EAAE,SAAS,MAAM;AACnC,YAAM,SAAS,MAAM,QAAQ,iBAAiB,QAAQ;AACtD,aAAO,SAAS,+BAA+B,MAAM,IAAI;AAAA,IAC3D;AAAA,EACF,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,QAAQ,mBAAmB;AACzB,uBAAiB,QAAQ,eAAe;AACtC,cAAM,MAAM;AAAA,UACV;AAAA,UACA,OAAO,EAAE,KAAK,MAAM,QAAQ,oBAA6B,cAAK,QAAQ,KAAK,IAAI,CAAC;AAAA,UAChF;AAAA,UACA,aAAa,OAAO;AAAA,QACtB;AAAA,MACF;AAAA,IACF,GAAG;AAAA,EACL;AACF;","names":[]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAGIC_HEADER_SIZE,
|
|
3
|
+
isEncryptedFile
|
|
4
|
+
} from "./chunk-XQ3HXAQH.js";
|
|
5
|
+
|
|
6
|
+
// src/offline-sync-file-io.ts
|
|
7
|
+
import { createHash } from "crypto";
|
|
8
|
+
import { createReadStream } from "fs";
|
|
9
|
+
import { open } from "fs/promises";
|
|
10
|
+
function throwIfAborted(signal) {
|
|
11
|
+
if (signal?.aborted) throw new Error("offline sync request aborted");
|
|
12
|
+
}
|
|
13
|
+
async function shouldExcludeOfflineSyncFile(excludeFile, target) {
|
|
14
|
+
return excludeFile ? await excludeFile(target) : false;
|
|
15
|
+
}
|
|
16
|
+
async function sha256OfflineSyncFile(filePath, signal) {
|
|
17
|
+
const hash = createHash("sha256");
|
|
18
|
+
let bytes = 0;
|
|
19
|
+
for await (const chunk of createReadStream(filePath)) {
|
|
20
|
+
throwIfAborted(signal);
|
|
21
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
22
|
+
hash.update(buffer);
|
|
23
|
+
bytes += buffer.length;
|
|
24
|
+
}
|
|
25
|
+
throwIfAborted(signal);
|
|
26
|
+
return { sha256: hash.digest("hex"), bytes };
|
|
27
|
+
}
|
|
28
|
+
async function isEncryptedOfflineSyncFile(filePath) {
|
|
29
|
+
const handle = await open(filePath, "r");
|
|
30
|
+
try {
|
|
31
|
+
const header = Buffer.alloc(MAGIC_HEADER_SIZE);
|
|
32
|
+
const { bytesRead } = await handle.read(header, 0, header.length, 0);
|
|
33
|
+
return bytesRead >= MAGIC_HEADER_SIZE && isEncryptedFile(header);
|
|
34
|
+
} finally {
|
|
35
|
+
await handle.close();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function readPlainOfflineSyncFileChunk(options) {
|
|
39
|
+
const chunkBytes = Math.min(options.length, options.bytes - options.offset);
|
|
40
|
+
const chunk = Buffer.alloc(chunkBytes);
|
|
41
|
+
if (chunkBytes === 0) return chunk;
|
|
42
|
+
const handle = await open(options.filePath, "r");
|
|
43
|
+
try {
|
|
44
|
+
const { bytesRead } = await handle.read(chunk, 0, chunk.length, options.offset);
|
|
45
|
+
return bytesRead === chunk.length ? chunk : chunk.subarray(0, bytesRead);
|
|
46
|
+
} finally {
|
|
47
|
+
await handle.close();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
shouldExcludeOfflineSyncFile,
|
|
53
|
+
sha256OfflineSyncFile,
|
|
54
|
+
isEncryptedOfflineSyncFile,
|
|
55
|
+
readPlainOfflineSyncFileChunk
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=chunk-LC2BDILZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/offline-sync-file-io.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createReadStream } from \"node:fs\";\nimport { open } from \"node:fs/promises\";\nimport { MAGIC_HEADER_SIZE, isEncryptedFile } from \"./secure-store/secure-fs.js\";\n\nexport interface OfflineSyncFileTarget {\n root: string;\n path: string;\n filePath: string;\n}\n\nexport type OfflineSyncExcludeFile = (target: OfflineSyncFileTarget) => boolean | Promise<boolean>;\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error(\"offline sync request aborted\");\n}\n\nexport async function shouldExcludeOfflineSyncFile(\n excludeFile: OfflineSyncExcludeFile | undefined,\n target: OfflineSyncFileTarget\n): Promise<boolean> {\n return excludeFile ? await excludeFile(target) : false;\n}\n\nexport async function sha256OfflineSyncFile(\n filePath: string,\n signal?: AbortSignal\n): Promise<{ sha256: string; bytes: number }> {\n const hash = createHash(\"sha256\");\n let bytes = 0;\n for await (const chunk of createReadStream(filePath)) {\n throwIfAborted(signal);\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n hash.update(buffer);\n bytes += buffer.length;\n }\n throwIfAborted(signal);\n return { sha256: hash.digest(\"hex\"), bytes };\n}\n\nexport async function isEncryptedOfflineSyncFile(filePath: string): Promise<boolean> {\n const handle = await open(filePath, \"r\");\n try {\n const header = Buffer.alloc(MAGIC_HEADER_SIZE);\n const { bytesRead } = await handle.read(header, 0, header.length, 0);\n return bytesRead >= MAGIC_HEADER_SIZE && isEncryptedFile(header);\n } finally {\n await handle.close();\n }\n}\n\nexport async function readPlainOfflineSyncFileChunk(options: {\n filePath: string;\n offset: number;\n length: number;\n bytes: number;\n}): Promise<Buffer> {\n const chunkBytes = Math.min(options.length, options.bytes - options.offset);\n const chunk = Buffer.alloc(chunkBytes);\n if (chunkBytes === 0) return chunk;\n const handle = await open(options.filePath, \"r\");\n try {\n const { bytesRead } = await handle.read(chunk, 0, chunk.length, options.offset);\n return bytesRead === chunk.length ? chunk : chunk.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,wBAAwB;AACjC,SAAS,YAAY;AAWrB,SAAS,eAAe,QAAuC;AAC7D,MAAI,QAAQ,QAAS,OAAM,IAAI,MAAM,8BAA8B;AACrE;AAEA,eAAsB,6BACpB,aACA,QACkB;AAClB,SAAO,cAAc,MAAM,YAAY,MAAM,IAAI;AACnD;AAEA,eAAsB,sBACpB,UACA,QAC4C;AAC5C,QAAM,OAAO,WAAW,QAAQ;AAChC,MAAI,QAAQ;AACZ,mBAAiB,SAAS,iBAAiB,QAAQ,GAAG;AACpD,mBAAe,MAAM;AACrB,UAAM,SAAS,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK;AACjE,SAAK,OAAO,MAAM;AAClB,aAAS,OAAO;AAAA,EAClB;AACA,iBAAe,MAAM;AACrB,SAAO,EAAE,QAAQ,KAAK,OAAO,KAAK,GAAG,MAAM;AAC7C;AAEA,eAAsB,2BAA2B,UAAoC;AACnF,QAAM,SAAS,MAAM,KAAK,UAAU,GAAG;AACvC,MAAI;AACF,UAAM,SAAS,OAAO,MAAM,iBAAiB;AAC7C,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,KAAK,QAAQ,GAAG,OAAO,QAAQ,CAAC;AACnE,WAAO,aAAa,qBAAqB,gBAAgB,MAAM;AAAA,EACjE,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;AAEA,eAAsB,8BAA8B,SAKhC;AAClB,QAAM,aAAa,KAAK,IAAI,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;AAC1E,QAAM,QAAQ,OAAO,MAAM,UAAU;AACrC,MAAI,eAAe,EAAG,QAAO;AAC7B,QAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,GAAG;AAC/C,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,KAAK,OAAO,GAAG,MAAM,QAAQ,QAAQ,MAAM;AAC9E,WAAO,cAAc,MAAM,SAAS,QAAQ,MAAM,SAAS,GAAG,SAAS;AAAA,EACzE,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;","names":[]}
|