@remnic/core 9.54.4 → 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
|
@@ -7,22 +7,22 @@ import {
|
|
|
7
7
|
syncWearableSource,
|
|
8
8
|
wearableSourceLabel
|
|
9
9
|
} from "./chunk-HOIRB77A.js";
|
|
10
|
+
import {
|
|
11
|
+
runPeerProfileReasoner
|
|
12
|
+
} from "./chunk-I74SUMNI.js";
|
|
10
13
|
import {
|
|
11
14
|
CompressionGuidelineCoordinator
|
|
12
15
|
} from "./chunk-6LKEUCEM.js";
|
|
13
16
|
import {
|
|
14
17
|
ActivityStore,
|
|
15
18
|
MaintenanceScheduler
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import {
|
|
18
|
-
runPeerProfileReasoner
|
|
19
|
-
} from "./chunk-I74SUMNI.js";
|
|
19
|
+
} from "./chunk-3OTSVCPK.js";
|
|
20
20
|
import {
|
|
21
21
|
migrateFromEngram
|
|
22
22
|
} from "./chunk-7HYPN2GC.js";
|
|
23
23
|
import {
|
|
24
24
|
LcmEngine
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-6NNNURLK.js";
|
|
26
26
|
import {
|
|
27
27
|
chunkTranscriptEntries
|
|
28
28
|
} from "./chunk-TECVW3JP.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
import {
|
|
33
33
|
CompoundingEngine,
|
|
34
34
|
defaultTierMigrationCycleBudget
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-F2Y6I74C.js";
|
|
36
36
|
import {
|
|
37
37
|
SharedContextManager
|
|
38
38
|
} from "./chunk-M23RF3LJ.js";
|
|
@@ -41,6 +41,9 @@ import {
|
|
|
41
41
|
applyUtilityRankingRuntimeDelta,
|
|
42
42
|
loadUtilityRuntimeValues
|
|
43
43
|
} from "./chunk-SEF4IOFE.js";
|
|
44
|
+
import {
|
|
45
|
+
decideTierTransition
|
|
46
|
+
} from "./chunk-6LEIWWAF.js";
|
|
44
47
|
import {
|
|
45
48
|
TmtBuilder
|
|
46
49
|
} from "./chunk-ECZU5BJH.js";
|
|
@@ -56,9 +59,6 @@ import {
|
|
|
56
59
|
import {
|
|
57
60
|
TierMigrationExecutor
|
|
58
61
|
} from "./chunk-YRFAYOFQ.js";
|
|
59
|
-
import {
|
|
60
|
-
decideTierTransition
|
|
61
|
-
} from "./chunk-S75M5ZRK.js";
|
|
62
62
|
import {
|
|
63
63
|
HourlySummarizer
|
|
64
64
|
} from "./chunk-LLJMAULE.js";
|
|
@@ -69,18 +69,6 @@ import {
|
|
|
69
69
|
import {
|
|
70
70
|
SessionObserverState
|
|
71
71
|
} from "./chunk-QFN4U7AG.js";
|
|
72
|
-
import {
|
|
73
|
-
applyRuntimeRetrievalPolicy
|
|
74
|
-
} from "./chunk-5IZL4DCV.js";
|
|
75
|
-
import {
|
|
76
|
-
RerankCache,
|
|
77
|
-
reorderByRankedKeys,
|
|
78
|
-
rerankLocalOrNoop
|
|
79
|
-
} from "./chunk-LUDXM7MD.js";
|
|
80
|
-
import {
|
|
81
|
-
buildResponseGuidanceRecallSection,
|
|
82
|
-
shouldRecallResponseGuidance
|
|
83
|
-
} from "./chunk-6TCXIRP5.js";
|
|
84
72
|
import {
|
|
85
73
|
mergeWithAgentResults,
|
|
86
74
|
runDirectAgent,
|
|
@@ -103,11 +91,23 @@ import {
|
|
|
103
91
|
resolvePromptTagPrefilterAsync
|
|
104
92
|
} from "./chunk-ZOKZIR5K.js";
|
|
105
93
|
import {
|
|
106
|
-
|
|
107
|
-
} from "./chunk-
|
|
94
|
+
applyRuntimeRetrievalPolicy
|
|
95
|
+
} from "./chunk-5IZL4DCV.js";
|
|
108
96
|
import {
|
|
109
97
|
RelevanceStore
|
|
110
98
|
} from "./chunk-KATEWTQJ.js";
|
|
99
|
+
import {
|
|
100
|
+
RerankCache,
|
|
101
|
+
reorderByRankedKeys,
|
|
102
|
+
rerankLocalOrNoop
|
|
103
|
+
} from "./chunk-LUDXM7MD.js";
|
|
104
|
+
import {
|
|
105
|
+
buildResponseGuidanceRecallSection,
|
|
106
|
+
shouldRecallResponseGuidance
|
|
107
|
+
} from "./chunk-6TCXIRP5.js";
|
|
108
|
+
import {
|
|
109
|
+
findUnresolvedEntityRefs
|
|
110
|
+
} from "./chunk-X7XN6YU4.js";
|
|
111
111
|
import {
|
|
112
112
|
dedupeResultsByNamespace,
|
|
113
113
|
hasMemoryForResult,
|
|
@@ -163,7 +163,7 @@ import {
|
|
|
163
163
|
} from "./chunk-OH7QLI6W.js";
|
|
164
164
|
import {
|
|
165
165
|
ExtractionEngine
|
|
166
|
-
} from "./chunk-
|
|
166
|
+
} from "./chunk-XKAR4RF6.js";
|
|
167
167
|
import {
|
|
168
168
|
parseMemoryActionEligibilityContext
|
|
169
169
|
} from "./chunk-WFLBP5GC.js";
|
|
@@ -190,7 +190,7 @@ import {
|
|
|
190
190
|
buildEntityRecallSection,
|
|
191
191
|
entityRecentTranscriptLookbackHours,
|
|
192
192
|
readRecentEntityTranscriptEntries
|
|
193
|
-
} from "./chunk-
|
|
193
|
+
} from "./chunk-XSV2YDCJ.js";
|
|
194
194
|
import {
|
|
195
195
|
buildEventOrderRecallSection,
|
|
196
196
|
shouldRecallEventOrderEvidence
|
|
@@ -207,7 +207,7 @@ import {
|
|
|
207
207
|
} from "./chunk-YK6BDDHP.js";
|
|
208
208
|
import {
|
|
209
209
|
refreshConvergedNamespaces
|
|
210
|
-
} from "./chunk-
|
|
210
|
+
} from "./chunk-5JKUSBMX.js";
|
|
211
211
|
import {
|
|
212
212
|
formatDaySummaryMemories
|
|
213
213
|
} from "./chunk-RPSYDM6C.js";
|
|
@@ -216,7 +216,7 @@ import {
|
|
|
216
216
|
ensureBuiltInWearableConnectors,
|
|
217
217
|
getWearableConnector,
|
|
218
218
|
listWearableConnectors
|
|
219
|
-
} from "./chunk-
|
|
219
|
+
} from "./chunk-LC3FD4BV.js";
|
|
220
220
|
import {
|
|
221
221
|
normalizeReplaySessionKey
|
|
222
222
|
} from "./chunk-AJVLDJU6.js";
|
|
@@ -226,34 +226,34 @@ import {
|
|
|
226
226
|
import {
|
|
227
227
|
compareVerifiedEpisodeResults,
|
|
228
228
|
searchVerifiedEpisodes
|
|
229
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-YKZLOPTC.js";
|
|
230
230
|
import {
|
|
231
231
|
ThreadingManager
|
|
232
232
|
} from "./chunk-OJP3N7UK.js";
|
|
233
233
|
import {
|
|
234
234
|
compareVerifiedSemanticRuleResults,
|
|
235
235
|
searchVerifiedSemanticRules
|
|
236
|
-
} from "./chunk-
|
|
236
|
+
} from "./chunk-6PZGKGAO.js";
|
|
237
237
|
import {
|
|
238
238
|
searchWorkProductLedgerEntries
|
|
239
239
|
} from "./chunk-WT6UL3UA.js";
|
|
240
240
|
import {
|
|
241
241
|
TranscriptManager
|
|
242
242
|
} from "./chunk-HYQJQGD3.js";
|
|
243
|
-
import {
|
|
244
|
-
PolicyRuntimeManager
|
|
245
|
-
} from "./chunk-33JBK2XP.js";
|
|
246
243
|
import {
|
|
247
244
|
runNamespaceMaintenanceFanout,
|
|
248
245
|
summarizeNamespaceMaintenanceHealth
|
|
249
|
-
} from "./chunk-
|
|
246
|
+
} from "./chunk-CLKBV3Z3.js";
|
|
250
247
|
import {
|
|
251
248
|
NamespaceCatalog,
|
|
252
249
|
planNamespaceMaintenance
|
|
253
|
-
} from "./chunk-
|
|
250
|
+
} from "./chunk-VYQ4URQB.js";
|
|
251
|
+
import {
|
|
252
|
+
PolicyRuntimeManager
|
|
253
|
+
} from "./chunk-33JBK2XP.js";
|
|
254
254
|
import {
|
|
255
255
|
searchHarmonicRetrieval
|
|
256
|
-
} from "./chunk-
|
|
256
|
+
} from "./chunk-B4T3FQMT.js";
|
|
257
257
|
import {
|
|
258
258
|
isAboveImportanceThreshold,
|
|
259
259
|
scoreImportance
|
|
@@ -285,7 +285,7 @@ import {
|
|
|
285
285
|
materializeAfterSemanticConsolidation,
|
|
286
286
|
parseConsolidationResponse,
|
|
287
287
|
parseOperatorAwareConsolidationResponse
|
|
288
|
-
} from "./chunk-
|
|
288
|
+
} from "./chunk-QOQQMVR2.js";
|
|
289
289
|
import {
|
|
290
290
|
BootstrapEngine
|
|
291
291
|
} from "./chunk-MYVPLJOO.js";
|
|
@@ -300,13 +300,13 @@ import {
|
|
|
300
300
|
} from "./chunk-UQ7RN5HK.js";
|
|
301
301
|
import {
|
|
302
302
|
SmartBuffer
|
|
303
|
-
} from "./chunk-
|
|
303
|
+
} from "./chunk-CUTFSLSD.js";
|
|
304
304
|
import {
|
|
305
305
|
isDisagreementPrompt
|
|
306
306
|
} from "./chunk-H3FZVNRN.js";
|
|
307
307
|
import {
|
|
308
308
|
detectMeetings
|
|
309
|
-
} from "./chunk-
|
|
309
|
+
} from "./chunk-2INWPC6E.js";
|
|
310
310
|
import {
|
|
311
311
|
compileCorrectionRule,
|
|
312
312
|
correctionsFilePath,
|
|
@@ -331,7 +331,7 @@ import {
|
|
|
331
331
|
} from "./chunk-KD3QD3A5.js";
|
|
332
332
|
import {
|
|
333
333
|
MeetingsInputError
|
|
334
|
-
} from "./chunk-
|
|
334
|
+
} from "./chunk-DDA55IVB.js";
|
|
335
335
|
import {
|
|
336
336
|
WearablesInputError,
|
|
337
337
|
describeErrorForOperator
|
|
@@ -352,13 +352,17 @@ import {
|
|
|
352
352
|
filterRecallCandidates,
|
|
353
353
|
isGenericRecallExcludedPath,
|
|
354
354
|
isTopLevelArchivePath
|
|
355
|
-
} from "./chunk-
|
|
355
|
+
} from "./chunk-3WGI7F4V.js";
|
|
356
356
|
import {
|
|
357
357
|
importCapsule
|
|
358
358
|
} from "./chunk-QYN5PXDZ.js";
|
|
359
359
|
import {
|
|
360
360
|
isCodingGraphInstalled
|
|
361
361
|
} from "./chunk-VQ7B2Q6M.js";
|
|
362
|
+
import {
|
|
363
|
+
listTrustZoneRecords,
|
|
364
|
+
searchTrustZoneRecords
|
|
365
|
+
} from "./chunk-KQS72GCR.js";
|
|
362
366
|
import {
|
|
363
367
|
applyTrustScoreStage,
|
|
364
368
|
buildTrustSignalsForRerank,
|
|
@@ -366,10 +370,6 @@ import {
|
|
|
366
370
|
trustResultFor,
|
|
367
371
|
trustResultKey
|
|
368
372
|
} from "./chunk-GNVZQ5JN.js";
|
|
369
|
-
import {
|
|
370
|
-
listTrustZoneRecords,
|
|
371
|
-
searchTrustZoneRecords
|
|
372
|
-
} from "./chunk-KQS72GCR.js";
|
|
373
373
|
import {
|
|
374
374
|
boundRecallContextComposition,
|
|
375
375
|
composeRecallContext,
|
|
@@ -379,7 +379,7 @@ import {
|
|
|
379
379
|
} from "./chunk-AHVEHR6K.js";
|
|
380
380
|
import {
|
|
381
381
|
buildXraySnapshot
|
|
382
|
-
} from "./chunk-
|
|
382
|
+
} from "./chunk-QPAQSG3F.js";
|
|
383
383
|
import {
|
|
384
384
|
objectiveStateStoreOverrideForNamespace,
|
|
385
385
|
searchObjectiveStateSnapshots
|
|
@@ -396,25 +396,10 @@ import {
|
|
|
396
396
|
} from "./chunk-QDW3E4RD.js";
|
|
397
397
|
import {
|
|
398
398
|
shouldSkipImplicitExtraction
|
|
399
|
-
} from "./chunk-
|
|
400
|
-
import {
|
|
401
|
-
NamespaceSearchRouter,
|
|
402
|
-
namespaceCollectionName
|
|
403
|
-
} from "./chunk-VMMQDEN5.js";
|
|
404
|
-
import {
|
|
405
|
-
createConversationIndexRuntime,
|
|
406
|
-
createSearchBackend,
|
|
407
|
-
isSearchBackendEnabled
|
|
408
|
-
} from "./chunk-AZM4CCAC.js";
|
|
409
|
-
import {
|
|
410
|
-
NoopSearchBackend
|
|
411
|
-
} from "./chunk-CYEPCZN5.js";
|
|
399
|
+
} from "./chunk-MRGJZ75M.js";
|
|
412
400
|
import {
|
|
413
401
|
NamespaceStorageRouter
|
|
414
|
-
} from "./chunk-
|
|
415
|
-
import {
|
|
416
|
-
writeConversationChunks
|
|
417
|
-
} from "./chunk-72A4L5JJ.js";
|
|
402
|
+
} from "./chunk-VZRFGJCH.js";
|
|
418
403
|
import {
|
|
419
404
|
searchCausalTrajectories
|
|
420
405
|
} from "./chunk-AHZOMHWN.js";
|
|
@@ -423,7 +408,7 @@ import {
|
|
|
423
408
|
} from "./chunk-BIJAWZYW.js";
|
|
424
409
|
import {
|
|
425
410
|
buildChainFollowupGenerator
|
|
426
|
-
} from "./chunk-
|
|
411
|
+
} from "./chunk-UYOXFFEY.js";
|
|
427
412
|
import {
|
|
428
413
|
QmdResultResolver,
|
|
429
414
|
StorageManager,
|
|
@@ -442,7 +427,7 @@ import {
|
|
|
442
427
|
reconstructFusionInputs,
|
|
443
428
|
selfDeps,
|
|
444
429
|
serializeFusionDay
|
|
445
|
-
} from "./chunk-
|
|
430
|
+
} from "./chunk-QQXWFDRD.js";
|
|
446
431
|
import {
|
|
447
432
|
bodyIsEscaped,
|
|
448
433
|
decodeTranscriptBody,
|
|
@@ -456,6 +441,21 @@ import {
|
|
|
456
441
|
import {
|
|
457
442
|
buildRetiredFactTombstoneInputs
|
|
458
443
|
} from "./chunk-MLPTFODV.js";
|
|
444
|
+
import {
|
|
445
|
+
NamespaceSearchRouter,
|
|
446
|
+
namespaceCollectionName
|
|
447
|
+
} from "./chunk-DN2IDKI5.js";
|
|
448
|
+
import {
|
|
449
|
+
createConversationIndexRuntime,
|
|
450
|
+
createSearchBackend,
|
|
451
|
+
isSearchBackendEnabled
|
|
452
|
+
} from "./chunk-UWJNKGBA.js";
|
|
453
|
+
import {
|
|
454
|
+
NoopSearchBackend
|
|
455
|
+
} from "./chunk-CYEPCZN5.js";
|
|
456
|
+
import {
|
|
457
|
+
writeConversationChunks
|
|
458
|
+
} from "./chunk-72A4L5JJ.js";
|
|
459
459
|
import {
|
|
460
460
|
canRecallToolScopedMemory,
|
|
461
461
|
shouldPromoteGlobalFactToShared,
|
|
@@ -463,7 +463,7 @@ import {
|
|
|
463
463
|
} from "./chunk-S2V7RUCL.js";
|
|
464
464
|
import {
|
|
465
465
|
confidenceTier
|
|
466
|
-
} from "./chunk-
|
|
466
|
+
} from "./chunk-BIF4SHMD.js";
|
|
467
467
|
import {
|
|
468
468
|
lintWorkspaceFiles,
|
|
469
469
|
rotateMarkdownFileToArchive
|
|
@@ -554,7 +554,7 @@ import {
|
|
|
554
554
|
throwIfRecallAborted,
|
|
555
555
|
tokenizeRecallQuery,
|
|
556
556
|
utcDateKeysForLocalDay
|
|
557
|
-
} from "./chunk-
|
|
557
|
+
} from "./chunk-N7CXQ5TA.js";
|
|
558
558
|
import {
|
|
559
559
|
inferIntentFromText,
|
|
560
560
|
intentCompatibilityScore,
|
|
@@ -597,17 +597,12 @@ import {
|
|
|
597
597
|
getConfiguredNamespaces,
|
|
598
598
|
resolveNamespaceFromStorageDir,
|
|
599
599
|
resolveScopePlan
|
|
600
|
-
} from "./chunk-
|
|
600
|
+
} from "./chunk-6QPI2ITJ.js";
|
|
601
601
|
import {
|
|
602
602
|
attachCitation,
|
|
603
603
|
hasCitationForTemplate,
|
|
604
604
|
stripCitationForTemplate
|
|
605
605
|
} from "./chunk-J6A3CX5N.js";
|
|
606
|
-
import {
|
|
607
|
-
namespaceIdentityFromToken,
|
|
608
|
-
namespaceIdentityToken,
|
|
609
|
-
normalizeNamespaceIdentity
|
|
610
|
-
} from "./chunk-EGGE52UU.js";
|
|
611
606
|
import {
|
|
612
607
|
assertPathInsideRoot
|
|
613
608
|
} from "./chunk-GCASGYIO.js";
|
|
@@ -619,6 +614,34 @@ import {
|
|
|
619
614
|
import {
|
|
620
615
|
ContentHashIndex
|
|
621
616
|
} from "./chunk-CX5SW2EW.js";
|
|
617
|
+
import {
|
|
618
|
+
serializeMutations,
|
|
619
|
+
withHeldFileLock
|
|
620
|
+
} from "./chunk-FFAZ2XFU.js";
|
|
621
|
+
import {
|
|
622
|
+
SUPPORT_PASSPORT_ATTRIBUTE_KEYS,
|
|
623
|
+
SUPPORT_PASSPORT_CARD_TAG,
|
|
624
|
+
SupportPassportCardCategorySchema,
|
|
625
|
+
SupportPassportCardListSchema,
|
|
626
|
+
SupportPassportCardMutationInputSchema,
|
|
627
|
+
SupportPassportListCardsInputSchema,
|
|
628
|
+
SupportPassportManualDraftInputSchema,
|
|
629
|
+
SupportPassportMemoryIdSchema,
|
|
630
|
+
SupportPassportNamespaceSchema,
|
|
631
|
+
SupportPassportReplaceCardInputSchema,
|
|
632
|
+
computeSupportPassportCardRevision,
|
|
633
|
+
computeSupportPassportOwnerKey,
|
|
634
|
+
decodeSupportPassportNamespaceAttributes,
|
|
635
|
+
encodeSupportPassportNamespaceAttributes,
|
|
636
|
+
excludeSupportPassportPrivateMemories,
|
|
637
|
+
isSupportPassportPrivateMemory,
|
|
638
|
+
projectSupportPassportCard
|
|
639
|
+
} from "./chunk-HNDIND7P.js";
|
|
640
|
+
import {
|
|
641
|
+
namespaceIdentityFromToken,
|
|
642
|
+
namespaceIdentityToken,
|
|
643
|
+
normalizeNamespaceIdentity
|
|
644
|
+
} from "./chunk-EGGE52UU.js";
|
|
622
645
|
import {
|
|
623
646
|
normalizeAttributePairs,
|
|
624
647
|
stripAttributesSuffix
|
|
@@ -636,6 +659,9 @@ import {
|
|
|
636
659
|
assertIsDirectoryNotSymlink,
|
|
637
660
|
assertRealpathInsideRoot
|
|
638
661
|
} from "./chunk-GCGJW34D.js";
|
|
662
|
+
import {
|
|
663
|
+
parseIsoOffsetTimestamp
|
|
664
|
+
} from "./chunk-P7FMDTKL.js";
|
|
639
665
|
import {
|
|
640
666
|
canReadNamespace,
|
|
641
667
|
defaultNamespaceForPrincipal,
|
|
@@ -811,23 +837,12 @@ var TierMigrationCoordinator = class {
|
|
|
811
837
|
);
|
|
812
838
|
this.inFlight = true;
|
|
813
839
|
try {
|
|
814
|
-
const coldStorage = this.deps.createColdStorage(
|
|
815
|
-
|
|
816
|
-
);
|
|
817
|
-
const [hotMemories, coldMemories] = await Promise.all([
|
|
818
|
-
storage.readAllMemories(),
|
|
819
|
-
coldStorage.readAllMemories()
|
|
820
|
-
]);
|
|
840
|
+
const coldStorage = this.deps.createColdStorage(path.join(storage.dir, "cold"));
|
|
841
|
+
const [hotMemories, coldMemories] = await Promise.all([storage.readAllMemories(), coldStorage.readAllMemories()]);
|
|
821
842
|
const now = /* @__PURE__ */ new Date();
|
|
822
843
|
const scanLimit = Math.max(0, Math.floor(budget.scanLimit));
|
|
823
|
-
const hotScanLimit = Math.min(
|
|
824
|
-
|
|
825
|
-
Math.ceil(scanLimit * 0.75)
|
|
826
|
-
);
|
|
827
|
-
const coldScanLimit = Math.min(
|
|
828
|
-
coldMemories.length,
|
|
829
|
-
Math.max(0, scanLimit - hotScanLimit)
|
|
830
|
-
);
|
|
844
|
+
const hotScanLimit = Math.min(hotMemories.length, Math.ceil(scanLimit * 0.75));
|
|
845
|
+
const coldScanLimit = Math.min(coldMemories.length, Math.max(0, scanLimit - hotScanLimit));
|
|
831
846
|
const toTimestamp = (memory) => Date.parse(memory.frontmatter.updated ?? memory.frontmatter.created);
|
|
832
847
|
const hotCandidates = hotMemories.map((memory) => ({ memory, tier: "hot" })).sort((a, b) => toTimestamp(a.memory) - toTimestamp(b.memory)).slice(0, hotScanLimit);
|
|
833
848
|
const coldCandidates = coldMemories.map((memory) => ({ memory, tier: "cold" })).sort((a, b) => toTimestamp(b.memory) - toTimestamp(a.memory)).slice(0, coldScanLimit);
|
|
@@ -844,12 +859,7 @@ var TierMigrationCoordinator = class {
|
|
|
844
859
|
let demoted = 0;
|
|
845
860
|
for (const candidate of candidates) {
|
|
846
861
|
if (migrated >= limit) break;
|
|
847
|
-
const decision = decideTierTransition(
|
|
848
|
-
candidate.memory,
|
|
849
|
-
candidate.tier,
|
|
850
|
-
policy,
|
|
851
|
-
now
|
|
852
|
-
);
|
|
862
|
+
const decision = decideTierTransition(candidate.memory, candidate.tier, policy, now);
|
|
853
863
|
if (!decision.changed) continue;
|
|
854
864
|
if (!dryRun) {
|
|
855
865
|
const res = await migration.migrateMemory({
|
|
@@ -1067,7 +1077,7 @@ var SemanticConsolidationCoordinator = class {
|
|
|
1067
1077
|
return result;
|
|
1068
1078
|
}
|
|
1069
1079
|
log.info("[semantic-consolidation] starting run");
|
|
1070
|
-
const allMemories = await targetStorage.readAllMemories();
|
|
1080
|
+
const allMemories = excludeSupportPassportPrivateMemories(await targetStorage.readAllMemories());
|
|
1071
1081
|
if (allMemories.length < 10) {
|
|
1072
1082
|
log.debug("[semantic-consolidation] too few memories, skipping");
|
|
1073
1083
|
return result;
|
|
@@ -1415,7 +1425,7 @@ var LifecyclePolicyCoordinator = class {
|
|
|
1415
1425
|
protectedCategories: this.config.lifecycleProtectedCategories
|
|
1416
1426
|
};
|
|
1417
1427
|
const actionPriors = await this.buildLifecycleActionPriors(storage);
|
|
1418
|
-
for (const memory of allMemories) {
|
|
1428
|
+
for (const memory of excludeSupportPassportPrivateMemories(allMemories)) {
|
|
1419
1429
|
if (memory.frontmatter.status === "superseded" || memory.frontmatter.status === "forgotten") {
|
|
1420
1430
|
continue;
|
|
1421
1431
|
}
|
|
@@ -2226,7 +2236,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
|
|
|
2226
2236
|
namespaces,
|
|
2227
2237
|
{
|
|
2228
2238
|
readNamespaceMemories: async (ns) => (await this.getStorage(ns)).readAllMemories(),
|
|
2229
|
-
readMemoryFrontmatter: async (
|
|
2239
|
+
readMemoryFrontmatter: async (path38, preferredNamespace) => {
|
|
2230
2240
|
if (!fallbackReader) {
|
|
2231
2241
|
for (const ns of namespaces) {
|
|
2232
2242
|
try {
|
|
@@ -2238,7 +2248,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
|
|
|
2238
2248
|
}
|
|
2239
2249
|
if (!fallbackReader) return null;
|
|
2240
2250
|
const memory = await this.readQmdResultMemory(
|
|
2241
|
-
|
|
2251
|
+
path38,
|
|
2242
2252
|
fallbackReader,
|
|
2243
2253
|
namespaces,
|
|
2244
2254
|
preferredNamespace
|
|
@@ -3107,6 +3117,7 @@ var ContradictionLinkingCoordinator = class {
|
|
|
3107
3117
|
const resultStorage2 = await this.storageForNamespace(resultNamespace);
|
|
3108
3118
|
const existingMemory = await resultStorage2.getMemoryById(memoryId);
|
|
3109
3119
|
if (!existingMemory) continue;
|
|
3120
|
+
if (isSupportPassportPrivateMemory(existingMemory)) continue;
|
|
3110
3121
|
if (inferLocalizationMemoryStatus(existingMemory, resultStorage2.dir) !== "active") continue;
|
|
3111
3122
|
const verification = await this.getExtraction().verifyContradiction(
|
|
3112
3123
|
{ content, category },
|
|
@@ -3156,6 +3167,9 @@ var ContradictionLinkingCoordinator = class {
|
|
|
3156
3167
|
]);
|
|
3157
3168
|
anchorSnapshot = mergeMemorySnapshots(hot, cold, resultStorage.dir);
|
|
3158
3169
|
}
|
|
3170
|
+
anchorSnapshot = anchorSnapshot?.filter(
|
|
3171
|
+
(memory) => !isSupportPassportPrivateMemory(memory)
|
|
3172
|
+
);
|
|
3159
3173
|
const anchorMemoryById = anchorSnapshot ? new Map(anchorSnapshot.map((memory) => [memory.frontmatter.id, memory])) : void 0;
|
|
3160
3174
|
const candidates = await localizeUpdateCandidates(
|
|
3161
3175
|
{
|
|
@@ -3182,7 +3196,7 @@ var ContradictionLinkingCoordinator = class {
|
|
|
3182
3196
|
if (!memoryId) continue;
|
|
3183
3197
|
if (this.namespaceFromPath(result.path) !== namespaceScope) continue;
|
|
3184
3198
|
const existingMemory = anchorMemoryById?.get(memoryId) ?? await resultStorage.getMemoryById(memoryId);
|
|
3185
|
-
if (!existingMemory || inferLocalizationMemoryStatus(existingMemory, resultStorage.dir) !== "active") continue;
|
|
3199
|
+
if (!existingMemory || isSupportPassportPrivateMemory(existingMemory) || inferLocalizationMemoryStatus(existingMemory, resultStorage.dir) !== "active") continue;
|
|
3186
3200
|
hits.push({
|
|
3187
3201
|
id: memoryId,
|
|
3188
3202
|
content: existingMemory.content,
|
|
@@ -3203,7 +3217,7 @@ var ContradictionLinkingCoordinator = class {
|
|
|
3203
3217
|
);
|
|
3204
3218
|
for (const candidate of candidates) {
|
|
3205
3219
|
const existingMemory = anchorMemoryById?.get(candidate.id) ?? await resultStorage.getMemoryById(candidate.id);
|
|
3206
|
-
if (!existingMemory || inferLocalizationMemoryStatus(existingMemory, resultStorage.dir) !== "active") continue;
|
|
3220
|
+
if (!existingMemory || isSupportPassportPrivateMemory(existingMemory) || inferLocalizationMemoryStatus(existingMemory, resultStorage.dir) !== "active") continue;
|
|
3207
3221
|
const verification = await this.getExtraction().verifyContradiction(
|
|
3208
3222
|
{ content, category },
|
|
3209
3223
|
{
|
|
@@ -3949,7 +3963,7 @@ var ConsolidationRunCoordinator = class {
|
|
|
3949
3963
|
await this.deps.flushAccessTracking();
|
|
3950
3964
|
}
|
|
3951
3965
|
await this.pruneHarmonicStores(storage);
|
|
3952
|
-
let allMemories = await storage.readAllMemories();
|
|
3966
|
+
let allMemories = excludeSupportPassportPrivateMemories(await storage.readAllMemories());
|
|
3953
3967
|
if (allMemories.length < 5) {
|
|
3954
3968
|
return { memoriesProcessed: allMemories.length, merged, invalidated };
|
|
3955
3969
|
}
|
|
@@ -4228,7 +4242,7 @@ var ConsolidationRunCoordinator = class {
|
|
|
4228
4242
|
const deepSleepStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4229
4243
|
const tierMigration = await this.deps.tierMigrationCoordinator.runCycle(storage, "maintenance");
|
|
4230
4244
|
if (tierMigration.migrated > 0) memoryItemMutated = true;
|
|
4231
|
-
allMemories = await storage.readAllMemories();
|
|
4245
|
+
allMemories = excludeSupportPassportPrivateMemories(await storage.readAllMemories());
|
|
4232
4246
|
if (resolveRecallEnhancementCapabilities(config).factArchival) {
|
|
4233
4247
|
const archived = await this.deps.lifecyclePolicyCoordinator.runFactArchival(allMemories);
|
|
4234
4248
|
if (archived > 0) {
|
|
@@ -4248,7 +4262,7 @@ var ConsolidationRunCoordinator = class {
|
|
|
4248
4262
|
} catch (err) {
|
|
4249
4263
|
log.warn(`deep-sleep maintenance pass failed (ignored): ${err}`);
|
|
4250
4264
|
try {
|
|
4251
|
-
allMemories = await storage.readAllMemories();
|
|
4265
|
+
allMemories = excludeSupportPassportPrivateMemories(await storage.readAllMemories());
|
|
4252
4266
|
} catch (readErr) {
|
|
4253
4267
|
log.warn(`deep-sleep maintenance recovery read failed: ${readErr}`);
|
|
4254
4268
|
throw err;
|
|
@@ -4282,7 +4296,7 @@ var ConsolidationRunCoordinator = class {
|
|
|
4282
4296
|
const semResult = await this.deps.semanticConsolidationCoordinator.runSemanticConsolidation();
|
|
4283
4297
|
let remItemsProcessed = allMemories.length;
|
|
4284
4298
|
try {
|
|
4285
|
-
allMemories = await storage.readAllMemories();
|
|
4299
|
+
allMemories = excludeSupportPassportPrivateMemories(await storage.readAllMemories());
|
|
4286
4300
|
remItemsProcessed = allMemories.length;
|
|
4287
4301
|
} catch (err) {
|
|
4288
4302
|
log.warn(
|
|
@@ -13158,6 +13172,7 @@ var RecallSearchPipelineCoordinator = class {
|
|
|
13158
13172
|
let forgottenFilteredCount = 0;
|
|
13159
13173
|
let blockedPathFilteredCount = 0;
|
|
13160
13174
|
let connectorPartitionFilteredCount = 0;
|
|
13175
|
+
let supportPassportFilteredCount = 0;
|
|
13161
13176
|
const filtered = [];
|
|
13162
13177
|
for (const r of results) {
|
|
13163
13178
|
if (options?.blockedPaths && resultHasKey(options.blockedPaths, r)) {
|
|
@@ -13177,6 +13192,10 @@ var RecallSearchPipelineCoordinator = class {
|
|
|
13177
13192
|
forgottenFilteredCount += 1;
|
|
13178
13193
|
continue;
|
|
13179
13194
|
}
|
|
13195
|
+
if (isSupportPassportPrivateMemory(memory)) {
|
|
13196
|
+
supportPassportFilteredCount += 1;
|
|
13197
|
+
continue;
|
|
13198
|
+
}
|
|
13180
13199
|
if (options?.allowLifecycleFiltered !== true && shouldFilterLifecycleRecallCandidate(memory.frontmatter, {
|
|
13181
13200
|
lifecyclePolicyEnabled: lifecycleCaps.lifecyclePolicy,
|
|
13182
13201
|
lifecycleFilterStaleEnabled: lifecycleCaps.lifecycleFilterStale
|
|
@@ -13228,6 +13247,11 @@ var RecallSearchPipelineCoordinator = class {
|
|
|
13228
13247
|
`connector partition filter removed ${connectorPartitionFilteredCount} tool-scoped candidates from recall`
|
|
13229
13248
|
);
|
|
13230
13249
|
}
|
|
13250
|
+
if (supportPassportFilteredCount > 0) {
|
|
13251
|
+
log.debug(
|
|
13252
|
+
`support passport filter removed ${supportPassportFilteredCount} owner-controlled records from generic recall`
|
|
13253
|
+
);
|
|
13254
|
+
}
|
|
13231
13255
|
if (lifecycleFilteredCount > 0) {
|
|
13232
13256
|
log.debug(
|
|
13233
13257
|
`lifecycle retrieval filter removed ${lifecycleFilteredCount} stale/archived candidates`
|
|
@@ -15019,7 +15043,7 @@ var OrchestratorInitCoordinator = class {
|
|
|
15019
15043
|
if (signal.aborted) return;
|
|
15020
15044
|
if (lifecycleCaps.lifecyclePolicy && resolveQmdCapabilities(this.deps.config).qmdTierMigration) {
|
|
15021
15045
|
try {
|
|
15022
|
-
const { runFirstStartMigration } = await import("./first-start-migration-
|
|
15046
|
+
const { runFirstStartMigration } = await import("./first-start-migration-LMMZFLKK.js");
|
|
15023
15047
|
const result = await runFirstStartMigration({
|
|
15024
15048
|
storage: this.deps.storage,
|
|
15025
15049
|
config: this.deps.config,
|
|
@@ -15560,7 +15584,7 @@ async function runPatternReinforcement(storage, options) {
|
|
|
15560
15584
|
if (targetCategories.size === 0) {
|
|
15561
15585
|
return emptyResult2();
|
|
15562
15586
|
}
|
|
15563
|
-
const memories = await storage.readAllMemories();
|
|
15587
|
+
const memories = excludeSupportPassportPrivateMemories(await storage.readAllMemories());
|
|
15564
15588
|
const eligible = memories.filter((m) => {
|
|
15565
15589
|
if (!targetCategories.has(m.frontmatter.category)) return false;
|
|
15566
15590
|
const status = m.frontmatter.status ?? ACTIVE_STATUS2;
|
|
@@ -15832,6 +15856,7 @@ Install it alongside Remnic:
|
|
|
15832
15856
|
const memories = await storage.readAllMemories();
|
|
15833
15857
|
const support = [];
|
|
15834
15858
|
for (const memory of memories) {
|
|
15859
|
+
if (isSupportPassportPrivateMemory(memory)) continue;
|
|
15835
15860
|
const status = inferMemoryStatus(
|
|
15836
15861
|
memory.frontmatter,
|
|
15837
15862
|
memory.path
|
|
@@ -17600,36 +17625,13 @@ var WorkspaceOpsCoordinator = class {
|
|
|
17600
17625
|
const fullPath = path17.join(dateDir, entry.name);
|
|
17601
17626
|
try {
|
|
17602
17627
|
assertPathInsideRoot(memoryRootReal, await realpath2(fullPath), fullPath);
|
|
17603
|
-
const
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
const fmBlock = fmMatch[1];
|
|
17607
|
-
const content = fmMatch[2].trim();
|
|
17608
|
-
const fm = {};
|
|
17609
|
-
for (const line of fmBlock.split("\n")) {
|
|
17610
|
-
const colonIdx = line.indexOf(":");
|
|
17611
|
-
if (colonIdx === -1) continue;
|
|
17612
|
-
fm[line.slice(0, colonIdx).trim()] = line.slice(colonIdx + 1).trim();
|
|
17613
|
-
}
|
|
17614
|
-
const created = fm.created || "unknown";
|
|
17615
|
-
const createdAt = parseFiniteDate(created);
|
|
17628
|
+
const memory = await storage.readMemoryByPath(fullPath);
|
|
17629
|
+
if (!memory || isSupportPassportPrivateMemory(memory)) continue;
|
|
17630
|
+
const createdAt = parseFiniteDate(memory.frontmatter.created);
|
|
17616
17631
|
if (createdAt && formatDateInTimeZone(createdAt, timeZone) !== targetLocalDate) {
|
|
17617
17632
|
continue;
|
|
17618
17633
|
}
|
|
17619
|
-
facts.push(
|
|
17620
|
-
path: fullPath,
|
|
17621
|
-
frontmatter: {
|
|
17622
|
-
id: fm.id || path17.basename(entry.name, ".md"),
|
|
17623
|
-
category: fm.category || "fact",
|
|
17624
|
-
created,
|
|
17625
|
-
updated: fm.updated || created,
|
|
17626
|
-
source: fm.source || "unknown",
|
|
17627
|
-
confidence: parseFloat(fm.confidence || "0.8"),
|
|
17628
|
-
confidenceTier: fm.confidenceTier || "implied",
|
|
17629
|
-
tags: []
|
|
17630
|
-
},
|
|
17631
|
-
content
|
|
17632
|
-
});
|
|
17634
|
+
facts.push(memory);
|
|
17633
17635
|
} catch {
|
|
17634
17636
|
}
|
|
17635
17637
|
}
|
|
@@ -19190,6 +19192,13 @@ var Orchestrator = class {
|
|
|
19190
19192
|
const ns = typeof namespace === "string" && namespace.trim().length > 0 ? namespace.trim() : this.config.defaultNamespace;
|
|
19191
19193
|
return this.storageRouter.storageFor(ns);
|
|
19192
19194
|
}
|
|
19195
|
+
filterPrivateSearchResults = (results, namespaces = [], preserveUnresolved = false, visibilityCache) => this.qmdResultResolver.filterPrivateSearchResults(
|
|
19196
|
+
results,
|
|
19197
|
+
this.storage,
|
|
19198
|
+
namespaces,
|
|
19199
|
+
preserveUnresolved,
|
|
19200
|
+
visibilityCache
|
|
19201
|
+
);
|
|
19193
19202
|
configuredNamespaceList() {
|
|
19194
19203
|
return getConfiguredNamespaces(this.config);
|
|
19195
19204
|
}
|
|
@@ -20833,12 +20842,6 @@ ${doc.content}` : doc.content,
|
|
|
20833
20842
|
return match ? match[1] : null;
|
|
20834
20843
|
}).filter((id) => id !== null);
|
|
20835
20844
|
}
|
|
20836
|
-
// ---------------------------------------------------------------------------
|
|
20837
|
-
// Contradiction Detection (Phase 2B)
|
|
20838
|
-
// ---------------------------------------------------------------------------
|
|
20839
|
-
// ---------------------------------------------------------------------------
|
|
20840
|
-
// Feedback (v2.2)
|
|
20841
|
-
// ---------------------------------------------------------------------------
|
|
20842
20845
|
async recordMemoryFeedback(memoryId, vote, note) {
|
|
20843
20846
|
await this.relevance.record(memoryId, vote, note);
|
|
20844
20847
|
}
|
|
@@ -20967,127 +20970,2301 @@ ${doc.content}` : doc.content,
|
|
|
20967
20970
|
}
|
|
20968
20971
|
};
|
|
20969
20972
|
|
|
20970
|
-
// src/
|
|
20971
|
-
var
|
|
20972
|
-
|
|
20973
|
-
|
|
20974
|
-
|
|
20975
|
-
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
}
|
|
20980
|
-
var testEvidence = {
|
|
20981
|
-
kind: "test",
|
|
20982
|
-
id: "test-checkout-contract",
|
|
20983
|
-
label: "Checkout integration contract",
|
|
20984
|
-
locator: "fixture://checkout-service/test/token-policy.test.ts",
|
|
20985
|
-
capture: "fixture"
|
|
20986
|
-
};
|
|
20987
|
-
var approvalEvidence = {
|
|
20988
|
-
kind: "approval",
|
|
20989
|
-
id: "approval-build-week-operator",
|
|
20990
|
-
label: "Human approval receipt",
|
|
20991
|
-
locator: "fixture://relay/approvals/correction-token-refresh.json",
|
|
20992
|
-
capture: "fixture"
|
|
20993
|
-
};
|
|
20994
|
-
var correctionEvidence = {
|
|
20995
|
-
kind: "correction",
|
|
20996
|
-
id: "correction-token-refresh",
|
|
20997
|
-
label: "Applied Remnic correction",
|
|
20998
|
-
locator: "fixture://remnic/corrections/correction-token-refresh.json",
|
|
20999
|
-
capture: "fixture"
|
|
20973
|
+
// src/support-passport/errors.ts
|
|
20974
|
+
var SupportPassportError = class extends Error {
|
|
20975
|
+
code;
|
|
20976
|
+
status;
|
|
20977
|
+
constructor(code, message, status) {
|
|
20978
|
+
super(message);
|
|
20979
|
+
this.name = "SupportPassportError";
|
|
20980
|
+
this.code = code;
|
|
20981
|
+
this.status = status;
|
|
20982
|
+
}
|
|
21000
20983
|
};
|
|
21001
|
-
|
|
21002
|
-
|
|
20984
|
+
|
|
20985
|
+
// src/support-passport/owner-lock.ts
|
|
20986
|
+
import { createHash as createHash9 } from "crypto";
|
|
20987
|
+
import path21 from "path";
|
|
20988
|
+
var CARD_MUTATION_LOCK_STALE_MS = 6e4;
|
|
20989
|
+
var CARD_MUTATION_LOCK_MAX_WAIT_MS = 3e4;
|
|
20990
|
+
function computeSupportPassportOwnerLockKey(namespace, principal) {
|
|
20991
|
+
return createHash9("sha256").update(JSON.stringify([namespace, principal])).digest("hex");
|
|
20992
|
+
}
|
|
20993
|
+
function supportPassportOwnerLockPath(storage, scope) {
|
|
20994
|
+
const ownerLockKey = scope.ownerKey ?? computeSupportPassportOwnerLockKey(scope.namespace, scope.principal);
|
|
20995
|
+
if (!/^[a-f0-9]{64}$/.test(ownerLockKey)) {
|
|
20996
|
+
throw new SupportPassportError("card_data_invalid", "The support passport owner scope is invalid.", 500);
|
|
20997
|
+
}
|
|
20998
|
+
return path21.join(storage.dir, "state", `support-passport-cards-${ownerLockKey}.lock`);
|
|
20999
|
+
}
|
|
21000
|
+
async function withSupportPassportOwnerLock(storage, scope, task) {
|
|
21001
|
+
const lockPath = supportPassportOwnerLockPath(storage, scope);
|
|
21002
|
+
return await serializeMutations(
|
|
21003
|
+
lockPath,
|
|
21004
|
+
() => withHeldFileLock(
|
|
21005
|
+
lockPath,
|
|
21006
|
+
{ staleMs: CARD_MUTATION_LOCK_STALE_MS, maxWaitMs: CARD_MUTATION_LOCK_MAX_WAIT_MS },
|
|
21007
|
+
async (acquired, lock) => {
|
|
21008
|
+
if (!acquired) {
|
|
21009
|
+
throw new SupportPassportError("storage_conflict", "The support passport is busy. Try again.", 409);
|
|
21010
|
+
}
|
|
21011
|
+
return await task(lock);
|
|
21012
|
+
}
|
|
21013
|
+
)
|
|
21014
|
+
);
|
|
21003
21015
|
}
|
|
21004
|
-
function
|
|
21005
|
-
return
|
|
21006
|
-
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21016
|
+
async function requireSupportPassportOwnerLock(lock) {
|
|
21017
|
+
if (await lock.refresh()) return;
|
|
21018
|
+
throw new SupportPassportError("storage_conflict", "The support passport changed during the request.", 409);
|
|
21019
|
+
}
|
|
21020
|
+
|
|
21021
|
+
// src/support-passport/card-service.ts
|
|
21022
|
+
var OWNER_VISIBLE_STATUSES = /* @__PURE__ */ new Set(["pending_review", "active"]);
|
|
21023
|
+
var MAX_OWNER_VISIBLE_CARDS = 100;
|
|
21024
|
+
function invalidInput() {
|
|
21025
|
+
return new SupportPassportError("invalid_input", "The support card request is invalid.", 400);
|
|
21026
|
+
}
|
|
21027
|
+
function isStorageConflict(error) {
|
|
21028
|
+
return error instanceof SupportPassportError && error.code === "storage_conflict";
|
|
21029
|
+
}
|
|
21030
|
+
var SupportPassportCardService = class {
|
|
21031
|
+
resolveOwner;
|
|
21032
|
+
now;
|
|
21033
|
+
constructor(dependencies) {
|
|
21034
|
+
this.resolveOwner = dependencies.resolveOwner;
|
|
21035
|
+
this.now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
21036
|
+
}
|
|
21037
|
+
async listCards(input) {
|
|
21038
|
+
const parsed = SupportPassportListCardsInputSchema.safeParse(input);
|
|
21039
|
+
if (!parsed.success) throw invalidInput();
|
|
21040
|
+
const { principal, namespace, storage } = await this.resolveOwnerScope(parsed.data.principal);
|
|
21041
|
+
return await withSupportPassportOwnerLock(storage, { namespace, principal }, async (lock) => {
|
|
21042
|
+
const stored = await this.readStoredCards(storage, lock, principal, namespace);
|
|
21043
|
+
const cards = this.ownerVisibleCards(stored).sort((a, b) => a.order - b.order || a.card.cardId.localeCompare(b.card.cardId)).map((item) => item.card);
|
|
21044
|
+
const output = SupportPassportCardListSchema.safeParse(cards);
|
|
21045
|
+
if (!output.success) {
|
|
21046
|
+
throw new SupportPassportError("card_data_invalid", "The support card data is invalid.", 500);
|
|
21047
|
+
}
|
|
21048
|
+
return output.data;
|
|
21049
|
+
});
|
|
21050
|
+
}
|
|
21051
|
+
async createManualDraft(input) {
|
|
21052
|
+
const parsed = SupportPassportManualDraftInputSchema.safeParse(input);
|
|
21053
|
+
if (!parsed.success) throw invalidInput();
|
|
21054
|
+
const { principal, namespace, storage } = await this.resolveOwnerScope(parsed.data.principal);
|
|
21055
|
+
return await withSupportPassportOwnerLock(
|
|
21056
|
+
storage,
|
|
21057
|
+
{ namespace, principal },
|
|
21058
|
+
(lock) => this.createDraft(
|
|
21059
|
+
storage,
|
|
21060
|
+
{
|
|
21061
|
+
title: parsed.data.title,
|
|
21062
|
+
statement: parsed.data.statement,
|
|
21063
|
+
category: parsed.data.category,
|
|
21064
|
+
reviewBy: parsed.data.reviewBy,
|
|
21065
|
+
sourceMemoryIds: []
|
|
21066
|
+
},
|
|
21067
|
+
lock,
|
|
21068
|
+
principal,
|
|
21069
|
+
namespace
|
|
21070
|
+
)
|
|
21071
|
+
);
|
|
21072
|
+
}
|
|
21073
|
+
async replaceCard(input) {
|
|
21074
|
+
const parsed = SupportPassportReplaceCardInputSchema.safeParse(input);
|
|
21075
|
+
if (!parsed.success) throw invalidInput();
|
|
21076
|
+
const { principal, namespace, storage } = await this.resolveOwnerScope(parsed.data.principal);
|
|
21077
|
+
return await withSupportPassportOwnerLock(storage, { namespace, principal }, async (lock) => {
|
|
21078
|
+
const loadedPrior = await this.requireCard(storage, parsed.data.cardId, namespace, principal);
|
|
21079
|
+
const storedCards = await this.readProjectedCards(storage, namespace, principal);
|
|
21080
|
+
const interruptedReplacements = storedCards.filter(
|
|
21081
|
+
(item) => item.card.status === "pending_review" && (item.replacesDraftId === loadedPrior.card.cardId || item.memory.frontmatter.supersedes === loadedPrior.card.cardId)
|
|
21082
|
+
);
|
|
21083
|
+
if (interruptedReplacements.length > 1) {
|
|
21084
|
+
throw new SupportPassportError(
|
|
21085
|
+
"storage_conflict",
|
|
21086
|
+
"Multiple support card edits already completed. Reload the support passport.",
|
|
21087
|
+
409
|
|
21088
|
+
);
|
|
21015
21089
|
}
|
|
21016
|
-
|
|
21017
|
-
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
|
|
21027
|
-
|
|
21090
|
+
const [interruptedReplacement] = interruptedReplacements;
|
|
21091
|
+
if (interruptedReplacement) {
|
|
21092
|
+
if (interruptedReplacement.replacedRevision !== parsed.data.expectedRevision) {
|
|
21093
|
+
throw new SupportPassportError("revision_conflict", "The support card changed after it was loaded.", 409);
|
|
21094
|
+
}
|
|
21095
|
+
const matchesRetry = interruptedReplacement.card.title === parsed.data.title && interruptedReplacement.card.statement === parsed.data.statement && interruptedReplacement.card.category === parsed.data.category && interruptedReplacement.card.reviewBy === parsed.data.reviewBy;
|
|
21096
|
+
if (!matchesRetry) {
|
|
21097
|
+
throw new SupportPassportError(
|
|
21098
|
+
"storage_conflict",
|
|
21099
|
+
"A different support card edit already completed. Reload the support passport.",
|
|
21100
|
+
409
|
|
21101
|
+
);
|
|
21102
|
+
}
|
|
21103
|
+
if (!interruptedReplacement.draftReplacementPrepared) return interruptedReplacement.card;
|
|
21104
|
+
const recovered = await this.recoverReplacementTransition(
|
|
21105
|
+
storage,
|
|
21106
|
+
interruptedReplacement.memory,
|
|
21107
|
+
lock,
|
|
21108
|
+
principal,
|
|
21109
|
+
namespace
|
|
21110
|
+
);
|
|
21111
|
+
return this.projectRequiredCard(recovered, namespace, principal).card;
|
|
21028
21112
|
}
|
|
21029
|
-
|
|
21030
|
-
|
|
21031
|
-
|
|
21032
|
-
|
|
21033
|
-
|
|
21034
|
-
|
|
21035
|
-
|
|
21036
|
-
|
|
21037
|
-
|
|
21038
|
-
|
|
21039
|
-
|
|
21040
|
-
|
|
21113
|
+
this.requireRevision(loadedPrior, parsed.data.expectedRevision);
|
|
21114
|
+
await this.readStoredCards(storage, lock, principal, namespace);
|
|
21115
|
+
const refreshedPrior = await this.requireCard(storage, parsed.data.cardId, namespace, principal);
|
|
21116
|
+
this.requireRevision(refreshedPrior, parsed.data.expectedRevision);
|
|
21117
|
+
const recoveredMemory = await this.recoverReplacementTransition(
|
|
21118
|
+
storage,
|
|
21119
|
+
refreshedPrior.memory,
|
|
21120
|
+
lock,
|
|
21121
|
+
principal,
|
|
21122
|
+
namespace
|
|
21123
|
+
);
|
|
21124
|
+
const prior = this.projectRequiredCard(recoveredMemory, namespace, principal);
|
|
21125
|
+
if (prior.card.status !== "active" && prior.card.status !== "pending_review") {
|
|
21126
|
+
throw new SupportPassportError(
|
|
21127
|
+
"invalid_card_status",
|
|
21128
|
+
"Only a draft or approved support card can be edited.",
|
|
21129
|
+
409
|
|
21130
|
+
);
|
|
21041
21131
|
}
|
|
21042
|
-
|
|
21043
|
-
|
|
21044
|
-
|
|
21045
|
-
|
|
21046
|
-
|
|
21047
|
-
|
|
21048
|
-
|
|
21049
|
-
|
|
21050
|
-
|
|
21051
|
-
|
|
21052
|
-
|
|
21053
|
-
|
|
21054
|
-
|
|
21055
|
-
|
|
21056
|
-
|
|
21057
|
-
|
|
21058
|
-
|
|
21059
|
-
|
|
21060
|
-
|
|
21132
|
+
const replacement = await this.createDraft(
|
|
21133
|
+
storage,
|
|
21134
|
+
{
|
|
21135
|
+
title: parsed.data.title,
|
|
21136
|
+
statement: parsed.data.statement,
|
|
21137
|
+
category: parsed.data.category,
|
|
21138
|
+
reviewBy: parsed.data.reviewBy,
|
|
21139
|
+
sourceMemoryIds: prior.sourceMemoryIds,
|
|
21140
|
+
supersedes: prior.card.status === "active" ? prior.card.cardId : prior.memory.frontmatter.supersedes,
|
|
21141
|
+
replacesDraftId: prior.card.status === "pending_review" ? prior.card.cardId : void 0,
|
|
21142
|
+
replacedRevision: prior.card.revision,
|
|
21143
|
+
order: prior.order,
|
|
21144
|
+
draftReplacementPrepared: prior.card.status === "pending_review"
|
|
21145
|
+
},
|
|
21146
|
+
lock,
|
|
21147
|
+
principal,
|
|
21148
|
+
namespace
|
|
21149
|
+
);
|
|
21150
|
+
if (prior.card.status === "active") return replacement;
|
|
21151
|
+
const replacedAt = this.now().toISOString();
|
|
21152
|
+
await this.requireOwnerLock(lock);
|
|
21153
|
+
const rejected = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21154
|
+
prior.memory,
|
|
21155
|
+
{ status: "rejected", updated: replacedAt },
|
|
21156
|
+
{ actor: principal, reasonCode: "owner-replaced-draft" }
|
|
21157
|
+
);
|
|
21158
|
+
if (rejected) {
|
|
21159
|
+
return await this.finishPreparedDraftReplacement(storage, replacement.cardId, lock, principal, namespace);
|
|
21061
21160
|
}
|
|
21062
|
-
|
|
21063
|
-
|
|
21064
|
-
|
|
21065
|
-
|
|
21066
|
-
payload: {
|
|
21067
|
-
kind: "belief_observed",
|
|
21068
|
-
agentId: "agent-atlas",
|
|
21069
|
-
sessionId: "session-atlas",
|
|
21070
|
-
beliefId: "belief-new-token-every-request",
|
|
21071
|
-
decisionId: "decision-new-token-every-request",
|
|
21072
|
-
statement: "Mint a new checkout token for every request and retry.",
|
|
21073
|
-
confidence: 0.91,
|
|
21074
|
-
evidence: [
|
|
21075
|
-
{
|
|
21076
|
-
kind: "memory",
|
|
21077
|
-
id: "memory-stale-token-policy",
|
|
21078
|
-
label: "Stale project decision",
|
|
21079
|
-
locator: "fixture://remnic/memories/memory-stale-token-policy.md",
|
|
21080
|
-
capture: "fixture"
|
|
21081
|
-
}
|
|
21082
|
-
]
|
|
21161
|
+
const currentPrior = await storage.getMemoryById(prior.card.cardId);
|
|
21162
|
+
const currentPriorCard = currentPrior ? this.projectOwnedCard(currentPrior, namespace, principal) : null;
|
|
21163
|
+
if (currentPriorCard?.card.status === "rejected") {
|
|
21164
|
+
return await this.finishPreparedDraftReplacement(storage, replacement.cardId, lock, principal, namespace);
|
|
21083
21165
|
}
|
|
21084
|
-
|
|
21085
|
-
|
|
21086
|
-
|
|
21087
|
-
|
|
21088
|
-
|
|
21089
|
-
|
|
21090
|
-
|
|
21166
|
+
await this.rejectCreatedDraft(
|
|
21167
|
+
storage,
|
|
21168
|
+
replacement.cardId,
|
|
21169
|
+
"draft-replacement-failed",
|
|
21170
|
+
lock,
|
|
21171
|
+
principal,
|
|
21172
|
+
namespace
|
|
21173
|
+
);
|
|
21174
|
+
throw new SupportPassportError("storage_conflict", "The support card changed before it was edited.", 409);
|
|
21175
|
+
});
|
|
21176
|
+
}
|
|
21177
|
+
async approveCard(input) {
|
|
21178
|
+
const parsed = SupportPassportCardMutationInputSchema.safeParse(input);
|
|
21179
|
+
if (!parsed.success) throw invalidInput();
|
|
21180
|
+
const { principal, namespace, storage } = await this.resolveOwnerScope(parsed.data.principal);
|
|
21181
|
+
return await withSupportPassportOwnerLock(storage, { namespace, principal }, async (lock) => {
|
|
21182
|
+
const loadedCard = await this.requireCard(storage, parsed.data.cardId, namespace, principal);
|
|
21183
|
+
this.requireRevision(loadedCard, parsed.data.expectedRevision);
|
|
21184
|
+
this.requireStatus(loadedCard, "pending_review");
|
|
21185
|
+
const recoveredMemory = await this.recoverReplacementTransition(
|
|
21186
|
+
storage,
|
|
21187
|
+
loadedCard.memory,
|
|
21188
|
+
lock,
|
|
21189
|
+
principal,
|
|
21190
|
+
namespace
|
|
21191
|
+
);
|
|
21192
|
+
const card = this.projectRequiredCard(recoveredMemory, namespace, principal);
|
|
21193
|
+
if (card.card.status === "active") return card.card;
|
|
21194
|
+
if (card.card.status === "rejected") {
|
|
21195
|
+
throw new SupportPassportError(
|
|
21196
|
+
"storage_conflict",
|
|
21197
|
+
"The support card was rejected while its replacement state was recovered.",
|
|
21198
|
+
409
|
|
21199
|
+
);
|
|
21200
|
+
}
|
|
21201
|
+
this.requireStatus(card, "pending_review");
|
|
21202
|
+
await this.validatePriorForReplacement(storage, card, namespace, principal);
|
|
21203
|
+
const updatedAt = this.now().toISOString();
|
|
21204
|
+
await this.requireOwnerLock(lock);
|
|
21205
|
+
let approved;
|
|
21206
|
+
try {
|
|
21207
|
+
approved = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21208
|
+
card.memory,
|
|
21209
|
+
{ status: "active", updated: updatedAt },
|
|
21210
|
+
{ actor: principal, reasonCode: "owner-approved" }
|
|
21211
|
+
);
|
|
21212
|
+
} catch (error) {
|
|
21213
|
+
const current = await storage.getMemoryById(card.card.cardId);
|
|
21214
|
+
const currentCard = current ? this.projectOwnedCard(current, namespace, principal) : null;
|
|
21215
|
+
if (currentCard?.card.status === "active") {
|
|
21216
|
+
return await this.finishCommittedApproval(storage, currentCard, lock, principal, namespace);
|
|
21217
|
+
}
|
|
21218
|
+
throw error;
|
|
21219
|
+
}
|
|
21220
|
+
if (!approved) {
|
|
21221
|
+
const current = await storage.getMemoryById(card.card.cardId);
|
|
21222
|
+
const currentCard = current ? this.projectOwnedCard(current, namespace, principal) : null;
|
|
21223
|
+
if (currentCard?.card.status === "active") {
|
|
21224
|
+
return await this.finishCommittedApproval(storage, currentCard, lock, principal, namespace);
|
|
21225
|
+
}
|
|
21226
|
+
throw new SupportPassportError("storage_conflict", "The support card changed before approval.", 409);
|
|
21227
|
+
}
|
|
21228
|
+
const committedCard = {
|
|
21229
|
+
...card.card,
|
|
21230
|
+
status: "active",
|
|
21231
|
+
updatedAt,
|
|
21232
|
+
revision: this.revisionFor(card.card, "active", updatedAt)
|
|
21233
|
+
};
|
|
21234
|
+
try {
|
|
21235
|
+
const current = await this.requireCard(storage, card.card.cardId, namespace, principal);
|
|
21236
|
+
return await this.finishCommittedApproval(storage, current, lock, principal, namespace);
|
|
21237
|
+
} catch (error) {
|
|
21238
|
+
if (isStorageConflict(error)) throw error;
|
|
21239
|
+
log.warn(
|
|
21240
|
+
`support passport could not finish replacement approval side effects: ${error instanceof Error ? error.message : String(error)}`
|
|
21241
|
+
);
|
|
21242
|
+
return committedCard;
|
|
21243
|
+
}
|
|
21244
|
+
});
|
|
21245
|
+
}
|
|
21246
|
+
async rejectCard(input) {
|
|
21247
|
+
return await this.changeStatus(input, "pending_review", "rejected");
|
|
21248
|
+
}
|
|
21249
|
+
async withdrawCard(input) {
|
|
21250
|
+
return await this.changeStatus(input, "active", "archived");
|
|
21251
|
+
}
|
|
21252
|
+
async createDraft(storage, input, lock, principal, namespace) {
|
|
21253
|
+
const now = this.now();
|
|
21254
|
+
const reviewBy = input.reviewBy ?? now.toISOString();
|
|
21255
|
+
const storedCards = await this.readStoredCards(storage, lock, principal, namespace);
|
|
21256
|
+
const visibleCards = this.ownerVisibleCards(storedCards);
|
|
21257
|
+
const replacesVisibleCard = visibleCards.some(
|
|
21258
|
+
(item) => input.replacesDraftId === item.card.cardId && item.card.status === "pending_review" || input.supersedes === item.card.cardId && item.card.status === "active"
|
|
21259
|
+
);
|
|
21260
|
+
if (visibleCards.length - (replacesVisibleCard ? 1 : 0) >= MAX_OWNER_VISIBLE_CARDS) {
|
|
21261
|
+
throw new SupportPassportError("invalid_input", "A support passport can contain at most 100 visible cards.", 400);
|
|
21262
|
+
}
|
|
21263
|
+
const maximumOrder = storedCards.reduce((maximum, card) => Math.max(maximum, card.order), -1);
|
|
21264
|
+
const order = input.order ?? maximumOrder + 1;
|
|
21265
|
+
if (!Number.isSafeInteger(order)) {
|
|
21266
|
+
throw new SupportPassportError("storage_conflict", "The support card order range is exhausted.", 409);
|
|
21267
|
+
}
|
|
21268
|
+
const envelope = composeMemoryEnvelope(
|
|
21269
|
+
{
|
|
21270
|
+
content: input.statement,
|
|
21271
|
+
category: "preference",
|
|
21272
|
+
tags: [SUPPORT_PASSPORT_CARD_TAG],
|
|
21273
|
+
structuredAttributes: {
|
|
21274
|
+
...encodeSupportPassportNamespaceAttributes(namespace),
|
|
21275
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.owner]: computeSupportPassportOwnerKey(principal),
|
|
21276
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.title]: input.title,
|
|
21277
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.category]: input.category,
|
|
21278
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.order]: String(order),
|
|
21279
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.reviewBy]: reviewBy,
|
|
21280
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.sourceMemoryIds]: input.sourceMemoryIds.join(","),
|
|
21281
|
+
...input.replacesDraftId ? { [SUPPORT_PASSPORT_ATTRIBUTE_KEYS.replacesDraftId]: input.replacesDraftId } : {},
|
|
21282
|
+
...input.replacedRevision ? { [SUPPORT_PASSPORT_ATTRIBUTE_KEYS.replacedRevision]: input.replacedRevision } : {},
|
|
21283
|
+
...input.draftReplacementPrepared ? { [SUPPORT_PASSPORT_ATTRIBUTE_KEYS.draftReplacementPrepared]: "true" } : {}
|
|
21284
|
+
},
|
|
21285
|
+
sourceReason: "support-passport"
|
|
21286
|
+
},
|
|
21287
|
+
{ source: "support-passport", now: this.now }
|
|
21288
|
+
);
|
|
21289
|
+
if (envelope.sanitizeViolations.length > 0) throw invalidInput();
|
|
21290
|
+
await this.requireOwnerLock(lock);
|
|
21291
|
+
const written = await storage.writeSealedMemory(envelope, {
|
|
21292
|
+
actor: principal,
|
|
21293
|
+
status: "pending_review",
|
|
21294
|
+
lineage: input.sourceMemoryIds.length > 0 ? input.sourceMemoryIds : void 0,
|
|
21295
|
+
supersedes: input.supersedes
|
|
21296
|
+
});
|
|
21297
|
+
if (written.tombstoneBlocked) {
|
|
21298
|
+
throw new SupportPassportError("storage_conflict", "The support card needs memory review before use.", 409);
|
|
21299
|
+
}
|
|
21300
|
+
return this.projectRequiredCard(written.memory, namespace, principal).card;
|
|
21301
|
+
}
|
|
21302
|
+
async changeStatus(input, expectedStatus, nextStatus) {
|
|
21303
|
+
const parsed = SupportPassportCardMutationInputSchema.safeParse(input);
|
|
21304
|
+
if (!parsed.success) throw invalidInput();
|
|
21305
|
+
const { principal, namespace, storage } = await this.resolveOwnerScope(parsed.data.principal);
|
|
21306
|
+
return await withSupportPassportOwnerLock(storage, { namespace, principal }, async (lock) => {
|
|
21307
|
+
let card = await this.requireCard(storage, parsed.data.cardId, namespace, principal);
|
|
21308
|
+
this.requireRevision(card, parsed.data.expectedRevision);
|
|
21309
|
+
this.requireStatus(card, expectedStatus);
|
|
21310
|
+
if (expectedStatus === "pending_review" && nextStatus === "rejected") {
|
|
21311
|
+
await this.rejectPendingReplacementForPredecessor(storage, card, lock, principal, namespace);
|
|
21312
|
+
card = await this.requireCard(storage, card.card.cardId, namespace, principal);
|
|
21313
|
+
this.requireRevision(card, parsed.data.expectedRevision);
|
|
21314
|
+
this.requireStatus(card, expectedStatus);
|
|
21315
|
+
}
|
|
21316
|
+
if (expectedStatus === "pending_review" || expectedStatus === "active" && (card.replacesDraftId || card.memory.frontmatter.supersedes)) {
|
|
21317
|
+
card = this.projectRequiredCard(
|
|
21318
|
+
await this.recoverReplacementTransition(storage, card.memory, lock, principal, namespace, {
|
|
21319
|
+
rollbackConflictedApproval: expectedStatus !== "active"
|
|
21320
|
+
}),
|
|
21321
|
+
namespace,
|
|
21322
|
+
principal
|
|
21323
|
+
);
|
|
21324
|
+
}
|
|
21325
|
+
if (card.card.status === nextStatus) return card.card;
|
|
21326
|
+
this.requireStatus(card, expectedStatus);
|
|
21327
|
+
if (expectedStatus === "active" && nextStatus === "archived") {
|
|
21328
|
+
await this.rejectPendingReplacementForPredecessor(storage, card, lock, principal, namespace);
|
|
21329
|
+
card = await this.requireCard(storage, card.card.cardId, namespace, principal);
|
|
21330
|
+
this.requireRevision(card, parsed.data.expectedRevision);
|
|
21331
|
+
this.requireStatus(card, expectedStatus);
|
|
21332
|
+
}
|
|
21333
|
+
const updatedAt = this.now().toISOString();
|
|
21334
|
+
await this.requireOwnerLock(lock);
|
|
21335
|
+
const changed = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21336
|
+
card.memory,
|
|
21337
|
+
{
|
|
21338
|
+
status: nextStatus,
|
|
21339
|
+
updated: updatedAt,
|
|
21340
|
+
...nextStatus === "archived" ? { archivedAt: updatedAt } : {}
|
|
21341
|
+
},
|
|
21342
|
+
{ actor: principal, reasonCode: nextStatus === "archived" ? "owner-withdrew" : "owner-rejected" }
|
|
21343
|
+
);
|
|
21344
|
+
if (!changed)
|
|
21345
|
+
throw new SupportPassportError(
|
|
21346
|
+
"storage_conflict",
|
|
21347
|
+
"The support card changed before the request completed.",
|
|
21348
|
+
409
|
|
21349
|
+
);
|
|
21350
|
+
return {
|
|
21351
|
+
...card.card,
|
|
21352
|
+
status: nextStatus,
|
|
21353
|
+
updatedAt,
|
|
21354
|
+
revision: this.revisionFor(card.card, nextStatus, updatedAt)
|
|
21355
|
+
};
|
|
21356
|
+
});
|
|
21357
|
+
}
|
|
21358
|
+
revisionFor(card, status, updatedAt) {
|
|
21359
|
+
return computeSupportPassportCardRevision({
|
|
21360
|
+
cardId: card.cardId,
|
|
21361
|
+
title: card.title,
|
|
21362
|
+
statement: card.statement,
|
|
21363
|
+
category: card.category,
|
|
21364
|
+
status,
|
|
21365
|
+
updatedAt,
|
|
21366
|
+
reviewBy: card.reviewBy
|
|
21367
|
+
});
|
|
21368
|
+
}
|
|
21369
|
+
async resolveOwnerScope(principal) {
|
|
21370
|
+
const requestedPrincipal = SupportPassportListCardsInputSchema.safeParse({ principal });
|
|
21371
|
+
const owner = await this.resolveOwner(principal);
|
|
21372
|
+
const namespace = SupportPassportNamespaceSchema.safeParse(owner.namespace);
|
|
21373
|
+
const ownerPrincipal = SupportPassportListCardsInputSchema.safeParse({ principal: owner.principal });
|
|
21374
|
+
if (!requestedPrincipal.success || !namespace.success || !ownerPrincipal.success || ownerPrincipal.data.principal !== requestedPrincipal.data.principal) {
|
|
21375
|
+
throw new SupportPassportError("card_data_invalid", "The support passport owner scope is invalid.", 500);
|
|
21376
|
+
}
|
|
21377
|
+
return { ...owner, principal: ownerPrincipal.data.principal, namespace: namespace.data };
|
|
21378
|
+
}
|
|
21379
|
+
async rejectCreatedDraft(storage, cardId, reasonCode, lock, principal, namespace) {
|
|
21380
|
+
try {
|
|
21381
|
+
const current = await this.requireCard(storage, cardId, namespace, principal);
|
|
21382
|
+
await this.requireOwnerLock(lock);
|
|
21383
|
+
const rejected = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21384
|
+
current.memory,
|
|
21385
|
+
{ status: "rejected", updated: this.now().toISOString() },
|
|
21386
|
+
{ actor: principal, reasonCode }
|
|
21387
|
+
);
|
|
21388
|
+
if (!rejected) log.warn("support passport could not roll back a replacement draft");
|
|
21389
|
+
} catch (error) {
|
|
21390
|
+
log.warn(
|
|
21391
|
+
`support passport could not roll back a replacement draft: ${error instanceof Error ? error.message : String(error)}`
|
|
21392
|
+
);
|
|
21393
|
+
}
|
|
21394
|
+
}
|
|
21395
|
+
async requireCard(storage, cardId, namespace, principal) {
|
|
21396
|
+
const stored = await storage.getMemoryById(cardId);
|
|
21397
|
+
const card = stored ? this.projectOwnedCard(stored, namespace, principal) : null;
|
|
21398
|
+
if (!card) throw new SupportPassportError("card_not_found", "The support card was not found.", 404);
|
|
21399
|
+
return card;
|
|
21400
|
+
}
|
|
21401
|
+
projectRequiredCard(memory, namespace, principal) {
|
|
21402
|
+
const card = projectSupportPassportCard(memory);
|
|
21403
|
+
if (!card) throw new SupportPassportError("card_data_invalid", "The support card data is invalid.", 500);
|
|
21404
|
+
if (card.namespace !== namespace || card.owner !== computeSupportPassportOwnerKey(principal)) {
|
|
21405
|
+
throw new SupportPassportError("card_not_found", "The support card was not found.", 404);
|
|
21406
|
+
}
|
|
21407
|
+
return card;
|
|
21408
|
+
}
|
|
21409
|
+
projectOwnedCard(memory, namespace, principal) {
|
|
21410
|
+
const card = projectSupportPassportCard(memory);
|
|
21411
|
+
return card?.namespace === namespace && card.owner === computeSupportPassportOwnerKey(principal) ? card : null;
|
|
21412
|
+
}
|
|
21413
|
+
ownsMemory(memory, namespace, principal) {
|
|
21414
|
+
const attributes = memory.frontmatter.structuredAttributes;
|
|
21415
|
+
return attributes !== void 0 && decodeSupportPassportNamespaceAttributes(attributes) === namespace && attributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.owner] === computeSupportPassportOwnerKey(principal);
|
|
21416
|
+
}
|
|
21417
|
+
async readProjectedCards(storage, namespace, principal) {
|
|
21418
|
+
const owner = computeSupportPassportOwnerKey(principal);
|
|
21419
|
+
const projected = (await storage.readAllMemories()).map(projectSupportPassportCard).filter((card) => card?.namespace === namespace && card.owner === owner);
|
|
21420
|
+
const ids = /* @__PURE__ */ new Set();
|
|
21421
|
+
for (const item of projected) {
|
|
21422
|
+
if (ids.has(item.card.cardId)) {
|
|
21423
|
+
throw new SupportPassportError("card_data_invalid", "Support card IDs must be unique.", 500);
|
|
21424
|
+
}
|
|
21425
|
+
ids.add(item.card.cardId);
|
|
21426
|
+
}
|
|
21427
|
+
return projected;
|
|
21428
|
+
}
|
|
21429
|
+
requireRevision(card, expectedRevision) {
|
|
21430
|
+
if (card.card.revision !== expectedRevision) {
|
|
21431
|
+
throw new SupportPassportError("revision_conflict", "The support card changed after it was loaded.", 409);
|
|
21432
|
+
}
|
|
21433
|
+
}
|
|
21434
|
+
requireStatus(card, status) {
|
|
21435
|
+
if (card.card.status !== status) {
|
|
21436
|
+
throw new SupportPassportError("invalid_card_status", `The support card must have status ${status}.`, 409);
|
|
21437
|
+
}
|
|
21438
|
+
}
|
|
21439
|
+
async readStoredCards(storage, lock, principal, namespace) {
|
|
21440
|
+
const initialVersion = storage.getCorpusScanVersion();
|
|
21441
|
+
const initial = await storage.readAllMemories();
|
|
21442
|
+
for (const memory of initial) {
|
|
21443
|
+
const card = this.projectOwnedCard(memory, namespace, principal);
|
|
21444
|
+
if (card) await this.recoverReplacementTransition(storage, memory, lock, principal, namespace);
|
|
21445
|
+
}
|
|
21446
|
+
const memories = storage.getCorpusScanVersion() === initialVersion ? initial : await storage.readAllMemories();
|
|
21447
|
+
const owner = computeSupportPassportOwnerKey(principal);
|
|
21448
|
+
const projected = memories.map(projectSupportPassportCard).filter((card) => card?.namespace === namespace && card.owner === owner);
|
|
21449
|
+
const ids = /* @__PURE__ */ new Set();
|
|
21450
|
+
for (const item of projected) {
|
|
21451
|
+
if (ids.has(item.card.cardId)) {
|
|
21452
|
+
throw new SupportPassportError("card_data_invalid", "Support card IDs must be unique.", 500);
|
|
21453
|
+
}
|
|
21454
|
+
ids.add(item.card.cardId);
|
|
21455
|
+
}
|
|
21456
|
+
return projected;
|
|
21457
|
+
}
|
|
21458
|
+
ownerVisibleCards(storedCards) {
|
|
21459
|
+
const visibleCards = storedCards.filter(
|
|
21460
|
+
(item) => OWNER_VISIBLE_STATUSES.has(item.card.status) && !item.draftReplacementPrepared
|
|
21461
|
+
);
|
|
21462
|
+
if (visibleCards.length <= MAX_OWNER_VISIBLE_CARDS) return visibleCards;
|
|
21463
|
+
const activeCardsWithPendingReplacements = new Set(
|
|
21464
|
+
visibleCards.filter((item) => item.card.status === "pending_review").map((item) => item.memory.frontmatter.supersedes).filter((cardId) => typeof cardId === "string")
|
|
21465
|
+
);
|
|
21466
|
+
return visibleCards.filter(
|
|
21467
|
+
(item) => item.card.status !== "active" || !activeCardsWithPendingReplacements.has(item.card.cardId)
|
|
21468
|
+
);
|
|
21469
|
+
}
|
|
21470
|
+
async preparePriorForReplacement(storage, replacement, lock, principal, namespace) {
|
|
21471
|
+
const priorId = replacement.memory.frontmatter.supersedes;
|
|
21472
|
+
if (!priorId) return null;
|
|
21473
|
+
const priorMemory = await storage.getMemoryById(priorId);
|
|
21474
|
+
const priorIsOwned = priorMemory ? this.ownsMemory(priorMemory, namespace, principal) : false;
|
|
21475
|
+
const alreadyRetired = priorIsOwned && priorMemory?.frontmatter.status === "superseded" && priorMemory.frontmatter.supersededBy === replacement.card.cardId;
|
|
21476
|
+
const prior = priorMemory && priorIsOwned && !alreadyRetired ? projectSupportPassportCard(priorMemory) : null;
|
|
21477
|
+
if (prior?.card.status === "rejected") return null;
|
|
21478
|
+
if (!priorMemory || !alreadyRetired && prior?.card.status !== "active") {
|
|
21479
|
+
throw new SupportPassportError("storage_conflict", "The prior support card changed before replacement.", 409);
|
|
21480
|
+
}
|
|
21481
|
+
if (alreadyRetired) return priorId;
|
|
21482
|
+
const retiredAt = this.now().toISOString();
|
|
21483
|
+
await this.requireOwnerLock(lock);
|
|
21484
|
+
const retired = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21485
|
+
priorMemory,
|
|
21486
|
+
{
|
|
21487
|
+
status: "superseded",
|
|
21488
|
+
supersededBy: replacement.card.cardId,
|
|
21489
|
+
supersededAt: retiredAt,
|
|
21490
|
+
supersessionCause: "direct",
|
|
21491
|
+
updated: retiredAt
|
|
21492
|
+
},
|
|
21493
|
+
{
|
|
21494
|
+
actor: principal,
|
|
21495
|
+
reasonCode: "support-passport-replacement-pending",
|
|
21496
|
+
relatedMemoryIds: [replacement.card.cardId]
|
|
21497
|
+
}
|
|
21498
|
+
);
|
|
21499
|
+
if (!retired) {
|
|
21500
|
+
throw new SupportPassportError("storage_conflict", "The prior support card changed before replacement.", 409);
|
|
21501
|
+
}
|
|
21502
|
+
return priorId;
|
|
21503
|
+
}
|
|
21504
|
+
async rejectPendingReplacementForPredecessor(storage, predecessor, lock, principal, namespace) {
|
|
21505
|
+
const storedCards = predecessor.card.status === "active" ? await this.readStoredCards(storage, lock, principal, namespace) : await this.readProjectedCards(storage, namespace, principal);
|
|
21506
|
+
const replacements = storedCards.filter((item) => {
|
|
21507
|
+
if (item.card.status !== "pending_review") return false;
|
|
21508
|
+
return predecessor.card.status === "pending_review" ? item.replacesDraftId === predecessor.card.cardId : item.memory.frontmatter.supersedes === predecessor.card.cardId;
|
|
21509
|
+
});
|
|
21510
|
+
if (replacements.length > 1) {
|
|
21511
|
+
throw new SupportPassportError(
|
|
21512
|
+
"storage_conflict",
|
|
21513
|
+
"Multiple support card edits target the card being withdrawn.",
|
|
21514
|
+
409
|
|
21515
|
+
);
|
|
21516
|
+
}
|
|
21517
|
+
const [replacement] = replacements;
|
|
21518
|
+
if (!replacement) return;
|
|
21519
|
+
await this.requireOwnerLock(lock);
|
|
21520
|
+
const rejected = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21521
|
+
replacement.memory,
|
|
21522
|
+
{ status: "rejected", updated: this.now().toISOString() },
|
|
21523
|
+
{
|
|
21524
|
+
actor: principal,
|
|
21525
|
+
reasonCode: predecessor.card.status === "pending_review" ? "source-draft-rejected" : "predecessor-withdrawn"
|
|
21526
|
+
}
|
|
21527
|
+
);
|
|
21528
|
+
if (rejected) return;
|
|
21529
|
+
const current = await storage.getMemoryById(replacement.card.cardId);
|
|
21530
|
+
const currentCard = current ? this.projectOwnedCard(current, namespace, principal) : null;
|
|
21531
|
+
if (currentCard?.card.status !== "rejected") {
|
|
21532
|
+
throw new SupportPassportError("storage_conflict", "The pending support card edit could not be cancelled.", 409);
|
|
21533
|
+
}
|
|
21534
|
+
}
|
|
21535
|
+
async validatePriorForReplacement(storage, replacement, namespace, principal) {
|
|
21536
|
+
const priorId = replacement.memory.frontmatter.supersedes;
|
|
21537
|
+
if (!priorId) return;
|
|
21538
|
+
const priorMemory = await storage.getMemoryById(priorId);
|
|
21539
|
+
const priorIsOwned = priorMemory ? this.ownsMemory(priorMemory, namespace, principal) : false;
|
|
21540
|
+
const alreadyRetired = priorIsOwned && priorMemory?.frontmatter.status === "superseded" && priorMemory.frontmatter.supersededBy === replacement.card.cardId;
|
|
21541
|
+
const prior = priorMemory && priorIsOwned && !alreadyRetired ? projectSupportPassportCard(priorMemory) : null;
|
|
21542
|
+
if (prior?.card.status === "rejected" || alreadyRetired) return;
|
|
21543
|
+
if (!priorMemory || prior?.card.status !== "active") {
|
|
21544
|
+
throw new SupportPassportError("storage_conflict", "The prior support card changed before replacement.", 409);
|
|
21545
|
+
}
|
|
21546
|
+
}
|
|
21547
|
+
async recoverReplacementTransition(storage, memory, lock, principal, namespace, options = {}) {
|
|
21548
|
+
const replacement = this.projectOwnedCard(memory, namespace, principal);
|
|
21549
|
+
if (replacement?.card.status !== "pending_review" && replacement?.card.status !== "active") return memory;
|
|
21550
|
+
if (!replacement.replacesDraftId && !memory.frontmatter.supersedes) return memory;
|
|
21551
|
+
if (replacement.card.status === "active" && memory.frontmatter.structuredAttributes?.[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.replacementComplete] === "true") {
|
|
21552
|
+
return memory;
|
|
21553
|
+
}
|
|
21554
|
+
await this.recoverReplacedDraft(storage, replacement, lock, principal, namespace);
|
|
21555
|
+
const currentMemory = await storage.getMemoryById(replacement.card.cardId) ?? memory;
|
|
21556
|
+
const currentCard = this.projectOwnedCard(currentMemory, namespace, principal);
|
|
21557
|
+
if (currentCard?.card.status !== "pending_review" && currentCard?.card.status !== "active") {
|
|
21558
|
+
return currentMemory;
|
|
21559
|
+
}
|
|
21560
|
+
if (currentCard.card.status === "pending_review") {
|
|
21561
|
+
const priorId = currentMemory.frontmatter.supersedes;
|
|
21562
|
+
if (!priorId) return currentMemory;
|
|
21563
|
+
const prior = await storage.getMemoryById(priorId);
|
|
21564
|
+
if (prior && this.ownsMemory(prior, namespace, principal) && prior.frontmatter.status === "superseded" && prior.frontmatter.supersededBy === replacement.card.cardId) {
|
|
21565
|
+
await this.restorePriorAfterApprovalFailure(
|
|
21566
|
+
storage,
|
|
21567
|
+
priorId,
|
|
21568
|
+
replacement.card.cardId,
|
|
21569
|
+
lock,
|
|
21570
|
+
principal,
|
|
21571
|
+
namespace
|
|
21572
|
+
);
|
|
21573
|
+
}
|
|
21574
|
+
return await storage.getMemoryById(replacement.card.cardId) ?? currentMemory;
|
|
21575
|
+
}
|
|
21576
|
+
try {
|
|
21577
|
+
await this.completeReplacementAfterActivation(storage, currentCard, lock, principal, namespace);
|
|
21578
|
+
} catch (error) {
|
|
21579
|
+
if (!isStorageConflict(error)) throw error;
|
|
21580
|
+
if (options.rollbackConflictedApproval === false) throw error;
|
|
21581
|
+
await this.rollbackConflictedApproval(storage, currentCard.card.cardId, lock, principal, namespace);
|
|
21582
|
+
}
|
|
21583
|
+
return await storage.getMemoryById(replacement.card.cardId) ?? currentMemory;
|
|
21584
|
+
}
|
|
21585
|
+
async completeReplacementAfterActivation(storage, replacement, lock, principal, namespace) {
|
|
21586
|
+
const priorId = await this.preparePriorForReplacement(storage, replacement, lock, principal, namespace);
|
|
21587
|
+
if (priorId && !await this.completePriorRetirement(storage, priorId, replacement, lock, principal, namespace)) {
|
|
21588
|
+
throw new SupportPassportError("storage_conflict", "The prior support card changed before replacement.", 409);
|
|
21589
|
+
}
|
|
21590
|
+
await this.markReplacementComplete(storage, replacement.card.cardId, lock, principal, namespace);
|
|
21591
|
+
}
|
|
21592
|
+
async finishCommittedApproval(storage, replacement, lock, principal, namespace) {
|
|
21593
|
+
try {
|
|
21594
|
+
if (replacement.replacesDraftId || replacement.memory.frontmatter.supersedes) {
|
|
21595
|
+
await this.completeReplacementAfterActivation(storage, replacement, lock, principal, namespace);
|
|
21596
|
+
}
|
|
21597
|
+
} catch (error) {
|
|
21598
|
+
if (isStorageConflict(error)) {
|
|
21599
|
+
await this.rollbackConflictedApproval(storage, replacement.card.cardId, lock, principal, namespace);
|
|
21600
|
+
throw error;
|
|
21601
|
+
}
|
|
21602
|
+
log.warn(
|
|
21603
|
+
`support passport could not finish replacement approval side effects: ${error instanceof Error ? error.message : String(error)}`
|
|
21604
|
+
);
|
|
21605
|
+
}
|
|
21606
|
+
return replacement.card;
|
|
21607
|
+
}
|
|
21608
|
+
async rollbackConflictedApproval(storage, replacementId, lock, principal, namespace) {
|
|
21609
|
+
const replacement = await this.requireCard(storage, replacementId, namespace, principal);
|
|
21610
|
+
if (replacement.card.status !== "active") return;
|
|
21611
|
+
await this.requireOwnerLock(lock);
|
|
21612
|
+
const rolledBack = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21613
|
+
replacement.memory,
|
|
21614
|
+
{ status: "pending_review", updated: this.now().toISOString() },
|
|
21615
|
+
{ actor: principal, reasonCode: "replacement-predecessor-conflict" }
|
|
21616
|
+
);
|
|
21617
|
+
if (!rolledBack) {
|
|
21618
|
+
throw new SupportPassportError(
|
|
21619
|
+
"storage_conflict",
|
|
21620
|
+
"The replacement changed while its approval was rolled back.",
|
|
21621
|
+
409
|
|
21622
|
+
);
|
|
21623
|
+
}
|
|
21624
|
+
const priorId = replacement.memory.frontmatter.supersedes;
|
|
21625
|
+
if (priorId) {
|
|
21626
|
+
await this.restorePriorAfterApprovalFailure(storage, priorId, replacementId, lock, principal, namespace);
|
|
21627
|
+
}
|
|
21628
|
+
}
|
|
21629
|
+
async completePriorRetirement(storage, priorId, replacement, lock, principal, namespace) {
|
|
21630
|
+
const replacementId = replacement.card.cardId;
|
|
21631
|
+
const prior = await storage.getMemoryById(priorId);
|
|
21632
|
+
if (!prior || !this.ownsMemory(prior, namespace, principal) || prior.frontmatter.status !== "superseded" || prior.frontmatter.supersededBy !== replacementId) {
|
|
21633
|
+
return false;
|
|
21634
|
+
}
|
|
21635
|
+
await this.requireOwnerLock(lock);
|
|
21636
|
+
const completed = await storage.supersedeMemory(
|
|
21637
|
+
priorId,
|
|
21638
|
+
replacementId,
|
|
21639
|
+
"support-passport-replacement",
|
|
21640
|
+
{ supersessionCause: "direct" },
|
|
21641
|
+
{
|
|
21642
|
+
actor: principal,
|
|
21643
|
+
requireActive: true,
|
|
21644
|
+
acceptExactReplay: true,
|
|
21645
|
+
expectedSnapshot: prior,
|
|
21646
|
+
audit: {
|
|
21647
|
+
content: stripAttributesSuffix(prior.content),
|
|
21648
|
+
tags: ["supersession", "auto-resolved", "support-passport-audit"],
|
|
21649
|
+
structuredAttributes: {
|
|
21650
|
+
...encodeSupportPassportNamespaceAttributes(namespace),
|
|
21651
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.owner]: computeSupportPassportOwnerKey(principal)
|
|
21652
|
+
},
|
|
21653
|
+
source: "support-passport"
|
|
21654
|
+
}
|
|
21655
|
+
}
|
|
21656
|
+
);
|
|
21657
|
+
if (!completed) log.warn("support passport could not complete replacement retirement side effects");
|
|
21658
|
+
return completed;
|
|
21659
|
+
}
|
|
21660
|
+
async recoverReplacedDraft(storage, replacement, lock, principal, namespace) {
|
|
21661
|
+
if (!replacement.replacesDraftId) return;
|
|
21662
|
+
const replacedDraft = await storage.getMemoryById(replacement.replacesDraftId);
|
|
21663
|
+
const projectedDraft = replacedDraft ? this.projectOwnedCard(replacedDraft, namespace, principal) : null;
|
|
21664
|
+
if (replacedDraft && projectedDraft?.card.status === "pending_review" && projectedDraft.card.revision === replacement.replacedRevision) {
|
|
21665
|
+
await this.requireOwnerLock(lock);
|
|
21666
|
+
const rejected = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21667
|
+
replacedDraft,
|
|
21668
|
+
{ status: "rejected", updated: this.now().toISOString() },
|
|
21669
|
+
{ actor: principal, reasonCode: "complete-draft-replacement" }
|
|
21670
|
+
);
|
|
21671
|
+
if (rejected) {
|
|
21672
|
+
await this.finishPreparedDraftReplacement(storage, replacement.card.cardId, lock, principal, namespace);
|
|
21673
|
+
return;
|
|
21674
|
+
}
|
|
21675
|
+
}
|
|
21676
|
+
const currentDraft = await storage.getMemoryById(replacement.replacesDraftId);
|
|
21677
|
+
if (currentDraft && this.ownsMemory(currentDraft, namespace, principal) && currentDraft.frontmatter.status === "rejected") {
|
|
21678
|
+
await this.finishPreparedDraftReplacement(storage, replacement.card.cardId, lock, principal, namespace);
|
|
21679
|
+
return;
|
|
21680
|
+
}
|
|
21681
|
+
if (currentDraft && this.ownsMemory(currentDraft, namespace, principal) && currentDraft.frontmatter.status === "pending_review") {
|
|
21682
|
+
throw new SupportPassportError("storage_conflict", "The replaced draft changed during recovery.", 409);
|
|
21683
|
+
}
|
|
21684
|
+
await this.rejectOrphanedReplacement(storage, replacement.card.cardId, lock, principal, namespace);
|
|
21685
|
+
}
|
|
21686
|
+
async finishPreparedDraftReplacement(storage, replacementId, lock, principal, namespace) {
|
|
21687
|
+
const replacement = await this.requireCard(storage, replacementId, namespace, principal);
|
|
21688
|
+
if (!replacement.draftReplacementPrepared) return replacement.card;
|
|
21689
|
+
const structuredAttributes = { ...replacement.memory.frontmatter.structuredAttributes };
|
|
21690
|
+
delete structuredAttributes[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.draftReplacementPrepared];
|
|
21691
|
+
await this.requireOwnerLock(lock);
|
|
21692
|
+
const finished = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21693
|
+
replacement.memory,
|
|
21694
|
+
{ structuredAttributes },
|
|
21695
|
+
{ actor: principal, reasonCode: "draft-replacement-complete" }
|
|
21696
|
+
);
|
|
21697
|
+
if (finished) {
|
|
21698
|
+
return this.projectRequiredCard(
|
|
21699
|
+
{ ...replacement.memory, frontmatter: { ...replacement.memory.frontmatter, structuredAttributes } },
|
|
21700
|
+
namespace,
|
|
21701
|
+
principal
|
|
21702
|
+
).card;
|
|
21703
|
+
}
|
|
21704
|
+
const current = await this.requireCard(storage, replacementId, namespace, principal);
|
|
21705
|
+
if (!current.draftReplacementPrepared) return current.card;
|
|
21706
|
+
throw new SupportPassportError("storage_conflict", "The support card edit could not be completed.", 409);
|
|
21707
|
+
}
|
|
21708
|
+
async rejectOrphanedReplacement(storage, replacementId, lock, principal, namespace) {
|
|
21709
|
+
const currentReplacement = await this.requireCard(storage, replacementId, namespace, principal);
|
|
21710
|
+
if (currentReplacement.card.status === "rejected") return;
|
|
21711
|
+
this.requireStatus(currentReplacement, "pending_review");
|
|
21712
|
+
await this.requireOwnerLock(lock);
|
|
21713
|
+
const rejected = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21714
|
+
currentReplacement.memory,
|
|
21715
|
+
{ status: "rejected", updated: this.now().toISOString() },
|
|
21716
|
+
{ actor: principal, reasonCode: "replaced-draft-approved" }
|
|
21717
|
+
);
|
|
21718
|
+
if (rejected) return;
|
|
21719
|
+
const latest = await storage.getMemoryById(replacementId);
|
|
21720
|
+
if (latest?.frontmatter.status !== "rejected") {
|
|
21721
|
+
throw new SupportPassportError("storage_conflict", "The orphaned replacement could not be rejected.", 409);
|
|
21722
|
+
}
|
|
21723
|
+
}
|
|
21724
|
+
async restorePriorAfterApprovalFailure(storage, priorId, replacementId, lock, principal, namespace) {
|
|
21725
|
+
const replacement = await storage.getMemoryById(replacementId);
|
|
21726
|
+
if (replacement && !this.ownsMemory(replacement, namespace, principal)) {
|
|
21727
|
+
throw new SupportPassportError("storage_conflict", "The replacement support card changed ownership.", 409);
|
|
21728
|
+
}
|
|
21729
|
+
if (replacement?.frontmatter.status === "active") return;
|
|
21730
|
+
const prior = await storage.getMemoryById(priorId);
|
|
21731
|
+
if (prior && !this.ownsMemory(prior, namespace, principal)) {
|
|
21732
|
+
throw new SupportPassportError("storage_conflict", "The prior support card changed ownership.", 409);
|
|
21733
|
+
}
|
|
21734
|
+
if (prior?.frontmatter.status === "active" && prior.frontmatter.supersededBy === void 0) return;
|
|
21735
|
+
if (prior?.frontmatter.status !== "superseded" || prior.frontmatter.supersededBy !== replacementId) {
|
|
21736
|
+
throw new SupportPassportError("storage_conflict", "The prior support card could not be restored.", 409);
|
|
21737
|
+
}
|
|
21738
|
+
await this.requireOwnerLock(lock);
|
|
21739
|
+
const restored = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21740
|
+
prior,
|
|
21741
|
+
{
|
|
21742
|
+
status: "active",
|
|
21743
|
+
supersededBy: void 0,
|
|
21744
|
+
supersededAt: void 0,
|
|
21745
|
+
supersessionCause: void 0,
|
|
21746
|
+
invalidatedBy: void 0,
|
|
21747
|
+
updated: this.now().toISOString()
|
|
21748
|
+
},
|
|
21749
|
+
{ actor: principal, reasonCode: "replacement-activation-failed" }
|
|
21750
|
+
);
|
|
21751
|
+
if (restored) return;
|
|
21752
|
+
const currentPrior = await storage.getMemoryById(priorId);
|
|
21753
|
+
if (currentPrior?.frontmatter.status === "active" && currentPrior.frontmatter.supersededBy === void 0) return;
|
|
21754
|
+
throw new SupportPassportError("storage_conflict", "The prior support card could not be restored.", 409);
|
|
21755
|
+
}
|
|
21756
|
+
async markReplacementComplete(storage, replacementId, lock, principal, namespace) {
|
|
21757
|
+
const replacement = await this.requireCard(storage, replacementId, namespace, principal);
|
|
21758
|
+
if (replacement.memory.frontmatter.structuredAttributes?.[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.replacementComplete] === "true") {
|
|
21759
|
+
return;
|
|
21760
|
+
}
|
|
21761
|
+
await this.requireOwnerLock(lock);
|
|
21762
|
+
const marked = await storage.writeMemoryFrontmatterIfUnchanged(
|
|
21763
|
+
replacement.memory,
|
|
21764
|
+
{
|
|
21765
|
+
structuredAttributes: {
|
|
21766
|
+
...replacement.memory.frontmatter.structuredAttributes,
|
|
21767
|
+
[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.replacementComplete]: "true"
|
|
21768
|
+
}
|
|
21769
|
+
},
|
|
21770
|
+
{ actor: principal, reasonCode: "replacement-side-effects-complete" }
|
|
21771
|
+
);
|
|
21772
|
+
if (!marked) log.warn("support passport could not mark replacement side effects complete");
|
|
21773
|
+
}
|
|
21774
|
+
async requireOwnerLock(lock) {
|
|
21775
|
+
if (await lock.refresh()) return;
|
|
21776
|
+
throw new SupportPassportError("storage_conflict", "The support passport lock changed. Try again.", 409);
|
|
21777
|
+
}
|
|
21778
|
+
};
|
|
21779
|
+
|
|
21780
|
+
// src/support-passport/grant-contracts.ts
|
|
21781
|
+
import { z } from "zod";
|
|
21782
|
+
var IsoTimestampSchema = z.string().datetime({ offset: true }).refine((value) => parseIsoOffsetTimestamp(value) !== null, "timestamp must be a real date");
|
|
21783
|
+
var RevisionSchema = z.string().regex(/^[a-f0-9]{64}$/);
|
|
21784
|
+
var GrantIdSchema = z.string().uuid().transform((value) => value.toLowerCase());
|
|
21785
|
+
var SupportPassportGrantCardRefSchema = z.object({
|
|
21786
|
+
cardId: SupportPassportMemoryIdSchema,
|
|
21787
|
+
revision: RevisionSchema
|
|
21788
|
+
}).strict();
|
|
21789
|
+
var SupportPassportGrantStateSchema = z.object({
|
|
21790
|
+
schemaVersion: z.literal(1),
|
|
21791
|
+
stateVersion: z.number().int().positive(),
|
|
21792
|
+
grantId: GrantIdSchema,
|
|
21793
|
+
namespace: SupportPassportNamespaceSchema,
|
|
21794
|
+
principalHash: RevisionSchema,
|
|
21795
|
+
ownerLockKey: RevisionSchema,
|
|
21796
|
+
secretHash: RevisionSchema,
|
|
21797
|
+
cards: z.array(SupportPassportGrantCardRefSchema).min(1).max(8),
|
|
21798
|
+
createdAt: IsoTimestampSchema,
|
|
21799
|
+
expiresAt: IsoTimestampSchema,
|
|
21800
|
+
revokedAt: IsoTimestampSchema.optional()
|
|
21801
|
+
}).strict().superRefine((grant, ctx) => {
|
|
21802
|
+
const ids = /* @__PURE__ */ new Set();
|
|
21803
|
+
for (const [index, card] of grant.cards.entries()) {
|
|
21804
|
+
if (ids.has(card.cardId)) {
|
|
21805
|
+
ctx.addIssue({
|
|
21806
|
+
code: z.ZodIssueCode.custom,
|
|
21807
|
+
message: "card IDs must be unique",
|
|
21808
|
+
path: ["cards", index, "cardId"]
|
|
21809
|
+
});
|
|
21810
|
+
}
|
|
21811
|
+
ids.add(card.cardId);
|
|
21812
|
+
}
|
|
21813
|
+
if (Date.parse(grant.expiresAt) <= Date.parse(grant.createdAt)) {
|
|
21814
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "expiresAt must follow createdAt", path: ["expiresAt"] });
|
|
21815
|
+
}
|
|
21816
|
+
});
|
|
21817
|
+
var SupportPassportCreateGrantInputSchema = z.object({
|
|
21818
|
+
principal: z.string().trim().min(1).max(512),
|
|
21819
|
+
cards: z.array(SupportPassportGrantCardRefSchema).min(1).max(8),
|
|
21820
|
+
expiresAt: IsoTimestampSchema
|
|
21821
|
+
}).strict().superRefine((input, ctx) => {
|
|
21822
|
+
const ids = /* @__PURE__ */ new Set();
|
|
21823
|
+
for (const [index, card] of input.cards.entries()) {
|
|
21824
|
+
if (ids.has(card.cardId)) {
|
|
21825
|
+
ctx.addIssue({
|
|
21826
|
+
code: z.ZodIssueCode.custom,
|
|
21827
|
+
message: "card IDs must be unique",
|
|
21828
|
+
path: ["cards", index, "cardId"]
|
|
21829
|
+
});
|
|
21830
|
+
}
|
|
21831
|
+
ids.add(card.cardId);
|
|
21832
|
+
}
|
|
21833
|
+
});
|
|
21834
|
+
var SupportPassportOwnerGrantSchema = z.object({
|
|
21835
|
+
grantId: GrantIdSchema,
|
|
21836
|
+
stateVersion: z.number().int().positive(),
|
|
21837
|
+
cards: z.array(SupportPassportGrantCardRefSchema).min(1).max(8),
|
|
21838
|
+
createdAt: IsoTimestampSchema,
|
|
21839
|
+
expiresAt: IsoTimestampSchema,
|
|
21840
|
+
revokedAt: IsoTimestampSchema.optional(),
|
|
21841
|
+
status: z.enum(["active", "expired", "revoked"])
|
|
21842
|
+
}).strict();
|
|
21843
|
+
var SupportPassportCreatedGrantSchema = z.object({
|
|
21844
|
+
grant: SupportPassportOwnerGrantSchema,
|
|
21845
|
+
secret: z.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
21846
|
+
}).strict();
|
|
21847
|
+
var SupportPassportPublicCardSchema = z.object({
|
|
21848
|
+
cardId: SupportPassportMemoryIdSchema,
|
|
21849
|
+
title: z.string().trim().min(1).max(80),
|
|
21850
|
+
statement: z.string().trim().min(1).max(500),
|
|
21851
|
+
category: SupportPassportCardCategorySchema,
|
|
21852
|
+
updatedAt: IsoTimestampSchema
|
|
21853
|
+
}).strict();
|
|
21854
|
+
var SupportPassportPublicGuideSchema = z.object({
|
|
21855
|
+
schemaVersion: z.literal(1),
|
|
21856
|
+
grantId: GrantIdSchema,
|
|
21857
|
+
expiresAt: IsoTimestampSchema,
|
|
21858
|
+
updatedAt: IsoTimestampSchema,
|
|
21859
|
+
cards: z.array(SupportPassportPublicCardSchema).min(1).max(8)
|
|
21860
|
+
}).strict();
|
|
21861
|
+
var SupportPassportRevokeGrantInputSchema = z.object({
|
|
21862
|
+
principal: z.string().trim().min(1).max(512),
|
|
21863
|
+
grantId: GrantIdSchema,
|
|
21864
|
+
expectedStateVersion: z.number().int().positive().optional()
|
|
21865
|
+
}).strict();
|
|
21866
|
+
var SupportPassportListGrantsInputSchema = z.object({
|
|
21867
|
+
principal: z.string().trim().min(1).max(512)
|
|
21868
|
+
}).strict();
|
|
21869
|
+
|
|
21870
|
+
// src/support-passport/grant-store.ts
|
|
21871
|
+
import { createHash as createHash10, randomBytes as randomBytes3, randomUUID as randomUUID3, timingSafeEqual } from "crypto";
|
|
21872
|
+
import { lstat as lstat4, open as open2, readdir as readdir4 } from "fs/promises";
|
|
21873
|
+
import path23 from "path";
|
|
21874
|
+
|
|
21875
|
+
// src/support-passport/private-file.ts
|
|
21876
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
21877
|
+
import { constants as fsConstants } from "fs";
|
|
21878
|
+
import { lstat as lstat3, mkdir as mkdir7, open, rename, rm, stat as stat4 } from "fs/promises";
|
|
21879
|
+
import path22 from "path";
|
|
21880
|
+
var UNSUPPORTED_DIRECTORY_SYNC_ERRORS = /* @__PURE__ */ new Set(["EINVAL", "ENOSYS", "ENOTSUP", "EOPNOTSUPP"]);
|
|
21881
|
+
var NON_WRITABLE_ANCESTOR_SYNC_ERRORS = /* @__PURE__ */ new Set(["EROFS", "EPERM", "EACCES"]);
|
|
21882
|
+
function assertStableDirectory(before, opened, after, errorMessage) {
|
|
21883
|
+
if (before.isSymbolicLink() || !before.isDirectory() || after.isSymbolicLink() || !after.isDirectory() || before.dev !== opened.dev || before.ino !== opened.ino || after.dev !== opened.dev || after.ino !== opened.ino) {
|
|
21884
|
+
throw new Error(errorMessage);
|
|
21885
|
+
}
|
|
21886
|
+
}
|
|
21887
|
+
function assertStableRegularFile(before, opened, after, errorMessage) {
|
|
21888
|
+
if (before.isSymbolicLink() || !before.isFile() || after.isSymbolicLink() || !after.isFile() || before.dev !== opened.dev || before.ino !== opened.ino || after.dev !== opened.dev || after.ino !== opened.ino || opened.nlink !== 1) {
|
|
21889
|
+
throw new Error(errorMessage);
|
|
21890
|
+
}
|
|
21891
|
+
}
|
|
21892
|
+
function openFlags(...flags) {
|
|
21893
|
+
let combined = 0;
|
|
21894
|
+
for (const flag of flags) combined |= typeof flag === "number" ? flag : 0;
|
|
21895
|
+
return combined;
|
|
21896
|
+
}
|
|
21897
|
+
function assertContainedPath(trustedRoot, directory, errorMessage) {
|
|
21898
|
+
const relative = path22.relative(path22.resolve(trustedRoot), path22.resolve(directory));
|
|
21899
|
+
if (relative === ".." || relative.startsWith(`..${path22.sep}`) || path22.isAbsolute(relative)) {
|
|
21900
|
+
throw new Error(errorMessage);
|
|
21901
|
+
}
|
|
21902
|
+
}
|
|
21903
|
+
async function openDirectoryNoFollow(directory, errorMessage) {
|
|
21904
|
+
const before = await lstat3(directory);
|
|
21905
|
+
if (before.isSymbolicLink() || !before.isDirectory()) throw new Error(errorMessage);
|
|
21906
|
+
const handle = await open(
|
|
21907
|
+
directory,
|
|
21908
|
+
openFlags(fsConstants.O_RDONLY, fsConstants.O_DIRECTORY, fsConstants.O_NOFOLLOW)
|
|
21909
|
+
);
|
|
21910
|
+
try {
|
|
21911
|
+
const opened = await handle.stat();
|
|
21912
|
+
assertStableDirectory(before, opened, await lstat3(directory), errorMessage);
|
|
21913
|
+
return { before, opened, handle };
|
|
21914
|
+
} catch (error) {
|
|
21915
|
+
await handle.close().catch(() => void 0);
|
|
21916
|
+
throw error;
|
|
21917
|
+
}
|
|
21918
|
+
}
|
|
21919
|
+
async function closeDirectoryHandles(handles) {
|
|
21920
|
+
for (const handle of handles.reverse()) await handle.close().catch(() => void 0);
|
|
21921
|
+
}
|
|
21922
|
+
async function syncDirectoryHandle(handle) {
|
|
21923
|
+
try {
|
|
21924
|
+
await handle.sync();
|
|
21925
|
+
} catch (error) {
|
|
21926
|
+
const code = error.code;
|
|
21927
|
+
if (!UNSUPPORTED_DIRECTORY_SYNC_ERRORS.has(code ?? "")) throw error;
|
|
21928
|
+
}
|
|
21929
|
+
}
|
|
21930
|
+
async function resolvePrivateDirectoryPath(directory, handle, opened, errorMessage, platform = process.platform) {
|
|
21931
|
+
const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
21932
|
+
const pinnedPath = path22.join(descriptorRoot, String(handle.fd));
|
|
21933
|
+
const metadata = await stat4(pinnedPath);
|
|
21934
|
+
if (!metadata.isDirectory() || metadata.dev !== opened.dev || metadata.ino !== opened.ino) {
|
|
21935
|
+
throw new Error(errorMessage);
|
|
21936
|
+
}
|
|
21937
|
+
return pinnedPath;
|
|
21938
|
+
}
|
|
21939
|
+
function requirePrivateFileDescriptorRoot(platform, errorMessage) {
|
|
21940
|
+
if (platform === "linux") return "/proc/self/fd";
|
|
21941
|
+
if (platform === "darwin" || platform === "freebsd" || platform === "openbsd") return "/dev/fd";
|
|
21942
|
+
throw new Error(errorMessage);
|
|
21943
|
+
}
|
|
21944
|
+
async function openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform = process.platform) {
|
|
21945
|
+
const root = path22.resolve(trustedRoot);
|
|
21946
|
+
const target = path22.resolve(directory);
|
|
21947
|
+
assertContainedPath(root, target, errorMessage);
|
|
21948
|
+
requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
21949
|
+
const relative = path22.relative(root, target);
|
|
21950
|
+
const components = relative === "" ? [] : relative.split(path22.sep);
|
|
21951
|
+
const handles = [];
|
|
21952
|
+
let currentPath = root;
|
|
21953
|
+
try {
|
|
21954
|
+
let current = await openDirectoryNoFollow(root, errorMessage);
|
|
21955
|
+
handles.push(current.handle);
|
|
21956
|
+
for (const component of components) {
|
|
21957
|
+
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
|
|
21958
|
+
const child = await openDirectoryNoFollow(path22.join(pinnedParent, component), errorMessage);
|
|
21959
|
+
handles.push(child.handle);
|
|
21960
|
+
assertStableDirectory(current.before, current.opened, await lstat3(currentPath), errorMessage);
|
|
21961
|
+
currentPath = path22.join(currentPath, component);
|
|
21962
|
+
current = child;
|
|
21963
|
+
}
|
|
21964
|
+
assertStableDirectory(current.before, current.opened, await lstat3(target), errorMessage);
|
|
21965
|
+
return {
|
|
21966
|
+
...current,
|
|
21967
|
+
handles,
|
|
21968
|
+
pinnedDirectory: await resolvePrivateDirectoryPath(
|
|
21969
|
+
target,
|
|
21970
|
+
current.handle,
|
|
21971
|
+
current.opened,
|
|
21972
|
+
errorMessage,
|
|
21973
|
+
platform
|
|
21974
|
+
)
|
|
21975
|
+
};
|
|
21976
|
+
} catch (error) {
|
|
21977
|
+
await closeDirectoryHandles(handles);
|
|
21978
|
+
throw error;
|
|
21979
|
+
}
|
|
21980
|
+
}
|
|
21981
|
+
async function ensurePrivateDirectoryNoFollow(trustedRoot, directory, errorMessage, syncVerifiedParent = syncDirectoryHandle, hardenExistingChildren = true, platform = process.platform) {
|
|
21982
|
+
const root = path22.resolve(trustedRoot);
|
|
21983
|
+
const target = path22.resolve(directory);
|
|
21984
|
+
assertContainedPath(root, target, errorMessage);
|
|
21985
|
+
const relative = path22.relative(root, target);
|
|
21986
|
+
requirePrivateFileDescriptorRoot(platform, errorMessage);
|
|
21987
|
+
const components = relative === "" ? [] : relative.split(path22.sep);
|
|
21988
|
+
const handles = [];
|
|
21989
|
+
let currentPath = root;
|
|
21990
|
+
try {
|
|
21991
|
+
let current = await openDirectoryNoFollow(root, errorMessage);
|
|
21992
|
+
handles.push(current.handle);
|
|
21993
|
+
for (const component of components) {
|
|
21994
|
+
const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
|
|
21995
|
+
const childPath = path22.join(pinnedParent, component);
|
|
21996
|
+
let created = false;
|
|
21997
|
+
try {
|
|
21998
|
+
await mkdir7(childPath, { mode: 448 });
|
|
21999
|
+
created = true;
|
|
22000
|
+
} catch (error) {
|
|
22001
|
+
if (error.code !== "EEXIST") throw error;
|
|
22002
|
+
}
|
|
22003
|
+
await syncVerifiedParent(current.handle);
|
|
22004
|
+
const child = await openDirectoryNoFollow(childPath, errorMessage);
|
|
22005
|
+
handles.push(child.handle);
|
|
22006
|
+
if (created || hardenExistingChildren && (child.opened.mode & 511) !== 448) {
|
|
22007
|
+
await child.handle.chmod(448);
|
|
22008
|
+
}
|
|
22009
|
+
assertStableDirectory(current.before, current.opened, await lstat3(currentPath), errorMessage);
|
|
22010
|
+
currentPath = path22.join(currentPath, component);
|
|
22011
|
+
current = child;
|
|
22012
|
+
}
|
|
22013
|
+
assertStableDirectory(current.before, current.opened, await lstat3(target), errorMessage);
|
|
22014
|
+
} finally {
|
|
22015
|
+
await closeDirectoryHandles(handles);
|
|
22016
|
+
}
|
|
22017
|
+
}
|
|
22018
|
+
async function ensurePrivateDirectoryTreeNoFollow(directory, errorMessage, syncVerifiedParent = syncDirectoryHandle, platform = process.platform) {
|
|
22019
|
+
const target = path22.resolve(directory);
|
|
22020
|
+
const filesystemRoot = path22.parse(target).root;
|
|
22021
|
+
const rootMetadata = await stat4(filesystemRoot);
|
|
22022
|
+
await ensurePrivateDirectoryNoFollow(
|
|
22023
|
+
filesystemRoot,
|
|
22024
|
+
target,
|
|
22025
|
+
errorMessage,
|
|
22026
|
+
async (handle) => {
|
|
22027
|
+
try {
|
|
22028
|
+
await syncVerifiedParent(handle);
|
|
22029
|
+
} catch (error) {
|
|
22030
|
+
const code = error.code;
|
|
22031
|
+
const metadata = await handle.stat();
|
|
22032
|
+
if (metadata.ino === rootMetadata.ino && metadata.dev === rootMetadata.dev && NON_WRITABLE_ANCESTOR_SYNC_ERRORS.has(code ?? "")) {
|
|
22033
|
+
return;
|
|
22034
|
+
}
|
|
22035
|
+
throw error;
|
|
22036
|
+
}
|
|
22037
|
+
},
|
|
22038
|
+
false,
|
|
22039
|
+
platform
|
|
22040
|
+
);
|
|
22041
|
+
}
|
|
22042
|
+
async function withPrivateDirectoryNoFollow(trustedRoot, directory, errorMessage, task, platform = process.platform) {
|
|
22043
|
+
let directoryHandles = [];
|
|
22044
|
+
try {
|
|
22045
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
22046
|
+
directoryHandles = stableDirectory.handles;
|
|
22047
|
+
const result = await task(stableDirectory.pinnedDirectory);
|
|
22048
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat3(directory), errorMessage);
|
|
22049
|
+
return result;
|
|
22050
|
+
} finally {
|
|
22051
|
+
await closeDirectoryHandles(directoryHandles);
|
|
22052
|
+
}
|
|
22053
|
+
}
|
|
22054
|
+
async function readPrivateFileNoFollow(directory, filePath, errorMessage, trustedRoot = directory, platform = process.platform) {
|
|
22055
|
+
if (path22.dirname(filePath) !== path22.resolve(directory)) throw new Error(errorMessage);
|
|
22056
|
+
const targetName = path22.basename(filePath);
|
|
22057
|
+
let directoryHandles = [];
|
|
22058
|
+
let fileHandle;
|
|
22059
|
+
try {
|
|
22060
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
22061
|
+
directoryHandles = stableDirectory.handles;
|
|
22062
|
+
const pinnedDirectory = stableDirectory.pinnedDirectory;
|
|
22063
|
+
const targetPath = path22.join(pinnedDirectory, targetName);
|
|
22064
|
+
const before = await lstat3(targetPath);
|
|
22065
|
+
if (before.isSymbolicLink() || !before.isFile()) throw new Error(errorMessage);
|
|
22066
|
+
try {
|
|
22067
|
+
fileHandle = await open(targetPath, openFlags(fsConstants.O_RDONLY, fsConstants.O_NOFOLLOW));
|
|
22068
|
+
} catch (error) {
|
|
22069
|
+
if (error.code === "ELOOP") throw new Error(errorMessage);
|
|
22070
|
+
throw error;
|
|
22071
|
+
}
|
|
22072
|
+
const fileMetadata = await fileHandle.stat();
|
|
22073
|
+
assertStableRegularFile(before, fileMetadata, await lstat3(targetPath), errorMessage);
|
|
22074
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat3(directory), errorMessage);
|
|
22075
|
+
return await fileHandle.readFile("utf8");
|
|
22076
|
+
} finally {
|
|
22077
|
+
await fileHandle?.close().catch(() => void 0);
|
|
22078
|
+
await closeDirectoryHandles(directoryHandles);
|
|
22079
|
+
}
|
|
22080
|
+
}
|
|
22081
|
+
async function writePrivateFileAtomicallyNoFollow(directory, filePath, content, errorMessage, trustedRoot = directory, platform = process.platform) {
|
|
22082
|
+
if (path22.dirname(filePath) !== path22.resolve(directory)) throw new Error(errorMessage);
|
|
22083
|
+
const targetName = path22.basename(filePath);
|
|
22084
|
+
const tempName = `.${targetName}.${randomUUID2()}.tmp`;
|
|
22085
|
+
let tempPath;
|
|
22086
|
+
let directoryHandles = [];
|
|
22087
|
+
let fileHandle;
|
|
22088
|
+
try {
|
|
22089
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
22090
|
+
directoryHandles = stableDirectory.handles;
|
|
22091
|
+
const pinnedDirectory = stableDirectory.pinnedDirectory;
|
|
22092
|
+
tempPath = path22.join(pinnedDirectory, tempName);
|
|
22093
|
+
const targetPath = path22.join(pinnedDirectory, targetName);
|
|
22094
|
+
try {
|
|
22095
|
+
fileHandle = await open(
|
|
22096
|
+
tempPath,
|
|
22097
|
+
openFlags(fsConstants.O_WRONLY, fsConstants.O_CREAT, fsConstants.O_EXCL, fsConstants.O_NOFOLLOW),
|
|
22098
|
+
384
|
|
22099
|
+
);
|
|
22100
|
+
} catch (error) {
|
|
22101
|
+
if (error.code === "ELOOP") throw new Error(errorMessage);
|
|
22102
|
+
throw error;
|
|
22103
|
+
}
|
|
22104
|
+
const fileMetadata = await fileHandle.stat();
|
|
22105
|
+
const tempMetadata = await lstat3(tempPath);
|
|
22106
|
+
assertStableRegularFile(tempMetadata, fileMetadata, await lstat3(tempPath), errorMessage);
|
|
22107
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat3(directory), errorMessage);
|
|
22108
|
+
await fileHandle.chmod(384);
|
|
22109
|
+
await fileHandle.writeFile(content, "utf8");
|
|
22110
|
+
await fileHandle.sync();
|
|
22111
|
+
await fileHandle.close();
|
|
22112
|
+
fileHandle = void 0;
|
|
22113
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat3(directory), errorMessage);
|
|
22114
|
+
await rename(tempPath, targetPath);
|
|
22115
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat3(directory), errorMessage);
|
|
22116
|
+
if (stableDirectory.handle) await syncDirectoryHandle(stableDirectory.handle);
|
|
22117
|
+
} finally {
|
|
22118
|
+
await fileHandle?.close().catch(() => void 0);
|
|
22119
|
+
if (tempPath) await rm(tempPath, { force: true }).catch(() => void 0);
|
|
22120
|
+
await closeDirectoryHandles(directoryHandles);
|
|
22121
|
+
}
|
|
22122
|
+
}
|
|
22123
|
+
async function removePrivateFilesNoFollow(directory, fileNames, errorMessage, trustedRoot = directory, platform = process.platform) {
|
|
22124
|
+
if (fileNames.some(
|
|
22125
|
+
(fileName) => fileName.length === 0 || fileName === "." || fileName === ".." || path22.basename(fileName) !== fileName
|
|
22126
|
+
)) {
|
|
22127
|
+
throw new Error(errorMessage);
|
|
22128
|
+
}
|
|
22129
|
+
let directoryHandles = [];
|
|
22130
|
+
try {
|
|
22131
|
+
const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
|
|
22132
|
+
directoryHandles = stableDirectory.handles;
|
|
22133
|
+
const pinnedDirectory = stableDirectory.pinnedDirectory;
|
|
22134
|
+
for (const fileName of fileNames) {
|
|
22135
|
+
await rm(path22.join(pinnedDirectory, fileName), { force: true });
|
|
22136
|
+
}
|
|
22137
|
+
assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat3(directory), errorMessage);
|
|
22138
|
+
if (stableDirectory.handle) await syncDirectoryHandle(stableDirectory.handle);
|
|
22139
|
+
} finally {
|
|
22140
|
+
await closeDirectoryHandles(directoryHandles);
|
|
22141
|
+
}
|
|
22142
|
+
}
|
|
22143
|
+
|
|
22144
|
+
// src/support-passport/grant-store.ts
|
|
22145
|
+
var GRANT_LOCK_STALE_MS = 3e4;
|
|
22146
|
+
var GRANT_LOCK_WAIT_MS = 5e3;
|
|
22147
|
+
var GRANT_LOCK_HEARTBEAT_MS = 1e4;
|
|
22148
|
+
var SAFE_GRANT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
22149
|
+
var UUID_INPUT = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
22150
|
+
var SAFE_OWNER_INDEX_HASH = /^[0-9a-f]{64}$/;
|
|
22151
|
+
var UNSUPPORTED_DIRECTORY_SYNC_ERRORS2 = /* @__PURE__ */ new Set(["EINVAL", "ENOSYS", "ENOTSUP", "EOPNOTSUPP"]);
|
|
22152
|
+
var MAX_OWNER_GRANT_HISTORY = 100;
|
|
22153
|
+
var MAX_OWNER_INDEX_RECOVERY_ATTEMPTS = 3;
|
|
22154
|
+
var OwnerIndexLockLostError = class extends Error {
|
|
22155
|
+
constructor(cause) {
|
|
22156
|
+
super("support passport owner index lock was lost", { cause });
|
|
22157
|
+
this.name = "OwnerIndexLockLostError";
|
|
22158
|
+
}
|
|
22159
|
+
};
|
|
22160
|
+
var MalformedGrantStateError = class extends Error {
|
|
22161
|
+
constructor(message = "support passport grant state is invalid", cause) {
|
|
22162
|
+
super(message, { cause });
|
|
22163
|
+
this.name = "MalformedGrantStateError";
|
|
22164
|
+
}
|
|
22165
|
+
};
|
|
22166
|
+
function sha256(domain, value) {
|
|
22167
|
+
return createHash10("sha256").update(domain).update("\0").update(value).digest("hex");
|
|
22168
|
+
}
|
|
22169
|
+
function hashesMatch(left, right) {
|
|
22170
|
+
const leftBytes = Buffer.from(left, "hex");
|
|
22171
|
+
const rightBytes = Buffer.from(right, "hex");
|
|
22172
|
+
return leftBytes.length === 32 && rightBytes.length === 32 && timingSafeEqual(leftBytes, rightBytes);
|
|
22173
|
+
}
|
|
22174
|
+
function sameGrantState(left, right) {
|
|
22175
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
22176
|
+
}
|
|
22177
|
+
function newestGrantFirst(left, right) {
|
|
22178
|
+
return Date.parse(right.createdAt) - Date.parse(left.createdAt) || left.grantId.localeCompare(right.grantId);
|
|
22179
|
+
}
|
|
22180
|
+
function grantNotFound() {
|
|
22181
|
+
return new SupportPassportError("grant_not_found", "The share link was not found.", 404);
|
|
22182
|
+
}
|
|
22183
|
+
function warnCommitNotificationFailure(error) {
|
|
22184
|
+
log.warn(`support passport commit notification failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
22185
|
+
}
|
|
22186
|
+
function notifySupportPassportCommitted(callback) {
|
|
22187
|
+
try {
|
|
22188
|
+
const completion = callback?.();
|
|
22189
|
+
if (completion) void Promise.resolve(completion).catch(warnCommitNotificationFailure);
|
|
22190
|
+
} catch (error) {
|
|
22191
|
+
warnCommitNotificationFailure(error);
|
|
22192
|
+
}
|
|
22193
|
+
}
|
|
22194
|
+
function normalizeGrantId(grantId) {
|
|
22195
|
+
if (typeof grantId !== "string" || !UUID_INPUT.test(grantId)) throw grantNotFound();
|
|
22196
|
+
return grantId.toLowerCase();
|
|
22197
|
+
}
|
|
22198
|
+
function normalizeNamespace(namespace) {
|
|
22199
|
+
const parsed = SupportPassportNamespaceSchema.safeParse(namespace);
|
|
22200
|
+
if (!parsed.success) {
|
|
22201
|
+
throw new SupportPassportError("invalid_input", "The share link request is invalid.", 400);
|
|
22202
|
+
}
|
|
22203
|
+
return parsed.data;
|
|
22204
|
+
}
|
|
22205
|
+
function normalizePrincipal(principal) {
|
|
22206
|
+
const normalized = typeof principal === "string" ? principal.trim() : "";
|
|
22207
|
+
if (normalized.length < 1 || normalized.length > 512) {
|
|
22208
|
+
throw new SupportPassportError("invalid_input", "The share link request is invalid.", 400);
|
|
22209
|
+
}
|
|
22210
|
+
return normalized;
|
|
22211
|
+
}
|
|
22212
|
+
async function syncDirectoryForDurability(directory, openDirectory = async (target) => await open2(target, "r")) {
|
|
22213
|
+
let handle;
|
|
22214
|
+
try {
|
|
22215
|
+
handle = await openDirectory(directory);
|
|
22216
|
+
await handle.sync();
|
|
22217
|
+
} catch (error) {
|
|
22218
|
+
const code = error.code;
|
|
22219
|
+
if (!UNSUPPORTED_DIRECTORY_SYNC_ERRORS2.has(code ?? "")) throw error;
|
|
22220
|
+
} finally {
|
|
22221
|
+
await handle?.close().catch(() => void 0);
|
|
22222
|
+
}
|
|
22223
|
+
}
|
|
22224
|
+
var SupportPassportGrantStore = class {
|
|
22225
|
+
memoryDir;
|
|
22226
|
+
grantsDir;
|
|
22227
|
+
ownerIndexesDir;
|
|
22228
|
+
memoryRootReady;
|
|
22229
|
+
safeDirectoriesReady;
|
|
22230
|
+
now;
|
|
22231
|
+
makeSecret;
|
|
22232
|
+
makeGrantId;
|
|
22233
|
+
runWithHeldFileLock;
|
|
22234
|
+
syncDirectory;
|
|
22235
|
+
constructor(options) {
|
|
22236
|
+
this.memoryDir = path23.resolve(expandTildePath(options.memoryDir));
|
|
22237
|
+
this.grantsDir = path23.join(this.memoryDir, "state", "support-passport", "grants");
|
|
22238
|
+
this.ownerIndexesDir = path23.join(this.grantsDir, "owners");
|
|
22239
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
22240
|
+
this.makeSecret = options.makeSecret ?? (() => randomBytes3(32));
|
|
22241
|
+
this.makeGrantId = options.makeGrantId ?? randomUUID3;
|
|
22242
|
+
this.runWithHeldFileLock = options.withHeldFileLock ?? withHeldFileLock;
|
|
22243
|
+
this.syncDirectory = options.syncDirectory ?? syncDirectoryForDurability;
|
|
22244
|
+
}
|
|
22245
|
+
async create(input, hooks = {}) {
|
|
22246
|
+
const mutationHooks = typeof hooks === "function" ? { beforeCommit: hooks } : hooks;
|
|
22247
|
+
const namespace = normalizeNamespace(input.namespace);
|
|
22248
|
+
const parsed = SupportPassportCreateGrantInputSchema.safeParse({
|
|
22249
|
+
principal: input.principal,
|
|
22250
|
+
cards: input.cards,
|
|
22251
|
+
expiresAt: input.expiresAt
|
|
22252
|
+
});
|
|
22253
|
+
if (!parsed.success) {
|
|
22254
|
+
throw new SupportPassportError("invalid_input", "The share link request is invalid.", 400);
|
|
22255
|
+
}
|
|
22256
|
+
const requestedAt = input.requestedAt ?? this.now();
|
|
22257
|
+
const durationMs = Date.parse(parsed.data.expiresAt) - requestedAt.getTime();
|
|
22258
|
+
if (!Number.isFinite(requestedAt.getTime()) || !Number.isFinite(durationMs) || durationMs < 3e5 || durationMs > 6048e5) {
|
|
22259
|
+
throw new SupportPassportError("invalid_input", "The share link request is invalid.", 400);
|
|
22260
|
+
}
|
|
22261
|
+
const principalHash = computeSupportPassportOwnerKey(parsed.data.principal);
|
|
22262
|
+
const ownerHash = this.ownerHash(namespace, principalHash);
|
|
22263
|
+
let committed;
|
|
22264
|
+
try {
|
|
22265
|
+
let finalized;
|
|
22266
|
+
try {
|
|
22267
|
+
finalized = await this.withOwnerIndexLock(ownerHash, async (lock) => {
|
|
22268
|
+
await this.requireAvailableOwnerGrantSlot(namespace, principalHash);
|
|
22269
|
+
committed = await this.withMutationLock(async (mutationLock) => {
|
|
22270
|
+
const secretBytes = this.makeSecret();
|
|
22271
|
+
if (!Buffer.isBuffer(secretBytes) || secretBytes.length !== 32) {
|
|
22272
|
+
throw new Error("SupportPassportGrantStore.makeSecret must return 32 bytes");
|
|
22273
|
+
}
|
|
22274
|
+
const rawGrantId = this.makeGrantId();
|
|
22275
|
+
if (!UUID_INPUT.test(rawGrantId)) {
|
|
22276
|
+
throw new Error("SupportPassportGrantStore.makeGrantId must return a UUID");
|
|
22277
|
+
}
|
|
22278
|
+
const grantId = rawGrantId.toLowerCase();
|
|
22279
|
+
const secret = secretBytes.toString("base64url");
|
|
22280
|
+
const createdAt = this.now();
|
|
22281
|
+
const createdAtMs = createdAt.getTime();
|
|
22282
|
+
const expiresAtMs = Date.parse(parsed.data.expiresAt);
|
|
22283
|
+
if (!Number.isFinite(createdAtMs) || requestedAt.getTime() > createdAtMs || expiresAtMs <= createdAtMs || expiresAtMs - createdAtMs > 6048e5) {
|
|
22284
|
+
throw new SupportPassportError("invalid_input", "The share link request is invalid.", 400);
|
|
22285
|
+
}
|
|
22286
|
+
const state = SupportPassportGrantStateSchema.parse({
|
|
22287
|
+
schemaVersion: 1,
|
|
22288
|
+
stateVersion: 1,
|
|
22289
|
+
grantId,
|
|
22290
|
+
namespace,
|
|
22291
|
+
principalHash,
|
|
22292
|
+
ownerLockKey: computeSupportPassportOwnerLockKey(namespace, parsed.data.principal),
|
|
22293
|
+
secretHash: sha256("support-passport-secret:v1", secret),
|
|
22294
|
+
cards: parsed.data.cards,
|
|
22295
|
+
createdAt: createdAt.toISOString(),
|
|
22296
|
+
expiresAt: parsed.data.expiresAt
|
|
22297
|
+
});
|
|
22298
|
+
await this.requireMutationLock(mutationLock);
|
|
22299
|
+
await mutationHooks.beforeCommit?.();
|
|
22300
|
+
await this.requireMutationLock(mutationLock);
|
|
22301
|
+
try {
|
|
22302
|
+
await this.writeState(state, true);
|
|
22303
|
+
} catch (error) {
|
|
22304
|
+
const persisted2 = await this.readState(state.grantId).catch(() => void 0);
|
|
22305
|
+
if (!persisted2 || !sameGrantState(persisted2, state)) throw error;
|
|
22306
|
+
try {
|
|
22307
|
+
await this.syncDirectory(this.grantsDir);
|
|
22308
|
+
} catch {
|
|
22309
|
+
await this.removeGrantStates([state.grantId]).catch(() => void 0);
|
|
22310
|
+
throw error;
|
|
22311
|
+
}
|
|
22312
|
+
}
|
|
22313
|
+
try {
|
|
22314
|
+
await this.writeOwnerMembership(state);
|
|
22315
|
+
} catch (error) {
|
|
22316
|
+
await this.removeStoredGrant(state).catch(() => void 0);
|
|
22317
|
+
throw error;
|
|
22318
|
+
}
|
|
22319
|
+
const persisted = { state, secret };
|
|
22320
|
+
committed = persisted;
|
|
22321
|
+
return persisted;
|
|
22322
|
+
});
|
|
22323
|
+
try {
|
|
22324
|
+
await this.addToOwnerIndex(committed.state, lock);
|
|
22325
|
+
} catch (error) {
|
|
22326
|
+
if (error instanceof OwnerIndexLockLostError) throw error;
|
|
22327
|
+
const indexed = await this.readOwnerIndexByHash(ownerHash).catch(() => void 0);
|
|
22328
|
+
if (!indexed?.includes(committed.state.grantId)) throw error;
|
|
22329
|
+
try {
|
|
22330
|
+
await this.syncDirectory(this.ownerIndexesDir);
|
|
22331
|
+
} catch {
|
|
22332
|
+
throw error;
|
|
22333
|
+
}
|
|
22334
|
+
await this.requireOwnerIndexLock(lock);
|
|
22335
|
+
}
|
|
22336
|
+
return committed;
|
|
22337
|
+
});
|
|
22338
|
+
} catch (error) {
|
|
22339
|
+
if (!(error instanceof OwnerIndexLockLostError) || !committed) throw error;
|
|
22340
|
+
finalized = await this.reconcileCreateAfterOwnerIndexLockLoss(committed);
|
|
22341
|
+
}
|
|
22342
|
+
notifySupportPassportCommitted(mutationHooks.onCommitted);
|
|
22343
|
+
return finalized;
|
|
22344
|
+
} catch (error) {
|
|
22345
|
+
if (committed) {
|
|
22346
|
+
await this.withGrantLock(committed.state.grantId, async (lock) => {
|
|
22347
|
+
await this.requireMutationLock(lock);
|
|
22348
|
+
await this.removeStoredGrant(committed.state);
|
|
22349
|
+
}).catch(() => void 0);
|
|
22350
|
+
}
|
|
22351
|
+
throw error;
|
|
22352
|
+
}
|
|
22353
|
+
}
|
|
22354
|
+
async authenticate(grantId, secret) {
|
|
22355
|
+
const normalizedGrantId = normalizeGrantId(grantId);
|
|
22356
|
+
if (typeof secret !== "string" || secret.length > 512) throw grantNotFound();
|
|
22357
|
+
let state;
|
|
22358
|
+
try {
|
|
22359
|
+
state = await this.readState(normalizedGrantId);
|
|
22360
|
+
} catch (error) {
|
|
22361
|
+
if (error.code === "ENOENT") throw grantNotFound();
|
|
22362
|
+
throw error;
|
|
22363
|
+
}
|
|
22364
|
+
const candidate = sha256("support-passport-secret:v1", secret);
|
|
22365
|
+
if (!hashesMatch(candidate, state.secretHash)) throw grantNotFound();
|
|
22366
|
+
if (state.revokedAt) {
|
|
22367
|
+
throw new SupportPassportError("grant_gone", "The share link is no longer active.", 410);
|
|
22368
|
+
}
|
|
22369
|
+
if (Date.parse(state.expiresAt) <= this.now().getTime()) {
|
|
22370
|
+
throw new SupportPassportError("grant_expired", "The share link has expired.", 410);
|
|
22371
|
+
}
|
|
22372
|
+
return state;
|
|
22373
|
+
}
|
|
22374
|
+
async withAuthenticatedGrant(grantId, secret, task, beforeReturn) {
|
|
22375
|
+
return await this.withGrantLock(grantId, async (lock) => {
|
|
22376
|
+
const state = await this.authenticate(grantId, secret);
|
|
22377
|
+
const result = await task(state);
|
|
22378
|
+
await this.requireMutationLock(lock);
|
|
22379
|
+
const finalState = await this.authenticate(grantId, secret);
|
|
22380
|
+
await this.requireMutationLock(lock);
|
|
22381
|
+
if (finalState.stateVersion !== state.stateVersion) {
|
|
22382
|
+
throw new SupportPassportError("grant_stale", "The shared support guide has changed.", 410);
|
|
22383
|
+
}
|
|
22384
|
+
this.requireActiveState(finalState);
|
|
22385
|
+
await beforeReturn?.(finalState);
|
|
22386
|
+
await this.requireMutationLock(lock);
|
|
22387
|
+
this.requireActiveState(finalState);
|
|
22388
|
+
return result;
|
|
22389
|
+
});
|
|
22390
|
+
}
|
|
22391
|
+
async listForOwner(namespace, principal) {
|
|
22392
|
+
const normalizedNamespace = normalizeNamespace(namespace);
|
|
22393
|
+
const principalHash = computeSupportPassportOwnerKey(normalizePrincipal(principal));
|
|
22394
|
+
const activeCutoff = this.now().getTime();
|
|
22395
|
+
const states = this.listedOwnerStates(
|
|
22396
|
+
await this.readOwnerMembershipStates(normalizedNamespace, principalHash),
|
|
22397
|
+
activeCutoff
|
|
22398
|
+
);
|
|
22399
|
+
return states.sort((a, b) => {
|
|
22400
|
+
const aActive = !a.revokedAt && Date.parse(a.expiresAt) > activeCutoff;
|
|
22401
|
+
const bActive = !b.revokedAt && Date.parse(b.expiresAt) > activeCutoff;
|
|
22402
|
+
if (aActive !== bActive) return aActive ? -1 : 1;
|
|
22403
|
+
return newestGrantFirst(a, b);
|
|
22404
|
+
});
|
|
22405
|
+
}
|
|
22406
|
+
async revoke(input, hooks = {}) {
|
|
22407
|
+
const mutationHooks = typeof hooks === "function" ? { beforeCommit: hooks } : hooks;
|
|
22408
|
+
const normalizedInput = { ...input, grantId: normalizeGrantId(input.grantId) };
|
|
22409
|
+
const namespace = normalizeNamespace(input.namespace);
|
|
22410
|
+
return await this.withGrantLock(normalizedInput.grantId, async (lock) => {
|
|
22411
|
+
let state;
|
|
22412
|
+
try {
|
|
22413
|
+
state = await this.readState(normalizedInput.grantId);
|
|
22414
|
+
} catch (error) {
|
|
22415
|
+
if (error.code === "ENOENT") throw grantNotFound();
|
|
22416
|
+
throw error;
|
|
22417
|
+
}
|
|
22418
|
+
const principalHash = computeSupportPassportOwnerKey(normalizePrincipal(input.principal));
|
|
22419
|
+
if (state.namespace !== namespace || !hashesMatch(state.principalHash, principalHash)) throw grantNotFound();
|
|
22420
|
+
if (state.revokedAt) {
|
|
22421
|
+
return state;
|
|
22422
|
+
}
|
|
22423
|
+
if (normalizedInput.expectedStateVersion !== void 0 && normalizedInput.expectedStateVersion !== state.stateVersion) {
|
|
22424
|
+
throw new SupportPassportError("state_conflict", "The share link changed after it was loaded.", 409);
|
|
22425
|
+
}
|
|
22426
|
+
const revoked = SupportPassportGrantStateSchema.parse({
|
|
22427
|
+
...state,
|
|
22428
|
+
stateVersion: state.stateVersion + 1,
|
|
22429
|
+
revokedAt: this.now().toISOString()
|
|
22430
|
+
});
|
|
22431
|
+
await this.requireMutationLock(lock);
|
|
22432
|
+
await mutationHooks.beforeCommit?.();
|
|
22433
|
+
await this.requireMutationLock(lock);
|
|
22434
|
+
try {
|
|
22435
|
+
await this.writeState(revoked);
|
|
22436
|
+
} catch (error) {
|
|
22437
|
+
const persisted = await this.readState(revoked.grantId).catch(() => void 0);
|
|
22438
|
+
if (!persisted || !sameGrantState(persisted, revoked)) throw error;
|
|
22439
|
+
await this.syncDirectory(this.grantsDir);
|
|
22440
|
+
}
|
|
22441
|
+
notifySupportPassportCommitted(mutationHooks.onCommitted);
|
|
22442
|
+
return revoked;
|
|
22443
|
+
});
|
|
22444
|
+
}
|
|
22445
|
+
filePath(grantId) {
|
|
22446
|
+
const normalizedGrantId = normalizeGrantId(grantId);
|
|
22447
|
+
return path23.join(this.grantsDir, `${normalizedGrantId}.json`);
|
|
22448
|
+
}
|
|
22449
|
+
ownerIndexPath(ownerHash) {
|
|
22450
|
+
if (!SAFE_OWNER_INDEX_HASH.test(ownerHash)) throw new Error("support passport owner index hash is invalid");
|
|
22451
|
+
return path23.join(this.ownerIndexesDir, `${ownerHash}.json`);
|
|
22452
|
+
}
|
|
22453
|
+
ownerHash(namespace, principalHash) {
|
|
22454
|
+
return sha256("support-passport-owner-index:v1", `${namespace}\0${principalHash}`);
|
|
22455
|
+
}
|
|
22456
|
+
async addToOwnerIndex(state, lock) {
|
|
22457
|
+
const ownerHash = this.ownerHash(state.namespace, state.principalHash);
|
|
22458
|
+
const ownerStates = (await this.readOwnerMembershipStates(state.namespace, state.principalHash)).filter((item) => item.grantId !== state.grantId).sort(newestGrantFirst);
|
|
22459
|
+
const current = ownerStates.map((item) => item.grantId);
|
|
22460
|
+
let retained = current;
|
|
22461
|
+
const indexedStates = [...ownerStates];
|
|
22462
|
+
if (current.length >= MAX_OWNER_GRANT_HISTORY) {
|
|
22463
|
+
const expiryCutoff = this.now().getTime();
|
|
22464
|
+
const active = indexedStates.filter((item) => !item.revokedAt && Date.parse(item.expiresAt) > expiryCutoff);
|
|
22465
|
+
if (active.length >= MAX_OWNER_GRANT_HISTORY) {
|
|
22466
|
+
throw new SupportPassportError(
|
|
22467
|
+
"invalid_input",
|
|
22468
|
+
"A support passport can contain at most 100 active share links.",
|
|
22469
|
+
400
|
|
22470
|
+
);
|
|
22471
|
+
}
|
|
22472
|
+
const inactive = indexedStates.filter((item) => item.revokedAt || Date.parse(item.expiresAt) <= expiryCutoff).sort(newestGrantFirst);
|
|
22473
|
+
retained = [
|
|
22474
|
+
...active.map((item) => item.grantId),
|
|
22475
|
+
...inactive.slice(0, MAX_OWNER_GRANT_HISTORY - active.length - 1).map((item) => item.grantId)
|
|
22476
|
+
];
|
|
22477
|
+
}
|
|
22478
|
+
const grantIds = [...retained, state.grantId];
|
|
22479
|
+
const retainedIds = new Set(grantIds);
|
|
22480
|
+
const indexedStateById = new Map(indexedStates.map((indexedState) => [indexedState.grantId, indexedState]));
|
|
22481
|
+
const evictedGrantIds = current.filter((grantId) => !retainedIds.has(grantId));
|
|
22482
|
+
await this.writeOwnerIndexWhileLocked(ownerHash, grantIds, lock);
|
|
22483
|
+
try {
|
|
22484
|
+
for (const grantId of evictedGrantIds) {
|
|
22485
|
+
await this.withGrantLock(grantId, async (grantLock) => {
|
|
22486
|
+
await this.requireMutationLock(grantLock);
|
|
22487
|
+
const evictedState = indexedStateById.get(grantId);
|
|
22488
|
+
if (evictedState) await this.removeStoredGrant(evictedState);
|
|
22489
|
+
else await this.removeGrantStates([grantId]);
|
|
22490
|
+
});
|
|
22491
|
+
}
|
|
22492
|
+
} catch (error) {
|
|
22493
|
+
log.warn(
|
|
22494
|
+
`support passport could not remove inactive grant state: ${error instanceof Error ? error.message : String(error)}`
|
|
22495
|
+
);
|
|
22496
|
+
}
|
|
22497
|
+
}
|
|
22498
|
+
async removeGrantStates(grantIds) {
|
|
22499
|
+
if (grantIds.length === 0) return;
|
|
22500
|
+
for (const grantId of grantIds) {
|
|
22501
|
+
if (!SAFE_GRANT_ID.test(grantId)) throw grantNotFound();
|
|
22502
|
+
}
|
|
22503
|
+
await removePrivateFilesNoFollow(
|
|
22504
|
+
this.grantsDir,
|
|
22505
|
+
grantIds.map((grantId) => `${grantId}.json`),
|
|
22506
|
+
"support passport grant files must be regular files in a stable directory",
|
|
22507
|
+
path23.parse(this.memoryDir).root
|
|
22508
|
+
);
|
|
22509
|
+
}
|
|
22510
|
+
ownerMembershipDirectory(ownerHash) {
|
|
22511
|
+
if (!SAFE_OWNER_INDEX_HASH.test(ownerHash)) throw new Error("support passport owner index hash is invalid");
|
|
22512
|
+
return path23.join(this.ownerIndexesDir, ownerHash);
|
|
22513
|
+
}
|
|
22514
|
+
async ensureOwnerMembershipDirectory(ownerHash) {
|
|
22515
|
+
const directory = this.ownerMembershipDirectory(ownerHash);
|
|
22516
|
+
await ensurePrivateDirectoryTreeNoFollow(
|
|
22517
|
+
directory,
|
|
22518
|
+
"support passport owner membership must remain inside the memory directory"
|
|
22519
|
+
);
|
|
22520
|
+
return directory;
|
|
22521
|
+
}
|
|
22522
|
+
async writeOwnerMembership(state) {
|
|
22523
|
+
const ownerHash = this.ownerHash(state.namespace, state.principalHash);
|
|
22524
|
+
const directory = await this.ensureOwnerMembershipDirectory(ownerHash);
|
|
22525
|
+
await writePrivateFileAtomicallyNoFollow(
|
|
22526
|
+
directory,
|
|
22527
|
+
path23.join(directory, `${state.grantId}.json`),
|
|
22528
|
+
`${JSON.stringify({ schemaVersion: 1, grantId: state.grantId })}
|
|
22529
|
+
`,
|
|
22530
|
+
"support passport owner membership must be regular files in a stable directory",
|
|
22531
|
+
path23.parse(this.memoryDir).root
|
|
22532
|
+
);
|
|
22533
|
+
}
|
|
22534
|
+
async removeOwnerMembership(state) {
|
|
22535
|
+
const ownerHash = this.ownerHash(state.namespace, state.principalHash);
|
|
22536
|
+
const directory = await this.ensureOwnerMembershipDirectory(ownerHash);
|
|
22537
|
+
await removePrivateFilesNoFollow(
|
|
22538
|
+
directory,
|
|
22539
|
+
[`${state.grantId}.json`],
|
|
22540
|
+
"support passport owner membership must be regular files in a stable directory",
|
|
22541
|
+
path23.parse(this.memoryDir).root
|
|
22542
|
+
);
|
|
22543
|
+
}
|
|
22544
|
+
async removeStoredGrant(state) {
|
|
22545
|
+
await this.removeGrantStates([state.grantId]);
|
|
22546
|
+
await this.removeOwnerMembership(state);
|
|
22547
|
+
}
|
|
22548
|
+
async writeOwnerIndexWhileLocked(ownerHash, grantIds, lock) {
|
|
22549
|
+
await this.requireOwnerIndexLock(lock);
|
|
22550
|
+
await this.writeOwnerIndex(ownerHash, grantIds);
|
|
22551
|
+
await this.requireOwnerIndexLock(lock);
|
|
22552
|
+
}
|
|
22553
|
+
async reconcileCreateAfterOwnerIndexLockLoss(committed) {
|
|
22554
|
+
const ownerHash = this.ownerHash(committed.state.namespace, committed.state.principalHash);
|
|
22555
|
+
for (let attempt = 1; attempt <= MAX_OWNER_INDEX_RECOVERY_ATTEMPTS; attempt += 1) {
|
|
22556
|
+
try {
|
|
22557
|
+
return await this.withOwnerIndexLock(
|
|
22558
|
+
ownerHash,
|
|
22559
|
+
async (lock) => await this.reconcileCommittedGrant(committed, ownerHash, lock)
|
|
22560
|
+
);
|
|
22561
|
+
} catch (error) {
|
|
22562
|
+
if (!(error instanceof OwnerIndexLockLostError)) throw error;
|
|
22563
|
+
}
|
|
22564
|
+
}
|
|
22565
|
+
throw new SupportPassportError("storage_conflict", "The share link store changed during the request.", 409);
|
|
22566
|
+
}
|
|
22567
|
+
async reconcileCommittedGrant(committed, ownerHash, lock) {
|
|
22568
|
+
const persisted = await this.readState(committed.state.grantId).catch(() => void 0);
|
|
22569
|
+
if (!persisted || !sameGrantState(persisted, committed.state)) {
|
|
22570
|
+
throw new SupportPassportError("storage_conflict", "The share link store changed during the request.", 409);
|
|
22571
|
+
}
|
|
22572
|
+
const ownerStates = await this.readOwnerMembershipStates(committed.state.namespace, committed.state.principalHash);
|
|
22573
|
+
const committedIndex = ownerStates.findIndex((state) => state.grantId === committed.state.grantId);
|
|
22574
|
+
if (committedIndex === -1) ownerStates.push(committed.state);
|
|
22575
|
+
else ownerStates[committedIndex] = committed.state;
|
|
22576
|
+
const activeCutoff = this.now().getTime();
|
|
22577
|
+
const active = ownerStates.filter((state) => !state.revokedAt && Date.parse(state.expiresAt) > activeCutoff);
|
|
22578
|
+
if (active.length > MAX_OWNER_GRANT_HISTORY) {
|
|
22579
|
+
await this.withGrantLock(committed.state.grantId, async (grantLock) => {
|
|
22580
|
+
await this.requireMutationLock(grantLock);
|
|
22581
|
+
await this.removeStoredGrant(committed.state);
|
|
22582
|
+
});
|
|
22583
|
+
const committedIndex2 = ownerStates.findIndex((state) => state.grantId === committed.state.grantId);
|
|
22584
|
+
if (committedIndex2 !== -1) ownerStates.splice(committedIndex2, 1);
|
|
22585
|
+
}
|
|
22586
|
+
const retained = this.retainedOwnerStates(ownerStates, activeCutoff);
|
|
22587
|
+
await this.writeOwnerIndexWhileLocked(
|
|
22588
|
+
ownerHash,
|
|
22589
|
+
retained.map((state) => state.grantId),
|
|
22590
|
+
lock
|
|
22591
|
+
);
|
|
22592
|
+
const retainedIds = new Set(retained.map((state) => state.grantId));
|
|
22593
|
+
for (const state of ownerStates) {
|
|
22594
|
+
if (retainedIds.has(state.grantId)) continue;
|
|
22595
|
+
try {
|
|
22596
|
+
await this.withGrantLock(state.grantId, async (grantLock) => {
|
|
22597
|
+
await this.requireMutationLock(grantLock);
|
|
22598
|
+
await this.removeStoredGrant(state);
|
|
22599
|
+
});
|
|
22600
|
+
} catch (error) {
|
|
22601
|
+
log.warn(
|
|
22602
|
+
`support passport could not remove inactive grant state: ${error instanceof Error ? error.message : String(error)}`
|
|
22603
|
+
);
|
|
22604
|
+
}
|
|
22605
|
+
}
|
|
22606
|
+
if (active.length > MAX_OWNER_GRANT_HISTORY) {
|
|
22607
|
+
throw new SupportPassportError("storage_conflict", "The share link store changed during the request.", 409);
|
|
22608
|
+
}
|
|
22609
|
+
return committed;
|
|
22610
|
+
}
|
|
22611
|
+
retainedOwnerStates(states, activeCutoff) {
|
|
22612
|
+
const active = states.filter((state) => !state.revokedAt && Date.parse(state.expiresAt) > activeCutoff);
|
|
22613
|
+
if (active.length > MAX_OWNER_GRANT_HISTORY) {
|
|
22614
|
+
throw new SupportPassportError("storage_conflict", "The share link store contains too many active links.", 409);
|
|
22615
|
+
}
|
|
22616
|
+
const inactive = states.filter((state) => state.revokedAt || Date.parse(state.expiresAt) <= activeCutoff).sort(newestGrantFirst);
|
|
22617
|
+
return [...active, ...inactive.slice(0, MAX_OWNER_GRANT_HISTORY - active.length)];
|
|
22618
|
+
}
|
|
22619
|
+
listedOwnerStates(states, activeCutoff) {
|
|
22620
|
+
const active = states.filter((state) => !state.revokedAt && Date.parse(state.expiresAt) > activeCutoff);
|
|
22621
|
+
const inactive = states.filter((state) => state.revokedAt || Date.parse(state.expiresAt) <= activeCutoff).sort(newestGrantFirst);
|
|
22622
|
+
return [...active, ...inactive.slice(0, Math.max(0, MAX_OWNER_GRANT_HISTORY - active.length))];
|
|
22623
|
+
}
|
|
22624
|
+
async requireAvailableOwnerGrantSlot(namespace, principalHash) {
|
|
22625
|
+
const activeCutoff = this.now().getTime();
|
|
22626
|
+
const active = (await this.readOwnerMembershipStates(namespace, principalHash)).filter(
|
|
22627
|
+
(state) => !state.revokedAt && Date.parse(state.expiresAt) > activeCutoff
|
|
22628
|
+
);
|
|
22629
|
+
if (active.length >= MAX_OWNER_GRANT_HISTORY) {
|
|
22630
|
+
throw new SupportPassportError(
|
|
22631
|
+
"invalid_input",
|
|
22632
|
+
"A support passport can contain at most 100 active share links.",
|
|
22633
|
+
400
|
|
22634
|
+
);
|
|
22635
|
+
}
|
|
22636
|
+
}
|
|
22637
|
+
async readOwnerMembershipStates(namespace, principalHash) {
|
|
22638
|
+
await this.ensureSafeDirectories();
|
|
22639
|
+
const ownerHash = this.ownerHash(namespace, principalHash);
|
|
22640
|
+
const directory = await this.ensureOwnerMembershipDirectory(ownerHash);
|
|
22641
|
+
const grantIds = await withPrivateDirectoryNoFollow(
|
|
22642
|
+
path23.parse(this.memoryDir).root,
|
|
22643
|
+
directory,
|
|
22644
|
+
"support passport owner membership must be regular files in a stable directory",
|
|
22645
|
+
async (pinnedDirectory) => {
|
|
22646
|
+
const entries = await readdir4(pinnedDirectory, { withFileTypes: true });
|
|
22647
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -5)).filter((grantId) => SAFE_GRANT_ID.test(grantId));
|
|
22648
|
+
}
|
|
22649
|
+
);
|
|
22650
|
+
const ownerStates = [];
|
|
22651
|
+
for (const grantId of grantIds) {
|
|
22652
|
+
try {
|
|
22653
|
+
const state = await this.readState(grantId);
|
|
22654
|
+
if (state.namespace === namespace && hashesMatch(state.principalHash, principalHash)) {
|
|
22655
|
+
ownerStates.push(state);
|
|
22656
|
+
}
|
|
22657
|
+
} catch (error) {
|
|
22658
|
+
if (error.code === "ENOENT" || error instanceof MalformedGrantStateError) continue;
|
|
22659
|
+
throw error;
|
|
22660
|
+
}
|
|
22661
|
+
}
|
|
22662
|
+
return ownerStates;
|
|
22663
|
+
}
|
|
22664
|
+
async readOwnerIndexByHash(ownerHash) {
|
|
22665
|
+
await this.ensureSafeDirectories();
|
|
22666
|
+
const filePath = this.ownerIndexPath(ownerHash);
|
|
22667
|
+
let content;
|
|
22668
|
+
try {
|
|
22669
|
+
content = await readPrivateFileNoFollow(
|
|
22670
|
+
this.ownerIndexesDir,
|
|
22671
|
+
filePath,
|
|
22672
|
+
"support passport owner indexes must be regular files in a stable directory",
|
|
22673
|
+
path23.parse(this.memoryDir).root
|
|
22674
|
+
);
|
|
22675
|
+
} catch (error) {
|
|
22676
|
+
if (error.code === "ENOENT") return [];
|
|
22677
|
+
throw error;
|
|
22678
|
+
}
|
|
22679
|
+
const parsed = JSON.parse(content);
|
|
22680
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
22681
|
+
throw new Error("support passport owner index is invalid");
|
|
22682
|
+
}
|
|
22683
|
+
const record = parsed;
|
|
22684
|
+
const grantIds = record.grantIds;
|
|
22685
|
+
if (record.schemaVersion !== 1 || record.ownerHash !== ownerHash || !Array.isArray(grantIds)) {
|
|
22686
|
+
throw new Error("support passport owner index is invalid");
|
|
22687
|
+
}
|
|
22688
|
+
if (grantIds.some((grantId) => typeof grantId !== "string" || !SAFE_GRANT_ID.test(grantId))) {
|
|
22689
|
+
throw new Error("support passport owner index is invalid");
|
|
22690
|
+
}
|
|
22691
|
+
const uniqueGrantIds = new Set(grantIds);
|
|
22692
|
+
if (uniqueGrantIds.size !== grantIds.length || uniqueGrantIds.size > MAX_OWNER_GRANT_HISTORY) {
|
|
22693
|
+
throw new Error("support passport owner index is invalid");
|
|
22694
|
+
}
|
|
22695
|
+
return [...uniqueGrantIds];
|
|
22696
|
+
}
|
|
22697
|
+
async writeOwnerIndex(ownerHash, grantIds) {
|
|
22698
|
+
const filePath = this.ownerIndexPath(ownerHash);
|
|
22699
|
+
try {
|
|
22700
|
+
const metadata = await lstat4(filePath);
|
|
22701
|
+
if (metadata.isSymbolicLink() || !metadata.isFile()) {
|
|
22702
|
+
throw new Error("support passport owner indexes must be regular files");
|
|
22703
|
+
}
|
|
22704
|
+
} catch (error) {
|
|
22705
|
+
if (error.code !== "ENOENT") throw error;
|
|
22706
|
+
}
|
|
22707
|
+
await writePrivateFileAtomicallyNoFollow(
|
|
22708
|
+
this.ownerIndexesDir,
|
|
22709
|
+
filePath,
|
|
22710
|
+
`${JSON.stringify({ schemaVersion: 1, ownerHash, grantIds }, null, 2)}
|
|
22711
|
+
`,
|
|
22712
|
+
"support passport owner indexes must be regular files in a stable directory",
|
|
22713
|
+
path23.parse(this.memoryDir).root
|
|
22714
|
+
);
|
|
22715
|
+
}
|
|
22716
|
+
async readState(grantId) {
|
|
22717
|
+
await this.ensureSafeDirectories();
|
|
22718
|
+
const filePath = this.filePath(grantId);
|
|
22719
|
+
const content = await readPrivateFileNoFollow(
|
|
22720
|
+
this.grantsDir,
|
|
22721
|
+
filePath,
|
|
22722
|
+
"support passport grant files must be regular files in a stable directory",
|
|
22723
|
+
path23.parse(this.memoryDir).root
|
|
22724
|
+
);
|
|
22725
|
+
try {
|
|
22726
|
+
const state = SupportPassportGrantStateSchema.parse(JSON.parse(content));
|
|
22727
|
+
if (state.grantId !== grantId) {
|
|
22728
|
+
throw new MalformedGrantStateError("support passport grant ID must match its file name");
|
|
22729
|
+
}
|
|
22730
|
+
return state;
|
|
22731
|
+
} catch (error) {
|
|
22732
|
+
if (error instanceof MalformedGrantStateError) throw error;
|
|
22733
|
+
if (error instanceof SyntaxError || error.name === "ZodError") {
|
|
22734
|
+
throw new MalformedGrantStateError(void 0, error);
|
|
22735
|
+
}
|
|
22736
|
+
throw error;
|
|
22737
|
+
}
|
|
22738
|
+
}
|
|
22739
|
+
async writeState(state, requireAbsent = false) {
|
|
22740
|
+
const filePath = this.filePath(state.grantId);
|
|
22741
|
+
try {
|
|
22742
|
+
const metadata = await lstat4(filePath);
|
|
22743
|
+
if (metadata.isSymbolicLink() || !metadata.isFile())
|
|
22744
|
+
throw new Error("support passport grant files must be regular files");
|
|
22745
|
+
if (requireAbsent) {
|
|
22746
|
+
throw new SupportPassportError("storage_conflict", "A new share link could not be allocated.", 409);
|
|
22747
|
+
}
|
|
22748
|
+
} catch (error) {
|
|
22749
|
+
if (error.code !== "ENOENT") throw error;
|
|
22750
|
+
}
|
|
22751
|
+
await writePrivateFileAtomicallyNoFollow(
|
|
22752
|
+
this.grantsDir,
|
|
22753
|
+
filePath,
|
|
22754
|
+
`${JSON.stringify(state, null, 2)}
|
|
22755
|
+
`,
|
|
22756
|
+
"support passport grant files must be regular files in a stable directory",
|
|
22757
|
+
path23.parse(this.memoryDir).root
|
|
22758
|
+
);
|
|
22759
|
+
}
|
|
22760
|
+
async ensureSafeDirectories() {
|
|
22761
|
+
if (!this.safeDirectoriesReady) {
|
|
22762
|
+
this.safeDirectoriesReady = (async () => {
|
|
22763
|
+
await this.ensureMemoryRoot();
|
|
22764
|
+
await ensurePrivateDirectoryNoFollow(
|
|
22765
|
+
this.memoryDir,
|
|
22766
|
+
this.ownerIndexesDir,
|
|
22767
|
+
"support passport grant directories must remain inside the memory directory",
|
|
22768
|
+
void 0,
|
|
22769
|
+
true
|
|
22770
|
+
);
|
|
22771
|
+
})();
|
|
22772
|
+
}
|
|
22773
|
+
const currentAttempt = this.safeDirectoriesReady;
|
|
22774
|
+
try {
|
|
22775
|
+
await currentAttempt;
|
|
22776
|
+
} catch (error) {
|
|
22777
|
+
if (this.safeDirectoriesReady === currentAttempt) this.safeDirectoriesReady = void 0;
|
|
22778
|
+
throw error;
|
|
22779
|
+
}
|
|
22780
|
+
}
|
|
22781
|
+
async ensureMemoryRoot() {
|
|
22782
|
+
const configuredMemoryDir = this.memoryDir;
|
|
22783
|
+
if (!this.memoryRootReady) {
|
|
22784
|
+
this.memoryRootReady = (async () => {
|
|
22785
|
+
await ensurePrivateDirectoryTreeNoFollow(
|
|
22786
|
+
configuredMemoryDir,
|
|
22787
|
+
"support passport memory directory must be a stable directory",
|
|
22788
|
+
void 0
|
|
22789
|
+
);
|
|
22790
|
+
this.memoryDir = configuredMemoryDir;
|
|
22791
|
+
this.grantsDir = path23.join(configuredMemoryDir, "state", "support-passport", "grants");
|
|
22792
|
+
this.ownerIndexesDir = path23.join(this.grantsDir, "owners");
|
|
22793
|
+
})();
|
|
22794
|
+
}
|
|
22795
|
+
const currentAttempt = this.memoryRootReady;
|
|
22796
|
+
try {
|
|
22797
|
+
await currentAttempt;
|
|
22798
|
+
} catch (error) {
|
|
22799
|
+
if (this.memoryRootReady === currentAttempt) this.memoryRootReady = void 0;
|
|
22800
|
+
throw error;
|
|
22801
|
+
}
|
|
22802
|
+
}
|
|
22803
|
+
async withMutationLock(task) {
|
|
22804
|
+
await this.ensureSafeDirectories();
|
|
22805
|
+
return await withPrivateDirectoryNoFollow(
|
|
22806
|
+
path23.parse(this.memoryDir).root,
|
|
22807
|
+
this.grantsDir,
|
|
22808
|
+
"support passport grant lock directory must remain inside the memory directory",
|
|
22809
|
+
async (pinnedDirectory) => await this.withExclusiveLock(
|
|
22810
|
+
`support-passport-grants:${this.grantsDir}`,
|
|
22811
|
+
path23.join(pinnedDirectory, ".grants.lock"),
|
|
22812
|
+
task
|
|
22813
|
+
)
|
|
22814
|
+
);
|
|
22815
|
+
}
|
|
22816
|
+
async withGrantLock(grantId, task) {
|
|
22817
|
+
const normalizedGrantId = normalizeGrantId(grantId);
|
|
22818
|
+
await this.ensureSafeDirectories();
|
|
22819
|
+
return await withPrivateDirectoryNoFollow(
|
|
22820
|
+
path23.parse(this.memoryDir).root,
|
|
22821
|
+
this.grantsDir,
|
|
22822
|
+
"support passport grant lock directory must remain inside the memory directory",
|
|
22823
|
+
async (pinnedDirectory) => await this.withExclusiveLock(
|
|
22824
|
+
`support-passport-grant:${this.grantsDir}:${normalizedGrantId}`,
|
|
22825
|
+
path23.join(pinnedDirectory, `.${normalizedGrantId}.lock`),
|
|
22826
|
+
task
|
|
22827
|
+
)
|
|
22828
|
+
);
|
|
22829
|
+
}
|
|
22830
|
+
async withOwnerIndexLock(ownerHash, task) {
|
|
22831
|
+
await this.ensureSafeDirectories();
|
|
22832
|
+
return await withPrivateDirectoryNoFollow(
|
|
22833
|
+
path23.parse(this.memoryDir).root,
|
|
22834
|
+
this.ownerIndexesDir,
|
|
22835
|
+
"support passport owner lock directory must remain inside the memory directory",
|
|
22836
|
+
async (pinnedDirectory) => await this.withExclusiveLock(
|
|
22837
|
+
`support-passport-owner:${this.ownerIndexesDir}:${ownerHash}`,
|
|
22838
|
+
path23.join(pinnedDirectory, `.${ownerHash}.lock`),
|
|
22839
|
+
task
|
|
22840
|
+
)
|
|
22841
|
+
);
|
|
22842
|
+
}
|
|
22843
|
+
async withExclusiveLock(serializationKey, lockPath, task) {
|
|
22844
|
+
return await serializeMutations(
|
|
22845
|
+
serializationKey,
|
|
22846
|
+
() => this.runWithHeldFileLock(
|
|
22847
|
+
lockPath,
|
|
22848
|
+
{
|
|
22849
|
+
staleMs: GRANT_LOCK_STALE_MS,
|
|
22850
|
+
maxWaitMs: GRANT_LOCK_WAIT_MS,
|
|
22851
|
+
heartbeatMs: GRANT_LOCK_HEARTBEAT_MS
|
|
22852
|
+
},
|
|
22853
|
+
async (acquired, lock) => {
|
|
22854
|
+
if (!acquired) {
|
|
22855
|
+
throw new SupportPassportError("storage_conflict", "The share link store is busy. Try again.", 409);
|
|
22856
|
+
}
|
|
22857
|
+
return await task(lock);
|
|
22858
|
+
}
|
|
22859
|
+
)
|
|
22860
|
+
);
|
|
22861
|
+
}
|
|
22862
|
+
async requireMutationLock(lock) {
|
|
22863
|
+
if (await lock.refresh()) return;
|
|
22864
|
+
throw new SupportPassportError("storage_conflict", "The share link store changed during the request.", 409);
|
|
22865
|
+
}
|
|
22866
|
+
async requireOwnerIndexLock(lock) {
|
|
22867
|
+
try {
|
|
22868
|
+
await this.requireMutationLock(lock);
|
|
22869
|
+
} catch (error) {
|
|
22870
|
+
throw new OwnerIndexLockLostError(error);
|
|
22871
|
+
}
|
|
22872
|
+
}
|
|
22873
|
+
requireActiveState(state) {
|
|
22874
|
+
if (state.revokedAt) {
|
|
22875
|
+
throw new SupportPassportError("grant_gone", "The share link is no longer active.", 410);
|
|
22876
|
+
}
|
|
22877
|
+
if (Date.parse(state.expiresAt) <= this.now().getTime()) {
|
|
22878
|
+
throw new SupportPassportError("grant_expired", "The share link has expired.", 410);
|
|
22879
|
+
}
|
|
22880
|
+
}
|
|
22881
|
+
};
|
|
22882
|
+
|
|
22883
|
+
// src/support-passport/grant-service.ts
|
|
22884
|
+
function invalidInput2() {
|
|
22885
|
+
return new SupportPassportError("invalid_input", "The share link request is invalid.", 400);
|
|
22886
|
+
}
|
|
22887
|
+
var SupportPassportGrantService = class {
|
|
22888
|
+
grantStore;
|
|
22889
|
+
resolveOwner;
|
|
22890
|
+
resolveNamespace;
|
|
22891
|
+
now;
|
|
22892
|
+
constructor(dependencies) {
|
|
22893
|
+
this.grantStore = dependencies.grantStore;
|
|
22894
|
+
this.resolveOwner = dependencies.resolveOwner;
|
|
22895
|
+
this.resolveNamespace = dependencies.resolveNamespace;
|
|
22896
|
+
this.now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
22897
|
+
}
|
|
22898
|
+
async createGrant(input, options = {}) {
|
|
22899
|
+
const requestedAt = this.now();
|
|
22900
|
+
const parsed = SupportPassportCreateGrantInputSchema.safeParse(input);
|
|
22901
|
+
if (!parsed.success) throw invalidInput2();
|
|
22902
|
+
const owner = await this.resolveOwnerScope(parsed.data.principal);
|
|
22903
|
+
return await withSupportPassportOwnerLock(
|
|
22904
|
+
owner.storage,
|
|
22905
|
+
{ namespace: owner.namespace, principal: owner.principal },
|
|
22906
|
+
async (ownerLock) => {
|
|
22907
|
+
options.signal?.throwIfAborted();
|
|
22908
|
+
const ownerHash = computeSupportPassportOwnerKey(owner.principal);
|
|
22909
|
+
const cardsById = new Map(
|
|
22910
|
+
this.projectOwnedCards(
|
|
22911
|
+
await owner.storage.readAllMemories(),
|
|
22912
|
+
owner.namespace,
|
|
22913
|
+
ownerHash
|
|
22914
|
+
).map((card) => [card.card.cardId, card])
|
|
22915
|
+
);
|
|
22916
|
+
for (const cardRef of parsed.data.cards) {
|
|
22917
|
+
const stored = cardsById.get(cardRef.cardId);
|
|
22918
|
+
if (!stored || stored.namespace !== owner.namespace || stored.owner !== ownerHash || stored.card.status !== "active") {
|
|
22919
|
+
throw new SupportPassportError("invalid_card_status", "Only approved support cards can be shared.", 409);
|
|
22920
|
+
}
|
|
22921
|
+
if (stored.card.revision !== cardRef.revision) {
|
|
22922
|
+
throw new SupportPassportError("revision_conflict", "A support card changed after it was selected.", 409);
|
|
22923
|
+
}
|
|
22924
|
+
if (!this.publicCard(stored.card).success) {
|
|
22925
|
+
throw new SupportPassportError("card_data_invalid", "The support card data is invalid.", 500);
|
|
22926
|
+
}
|
|
22927
|
+
}
|
|
22928
|
+
const created = await this.grantStore.create(
|
|
22929
|
+
{
|
|
22930
|
+
namespace: owner.namespace,
|
|
22931
|
+
principal: owner.principal,
|
|
22932
|
+
cards: parsed.data.cards,
|
|
22933
|
+
expiresAt: parsed.data.expiresAt,
|
|
22934
|
+
requestedAt
|
|
22935
|
+
},
|
|
22936
|
+
{
|
|
22937
|
+
beforeCommit: async () => {
|
|
22938
|
+
options.signal?.throwIfAborted();
|
|
22939
|
+
await requireSupportPassportOwnerLock(ownerLock);
|
|
22940
|
+
}
|
|
22941
|
+
}
|
|
22942
|
+
);
|
|
22943
|
+
try {
|
|
22944
|
+
await requireSupportPassportOwnerLock(ownerLock);
|
|
22945
|
+
} catch (error) {
|
|
22946
|
+
await this.revokeCommittedGrant(created, owner);
|
|
22947
|
+
throw error;
|
|
22948
|
+
}
|
|
22949
|
+
if (options.signal?.aborted) {
|
|
22950
|
+
await this.revokeCommittedGrant(created, owner);
|
|
22951
|
+
options.signal.throwIfAborted();
|
|
22952
|
+
}
|
|
22953
|
+
const output = SupportPassportCreatedGrantSchema.parse({
|
|
22954
|
+
grant: this.ownerGrant(created.state),
|
|
22955
|
+
secret: created.secret
|
|
22956
|
+
});
|
|
22957
|
+
notifySupportPassportCommitted(options.onCommitted);
|
|
22958
|
+
return output;
|
|
22959
|
+
}
|
|
22960
|
+
);
|
|
22961
|
+
}
|
|
22962
|
+
async listGrants(input) {
|
|
22963
|
+
const parsed = SupportPassportListGrantsInputSchema.safeParse(input);
|
|
22964
|
+
if (!parsed.success) throw invalidInput2();
|
|
22965
|
+
const owner = await this.resolveOwnerScope(parsed.data.principal);
|
|
22966
|
+
return (await this.grantStore.listForOwner(owner.namespace, owner.principal)).map(
|
|
22967
|
+
(state) => this.ownerGrant(state)
|
|
22968
|
+
);
|
|
22969
|
+
}
|
|
22970
|
+
async revokeGrant(input, options = {}) {
|
|
22971
|
+
const parsed = SupportPassportRevokeGrantInputSchema.safeParse(input);
|
|
22972
|
+
if (!parsed.success) throw invalidInput2();
|
|
22973
|
+
const owner = await this.resolveOwnerScope(parsed.data.principal);
|
|
22974
|
+
return await withSupportPassportOwnerLock(
|
|
22975
|
+
owner.storage,
|
|
22976
|
+
{ namespace: owner.namespace, principal: owner.principal },
|
|
22977
|
+
async (ownerLock) => {
|
|
22978
|
+
options.signal?.throwIfAborted();
|
|
22979
|
+
const state = await this.grantStore.revoke(
|
|
22980
|
+
{
|
|
22981
|
+
grantId: parsed.data.grantId,
|
|
22982
|
+
namespace: owner.namespace,
|
|
22983
|
+
principal: owner.principal,
|
|
22984
|
+
expectedStateVersion: parsed.data.expectedStateVersion
|
|
22985
|
+
},
|
|
22986
|
+
{
|
|
22987
|
+
beforeCommit: async () => {
|
|
22988
|
+
options.signal?.throwIfAborted();
|
|
22989
|
+
await requireSupportPassportOwnerLock(ownerLock);
|
|
22990
|
+
}
|
|
22991
|
+
}
|
|
22992
|
+
);
|
|
22993
|
+
const output = this.ownerGrant(state);
|
|
22994
|
+
notifySupportPassportCommitted(options.onCommitted);
|
|
22995
|
+
return output;
|
|
22996
|
+
}
|
|
22997
|
+
);
|
|
22998
|
+
}
|
|
22999
|
+
async readGrant(input) {
|
|
23000
|
+
if (!input || typeof input.grantId !== "string" || typeof input.secret !== "string") {
|
|
23001
|
+
throw new SupportPassportError("grant_not_found", "The share link was not found.", 404);
|
|
23002
|
+
}
|
|
23003
|
+
return await this.readGrantAttempt(input);
|
|
23004
|
+
}
|
|
23005
|
+
async readGrantAttempt(input) {
|
|
23006
|
+
const initialState = await this.grantStore.authenticate(input.grantId, input.secret);
|
|
23007
|
+
const storage = await this.resolveNamespace(initialState.namespace);
|
|
23008
|
+
const initialSnapshot = await this.readStoredCardSnapshot(
|
|
23009
|
+
storage,
|
|
23010
|
+
initialState.namespace,
|
|
23011
|
+
initialState.principalHash
|
|
23012
|
+
);
|
|
23013
|
+
const cards = this.readGrantCards(initialSnapshot, initialState);
|
|
23014
|
+
const firstCard = cards[0];
|
|
23015
|
+
if (!firstCard) throw new SupportPassportError("grant_stale", "The shared support guide has changed.", 410);
|
|
23016
|
+
const updatedAt = cards.reduce((latest, card) => {
|
|
23017
|
+
return Date.parse(card.updatedAt) > Date.parse(latest) ? card.updatedAt : latest;
|
|
23018
|
+
}, firstCard.updatedAt);
|
|
23019
|
+
return await withSupportPassportOwnerLock(
|
|
23020
|
+
storage,
|
|
23021
|
+
{ namespace: initialState.namespace, ownerKey: initialState.ownerLockKey },
|
|
23022
|
+
async (ownerLock) => {
|
|
23023
|
+
return await this.grantStore.withAuthenticatedGrant(
|
|
23024
|
+
input.grantId,
|
|
23025
|
+
input.secret,
|
|
23026
|
+
async (finalState) => {
|
|
23027
|
+
if (finalState.namespace !== initialState.namespace) {
|
|
23028
|
+
throw new SupportPassportError("grant_stale", "The shared support guide has changed.", 410);
|
|
23029
|
+
}
|
|
23030
|
+
await requireSupportPassportOwnerLock(ownerLock);
|
|
23031
|
+
const currentSnapshot = await this.readStoredCardSnapshot(
|
|
23032
|
+
storage,
|
|
23033
|
+
finalState.namespace,
|
|
23034
|
+
finalState.principalHash
|
|
23035
|
+
);
|
|
23036
|
+
const currentCards = this.readGrantCards(currentSnapshot, finalState);
|
|
23037
|
+
await requireSupportPassportOwnerLock(ownerLock);
|
|
23038
|
+
if (JSON.stringify(currentCards) !== JSON.stringify(cards)) {
|
|
23039
|
+
throw new SupportPassportError("grant_stale", "The shared support guide has changed.", 410);
|
|
23040
|
+
}
|
|
23041
|
+
return SupportPassportPublicGuideSchema.parse({
|
|
23042
|
+
schemaVersion: 1,
|
|
23043
|
+
grantId: finalState.grantId,
|
|
23044
|
+
expiresAt: finalState.expiresAt,
|
|
23045
|
+
updatedAt,
|
|
23046
|
+
cards
|
|
23047
|
+
});
|
|
23048
|
+
},
|
|
23049
|
+
async () => await requireSupportPassportOwnerLock(ownerLock)
|
|
23050
|
+
);
|
|
23051
|
+
}
|
|
23052
|
+
);
|
|
23053
|
+
}
|
|
23054
|
+
readGrantCards(snapshot, state) {
|
|
23055
|
+
return state.cards.map((cardRef) => {
|
|
23056
|
+
const stored = snapshot.cardsById.get(cardRef.cardId);
|
|
23057
|
+
if (!stored || stored.namespace !== state.namespace || stored.owner !== state.principalHash || stored.card.status !== "active" || stored.card.revision !== cardRef.revision) {
|
|
23058
|
+
throw new SupportPassportError("grant_stale", "The shared support guide has changed.", 410);
|
|
23059
|
+
}
|
|
23060
|
+
const publicCard = this.publicCard(stored.card);
|
|
23061
|
+
if (!publicCard.success) {
|
|
23062
|
+
throw new SupportPassportError("grant_stale", "The shared support guide has changed.", 410);
|
|
23063
|
+
}
|
|
23064
|
+
return publicCard.data;
|
|
23065
|
+
});
|
|
23066
|
+
}
|
|
23067
|
+
async readStoredCardSnapshot(storage, namespace, owner) {
|
|
23068
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
23069
|
+
const before = this.cardSnapshotVersion(storage);
|
|
23070
|
+
const memories = await storage.readAllMemories();
|
|
23071
|
+
const after = this.cardSnapshotVersion(storage);
|
|
23072
|
+
if (before !== after) continue;
|
|
23073
|
+
const cards = this.projectOwnedCards(memories, namespace, owner);
|
|
23074
|
+
const snapshot = {
|
|
23075
|
+
version: after,
|
|
23076
|
+
cardsById: new Map(cards.map((card) => [card.card.cardId, card]))
|
|
23077
|
+
};
|
|
23078
|
+
return snapshot;
|
|
23079
|
+
}
|
|
23080
|
+
throw new SupportPassportError("storage_conflict", "The support card list changed during review.", 409);
|
|
23081
|
+
}
|
|
23082
|
+
projectOwnedCards(memories, namespace, owner) {
|
|
23083
|
+
const cards = memories.map((memory) => projectSupportPassportCard(memory)).filter(
|
|
23084
|
+
(card) => card !== null && card.namespace === namespace && card.owner === owner
|
|
23085
|
+
);
|
|
23086
|
+
const cardIds = /* @__PURE__ */ new Set();
|
|
23087
|
+
for (const card of cards) {
|
|
23088
|
+
if (cardIds.has(card.card.cardId)) {
|
|
23089
|
+
throw new SupportPassportError("card_data_invalid", "Support card IDs must be unique.", 500);
|
|
23090
|
+
}
|
|
23091
|
+
cardIds.add(card.card.cardId);
|
|
23092
|
+
}
|
|
23093
|
+
return cards;
|
|
23094
|
+
}
|
|
23095
|
+
cardSnapshotVersion(storage) {
|
|
23096
|
+
return `${storage.getCorpusScanVersion()}:${storage.hotCacheKeyId()}`;
|
|
23097
|
+
}
|
|
23098
|
+
async revokeCommittedGrant(created, owner) {
|
|
23099
|
+
try {
|
|
23100
|
+
await this.grantStore.revoke({
|
|
23101
|
+
grantId: created.state.grantId,
|
|
23102
|
+
namespace: owner.namespace,
|
|
23103
|
+
principal: owner.principal,
|
|
23104
|
+
expectedStateVersion: created.state.stateVersion
|
|
23105
|
+
});
|
|
23106
|
+
} catch {
|
|
23107
|
+
throw new SupportPassportError(
|
|
23108
|
+
"storage_conflict",
|
|
23109
|
+
"A share link could not be stopped after owner access changed. Review the active share list.",
|
|
23110
|
+
500
|
|
23111
|
+
);
|
|
23112
|
+
}
|
|
23113
|
+
}
|
|
23114
|
+
publicCard(card) {
|
|
23115
|
+
return SupportPassportPublicCardSchema.safeParse({
|
|
23116
|
+
cardId: card.cardId,
|
|
23117
|
+
title: card.title,
|
|
23118
|
+
statement: card.statement,
|
|
23119
|
+
category: card.category,
|
|
23120
|
+
updatedAt: card.updatedAt
|
|
23121
|
+
});
|
|
23122
|
+
}
|
|
23123
|
+
async resolveOwnerScope(principal) {
|
|
23124
|
+
const requestedPrincipal = SupportPassportListCardsInputSchema.safeParse({ principal });
|
|
23125
|
+
const owner = await this.resolveOwner(principal);
|
|
23126
|
+
const namespace = SupportPassportNamespaceSchema.safeParse(owner.namespace);
|
|
23127
|
+
const ownerPrincipal = SupportPassportListCardsInputSchema.safeParse({ principal: owner.principal });
|
|
23128
|
+
if (!requestedPrincipal.success || !namespace.success || !ownerPrincipal.success || ownerPrincipal.data.principal !== requestedPrincipal.data.principal) {
|
|
23129
|
+
throw new SupportPassportError("card_data_invalid", "The support passport owner scope is invalid.", 500);
|
|
23130
|
+
}
|
|
23131
|
+
return { ...owner, principal: ownerPrincipal.data.principal, namespace: namespace.data };
|
|
23132
|
+
}
|
|
23133
|
+
ownerGrant(state) {
|
|
23134
|
+
const status = state.revokedAt ? "revoked" : Date.parse(state.expiresAt) <= this.now().getTime() ? "expired" : "active";
|
|
23135
|
+
return SupportPassportOwnerGrantSchema.parse({
|
|
23136
|
+
grantId: state.grantId,
|
|
23137
|
+
stateVersion: state.stateVersion,
|
|
23138
|
+
cards: state.cards,
|
|
23139
|
+
createdAt: state.createdAt,
|
|
23140
|
+
expiresAt: state.expiresAt,
|
|
23141
|
+
revokedAt: state.revokedAt,
|
|
23142
|
+
status
|
|
23143
|
+
});
|
|
23144
|
+
}
|
|
23145
|
+
};
|
|
23146
|
+
|
|
23147
|
+
// src/relay/mission-fixture.ts
|
|
23148
|
+
var RELAY_DEMO_MISSION_ID = "checkout-token-recovery";
|
|
23149
|
+
var RELAY_DEMO_NAMESPACE = "relay-build-week";
|
|
23150
|
+
var sourceEvidence = {
|
|
23151
|
+
kind: "source",
|
|
23152
|
+
id: "source-checkout-contract",
|
|
23153
|
+
label: "Checkout token contract",
|
|
23154
|
+
locator: "fixture://checkout-service/src/token-policy.ts",
|
|
23155
|
+
capture: "fixture"
|
|
23156
|
+
};
|
|
23157
|
+
var testEvidence = {
|
|
23158
|
+
kind: "test",
|
|
23159
|
+
id: "test-checkout-contract",
|
|
23160
|
+
label: "Checkout integration contract",
|
|
23161
|
+
locator: "fixture://checkout-service/test/token-policy.test.ts",
|
|
23162
|
+
capture: "fixture"
|
|
23163
|
+
};
|
|
23164
|
+
var approvalEvidence = {
|
|
23165
|
+
kind: "approval",
|
|
23166
|
+
id: "approval-build-week-operator",
|
|
23167
|
+
label: "Human approval receipt",
|
|
23168
|
+
locator: "fixture://relay/approvals/correction-token-refresh.json",
|
|
23169
|
+
capture: "fixture"
|
|
23170
|
+
};
|
|
23171
|
+
var correctionEvidence = {
|
|
23172
|
+
kind: "correction",
|
|
23173
|
+
id: "correction-token-refresh",
|
|
23174
|
+
label: "Applied Remnic correction",
|
|
23175
|
+
locator: "fixture://remnic/corrections/correction-token-refresh.json",
|
|
23176
|
+
capture: "fixture"
|
|
23177
|
+
};
|
|
23178
|
+
function at(second) {
|
|
23179
|
+
return `2026-07-17T18:00:${String(second).padStart(2, "0")}.000Z`;
|
|
23180
|
+
}
|
|
23181
|
+
function createRelayMissionFixture() {
|
|
23182
|
+
return [
|
|
23183
|
+
{
|
|
23184
|
+
occurredAt: at(0),
|
|
23185
|
+
idempotencyKey: "fixture-001",
|
|
23186
|
+
payload: {
|
|
23187
|
+
kind: "mission_started",
|
|
23188
|
+
title: "The checkout token split",
|
|
23189
|
+
objective: "Resolve a conflicting token refresh policy and prove a cold agent acts on it.",
|
|
23190
|
+
runMode: "fixture",
|
|
23191
|
+
evidence: [sourceEvidence]
|
|
23192
|
+
}
|
|
23193
|
+
},
|
|
23194
|
+
{
|
|
23195
|
+
occurredAt: at(2),
|
|
23196
|
+
idempotencyKey: "fixture-002",
|
|
23197
|
+
payload: {
|
|
23198
|
+
kind: "agent_status",
|
|
23199
|
+
agentId: "agent-atlas",
|
|
23200
|
+
sessionId: "session-atlas",
|
|
23201
|
+
label: "Atlas",
|
|
23202
|
+
role: "Checkout implementation",
|
|
23203
|
+
status: "working",
|
|
23204
|
+
evidence: [sourceEvidence]
|
|
23205
|
+
}
|
|
23206
|
+
},
|
|
23207
|
+
{
|
|
23208
|
+
occurredAt: at(3),
|
|
23209
|
+
idempotencyKey: "fixture-003",
|
|
23210
|
+
payload: {
|
|
23211
|
+
kind: "agent_status",
|
|
23212
|
+
agentId: "agent-nova",
|
|
23213
|
+
sessionId: "session-nova",
|
|
23214
|
+
label: "Nova",
|
|
23215
|
+
role: "Integration verification",
|
|
23216
|
+
status: "working",
|
|
23217
|
+
evidence: [testEvidence]
|
|
23218
|
+
}
|
|
23219
|
+
},
|
|
23220
|
+
{
|
|
23221
|
+
occurredAt: at(6),
|
|
23222
|
+
idempotencyKey: "fixture-004",
|
|
23223
|
+
payload: {
|
|
23224
|
+
kind: "agent_output",
|
|
23225
|
+
agentId: "agent-atlas",
|
|
23226
|
+
sessionId: "session-atlas",
|
|
23227
|
+
outputId: "output-atlas-policy",
|
|
23228
|
+
summary: "Implemented a new token for every checkout retry.",
|
|
23229
|
+
evidence: [
|
|
23230
|
+
{
|
|
23231
|
+
kind: "agent_output",
|
|
23232
|
+
id: "output-atlas-policy",
|
|
23233
|
+
label: "Atlas one-shot output",
|
|
23234
|
+
locator: "fixture://codex/atlas/final-output.txt",
|
|
23235
|
+
capture: "fixture"
|
|
23236
|
+
}
|
|
23237
|
+
]
|
|
23238
|
+
}
|
|
23239
|
+
},
|
|
23240
|
+
{
|
|
23241
|
+
occurredAt: at(7),
|
|
23242
|
+
idempotencyKey: "fixture-005",
|
|
23243
|
+
payload: {
|
|
23244
|
+
kind: "belief_observed",
|
|
23245
|
+
agentId: "agent-atlas",
|
|
23246
|
+
sessionId: "session-atlas",
|
|
23247
|
+
beliefId: "belief-new-token-every-request",
|
|
23248
|
+
decisionId: "decision-new-token-every-request",
|
|
23249
|
+
statement: "Mint a new checkout token for every request and retry.",
|
|
23250
|
+
confidence: 0.91,
|
|
23251
|
+
evidence: [
|
|
23252
|
+
{
|
|
23253
|
+
kind: "memory",
|
|
23254
|
+
id: "memory-stale-token-policy",
|
|
23255
|
+
label: "Stale project decision",
|
|
23256
|
+
locator: "fixture://remnic/memories/memory-stale-token-policy.md",
|
|
23257
|
+
capture: "fixture"
|
|
23258
|
+
}
|
|
23259
|
+
]
|
|
23260
|
+
}
|
|
23261
|
+
},
|
|
23262
|
+
{
|
|
23263
|
+
occurredAt: at(9),
|
|
23264
|
+
idempotencyKey: "fixture-006",
|
|
23265
|
+
payload: {
|
|
23266
|
+
kind: "agent_output",
|
|
23267
|
+
agentId: "agent-nova",
|
|
21091
23268
|
sessionId: "session-nova",
|
|
21092
23269
|
outputId: "output-nova-policy",
|
|
21093
23270
|
summary: "The contract requires one token per checkout session, refreshed only after expiry.",
|
|
@@ -21712,9 +23889,9 @@ function readStructuralSymbol(entry) {
|
|
|
21712
23889
|
const rec = entry;
|
|
21713
23890
|
const symbol = typeof rec.symbol === "string" ? rec.symbol.trim() : "";
|
|
21714
23891
|
if (!symbol) return null;
|
|
21715
|
-
const
|
|
23892
|
+
const path38 = typeof rec.path === "string" && rec.path.length > 0 ? rec.path : void 0;
|
|
21716
23893
|
const kind = typeof rec.kind === "string" && rec.kind.length > 0 ? rec.kind : void 0;
|
|
21717
|
-
const item =
|
|
23894
|
+
const item = path38 !== void 0 && kind !== void 0 ? { symbol, path: path38, kind } : path38 !== void 0 ? { symbol, path: path38 } : kind !== void 0 ? { symbol, kind } : { symbol };
|
|
21718
23895
|
return item;
|
|
21719
23896
|
}
|
|
21720
23897
|
function classifySpawnError(err) {
|
|
@@ -21788,7 +23965,7 @@ var DEFAULT_GRACE_PERIOD_DAYS = 7;
|
|
|
21788
23965
|
|
|
21789
23966
|
// src/binary-lifecycle/backend.ts
|
|
21790
23967
|
import fsp from "fs/promises";
|
|
21791
|
-
import
|
|
23968
|
+
import path24 from "path";
|
|
21792
23969
|
var FilesystemBackend = class {
|
|
21793
23970
|
type = "filesystem";
|
|
21794
23971
|
basePath;
|
|
@@ -21796,27 +23973,27 @@ var FilesystemBackend = class {
|
|
|
21796
23973
|
if (!basePath || basePath.trim().length === 0) {
|
|
21797
23974
|
throw new Error("FilesystemBackend requires a non-empty basePath");
|
|
21798
23975
|
}
|
|
21799
|
-
this.basePath =
|
|
23976
|
+
this.basePath = path24.resolve(basePath);
|
|
21800
23977
|
}
|
|
21801
23978
|
resolveRemotePath(remotePath) {
|
|
21802
|
-
const resolved =
|
|
21803
|
-
const relative =
|
|
21804
|
-
if (relative === ".." || relative.startsWith(`..${
|
|
23979
|
+
const resolved = path24.isAbsolute(remotePath) ? path24.resolve(remotePath) : path24.resolve(this.basePath, remotePath);
|
|
23980
|
+
const relative = path24.relative(this.basePath, resolved);
|
|
23981
|
+
if (relative === ".." || relative.startsWith(`..${path24.sep}`) || path24.isAbsolute(relative)) {
|
|
21805
23982
|
throw new Error(`FilesystemBackend remotePath escapes basePath: ${JSON.stringify(remotePath)}`);
|
|
21806
23983
|
}
|
|
21807
23984
|
return resolved;
|
|
21808
23985
|
}
|
|
21809
23986
|
isInsideBase(candidate, realBase) {
|
|
21810
|
-
const relative =
|
|
21811
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
23987
|
+
const relative = path24.relative(realBase, candidate);
|
|
23988
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path24.sep}`) && !path24.isAbsolute(relative);
|
|
21812
23989
|
}
|
|
21813
23990
|
async realBasePathIfExists() {
|
|
21814
23991
|
try {
|
|
21815
|
-
const
|
|
21816
|
-
if (
|
|
23992
|
+
const stat6 = await fsp.lstat(this.basePath);
|
|
23993
|
+
if (stat6.isSymbolicLink()) {
|
|
21817
23994
|
throw new Error(`FilesystemBackend basePath must not be a symlink: ${this.basePath}`);
|
|
21818
23995
|
}
|
|
21819
|
-
if (!
|
|
23996
|
+
if (!stat6.isDirectory()) {
|
|
21820
23997
|
throw new Error(`FilesystemBackend basePath must be a directory: ${this.basePath}`);
|
|
21821
23998
|
}
|
|
21822
23999
|
return await fsp.realpath(this.basePath);
|
|
@@ -21837,19 +24014,19 @@ var FilesystemBackend = class {
|
|
|
21837
24014
|
}
|
|
21838
24015
|
async ensureSafeParentDirectory(dest) {
|
|
21839
24016
|
const realBase = await this.ensureBaseDirectory();
|
|
21840
|
-
const destDir =
|
|
21841
|
-
const relativeDir =
|
|
21842
|
-
const segments = relativeDir === "" ? [] : relativeDir.split(
|
|
24017
|
+
const destDir = path24.dirname(dest);
|
|
24018
|
+
const relativeDir = path24.relative(this.basePath, destDir);
|
|
24019
|
+
const segments = relativeDir === "" ? [] : relativeDir.split(path24.sep);
|
|
21843
24020
|
let current = this.basePath;
|
|
21844
24021
|
for (const segment of segments) {
|
|
21845
24022
|
if (segment === "." || segment === "") continue;
|
|
21846
|
-
current =
|
|
24023
|
+
current = path24.join(current, segment);
|
|
21847
24024
|
try {
|
|
21848
|
-
const
|
|
21849
|
-
if (
|
|
24025
|
+
const stat6 = await fsp.lstat(current);
|
|
24026
|
+
if (stat6.isSymbolicLink()) {
|
|
21850
24027
|
throw new Error(`FilesystemBackend remotePath traverses symlink: ${current}`);
|
|
21851
24028
|
}
|
|
21852
|
-
if (!
|
|
24029
|
+
if (!stat6.isDirectory()) {
|
|
21853
24030
|
throw new Error(`FilesystemBackend remotePath parent is not a directory: ${current}`);
|
|
21854
24031
|
}
|
|
21855
24032
|
} catch (err) {
|
|
@@ -21871,26 +24048,26 @@ var FilesystemBackend = class {
|
|
|
21871
24048
|
if (realBase === null) {
|
|
21872
24049
|
return null;
|
|
21873
24050
|
}
|
|
21874
|
-
const destDir =
|
|
21875
|
-
const relativeDir =
|
|
21876
|
-
const segments = relativeDir === "" ? [] : relativeDir.split(
|
|
24051
|
+
const destDir = path24.dirname(dest);
|
|
24052
|
+
const relativeDir = path24.relative(this.basePath, destDir);
|
|
24053
|
+
const segments = relativeDir === "" ? [] : relativeDir.split(path24.sep);
|
|
21877
24054
|
let current = this.basePath;
|
|
21878
24055
|
for (const segment of segments) {
|
|
21879
24056
|
if (segment === "." || segment === "") continue;
|
|
21880
|
-
current =
|
|
21881
|
-
let
|
|
24057
|
+
current = path24.join(current, segment);
|
|
24058
|
+
let stat6;
|
|
21882
24059
|
try {
|
|
21883
|
-
|
|
24060
|
+
stat6 = await fsp.lstat(current);
|
|
21884
24061
|
} catch (err) {
|
|
21885
24062
|
if (err.code === "ENOENT") {
|
|
21886
24063
|
return null;
|
|
21887
24064
|
}
|
|
21888
24065
|
throw err;
|
|
21889
24066
|
}
|
|
21890
|
-
if (
|
|
24067
|
+
if (stat6.isSymbolicLink()) {
|
|
21891
24068
|
throw new Error(`FilesystemBackend remotePath traverses symlink: ${current}`);
|
|
21892
24069
|
}
|
|
21893
|
-
if (!
|
|
24070
|
+
if (!stat6.isDirectory()) {
|
|
21894
24071
|
return null;
|
|
21895
24072
|
}
|
|
21896
24073
|
}
|
|
@@ -21905,11 +24082,11 @@ var FilesystemBackend = class {
|
|
|
21905
24082
|
throw new Error(`FilesystemBackend remotePath parent escapes basePath: ${JSON.stringify(remotePath)}`);
|
|
21906
24083
|
}
|
|
21907
24084
|
try {
|
|
21908
|
-
const
|
|
21909
|
-
if (
|
|
24085
|
+
const stat6 = await fsp.lstat(dest);
|
|
24086
|
+
if (stat6.isSymbolicLink()) {
|
|
21910
24087
|
throw new Error(`FilesystemBackend remotePath points to symlink: ${dest}`);
|
|
21911
24088
|
}
|
|
21912
|
-
if (!
|
|
24089
|
+
if (!stat6.isFile()) {
|
|
21913
24090
|
return null;
|
|
21914
24091
|
}
|
|
21915
24092
|
} catch (err) {
|
|
@@ -21925,14 +24102,14 @@ var FilesystemBackend = class {
|
|
|
21925
24102
|
return dest;
|
|
21926
24103
|
}
|
|
21927
24104
|
async upload(localPath, remotePath) {
|
|
21928
|
-
if (
|
|
24105
|
+
if (path24.isAbsolute(remotePath)) {
|
|
21929
24106
|
throw new Error(`FilesystemBackend upload remotePath must be relative: ${JSON.stringify(remotePath)}`);
|
|
21930
24107
|
}
|
|
21931
24108
|
const dest = this.resolveRemotePath(remotePath);
|
|
21932
24109
|
const realBase = await this.ensureSafeParentDirectory(dest);
|
|
21933
24110
|
try {
|
|
21934
|
-
const
|
|
21935
|
-
if (
|
|
24111
|
+
const stat6 = await fsp.lstat(dest);
|
|
24112
|
+
if (stat6.isSymbolicLink()) {
|
|
21936
24113
|
throw new Error(`FilesystemBackend remotePath points to symlink: ${dest}`);
|
|
21937
24114
|
}
|
|
21938
24115
|
} catch (err) {
|
|
@@ -21998,7 +24175,7 @@ function createBackend(cfg) {
|
|
|
21998
24175
|
|
|
21999
24176
|
// src/binary-lifecycle/scanner.ts
|
|
22000
24177
|
import fsp2 from "fs/promises";
|
|
22001
|
-
import
|
|
24178
|
+
import path25 from "path";
|
|
22002
24179
|
import crypto from "crypto";
|
|
22003
24180
|
function matchesPatterns(filename, patterns) {
|
|
22004
24181
|
const lower = filename.toLowerCase();
|
|
@@ -22023,8 +24200,8 @@ async function scanForBinaries(memoryDir, config, manifest) {
|
|
|
22023
24200
|
return;
|
|
22024
24201
|
}
|
|
22025
24202
|
for (const entry of entries) {
|
|
22026
|
-
const fullPath =
|
|
22027
|
-
const relativePath =
|
|
24203
|
+
const fullPath = path25.join(dir, entry.name);
|
|
24204
|
+
const relativePath = path25.relative(memoryDir, fullPath).split(path25.sep).join("/");
|
|
22028
24205
|
if (entry.isDirectory()) {
|
|
22029
24206
|
if (entry.name === ".binary-lifecycle") continue;
|
|
22030
24207
|
await walk(fullPath);
|
|
@@ -22033,12 +24210,12 @@ async function scanForBinaries(memoryDir, config, manifest) {
|
|
|
22033
24210
|
if (!entry.isFile()) continue;
|
|
22034
24211
|
if (!matchesPatterns(entry.name, config.scanPatterns)) continue;
|
|
22035
24212
|
try {
|
|
22036
|
-
const
|
|
22037
|
-
if (
|
|
22038
|
-
if (
|
|
24213
|
+
const stat6 = await fsp2.stat(fullPath);
|
|
24214
|
+
if (stat6.size > config.maxBinarySizeBytes) continue;
|
|
24215
|
+
if (stat6.size === 0) continue;
|
|
22039
24216
|
const existing = tracked.get(relativePath);
|
|
22040
24217
|
if (existing) {
|
|
22041
|
-
if (existing.sizeBytes !==
|
|
24218
|
+
if (existing.sizeBytes !== stat6.size) {
|
|
22042
24219
|
results.push(relativePath);
|
|
22043
24220
|
continue;
|
|
22044
24221
|
}
|
|
@@ -22064,15 +24241,15 @@ async function hashFile(filePath) {
|
|
|
22064
24241
|
|
|
22065
24242
|
// src/binary-lifecycle/manifest.ts
|
|
22066
24243
|
import fsp3 from "fs/promises";
|
|
22067
|
-
import
|
|
24244
|
+
import path26 from "path";
|
|
22068
24245
|
import crypto2 from "crypto";
|
|
22069
24246
|
var MANIFEST_DIR = ".binary-lifecycle";
|
|
22070
24247
|
var MANIFEST_FILE = "manifest.json";
|
|
22071
24248
|
function manifestDir(memoryDir) {
|
|
22072
|
-
return
|
|
24249
|
+
return path26.join(memoryDir, MANIFEST_DIR);
|
|
22073
24250
|
}
|
|
22074
24251
|
function manifestPath(memoryDir) {
|
|
22075
|
-
return
|
|
24252
|
+
return path26.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);
|
|
22076
24253
|
}
|
|
22077
24254
|
async function readManifest(memoryDir) {
|
|
22078
24255
|
const filePath = manifestPath(memoryDir);
|
|
@@ -22124,13 +24301,13 @@ function emptyManifest() {
|
|
|
22124
24301
|
|
|
22125
24302
|
// src/binary-lifecycle/pipeline.ts
|
|
22126
24303
|
import fsp4 from "fs/promises";
|
|
22127
|
-
import
|
|
24304
|
+
import path27 from "path";
|
|
22128
24305
|
import crypto3 from "crypto";
|
|
22129
24306
|
var historicalIdCache = new HistoricalEntityCanonicalIdCache();
|
|
22130
24307
|
function isRecallCorpusMarkdown(mdPath, memoryDir) {
|
|
22131
|
-
const rel =
|
|
22132
|
-
if (rel.startsWith("..") ||
|
|
22133
|
-
const top = rel.split(
|
|
24308
|
+
const rel = path27.relative(memoryDir, mdPath);
|
|
24309
|
+
if (rel.startsWith("..") || path27.isAbsolute(rel)) return false;
|
|
24310
|
+
const top = rel.split(path27.sep)[0];
|
|
22134
24311
|
return RECALL_FALLBACK_DIRS.includes(top);
|
|
22135
24312
|
}
|
|
22136
24313
|
async function hashFile2(filePath) {
|
|
@@ -22154,13 +24331,13 @@ function escapeRegex(s) {
|
|
|
22154
24331
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22155
24332
|
}
|
|
22156
24333
|
function resolveManifestAssetPath(memoryDir, originalPath) {
|
|
22157
|
-
if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") ||
|
|
24334
|
+
if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") || path27.isAbsolute(originalPath) || path27.win32.isAbsolute(originalPath)) {
|
|
22158
24335
|
return null;
|
|
22159
24336
|
}
|
|
22160
|
-
const memoryRoot =
|
|
22161
|
-
const fullPath =
|
|
22162
|
-
const relative =
|
|
22163
|
-
if (relative === "" || relative === ".." || relative.startsWith(`..${
|
|
24337
|
+
const memoryRoot = path27.resolve(memoryDir);
|
|
24338
|
+
const fullPath = path27.resolve(memoryRoot, originalPath);
|
|
24339
|
+
const relative = path27.relative(memoryRoot, fullPath);
|
|
24340
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path27.sep}`) || path27.isAbsolute(relative)) {
|
|
22164
24341
|
return null;
|
|
22165
24342
|
}
|
|
22166
24343
|
return fullPath;
|
|
@@ -22171,7 +24348,7 @@ function validateBinaryLifecycleConfig(config) {
|
|
|
22171
24348
|
}
|
|
22172
24349
|
}
|
|
22173
24350
|
function remotePathForAsset(backend, relPath) {
|
|
22174
|
-
const normalized = relPath.split(
|
|
24351
|
+
const normalized = relPath.split(path27.sep).join("/");
|
|
22175
24352
|
if (backend.type === "filesystem") {
|
|
22176
24353
|
return `.binary-lifecycle/mirrors/${normalized}`;
|
|
22177
24354
|
}
|
|
@@ -22184,11 +24361,11 @@ async function stageMirror(memoryDir, newPaths, backend, assets, log2, dryRun) {
|
|
|
22184
24361
|
let mirrored = 0;
|
|
22185
24362
|
const errors = [];
|
|
22186
24363
|
for (const relPath of newPaths) {
|
|
22187
|
-
const fullPath =
|
|
24364
|
+
const fullPath = path27.join(memoryDir, relPath);
|
|
22188
24365
|
try {
|
|
22189
|
-
const
|
|
24366
|
+
const stat6 = await fsp4.stat(fullPath);
|
|
22190
24367
|
const contentHash = await hashFile2(fullPath);
|
|
22191
|
-
const ext =
|
|
24368
|
+
const ext = path27.extname(relPath);
|
|
22192
24369
|
const mimeType = guessMimeType(ext);
|
|
22193
24370
|
const remotePath = remotePathForAsset(backend, relPath);
|
|
22194
24371
|
let backendLocation = remotePath;
|
|
@@ -22201,7 +24378,7 @@ async function stageMirror(memoryDir, newPaths, backend, assets, log2, dryRun) {
|
|
|
22201
24378
|
mirroredPath: backendLocation,
|
|
22202
24379
|
...redirectPath ? { redirectPath } : {},
|
|
22203
24380
|
contentHash,
|
|
22204
|
-
sizeBytes:
|
|
24381
|
+
sizeBytes: stat6.size,
|
|
22205
24382
|
mimeType,
|
|
22206
24383
|
mirroredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22207
24384
|
status: "mirrored"
|
|
@@ -22214,7 +24391,7 @@ async function stageMirror(memoryDir, newPaths, backend, assets, log2, dryRun) {
|
|
|
22214
24391
|
assets.push(record);
|
|
22215
24392
|
}
|
|
22216
24393
|
mirrored++;
|
|
22217
|
-
log2.info(`[binary-lifecycle] mirrored: ${relPath} (${
|
|
24394
|
+
log2.info(`[binary-lifecycle] mirrored: ${relPath} (${stat6.size} bytes)${dryRun ? " [dry-run]" : ""}`);
|
|
22218
24395
|
} catch (err) {
|
|
22219
24396
|
const msg = `mirror failed for ${relPath}: ${err instanceof Error ? err.message : String(err)}`;
|
|
22220
24397
|
log2.error(`[binary-lifecycle] ${msg}`);
|
|
@@ -22248,8 +24425,8 @@ async function stageRedirect(memoryDir, assets, log2, dryRun, readMarkdownFile,
|
|
|
22248
24425
|
const pattern = markdownReferencePattern(asset, assetAbsolute, mdPath);
|
|
22249
24426
|
if (!pattern.test(content)) continue;
|
|
22250
24427
|
pattern.lastIndex = 0;
|
|
22251
|
-
const updated = content.replace(pattern, (_match,
|
|
22252
|
-
return `${
|
|
24428
|
+
const updated = content.replace(pattern, (_match, open3, _target, close) => {
|
|
24429
|
+
return `${open3}${markdownTargetForAsset(asset)}${close}`;
|
|
22253
24430
|
});
|
|
22254
24431
|
updates.push({ mdPath, content: updated });
|
|
22255
24432
|
} catch (err) {
|
|
@@ -22326,7 +24503,7 @@ async function stageRedirect(memoryDir, assets, log2, dryRun, readMarkdownFile,
|
|
|
22326
24503
|
for (const update of updates) {
|
|
22327
24504
|
try {
|
|
22328
24505
|
await withRawEntityPageMutation(memoryDir, update.mdPath, async () => {
|
|
22329
|
-
const stateDir =
|
|
24506
|
+
const stateDir = path27.join(memoryDir, "state");
|
|
22330
24507
|
const kind = classifyEntityRefWritePath(memoryDir, update.mdPath);
|
|
22331
24508
|
if (kind === "memory") {
|
|
22332
24509
|
const idsAtWrite = historicalIdCache.get(stateDir);
|
|
@@ -22420,10 +24597,10 @@ async function countRemainingLocalReferences(memoryDir, asset, assetAbsolute, md
|
|
|
22420
24597
|
return { remaining, errors };
|
|
22421
24598
|
}
|
|
22422
24599
|
function markdownReferencePattern(asset, assetAbsolute, mdPath) {
|
|
22423
|
-
const mdDir =
|
|
24600
|
+
const mdDir = path27.dirname(mdPath);
|
|
22424
24601
|
const candidates = /* @__PURE__ */ new Set();
|
|
22425
24602
|
const addCandidate = (candidate) => {
|
|
22426
|
-
const normalized = candidate.split(
|
|
24603
|
+
const normalized = candidate.split(path27.sep).join("/");
|
|
22427
24604
|
if (normalized.length === 0) return;
|
|
22428
24605
|
candidates.add(normalized);
|
|
22429
24606
|
const isParentTraversal = normalized === ".." || normalized.startsWith("../");
|
|
@@ -22431,10 +24608,10 @@ function markdownReferencePattern(asset, assetAbsolute, mdPath) {
|
|
|
22431
24608
|
candidates.add(`./${normalized}`);
|
|
22432
24609
|
}
|
|
22433
24610
|
};
|
|
22434
|
-
addCandidate(
|
|
22435
|
-
const originalPath = asset.originalPath.split(
|
|
22436
|
-
const originalAsFileRelative =
|
|
22437
|
-
if (
|
|
24611
|
+
addCandidate(path27.relative(mdDir, assetAbsolute));
|
|
24612
|
+
const originalPath = asset.originalPath.split(path27.sep).join("/");
|
|
24613
|
+
const originalAsFileRelative = path27.resolve(mdDir, ...originalPath.split("/"));
|
|
24614
|
+
if (path27.resolve(originalAsFileRelative) === path27.resolve(assetAbsolute)) {
|
|
22438
24615
|
addCandidate(originalPath);
|
|
22439
24616
|
}
|
|
22440
24617
|
addCandidate(`/${originalPath}`);
|
|
@@ -22530,7 +24707,7 @@ async function findMarkdownFiles(dir) {
|
|
|
22530
24707
|
return;
|
|
22531
24708
|
}
|
|
22532
24709
|
for (const entry of entries) {
|
|
22533
|
-
const full =
|
|
24710
|
+
const full = path27.join(current, entry.name);
|
|
22534
24711
|
if (entry.isDirectory()) {
|
|
22535
24712
|
if (entry.name === ".binary-lifecycle") continue;
|
|
22536
24713
|
await walk(full);
|
|
@@ -22605,7 +24782,7 @@ async function runBinaryLifecyclePipeline(memoryDir, config, backend, log2, opts
|
|
|
22605
24782
|
|
|
22606
24783
|
// src/projection/index.ts
|
|
22607
24784
|
import fs from "fs";
|
|
22608
|
-
import
|
|
24785
|
+
import path28 from "path";
|
|
22609
24786
|
var VALID_PROJECTION_CATEGORIES = new Set(ALL_CATEGORY_KEYS);
|
|
22610
24787
|
async function generateContextTree(options) {
|
|
22611
24788
|
const startTime = Date.now();
|
|
@@ -22620,8 +24797,8 @@ async function generateContextTree(options) {
|
|
|
22620
24797
|
let nodesGenerated = 0;
|
|
22621
24798
|
let nodesSkipped = 0;
|
|
22622
24799
|
const categoryCounts = {};
|
|
22623
|
-
const resolvedMemoryDir =
|
|
22624
|
-
const resolvedOutputDir =
|
|
24800
|
+
const resolvedMemoryDir = path28.resolve(memoryDir);
|
|
24801
|
+
const resolvedOutputDir = path28.resolve(outputDir);
|
|
22625
24802
|
const requestedCategories = validateProjectionCategories(filterCategories);
|
|
22626
24803
|
const realMemoryDir = assertSafeMemoryRoot(resolvedMemoryDir);
|
|
22627
24804
|
assertNotSymlink(resolvedOutputDir, "context tree outputDir");
|
|
@@ -22659,7 +24836,7 @@ async function generateContextTree(options) {
|
|
|
22659
24836
|
}
|
|
22660
24837
|
}
|
|
22661
24838
|
if (includeEntities) {
|
|
22662
|
-
const entitiesDir =
|
|
24839
|
+
const entitiesDir = path28.join(memoryDir, "entities");
|
|
22663
24840
|
if (fs.existsSync(entitiesDir)) {
|
|
22664
24841
|
assertSafeInputRoot(realMemoryDir, entitiesDir, "entities root");
|
|
22665
24842
|
categoryCounts["entity"] = 0;
|
|
@@ -22671,7 +24848,7 @@ async function generateContextTree(options) {
|
|
|
22671
24848
|
continue;
|
|
22672
24849
|
}
|
|
22673
24850
|
const content = fs.readFileSync(filePath, "utf8");
|
|
22674
|
-
const fileName =
|
|
24851
|
+
const fileName = path28.basename(filePath, ".md");
|
|
22675
24852
|
const node = projectEntityNode(fileName, content);
|
|
22676
24853
|
const outputPath = resolveContainedOutputPath(realOutputDir, "entities", `${fileName}.md`);
|
|
22677
24854
|
writeProjectedContent(realOutputDir, outputPath, node.content);
|
|
@@ -22683,7 +24860,7 @@ async function generateContextTree(options) {
|
|
|
22683
24860
|
}
|
|
22684
24861
|
const shouldIncludeQuestions = includeQuestions && (requestedCategories === void 0 || requestedCategories.includes("question"));
|
|
22685
24862
|
if (shouldIncludeQuestions) {
|
|
22686
|
-
const questionsDir =
|
|
24863
|
+
const questionsDir = path28.join(memoryDir, "questions");
|
|
22687
24864
|
if (fs.existsSync(questionsDir)) {
|
|
22688
24865
|
assertSafeInputRoot(realMemoryDir, questionsDir, "questions root");
|
|
22689
24866
|
categoryCounts["question"] = 0;
|
|
@@ -22724,8 +24901,8 @@ async function generateContextTree(options) {
|
|
|
22724
24901
|
};
|
|
22725
24902
|
}
|
|
22726
24903
|
function isPathInside(root, candidate) {
|
|
22727
|
-
const relative =
|
|
22728
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
24904
|
+
const relative = path28.relative(root, candidate);
|
|
24905
|
+
return relative === "" || !relative.startsWith("..") && !path28.isAbsolute(relative);
|
|
22729
24906
|
}
|
|
22730
24907
|
function assertNotSymlink(targetPath, label) {
|
|
22731
24908
|
try {
|
|
@@ -22738,21 +24915,21 @@ function assertNotSymlink(targetPath, label) {
|
|
|
22738
24915
|
}
|
|
22739
24916
|
}
|
|
22740
24917
|
function assertSafeMemoryRoot(targetPath) {
|
|
22741
|
-
const
|
|
22742
|
-
if (
|
|
24918
|
+
const stat6 = fs.lstatSync(targetPath);
|
|
24919
|
+
if (stat6.isSymbolicLink()) {
|
|
22743
24920
|
throw new Error(`memoryDir must not be a symlink: ${targetPath}`);
|
|
22744
24921
|
}
|
|
22745
|
-
if (!
|
|
24922
|
+
if (!stat6.isDirectory()) {
|
|
22746
24923
|
throw new Error(`memoryDir must be a directory: ${targetPath}`);
|
|
22747
24924
|
}
|
|
22748
24925
|
return fs.realpathSync(targetPath);
|
|
22749
24926
|
}
|
|
22750
24927
|
function assertSafeInputRoot(realMemoryDir, targetPath, label) {
|
|
22751
|
-
const
|
|
22752
|
-
if (
|
|
24928
|
+
const stat6 = fs.lstatSync(targetPath);
|
|
24929
|
+
if (stat6.isSymbolicLink()) {
|
|
22753
24930
|
throw new Error(`${label} must not be a symlink: ${targetPath}`);
|
|
22754
24931
|
}
|
|
22755
|
-
if (!
|
|
24932
|
+
if (!stat6.isDirectory()) {
|
|
22756
24933
|
throw new Error(`${label} must be a directory: ${targetPath}`);
|
|
22757
24934
|
}
|
|
22758
24935
|
const realTarget = fs.realpathSync(targetPath);
|
|
@@ -22762,15 +24939,15 @@ function assertSafeInputRoot(realMemoryDir, targetPath, label) {
|
|
|
22762
24939
|
}
|
|
22763
24940
|
function assertSafeOutputTarget(realOutputDir, outputPath) {
|
|
22764
24941
|
let current = realOutputDir;
|
|
22765
|
-
const relative =
|
|
22766
|
-
if (relative.startsWith("..") ||
|
|
24942
|
+
const relative = path28.relative(realOutputDir, outputPath);
|
|
24943
|
+
if (relative.startsWith("..") || path28.isAbsolute(relative)) {
|
|
22767
24944
|
throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
|
|
22768
24945
|
}
|
|
22769
|
-
for (const segment of relative.split(
|
|
22770
|
-
current =
|
|
24946
|
+
for (const segment of relative.split(path28.sep).filter(Boolean)) {
|
|
24947
|
+
current = path28.join(current, segment);
|
|
22771
24948
|
try {
|
|
22772
|
-
const
|
|
22773
|
-
if (
|
|
24949
|
+
const stat6 = fs.lstatSync(current);
|
|
24950
|
+
if (stat6.isSymbolicLink()) {
|
|
22774
24951
|
throw new Error(`context tree output path contains symlink: ${current}`);
|
|
22775
24952
|
}
|
|
22776
24953
|
} catch (err) {
|
|
@@ -22797,17 +24974,17 @@ function validateProjectionCategories(categories) {
|
|
|
22797
24974
|
return validated;
|
|
22798
24975
|
}
|
|
22799
24976
|
function resolveContainedOutputPath(outputRoot, ...segments) {
|
|
22800
|
-
const resolved =
|
|
22801
|
-
const relative =
|
|
22802
|
-
if (relative === "" || !relative.startsWith("..") && !
|
|
24977
|
+
const resolved = path28.resolve(outputRoot, ...segments);
|
|
24978
|
+
const relative = path28.relative(outputRoot, resolved);
|
|
24979
|
+
if (relative === "" || !relative.startsWith("..") && !path28.isAbsolute(relative)) {
|
|
22803
24980
|
return resolved;
|
|
22804
24981
|
}
|
|
22805
24982
|
throw new Error(`context tree output path escapes outputDir: ${segments.join("/")}`);
|
|
22806
24983
|
}
|
|
22807
24984
|
function writeProjectedContent(realOutputDir, outputPath, generatedContent) {
|
|
22808
24985
|
assertSafeOutputTarget(realOutputDir, outputPath);
|
|
22809
|
-
fs.mkdirSync(
|
|
22810
|
-
const realParent = fs.realpathSync(
|
|
24986
|
+
fs.mkdirSync(path28.dirname(outputPath), { recursive: true });
|
|
24987
|
+
const realParent = fs.realpathSync(path28.dirname(outputPath));
|
|
22811
24988
|
if (!isPathInside(realOutputDir, realParent)) {
|
|
22812
24989
|
throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
|
|
22813
24990
|
}
|
|
@@ -22846,7 +25023,7 @@ function walkR(dir, realMemoryDir) {
|
|
|
22846
25023
|
const results = [];
|
|
22847
25024
|
function walk(directory) {
|
|
22848
25025
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
22849
|
-
const fullPath =
|
|
25026
|
+
const fullPath = path28.join(directory, entry.name);
|
|
22850
25027
|
if (entry.isSymbolicLink()) {
|
|
22851
25028
|
throw new Error(`context tree input path contains symlink: ${fullPath}`);
|
|
22852
25029
|
}
|
|
@@ -22899,13 +25076,13 @@ function extractBody(content) {
|
|
|
22899
25076
|
}
|
|
22900
25077
|
function projectNode(filePath, category, fm, rawContent) {
|
|
22901
25078
|
const body = extractBody(rawContent);
|
|
22902
|
-
const fileName =
|
|
22903
|
-
const dateDir =
|
|
25079
|
+
const fileName = path28.basename(filePath, ".md");
|
|
25080
|
+
const dateDir = path28.basename(path28.dirname(filePath));
|
|
22904
25081
|
let relPath;
|
|
22905
25082
|
if (/^\d{4}-\d{2}-\d{2}$/.test(dateDir)) {
|
|
22906
|
-
relPath =
|
|
25083
|
+
relPath = path28.join(category, dateDir, `${fileName}.md`);
|
|
22907
25084
|
} else {
|
|
22908
|
-
relPath =
|
|
25085
|
+
relPath = path28.join(category, `${fileName}.md`);
|
|
22909
25086
|
}
|
|
22910
25087
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
22911
25088
|
const md = `# ${fm.id}
|
|
@@ -22951,7 +25128,7 @@ function projectEntityNode(fileName, content) {
|
|
|
22951
25128
|
${content}
|
|
22952
25129
|
`;
|
|
22953
25130
|
return {
|
|
22954
|
-
path:
|
|
25131
|
+
path: path28.join("entities", `${fileName}.md`),
|
|
22955
25132
|
category: "entity",
|
|
22956
25133
|
title: fileName,
|
|
22957
25134
|
content: md,
|
|
@@ -23004,7 +25181,7 @@ function generateIndex(categoryCounts, outputDir) {
|
|
|
23004
25181
|
|
|
23005
25182
|
// src/onboarding/index.ts
|
|
23006
25183
|
import fs2 from "fs";
|
|
23007
|
-
import
|
|
25184
|
+
import path29 from "path";
|
|
23008
25185
|
var LANGUAGE_RULES = [
|
|
23009
25186
|
{
|
|
23010
25187
|
language: "TypeScript",
|
|
@@ -23106,7 +25283,7 @@ function onboard(options) {
|
|
|
23106
25283
|
maxDepth = 6,
|
|
23107
25284
|
excludeDirs = []
|
|
23108
25285
|
} = options;
|
|
23109
|
-
const directory =
|
|
25286
|
+
const directory = path29.resolve(options.directory ?? process.cwd());
|
|
23110
25287
|
let rootStat;
|
|
23111
25288
|
try {
|
|
23112
25289
|
rootStat = fs2.statSync(directory);
|
|
@@ -23148,7 +25325,7 @@ function walkDir(root, exclude, maxDepth) {
|
|
|
23148
25325
|
}
|
|
23149
25326
|
for (const entry of entries) {
|
|
23150
25327
|
if (exclude.has(entry.name)) continue;
|
|
23151
|
-
const fullPath =
|
|
25328
|
+
const fullPath = path29.join(dir, entry.name);
|
|
23152
25329
|
if (entry.isDirectory()) {
|
|
23153
25330
|
walk(fullPath, depth + 1);
|
|
23154
25331
|
} else if (entry.isFile()) {
|
|
@@ -23163,11 +25340,11 @@ function detectLanguages(files, root) {
|
|
|
23163
25340
|
const results = [];
|
|
23164
25341
|
const extCounts = /* @__PURE__ */ new Map();
|
|
23165
25342
|
for (const f of files) {
|
|
23166
|
-
const ext =
|
|
25343
|
+
const ext = path29.extname(f).toLowerCase();
|
|
23167
25344
|
if (ext) extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
|
|
23168
25345
|
}
|
|
23169
25346
|
const rootFiles = new Set(
|
|
23170
|
-
files.filter((f) =>
|
|
25347
|
+
files.filter((f) => path29.dirname(f) === root).map((f) => path29.basename(f))
|
|
23171
25348
|
);
|
|
23172
25349
|
for (const rule of LANGUAGE_RULES) {
|
|
23173
25350
|
const evidence = [];
|
|
@@ -23211,7 +25388,7 @@ function detectLanguages(files, root) {
|
|
|
23211
25388
|
}
|
|
23212
25389
|
function detectShape(files, root) {
|
|
23213
25390
|
const rootFiles = new Set(
|
|
23214
|
-
files.filter((f) =>
|
|
25391
|
+
files.filter((f) => path29.dirname(f) === root).map((f) => path29.basename(f))
|
|
23215
25392
|
);
|
|
23216
25393
|
const rootDirs = /* @__PURE__ */ new Set();
|
|
23217
25394
|
try {
|
|
@@ -23222,7 +25399,7 @@ function detectShape(files, root) {
|
|
|
23222
25399
|
}
|
|
23223
25400
|
const evidence = [];
|
|
23224
25401
|
if (rootFiles.has("package.json")) {
|
|
23225
|
-
const pkg = readJsonSafe(
|
|
25402
|
+
const pkg = readJsonSafe(path29.join(root, "package.json"));
|
|
23226
25403
|
if (pkg?.workspaces) {
|
|
23227
25404
|
evidence.push("package.json has workspaces");
|
|
23228
25405
|
return { shape: "monorepo", evidence };
|
|
@@ -23236,13 +25413,13 @@ function detectShape(files, root) {
|
|
|
23236
25413
|
evidence.push("workspace manifest found");
|
|
23237
25414
|
return { shape: "workspace", evidence };
|
|
23238
25415
|
}
|
|
23239
|
-
const cargoToml = readTomlWorkspace(
|
|
25416
|
+
const cargoToml = readTomlWorkspace(path29.join(root, "Cargo.toml"));
|
|
23240
25417
|
if (cargoToml) {
|
|
23241
25418
|
evidence.push("Cargo.toml has workspace");
|
|
23242
25419
|
return { shape: "workspace", evidence };
|
|
23243
25420
|
}
|
|
23244
25421
|
if (rootFiles.has("package.json")) {
|
|
23245
|
-
const pkg = readJsonSafe(
|
|
25422
|
+
const pkg = readJsonSafe(path29.join(root, "package.json"));
|
|
23246
25423
|
if (pkg?.exports || pkg?.main) {
|
|
23247
25424
|
if (pkg?.bin) {
|
|
23248
25425
|
evidence.push("package.json has bin");
|
|
@@ -23276,8 +25453,8 @@ function discoverDocs(files, root) {
|
|
|
23276
25453
|
{ pattern: /^\.editorconfig$/i, kind: "config" }
|
|
23277
25454
|
];
|
|
23278
25455
|
for (const filePath of files) {
|
|
23279
|
-
const basename =
|
|
23280
|
-
const relPath =
|
|
25456
|
+
const basename = path29.basename(filePath).toLowerCase();
|
|
25457
|
+
const relPath = path29.relative(root, filePath);
|
|
23281
25458
|
let kind;
|
|
23282
25459
|
for (const { pattern, kind: k } of docPatterns) {
|
|
23283
25460
|
if (pattern.test(basename)) {
|
|
@@ -23289,7 +25466,7 @@ function discoverDocs(files, root) {
|
|
|
23289
25466
|
kind = "docs";
|
|
23290
25467
|
}
|
|
23291
25468
|
if (!kind && (basename.endsWith(".md") || basename.endsWith(".mdx"))) {
|
|
23292
|
-
if (
|
|
25469
|
+
if (path29.dirname(relPath) === "." || isUnderDocsDir(relPath)) {
|
|
23293
25470
|
kind = "docs";
|
|
23294
25471
|
}
|
|
23295
25472
|
}
|
|
@@ -23310,7 +25487,7 @@ function discoverDocs(files, root) {
|
|
|
23310
25487
|
return docs;
|
|
23311
25488
|
}
|
|
23312
25489
|
function isUnderDocsDir(relPath) {
|
|
23313
|
-
const parts = relPath.split(
|
|
25490
|
+
const parts = relPath.split(path29.sep);
|
|
23314
25491
|
return parts[0] === "docs" || parts[0] === "doc" || parts[0] === "documentation";
|
|
23315
25492
|
}
|
|
23316
25493
|
function buildPlan(languages, shape, docs, _root) {
|
|
@@ -23354,7 +25531,7 @@ function readTomlWorkspace(filePath) {
|
|
|
23354
25531
|
|
|
23355
25532
|
// src/curation/index.ts
|
|
23356
25533
|
import fs3 from "fs";
|
|
23357
|
-
import
|
|
25534
|
+
import path30 from "path";
|
|
23358
25535
|
import crypto4 from "crypto";
|
|
23359
25536
|
async function curate(options) {
|
|
23360
25537
|
const startTime = Date.now();
|
|
@@ -23439,18 +25616,18 @@ async function curate(options) {
|
|
|
23439
25616
|
};
|
|
23440
25617
|
}
|
|
23441
25618
|
function resolveTargets(targetPath) {
|
|
23442
|
-
const
|
|
23443
|
-
if (
|
|
25619
|
+
const stat6 = fs3.statSync(targetPath);
|
|
25620
|
+
if (stat6.isFile()) return [targetPath];
|
|
23444
25621
|
const results = [];
|
|
23445
25622
|
const extensions = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
|
|
23446
25623
|
function walk(dir) {
|
|
23447
25624
|
for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
23448
|
-
const fullPath =
|
|
25625
|
+
const fullPath = path30.join(dir, entry.name);
|
|
23449
25626
|
if (entry.isDirectory()) {
|
|
23450
25627
|
if (entry.name !== "node_modules" && entry.name !== ".git") {
|
|
23451
25628
|
walk(fullPath);
|
|
23452
25629
|
}
|
|
23453
|
-
} else if (extensions.has(
|
|
25630
|
+
} else if (extensions.has(path30.extname(entry.name).toLowerCase())) {
|
|
23454
25631
|
results.push(fullPath);
|
|
23455
25632
|
}
|
|
23456
25633
|
}
|
|
@@ -23459,12 +25636,12 @@ function resolveTargets(targetPath) {
|
|
|
23459
25636
|
return results;
|
|
23460
25637
|
}
|
|
23461
25638
|
function resolveProvenanceRoot(targetPath) {
|
|
23462
|
-
const resolvedTarget =
|
|
23463
|
-
const
|
|
23464
|
-
return
|
|
25639
|
+
const resolvedTarget = path30.resolve(targetPath);
|
|
25640
|
+
const stat6 = fs3.statSync(resolvedTarget);
|
|
25641
|
+
return stat6.isFile() ? path30.dirname(resolvedTarget) : resolvedTarget;
|
|
23465
25642
|
}
|
|
23466
25643
|
function extractStatements(content, filePath, projectRoot, source, sourceFileHash, categoryOverride, confidence, entityRef, tags) {
|
|
23467
|
-
const relativePath =
|
|
25644
|
+
const relativePath = path30.relative(projectRoot, path30.resolve(filePath)) || path30.basename(filePath);
|
|
23468
25645
|
const statements = [];
|
|
23469
25646
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23470
25647
|
const paragraphs = content.split(/\n{2,}/).map((p) => p.trim()).filter((p) => p.length > 20 && p.length < 2e3);
|
|
@@ -23558,7 +25735,7 @@ function loadExistingMemories(memoryDir) {
|
|
|
23558
25735
|
if (!fs3.existsSync(memoryDir)) return result;
|
|
23559
25736
|
const dirs = ALL_CATEGORY_DIRS;
|
|
23560
25737
|
for (const dir of dirs) {
|
|
23561
|
-
const fullDir =
|
|
25738
|
+
const fullDir = path30.join(memoryDir, dir);
|
|
23562
25739
|
if (!fs3.existsSync(fullDir)) continue;
|
|
23563
25740
|
walkFiles(fullDir, (filePath) => {
|
|
23564
25741
|
const content = readFileSafe(filePath);
|
|
@@ -23581,11 +25758,11 @@ function writeStatement(stmt, memoryDir) {
|
|
|
23581
25758
|
const now = /* @__PURE__ */ new Date();
|
|
23582
25759
|
const dateDir = now.toISOString().split("T")[0];
|
|
23583
25760
|
const categoryDir = getCategoryDir(memoryDir, stmt.category);
|
|
23584
|
-
const dir =
|
|
25761
|
+
const dir = path30.join(categoryDir, dateDir);
|
|
23585
25762
|
fs3.mkdirSync(dir, { recursive: true });
|
|
23586
25763
|
const fileName = `${stmt.category}-${Date.now()}-${stmt.id.slice(0, 8)}.md`;
|
|
23587
|
-
const filePath =
|
|
23588
|
-
const historicalIds = historicalIdCache2.get(
|
|
25764
|
+
const filePath = path30.join(dir, fileName);
|
|
25765
|
+
const historicalIds = historicalIdCache2.get(path30.join(memoryDir, "state"));
|
|
23589
25766
|
const rawBody = `${[
|
|
23590
25767
|
"---",
|
|
23591
25768
|
`id: ${stmt.id}`,
|
|
@@ -23608,7 +25785,7 @@ ${stmt.content}
|
|
|
23608
25785
|
fs3.writeFileSync(filePath, body);
|
|
23609
25786
|
try {
|
|
23610
25787
|
repairContentAfterJournalMoveSync({
|
|
23611
|
-
stateDir:
|
|
25788
|
+
stateDir: path30.join(memoryDir, "state"),
|
|
23612
25789
|
cache: historicalIdCache2,
|
|
23613
25790
|
idsAtWrite: historicalIds,
|
|
23614
25791
|
rawContent: rawBody,
|
|
@@ -23669,7 +25846,7 @@ function extractBody2(content) {
|
|
|
23669
25846
|
}
|
|
23670
25847
|
function walkFiles(dir, callback) {
|
|
23671
25848
|
for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
23672
|
-
const fullPath =
|
|
25849
|
+
const fullPath = path30.join(dir, entry.name);
|
|
23673
25850
|
if (entry.isDirectory()) {
|
|
23674
25851
|
walkFiles(fullPath, callback);
|
|
23675
25852
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -23681,7 +25858,7 @@ function walkFiles(dir, callback) {
|
|
|
23681
25858
|
// src/dedup/index.ts
|
|
23682
25859
|
import crypto5 from "crypto";
|
|
23683
25860
|
import fs4 from "fs";
|
|
23684
|
-
import
|
|
25861
|
+
import path31 from "path";
|
|
23685
25862
|
var DEFAULT_DEDUP_THRESHOLD = 0.85;
|
|
23686
25863
|
var DEFAULT_MAX_LOAD = 1e4;
|
|
23687
25864
|
function normalizeThreshold(value, defaultValue) {
|
|
@@ -23875,7 +26052,7 @@ function loadMemories(memoryDir, categories, maxLoad = 1e4) {
|
|
|
23875
26052
|
const memoryRootReal = fs4.realpathSync(memoryDir);
|
|
23876
26053
|
for (const category of allCategories) {
|
|
23877
26054
|
if (result.length >= maxLoad) break;
|
|
23878
|
-
const dir =
|
|
26055
|
+
const dir = path31.join(memoryDir, category);
|
|
23879
26056
|
if (!fs4.existsSync(dir)) continue;
|
|
23880
26057
|
const categoryStat = fs4.lstatSync(dir);
|
|
23881
26058
|
if (categoryStat.isSymbolicLink()) {
|
|
@@ -23936,15 +26113,15 @@ function extractBody3(content) {
|
|
|
23936
26113
|
return match ? match[1].trim() : content.trim();
|
|
23937
26114
|
}
|
|
23938
26115
|
function assertPathInsideRoot2(rootReal, targetReal, sourcePath) {
|
|
23939
|
-
const rel =
|
|
23940
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
26116
|
+
const rel = path31.relative(rootReal, targetReal);
|
|
26117
|
+
if (rel === "" || !rel.startsWith("..") && !path31.isAbsolute(rel)) {
|
|
23941
26118
|
return;
|
|
23942
26119
|
}
|
|
23943
26120
|
throw new Error(`Refusing to scan memory path outside root: ${sourcePath}`);
|
|
23944
26121
|
}
|
|
23945
26122
|
function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
|
|
23946
26123
|
for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
|
|
23947
|
-
const fullPath =
|
|
26124
|
+
const fullPath = path31.join(dir, entry.name);
|
|
23948
26125
|
const entryStat = fs4.lstatSync(fullPath);
|
|
23949
26126
|
if (entryStat.isSymbolicLink()) {
|
|
23950
26127
|
throw new Error(`Refusing to scan symlinked memory path: ${fullPath}`);
|
|
@@ -23962,10 +26139,10 @@ function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
|
|
|
23962
26139
|
|
|
23963
26140
|
// src/review/index.ts
|
|
23964
26141
|
import fs5 from "fs";
|
|
23965
|
-
import
|
|
26142
|
+
import path32 from "path";
|
|
23966
26143
|
var historicalIdCache3 = new HistoricalEntityCanonicalIdCache();
|
|
23967
26144
|
function writeReviewFileCanonicalized(memoryDir, filePath, content) {
|
|
23968
|
-
const stateDir =
|
|
26145
|
+
const stateDir = path32.join(memoryDir, "state");
|
|
23969
26146
|
const idsAtWrite = historicalIdCache3.get(stateDir);
|
|
23970
26147
|
let originalBytes = null;
|
|
23971
26148
|
try {
|
|
@@ -23999,21 +26176,21 @@ function writeReviewFileCanonicalized(memoryDir, filePath, content) {
|
|
|
23999
26176
|
var DEFAULT_CONFIDENCE_THRESHOLD = 0.7;
|
|
24000
26177
|
function realMemoryRoot(memoryDir) {
|
|
24001
26178
|
try {
|
|
24002
|
-
const
|
|
24003
|
-
if (!
|
|
26179
|
+
const stat6 = fs5.lstatSync(memoryDir);
|
|
26180
|
+
if (!stat6.isDirectory() || stat6.isSymbolicLink()) return null;
|
|
24004
26181
|
return fs5.realpathSync(memoryDir);
|
|
24005
26182
|
} catch {
|
|
24006
26183
|
return null;
|
|
24007
26184
|
}
|
|
24008
26185
|
}
|
|
24009
26186
|
function isPathInside2(rootReal, candidateReal) {
|
|
24010
|
-
const relative =
|
|
24011
|
-
return relative === "" || !!relative && !relative.startsWith("..") && !
|
|
26187
|
+
const relative = path32.relative(rootReal, candidateReal);
|
|
26188
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path32.isAbsolute(relative);
|
|
24012
26189
|
}
|
|
24013
26190
|
function isSafeDirectory(rootReal, dir) {
|
|
24014
26191
|
try {
|
|
24015
|
-
const
|
|
24016
|
-
if (!
|
|
26192
|
+
const stat6 = fs5.lstatSync(dir);
|
|
26193
|
+
if (!stat6.isDirectory() || stat6.isSymbolicLink()) return false;
|
|
24017
26194
|
return isPathInside2(rootReal, fs5.realpathSync(dir));
|
|
24018
26195
|
} catch {
|
|
24019
26196
|
return false;
|
|
@@ -24051,7 +26228,7 @@ function listReviewItems(options) {
|
|
|
24051
26228
|
if (filterReason && item.reviewReason !== filterReason) return;
|
|
24052
26229
|
items.push(item);
|
|
24053
26230
|
};
|
|
24054
|
-
const suggestionsDir =
|
|
26231
|
+
const suggestionsDir = path32.join(memoryDir, "suggestions");
|
|
24055
26232
|
if (!isLimitReached() && fs5.existsSync(suggestionsDir) && isSafeDirectory(rootReal, suggestionsDir)) {
|
|
24056
26233
|
walkMd(rootReal, suggestionsDir, (filePath, content) => {
|
|
24057
26234
|
if (isLimitReached()) return true;
|
|
@@ -24072,7 +26249,7 @@ function listReviewItems(options) {
|
|
|
24072
26249
|
return isLimitReached();
|
|
24073
26250
|
});
|
|
24074
26251
|
}
|
|
24075
|
-
const reviewDir =
|
|
26252
|
+
const reviewDir = path32.join(memoryDir, "review");
|
|
24076
26253
|
if (!isLimitReached() && fs5.existsSync(reviewDir) && isSafeDirectory(rootReal, reviewDir)) {
|
|
24077
26254
|
walkMd(rootReal, reviewDir, (filePath, content) => {
|
|
24078
26255
|
if (isLimitReached()) return true;
|
|
@@ -24097,7 +26274,7 @@ function listReviewItems(options) {
|
|
|
24097
26274
|
const categories = ALL_CATEGORY_DIRS;
|
|
24098
26275
|
for (const category of categories) {
|
|
24099
26276
|
if (isLimitReached()) break;
|
|
24100
|
-
const dir =
|
|
26277
|
+
const dir = path32.join(memoryDir, category);
|
|
24101
26278
|
if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
|
|
24102
26279
|
walkMd(rootReal, dir, (filePath, content) => {
|
|
24103
26280
|
if (isLimitReached()) return true;
|
|
@@ -24179,9 +26356,9 @@ function approveItem(memoryDir, itemId, options) {
|
|
|
24179
26356
|
const targetDir = getCategoryDir(memoryDir, category);
|
|
24180
26357
|
const dateDir = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
24181
26358
|
ensureSafeDirectory(rootReal, targetDir);
|
|
24182
|
-
const outputPath =
|
|
24183
|
-
ensureSafeDirectory(rootReal,
|
|
24184
|
-
const reviewStateDir =
|
|
26359
|
+
const outputPath = path32.join(targetDir, dateDir, path32.basename(found.filePath));
|
|
26360
|
+
ensureSafeDirectory(rootReal, path32.dirname(outputPath));
|
|
26361
|
+
const reviewStateDir = path32.join(memoryDir, "state");
|
|
24185
26362
|
const idsAtWrite = historicalIdCache3.get(reviewStateDir);
|
|
24186
26363
|
const promotedWritten = canonicalizeEntityRefFrontmatter(updatedContent, idsAtWrite);
|
|
24187
26364
|
const promotedPath = writeFileWithoutClobber(outputPath, promotedWritten, itemId);
|
|
@@ -24258,13 +26435,13 @@ function findReviewFileById(memoryDir, id, options = {}) {
|
|
|
24258
26435
|
const rootReal = realMemoryRoot(memoryDir);
|
|
24259
26436
|
if (!rootReal) return null;
|
|
24260
26437
|
for (const loc of ["suggestions", "review"]) {
|
|
24261
|
-
const dir =
|
|
26438
|
+
const dir = path32.join(memoryDir, loc);
|
|
24262
26439
|
if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
|
|
24263
26440
|
const found = findFileById(rootReal, dir, id);
|
|
24264
26441
|
if (found) return { filePath: found, location: "queue" };
|
|
24265
26442
|
}
|
|
24266
26443
|
for (const category of ALL_CATEGORY_DIRS) {
|
|
24267
|
-
const dir =
|
|
26444
|
+
const dir = path32.join(memoryDir, category);
|
|
24268
26445
|
if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
|
|
24269
26446
|
const found = findFileById(rootReal, dir, id, (fm) => isLowConfidenceReviewCandidate(fm, options));
|
|
24270
26447
|
if (found) return { filePath: found, location: "category" };
|
|
@@ -24340,10 +26517,10 @@ function readFileSafe3(filePath) {
|
|
|
24340
26517
|
}
|
|
24341
26518
|
}
|
|
24342
26519
|
function writeFileWithoutClobber(basePath, content, discriminator) {
|
|
24343
|
-
const parsed =
|
|
26520
|
+
const parsed = path32.parse(basePath);
|
|
24344
26521
|
const safeDiscriminator = sanitizeFilePart(discriminator);
|
|
24345
26522
|
for (let attempt = 0; attempt < 1e3; attempt++) {
|
|
24346
|
-
const candidate = attempt === 0 ? basePath :
|
|
26523
|
+
const candidate = attempt === 0 ? basePath : path32.join(
|
|
24347
26524
|
parsed.dir,
|
|
24348
26525
|
`${parsed.name}-${safeDiscriminator}${attempt === 1 ? "" : `-${attempt}`}${parsed.ext || ".md"}`
|
|
24349
26526
|
);
|
|
@@ -24399,7 +26576,7 @@ function extractBody4(content) {
|
|
|
24399
26576
|
function walkMd(rootReal, dir, callback) {
|
|
24400
26577
|
if (!isSafeDirectory(rootReal, dir)) return false;
|
|
24401
26578
|
for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
|
|
24402
|
-
const fullPath =
|
|
26579
|
+
const fullPath = path32.join(dir, entry.name);
|
|
24403
26580
|
if (entry.isSymbolicLink()) continue;
|
|
24404
26581
|
if (entry.isDirectory()) {
|
|
24405
26582
|
if (walkMd(rootReal, fullPath, callback)) return true;
|
|
@@ -24414,7 +26591,7 @@ function walkMdPaths(rootReal, dir) {
|
|
|
24414
26591
|
const results = [];
|
|
24415
26592
|
if (!isSafeDirectory(rootReal, dir)) return results;
|
|
24416
26593
|
for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
|
|
24417
|
-
const fullPath =
|
|
26594
|
+
const fullPath = path32.join(dir, entry.name);
|
|
24418
26595
|
if (entry.isSymbolicLink()) continue;
|
|
24419
26596
|
if (entry.isDirectory()) {
|
|
24420
26597
|
results.push(...walkMdPaths(rootReal, fullPath));
|
|
@@ -24427,7 +26604,7 @@ function walkMdPaths(rootReal, dir) {
|
|
|
24427
26604
|
|
|
24428
26605
|
// src/sync/index.ts
|
|
24429
26606
|
import fs6 from "fs";
|
|
24430
|
-
import
|
|
26607
|
+
import path33 from "path";
|
|
24431
26608
|
import crypto6 from "crypto";
|
|
24432
26609
|
var DEFAULT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
|
|
24433
26610
|
var DEFAULT_EXCLUDE2 = /* @__PURE__ */ new Set([
|
|
@@ -24449,7 +26626,7 @@ function syncChanges(options) {
|
|
|
24449
26626
|
} = options;
|
|
24450
26627
|
const extSet = new Set(extensions);
|
|
24451
26628
|
const excludeSet = /* @__PURE__ */ new Set([...DEFAULT_EXCLUDE2, ...excludeDirs]);
|
|
24452
|
-
const stateFilePath = options.stateFile ??
|
|
26629
|
+
const stateFilePath = options.stateFile ?? path33.join(memoryDir, ".sync-state.json");
|
|
24453
26630
|
const prevState = loadState(stateFilePath);
|
|
24454
26631
|
const currentFiles = scanFiles(sourceDir, extSet, excludeSet);
|
|
24455
26632
|
const changes = computeDiff(currentFiles, prevState.fileHashes, sourceDir);
|
|
@@ -24465,7 +26642,7 @@ function syncChanges(options) {
|
|
|
24465
26642
|
for (const [relPath, hash] of Object.entries(currentFiles)) {
|
|
24466
26643
|
newState.fileHashes[relPath] = hash;
|
|
24467
26644
|
}
|
|
24468
|
-
fs6.mkdirSync(
|
|
26645
|
+
fs6.mkdirSync(path33.dirname(stateFilePath), { recursive: true });
|
|
24469
26646
|
fs6.writeFileSync(stateFilePath, JSON.stringify(newState, null, 2));
|
|
24470
26647
|
}
|
|
24471
26648
|
return {
|
|
@@ -24531,13 +26708,13 @@ function scanFiles(root, extensions, exclude) {
|
|
|
24531
26708
|
}
|
|
24532
26709
|
for (const entry of entries) {
|
|
24533
26710
|
if (exclude.has(entry.name)) continue;
|
|
24534
|
-
const fullPath =
|
|
26711
|
+
const fullPath = path33.join(dir, entry.name);
|
|
24535
26712
|
if (entry.isDirectory()) {
|
|
24536
26713
|
walk(fullPath);
|
|
24537
26714
|
} else if (entry.isFile()) {
|
|
24538
|
-
const ext =
|
|
26715
|
+
const ext = path33.extname(entry.name).toLowerCase();
|
|
24539
26716
|
if (!extensions.has(ext)) continue;
|
|
24540
|
-
const relPath =
|
|
26717
|
+
const relPath = path33.relative(root, fullPath);
|
|
24541
26718
|
try {
|
|
24542
26719
|
const content = fs6.readFileSync(fullPath, "utf8");
|
|
24543
26720
|
result[relPath] = hashContent3(content);
|
|
@@ -24552,7 +26729,7 @@ function scanFiles(root, extensions, exclude) {
|
|
|
24552
26729
|
function computeDiff(current, previous, sourceDir) {
|
|
24553
26730
|
const changes = [];
|
|
24554
26731
|
for (const [relPath, hash] of Object.entries(current)) {
|
|
24555
|
-
const fullPath =
|
|
26732
|
+
const fullPath = path33.join(sourceDir, relPath);
|
|
24556
26733
|
if (!(relPath in previous)) {
|
|
24557
26734
|
let size = 0;
|
|
24558
26735
|
try {
|
|
@@ -24585,7 +26762,7 @@ function computeDiff(current, previous, sourceDir) {
|
|
|
24585
26762
|
for (const relPath of Object.keys(previous)) {
|
|
24586
26763
|
if (!(relPath in current)) {
|
|
24587
26764
|
changes.push({
|
|
24588
|
-
filePath:
|
|
26765
|
+
filePath: path33.join(sourceDir, relPath),
|
|
24589
26766
|
relativePath: relPath,
|
|
24590
26767
|
type: "deleted",
|
|
24591
26768
|
currentHash: "",
|
|
@@ -24615,21 +26792,21 @@ function hashContent3(content) {
|
|
|
24615
26792
|
import { spawnSync } from "child_process";
|
|
24616
26793
|
import crypto7 from "crypto";
|
|
24617
26794
|
import fs7 from "fs";
|
|
24618
|
-
import
|
|
26795
|
+
import path34 from "path";
|
|
24619
26796
|
var historicalIdCache4 = new HistoricalEntityCanonicalIdCache();
|
|
24620
26797
|
var MANIFEST_VERSION = 1;
|
|
24621
26798
|
var MANIFEST_LOCK_STALE_MS = 3e4;
|
|
24622
26799
|
var MANIFEST_LOCK_TIMEOUT_MS = MANIFEST_LOCK_STALE_MS + 1e4;
|
|
24623
26800
|
var MANIFEST_LOCK_SLEEP_MS = 20;
|
|
24624
26801
|
function normalizeSpaceMemoryDir(memoryDir) {
|
|
24625
|
-
return
|
|
26802
|
+
return path34.resolve(memoryDir);
|
|
24626
26803
|
}
|
|
24627
26804
|
function getSpacesDir(baseDir) {
|
|
24628
26805
|
const homeDir = baseDir ?? resolveHomeDir();
|
|
24629
|
-
return
|
|
26806
|
+
return path34.join(homeDir, ".config", "engram", "spaces");
|
|
24630
26807
|
}
|
|
24631
26808
|
function getManifestPath(baseDir) {
|
|
24632
|
-
return
|
|
26809
|
+
return path34.join(getSpacesDir(baseDir), "manifest.json");
|
|
24633
26810
|
}
|
|
24634
26811
|
function loadManifest(baseDir, memoryDirOverride) {
|
|
24635
26812
|
if (fs7.existsSync(getManifestPath(baseDir))) {
|
|
@@ -24676,9 +26853,9 @@ function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
|
|
|
24676
26853
|
}
|
|
24677
26854
|
function saveManifestUnlocked(manifest, baseDir) {
|
|
24678
26855
|
const manifestPath2 = getManifestPath(baseDir);
|
|
24679
|
-
const manifestDir2 =
|
|
26856
|
+
const manifestDir2 = path34.dirname(manifestPath2);
|
|
24680
26857
|
fs7.mkdirSync(manifestDir2, { recursive: true });
|
|
24681
|
-
const tempPath =
|
|
26858
|
+
const tempPath = path34.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto7.randomUUID()}.tmp`);
|
|
24682
26859
|
try {
|
|
24683
26860
|
fs7.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
|
|
24684
26861
|
`, { flag: "wx" });
|
|
@@ -24693,7 +26870,7 @@ function saveManifestUnlocked(manifest, baseDir) {
|
|
|
24693
26870
|
}
|
|
24694
26871
|
function withManifestLock(baseDir, operation) {
|
|
24695
26872
|
const lockDir = `${getManifestPath(baseDir)}.lock`;
|
|
24696
|
-
fs7.mkdirSync(
|
|
26873
|
+
fs7.mkdirSync(path34.dirname(lockDir), { recursive: true });
|
|
24697
26874
|
const lockOwner = acquireManifestLock(lockDir);
|
|
24698
26875
|
try {
|
|
24699
26876
|
return operation();
|
|
@@ -24719,7 +26896,7 @@ function acquireManifestLock(lockDir) {
|
|
|
24719
26896
|
try {
|
|
24720
26897
|
fs7.mkdirSync(lockDir, { recursive: false });
|
|
24721
26898
|
try {
|
|
24722
|
-
fs7.writeFileSync(
|
|
26899
|
+
fs7.writeFileSync(path34.join(lockDir, "owner"), `${owner}
|
|
24723
26900
|
`, { flag: "wx" });
|
|
24724
26901
|
} catch (error) {
|
|
24725
26902
|
fs7.rmSync(lockDir, { recursive: true, force: true });
|
|
@@ -24749,7 +26926,7 @@ function acquireManifestLock(lockDir) {
|
|
|
24749
26926
|
}
|
|
24750
26927
|
function releaseManifestLock(lockDir, owner) {
|
|
24751
26928
|
try {
|
|
24752
|
-
const ownerPath =
|
|
26929
|
+
const ownerPath = path34.join(lockDir, "owner");
|
|
24753
26930
|
if (fs7.readFileSync(ownerPath, "utf8").trim() === owner) {
|
|
24754
26931
|
fs7.rmSync(lockDir, { recursive: true, force: true });
|
|
24755
26932
|
}
|
|
@@ -24765,7 +26942,7 @@ function removeStaleManifestLock(lockDir) {
|
|
|
24765
26942
|
try {
|
|
24766
26943
|
fs7.mkdirSync(reclaimDir, { recursive: false });
|
|
24767
26944
|
try {
|
|
24768
|
-
fs7.writeFileSync(
|
|
26945
|
+
fs7.writeFileSync(path34.join(reclaimDir, "owner"), `${reclaimOwner}
|
|
24769
26946
|
`, { flag: "wx" });
|
|
24770
26947
|
} catch (error) {
|
|
24771
26948
|
fs7.rmSync(reclaimDir, { recursive: true, force: true });
|
|
@@ -24830,9 +27007,9 @@ function removeStaleManifestReclaimLock(reclaimDir) {
|
|
|
24830
27007
|
fs7.rmSync(tombstoneDir, { recursive: true, force: true });
|
|
24831
27008
|
}
|
|
24832
27009
|
function readManifestLockSnapshot(lockDir) {
|
|
24833
|
-
let
|
|
27010
|
+
let stat6;
|
|
24834
27011
|
try {
|
|
24835
|
-
|
|
27012
|
+
stat6 = fs7.statSync(lockDir);
|
|
24836
27013
|
} catch (error) {
|
|
24837
27014
|
if (error.code === "ENOENT") {
|
|
24838
27015
|
return void 0;
|
|
@@ -24840,11 +27017,11 @@ function readManifestLockSnapshot(lockDir) {
|
|
|
24840
27017
|
throw error;
|
|
24841
27018
|
}
|
|
24842
27019
|
try {
|
|
24843
|
-
const owner = fs7.readFileSync(
|
|
24844
|
-
return { mtimeMs:
|
|
27020
|
+
const owner = fs7.readFileSync(path34.join(lockDir, "owner"), "utf8").trim();
|
|
27021
|
+
return { mtimeMs: stat6.mtimeMs, owner };
|
|
24845
27022
|
} catch (error) {
|
|
24846
27023
|
if (error.code === "ENOENT") {
|
|
24847
|
-
return { mtimeMs:
|
|
27024
|
+
return { mtimeMs: stat6.mtimeMs };
|
|
24848
27025
|
}
|
|
24849
27026
|
throw error;
|
|
24850
27027
|
}
|
|
@@ -24912,8 +27089,8 @@ function sleepSync(ms) {
|
|
|
24912
27089
|
}
|
|
24913
27090
|
function createPersonalSpace(baseDir, memoryDirOverride) {
|
|
24914
27091
|
const homeDir = baseDir ?? resolveHomeDir();
|
|
24915
|
-
const standalonePath =
|
|
24916
|
-
const openclawPath =
|
|
27092
|
+
const standalonePath = path34.join(homeDir, ".engram", "memory");
|
|
27093
|
+
const openclawPath = path34.join(homeDir, ".openclaw", "workspace", "memory", "local");
|
|
24917
27094
|
const memoryDir = memoryDirOverride ?? readEnvVar("REMNIC_MEMORY_DIR") ?? readEnvVar("ENGRAM_MEMORY_DIR") ?? (fs7.existsSync(standalonePath) ? standalonePath : fs7.existsSync(openclawPath) ? openclawPath : standalonePath);
|
|
24918
27095
|
const normalizedMemoryDir = normalizeSpaceMemoryDir(memoryDir);
|
|
24919
27096
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24942,7 +27119,7 @@ function createSpace(options) {
|
|
|
24942
27119
|
const id = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
|
|
24943
27120
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24944
27121
|
const memoryDir = normalizeSpaceMemoryDir(
|
|
24945
|
-
options.memoryDir ??
|
|
27122
|
+
options.memoryDir ?? path34.join(getSpacesDir(options.baseDir), id, "memory")
|
|
24946
27123
|
);
|
|
24947
27124
|
const space = updateManifest(options.baseDir, (manifest) => {
|
|
24948
27125
|
if (manifest.spaces.some((s) => s.id === id)) {
|
|
@@ -25163,14 +27340,14 @@ async function mergeSpaces(sourceSpaceId, targetSpaceId, options) {
|
|
|
25163
27340
|
};
|
|
25164
27341
|
}
|
|
25165
27342
|
function getAuditLog(baseDir) {
|
|
25166
|
-
const auditPath =
|
|
27343
|
+
const auditPath = path34.join(getSpacesDir(baseDir), "audit.jsonl");
|
|
25167
27344
|
if (!fs7.existsSync(auditPath)) return [];
|
|
25168
27345
|
const lines = fs7.readFileSync(auditPath, "utf8").trim().split("\n");
|
|
25169
27346
|
return lines.filter((l) => l.trim()).map((l) => JSON.parse(l));
|
|
25170
27347
|
}
|
|
25171
27348
|
function appendAudit(entry, baseDir) {
|
|
25172
|
-
const auditPath =
|
|
25173
|
-
fs7.mkdirSync(
|
|
27349
|
+
const auditPath = path34.join(getSpacesDir(baseDir), "audit.jsonl");
|
|
27350
|
+
fs7.mkdirSync(path34.dirname(auditPath), { recursive: true });
|
|
25174
27351
|
const full = {
|
|
25175
27352
|
id: crypto7.randomUUID(),
|
|
25176
27353
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -25191,11 +27368,11 @@ async function copyMemories(sourceDir, targetDir, options) {
|
|
|
25191
27368
|
sourceFiles.filter((p) => {
|
|
25192
27369
|
const rp = safeRealpath(p);
|
|
25193
27370
|
if (!rp || !isPathInsideRoot(rp, sourceRoot)) return false;
|
|
25194
|
-
const rel =
|
|
25195
|
-
const mapped =
|
|
27371
|
+
const rel = path34.relative(sourceRoot, rp);
|
|
27372
|
+
const mapped = path34.resolve(targetRoot, rel);
|
|
25196
27373
|
if (!isPathInsideRoot(mapped, targetRoot)) return false;
|
|
25197
27374
|
return classifyEntityRefWritePath(targetRoot, mapped) === "entity";
|
|
25198
|
-
}).map((p) =>
|
|
27375
|
+
}).map((p) => path34.basename(p, ".md"))
|
|
25199
27376
|
);
|
|
25200
27377
|
const firstEntityId = sourceEntityIds.values().next().value;
|
|
25201
27378
|
if (firstEntityId === void 0) {
|
|
@@ -25203,7 +27380,7 @@ async function copyMemories(sourceDir, targetDir, options) {
|
|
|
25203
27380
|
}
|
|
25204
27381
|
return withRawEntityPageMutation(
|
|
25205
27382
|
targetRoot,
|
|
25206
|
-
|
|
27383
|
+
path34.join(targetRoot, "entities", `${firstEntityId}.md`),
|
|
25207
27384
|
async () => copyPreparedMemories(
|
|
25208
27385
|
sourceFiles,
|
|
25209
27386
|
sourceRoot,
|
|
@@ -25230,14 +27407,14 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
25230
27407
|
continue;
|
|
25231
27408
|
}
|
|
25232
27409
|
const content = fs7.readFileSync(sourcePath, "utf8");
|
|
25233
|
-
const relativePath =
|
|
25234
|
-
const targetPath =
|
|
27410
|
+
const relativePath = path34.relative(sourceRoot, sourceRealPath);
|
|
27411
|
+
const targetPath = path34.resolve(targetRoot, relativePath);
|
|
25235
27412
|
if (!isPathInsideRoot(targetPath, targetRoot)) {
|
|
25236
27413
|
skipped++;
|
|
25237
27414
|
continue;
|
|
25238
27415
|
}
|
|
25239
27416
|
const targetKind = classifyEntityRefWritePath(targetRoot, targetPath);
|
|
25240
|
-
const idsAtCompare = historicalIdCache4.get(
|
|
27417
|
+
const idsAtCompare = historicalIdCache4.get(path34.join(targetRoot, "state"));
|
|
25241
27418
|
const sourceRef = targetKind === "memory" ? readEntityRefFromFrontmatter(content) : null;
|
|
25242
27419
|
const refHasSourceClaimant = sourceRef !== null && sourceEntityIds.has(sourceRef);
|
|
25243
27420
|
const canonicalContent = targetKind === "memory" && !refHasSourceClaimant ? canonicalizeEntityRefFrontmatter(content, idsAtCompare) : content;
|
|
@@ -25264,7 +27441,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
25264
27441
|
if (fs7.existsSync(targetPath) && !options?.force) {
|
|
25265
27442
|
const targetContent = fs7.readFileSync(targetPath, "utf8");
|
|
25266
27443
|
const targetHash = hashContent4(targetContent);
|
|
25267
|
-
if (hashContent4(canonicalContent) === targetHash && historicalIdCache4.get(
|
|
27444
|
+
if (hashContent4(canonicalContent) === targetHash && historicalIdCache4.get(path34.join(targetRoot, "state")) === idsAtCompare) {
|
|
25268
27445
|
skipped++;
|
|
25269
27446
|
continue;
|
|
25270
27447
|
}
|
|
@@ -25280,13 +27457,13 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
25280
27457
|
continue;
|
|
25281
27458
|
}
|
|
25282
27459
|
}
|
|
25283
|
-
fs7.mkdirSync(
|
|
25284
|
-
const targetParentRealPath = safeRealpath(
|
|
27460
|
+
fs7.mkdirSync(path34.dirname(targetPath), { recursive: true });
|
|
27461
|
+
const targetParentRealPath = safeRealpath(path34.dirname(targetPath));
|
|
25285
27462
|
if (!targetParentRealPath || !isPathInsideRoot(targetParentRealPath, targetRoot)) {
|
|
25286
27463
|
skipped++;
|
|
25287
27464
|
continue;
|
|
25288
27465
|
}
|
|
25289
|
-
const stateDir =
|
|
27466
|
+
const stateDir = path34.join(targetRoot, "state");
|
|
25290
27467
|
if (targetKind === "memory" && !refHasSourceClaimant) {
|
|
25291
27468
|
const idsAtWrite = historicalIdCache4.get(stateDir);
|
|
25292
27469
|
const written = canonicalizeEntityRefFrontmatter(content, idsAtWrite);
|
|
@@ -25330,7 +27507,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
|
|
|
25330
27507
|
bumpMemoryCorpusVersionForDir(targetRoot);
|
|
25331
27508
|
}
|
|
25332
27509
|
if (reconcileNeeded) {
|
|
25333
|
-
requestEntityCanonicalIdReconcileSync(
|
|
27510
|
+
requestEntityCanonicalIdReconcileSync(path34.join(targetRoot, "state"));
|
|
25334
27511
|
}
|
|
25335
27512
|
return { merged, conflicts, skipped };
|
|
25336
27513
|
}
|
|
@@ -25341,7 +27518,7 @@ function walkMd2(dir) {
|
|
|
25341
27518
|
const results = [];
|
|
25342
27519
|
function walk(d) {
|
|
25343
27520
|
for (const entry of fs7.readdirSync(d, { withFileTypes: true })) {
|
|
25344
|
-
const fullPath =
|
|
27521
|
+
const fullPath = path34.join(d, entry.name);
|
|
25345
27522
|
if (entry.isSymbolicLink()) {
|
|
25346
27523
|
continue;
|
|
25347
27524
|
}
|
|
@@ -25370,8 +27547,8 @@ function safeRealpath(filePath) {
|
|
|
25370
27547
|
}
|
|
25371
27548
|
}
|
|
25372
27549
|
function isPathInsideRoot(candidatePath, rootPath) {
|
|
25373
|
-
const relative =
|
|
25374
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
27550
|
+
const relative = path34.relative(rootPath, candidatePath);
|
|
27551
|
+
return relative === "" || !relative.startsWith("..") && !path34.isAbsolute(relative);
|
|
25375
27552
|
}
|
|
25376
27553
|
function parseSimpleFrontmatter(content) {
|
|
25377
27554
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
@@ -25492,7 +27669,7 @@ var REMNIC_RECALL_DECISION_RULES = `## When to Use Recall vs Direct Read
|
|
|
25492
27669
|
// src/memory-extension/codex-publisher.ts
|
|
25493
27670
|
import fs8 from "fs";
|
|
25494
27671
|
import os from "os";
|
|
25495
|
-
import
|
|
27672
|
+
import path35 from "path";
|
|
25496
27673
|
var REMNIC_EXTENSION_DIR_NAME = "remnic";
|
|
25497
27674
|
function resolveEnvHome(env) {
|
|
25498
27675
|
if (env === void 0) return resolveHomeDir();
|
|
@@ -25501,12 +27678,12 @@ function resolveEnvHome(env) {
|
|
|
25501
27678
|
function expandTildeWithHome(input, homeDir) {
|
|
25502
27679
|
if (input === "~") return homeDir;
|
|
25503
27680
|
if (input.startsWith("~/") || input.startsWith("~\\")) {
|
|
25504
|
-
return
|
|
27681
|
+
return path35.join(homeDir, input.slice(2));
|
|
25505
27682
|
}
|
|
25506
27683
|
return input;
|
|
25507
27684
|
}
|
|
25508
27685
|
function normalizeHostRoot(input, homeDir) {
|
|
25509
|
-
return
|
|
27686
|
+
return path35.resolve(expandTildeWithHome(input.trim(), homeDir));
|
|
25510
27687
|
}
|
|
25511
27688
|
var CodexMemoryExtensionPublisher = class {
|
|
25512
27689
|
hostId = "codex";
|
|
@@ -25520,12 +27697,12 @@ var CodexMemoryExtensionPublisher = class {
|
|
|
25520
27697
|
async resolveExtensionRoot(env) {
|
|
25521
27698
|
const homeDir = resolveEnvHome(env);
|
|
25522
27699
|
const codexHomeInput = env === void 0 ? readEnvVar("CODEX_HOME")?.trim() : env.CODEX_HOME?.trim();
|
|
25523
|
-
const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) :
|
|
25524
|
-
return
|
|
27700
|
+
const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) : path35.resolve(homeDir, ".codex");
|
|
27701
|
+
return path35.join(codexHome, "memories_extensions", REMNIC_EXTENSION_DIR_NAME);
|
|
25525
27702
|
}
|
|
25526
27703
|
async isHostAvailable() {
|
|
25527
27704
|
try {
|
|
25528
|
-
const home = readEnvVar("CODEX_HOME")?.trim() ||
|
|
27705
|
+
const home = readEnvVar("CODEX_HOME")?.trim() || path35.join(resolveHomeDir(), ".codex");
|
|
25529
27706
|
return fs8.existsSync(home);
|
|
25530
27707
|
} catch {
|
|
25531
27708
|
return false;
|
|
@@ -25576,7 +27753,7 @@ When running inside the Codex phase-2 consolidation sandbox:
|
|
|
25576
27753
|
}
|
|
25577
27754
|
async publish(ctx) {
|
|
25578
27755
|
const extensionRoot = await this.resolveExtensionRoot();
|
|
25579
|
-
const instructionsPath =
|
|
27756
|
+
const instructionsPath = path35.join(extensionRoot, "instructions.md");
|
|
25580
27757
|
const filesWritten = [];
|
|
25581
27758
|
const skipped = [];
|
|
25582
27759
|
ctx.log.info(`Publishing Codex memory extension to ${extensionRoot}`);
|
|
@@ -25950,9 +28127,9 @@ async function generateActivityMemories(date, digestBody, config, deps) {
|
|
|
25950
28127
|
}
|
|
25951
28128
|
|
|
25952
28129
|
// src/session-summaries/index.ts
|
|
25953
|
-
import { createHash as
|
|
25954
|
-
import { lstat as
|
|
25955
|
-
import
|
|
28130
|
+
import { createHash as createHash11 } from "crypto";
|
|
28131
|
+
import { lstat as lstat5, mkdir as mkdir8, readFile as readFile7, readdir as readdir5, rename as rename2, rm as rm2, stat as stat5, writeFile as writeFile7 } from "fs/promises";
|
|
28132
|
+
import path36 from "path";
|
|
25956
28133
|
|
|
25957
28134
|
// src/session-summaries/adapters.ts
|
|
25958
28135
|
var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool", "system", "other"]);
|
|
@@ -26452,28 +28629,28 @@ var DEFAULT_MAX_FILES = 5e3;
|
|
|
26452
28629
|
var DEFAULT_MAX_SESSIONS = 500;
|
|
26453
28630
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".jsonl"]);
|
|
26454
28631
|
function shortHash(value, length = 16) {
|
|
26455
|
-
return
|
|
28632
|
+
return createHash11("sha256").update(value).digest("hex").slice(0, length);
|
|
26456
28633
|
}
|
|
26457
28634
|
async function listTranscriptFiles(root, maxFiles) {
|
|
26458
28635
|
const out = [];
|
|
26459
28636
|
let truncated = false;
|
|
26460
|
-
const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${
|
|
28637
|
+
const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${path36.sep}` : entryName;
|
|
26461
28638
|
async function visit(dir) {
|
|
26462
28639
|
if (truncated) return;
|
|
26463
|
-
const entries = (await
|
|
28640
|
+
const entries = (await readdir5(dir, { withFileTypes: true })).sort(
|
|
26464
28641
|
(a, b) => entrySortKey(a.name, a.isDirectory()).localeCompare(entrySortKey(b.name, b.isDirectory()))
|
|
26465
28642
|
);
|
|
26466
28643
|
for (const entry of entries) {
|
|
26467
28644
|
if (truncated) return;
|
|
26468
28645
|
if (entry.name.startsWith(".")) continue;
|
|
26469
28646
|
if (entry.isSymbolicLink()) continue;
|
|
26470
|
-
const fullPath =
|
|
28647
|
+
const fullPath = path36.join(dir, entry.name);
|
|
26471
28648
|
if (entry.isDirectory()) {
|
|
26472
28649
|
await visit(fullPath);
|
|
26473
28650
|
continue;
|
|
26474
28651
|
}
|
|
26475
28652
|
if (!entry.isFile()) continue;
|
|
26476
|
-
const ext =
|
|
28653
|
+
const ext = path36.extname(entry.name).toLowerCase();
|
|
26477
28654
|
if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
|
|
26478
28655
|
out.push(fullPath);
|
|
26479
28656
|
if (out.length > maxFiles) {
|
|
@@ -26500,7 +28677,7 @@ function normalizeInputDir(inputDir) {
|
|
|
26500
28677
|
if (typeof inputDir !== "string" || inputDir.trim().length === 0) {
|
|
26501
28678
|
throw new Error("inputDir must be a non-empty string");
|
|
26502
28679
|
}
|
|
26503
|
-
return
|
|
28680
|
+
return path36.resolve(expandTildePath(inputDir.trim()));
|
|
26504
28681
|
}
|
|
26505
28682
|
function initRoleCounts() {
|
|
26506
28683
|
return {
|
|
@@ -26595,11 +28772,11 @@ function buildDraft(options) {
|
|
|
26595
28772
|
}
|
|
26596
28773
|
async function collectLocalSessionSummaries(options) {
|
|
26597
28774
|
const inputDir = normalizeInputDir(options.inputDir);
|
|
26598
|
-
const inputLstat = await
|
|
28775
|
+
const inputLstat = await lstat5(inputDir);
|
|
26599
28776
|
if (inputLstat.isSymbolicLink()) {
|
|
26600
28777
|
throw new Error(`inputDir must not be a symbolic link: ${options.inputDir}`);
|
|
26601
28778
|
}
|
|
26602
|
-
const inputStat = await
|
|
28779
|
+
const inputStat = await stat5(inputDir);
|
|
26603
28780
|
if (!inputStat.isDirectory()) {
|
|
26604
28781
|
throw new Error(`inputDir must be a directory: ${options.inputDir}`);
|
|
26605
28782
|
}
|
|
@@ -26657,11 +28834,11 @@ async function collectLocalSessionSummaries(options) {
|
|
|
26657
28834
|
continue;
|
|
26658
28835
|
}
|
|
26659
28836
|
seenFileHashes.add(fileHash);
|
|
26660
|
-
const fileExtension =
|
|
28837
|
+
const fileExtension = path36.extname(filePath).toLowerCase();
|
|
26661
28838
|
const parsed = await adapter.parseFile(
|
|
26662
28839
|
{
|
|
26663
28840
|
content,
|
|
26664
|
-
fileName:
|
|
28841
|
+
fileName: path36.basename(filePath),
|
|
26665
28842
|
fileExtension,
|
|
26666
28843
|
fileRef: fileHash
|
|
26667
28844
|
},
|
|
@@ -26717,7 +28894,7 @@ async function collectLocalSessionSummaries(options) {
|
|
|
26717
28894
|
}
|
|
26718
28895
|
async function readRedactionConfig(pathLike) {
|
|
26719
28896
|
if (!pathLike) return void 0;
|
|
26720
|
-
const raw = await readFile7(
|
|
28897
|
+
const raw = await readFile7(path36.resolve(expandTildePath(pathLike)), "utf-8");
|
|
26721
28898
|
const parsed = JSON.parse(raw);
|
|
26722
28899
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
26723
28900
|
throw new Error("redaction config must be a JSON object");
|
|
@@ -26730,21 +28907,21 @@ function toJsonl(drafts) {
|
|
|
26730
28907
|
}
|
|
26731
28908
|
function defaultDraftOutputPath(memoryDir, generatedAt) {
|
|
26732
28909
|
const stamp = generatedAt.replace(/[:.]/g, "-");
|
|
26733
|
-
return
|
|
26734
|
-
|
|
28910
|
+
return path36.join(
|
|
28911
|
+
path36.resolve(expandTildePath(memoryDir)),
|
|
26735
28912
|
"state",
|
|
26736
28913
|
"session-summary-drafts",
|
|
26737
28914
|
`session-summaries-${stamp}.jsonl`
|
|
26738
28915
|
);
|
|
26739
28916
|
}
|
|
26740
28917
|
async function writeDrafts(filePath, drafts) {
|
|
26741
|
-
await
|
|
28918
|
+
await mkdir8(path36.dirname(filePath), { recursive: true });
|
|
26742
28919
|
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
26743
28920
|
try {
|
|
26744
28921
|
await writeFile7(tempPath, toJsonl(drafts), "utf-8");
|
|
26745
|
-
await
|
|
28922
|
+
await rename2(tempPath, filePath);
|
|
26746
28923
|
} catch (error) {
|
|
26747
|
-
await
|
|
28924
|
+
await rm2(tempPath, { force: true });
|
|
26748
28925
|
throw error;
|
|
26749
28926
|
}
|
|
26750
28927
|
}
|
|
@@ -26756,7 +28933,7 @@ async function runLocalSessionSummaryCliCommand(options) {
|
|
|
26756
28933
|
});
|
|
26757
28934
|
const wroteFiles = [];
|
|
26758
28935
|
if (options.output) {
|
|
26759
|
-
const outputPath =
|
|
28936
|
+
const outputPath = path36.resolve(expandTildePath(options.output));
|
|
26760
28937
|
await writeDrafts(outputPath, report.drafts);
|
|
26761
28938
|
wroteFiles.push(outputPath);
|
|
26762
28939
|
}
|
|
@@ -26790,20 +28967,20 @@ async function runLocalSessionSummaryCliCommand(options) {
|
|
|
26790
28967
|
}
|
|
26791
28968
|
|
|
26792
28969
|
// src/transfer/capsule-fork.ts
|
|
26793
|
-
import { lstat as
|
|
26794
|
-
import
|
|
28970
|
+
import { lstat as lstat6, mkdir as mkdir9, readFile as readFile8, realpath as realpath3, writeFile as writeFile8 } from "fs/promises";
|
|
28971
|
+
import path37 from "path";
|
|
26795
28972
|
async function forkCapsule(opts) {
|
|
26796
28973
|
validateForkId(opts.forkId);
|
|
26797
|
-
const rootAbs =
|
|
28974
|
+
const rootAbs = path37.resolve(opts.targetRoot);
|
|
26798
28975
|
await assertIsDirectoryNotSymlink(rootAbs, "forkCapsule", "targetRoot");
|
|
26799
|
-
const forkDirAbs =
|
|
28976
|
+
const forkDirAbs = path37.join(rootAbs, "forks", opts.forkId);
|
|
26800
28977
|
const forkEntryExists = await pathEntryExists(forkDirAbs);
|
|
26801
28978
|
if (forkEntryExists) {
|
|
26802
28979
|
throw new Error(
|
|
26803
28980
|
`forkCapsule: fork path already exists \u2014 forkId "${opts.forkId}" is already in use at: ${forkDirAbs}`
|
|
26804
28981
|
);
|
|
26805
28982
|
}
|
|
26806
|
-
const archiveAbs =
|
|
28983
|
+
const archiveAbs = path37.resolve(opts.sourceArchive);
|
|
26807
28984
|
const importResult = await importCapsule({
|
|
26808
28985
|
archivePath: archiveAbs,
|
|
26809
28986
|
root: rootAbs,
|
|
@@ -26825,10 +29002,10 @@ async function forkCapsule(opts) {
|
|
|
26825
29002
|
importedRecords: importResult.imported.length,
|
|
26826
29003
|
skippedRecords: importResult.skipped.length
|
|
26827
29004
|
};
|
|
26828
|
-
const lineagePath =
|
|
29005
|
+
const lineagePath = path37.join(forkDirAbs, "lineage.json");
|
|
26829
29006
|
const rootReal = await realpath3(rootAbs);
|
|
26830
29007
|
await assertRealpathInsideRoot(rootReal, lineagePath, `forks/${opts.forkId}/lineage.json`, "forkCapsule");
|
|
26831
|
-
await
|
|
29008
|
+
await mkdir9(path37.dirname(lineagePath), { recursive: true });
|
|
26832
29009
|
await writeFile8(lineagePath, JSON.stringify(lineage, null, 2) + "\n", "utf-8");
|
|
26833
29010
|
return {
|
|
26834
29011
|
archivePath: archiveAbs,
|
|
@@ -26842,11 +29019,11 @@ async function readForkLineage(targetRoot, forkId) {
|
|
|
26842
29019
|
if (typeof forkId !== "string" || forkId.length === 0 || forkId.length > 64 || !CAPSULE_ID_PATTERN.test(forkId)) {
|
|
26843
29020
|
return null;
|
|
26844
29021
|
}
|
|
26845
|
-
const rootAbs =
|
|
29022
|
+
const rootAbs = path37.resolve(targetRoot);
|
|
26846
29023
|
const rootReal = await realpath3(rootAbs).catch(() => rootAbs);
|
|
26847
|
-
const lineagePath =
|
|
26848
|
-
const rel =
|
|
26849
|
-
if (rel.startsWith("..") ||
|
|
29024
|
+
const lineagePath = path37.join(rootReal, "forks", forkId, "lineage.json");
|
|
29025
|
+
const rel = path37.relative(rootReal, lineagePath);
|
|
29026
|
+
if (rel.startsWith("..") || path37.isAbsolute(rel)) {
|
|
26850
29027
|
return null;
|
|
26851
29028
|
}
|
|
26852
29029
|
if (!await isLineagePathContained(rootReal, lineagePath)) {
|
|
@@ -26881,30 +29058,45 @@ function validateForkId(forkId) {
|
|
|
26881
29058
|
}
|
|
26882
29059
|
}
|
|
26883
29060
|
async function pathEntryExists(absPath) {
|
|
26884
|
-
const st = await
|
|
29061
|
+
const st = await lstat6(absPath).catch(() => null);
|
|
26885
29062
|
return st !== null;
|
|
26886
29063
|
}
|
|
26887
29064
|
async function isLineagePathContained(rootReal, lineagePath) {
|
|
26888
29065
|
let existing = lineagePath;
|
|
26889
29066
|
const suffix = [];
|
|
26890
|
-
while (existing !==
|
|
26891
|
-
const st = await
|
|
29067
|
+
while (existing !== path37.dirname(existing)) {
|
|
29068
|
+
const st = await lstat6(existing).catch(() => null);
|
|
26892
29069
|
if (st !== null) break;
|
|
26893
|
-
suffix.unshift(
|
|
26894
|
-
existing =
|
|
29070
|
+
suffix.unshift(path37.basename(existing));
|
|
29071
|
+
existing = path37.dirname(existing);
|
|
26895
29072
|
}
|
|
26896
29073
|
const existingReal = await realpath3(existing).catch(() => existing);
|
|
26897
|
-
const targetReal = suffix.length > 0 ?
|
|
26898
|
-
const rel =
|
|
29074
|
+
const targetReal = suffix.length > 0 ? path37.join(existingReal, ...suffix) : existingReal;
|
|
29075
|
+
const rel = path37.relative(rootReal, targetReal);
|
|
26899
29076
|
if (rel === "") return true;
|
|
26900
29077
|
if (rel === "..") return false;
|
|
26901
|
-
if (rel.startsWith(`..${
|
|
26902
|
-
if (
|
|
29078
|
+
if (rel.startsWith(`..${path37.sep}`)) return false;
|
|
29079
|
+
if (path37.isAbsolute(rel)) return false;
|
|
26903
29080
|
return true;
|
|
26904
29081
|
}
|
|
26905
29082
|
|
|
26906
29083
|
export {
|
|
26907
29084
|
buildProcedureRecallSection,
|
|
29085
|
+
SupportPassportError,
|
|
29086
|
+
SupportPassportCardService,
|
|
29087
|
+
SupportPassportGrantCardRefSchema,
|
|
29088
|
+
SupportPassportGrantStateSchema,
|
|
29089
|
+
SupportPassportCreateGrantInputSchema,
|
|
29090
|
+
SupportPassportOwnerGrantSchema,
|
|
29091
|
+
SupportPassportCreatedGrantSchema,
|
|
29092
|
+
SupportPassportPublicCardSchema,
|
|
29093
|
+
SupportPassportPublicGuideSchema,
|
|
29094
|
+
SupportPassportRevokeGrantInputSchema,
|
|
29095
|
+
SupportPassportListGrantsInputSchema,
|
|
29096
|
+
notifySupportPassportCommitted,
|
|
29097
|
+
syncDirectoryForDurability,
|
|
29098
|
+
SupportPassportGrantStore,
|
|
29099
|
+
SupportPassportGrantService,
|
|
26908
29100
|
RELAY_DEMO_MISSION_ID,
|
|
26909
29101
|
RELAY_DEMO_NAMESPACE,
|
|
26910
29102
|
createRelayMissionFixture,
|
|
@@ -27018,4 +29210,4 @@ export {
|
|
|
27018
29210
|
blendGraphExpandedRecallScore,
|
|
27019
29211
|
Orchestrator
|
|
27020
29212
|
};
|
|
27021
|
-
//# sourceMappingURL=chunk-
|
|
29213
|
+
//# sourceMappingURL=chunk-7HG7MGAP.js.map
|