@remnic/core 9.54.5 → 9.54.7
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 +24 -24
- package/dist/access-admin-ops-surface.js +32 -29
- package/dist/access-authorization-probe.d.ts +25 -23
- package/dist/access-authorization-probe.js +35 -30
- package/dist/access-boundary.d.ts +26 -24
- package/dist/access-boundary.js +36 -31
- package/dist/access-cli.js +86 -80
- 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 +19 -19
- package/dist/access-extraction-force-flush.js +32 -29
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +22 -22
- package/dist/access-http-lifecycle-flush.d.ts +22 -22
- package/dist/access-http-offline-stream.d.ts +18 -18
- package/dist/access-http-query.js +32 -29
- package/dist/access-http.d.ts +55 -37
- package/dist/access-http.js +52 -43
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +32 -29
- package/dist/access-lcm-surface.d.ts +21 -21
- package/dist/access-lcm-surface.js +32 -29
- package/dist/access-mcp.d.ts +22 -22
- package/dist/access-mcp.js +40 -36
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-BdHlGd_T.d.ts → access-namespace-preflight-CCNn_6NE.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +32 -29
- package/dist/access-observe-write-surface.d.ts +22 -22
- package/dist/access-observe-write-surface.js +32 -29
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-offline-manifest.js +2 -2
- package/dist/access-operations-batch.js +34 -31
- package/dist/access-operations.d.ts +23 -23
- package/dist/access-operations.js +38 -34
- package/dist/access-recall-concurrency.d.ts +21 -21
- package/dist/access-recall-concurrency.js +32 -29
- package/dist/access-recall-response.d.ts +19 -19
- package/dist/access-recall-response.js +32 -29
- package/dist/access-recall-surface.d.ts +19 -19
- package/dist/access-recall-surface.js +32 -29
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DL7X9hrg.d.ts → access-service-DK82xdNi.d.ts} +1314 -83
- package/dist/access-service-helpers.d.ts +22 -22
- package/dist/access-service.d.ts +31 -31
- package/dist/access-service.js +32 -29
- package/dist/access-surface-catalog.d.ts +22 -22
- package/dist/access-surface-catalog.js +209 -2
- package/dist/access-surface-catalog.js.map +1 -1
- 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 +6 -5
- package/dist/active-memory-bridge.js +2 -2
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +2 -1
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/admin-console/public/what-helps-me/app.js +1594 -0
- package/dist/admin-console/public/what-helps-me/index.html +339 -0
- package/dist/admin-console/public/what-helps-me/model.js +685 -0
- package/dist/admin-console/public/what-helps-me/what-helps-me.css +1296 -0
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/{auto-sync-TDHLZPVP.js → auto-sync-NR3GPCOA.js} +4 -4
- 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 +14 -14
- 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 +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-B90is4d8.d.ts → catalog-DGFf8mD9.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 +15 -15
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-CSY44OKU.js → chunk-26ZXBTYN.js} +4 -4
- package/dist/chunk-26ZXBTYN.js.map +1 -0
- package/dist/{chunk-LLJMAULE.js → chunk-2FKEIPIT.js} +5 -5
- package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
- package/dist/chunk-37DZZFFM.js +502 -0
- package/dist/chunk-37DZZFFM.js.map +1 -0
- package/dist/chunk-3HSRWJTC.js +9 -0
- package/dist/chunk-3HSRWJTC.js.map +1 -0
- package/dist/{chunk-TKFGFDPJ.js → chunk-3TIZPSJC.js} +4 -4
- package/dist/{chunk-B4T3FQMT.js → chunk-43SEBY2M.js} +1 -1
- package/dist/{chunk-LVKYLMSM.js → chunk-46SZBVDS.js} +2 -2
- package/dist/{chunk-YKZLOPTC.js → chunk-5FD7JMTX.js} +2 -2
- package/dist/{chunk-2PNXOQ4P.js → chunk-5NB726GV.js} +3 -7
- package/dist/chunk-5NB726GV.js.map +1 -0
- package/dist/chunk-62OKGIJA.js +39 -0
- package/dist/chunk-62OKGIJA.js.map +1 -0
- package/dist/{chunk-WM5LD6AT.js → chunk-6CYHUWCO.js} +2 -2
- package/dist/{chunk-BIF4SHMD.js → chunk-76JJSXK7.js} +1 -1
- package/dist/chunk-76JJSXK7.js.map +1 -0
- package/dist/{chunk-3SXLD5T3.js → chunk-7BD6O7ZV.js} +189 -4
- package/dist/chunk-7BD6O7ZV.js.map +1 -0
- package/dist/chunk-7TBXNYLO.js +346 -0
- package/dist/chunk-7TBXNYLO.js.map +1 -0
- package/dist/{chunk-YR65W7BV.js → chunk-BRR5EUTF.js} +3 -3
- package/dist/{chunk-JKW663ZN.js → chunk-CIAI24TQ.js} +2 -2
- package/dist/{chunk-3WGI7F4V.js → chunk-D3LBIUWV.js} +9219 -5233
- package/dist/chunk-D3LBIUWV.js.map +1 -0
- package/dist/{chunk-YDWPYDML.js → chunk-EKTP7MNS.js} +4 -4
- package/dist/{chunk-6LEIWWAF.js → chunk-F2JRDPON.js} +2 -2
- package/dist/{chunk-VYQ4URQB.js → chunk-FFRTNF3L.js} +2 -2
- package/dist/{chunk-TY6MKIWP.js → chunk-FONWBCVY.js} +3 -3
- package/dist/{chunk-XSV2YDCJ.js → chunk-FPLM2TKD.js} +2 -2
- package/dist/{chunk-VTU5IEKY.js → chunk-FTPONF6B.js} +2 -2
- package/dist/{chunk-6PZGKGAO.js → chunk-FWX7W5PM.js} +2 -2
- package/dist/{chunk-QPW3PAIH.js → chunk-G6DFSS6A.js} +2 -2
- package/dist/{chunk-2DD6ZHOM.js → chunk-GJ776BC2.js} +3 -3
- package/dist/{chunk-XKAR4RF6.js → chunk-GN3WRFJY.js} +6 -6
- package/dist/{chunk-RG426DJO.js → chunk-H7NAZ3UB.js} +2 -2
- package/dist/{chunk-BSXGTDVJ.js → chunk-HBUFQRIM.js} +2 -2
- package/dist/{chunk-FFEBSFVT.js → chunk-HGJ6NJ4E.js} +2 -2
- package/dist/{chunk-WB7JSQCP.js → chunk-HIEWDJ2O.js} +5 -2
- package/dist/{chunk-WB7JSQCP.js.map → chunk-HIEWDJ2O.js.map} +1 -1
- package/dist/{chunk-QTO6BMQZ.js → chunk-I3MSGFGD.js} +17 -6
- package/dist/chunk-I3MSGFGD.js.map +1 -0
- package/dist/{chunk-5JKUSBMX.js → chunk-IKP5OYQQ.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
- package/dist/{chunk-DBY26JJJ.js → chunk-IQ4NVWEF.js} +2 -2
- package/dist/{chunk-7Q7JWLQ4.js → chunk-JYG4OLWH.js} +2 -2
- package/dist/{chunk-3OTSVCPK.js → chunk-K26NWEOC.js} +4 -4
- package/dist/{chunk-WZWZRZ2V.js → chunk-K2UGPTYY.js} +484 -43
- package/dist/chunk-K2UGPTYY.js.map +1 -0
- package/dist/{chunk-L6CQ7WIW.js → chunk-KFBOPMF6.js} +11 -11
- package/dist/chunk-KFBOPMF6.js.map +1 -0
- package/dist/{chunk-HOIRB77A.js → chunk-L6Z5OAS4.js} +2 -2
- package/dist/{chunk-7RZJWZ46.js → chunk-LEPOP46W.js} +5 -5
- package/dist/{chunk-QQXWFDRD.js → chunk-M4YHAODQ.js} +270 -684
- package/dist/chunk-M4YHAODQ.js.map +1 -0
- package/dist/{chunk-NGT35IGQ.js → chunk-MIH3CJPN.js} +3 -3
- package/dist/{chunk-QPAQSG3F.js → chunk-MK5VEWCR.js} +2 -2
- package/dist/chunk-NISZQW72.js +140 -0
- package/dist/chunk-NISZQW72.js.map +1 -0
- package/dist/{chunk-L7RTUEDG.js → chunk-OCIP6YMD.js} +2 -2
- package/dist/{chunk-2INWPC6E.js → chunk-PG4QBPVG.js} +20 -10
- package/dist/chunk-PG4QBPVG.js.map +1 -0
- package/dist/{chunk-DDA55IVB.js → chunk-PT54WISF.js} +189 -55
- package/dist/chunk-PT54WISF.js.map +1 -0
- package/dist/{chunk-UWJNKGBA.js → chunk-QZLDRQ4I.js} +5 -5
- package/dist/{chunk-UYOXFFEY.js → chunk-RUNORG2P.js} +3 -3
- package/dist/{chunk-LC3FD4BV.js → chunk-RZQ6W2W4.js} +39 -39
- package/dist/{chunk-MRGJZ75M.js → chunk-SMNYAMJD.js} +3 -3
- package/dist/{chunk-MKQBXQKU.js → chunk-TB6CBZ5V.js} +22 -15
- package/dist/chunk-TB6CBZ5V.js.map +1 -0
- package/dist/{chunk-DN2IDKI5.js → chunk-TO7ZTWJW.js} +2 -2
- package/dist/{chunk-K2TDK7GG.js → chunk-TWTUGCTY.js} +2 -2
- package/dist/{chunk-CUTFSLSD.js → chunk-UCTS3DDH.js} +2 -2
- package/dist/{chunk-JYIJNNN3.js → chunk-UG6VXIOK.js} +2 -2
- package/dist/{chunk-QOQQMVR2.js → chunk-UKYS3NU5.js} +2 -2
- package/dist/{chunk-VZRFGJCH.js → chunk-UPLZNJKN.js} +2 -2
- package/dist/{chunk-C23BYTJV.js → chunk-UW5NPZJ2.js} +40 -12
- package/dist/chunk-UW5NPZJ2.js.map +1 -0
- package/dist/{chunk-DOOWFDWN.js → chunk-V6ERBR7U.js} +2 -2
- package/dist/{chunk-7HG7MGAP.js → chunk-W5ZKPI34.js} +462 -2673
- package/dist/chunk-W5ZKPI34.js.map +1 -0
- package/dist/{chunk-HNDIND7P.js → chunk-XX4HPFFM.js} +54 -15
- package/dist/chunk-XX4HPFFM.js.map +1 -0
- package/dist/{chunk-N7CXQ5TA.js → chunk-XZHE2DEN.js} +1 -1
- package/dist/chunk-YXQEKCZM.js +223 -0
- package/dist/chunk-YXQEKCZM.js.map +1 -0
- package/dist/{chunk-6NNNURLK.js → chunk-Z54EM5YZ.js} +4 -4
- package/dist/{chunk-F2Y6I74C.js → chunk-ZRZVLB7O.js} +2 -2
- package/dist/{chunk-CLKBV3Z3.js → chunk-ZUKIUXHQ.js} +9 -9
- package/dist/{cli-jfuZXixq.d.ts → cli-BPDUXnAl.d.ts} +5 -5
- package/dist/cli.d.ts +22 -22
- package/dist/cli.js +66 -58
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +14 -14
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +14 -14
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +14 -14
- 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 +2 -2
- package/dist/{contradiction-scan-KCADHXPE.js → contradiction-scan-JDTBXRGW.js} +3 -3
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/convergence-refresh.js +16 -16
- 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 +16 -16
- 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-CTDuAVJc.d.ts → dependency-propagation-delivery-CzEfB31P.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 +14 -14
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/explicit-capture.js +4 -3
- package/dist/external-wiki-access.d.ts +22 -22
- package/dist/external-wiki-access.js +33 -30
- 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 +22 -22
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-faithfulness.js +2 -2
- 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-judge.js +2 -2
- 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 +20 -20
- package/dist/fallback-llm.d.ts +14 -1
- package/dist/fallback-llm.js +1 -1
- package/dist/{first-start-migration-LMMZFLKK.js → first-start-migration-CRZCZAJN.js} +5 -5
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-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 +30 -747
- package/dist/index.js +203 -118
- package/dist/inline-script.d.ts +3 -0
- package/dist/inline-script.js +8 -0
- package/dist/inline-script.js.map +1 -0
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +2 -2
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +2 -1
- package/dist/local-llm.js +2 -2
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +14 -14
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -14
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +15 -15
- package/dist/{maintenance-heMZSq7G.d.ts → maintenance-C8aRWfhq.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +22 -22
- package/dist/mcp-read-only-tools.js +1 -1
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +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 +15 -15
- 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 +14 -14
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +17 -17
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +23 -22
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +19 -19
- package/dist/{orchestrator-BQQPsSjs.d.ts → orchestrator-D85CzDDY.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +86 -80
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-DFfmsnGk.d.ts → pipeline-DPcUifCO.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/public-http-D3GBeII4.d.ts +25 -0
- package/dist/{qmd-BF5bcVRs.d.ts → qmd-dFCHp1r0.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-planner-llm.js +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +3 -2
- 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/{scope-profiles-CbUOsE_E.d.ts → scope-profiles-DICK7GtN.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 +5 -5
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +9 -9
- 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-DAB3xdKM.d.ts → semantic-consolidation-BP7NAX0N.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +15 -15
- package/dist/semantic-rule-promotion.js +14 -14
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +14 -14
- package/dist/{service-DGKg2O6r.d.ts → service-DT-pJyda.d.ts} +660 -660
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +16 -16
- package/dist/{storage-CC1jKcWg.d.ts → storage-qOnaJ1Lw.d.ts} +4 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +13 -13
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +5 -5
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +518 -0
- package/dist/support-passport/index.js +357 -0
- package/dist/support-passport/index.js.map +1 -0
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/tier-routing.js +2 -2
- package/dist/{tier-stats-JDN6HRNT.js → tier-stats-EXYSKSVN.js} +3 -3
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/types.d.ts +24 -24
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-BnQza1y8.d.ts → types-ed-SvS2t.d.ts} +7 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +14 -14
- package/dist/write-envelope.d.ts +1 -1
- package/dist/write-rate-limiter.d.ts +11 -5
- package/dist/write-rate-limiter.js +1 -1
- package/package.json +12 -2
- package/scripts/build-with-heap.mjs +11 -1
- package/src/access-authorization-probe.ts +20 -2
- package/src/access-boundary.ts +32 -0
- package/src/access-http.test.ts +1940 -643
- package/src/access-http.ts +81 -74
- package/src/access-mcp.ts +3 -1
- package/src/access-operations.ts +2 -0
- package/src/access-service.ts +612 -845
- package/src/access-surface-catalog.test.ts +40 -10
- package/src/access-surface-catalog.ts +15 -0
- package/src/active-memory-bridge.ts +17 -19
- package/src/config.ts +18 -9
- package/src/fallback-llm.test.ts +54 -0
- package/src/fallback-llm.ts +54 -9
- package/src/inline-script.ts +6 -0
- package/src/local-llm-headers-timeout.test.ts +42 -0
- package/src/local-llm.ts +8 -8
- package/src/mcp-read-only-tools.ts +3 -0
- package/src/relay/mission-access.test.ts +11 -11
- package/src/relay/mission-ui.test.ts +3 -1
- package/src/storage/tombstone-blocked-capture-sync.ts +31 -0
- package/src/storage-contract/failure-semantics.test.ts +102 -0
- package/src/support-passport/access-http-base.ts +143 -0
- package/src/support-passport/access-http.ts +172 -0
- package/src/support-passport/access-operations.ts +272 -0
- package/src/support-passport/access-service-base.test.ts +193 -0
- package/src/support-passport/access-service-base.ts +176 -0
- package/src/support-passport/access-surface.test.ts +283 -0
- package/src/support-passport/access-surface.ts +286 -0
- package/src/support-passport/card-projection.test.ts +8 -0
- package/src/support-passport/card-projection.ts +77 -16
- package/src/support-passport/card-replacement.ts +126 -0
- package/src/support-passport/card-service-commit-forwarding.test.ts +160 -0
- package/src/support-passport/card-service.test.ts +533 -31
- package/src/support-passport/card-service.ts +506 -364
- package/src/support-passport/card-state.ts +158 -0
- package/src/support-passport/config-model-routing.test.ts +37 -0
- package/src/support-passport/config.test.ts +34 -0
- package/src/support-passport/config.ts +41 -0
- package/src/support-passport/contracts.test.ts +15 -2
- package/src/support-passport/contracts.ts +13 -0
- package/src/support-passport/errors.ts +6 -0
- package/src/support-passport/generated-batch.test.ts +185 -0
- package/src/support-passport/generated-batch.ts +338 -0
- package/src/support-passport/grant-contracts.ts +45 -1
- package/src/support-passport/grant-generated-batch-scope.test.ts +201 -0
- package/src/support-passport/grant-service-cache.test.ts +2 -2
- package/src/support-passport/grant-service-cancellation.test.ts +2 -0
- package/src/support-passport/grant-service-marker-cache.test.ts +62 -0
- package/src/support-passport/grant-service.test.ts +853 -239
- package/src/support-passport/grant-service.ts +206 -57
- package/src/support-passport/grant-store.ts +7 -3
- package/src/support-passport/index.ts +10 -0
- package/src/support-passport/mcp-tools.test.ts +222 -0
- package/src/support-passport/mcp-tools.ts +187 -0
- package/src/support-passport/model-adapter.test.ts +840 -0
- package/src/support-passport/model-adapter.ts +602 -0
- package/src/support-passport/model-audit-pinned-write.test.ts +59 -0
- package/src/support-passport/model-audit.ts +152 -0
- package/src/support-passport/model-bridge.test.ts +456 -0
- package/src/support-passport/model-bridge.ts +439 -0
- package/src/support-passport/model-contracts.ts +124 -0
- package/src/support-passport/model-service.test.ts +1731 -0
- package/src/support-passport/model-service.ts +399 -0
- package/src/support-passport/owner-http.test.ts +671 -0
- package/src/support-passport/owner-lock.ts +26 -9
- package/src/support-passport/private-file.test.ts +99 -0
- package/src/support-passport/private-file.ts +139 -12
- package/src/support-passport/public-http.test.ts +476 -0
- package/src/support-passport/public-http.ts +601 -0
- package/src/support-passport/ui.test.ts +181 -0
- package/src/types.ts +7 -0
- package/src/write-rate-limiter.test.ts +30 -6
- package/src/write-rate-limiter.ts +29 -18
- package/dist/chunk-2INWPC6E.js.map +0 -1
- package/dist/chunk-2PNXOQ4P.js.map +0 -1
- package/dist/chunk-3SXLD5T3.js.map +0 -1
- package/dist/chunk-3WGI7F4V.js.map +0 -1
- package/dist/chunk-7HG7MGAP.js.map +0 -1
- package/dist/chunk-BIF4SHMD.js.map +0 -1
- package/dist/chunk-C23BYTJV.js.map +0 -1
- package/dist/chunk-CSY44OKU.js.map +0 -1
- package/dist/chunk-DDA55IVB.js.map +0 -1
- package/dist/chunk-HNDIND7P.js.map +0 -1
- package/dist/chunk-L6CQ7WIW.js.map +0 -1
- package/dist/chunk-MKQBXQKU.js.map +0 -1
- package/dist/chunk-QQXWFDRD.js.map +0 -1
- package/dist/chunk-QTO6BMQZ.js.map +0 -1
- package/dist/chunk-WZWZRZ2V.js.map +0 -1
- /package/dist/{auto-sync-TDHLZPVP.js.map → auto-sync-NR3GPCOA.js.map} +0 -0
- /package/dist/{chunk-LLJMAULE.js.map → chunk-2FKEIPIT.js.map} +0 -0
- /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-TKFGFDPJ.js.map → chunk-3TIZPSJC.js.map} +0 -0
- /package/dist/{chunk-B4T3FQMT.js.map → chunk-43SEBY2M.js.map} +0 -0
- /package/dist/{chunk-LVKYLMSM.js.map → chunk-46SZBVDS.js.map} +0 -0
- /package/dist/{chunk-YKZLOPTC.js.map → chunk-5FD7JMTX.js.map} +0 -0
- /package/dist/{chunk-WM5LD6AT.js.map → chunk-6CYHUWCO.js.map} +0 -0
- /package/dist/{chunk-YR65W7BV.js.map → chunk-BRR5EUTF.js.map} +0 -0
- /package/dist/{chunk-JKW663ZN.js.map → chunk-CIAI24TQ.js.map} +0 -0
- /package/dist/{chunk-YDWPYDML.js.map → chunk-EKTP7MNS.js.map} +0 -0
- /package/dist/{chunk-6LEIWWAF.js.map → chunk-F2JRDPON.js.map} +0 -0
- /package/dist/{chunk-VYQ4URQB.js.map → chunk-FFRTNF3L.js.map} +0 -0
- /package/dist/{chunk-TY6MKIWP.js.map → chunk-FONWBCVY.js.map} +0 -0
- /package/dist/{chunk-XSV2YDCJ.js.map → chunk-FPLM2TKD.js.map} +0 -0
- /package/dist/{chunk-VTU5IEKY.js.map → chunk-FTPONF6B.js.map} +0 -0
- /package/dist/{chunk-6PZGKGAO.js.map → chunk-FWX7W5PM.js.map} +0 -0
- /package/dist/{chunk-QPW3PAIH.js.map → chunk-G6DFSS6A.js.map} +0 -0
- /package/dist/{chunk-2DD6ZHOM.js.map → chunk-GJ776BC2.js.map} +0 -0
- /package/dist/{chunk-XKAR4RF6.js.map → chunk-GN3WRFJY.js.map} +0 -0
- /package/dist/{chunk-RG426DJO.js.map → chunk-H7NAZ3UB.js.map} +0 -0
- /package/dist/{chunk-BSXGTDVJ.js.map → chunk-HBUFQRIM.js.map} +0 -0
- /package/dist/{chunk-FFEBSFVT.js.map → chunk-HGJ6NJ4E.js.map} +0 -0
- /package/dist/{chunk-5JKUSBMX.js.map → chunk-IKP5OYQQ.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-DBY26JJJ.js.map → chunk-IQ4NVWEF.js.map} +0 -0
- /package/dist/{chunk-7Q7JWLQ4.js.map → chunk-JYG4OLWH.js.map} +0 -0
- /package/dist/{chunk-3OTSVCPK.js.map → chunk-K26NWEOC.js.map} +0 -0
- /package/dist/{chunk-HOIRB77A.js.map → chunk-L6Z5OAS4.js.map} +0 -0
- /package/dist/{chunk-7RZJWZ46.js.map → chunk-LEPOP46W.js.map} +0 -0
- /package/dist/{chunk-NGT35IGQ.js.map → chunk-MIH3CJPN.js.map} +0 -0
- /package/dist/{chunk-QPAQSG3F.js.map → chunk-MK5VEWCR.js.map} +0 -0
- /package/dist/{chunk-L7RTUEDG.js.map → chunk-OCIP6YMD.js.map} +0 -0
- /package/dist/{chunk-UWJNKGBA.js.map → chunk-QZLDRQ4I.js.map} +0 -0
- /package/dist/{chunk-UYOXFFEY.js.map → chunk-RUNORG2P.js.map} +0 -0
- /package/dist/{chunk-LC3FD4BV.js.map → chunk-RZQ6W2W4.js.map} +0 -0
- /package/dist/{chunk-MRGJZ75M.js.map → chunk-SMNYAMJD.js.map} +0 -0
- /package/dist/{chunk-DN2IDKI5.js.map → chunk-TO7ZTWJW.js.map} +0 -0
- /package/dist/{chunk-K2TDK7GG.js.map → chunk-TWTUGCTY.js.map} +0 -0
- /package/dist/{chunk-CUTFSLSD.js.map → chunk-UCTS3DDH.js.map} +0 -0
- /package/dist/{chunk-JYIJNNN3.js.map → chunk-UG6VXIOK.js.map} +0 -0
- /package/dist/{chunk-QOQQMVR2.js.map → chunk-UKYS3NU5.js.map} +0 -0
- /package/dist/{chunk-VZRFGJCH.js.map → chunk-UPLZNJKN.js.map} +0 -0
- /package/dist/{chunk-DOOWFDWN.js.map → chunk-V6ERBR7U.js.map} +0 -0
- /package/dist/{chunk-N7CXQ5TA.js.map → chunk-XZHE2DEN.js.map} +0 -0
- /package/dist/{chunk-6NNNURLK.js.map → chunk-Z54EM5YZ.js.map} +0 -0
- /package/dist/{chunk-F2Y6I74C.js.map → chunk-ZRZVLB7O.js.map} +0 -0
- /package/dist/{chunk-CLKBV3Z3.js.map → chunk-ZUKIUXHQ.js.map} +0 -0
- /package/dist/{contradiction-scan-KCADHXPE.js.map → contradiction-scan-JDTBXRGW.js.map} +0 -0
- /package/dist/{first-start-migration-LMMZFLKK.js.map → first-start-migration-CRZCZAJN.js.map} +0 -0
- /package/dist/{tier-stats-JDN6HRNT.js.map → tier-stats-EXYSKSVN.js.map} +0 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import type { StorageManager } from "../index.js";
|
|
6
|
+
import type { MemoryFile } from "../types.js";
|
|
7
|
+
import {
|
|
8
|
+
type StoredSupportPassportCard,
|
|
9
|
+
computeSupportPassportOwnerKey,
|
|
10
|
+
projectSupportPassportCard,
|
|
11
|
+
} from "./card-projection.js";
|
|
12
|
+
import { SupportPassportNamespaceSchema } from "./contracts.js";
|
|
13
|
+
import { SupportPassportError } from "./errors.js";
|
|
14
|
+
import {
|
|
15
|
+
ensurePrivateDirectoryNoFollow,
|
|
16
|
+
ensurePrivateDirectoryTreeNoFollow,
|
|
17
|
+
readPrivateFileNoFollow,
|
|
18
|
+
removePrivateFilesNoFollow,
|
|
19
|
+
writePrivateFileAtomicallyNoFollow,
|
|
20
|
+
} from "./private-file.js";
|
|
21
|
+
|
|
22
|
+
const BatchIdSchema = z
|
|
23
|
+
.string()
|
|
24
|
+
.uuid()
|
|
25
|
+
.transform((value) => value.toLowerCase());
|
|
26
|
+
const GeneratedBatchMarkerSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
schemaVersion: z.literal(1),
|
|
29
|
+
batchId: BatchIdSchema,
|
|
30
|
+
size: z.number().int().min(1).max(8),
|
|
31
|
+
namespace: SupportPassportNamespaceSchema,
|
|
32
|
+
owner: z.string().regex(/^[a-f0-9]{64}$/),
|
|
33
|
+
complete: z.boolean(),
|
|
34
|
+
})
|
|
35
|
+
.strict();
|
|
36
|
+
|
|
37
|
+
export type GeneratedBatchMarker = z.infer<typeof GeneratedBatchMarkerSchema>;
|
|
38
|
+
|
|
39
|
+
export interface GeneratedBatchContext {
|
|
40
|
+
storage: StorageManager;
|
|
41
|
+
principal: string;
|
|
42
|
+
namespace: string;
|
|
43
|
+
now: () => Date;
|
|
44
|
+
requireOwnerLock: () => Promise<void>;
|
|
45
|
+
onCommitted?: () => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type GeneratedBatchMarkerWriter = (storage: StorageManager, marker: GeneratedBatchMarker) => Promise<void>;
|
|
49
|
+
|
|
50
|
+
const BATCH_ERROR = "The generated draft batch state is not safe.";
|
|
51
|
+
|
|
52
|
+
function batchDirectory(storage: StorageManager): string {
|
|
53
|
+
return path.join(storage.dir, "state", "support-passport", "generated-batches");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function batchFileName(batchId: string): string {
|
|
57
|
+
return `${BatchIdSchema.parse(batchId)}.json`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function batchPath(storage: StorageManager, batchId: string): string {
|
|
61
|
+
return path.join(batchDirectory(storage), batchFileName(batchId));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function markerFor(context: GeneratedBatchContext, batchId: string, size: number, complete: boolean) {
|
|
65
|
+
return GeneratedBatchMarkerSchema.parse({
|
|
66
|
+
schemaVersion: 1,
|
|
67
|
+
batchId,
|
|
68
|
+
size,
|
|
69
|
+
namespace: context.namespace,
|
|
70
|
+
owner: computeSupportPassportOwnerKey(context.principal),
|
|
71
|
+
complete,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function sameOwner(marker: GeneratedBatchMarker, context: GeneratedBatchContext): boolean {
|
|
76
|
+
return marker.namespace === context.namespace && marker.owner === computeSupportPassportOwnerKey(context.principal);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function ensureBatchDirectory(storage: StorageManager): Promise<void> {
|
|
80
|
+
const directory = batchDirectory(storage);
|
|
81
|
+
await ensurePrivateDirectoryTreeNoFollow(directory, BATCH_ERROR);
|
|
82
|
+
await ensurePrivateDirectoryNoFollow(storage.dir, directory, BATCH_ERROR);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function readSupportPassportGeneratedBatchMarker(
|
|
86
|
+
storage: StorageManager,
|
|
87
|
+
batchId: string
|
|
88
|
+
): Promise<GeneratedBatchMarker | null> {
|
|
89
|
+
const directory = batchDirectory(storage);
|
|
90
|
+
try {
|
|
91
|
+
const content = await readPrivateFileNoFollow(directory, batchPath(storage, batchId), BATCH_ERROR, storage.dir);
|
|
92
|
+
let value: unknown;
|
|
93
|
+
try {
|
|
94
|
+
value = JSON.parse(content);
|
|
95
|
+
} catch {
|
|
96
|
+
throw new SupportPassportError("card_data_invalid", "The generated draft batch state is invalid.", 500);
|
|
97
|
+
}
|
|
98
|
+
const parsed = GeneratedBatchMarkerSchema.safeParse(value);
|
|
99
|
+
if (!parsed.success || parsed.data.batchId !== BatchIdSchema.parse(batchId)) {
|
|
100
|
+
throw new SupportPassportError("card_data_invalid", "The generated draft batch state is invalid.", 500);
|
|
101
|
+
}
|
|
102
|
+
return parsed.data;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function isSupportPassportGeneratedBatchCommitted(
|
|
110
|
+
context: GeneratedBatchContext,
|
|
111
|
+
marker: GeneratedBatchMarker
|
|
112
|
+
): Promise<boolean> {
|
|
113
|
+
const current = await readSupportPassportGeneratedBatchMarker(context.storage, marker.batchId);
|
|
114
|
+
return Boolean(current?.complete && sameOwner(current, context) && current.size === marker.size);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function writeBatchMarker(storage: StorageManager, marker: GeneratedBatchMarker): Promise<void> {
|
|
118
|
+
const directory = batchDirectory(storage);
|
|
119
|
+
await writePrivateFileAtomicallyNoFollow(
|
|
120
|
+
directory,
|
|
121
|
+
batchPath(storage, marker.batchId),
|
|
122
|
+
`${JSON.stringify(marker)}\n`,
|
|
123
|
+
BATCH_ERROR,
|
|
124
|
+
storage.dir
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function removeBatchMarker(storage: StorageManager, batchId: string): Promise<void> {
|
|
129
|
+
await removePrivateFilesNoFollow(batchDirectory(storage), [batchFileName(batchId)], BATCH_ERROR, storage.dir);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function projectOwnedCard(memory: MemoryFile, namespace: string, principal: string): StoredSupportPassportCard | null {
|
|
133
|
+
const card = projectSupportPassportCard(memory);
|
|
134
|
+
return card?.namespace === namespace && card.owner === computeSupportPassportOwnerKey(principal) ? card : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function persistSupportPassportGeneratedBatchMarker(
|
|
138
|
+
context: GeneratedBatchContext,
|
|
139
|
+
batchId: string,
|
|
140
|
+
size: number,
|
|
141
|
+
writeMarker: GeneratedBatchMarkerWriter = writeBatchMarker
|
|
142
|
+
): Promise<GeneratedBatchMarker> {
|
|
143
|
+
const marker = markerFor(context, batchId, size, false);
|
|
144
|
+
await context.requireOwnerLock();
|
|
145
|
+
await ensureBatchDirectory(context.storage);
|
|
146
|
+
await writeMarker(context.storage, marker);
|
|
147
|
+
context.onCommitted?.();
|
|
148
|
+
return marker;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function commitSupportPassportGeneratedBatch(
|
|
152
|
+
context: GeneratedBatchContext,
|
|
153
|
+
marker: GeneratedBatchMarker,
|
|
154
|
+
cards: readonly StoredSupportPassportCard[],
|
|
155
|
+
writeMarker: GeneratedBatchMarkerWriter = writeBatchMarker
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
const cardIds = cards.map((card) => card.card.cardId);
|
|
158
|
+
if (!sameOwner(marker, context) || cards.length !== marker.size || new Set(cardIds).size !== cards.length) {
|
|
159
|
+
throw new SupportPassportError("storage_conflict", "The generated draft batch is incomplete.", 409);
|
|
160
|
+
}
|
|
161
|
+
for (const card of cards) {
|
|
162
|
+
if (
|
|
163
|
+
card.namespace !== context.namespace ||
|
|
164
|
+
card.owner !== computeSupportPassportOwnerKey(context.principal) ||
|
|
165
|
+
card.card.status !== "pending_review" ||
|
|
166
|
+
card.generatedBatchId !== marker.batchId ||
|
|
167
|
+
card.generatedBatchSize !== marker.size
|
|
168
|
+
) {
|
|
169
|
+
throw new SupportPassportError("storage_conflict", "The generated draft batch is incomplete.", 409);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
await context.requireOwnerLock();
|
|
173
|
+
const committed = { ...marker, complete: true };
|
|
174
|
+
try {
|
|
175
|
+
await writeMarker(context.storage, committed);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (!(await isSupportPassportGeneratedBatchCommitted(context, marker))) throw error;
|
|
178
|
+
}
|
|
179
|
+
context.onCommitted?.();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function rejectGeneratedDraft(context: GeneratedBatchContext, cardId: string): Promise<boolean> {
|
|
183
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
184
|
+
const memory = await context.storage.getMemoryById(cardId);
|
|
185
|
+
if (!memory) return true;
|
|
186
|
+
const stored = projectOwnedCard(memory, context.namespace, context.principal);
|
|
187
|
+
if (!stored) return false;
|
|
188
|
+
if (stored.card.status === "rejected") return true;
|
|
189
|
+
if (stored.card.status !== "pending_review") return false;
|
|
190
|
+
await context.requireOwnerLock();
|
|
191
|
+
if (
|
|
192
|
+
await context.storage.writeMemoryFrontmatterIfUnchanged(
|
|
193
|
+
memory,
|
|
194
|
+
{ status: "rejected", updated: context.now().toISOString() },
|
|
195
|
+
{ actor: context.principal, reasonCode: "draft-batch-failed" }
|
|
196
|
+
)
|
|
197
|
+
) {
|
|
198
|
+
context.onCommitted?.();
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export async function rollbackSupportPassportGeneratedBatch(
|
|
206
|
+
context: GeneratedBatchContext,
|
|
207
|
+
batchId: string,
|
|
208
|
+
knownCardIds: readonly string[]
|
|
209
|
+
): Promise<boolean> {
|
|
210
|
+
const marker = await readSupportPassportGeneratedBatchMarker(context.storage, batchId);
|
|
211
|
+
if (marker && !sameOwner(marker, context)) return false;
|
|
212
|
+
if (marker?.complete) return false;
|
|
213
|
+
const cardIds = new Set(knownCardIds);
|
|
214
|
+
let scanComplete = true;
|
|
215
|
+
try {
|
|
216
|
+
for (const memory of await context.storage.readAllMemories()) {
|
|
217
|
+
const card = projectOwnedCard(memory, context.namespace, context.principal);
|
|
218
|
+
if (card?.generatedBatchId === batchId) cardIds.add(card.card.cardId);
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
scanComplete = false;
|
|
222
|
+
}
|
|
223
|
+
let complete = scanComplete;
|
|
224
|
+
for (const cardId of cardIds) {
|
|
225
|
+
try {
|
|
226
|
+
if (!(await rejectGeneratedDraft(context, cardId))) complete = false;
|
|
227
|
+
} catch {
|
|
228
|
+
complete = false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!complete) return false;
|
|
232
|
+
try {
|
|
233
|
+
await removeBatchMarker(context.storage, batchId);
|
|
234
|
+
context.onCommitted?.();
|
|
235
|
+
} catch {
|
|
236
|
+
complete = false;
|
|
237
|
+
}
|
|
238
|
+
return complete;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export async function isCommittedGeneratedCard(
|
|
242
|
+
storage: StorageManager,
|
|
243
|
+
card: StoredSupportPassportCard,
|
|
244
|
+
markerCache?: Map<string, GeneratedBatchMarker | null>
|
|
245
|
+
): Promise<boolean> {
|
|
246
|
+
if (!card.generatedBatchId) return true;
|
|
247
|
+
let marker = markerCache?.get(card.generatedBatchId);
|
|
248
|
+
if (!markerCache?.has(card.generatedBatchId)) {
|
|
249
|
+
marker = await readSupportPassportGeneratedBatchMarker(storage, card.generatedBatchId);
|
|
250
|
+
markerCache?.set(card.generatedBatchId, marker);
|
|
251
|
+
}
|
|
252
|
+
return Boolean(
|
|
253
|
+
marker?.complete &&
|
|
254
|
+
marker.size === card.generatedBatchSize &&
|
|
255
|
+
marker.namespace === card.namespace &&
|
|
256
|
+
marker.owner === card.owner
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export async function projectCommittedSupportPassportCards(
|
|
261
|
+
storage: StorageManager,
|
|
262
|
+
memories: readonly MemoryFile[],
|
|
263
|
+
namespace: string,
|
|
264
|
+
principal: string
|
|
265
|
+
): Promise<StoredSupportPassportCard[]> {
|
|
266
|
+
const projected = memories
|
|
267
|
+
.map(projectSupportPassportCard)
|
|
268
|
+
.filter(
|
|
269
|
+
(card): card is StoredSupportPassportCard =>
|
|
270
|
+
card?.namespace === namespace && card.owner === computeSupportPassportOwnerKey(principal)
|
|
271
|
+
);
|
|
272
|
+
const markers = new Map<string, GeneratedBatchMarker | null>();
|
|
273
|
+
const committed: StoredSupportPassportCard[] = [];
|
|
274
|
+
for (const card of projected) {
|
|
275
|
+
if (await isCommittedGeneratedCard(storage, card, markers)) committed.push(card);
|
|
276
|
+
}
|
|
277
|
+
const ids = new Set<string>();
|
|
278
|
+
for (const card of committed) {
|
|
279
|
+
if (ids.has(card.card.cardId)) {
|
|
280
|
+
throw new SupportPassportError("card_data_invalid", "Support card IDs must be unique.", 500);
|
|
281
|
+
}
|
|
282
|
+
ids.add(card.card.cardId);
|
|
283
|
+
}
|
|
284
|
+
return committed;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export async function readCommittedSupportPassportCards(
|
|
288
|
+
storage: StorageManager,
|
|
289
|
+
namespace: string,
|
|
290
|
+
principal: string
|
|
291
|
+
): Promise<StoredSupportPassportCard[]> {
|
|
292
|
+
return await projectCommittedSupportPassportCards(
|
|
293
|
+
storage,
|
|
294
|
+
await storage.readAllMemories(),
|
|
295
|
+
namespace,
|
|
296
|
+
principal
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export async function recoverSupportPassportGeneratedBatches(
|
|
301
|
+
context: GeneratedBatchContext,
|
|
302
|
+
memories: readonly MemoryFile[]
|
|
303
|
+
): Promise<void> {
|
|
304
|
+
const cardsByBatch = new Map<string, StoredSupportPassportCard[]>();
|
|
305
|
+
for (const memory of memories) {
|
|
306
|
+
const card = projectOwnedCard(memory, context.namespace, context.principal);
|
|
307
|
+
if (!card?.generatedBatchId) continue;
|
|
308
|
+
const cards = cardsByBatch.get(card.generatedBatchId) ?? [];
|
|
309
|
+
cards.push(card);
|
|
310
|
+
cardsByBatch.set(card.generatedBatchId, cards);
|
|
311
|
+
}
|
|
312
|
+
for (const [batchId, cards] of cardsByBatch) {
|
|
313
|
+
const marker = await readSupportPassportGeneratedBatchMarker(context.storage, batchId);
|
|
314
|
+
if (marker?.complete === true && sameOwner(marker, context)) continue;
|
|
315
|
+
if (!marker) {
|
|
316
|
+
let recovered = true;
|
|
317
|
+
for (const card of cards) {
|
|
318
|
+
if (card.card.status !== "pending_review") continue;
|
|
319
|
+
try {
|
|
320
|
+
if (!(await rejectGeneratedDraft(context, card.card.cardId))) recovered = false;
|
|
321
|
+
} catch {
|
|
322
|
+
recovered = false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (recovered) continue;
|
|
326
|
+
throw new SupportPassportError("storage_conflict", "An incomplete generated draft batch could not recover.", 500);
|
|
327
|
+
}
|
|
328
|
+
if (
|
|
329
|
+
!(await rollbackSupportPassportGeneratedBatch(
|
|
330
|
+
context,
|
|
331
|
+
batchId,
|
|
332
|
+
cards.map((card) => card.card.cardId)
|
|
333
|
+
))
|
|
334
|
+
) {
|
|
335
|
+
throw new SupportPassportError("storage_conflict", "An incomplete generated draft batch could not recover.", 500);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -60,7 +60,8 @@ export const SupportPassportCreateGrantInputSchema = z
|
|
|
60
60
|
.object({
|
|
61
61
|
principal: z.string().trim().min(1).max(512),
|
|
62
62
|
cards: z.array(SupportPassportGrantCardRefSchema).min(1).max(8),
|
|
63
|
-
expiresAt: IsoTimestampSchema,
|
|
63
|
+
expiresAt: IsoTimestampSchema.optional(),
|
|
64
|
+
durationMs: z.number().int().min(300_000).max(604_800_000).optional(),
|
|
64
65
|
})
|
|
65
66
|
.strict()
|
|
66
67
|
.superRefine((input, ctx) => {
|
|
@@ -75,8 +76,49 @@ export const SupportPassportCreateGrantInputSchema = z
|
|
|
75
76
|
}
|
|
76
77
|
ids.add(card.cardId);
|
|
77
78
|
}
|
|
79
|
+
if ((input.expiresAt === undefined) === (input.durationMs === undefined)) {
|
|
80
|
+
ctx.addIssue({
|
|
81
|
+
code: z.ZodIssueCode.custom,
|
|
82
|
+
message: "one expiry value is required",
|
|
83
|
+
path: ["expiresAt"],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
78
86
|
});
|
|
79
87
|
|
|
88
|
+
export const SupportPassportCreateGrantRequestSchema = z
|
|
89
|
+
.object({
|
|
90
|
+
cardIds: z.array(SupportPassportMemoryIdSchema).min(1).max(8),
|
|
91
|
+
cardRevisions: z.array(SupportPassportGrantCardRefSchema).min(1).max(8),
|
|
92
|
+
expiresAt: IsoTimestampSchema.optional(),
|
|
93
|
+
durationMs: z.number().int().min(300_000).max(604_800_000).optional(),
|
|
94
|
+
})
|
|
95
|
+
.strict()
|
|
96
|
+
.superRefine((input, ctx) => {
|
|
97
|
+
const cardIds = new Set(input.cardIds);
|
|
98
|
+
const revisionIds = new Set(input.cardRevisions.map((card) => card.cardId));
|
|
99
|
+
if (
|
|
100
|
+
cardIds.size !== input.cardIds.length ||
|
|
101
|
+
revisionIds.size !== input.cardRevisions.length ||
|
|
102
|
+
cardIds.size !== revisionIds.size ||
|
|
103
|
+
input.cardIds.some((cardId) => !revisionIds.has(cardId))
|
|
104
|
+
) {
|
|
105
|
+
ctx.addIssue({
|
|
106
|
+
code: z.ZodIssueCode.custom,
|
|
107
|
+
message: "card revisions must match the selected card IDs",
|
|
108
|
+
path: ["cardRevisions"],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if ((input.expiresAt === undefined) === (input.durationMs === undefined)) {
|
|
112
|
+
ctx.addIssue({
|
|
113
|
+
code: z.ZodIssueCode.custom,
|
|
114
|
+
message: "one expiry value is required",
|
|
115
|
+
path: ["expiresAt"],
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export const SupportPassportOwnerCreateGrantRequestSchema = SupportPassportCreateGrantRequestSchema;
|
|
121
|
+
|
|
80
122
|
export const SupportPassportOwnerGrantSchema = z
|
|
81
123
|
.object({
|
|
82
124
|
grantId: GrantIdSchema,
|
|
@@ -133,6 +175,8 @@ export const SupportPassportListGrantsInputSchema = z
|
|
|
133
175
|
export type SupportPassportGrantCardRef = z.infer<typeof SupportPassportGrantCardRefSchema>;
|
|
134
176
|
export type SupportPassportGrantState = z.infer<typeof SupportPassportGrantStateSchema>;
|
|
135
177
|
export type SupportPassportCreateGrantInput = z.input<typeof SupportPassportCreateGrantInputSchema>;
|
|
178
|
+
export type SupportPassportCreateGrantRequest = z.input<typeof SupportPassportCreateGrantRequestSchema>;
|
|
179
|
+
export type SupportPassportOwnerCreateGrantRequest = z.input<typeof SupportPassportOwnerCreateGrantRequestSchema>;
|
|
136
180
|
export type SupportPassportOwnerGrant = z.infer<typeof SupportPassportOwnerGrantSchema>;
|
|
137
181
|
export type SupportPassportCreatedGrant = z.infer<typeof SupportPassportCreatedGrantSchema>;
|
|
138
182
|
export type SupportPassportPublicGuide = z.infer<typeof SupportPassportPublicGuideSchema>;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { test } from "node:test";
|
|
6
|
+
|
|
7
|
+
import { StorageManager } from "../storage.js";
|
|
8
|
+
import { SUPPORT_PASSPORT_ATTRIBUTE_KEYS, computeSupportPassportOwnerKey } from "./card-projection.js";
|
|
9
|
+
import { SupportPassportCardService } from "./card-service.js";
|
|
10
|
+
import { SupportPassportError } from "./errors.js";
|
|
11
|
+
import { SupportPassportGrantService } from "./grant-service.js";
|
|
12
|
+
import { SupportPassportGrantStore } from "./grant-store.js";
|
|
13
|
+
|
|
14
|
+
test("grant reads do not open another owner's generated batch marker", async () => {
|
|
15
|
+
StorageManager.clearAllStaticCaches();
|
|
16
|
+
const root = await mkdtemp(path.join(tmpdir(), "remnic-support-grant-batch-scope-"));
|
|
17
|
+
try {
|
|
18
|
+
const storage = new StorageManager(path.join(root, "shared"));
|
|
19
|
+
await storage.ensureDirectories();
|
|
20
|
+
const now = () => new Date("2026-08-13T12:00:00.000Z");
|
|
21
|
+
const resolveOwner = async (principal: string) => ({ principal, namespace: "team", storage });
|
|
22
|
+
const cardService = new SupportPassportCardService({ resolveOwner, now });
|
|
23
|
+
const grantStore = new SupportPassportGrantStore({ memoryDir: path.join(root, "grants"), now });
|
|
24
|
+
const grantService = new SupportPassportGrantService({
|
|
25
|
+
grantStore,
|
|
26
|
+
resolveOwner,
|
|
27
|
+
resolveNamespace: async () => storage,
|
|
28
|
+
now,
|
|
29
|
+
});
|
|
30
|
+
const cards = new Map<string, Awaited<ReturnType<typeof cardService.approveCard>>>();
|
|
31
|
+
for (const principal of ["owner:alice", "owner:bob"]) {
|
|
32
|
+
const [draft] = await cardService.createGeneratedDrafts({
|
|
33
|
+
principal,
|
|
34
|
+
cards: [{
|
|
35
|
+
title: `${principal} support`,
|
|
36
|
+
statement: "Give me time to answer.",
|
|
37
|
+
category: "communication",
|
|
38
|
+
sourceMemoryIds: ["source-1"],
|
|
39
|
+
}],
|
|
40
|
+
});
|
|
41
|
+
assert.ok(draft);
|
|
42
|
+
cards.set(principal, await cardService.approveCard({
|
|
43
|
+
principal,
|
|
44
|
+
cardId: draft.cardId,
|
|
45
|
+
expectedRevision: draft.revision,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
const grants = new Map<string, Awaited<ReturnType<typeof grantService.createGrant>>>();
|
|
49
|
+
for (const principal of ["owner:alice", "owner:bob"]) {
|
|
50
|
+
const card = cards.get(principal);
|
|
51
|
+
assert.ok(card);
|
|
52
|
+
grants.set(principal, await grantService.createGrant({
|
|
53
|
+
principal,
|
|
54
|
+
cards: [{ cardId: card.cardId, revision: card.revision }],
|
|
55
|
+
expiresAt: "2026-08-13T13:00:00.000Z",
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const bobCard = cards.get("owner:bob");
|
|
60
|
+
assert.ok(bobCard);
|
|
61
|
+
const bobMemory = await storage.getMemoryById(bobCard.cardId);
|
|
62
|
+
const batchId = bobMemory?.frontmatter.structuredAttributes?.[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.generatedBatchId];
|
|
63
|
+
assert.ok(batchId);
|
|
64
|
+
await writeFile(
|
|
65
|
+
path.join(storage.dir, "state", "support-passport", "generated-batches", `${batchId}.json`),
|
|
66
|
+
"not-json\n",
|
|
67
|
+
"utf8",
|
|
68
|
+
);
|
|
69
|
+
const freshService = new SupportPassportGrantService({
|
|
70
|
+
grantStore,
|
|
71
|
+
resolveOwner,
|
|
72
|
+
resolveNamespace: async () => storage,
|
|
73
|
+
now,
|
|
74
|
+
});
|
|
75
|
+
const aliceGrant = grants.get("owner:alice");
|
|
76
|
+
const bobGrant = grants.get("owner:bob");
|
|
77
|
+
assert.ok(aliceGrant);
|
|
78
|
+
assert.ok(bobGrant);
|
|
79
|
+
|
|
80
|
+
assert.equal(
|
|
81
|
+
(await freshService.readGrant({ grantId: aliceGrant.grant.grantId, secret: aliceGrant.secret })).cards[0]?.cardId,
|
|
82
|
+
cards.get("owner:alice")?.cardId,
|
|
83
|
+
);
|
|
84
|
+
await assert.rejects(
|
|
85
|
+
freshService.readGrant({ grantId: bobGrant.grant.grantId, secret: bobGrant.secret }),
|
|
86
|
+
(error: unknown) => error instanceof SupportPassportError && error.code === "card_data_invalid",
|
|
87
|
+
);
|
|
88
|
+
} finally {
|
|
89
|
+
StorageManager.clearAllStaticCaches();
|
|
90
|
+
await rm(root, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("a removed generated batch marker invalidates a cached grant snapshot", async () => {
|
|
95
|
+
StorageManager.clearAllStaticCaches();
|
|
96
|
+
const root = await mkdtemp(path.join(tmpdir(), "remnic-support-grant-batch-cache-"));
|
|
97
|
+
try {
|
|
98
|
+
const storage = new StorageManager(path.join(root, "owner"));
|
|
99
|
+
await storage.ensureDirectories();
|
|
100
|
+
const now = () => new Date("2026-08-13T12:00:00.000Z");
|
|
101
|
+
const resolveOwner = async (principal: string) => ({ principal, namespace: "owner", storage });
|
|
102
|
+
const cardService = new SupportPassportCardService({ resolveOwner, now });
|
|
103
|
+
const grantStore = new SupportPassportGrantStore({ memoryDir: path.join(root, "grants"), now });
|
|
104
|
+
const grantService = new SupportPassportGrantService({
|
|
105
|
+
grantStore,
|
|
106
|
+
resolveOwner,
|
|
107
|
+
resolveNamespace: async () => storage,
|
|
108
|
+
now,
|
|
109
|
+
});
|
|
110
|
+
const [draft] = await cardService.createGeneratedDrafts({
|
|
111
|
+
principal: "owner:alice",
|
|
112
|
+
cards: [{
|
|
113
|
+
title: "Time to answer",
|
|
114
|
+
statement: "Give me time to answer.",
|
|
115
|
+
category: "communication",
|
|
116
|
+
sourceMemoryIds: ["source-1"],
|
|
117
|
+
}],
|
|
118
|
+
});
|
|
119
|
+
assert.ok(draft);
|
|
120
|
+
const card = await cardService.approveCard({
|
|
121
|
+
principal: "owner:alice",
|
|
122
|
+
cardId: draft.cardId,
|
|
123
|
+
expectedRevision: draft.revision,
|
|
124
|
+
});
|
|
125
|
+
const grant = await grantService.createGrant({
|
|
126
|
+
principal: "owner:alice",
|
|
127
|
+
cards: [{ cardId: card.cardId, revision: card.revision }],
|
|
128
|
+
expiresAt: "2026-08-13T13:00:00.000Z",
|
|
129
|
+
});
|
|
130
|
+
await grantService.readGrant({ grantId: grant.grant.grantId, secret: grant.secret });
|
|
131
|
+
|
|
132
|
+
const memory = await storage.getMemoryById(card.cardId);
|
|
133
|
+
const batchId = memory?.frontmatter.structuredAttributes?.[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.generatedBatchId];
|
|
134
|
+
assert.ok(batchId);
|
|
135
|
+
await unlink(path.join(storage.dir, "state", "support-passport", "generated-batches", `${batchId}.json`));
|
|
136
|
+
|
|
137
|
+
await assert.rejects(
|
|
138
|
+
grantService.readGrant({ grantId: grant.grant.grantId, secret: grant.secret }),
|
|
139
|
+
(error: unknown) => error instanceof SupportPassportError && error.code === "grant_stale",
|
|
140
|
+
);
|
|
141
|
+
} finally {
|
|
142
|
+
StorageManager.clearAllStaticCaches();
|
|
143
|
+
await rm(root, { recursive: true, force: true });
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("a completed generated batch invalidates a cached omission", async () => {
|
|
148
|
+
StorageManager.clearAllStaticCaches();
|
|
149
|
+
const root = await mkdtemp(path.join(tmpdir(), "remnic-support-grant-batch-complete-"));
|
|
150
|
+
try {
|
|
151
|
+
const storage = new StorageManager(path.join(root, "owner"));
|
|
152
|
+
await storage.ensureDirectories();
|
|
153
|
+
const now = () => new Date("2026-08-13T12:00:00.000Z");
|
|
154
|
+
const principal = "owner:alice";
|
|
155
|
+
const resolveOwner = async () => ({ principal, namespace: "owner", storage });
|
|
156
|
+
const cardService = new SupportPassportCardService({ resolveOwner, now });
|
|
157
|
+
const [draft] = await cardService.createGeneratedDrafts({
|
|
158
|
+
principal,
|
|
159
|
+
cards: [{
|
|
160
|
+
title: "Time to answer",
|
|
161
|
+
statement: "Give me time to answer.",
|
|
162
|
+
category: "communication",
|
|
163
|
+
sourceMemoryIds: ["source-1"],
|
|
164
|
+
}],
|
|
165
|
+
});
|
|
166
|
+
assert.ok(draft);
|
|
167
|
+
const card = await cardService.approveCard({
|
|
168
|
+
principal,
|
|
169
|
+
cardId: draft.cardId,
|
|
170
|
+
expectedRevision: draft.revision,
|
|
171
|
+
});
|
|
172
|
+
const memory = await storage.getMemoryById(card.cardId);
|
|
173
|
+
const batchId = memory?.frontmatter.structuredAttributes?.[SUPPORT_PASSPORT_ATTRIBUTE_KEYS.generatedBatchId];
|
|
174
|
+
assert.ok(batchId);
|
|
175
|
+
const markerPath = path.join(storage.dir, "state", "support-passport", "generated-batches", `${batchId}.json`);
|
|
176
|
+
const completeMarker = JSON.parse(await readFile(markerPath, "utf8")) as Record<string, unknown>;
|
|
177
|
+
await writeFile(markerPath, `${JSON.stringify({ ...completeMarker, complete: false })}\n`, "utf8");
|
|
178
|
+
const service = new SupportPassportGrantService({
|
|
179
|
+
grantStore: {} as SupportPassportGrantStore,
|
|
180
|
+
resolveOwner,
|
|
181
|
+
resolveNamespace: async () => storage,
|
|
182
|
+
now,
|
|
183
|
+
});
|
|
184
|
+
const inspected = service as unknown as {
|
|
185
|
+
readStoredCardSnapshot(
|
|
186
|
+
target: StorageManager,
|
|
187
|
+
namespace: string,
|
|
188
|
+
ownerKey: string,
|
|
189
|
+
): Promise<{ cardsById: ReadonlyMap<string, unknown> }>;
|
|
190
|
+
};
|
|
191
|
+
const ownerKey = computeSupportPassportOwnerKey(principal);
|
|
192
|
+
|
|
193
|
+
assert.equal((await inspected.readStoredCardSnapshot(storage, "owner", ownerKey)).cardsById.size, 0);
|
|
194
|
+
await writeFile(markerPath, `${JSON.stringify(completeMarker)}\n`, "utf8");
|
|
195
|
+
|
|
196
|
+
assert.equal((await inspected.readStoredCardSnapshot(storage, "owner", ownerKey)).cardsById.size, 1);
|
|
197
|
+
} finally {
|
|
198
|
+
StorageManager.clearAllStaticCaches();
|
|
199
|
+
await rm(root, { recursive: true, force: true });
|
|
200
|
+
}
|
|
201
|
+
});
|
|
@@ -79,12 +79,12 @@ test("card snapshots match the storage cache freshness window", async () => {
|
|
|
79
79
|
diskMemories = [];
|
|
80
80
|
nowMs += 1_001;
|
|
81
81
|
assert.equal((await inspected.readStoredCardSnapshot(storage, "alice", ownerKey)).cardsById.size, 1);
|
|
82
|
-
assert.equal(calls,
|
|
82
|
+
assert.equal(calls, 1);
|
|
83
83
|
assert.equal(diskReads, 1);
|
|
84
84
|
nowMs += 59_000;
|
|
85
85
|
|
|
86
86
|
assert.equal((await inspected.readStoredCardSnapshot(storage, "alice", ownerKey)).cardsById.size, 0);
|
|
87
|
-
assert.equal(calls,
|
|
87
|
+
assert.equal(calls, 2);
|
|
88
88
|
assert.equal(diskReads, 2);
|
|
89
89
|
});
|
|
90
90
|
|
|
@@ -45,6 +45,8 @@ test("grant creation checks cancellation again before commit", async () => {
|
|
|
45
45
|
readAllMemories: async () => [memory],
|
|
46
46
|
getCorpusScanVersion: () => 1,
|
|
47
47
|
hotCacheKeyId: () => "owner-cache",
|
|
48
|
+
hotCacheTtlMs: () => 60_000,
|
|
49
|
+
isHotCacheEnabled: () => true,
|
|
48
50
|
} as unknown as StorageManager;
|
|
49
51
|
const controller = new AbortController();
|
|
50
52
|
let committed = false;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtemp, rm, unlink } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { StorageManager } from "../storage.js";
|
|
8
|
+
import {
|
|
9
|
+
computeSupportPassportOwnerKey,
|
|
10
|
+
type StoredSupportPassportCard,
|
|
11
|
+
} from "./card-projection.js";
|
|
12
|
+
import {
|
|
13
|
+
commitSupportPassportGeneratedBatch,
|
|
14
|
+
isCommittedGeneratedCard,
|
|
15
|
+
persistSupportPassportGeneratedBatchMarker,
|
|
16
|
+
} from "./generated-batch.js";
|
|
17
|
+
|
|
18
|
+
test("generated card checks reuse a marker within one snapshot build", async () => {
|
|
19
|
+
StorageManager.clearAllStaticCaches();
|
|
20
|
+
const root = await mkdtemp(path.join(tmpdir(), "remnic-support-grant-marker-cache-"));
|
|
21
|
+
const storage = new StorageManager(path.join(root, "owner"));
|
|
22
|
+
await storage.ensureDirectories();
|
|
23
|
+
const batchId = "00000000-0000-4000-8000-000000000001";
|
|
24
|
+
const context = {
|
|
25
|
+
storage,
|
|
26
|
+
principal: "owner:alice",
|
|
27
|
+
namespace: "alice",
|
|
28
|
+
now: () => new Date("2026-08-13T12:00:00.000Z"),
|
|
29
|
+
requireOwnerLock: async () => undefined,
|
|
30
|
+
};
|
|
31
|
+
const owner = computeSupportPassportOwnerKey(context.principal);
|
|
32
|
+
const cards = [
|
|
33
|
+
{
|
|
34
|
+
namespace: "alice",
|
|
35
|
+
owner,
|
|
36
|
+
generatedBatchId: batchId,
|
|
37
|
+
generatedBatchSize: 2,
|
|
38
|
+
card: { cardId: "00000000-0000-4000-8000-000000000002", status: "pending_review" },
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
namespace: "alice",
|
|
42
|
+
owner,
|
|
43
|
+
generatedBatchId: batchId,
|
|
44
|
+
generatedBatchSize: 2,
|
|
45
|
+
card: { cardId: "00000000-0000-4000-8000-000000000003", status: "pending_review" },
|
|
46
|
+
},
|
|
47
|
+
] as StoredSupportPassportCard[];
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const marker = await persistSupportPassportGeneratedBatchMarker(context, batchId, cards.length);
|
|
51
|
+
await commitSupportPassportGeneratedBatch(context, marker, cards);
|
|
52
|
+
const markerCache = new Map();
|
|
53
|
+
|
|
54
|
+
assert.equal(await isCommittedGeneratedCard(storage, cards[0]!, markerCache), true);
|
|
55
|
+
await unlink(path.join(storage.dir, "state", "support-passport", "generated-batches", `${batchId}.json`));
|
|
56
|
+
assert.equal(await isCommittedGeneratedCard(storage, cards[1]!, markerCache), true);
|
|
57
|
+
assert.equal(await isCommittedGeneratedCard(storage, cards[1]!, new Map()), false);
|
|
58
|
+
} finally {
|
|
59
|
+
StorageManager.clearAllStaticCaches();
|
|
60
|
+
await rm(root, { recursive: true, force: true });
|
|
61
|
+
}
|
|
62
|
+
});
|