@remnic/core 10.0.92 → 10.0.93
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.js +331 -58
- package/dist/access-authorization-probe.js +2 -283
- package/dist/access-boundary.d.ts +1 -1
- package/dist/access-boundary.d.ts.map +1 -1
- package/dist/access-boundary.js +1 -282
- package/dist/access-cli.js +286 -302
- package/dist/access-cli.js.map +1 -1
- package/dist/access-coding-context-resolution.js +3 -3
- package/dist/access-extraction-force-flush.js +331 -58
- package/dist/access-http-briefing.js +331 -59
- package/dist/access-http-query.js +331 -60
- package/dist/access-http.js +329 -111
- package/dist/access-identity-continuity-surface.js +331 -58
- package/dist/access-lcm-surface.js +331 -58
- package/dist/access-mcp-frames.d.ts +20 -6
- package/dist/access-mcp-frames.d.ts.map +1 -1
- package/dist/access-mcp-frames.js +1 -1
- package/dist/access-mcp.d.ts +5 -0
- package/dist/access-mcp.d.ts.map +1 -1
- package/dist/access-mcp.js +329 -86
- package/dist/access-namespace-preflight.js +2 -282
- package/dist/access-observe-transcript.js +3 -3
- package/dist/access-observe-write-category.js +6 -6
- package/dist/access-observe-write-surface.js +331 -58
- package/dist/access-offline-manifest.js +12 -11
- package/dist/access-operations-batch.js +329 -63
- package/dist/access-operations.js +329 -72
- package/dist/access-recall-concurrency.js +331 -58
- package/dist/access-recall-diagnostics.js +5 -4
- package/dist/access-recall-response.js +331 -58
- package/dist/access-recall-surface.js +331 -58
- package/dist/access-schema.js +2 -2
- package/dist/access-service.js +334 -61
- package/dist/access-surface-catalog.js +6 -283
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-context-transform.js +1 -1
- package/dist/active-memory-bridge.js +3 -2
- package/dist/active-recall.js +35 -36
- package/dist/active-recall.js.map +1 -1
- package/dist/activity/journal-extract.d.ts +13 -1
- package/dist/activity/journal-extract.d.ts.map +1 -1
- package/dist/{auto-sync-3AZW7MXI.js → auto-sync-BO5YZKM6.js} +13 -15
- package/dist/{auto-sync-3AZW7MXI.js.map → auto-sync-BO5YZKM6.js.map} +1 -1
- package/dist/briefing.js +19 -19
- package/dist/buffer.js +16 -16
- package/dist/calibration.js +10 -10
- package/dist/{capsule-merge-2IIXFXN5.js → capsule-merge-6IDAYTKG.js} +6 -6
- package/dist/causal-consolidation.js +22 -22
- package/dist/{chunk-ATPWZ4F7.js → chunk-2LMA22UU.js} +5 -5
- package/dist/{chunk-RGUUBAT5.js → chunk-2WCLTWV2.js} +3 -3
- package/dist/{chunk-BQZEGOZ4.js → chunk-2YODOG2O.js} +2 -2
- package/dist/{chunk-WYG5UNDY.js → chunk-3AUDELZL.js} +2 -2
- package/dist/{chunk-N4KAE2NB.js → chunk-3CYOCE6U.js} +3 -3
- package/dist/{chunk-MB6ZJDN6.js → chunk-3FBAEPCU.js} +2 -2
- package/dist/{chunk-PIITB2YZ.js → chunk-3ZS3NUEI.js} +4 -4
- package/dist/{chunk-ANZZLRUX.js → chunk-4IVRQMZW.js} +2 -2
- package/dist/{chunk-63E37JCN.js → chunk-4RAJT5L2.js} +4 -4
- package/dist/{chunk-45VUHDV2.js → chunk-5CO5G4MN.js} +2 -2
- package/dist/{chunk-BRLXOZLM.js → chunk-5YT2IIFX.js} +729 -17
- package/dist/chunk-5YT2IIFX.js.map +1 -0
- package/dist/{chunk-E4IRW2FG.js → chunk-622V2AZA.js} +233 -40
- package/dist/chunk-622V2AZA.js.map +1 -0
- package/dist/{chunk-LUJCQQ3L.js → chunk-65HWAQ4F.js} +2 -2
- package/dist/{chunk-T67LUYCR.js → chunk-77NTFICT.js} +5 -5
- package/dist/chunk-A2WFUMUN.js +86 -0
- package/dist/chunk-A2WFUMUN.js.map +1 -0
- package/dist/{chunk-UE6RRXMF.js → chunk-A4F7OIC5.js} +2 -2
- package/dist/chunk-AHA5VQGP.js +117 -0
- package/dist/chunk-AHA5VQGP.js.map +1 -0
- package/dist/{chunk-SUSW74WU.js → chunk-ATG2PQ4V.js} +56 -5
- package/dist/chunk-ATG2PQ4V.js.map +1 -0
- package/dist/{chunk-U6UNF5ZS.js → chunk-B33YWUJV.js} +3 -3
- package/dist/{chunk-RRJBDXAQ.js → chunk-B377OKUP.js} +2 -2
- package/dist/{chunk-IUKYFTGW.js → chunk-B3DR2JBZ.js} +140 -125
- package/dist/chunk-B3DR2JBZ.js.map +1 -0
- package/dist/{chunk-ETZLQOUR.js → chunk-BGHCSLE5.js} +2 -2
- package/dist/{chunk-YYOMGOHB.js → chunk-BOK6BN5M.js} +2 -2
- package/dist/{chunk-OH7QLI6W.js → chunk-BPPUH5JM.js} +5 -5
- package/dist/{chunk-4J363OAA.js → chunk-BTI4C4MA.js} +2 -2
- package/dist/{chunk-O6ZSWHVR.js → chunk-C2KE5DMD.js} +4 -4
- package/dist/{chunk-47F6WWLQ.js → chunk-C73N66PR.js} +2 -2
- package/dist/{chunk-E3A7ELSG.js → chunk-CGJYL633.js} +2 -2
- package/dist/{chunk-WRT7GJ2D.js → chunk-CIWMTNHW.js} +4 -4
- package/dist/{chunk-O55EKIEC.js → chunk-D3ZKHLN2.js} +2 -2
- package/dist/{chunk-CD4BCCNP.js → chunk-DJ7WVEWX.js} +2 -2
- package/dist/{chunk-PWXPRVLK.js → chunk-EFL24KOI.js} +2 -2
- package/dist/{chunk-4CJTQHEG.js → chunk-ESLOLCD4.js} +3 -3
- package/dist/{chunk-6F42QONS.js → chunk-ETBVCRVL.js} +4 -4
- package/dist/{chunk-VILPVT4S.js → chunk-EZ47HOJ6.js} +4 -4
- package/dist/{chunk-EFYN5I7Z.js → chunk-F4QFU5IZ.js} +2 -2
- package/dist/{chunk-T6OZW2WR.js → chunk-FA7SLQ5X.js} +7 -1
- package/dist/chunk-FA7SLQ5X.js.map +1 -0
- package/dist/{chunk-7W3RXOIG.js → chunk-FMKILENU.js} +14 -14
- package/dist/{chunk-YLXETSAH.js → chunk-GCZJ7XRI.js} +2 -2
- package/dist/{chunk-5HFXGXE5.js → chunk-GQXOBKPL.js} +7 -9
- package/dist/{chunk-5HFXGXE5.js.map → chunk-GQXOBKPL.js.map} +1 -1
- package/dist/{chunk-UDKWFDV2.js → chunk-HLUHEMN4.js} +27 -3
- package/dist/chunk-HLUHEMN4.js.map +1 -0
- package/dist/{chunk-QFEDITXM.js → chunk-JNDMIAJ4.js} +2 -2
- package/dist/chunk-JTSSSR4L.js +359 -0
- package/dist/chunk-JTSSSR4L.js.map +1 -0
- package/dist/{chunk-5KBIOI4G.js → chunk-JVBAPAWZ.js} +217 -13
- package/dist/chunk-JVBAPAWZ.js.map +1 -0
- package/dist/{chunk-E5PPE4VA.js → chunk-K2VRKCPA.js} +2 -2
- package/dist/{chunk-MKRYQROF.js → chunk-LGEHAQHK.js} +4 -4
- package/dist/{chunk-7HTUYFR3.js → chunk-LLPMMMWG.js} +27 -10
- package/dist/chunk-LLPMMMWG.js.map +1 -0
- package/dist/{chunk-XYTQJD7F.js → chunk-LMJVP5CM.js} +2 -2
- package/dist/{chunk-FQKBJJYB.js → chunk-LPV72KRQ.js} +2 -2
- package/dist/{chunk-2LCRG2YV.js → chunk-M2XR6B6V.js} +2 -2
- package/dist/{chunk-P4WNBSTW.js → chunk-MH632KPO.js} +2 -2
- package/dist/{chunk-EQ4U73UH.js → chunk-MLAMXQAW.js} +14 -2
- package/dist/chunk-MLAMXQAW.js.map +1 -0
- package/dist/{chunk-ZU5JDLOH.js → chunk-MMRTPH3J.js} +2 -2
- package/dist/{chunk-PMJFRDJI.js → chunk-MUX7IL77.js} +2 -2
- package/dist/{chunk-HF3BKUVV.js → chunk-NAQ4A5IX.js} +7 -7
- package/dist/{chunk-TVIVY7KU.js → chunk-NED7IR7B.js} +4 -4
- package/dist/{chunk-6F5MJFXY.js → chunk-NHVAX7MH.js} +2 -2
- package/dist/{chunk-TG3R2SID.js → chunk-NMGHGTVR.js} +5 -5
- package/dist/{chunk-YCNIGRRF.js → chunk-NS7U4ZYU.js} +13 -117
- package/dist/chunk-NS7U4ZYU.js.map +1 -0
- package/dist/{chunk-NGHDW6UU.js → chunk-NUQEREIP.js} +2 -2
- package/dist/{chunk-5IIQU4JZ.js → chunk-OB72KNMF.js} +4 -4
- package/dist/{chunk-JV4S6VDH.js → chunk-OJXJWNFY.js} +2 -2
- package/dist/{chunk-QJPJXCS4.js → chunk-P3WRNWWM.js} +157 -4
- package/dist/chunk-P3WRNWWM.js.map +1 -0
- package/dist/{chunk-E6M225BJ.js → chunk-PC5DRBGD.js} +18 -8
- package/dist/chunk-PC5DRBGD.js.map +1 -0
- package/dist/{chunk-Z7JIZ42P.js → chunk-PRAUN6PN.js} +3 -3
- package/dist/{chunk-RWGRSKN5.js → chunk-PT4DZDJA.js} +2 -2
- package/dist/{chunk-7EPY3IQL.js → chunk-PZJQ46DH.js} +2 -2
- package/dist/{chunk-MXHMNKWG.js → chunk-QDESY76F.js} +3 -3
- package/dist/{chunk-I6QGI65T.js → chunk-QRMG4HK4.js} +3 -3
- package/dist/{chunk-ZU3NJNO7.js → chunk-QSF5SJAM.js} +4 -4
- package/dist/{chunk-5IYOKDM4.js → chunk-R23JWJD3.js} +5 -5
- package/dist/{chunk-RHQLIHMB.js → chunk-R4BBGPI2.js} +218 -218
- package/dist/chunk-R4BBGPI2.js.map +1 -0
- package/dist/{chunk-7GLJMDEQ.js → chunk-RM2YUFH6.js} +6 -6
- package/dist/{chunk-IPT2OY4B.js → chunk-RQFXZYTF.js} +35 -33
- package/dist/chunk-RQFXZYTF.js.map +1 -0
- package/dist/{chunk-AP6NCP3V.js → chunk-S3S72HRM.js} +8 -6
- package/dist/chunk-S3S72HRM.js.map +1 -0
- package/dist/{chunk-RIMK327Y.js → chunk-SGQPR5SX.js} +7 -7
- package/dist/{chunk-ZGFLIK5H.js → chunk-SPAKBUJY.js} +2 -2
- package/dist/{chunk-ER7XANP3.js → chunk-SQ3GEIEL.js} +4 -4
- package/dist/{chunk-BLJT6JP2.js → chunk-SVT5IA2H.js} +2 -2
- package/dist/{chunk-HVX4LPNY.js → chunk-T4KY6UYF.js} +5 -3
- package/dist/{chunk-HVX4LPNY.js.map → chunk-T4KY6UYF.js.map} +1 -1
- package/dist/{chunk-GVGAO3WV.js → chunk-TJ4F6H4H.js} +3 -3
- package/dist/{chunk-R6HMAIKM.js → chunk-TOSPNM4T.js} +2 -2
- package/dist/{chunk-HH5LY6RR.js → chunk-TRQE3RYR.js} +5 -5
- package/dist/{chunk-RMXS4I66.js → chunk-TVDJEUV5.js} +2 -2
- package/dist/{chunk-PLCYKAWH.js → chunk-TYRWSE4B.js} +2 -2
- package/dist/{chunk-B4QYQFGY.js → chunk-UBEKET2V.js} +383 -36
- package/dist/chunk-UBEKET2V.js.map +1 -0
- package/dist/{chunk-IQWUACVG.js → chunk-UHMHJX5G.js} +3 -3
- package/dist/{chunk-YODUDA6R.js → chunk-UN4RVUG2.js} +22 -22
- package/dist/{chunk-E6Z4FBX6.js → chunk-V2QSNFCT.js} +2 -2
- package/dist/{chunk-NBH6K6G5.js → chunk-V65H7MI6.js} +345 -16
- package/dist/chunk-V65H7MI6.js.map +1 -0
- package/dist/chunk-W54Z2MOP.js +4500 -0
- package/dist/chunk-W54Z2MOP.js.map +1 -0
- package/dist/{chunk-E7YZXWIS.js → chunk-WKSQTMU7.js} +2 -2
- package/dist/{chunk-RIZ5NUNN.js → chunk-WQ4XKIOX.js} +4 -4
- package/dist/{chunk-FSO3G4JJ.js → chunk-WQURALGV.js} +4995 -2747
- package/dist/chunk-WQURALGV.js.map +1 -0
- package/dist/{chunk-QEYTRFR3.js → chunk-X26YOFGS.js} +2 -2
- package/dist/{chunk-ITHZFHST.js → chunk-X2BVB74H.js} +2 -2
- package/dist/{chunk-XYKBWTWI.js → chunk-XIYLWEXC.js} +2 -2
- package/dist/{chunk-IBJ6U753.js → chunk-XKZBZOKV.js} +4 -4
- package/dist/{chunk-65AUYLU3.js → chunk-XPXHQI6N.js} +2 -2
- package/dist/{chunk-EXHXJETE.js → chunk-XRDYRWZI.js} +2 -2
- package/dist/chunk-XRWWCW4H.js +102 -0
- package/dist/chunk-XRWWCW4H.js.map +1 -0
- package/dist/chunk-YKJBSMMP.js +67137 -0
- package/dist/chunk-YKJBSMMP.js.map +1 -0
- package/dist/{chunk-2JSDPUKD.js → chunk-YMFPFXEX.js} +2 -2
- package/dist/{chunk-645WOU5I.js → chunk-ZDZJNXL6.js} +4 -4
- package/dist/{chunk-6TCXIRP5.js → chunk-ZKE4CO6B.js} +4 -4
- package/dist/{chunk-FNXQZGUS.js → chunk-ZSE774RV.js} +2 -2
- package/dist/cli.js +302 -184
- package/dist/coding/export-okf-codegraph.js +28 -28
- package/dist/compounding/engine.js +22 -23
- package/dist/config.js +35 -36
- package/dist/connectors/codex-materialize-runner.js +19 -19
- package/dist/connectors/index.js +20 -20
- package/dist/connectors/live/google-drive.d.ts +2 -0
- package/dist/connectors/live/google-drive.d.ts.map +1 -1
- package/dist/consolidation-provenance-check.js +4 -4
- package/dist/consolidation-undo.js +5 -5
- package/dist/contradiction/index.js +7 -6
- package/dist/{contradiction-scan-P2MHYFVG.js → contradiction-scan-IBPYGQZF.js} +4 -3
- package/dist/convergence-refresh.js +21 -21
- package/dist/corpus-watermark.js +22 -22
- package/dist/deep-recall-http-glue.js +2 -2
- package/dist/deep-recall-policy-llm.js +4 -4
- package/dist/direct-answer-wiring.js +2 -2
- package/dist/embedding-fallback.js +1 -1
- package/dist/entity-retrieval.js +20 -20
- package/dist/eval-trajectory-store.js +2 -2
- package/dist/eval-trajectory.js +2 -2
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.d.ts +3 -2
- package/dist/explicit-capture.d.ts.map +1 -1
- package/dist/explicit-capture.js +530 -6
- package/dist/explicit-cue-recall.js +2 -2
- package/dist/external-wiki-access.js +2 -283
- package/dist/extraction-faithfulness.js +4 -4
- package/dist/extraction-judge.js +5 -5
- package/dist/extraction-normalization.js +1 -1
- package/dist/extraction-quote-grounding.d.ts.map +1 -1
- package/dist/extraction-quote-grounding.js +1 -1
- package/dist/extraction-source-grounding-rules.js +2 -2
- package/dist/extraction-source-grounding.js +3 -3
- package/dist/extraction.js +42 -43
- package/dist/fallback-llm-codex-subscription.js +2 -2
- package/dist/fallback-llm.js +3 -3
- package/dist/{first-start-migration-QFI7B5SH.js → first-start-migration-FILJH3RO.js} +6 -5
- package/dist/{first-start-migration-QFI7B5SH.js.map → first-start-migration-FILJH3RO.js.map} +1 -1
- package/dist/focused-list-recall.js +2 -2
- package/dist/harmonic-retrieval.js +19 -19
- package/dist/index.js +772 -793
- package/dist/lcm/index.js +2 -2
- package/dist/live-connectors-runner.js +2 -3
- package/dist/local-llm-config.js +2 -2
- package/dist/local-llm-helpers.d.ts +4 -0
- package/dist/local-llm-helpers.d.ts.map +1 -1
- package/dist/local-llm-helpers.js +3 -1
- package/dist/local-llm.d.ts +4 -1
- package/dist/local-llm.d.ts.map +1 -1
- package/dist/local-llm.js +4 -4
- package/dist/location/index.js +26 -28
- package/dist/maintenance/memory-governance.js +19 -19
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +19 -19
- package/dist/maintenance/rebuild-memory-projection-runner.js +21 -21
- package/dist/maintenance/rebuild-memory-projection.js +20 -20
- package/dist/memory-browse-operations.js +2 -283
- package/dist/memory-browse.js +8 -7
- package/dist/memory-controller.js +2 -2
- package/dist/memory-subject.js +2 -2
- package/dist/namespaces/migrate.js +20 -20
- package/dist/namespaces/search.js +5 -5
- package/dist/namespaces/storage.js +19 -19
- package/dist/offline-sync-changeset-generations.js +1 -1
- package/dist/offline-sync-embedding-generation.js +1 -1
- package/dist/offline-sync-impression-drain.js +6 -6
- package/dist/offline-sync-snapshot-stream.js +1 -1
- package/dist/offline-sync.js +1 -1
- package/dist/operator-doctor-corpus.js +23 -23
- package/dist/operator-toolkit.js +57 -60
- package/dist/orchestration/backlog-scheduler.js +277 -293
- package/dist/orchestration/backlog-scheduler.js.map +1 -1
- package/dist/orchestration/buffer-drain-runner.js +277 -293
- package/dist/orchestration/extraction-run.d.ts +4 -0
- package/dist/orchestration/extraction-run.d.ts.map +1 -1
- package/dist/orchestration/fact-dedup-window.d.ts +1 -1
- package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +38 -41
- package/dist/orchestrator.d.ts +9 -11
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +289 -306
- package/dist/page-versioning.js +3 -3
- package/dist/qmd-daemon-session.js +2 -2
- package/dist/qmd.js +3 -3
- package/dist/recall-explain-renderer.js +1 -1
- package/dist/recall-navigation-operations.js +2 -283
- package/dist/recall-navigation.js +331 -58
- package/dist/recall-planner-llm.js +3 -3
- package/dist/recall-state-view-admission.js +6 -6
- package/dist/recall-why-service.js +10 -9
- package/dist/recall-why.js +4 -3
- package/dist/recall-xray-cli.js +2 -2
- package/dist/recall-xray-renderer.js +1 -1
- package/dist/reconcile/manifest.js +3 -3
- package/dist/{resolution-N7H3F2E4.js → resolution-OVVIF7ZY.js} +8 -7
- package/dist/response-guidance-recall.js +2 -2
- package/dist/resume-bundles.js +39 -40
- package/dist/salvage-envelope.js +4 -4
- package/dist/search/factory.js +4 -4
- package/dist/search/index.js +4 -4
- package/dist/semantic-consolidation.js +22 -22
- package/dist/semantic-rule-promotion.js +19 -19
- package/dist/semantic-rule-verifier.js +19 -19
- package/dist/shared-context/manager.js +2 -3
- package/dist/source-agent-qualifier.js +31 -32
- package/dist/standup/build.js +19 -19
- package/dist/storage/content-hash-index.d.ts +10 -0
- package/dist/storage/content-hash-index.d.ts.map +1 -1
- package/dist/storage.js +18 -18
- package/dist/subject-config.js +3 -3
- package/dist/summarizer.d.ts +4 -1
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +6 -6
- package/dist/support-passport/index.js +53 -297
- package/dist/targeted-fact-recall.js +2 -2
- package/dist/tier-routing.js +3 -2
- package/dist/{tier-stats-FQGQSFET.js → tier-stats-FXEUJN73.js} +6 -5
- package/dist/{tier-stats-FQGQSFET.js.map → tier-stats-FXEUJN73.js.map} +1 -1
- package/dist/transcript.js +2 -2
- package/dist/transfer/capsule-import.js +4 -4
- package/dist/transfer/export-okf.js +19 -19
- package/dist/verified-recall.js +19 -19
- package/dist/wearables/memory-gen.d.ts +5 -1
- package/dist/wearables/memory-gen.d.ts.map +1 -1
- package/dist/wearables/service.d.ts.map +1 -1
- package/dist/who-knows-cli.js +20 -20
- package/dist/who-knows.js +19 -19
- package/dist/write-envelope.js +3 -3
- package/package.json +2 -2
- package/src/access-boundary.ts +5 -1
- package/src/access-category-alias.test.ts +5 -0
- package/src/access-mcp-frames.test.ts +60 -5
- package/src/access-mcp-frames.ts +72 -18
- package/src/access-mcp.test.ts +66 -2
- package/src/access-mcp.ts +19 -13
- package/src/access-service-coding-write.test.ts +7 -0
- package/src/activity/journal-extract-storage.test.ts +31 -0
- package/src/activity/journal-extract.test.ts +1 -0
- package/src/activity/journal-extract.ts +24 -3
- package/src/connectors/live/google-drive.test.ts +39 -0
- package/src/connectors/live/google-drive.ts +41 -12
- package/src/explicit-capture.test.ts +109 -17
- package/src/explicit-capture.ts +90 -37
- package/src/extraction-quote-grounding.test.ts +20 -0
- package/src/extraction-quote-grounding.ts +21 -11
- package/src/fact-dedup-window.test.ts +37 -0
- package/src/local-llm-helpers.ts +12 -0
- package/src/local-llm-shutdown.test.ts +194 -0
- package/src/local-llm.ts +32 -30
- package/src/orchestration/extraction-run.ts +12 -3
- package/src/orchestration/fact-dedup-window.ts +5 -1
- package/src/orchestrator-connector-dedup.test.ts +9 -5
- package/src/orchestrator.ts +47 -50
- package/src/storage/content-hash-index.ts +27 -2
- package/src/storage-fact-hash-publish-failure.test.ts +87 -0
- package/src/summarizer-shutdown.test.ts +195 -0
- package/src/summarizer.ts +15 -2
- package/src/wearables/memory-gen.test.ts +20 -0
- package/src/wearables/memory-gen.ts +14 -7
- package/src/wearables/service.ts +14 -1
- package/src/wearables/storage-io.test.ts +25 -0
- package/dist/chunk-2FGLEF7C.js +0 -61
- package/dist/chunk-2FGLEF7C.js.map +0 -1
- package/dist/chunk-3RLK65ZK.js +0 -203
- package/dist/chunk-3RLK65ZK.js.map +0 -1
- package/dist/chunk-4WR2OD7K.js +0 -43
- package/dist/chunk-4WR2OD7K.js.map +0 -1
- package/dist/chunk-5KBIOI4G.js.map +0 -1
- package/dist/chunk-5M5M22GW.js +0 -346
- package/dist/chunk-5M5M22GW.js.map +0 -1
- package/dist/chunk-7HTUYFR3.js.map +0 -1
- package/dist/chunk-AP6NCP3V.js.map +0 -1
- package/dist/chunk-B4QYQFGY.js.map +0 -1
- package/dist/chunk-BJRTMNCB.js +0 -1919
- package/dist/chunk-BJRTMNCB.js.map +0 -1
- package/dist/chunk-BRLXOZLM.js.map +0 -1
- package/dist/chunk-BSACVS6R.js +0 -621
- package/dist/chunk-BSACVS6R.js.map +0 -1
- package/dist/chunk-DKAA2IHQ.js +0 -3644
- package/dist/chunk-DKAA2IHQ.js.map +0 -1
- package/dist/chunk-E4IRW2FG.js.map +0 -1
- package/dist/chunk-E6M225BJ.js.map +0 -1
- package/dist/chunk-EQ4U73UH.js.map +0 -1
- package/dist/chunk-ETSJD5IV.js +0 -361
- package/dist/chunk-ETSJD5IV.js.map +0 -1
- package/dist/chunk-F55YQP7N.js +0 -336
- package/dist/chunk-F55YQP7N.js.map +0 -1
- package/dist/chunk-FHGWKODN.js +0 -2300
- package/dist/chunk-FHGWKODN.js.map +0 -1
- package/dist/chunk-FLRP63GU.js +0 -32219
- package/dist/chunk-FLRP63GU.js.map +0 -1
- package/dist/chunk-FSO3G4JJ.js.map +0 -1
- package/dist/chunk-ID6JGIMD.js +0 -25
- package/dist/chunk-ID6JGIMD.js.map +0 -1
- package/dist/chunk-IGLJNHOX.js +0 -62
- package/dist/chunk-IGLJNHOX.js.map +0 -1
- package/dist/chunk-IPT2OY4B.js.map +0 -1
- package/dist/chunk-IUKYFTGW.js.map +0 -1
- package/dist/chunk-NBH6K6G5.js.map +0 -1
- package/dist/chunk-NTRUKAAE.js +0 -223
- package/dist/chunk-NTRUKAAE.js.map +0 -1
- package/dist/chunk-OGXXGHW6.js +0 -223
- package/dist/chunk-OGXXGHW6.js.map +0 -1
- package/dist/chunk-PPCLZ4JU.js +0 -28
- package/dist/chunk-PPCLZ4JU.js.map +0 -1
- package/dist/chunk-QJPJXCS4.js.map +0 -1
- package/dist/chunk-R2LBF4HH.js +0 -729
- package/dist/chunk-R2LBF4HH.js.map +0 -1
- package/dist/chunk-RHQLIHMB.js.map +0 -1
- package/dist/chunk-RRTVGD3J.js +0 -792
- package/dist/chunk-RRTVGD3J.js.map +0 -1
- package/dist/chunk-SUSW74WU.js.map +0 -1
- package/dist/chunk-T6OZW2WR.js.map +0 -1
- package/dist/chunk-UDKWFDV2.js.map +0 -1
- package/dist/chunk-V7IZKJI3.js +0 -22
- package/dist/chunk-V7IZKJI3.js.map +0 -1
- package/dist/chunk-X562NQFA.js +0 -10144
- package/dist/chunk-X562NQFA.js.map +0 -1
- package/dist/chunk-XDSCJOH7.js +0 -3991
- package/dist/chunk-XDSCJOH7.js.map +0 -1
- package/dist/chunk-YCNIGRRF.js.map +0 -1
- package/dist/chunk-ZTTCDQ4I.js +0 -18796
- package/dist/chunk-ZTTCDQ4I.js.map +0 -1
- /package/dist/{capsule-merge-2IIXFXN5.js.map → capsule-merge-6IDAYTKG.js.map} +0 -0
- /package/dist/{chunk-ATPWZ4F7.js.map → chunk-2LMA22UU.js.map} +0 -0
- /package/dist/{chunk-RGUUBAT5.js.map → chunk-2WCLTWV2.js.map} +0 -0
- /package/dist/{chunk-BQZEGOZ4.js.map → chunk-2YODOG2O.js.map} +0 -0
- /package/dist/{chunk-WYG5UNDY.js.map → chunk-3AUDELZL.js.map} +0 -0
- /package/dist/{chunk-N4KAE2NB.js.map → chunk-3CYOCE6U.js.map} +0 -0
- /package/dist/{chunk-MB6ZJDN6.js.map → chunk-3FBAEPCU.js.map} +0 -0
- /package/dist/{chunk-PIITB2YZ.js.map → chunk-3ZS3NUEI.js.map} +0 -0
- /package/dist/{chunk-ANZZLRUX.js.map → chunk-4IVRQMZW.js.map} +0 -0
- /package/dist/{chunk-63E37JCN.js.map → chunk-4RAJT5L2.js.map} +0 -0
- /package/dist/{chunk-45VUHDV2.js.map → chunk-5CO5G4MN.js.map} +0 -0
- /package/dist/{chunk-LUJCQQ3L.js.map → chunk-65HWAQ4F.js.map} +0 -0
- /package/dist/{chunk-T67LUYCR.js.map → chunk-77NTFICT.js.map} +0 -0
- /package/dist/{chunk-UE6RRXMF.js.map → chunk-A4F7OIC5.js.map} +0 -0
- /package/dist/{chunk-U6UNF5ZS.js.map → chunk-B33YWUJV.js.map} +0 -0
- /package/dist/{chunk-RRJBDXAQ.js.map → chunk-B377OKUP.js.map} +0 -0
- /package/dist/{chunk-ETZLQOUR.js.map → chunk-BGHCSLE5.js.map} +0 -0
- /package/dist/{chunk-YYOMGOHB.js.map → chunk-BOK6BN5M.js.map} +0 -0
- /package/dist/{chunk-OH7QLI6W.js.map → chunk-BPPUH5JM.js.map} +0 -0
- /package/dist/{chunk-4J363OAA.js.map → chunk-BTI4C4MA.js.map} +0 -0
- /package/dist/{chunk-O6ZSWHVR.js.map → chunk-C2KE5DMD.js.map} +0 -0
- /package/dist/{chunk-47F6WWLQ.js.map → chunk-C73N66PR.js.map} +0 -0
- /package/dist/{chunk-E3A7ELSG.js.map → chunk-CGJYL633.js.map} +0 -0
- /package/dist/{chunk-WRT7GJ2D.js.map → chunk-CIWMTNHW.js.map} +0 -0
- /package/dist/{chunk-O55EKIEC.js.map → chunk-D3ZKHLN2.js.map} +0 -0
- /package/dist/{chunk-CD4BCCNP.js.map → chunk-DJ7WVEWX.js.map} +0 -0
- /package/dist/{chunk-PWXPRVLK.js.map → chunk-EFL24KOI.js.map} +0 -0
- /package/dist/{chunk-4CJTQHEG.js.map → chunk-ESLOLCD4.js.map} +0 -0
- /package/dist/{chunk-6F42QONS.js.map → chunk-ETBVCRVL.js.map} +0 -0
- /package/dist/{chunk-VILPVT4S.js.map → chunk-EZ47HOJ6.js.map} +0 -0
- /package/dist/{chunk-EFYN5I7Z.js.map → chunk-F4QFU5IZ.js.map} +0 -0
- /package/dist/{chunk-7W3RXOIG.js.map → chunk-FMKILENU.js.map} +0 -0
- /package/dist/{chunk-YLXETSAH.js.map → chunk-GCZJ7XRI.js.map} +0 -0
- /package/dist/{chunk-QFEDITXM.js.map → chunk-JNDMIAJ4.js.map} +0 -0
- /package/dist/{chunk-E5PPE4VA.js.map → chunk-K2VRKCPA.js.map} +0 -0
- /package/dist/{chunk-MKRYQROF.js.map → chunk-LGEHAQHK.js.map} +0 -0
- /package/dist/{chunk-XYTQJD7F.js.map → chunk-LMJVP5CM.js.map} +0 -0
- /package/dist/{chunk-FQKBJJYB.js.map → chunk-LPV72KRQ.js.map} +0 -0
- /package/dist/{chunk-2LCRG2YV.js.map → chunk-M2XR6B6V.js.map} +0 -0
- /package/dist/{chunk-P4WNBSTW.js.map → chunk-MH632KPO.js.map} +0 -0
- /package/dist/{chunk-ZU5JDLOH.js.map → chunk-MMRTPH3J.js.map} +0 -0
- /package/dist/{chunk-PMJFRDJI.js.map → chunk-MUX7IL77.js.map} +0 -0
- /package/dist/{chunk-HF3BKUVV.js.map → chunk-NAQ4A5IX.js.map} +0 -0
- /package/dist/{chunk-TVIVY7KU.js.map → chunk-NED7IR7B.js.map} +0 -0
- /package/dist/{chunk-6F5MJFXY.js.map → chunk-NHVAX7MH.js.map} +0 -0
- /package/dist/{chunk-TG3R2SID.js.map → chunk-NMGHGTVR.js.map} +0 -0
- /package/dist/{chunk-NGHDW6UU.js.map → chunk-NUQEREIP.js.map} +0 -0
- /package/dist/{chunk-5IIQU4JZ.js.map → chunk-OB72KNMF.js.map} +0 -0
- /package/dist/{chunk-JV4S6VDH.js.map → chunk-OJXJWNFY.js.map} +0 -0
- /package/dist/{chunk-Z7JIZ42P.js.map → chunk-PRAUN6PN.js.map} +0 -0
- /package/dist/{chunk-RWGRSKN5.js.map → chunk-PT4DZDJA.js.map} +0 -0
- /package/dist/{chunk-7EPY3IQL.js.map → chunk-PZJQ46DH.js.map} +0 -0
- /package/dist/{chunk-MXHMNKWG.js.map → chunk-QDESY76F.js.map} +0 -0
- /package/dist/{chunk-I6QGI65T.js.map → chunk-QRMG4HK4.js.map} +0 -0
- /package/dist/{chunk-ZU3NJNO7.js.map → chunk-QSF5SJAM.js.map} +0 -0
- /package/dist/{chunk-5IYOKDM4.js.map → chunk-R23JWJD3.js.map} +0 -0
- /package/dist/{chunk-7GLJMDEQ.js.map → chunk-RM2YUFH6.js.map} +0 -0
- /package/dist/{chunk-RIMK327Y.js.map → chunk-SGQPR5SX.js.map} +0 -0
- /package/dist/{chunk-ZGFLIK5H.js.map → chunk-SPAKBUJY.js.map} +0 -0
- /package/dist/{chunk-ER7XANP3.js.map → chunk-SQ3GEIEL.js.map} +0 -0
- /package/dist/{chunk-BLJT6JP2.js.map → chunk-SVT5IA2H.js.map} +0 -0
- /package/dist/{chunk-GVGAO3WV.js.map → chunk-TJ4F6H4H.js.map} +0 -0
- /package/dist/{chunk-R6HMAIKM.js.map → chunk-TOSPNM4T.js.map} +0 -0
- /package/dist/{chunk-HH5LY6RR.js.map → chunk-TRQE3RYR.js.map} +0 -0
- /package/dist/{chunk-RMXS4I66.js.map → chunk-TVDJEUV5.js.map} +0 -0
- /package/dist/{chunk-PLCYKAWH.js.map → chunk-TYRWSE4B.js.map} +0 -0
- /package/dist/{chunk-IQWUACVG.js.map → chunk-UHMHJX5G.js.map} +0 -0
- /package/dist/{chunk-YODUDA6R.js.map → chunk-UN4RVUG2.js.map} +0 -0
- /package/dist/{chunk-E6Z4FBX6.js.map → chunk-V2QSNFCT.js.map} +0 -0
- /package/dist/{chunk-E7YZXWIS.js.map → chunk-WKSQTMU7.js.map} +0 -0
- /package/dist/{chunk-RIZ5NUNN.js.map → chunk-WQ4XKIOX.js.map} +0 -0
- /package/dist/{chunk-QEYTRFR3.js.map → chunk-X26YOFGS.js.map} +0 -0
- /package/dist/{chunk-ITHZFHST.js.map → chunk-X2BVB74H.js.map} +0 -0
- /package/dist/{chunk-XYKBWTWI.js.map → chunk-XIYLWEXC.js.map} +0 -0
- /package/dist/{chunk-IBJ6U753.js.map → chunk-XKZBZOKV.js.map} +0 -0
- /package/dist/{chunk-65AUYLU3.js.map → chunk-XPXHQI6N.js.map} +0 -0
- /package/dist/{chunk-EXHXJETE.js.map → chunk-XRDYRWZI.js.map} +0 -0
- /package/dist/{chunk-2JSDPUKD.js.map → chunk-YMFPFXEX.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-ZDZJNXL6.js.map} +0 -0
- /package/dist/{chunk-6TCXIRP5.js.map → chunk-ZKE4CO6B.js.map} +0 -0
- /package/dist/{chunk-FNXQZGUS.js.map → chunk-ZSE774RV.js.map} +0 -0
- /package/dist/{contradiction-scan-P2MHYFVG.js.map → contradiction-scan-IBPYGQZF.js.map} +0 -0
- /package/dist/{resolution-N7H3F2E4.js.map → resolution-OVVIF7ZY.js.map} +0 -0
package/src/access-mcp-frames.ts
CHANGED
|
@@ -1,28 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Framing for the MCP stdio transport.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The MCP spec sends one JSON message per line. Some clients send a
|
|
5
|
+
* `Content-Length` header instead. `push` appends raw bytes. `next` returns one
|
|
6
|
+
* frame at a time and `null` when the buffered bytes hold no complete frame
|
|
7
|
+
* yet. A buffer whose first non-blank byte is `{` or `[` is read up to the next
|
|
8
|
+
* newline. Any other buffer is a header block. Each form is bounded to
|
|
9
|
+
* MCP_STDIO_MAX_FRAME_BYTES, so a peer cannot grow the buffer without limit.
|
|
8
10
|
*/
|
|
9
11
|
export const MCP_STDIO_MAX_FRAME_BYTES = 131072;
|
|
10
12
|
|
|
11
|
-
export type
|
|
13
|
+
export type McpFrameForm = "header" | "line";
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
export type McpFrame =
|
|
16
|
+
| { kind: "body"; text: string; form: McpFrameForm }
|
|
17
|
+
| { kind: "too-large"; form: McpFrameForm };
|
|
18
|
+
|
|
19
|
+
const HEADER_PREFIX = "content-length:";
|
|
14
20
|
|
|
15
21
|
export class McpFrameReader {
|
|
16
22
|
private buffer = Buffer.alloc(0);
|
|
17
|
-
/** Body bytes of a refused oversized frame that have not arrived yet. */
|
|
23
|
+
/** Body bytes of a refused oversized header frame that have not arrived yet. */
|
|
18
24
|
private skipBytes = 0;
|
|
25
|
+
/** True while the rest of a refused oversized line has not arrived yet. */
|
|
26
|
+
private skipLine = false;
|
|
19
27
|
/** Buffer length when `next` last stopped on an incomplete frame. */
|
|
20
28
|
private waitingBytes = 0;
|
|
21
29
|
|
|
22
30
|
push(chunk: Buffer): void {
|
|
23
31
|
const dropped = Math.min(this.skipBytes, chunk.length);
|
|
24
32
|
this.skipBytes -= dropped;
|
|
25
|
-
|
|
33
|
+
let kept = chunk.subarray(dropped);
|
|
34
|
+
if (this.skipLine) {
|
|
35
|
+
const newline = kept.indexOf("\n");
|
|
36
|
+
this.skipLine = newline < 0;
|
|
37
|
+
kept = kept.subarray(newline < 0 ? kept.length : newline + 1);
|
|
38
|
+
}
|
|
26
39
|
if (kept.length > 0) this.buffer = Buffer.concat([this.buffer, kept]);
|
|
27
40
|
}
|
|
28
41
|
|
|
@@ -33,15 +46,33 @@ export class McpFrameReader {
|
|
|
33
46
|
|
|
34
47
|
next(): McpFrame | null {
|
|
35
48
|
this.waitingBytes = 0;
|
|
49
|
+
// A blank line carries no message. Skip it and read the next frame, so a
|
|
50
|
+
// run of blank lines cannot stall the frames that follow in this buffer.
|
|
51
|
+
for (;;) {
|
|
52
|
+
const frame = this.isHeaderFrame() ? this.nextHeader() : this.nextLine();
|
|
53
|
+
if (frame !== "blank") return frame;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A line frame starts with `{` or `[`, as every JSON-RPC message does. Any other first byte starts a header block. */
|
|
58
|
+
private isHeaderFrame(): boolean {
|
|
59
|
+
const first = this.buffer.find((byte) => byte > 0x20);
|
|
60
|
+
return first !== undefined && first !== 0x7b && first !== 0x5b;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private nextHeader(): McpFrame | null {
|
|
64
|
+
// Blank lines before a header block carry no message; drop them so they
|
|
65
|
+
// cannot end up inside the first header line.
|
|
66
|
+
this.buffer = this.release(this.buffer.subarray(this.buffer.findIndex((byte) => byte > 0x20)));
|
|
36
67
|
const headerEnd = this.buffer.indexOf("\r\n\r\n");
|
|
37
68
|
if (headerEnd < 0) {
|
|
38
69
|
if (this.buffer.length <= MCP_STDIO_MAX_FRAME_BYTES) return this.wait();
|
|
39
|
-
return this.discardBuffer();
|
|
70
|
+
return this.discardBuffer("header");
|
|
40
71
|
}
|
|
41
|
-
if (headerEnd > MCP_STDIO_MAX_FRAME_BYTES) return this.discardBuffer();
|
|
72
|
+
if (headerEnd > MCP_STDIO_MAX_FRAME_BYTES) return this.discardBuffer("header");
|
|
42
73
|
|
|
43
74
|
const headers = this.buffer.subarray(0, headerEnd).toString("utf-8").split("\r\n");
|
|
44
|
-
const lengthHeader = headers.find((line) => line.toLowerCase().startsWith(
|
|
75
|
+
const lengthHeader = headers.find((line) => line.toLowerCase().startsWith(HEADER_PREFIX));
|
|
45
76
|
const contentLength = lengthHeader ? Number.parseInt(lengthHeader.split(":")[1]?.trim() ?? "0", 10) : NaN;
|
|
46
77
|
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
47
78
|
this.buffer = Buffer.alloc(0);
|
|
@@ -52,14 +83,37 @@ export class McpFrameReader {
|
|
|
52
83
|
if (contentLength > MCP_STDIO_MAX_FRAME_BYTES) {
|
|
53
84
|
const rest = this.buffer.subarray(bodyStart);
|
|
54
85
|
this.skipBytes = Math.max(0, contentLength - rest.length);
|
|
55
|
-
this.buffer = rest.subarray(Math.min(contentLength, rest.length));
|
|
56
|
-
return
|
|
86
|
+
this.buffer = this.release(rest.subarray(Math.min(contentLength, rest.length)));
|
|
87
|
+
return { kind: "too-large", form: "header" };
|
|
57
88
|
}
|
|
58
89
|
const bodyEnd = bodyStart + contentLength;
|
|
59
90
|
if (this.buffer.length < bodyEnd) return this.wait();
|
|
60
91
|
const text = this.buffer.subarray(bodyStart, bodyEnd).toString("utf-8");
|
|
61
|
-
this.buffer = this.buffer.subarray(bodyEnd);
|
|
62
|
-
return { kind: "body", text };
|
|
92
|
+
this.buffer = this.release(this.buffer.subarray(bodyEnd));
|
|
93
|
+
return { kind: "body", text, form: "header" };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** A line frame, `"blank"` for an empty line, or `null` when more bytes must arrive. */
|
|
97
|
+
private nextLine(): McpFrame | "blank" | null {
|
|
98
|
+
const newline = this.buffer.indexOf("\n");
|
|
99
|
+
if (newline < 0) {
|
|
100
|
+
if (this.buffer.length <= MCP_STDIO_MAX_FRAME_BYTES) return this.wait();
|
|
101
|
+
this.skipLine = true;
|
|
102
|
+
return this.discardBuffer("line");
|
|
103
|
+
}
|
|
104
|
+
if (newline > MCP_STDIO_MAX_FRAME_BYTES) {
|
|
105
|
+
this.buffer = this.release(this.buffer.subarray(newline + 1));
|
|
106
|
+
return { kind: "too-large", form: "line" };
|
|
107
|
+
}
|
|
108
|
+
const text = this.buffer.subarray(0, newline).toString("utf-8").replace(/\r$/, "");
|
|
109
|
+
this.buffer = this.release(this.buffer.subarray(newline + 1));
|
|
110
|
+
if (text.trim().length === 0) return "blank";
|
|
111
|
+
return { kind: "body", text, form: "line" };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** An empty view would keep the old allocation alive while the connection idles. */
|
|
115
|
+
private release(view: Buffer<ArrayBuffer>): Buffer<ArrayBuffer> {
|
|
116
|
+
return view.length === 0 ? Buffer.alloc(0) : view;
|
|
63
117
|
}
|
|
64
118
|
|
|
65
119
|
private wait(): null {
|
|
@@ -67,8 +121,8 @@ export class McpFrameReader {
|
|
|
67
121
|
return null;
|
|
68
122
|
}
|
|
69
123
|
|
|
70
|
-
private discardBuffer(): McpFrame {
|
|
124
|
+
private discardBuffer(form: McpFrameForm): McpFrame {
|
|
71
125
|
this.buffer = Buffer.alloc(0);
|
|
72
|
-
return
|
|
126
|
+
return { kind: "too-large", form };
|
|
73
127
|
}
|
|
74
128
|
}
|
package/src/access-mcp.test.ts
CHANGED
|
@@ -1624,6 +1624,15 @@ function stdioFrame(payload: StdioRequest): string {
|
|
|
1624
1624
|
}
|
|
1625
1625
|
|
|
1626
1626
|
function readStdioMessages(raw: string): StdioMessage[] {
|
|
1627
|
+
if (!raw.toLowerCase().startsWith("content-length:")) {
|
|
1628
|
+
return raw
|
|
1629
|
+
.split("\n")
|
|
1630
|
+
.filter((line) => line.trim().length > 0)
|
|
1631
|
+
.map((line) => {
|
|
1632
|
+
const message: StdioMessage = JSON.parse(line);
|
|
1633
|
+
return message;
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1627
1636
|
const messages: StdioMessage[] = [];
|
|
1628
1637
|
let rest = raw;
|
|
1629
1638
|
while (rest.length > 0) {
|
|
@@ -1638,7 +1647,7 @@ function readStdioMessages(raw: string): StdioMessage[] {
|
|
|
1638
1647
|
}
|
|
1639
1648
|
|
|
1640
1649
|
/** Feeds each chunk in its own tick so the server sees separate data events. */
|
|
1641
|
-
async function
|
|
1650
|
+
async function runStdioRaw(chunks: string[]): Promise<string> {
|
|
1642
1651
|
const input = new PassThrough();
|
|
1643
1652
|
const output = new PassThrough();
|
|
1644
1653
|
output.setEncoding("utf8");
|
|
@@ -1653,7 +1662,11 @@ async function runStdioChunks(chunks: string[]): Promise<StdioMessage[]> {
|
|
|
1653
1662
|
}
|
|
1654
1663
|
input.end();
|
|
1655
1664
|
await running;
|
|
1656
|
-
return
|
|
1665
|
+
return raw;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
async function runStdioChunks(chunks: string[]): Promise<StdioMessage[]> {
|
|
1669
|
+
return readStdioMessages(await runStdioRaw(chunks));
|
|
1657
1670
|
}
|
|
1658
1671
|
|
|
1659
1672
|
const stdioPing = (id: number) => stdioFrame({ jsonrpc: "2.0", id, method: "ping" });
|
|
@@ -1685,3 +1698,54 @@ test("MCP stdio reassembles a frame split inside its header and inside its body"
|
|
|
1685
1698
|
assert.equal(messages.length, 1);
|
|
1686
1699
|
assert.equal(messages[0]?.id, 9);
|
|
1687
1700
|
});
|
|
1701
|
+
|
|
1702
|
+
// Issue #3357: a spec client sends one JSON message per line and reads the
|
|
1703
|
+
// reply in the same form. The server keeps Content-Length for a header client.
|
|
1704
|
+
type StdioLineRequest = { jsonrpc: "2.0"; id: number; method: string; params?: { protocolVersion?: string } };
|
|
1705
|
+
const stdioInit: StdioLineRequest = { jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-06-18" } };
|
|
1706
|
+
const stdioTools: StdioLineRequest = { jsonrpc: "2.0", id: 2, method: "tools/list" };
|
|
1707
|
+
|
|
1708
|
+
function stdioLine(payload: StdioLineRequest): string {
|
|
1709
|
+
return `${JSON.stringify(payload)}\n`;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
test("MCP stdio answers a newline-delimited initialize and tools/list with newline-delimited replies", async () => {
|
|
1713
|
+
const messages = await runStdioChunks([stdioLine(stdioInit), stdioLine(stdioTools)]);
|
|
1714
|
+
assert.equal(messages.length, 2);
|
|
1715
|
+
assert.equal(messages[0]?.id, 1);
|
|
1716
|
+
assert.equal(messages[1]?.id, 2);
|
|
1717
|
+
assert.ok(messages[1]?.result, "tools/list returns a result");
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
test("MCP stdio answers a Content-Length initialize and tools/list with Content-Length replies", async () => {
|
|
1721
|
+
const header = (payload: StdioLineRequest): string => {
|
|
1722
|
+
const body = JSON.stringify(payload);
|
|
1723
|
+
return `Content-Length: ${Buffer.byteLength(body, "utf-8")}\r\n\r\n${body}`;
|
|
1724
|
+
};
|
|
1725
|
+
const messages = await runStdioChunks([header(stdioInit), header(stdioTools)]);
|
|
1726
|
+
assert.equal(messages.length, 2);
|
|
1727
|
+
assert.equal(messages[0]?.id, 1);
|
|
1728
|
+
assert.equal(messages[1]?.id, 2);
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
test("MCP stdio refuses an oversized newline-delimited line and answers the next line", async () => {
|
|
1732
|
+
const messages = await runStdioChunks([
|
|
1733
|
+
`{${"A".repeat(MCP_STDIO_MAX_FRAME_BYTES + 1)}\n`,
|
|
1734
|
+
stdioLine({ jsonrpc: "2.0", id: 4, method: "ping" }),
|
|
1735
|
+
]);
|
|
1736
|
+
assert.equal(messages.length, 2);
|
|
1737
|
+
assert.match(messages[0]?.error?.message ?? "", /frame too large/);
|
|
1738
|
+
assert.equal(messages[1]?.id, 4);
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1741
|
+
test("MCP stdio takes the reply form from the first JSON frame, not from a refused first frame", async () => {
|
|
1742
|
+
const raw = await runStdioRaw([
|
|
1743
|
+
"A".repeat(MCP_STDIO_MAX_FRAME_BYTES + 1),
|
|
1744
|
+
stdioLine({ jsonrpc: "2.0", id: 5, method: "ping" }),
|
|
1745
|
+
stdioLine({ jsonrpc: "2.0", id: 6, method: "ping" }),
|
|
1746
|
+
]);
|
|
1747
|
+
assert.equal(raw.split("Content-Length").length - 1, 1, "only the reply to the refused frame is Content-Length framed");
|
|
1748
|
+
const afterHeader = raw.slice(raw.indexOf("\r\n\r\n") + 4);
|
|
1749
|
+
const replies = afterHeader.slice(afterHeader.indexOf("}}") + 2).split("\n").filter((line) => line.length > 0);
|
|
1750
|
+
assert.deepEqual(replies.map((line) => JSON.parse(line).id), [5, 6]);
|
|
1751
|
+
});
|
package/src/access-mcp.ts
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
import { EngramAccessInputError, type EngramAccessRecallResponse, type EngramAccessService } from "./access-service.js";
|
|
33
33
|
import { EngramAccessForbiddenError, unknownToolError } from "./access-errors.js";
|
|
34
34
|
import { log } from "./logger.js";
|
|
35
|
-
import { McpFrameReader } from "./access-mcp-frames.js";
|
|
35
|
+
import { McpFrameReader, type McpFrameForm } from "./access-mcp-frames.js";
|
|
36
36
|
import "./access-operations.js";
|
|
37
37
|
import { validateBriefingFormat } from "./briefing.js";
|
|
38
38
|
import { processChatMessage } from "./chat/chat-factory.js";
|
|
@@ -470,6 +470,8 @@ async function getMcpServerVersion(): Promise<string> {
|
|
|
470
470
|
|
|
471
471
|
export class EngramMcpServer {
|
|
472
472
|
private readonly frames = new McpFrameReader();
|
|
473
|
+
/** The form of the first frame on this connection. Replies use the same form. */
|
|
474
|
+
private replyForm: McpFrameForm | null = null;
|
|
473
475
|
private flushTask: Promise<void> | null = null;
|
|
474
476
|
private readonly tools: McpTool[];
|
|
475
477
|
private readonly resources: McpResource[];
|
|
@@ -2493,7 +2495,7 @@ export class EngramMcpServer {
|
|
|
2493
2495
|
private async flushBuffer(output: Writable): Promise<void> {
|
|
2494
2496
|
for (let frame = this.frames.next(); frame; frame = this.frames.next()) {
|
|
2495
2497
|
if (frame.kind === "too-large") {
|
|
2496
|
-
this.
|
|
2498
|
+
this.writeRefusal(output, frame.form, -32600, "frame too large");
|
|
2497
2499
|
continue;
|
|
2498
2500
|
}
|
|
2499
2501
|
let parsed: JsonRpcRequest;
|
|
@@ -2501,16 +2503,10 @@ export class EngramMcpServer {
|
|
|
2501
2503
|
// SAFETY: handleRequest defaults missing fields; a non-object body throws and scheduleFlush answers -32700.
|
|
2502
2504
|
parsed = JSON.parse(frame.text) as JsonRpcRequest;
|
|
2503
2505
|
} catch {
|
|
2504
|
-
this.
|
|
2505
|
-
jsonrpc: "2.0",
|
|
2506
|
-
id: null,
|
|
2507
|
-
error: {
|
|
2508
|
-
code: -32700,
|
|
2509
|
-
message: "Parse error",
|
|
2510
|
-
},
|
|
2511
|
-
});
|
|
2506
|
+
this.writeRefusal(output, frame.form, -32700, "Parse error");
|
|
2512
2507
|
continue;
|
|
2513
2508
|
}
|
|
2509
|
+
this.replyForm ??= frame.form;
|
|
2514
2510
|
const response = await this.handleRequest(parsed);
|
|
2515
2511
|
if (response) {
|
|
2516
2512
|
this.writeMessage(output, response);
|
|
@@ -2519,9 +2515,19 @@ export class EngramMcpServer {
|
|
|
2519
2515
|
}
|
|
2520
2516
|
|
|
2521
2517
|
private writeMessage(output: Writable, payload: Record<string, unknown>): void {
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2518
|
+
output.write(this.frameBody(JSON.stringify(payload), this.replyForm));
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
/** Answers a frame that held no valid JSON in its own form, without fixing the connection's reply form. */
|
|
2522
|
+
private writeRefusal(output: Writable, frameForm: McpFrameForm, code: number, message: string): void {
|
|
2523
|
+
const body = JSON.stringify({ jsonrpc: "2.0", id: null, error: { code, message } });
|
|
2524
|
+
output.write(this.frameBody(body, this.replyForm ?? frameForm));
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
private frameBody(body: string, form: McpFrameForm | null): string {
|
|
2528
|
+
return form === "line"
|
|
2529
|
+
? `${body}\n`
|
|
2530
|
+
: `Content-Length: ${Buffer.byteLength(body, "utf-8")}\r\n\r\n${body}`;
|
|
2525
2531
|
}
|
|
2526
2532
|
|
|
2527
2533
|
private toolAcceptsArgument(name: string, key: string): boolean {
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* - Precedence: explicit `namespace` wins; namespaces-disabled is a no-op.
|
|
20
20
|
*/
|
|
21
21
|
import assert from "node:assert/strict";
|
|
22
|
+
import os from "node:os";
|
|
23
|
+
import path from "node:path";
|
|
22
24
|
import test from "node:test";
|
|
23
25
|
|
|
24
26
|
import { EngramAccessService } from "./access-service.js";
|
|
@@ -39,9 +41,14 @@ import { sealedWriteToLegacyArgs, type SealedMemoryEnvelope } from "./write-enve
|
|
|
39
41
|
// the PRODUCTION mapping (`sealedWriteToLegacyArgs`), so mock behavior
|
|
40
42
|
// cannot drift from the real envelope→options translation.
|
|
41
43
|
function withSealedWrite<T extends { writeMemory: (...args: never[]) => unknown }>(stub: T): T {
|
|
44
|
+
// SAFETY: the decorated stub only gains writeSealedMemory and dir, both assigned below before any caller reads them.
|
|
42
45
|
const decorated = stub as T & {
|
|
43
46
|
writeSealedMemory?: (envelope: SealedMemoryEnvelope, extras?: Record<string, unknown>) => unknown;
|
|
47
|
+
dir?: string;
|
|
44
48
|
};
|
|
49
|
+
// #3354: the fact-dedup window keys its lock on storage.dir and takes a
|
|
50
|
+
// real file lock, so the fake needs a writable per-process dir.
|
|
51
|
+
decorated.dir = path.join(os.tmpdir(), `remnic-coding-write-fake-${process.pid}`);
|
|
45
52
|
decorated.writeSealedMemory = (envelope, extras = {}) => {
|
|
46
53
|
const { category, content, options } = sealedWriteToLegacyArgs(envelope, extras);
|
|
47
54
|
return (stub.writeMemory as (c: unknown, b: unknown, o: unknown) => unknown)(category, content, options);
|
|
@@ -6,6 +6,7 @@ import test from "node:test";
|
|
|
6
6
|
import { parseConfig } from "../config.js";
|
|
7
7
|
import { NamespaceStorageRouter } from "../namespaces/storage.js";
|
|
8
8
|
import { StorageManager } from "../storage.js";
|
|
9
|
+
import { persistUnderFactDedupWindow } from "../orchestration/fact-dedup-window.js";
|
|
9
10
|
import { withTempDir } from "../testing/tmp-dir.js";
|
|
10
11
|
import {
|
|
11
12
|
createJournalMemoryWriter,
|
|
@@ -127,3 +128,33 @@ test("tombstoned journal facts are skipped and not written as pending_review", a
|
|
|
127
128
|
assert.equal(pending.length, 0, "tombstoned content must not land as a new pending_review file");
|
|
128
129
|
});
|
|
129
130
|
});
|
|
131
|
+
|
|
132
|
+
test("a fact an extraction run commits after the journal probe is not written again", async () => {
|
|
133
|
+
await withTempDir(async (dir) => {
|
|
134
|
+
const config = parseConfig({ memoryDir: dir });
|
|
135
|
+
const storage = new StorageManager(config.memoryDir);
|
|
136
|
+
await storage.ensureDirectories();
|
|
137
|
+
const real = createJournalMemoryWriter(storage);
|
|
138
|
+
const deps = extractDeps(storage, "fact");
|
|
139
|
+
deps.writer = {
|
|
140
|
+
writeSealedMemory: real.writeSealedMemory,
|
|
141
|
+
hasJournalMemoryContent: async (content) => {
|
|
142
|
+
const known = await real.hasJournalMemoryContent(content);
|
|
143
|
+
assert.equal(known, false, "the probe runs before the other writer commits");
|
|
144
|
+
await persistUnderFactDedupWindow(storage.dir, async () => false, () => storage.writeMemory("fact", FACT));
|
|
145
|
+
return known;
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
const result = await runJournalReviewExtraction({
|
|
149
|
+
date: "2026-08-20",
|
|
150
|
+
journalText: FACT,
|
|
151
|
+
source: "memoryDir",
|
|
152
|
+
journalConfig: { extractionMode: "review" },
|
|
153
|
+
deps,
|
|
154
|
+
});
|
|
155
|
+
assert.equal(result.pendingReview, 0);
|
|
156
|
+
assert.equal(result.skipped, 1);
|
|
157
|
+
const copies = (await storage.readAllMemories()).filter((memory) => memory.content.includes(FACT));
|
|
158
|
+
assert.equal(copies.length, 1, "the corpus holds one copy of the fact");
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -298,6 +298,7 @@ test("memoryDir source provenance differs only in the attribute value", async ()
|
|
|
298
298
|
test("createJournalMemoryWriter dedups journal-tagged memories beyond the fact hash", async () => {
|
|
299
299
|
const calls: string[] = [];
|
|
300
300
|
const storage = {
|
|
301
|
+
dir: "/tmp/journal-writer-test-unused",
|
|
301
302
|
writeSealedMemory: async () => ({}),
|
|
302
303
|
hasFactContentHash: async (content: string) => {
|
|
303
304
|
calls.push(`hash:${content}`);
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* Per-day change detection lives in journal-state.ts (timeline.json);
|
|
12
12
|
* this module is the pure pass over one day's POST-STRIP text.
|
|
13
13
|
*/
|
|
14
|
+
import { persistUnderFactDedupWindow } from "../orchestration/fact-dedup-window.js";
|
|
14
15
|
import { scoreImportance } from "../importance.js";
|
|
15
16
|
import { getVerdictKind, type JudgeCandidate, type JudgeVerdict } from "../extraction-judge.js";
|
|
16
17
|
import { log } from "../logger.js";
|
|
@@ -29,6 +30,7 @@ import type { ActivityTimelineJournalConfig } from "./types.js";
|
|
|
29
30
|
|
|
30
31
|
/** The subset of StorageManager the journal pass writes through. */
|
|
31
32
|
export interface JournalStorageIo {
|
|
33
|
+
readonly dir: string;
|
|
32
34
|
writeSealedMemory(
|
|
33
35
|
envelope: SealedMemoryEnvelope,
|
|
34
36
|
extras: { status: MemoryStatus; contentHashSource: string; importance?: ImportanceScore },
|
|
@@ -38,7 +40,14 @@ export interface JournalStorageIo {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export interface JournalMemoryWriter {
|
|
41
|
-
|
|
43
|
+
/**
|
|
44
|
+
* `duplicate` is true when a concurrent writer committed the same fact
|
|
45
|
+
* first and nothing was written.
|
|
46
|
+
*/
|
|
47
|
+
writeSealedMemory(
|
|
48
|
+
envelope: SealedMemoryEnvelope,
|
|
49
|
+
extras: { status: MemoryStatus; contentHashSource: string; importance?: ImportanceScore },
|
|
50
|
+
): Promise<{ tombstoneBlocked?: boolean; duplicate?: boolean }>;
|
|
42
51
|
/** Dedup beyond the fact-only hash index: scans journal-tagged memories. */
|
|
43
52
|
hasJournalMemoryContent(content: string): Promise<boolean>;
|
|
44
53
|
}
|
|
@@ -51,7 +60,19 @@ export interface JournalMemoryWriter {
|
|
|
51
60
|
*/
|
|
52
61
|
export function createJournalMemoryWriter(storage: JournalStorageIo): JournalMemoryWriter {
|
|
53
62
|
return {
|
|
54
|
-
writeSealedMemory:
|
|
63
|
+
writeSealedMemory: async (envelope, extras) => {
|
|
64
|
+
// Facts register in the fact hash index, so extraction and this pass can
|
|
65
|
+
// commit the same new fact. Commit it under the same window extraction
|
|
66
|
+
// uses and recheck the index there. The scan in hasJournalMemoryContent
|
|
67
|
+
// stays outside: it is corpus-sized and the window is bounded.
|
|
68
|
+
if (envelope.category !== "fact") return storage.writeSealedMemory(envelope, extras);
|
|
69
|
+
const outcome = await persistUnderFactDedupWindow(
|
|
70
|
+
storage.dir,
|
|
71
|
+
() => storage.hasFactContentHash(extras.contentHashSource),
|
|
72
|
+
() => storage.writeSealedMemory(envelope, extras),
|
|
73
|
+
);
|
|
74
|
+
return outcome.suppressed ? { duplicate: true } : outcome.result;
|
|
75
|
+
},
|
|
55
76
|
hasJournalMemoryContent: async (content: string) => {
|
|
56
77
|
if (await storage.hasFactContentHash(content)) return true;
|
|
57
78
|
const needle = stripAttributesSuffix(content);
|
|
@@ -210,7 +231,7 @@ export async function runJournalReviewExtraction(input: {
|
|
|
210
231
|
contentHashSource: normalizedKey,
|
|
211
232
|
importance: scoreImportance(fact.content, fact.category, fact.tags),
|
|
212
233
|
});
|
|
213
|
-
if (write.tombstoneBlocked) {
|
|
234
|
+
if (write.tombstoneBlocked || write.duplicate) {
|
|
214
235
|
result.skipped += 1;
|
|
215
236
|
continue;
|
|
216
237
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type GoogleDriveClientFactory,
|
|
15
15
|
type GoogleDriveSyncResult,
|
|
16
16
|
} from "./index.js";
|
|
17
|
+
import { SECRET_RESOLVE_DEADLINE_MS } from "./google-drive.js";
|
|
17
18
|
import type { ConnectorConfig, ConnectorCursor } from "./framework.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -123,6 +124,44 @@ test("a SecretRef with no resolver fails the connector and hides the reference",
|
|
|
123
124
|
assert.equal(message.includes("keychain"), false);
|
|
124
125
|
});
|
|
125
126
|
|
|
127
|
+
const DRIVE_REF_CONFIG: ConnectorConfig = {
|
|
128
|
+
...SYNTHETIC_CREDS,
|
|
129
|
+
clientSecret: { source: "keychain", id: "projects/drive-secret" },
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
async function driveSyncError(args: { resolveSecretRef: () => Promise<string | undefined>; abortSignal?: AbortSignal }): Promise<Error> {
|
|
133
|
+
const connector = createGoogleDriveConnector({ clientFactory: makeFactory(makeMockClient({})) });
|
|
134
|
+
try {
|
|
135
|
+
await connector.syncIncremental({ cursor: null, config: DRIVE_REF_CONFIG, ...args });
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return err instanceof Error ? err : new Error("non-error thrown");
|
|
138
|
+
}
|
|
139
|
+
return new Error("sync did not fail");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
test("a SecretRef resolver that never settles is cut off by the abort signal", async () => {
|
|
143
|
+
const controller = new AbortController();
|
|
144
|
+
const failure = driveSyncError({ resolveSecretRef: () => new Promise(() => undefined), abortSignal: controller.signal });
|
|
145
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
146
|
+
controller.abort();
|
|
147
|
+
assert.equal((await failure).name, "AbortError");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("a SecretRef resolver that never settles fails at the deadline", async (t) => {
|
|
151
|
+
t.mock.timers.enable({ apis: ["setTimeout"] });
|
|
152
|
+
const failure = driveSyncError({ resolveSecretRef: () => new Promise(() => undefined) });
|
|
153
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
154
|
+
t.mock.timers.tick(SECRET_RESOLVE_DEADLINE_MS);
|
|
155
|
+
const err = await failure;
|
|
156
|
+
assert.match(err.message, /clientSecret secret reference was not resolved within 30000 ms/);
|
|
157
|
+
assert.equal(err.message.includes("projects/drive-secret"), false);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("a SecretRef that resolves to no value says the resolver may be unavailable", async () => {
|
|
161
|
+
const err = await driveSyncError({ resolveSecretRef: async () => undefined });
|
|
162
|
+
assert.match(err.message, /resolved to no value \(the host secret resolver may be unavailable\)/);
|
|
163
|
+
});
|
|
164
|
+
|
|
126
165
|
test("validateGoogleDriveConfig rejects non-object input", () => {
|
|
127
166
|
assert.throws(() => validateGoogleDriveConfig(null), /must be an object/);
|
|
128
167
|
assert.throws(() => validateGoogleDriveConfig([]), /must be an object/);
|
|
@@ -64,7 +64,7 @@ import type {
|
|
|
64
64
|
SyncIncrementalResult,
|
|
65
65
|
} from "./framework.js";
|
|
66
66
|
import { isTransientHttpError } from "./transient-errors.js";
|
|
67
|
-
import { throwIfAborted } from "../../abort-error.js";
|
|
67
|
+
import { raceAbort, throwIfAborted } from "../../abort-error.js";
|
|
68
68
|
import type { ResolveSecretRefFn } from "../../resolve-auth-token.js";
|
|
69
69
|
import type { SecretRef } from "../../types.js";
|
|
70
70
|
|
|
@@ -370,10 +370,37 @@ function requireNonEmptyString(value: unknown, key: string): string {
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
|
|
373
|
+
/** Longest wait for the host secret resolver, so a stalled secret store cannot hold the sync loop. */
|
|
374
|
+
export const SECRET_RESOLVE_DEADLINE_MS = 30_000;
|
|
375
|
+
|
|
376
|
+
async function resolveWithinDeadline(
|
|
377
|
+
ref: SecretRef,
|
|
378
|
+
key: string,
|
|
379
|
+
resolver: ResolveSecretRefFn,
|
|
380
|
+
abortSignal: AbortSignal | undefined,
|
|
381
|
+
): Promise<string | undefined> {
|
|
382
|
+
throwIfAborted(abortSignal, "googleDrive: sync aborted");
|
|
383
|
+
const deadline = new AbortController();
|
|
384
|
+
const timer = setTimeout(() => deadline.abort(), SECRET_RESOLVE_DEADLINE_MS);
|
|
385
|
+
const signal = abortSignal ? AbortSignal.any([abortSignal, deadline.signal]) : deadline.signal;
|
|
386
|
+
try {
|
|
387
|
+
return await raceAbort(Promise.resolve(resolver(ref)), signal, "googleDrive: secret resolve aborted");
|
|
388
|
+
} catch {
|
|
389
|
+
throwIfAborted(abortSignal, "googleDrive: sync aborted");
|
|
390
|
+
if (deadline.signal.aborted) {
|
|
391
|
+
throw new Error(`googleDrive: ${key} secret reference was not resolved within ${SECRET_RESOLVE_DEADLINE_MS} ms`);
|
|
392
|
+
}
|
|
393
|
+
throw new Error(`googleDrive: ${key} secret reference could not be resolved`);
|
|
394
|
+
} finally {
|
|
395
|
+
clearTimeout(timer);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
373
399
|
async function resolveCredential(
|
|
374
400
|
value: DriveCredential,
|
|
375
401
|
key: string,
|
|
376
402
|
resolver: ResolveSecretRefFn | null | undefined,
|
|
403
|
+
abortSignal: AbortSignal | undefined,
|
|
377
404
|
): Promise<string> {
|
|
378
405
|
if (Object.prototype.toString.call(value) !== "[object Object]") {
|
|
379
406
|
// SAFETY: a DriveCredential that is not an object is the plain string form.
|
|
@@ -384,17 +411,14 @@ async function resolveCredential(
|
|
|
384
411
|
if (resolver == null) {
|
|
385
412
|
throw new Error(`googleDrive: ${key} is a secret reference but no resolver is configured`);
|
|
386
413
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
resolved
|
|
390
|
-
} catch {
|
|
391
|
-
throw new Error(`googleDrive: ${key} secret reference could not be resolved`);
|
|
414
|
+
const resolved = await resolveWithinDeadline(ref, key, resolver, abortSignal);
|
|
415
|
+
if (resolved === undefined) {
|
|
416
|
+
throw new Error(`googleDrive: ${key} secret reference resolved to no value (the host secret resolver may be unavailable)`);
|
|
392
417
|
}
|
|
393
418
|
if (Object.prototype.toString.call(resolved) !== "[object String]") {
|
|
394
419
|
throw new Error(`googleDrive: ${key} secret reference resolved to a non-string value`);
|
|
395
420
|
}
|
|
396
|
-
|
|
397
|
-
const text = (resolved as string).trim();
|
|
421
|
+
const text = resolved.trim();
|
|
398
422
|
if (text.length === 0) {
|
|
399
423
|
throw new Error(`googleDrive: ${key} secret reference resolved to an empty value`);
|
|
400
424
|
}
|
|
@@ -404,11 +428,12 @@ async function resolveCredential(
|
|
|
404
428
|
async function resolveGoogleDriveConfig(
|
|
405
429
|
stored: GoogleDriveConnectorConfig,
|
|
406
430
|
resolver: ResolveSecretRefFn | null | undefined,
|
|
431
|
+
abortSignal: AbortSignal | undefined,
|
|
407
432
|
): Promise<GoogleDriveResolvedConfig> {
|
|
408
433
|
return {
|
|
409
|
-
clientId: await resolveCredential(stored.clientId, "clientId", resolver),
|
|
410
|
-
[CLIENT_SECRET_FIELD]: await resolveCredential(stored[CLIENT_SECRET_FIELD], CLIENT_SECRET_FIELD, resolver),
|
|
411
|
-
[REFRESH_TOKEN_FIELD]: await resolveCredential(stored[REFRESH_TOKEN_FIELD], REFRESH_TOKEN_FIELD, resolver),
|
|
434
|
+
clientId: await resolveCredential(stored.clientId, "clientId", resolver, abortSignal),
|
|
435
|
+
[CLIENT_SECRET_FIELD]: await resolveCredential(stored[CLIENT_SECRET_FIELD], CLIENT_SECRET_FIELD, resolver, abortSignal),
|
|
436
|
+
[REFRESH_TOKEN_FIELD]: await resolveCredential(stored[REFRESH_TOKEN_FIELD], REFRESH_TOKEN_FIELD, resolver, abortSignal),
|
|
412
437
|
pollIntervalMs: stored.pollIntervalMs,
|
|
413
438
|
folderIds: stored.folderIds,
|
|
414
439
|
};
|
|
@@ -512,7 +537,11 @@ export function createGoogleDriveConnector(
|
|
|
512
537
|
|
|
513
538
|
async syncIncremental(args: SyncIncrementalArgs): Promise<SyncIncrementalResult> {
|
|
514
539
|
// Re-validate on every pass: a JS caller could mutate it between passes.
|
|
515
|
-
const config = await resolveGoogleDriveConfig(
|
|
540
|
+
const config = await resolveGoogleDriveConfig(
|
|
541
|
+
validateGoogleDriveConfig(args.config),
|
|
542
|
+
args.resolveSecretRef,
|
|
543
|
+
args.abortSignal,
|
|
544
|
+
);
|
|
516
545
|
throwIfAborted(args.abortSignal, "googleDrive: sync aborted");
|
|
517
546
|
|
|
518
547
|
const client = await clientFactory(config);
|