@remnic/core 10.0.84 → 10.0.86
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 +90 -88
- package/dist/access-authorization-probe.js +91 -89
- package/dist/access-boundary.d.ts.map +1 -1
- package/dist/access-boundary.js +90 -88
- package/dist/access-cli.js +198 -194
- package/dist/access-cli.js.map +1 -1
- package/dist/access-errors.d.ts +14 -0
- package/dist/access-errors.d.ts.map +1 -1
- package/dist/access-errors.js +5 -1
- package/dist/access-extraction-force-flush.js +90 -88
- package/dist/access-http-briefing.js +91 -89
- package/dist/access-http-query.js +92 -90
- package/dist/access-http.d.ts +12 -0
- package/dist/access-http.d.ts.map +1 -1
- package/dist/access-http.js +119 -116
- package/dist/access-identity-continuity-surface.js +90 -88
- package/dist/access-lcm-surface.js +90 -88
- package/dist/access-mcp-frames.d.ts +29 -0
- package/dist/access-mcp-frames.d.ts.map +1 -0
- package/dist/access-mcp-frames.js +10 -0
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.d.ts.map +1 -1
- package/dist/access-mcp.js +105 -102
- package/dist/access-memory-search-fanout.js +2 -2
- package/dist/access-namespace-preflight.js +90 -88
- package/dist/access-observe-write-category.js +15 -14
- package/dist/access-observe-write-surface.d.ts.map +1 -1
- package/dist/access-observe-write-surface.js +90 -88
- package/dist/access-offline-manifest.js +10 -10
- package/dist/access-operations-batch.js +94 -92
- package/dist/access-operations.js +101 -99
- package/dist/access-recall-concurrency.js +90 -88
- package/dist/access-recall-diagnostics.js +2 -2
- package/dist/access-recall-response.js +90 -88
- package/dist/access-recall-surface.js +90 -88
- package/dist/access-schema.d.ts.map +1 -1
- package/dist/access-schema.js +10 -9
- package/dist/access-service.d.ts +3 -3
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +90 -88
- package/dist/access-surface-catalog.js +92 -90
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/access-token-capabilities.js +2 -2
- package/dist/active-recall.js +38 -38
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/{auto-sync-C7E6G7Y2.js → auto-sync-3AZW7MXI.js} +6 -6
- package/dist/briefing-deadline.d.ts +2 -2
- package/dist/briefing-deadline.d.ts.map +1 -1
- package/dist/briefing-deadline.js +1 -1
- package/dist/briefing.js +25 -25
- package/dist/buffer-admission.d.ts +22 -0
- package/dist/buffer-admission.d.ts.map +1 -0
- package/dist/buffer-admission.js +13 -0
- package/dist/buffer-eviction.d.ts +4 -5
- package/dist/buffer-eviction.d.ts.map +1 -1
- package/dist/buffer-eviction.js +1 -1
- package/dist/buffer-persistence.d.ts +8 -4
- package/dist/buffer-persistence.d.ts.map +1 -1
- package/dist/buffer-persistence.js +1 -1
- package/dist/buffer-turn-helpers.d.ts +12 -0
- package/dist/buffer-turn-helpers.d.ts.map +1 -1
- package/dist/buffer-turn-helpers.js +9 -1
- package/dist/buffer-types.d.ts +24 -1
- package/dist/buffer-types.d.ts.map +1 -1
- package/dist/buffer.d.ts +21 -19
- package/dist/buffer.d.ts.map +1 -1
- package/dist/buffer.js +17 -13
- package/dist/calibration.js +3 -3
- package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/capsule-gunzip-KGBDGF7P.js +11 -0
- package/dist/capsule-gunzip-KGBDGF7P.js.map +1 -0
- package/dist/capsule-import-gate.d.ts +11 -0
- package/dist/capsule-import-gate.d.ts.map +1 -0
- package/dist/capsule-import-gate.js +12 -0
- package/dist/capsule-import-gate.js.map +1 -0
- package/dist/{capsule-merge-XVTQJJ6R.js → capsule-merge-2IIXFXN5.js} +5 -11
- package/dist/capsule-merge-2IIXFXN5.js.map +1 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +27 -27
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-MSIRVXQ3.js → chunk-2G4TV2ML.js} +12 -12
- package/dist/{chunk-B74NPUHT.js → chunk-2JG2NI7C.js} +54 -54
- package/dist/chunk-2JG2NI7C.js.map +1 -0
- package/dist/{chunk-IR4C3DP2.js → chunk-2K3AUDH7.js} +2 -2
- package/dist/{chunk-2IW5PT7U.js → chunk-2N4OUSIA.js} +2 -2
- package/dist/{chunk-BXLOS5AJ.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-QZSIDSB2.js → chunk-2NUDHVYM.js} +2 -2
- package/dist/{chunk-HPFVZPRH.js → chunk-2U4LMSEX.js} +3 -3
- package/dist/{chunk-WHQXWFH7.js → chunk-2XOFVTTT.js} +2 -2
- package/dist/{chunk-I6ORDT54.js → chunk-32CA7B6L.js} +2 -2
- package/dist/{chunk-T77AX6DG.js → chunk-32YX7EJ3.js} +4 -4
- package/dist/{chunk-HLKUZW42.js → chunk-3CTKEXXF.js} +144 -156
- package/dist/chunk-3CTKEXXF.js.map +1 -0
- package/dist/{chunk-HLWXCDUQ.js → chunk-3DAWHP5K.js} +5 -5
- package/dist/{chunk-ZJJFUEPO.js → chunk-3PJEDHLD.js} +2 -2
- package/dist/{chunk-PJYK7HH2.js → chunk-3UZ2VMKD.js} +2 -2
- package/dist/{chunk-IN4OFAHX.js → chunk-3Y5X6YFH.js} +2 -2
- package/dist/{chunk-YUTT4ZTI.js → chunk-45VUHDV2.js} +2 -2
- package/dist/{chunk-T6T2RMWW.js → chunk-4GMI5HFH.js} +12 -12
- package/dist/{chunk-YOEYL6CH.js → chunk-4LCVA2LW.js} +3 -3
- package/dist/{chunk-66JOLMWS.js → chunk-4X7HQKAG.js} +2 -2
- package/dist/{chunk-65SKM4BF.js → chunk-4XYAWK5H.js} +2 -2
- package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
- package/dist/{chunk-TPX7XXY4.js → chunk-5SOSQTVE.js} +2 -2
- package/dist/{chunk-G3T6YMBO.js → chunk-64S6X4IU.js} +7 -7
- package/dist/{chunk-BK7AJUWB.js → chunk-6BCWFCDG.js} +7 -7
- package/dist/{chunk-V27FK6D6.js → chunk-6XTUZKCZ.js} +2 -2
- package/dist/{chunk-KASJJGRG.js → chunk-6ZELOK6O.js} +3 -8
- package/dist/chunk-6ZELOK6O.js.map +1 -0
- package/dist/{chunk-LECSS7EE.js → chunk-73UZZFJC.js} +3 -3
- package/dist/{chunk-4EACCIPO.js → chunk-77PDJ4OE.js} +3 -3
- package/dist/{chunk-GQZSGXR6.js → chunk-7GSL4LBG.js} +2 -2
- package/dist/{chunk-B6SPPYGJ.js → chunk-7IWRLYFJ.js} +2 -2
- package/dist/{chunk-R45L4TV6.js → chunk-7LCGFUMB.js} +6 -6
- package/dist/{chunk-A7BM2JTH.js → chunk-AJHNXIQU.js} +2 -2
- package/dist/{chunk-2D7BTSDW.js → chunk-ANZZLRUX.js} +2 -2
- package/dist/{chunk-ZXF3O7P4.js → chunk-ARRTLPRC.js} +2 -2
- package/dist/{chunk-S5CQI2KF.js → chunk-B3GKQ6DU.js} +2 -2
- package/dist/{chunk-XRBHIR7Q.js → chunk-B56LORLY.js} +2 -2
- package/dist/{chunk-KO3YBHOV.js → chunk-BIVKPQ6Q.js} +3 -3
- package/dist/{chunk-QZBTIYKD.js → chunk-BLSHFIR6.js} +7 -6
- package/dist/chunk-BLSHFIR6.js.map +1 -0
- package/dist/{chunk-54FDG7K7.js → chunk-C2HORX76.js} +3 -3
- package/dist/{chunk-WAD32CZG.js → chunk-CC4V7HNP.js} +43 -30
- package/dist/chunk-CC4V7HNP.js.map +1 -0
- package/dist/{chunk-LGEH473S.js → chunk-CJCTKL6Y.js} +9 -1
- package/dist/chunk-CJCTKL6Y.js.map +1 -0
- package/dist/{chunk-E33UJHY3.js → chunk-CN7Q3T2J.js} +4 -4
- package/dist/{chunk-YUTG35M4.js → chunk-CYCEROPR.js} +3 -3
- package/dist/{chunk-TFLPDNQV.js → chunk-D5EV6Y2L.js} +3 -3
- package/dist/{chunk-BFBS7OFQ.js → chunk-DC55RIEZ.js} +4 -4
- package/dist/{chunk-B4ZU34JQ.js → chunk-DT7SNJH6.js} +3 -3
- package/dist/{chunk-VLMBSGQF.js → chunk-DZ7DPDVW.js} +10 -8
- package/dist/chunk-DZ7DPDVW.js.map +1 -0
- package/dist/{chunk-FVRTCYZ3.js → chunk-EA2LA5RI.js} +2 -2
- package/dist/{chunk-WBN66HPI.js → chunk-ELHJK7LM.js} +6 -6
- package/dist/{chunk-77R7BQZ2.js → chunk-EQ4U73UH.js} +77 -24
- package/dist/chunk-EQ4U73UH.js.map +1 -0
- package/dist/{chunk-3JPTUCD7.js → chunk-EUBDYOLD.js} +32 -23
- package/dist/chunk-EUBDYOLD.js.map +1 -0
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-YAUHSNPB.js → chunk-F74G4KIH.js} +2 -2
- package/dist/{chunk-MPGLM2VU.js → chunk-FF3EFLUT.js} +7 -5
- package/dist/chunk-FF3EFLUT.js.map +1 -0
- package/dist/{chunk-BV2BRHD4.js → chunk-FHGWKODN.js} +2 -2
- package/dist/chunk-FKBGK3NC.js +62 -0
- package/dist/chunk-FKBGK3NC.js.map +1 -0
- package/dist/{chunk-X2KO4FK5.js → chunk-FNFURZBZ.js} +5 -5
- package/dist/chunk-FNFURZBZ.js.map +1 -0
- package/dist/{chunk-KESJ5XX3.js → chunk-G54BOKZ6.js} +2 -2
- package/dist/{chunk-YK7GOPIE.js → chunk-G67EUQAB.js} +9 -7
- package/dist/chunk-G67EUQAB.js.map +1 -0
- package/dist/{chunk-K36RMINQ.js → chunk-GOZXQNIM.js} +3 -3
- package/dist/{chunk-OQSSQOKQ.js → chunk-GULUWOA5.js} +2 -2
- package/dist/{chunk-K6RTQZA4.js → chunk-HFUKXOUW.js} +2 -2
- package/dist/{chunk-WV254W4V.js → chunk-HLN2HOXX.js} +2 -2
- package/dist/{chunk-FUOAIJKQ.js → chunk-IBDWYRDR.js} +3 -3
- package/dist/{chunk-Z3DDQXM4.js → chunk-IBGU6AN6.js} +45 -45
- package/dist/chunk-IBGU6AN6.js.map +1 -0
- package/dist/{chunk-AJG2TAMR.js → chunk-IEEV27BC.js} +2 -2
- package/dist/chunk-IGLJNHOX.js +62 -0
- package/dist/chunk-IGLJNHOX.js.map +1 -0
- package/dist/{chunk-LTDLJCMQ.js → chunk-IJ3OEECW.js} +4 -4
- package/dist/{chunk-BK4FBHZ3.js → chunk-IPD5GC7C.js} +3 -3
- package/dist/{chunk-KFLD7ZZG.js → chunk-IUKYFTGW.js} +3 -3
- package/dist/{chunk-GCAQ3VUJ.js → chunk-JM2YZKCQ.js} +2 -2
- package/dist/{chunk-BNQJKWWZ.js → chunk-K3FH3LAQ.js} +4 -4
- package/dist/{chunk-LU66I56Q.js → chunk-KVSOEVAF.js} +61 -12
- package/dist/chunk-KVSOEVAF.js.map +1 -0
- package/dist/chunk-M3BEJJNV.js +31 -0
- package/dist/chunk-M3BEJJNV.js.map +1 -0
- package/dist/{chunk-RZH5RVJC.js → chunk-MTTYIXSU.js} +2 -2
- package/dist/{chunk-XHGYZRVW.js → chunk-NIVKG3DS.js} +5 -5
- package/dist/{chunk-VZELUMKK.js → chunk-NQP4ZYGG.js} +2 -2
- package/dist/{chunk-7PMOCQOL.js → chunk-NT7F7WJQ.js} +192 -63
- package/dist/chunk-NT7F7WJQ.js.map +1 -0
- package/dist/{chunk-YO2JEX2Z.js → chunk-OGBQMY46.js} +2 -2
- package/dist/{chunk-TLHF75OR.js → chunk-OL553AWT.js} +12 -12
- package/dist/{chunk-CLVILSBR.js → chunk-OZNBZJ3S.js} +22 -22
- package/dist/{chunk-VUROWXFL.js → chunk-P452SEKO.js} +2 -2
- package/dist/{chunk-JX3E4XCW.js → chunk-PDOOUXCS.js} +2 -2
- package/dist/{chunk-6QYPL6NI.js → chunk-PKZ274PI.js} +3 -3
- package/dist/{chunk-KJCL2FZO.js → chunk-QD5R6U5J.js} +2 -2
- package/dist/{chunk-JRVMCZE3.js → chunk-QEYTRFR3.js} +2 -2
- package/dist/{chunk-VKDAJJQP.js → chunk-QEZ3DW5O.js} +11 -11
- package/dist/{chunk-WYPCJK6K.js → chunk-QKERFEJS.js} +2 -2
- package/dist/{chunk-BDN3TDGV.js → chunk-QKES7GU6.js} +2 -2
- package/dist/{chunk-PCQWKMOL.js → chunk-QNSZYTSG.js} +2 -2
- package/dist/chunk-QV2VZKD5.js +31 -0
- package/dist/chunk-QV2VZKD5.js.map +1 -0
- package/dist/{chunk-IZJQNDGI.js → chunk-R6HMAIKM.js} +2 -2
- package/dist/{chunk-CIIHIAJ2.js → chunk-ROGO7FRA.js} +6 -5
- package/dist/{chunk-CIIHIAJ2.js.map → chunk-ROGO7FRA.js.map} +1 -1
- package/dist/{chunk-NUSAAN75.js → chunk-ROKDHO74.js} +2 -2
- package/dist/{chunk-D5SSBCYY.js → chunk-RWGRSKN5.js} +7 -5
- package/dist/chunk-RWGRSKN5.js.map +1 -0
- package/dist/{chunk-FF6N7BXC.js → chunk-S45INSV3.js} +4 -4
- package/dist/{chunk-2RUTN7PR.js → chunk-S7GJNA3K.js} +2 -2
- package/dist/{chunk-XT45WCKX.js → chunk-SYKJBS7S.js} +10 -10
- package/dist/{chunk-5BE3FLKL.js → chunk-TAUHAP5J.js} +4 -4
- package/dist/{chunk-RJWL3RHB.js → chunk-TFEHSPGQ.js} +2 -2
- package/dist/{chunk-XYP5WBDO.js → chunk-TQ4SCPGQ.js} +76 -60
- package/dist/chunk-TQ4SCPGQ.js.map +1 -0
- package/dist/{chunk-K3B72DKJ.js → chunk-TRMZHGIX.js} +8 -8
- package/dist/{chunk-WBDIZWJQ.js → chunk-TSS3D7ZY.js} +2 -2
- package/dist/{chunk-6LCWKS62.js → chunk-UAUV3BWS.js} +104 -121
- package/dist/chunk-UAUV3BWS.js.map +1 -0
- package/dist/{chunk-KQUSP6LB.js → chunk-UDKWFDV2.js} +1 -1
- package/dist/chunk-UDKWFDV2.js.map +1 -0
- package/dist/{chunk-HQPFACXO.js → chunk-UH3FPX3L.js} +2 -2
- package/dist/{chunk-GGJFRVRD.js → chunk-US7KPQA5.js} +2 -2
- package/dist/{chunk-FJ2VJTGA.js → chunk-UWRHHRDK.js} +3 -3
- package/dist/{chunk-4KLQI3L2.js → chunk-UXXK523X.js} +40 -2
- package/dist/chunk-UXXK523X.js.map +1 -0
- package/dist/{chunk-SDESKFKT.js → chunk-UYHDMCY2.js} +2 -2
- package/dist/{chunk-3T3VHXUH.js → chunk-VKILERSL.js} +2 -2
- package/dist/{chunk-2TGIKMMZ.js → chunk-WXDTOE6F.js} +2 -2
- package/dist/{chunk-KQLBXUUQ.js → chunk-WYG5UNDY.js} +2 -2
- package/dist/{chunk-DQEMWVMT.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-MPDLLLOG.js → chunk-XO23OKMK.js} +9 -5
- package/dist/chunk-XO23OKMK.js.map +1 -0
- package/dist/{chunk-WC475JOG.js → chunk-XU37EJTW.js} +24 -24
- package/dist/chunk-XU37EJTW.js.map +1 -0
- package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
- package/dist/{chunk-QA3Y2O6H.js → chunk-YAVIBVCU.js} +2 -2
- package/dist/{chunk-2XJZLSXE.js → chunk-YL4UKMD7.js} +124 -58
- package/dist/chunk-YL4UKMD7.js.map +1 -0
- package/dist/{chunk-VPEIWMUT.js → chunk-YLTU2DC4.js} +2 -2
- package/dist/{chunk-HWHNQVRV.js → chunk-YMJRLT7S.js} +28 -8
- package/dist/chunk-YMJRLT7S.js.map +1 -0
- package/dist/{chunk-EBOHFZI4.js → chunk-YRZPBN46.js} +3 -3
- package/dist/{chunk-FMFCQRDM.js → chunk-ZANNIXXE.js} +3 -3
- package/dist/{chunk-HQGATKG2.js → chunk-ZBFLV657.js} +3 -3
- package/dist/{chunk-6L63IN3M.js → chunk-ZOJUWVAU.js} +1 -1
- package/dist/cli.js +144 -141
- package/dist/coding/export-okf-codegraph.js +24 -24
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +26 -26
- package/dist/config.js +35 -35
- package/dist/connectors/codex-materialize-runner.js +24 -24
- package/dist/connectors/index.js +28 -28
- package/dist/contradiction/index.js +4 -4
- package/dist/convergence-refresh.js +26 -26
- package/dist/corpus-watermark.js +28 -28
- package/dist/dashboard-runtime.js +2 -2
- package/dist/deep-recall-config.js +2 -2
- package/dist/deep-recall-http-glue.js +12 -12
- package/dist/entity-retrieval.js +25 -25
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.js +6 -6
- package/dist/external-wiki-access.js +91 -89
- package/dist/extraction-evidence-class.js +2 -2
- package/dist/extraction-quote-grounding.d.ts +5 -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.d.ts +3 -1
- package/dist/extraction-source-grounding-rules.d.ts.map +1 -1
- package/dist/extraction-source-grounding-rules.js +2 -2
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts.map +1 -1
- package/dist/extraction-source-grounding.js +5 -5
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction.d.ts.map +1 -1
- package/dist/extraction.js +43 -43
- package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
- package/dist/{graph-edge-decay-CXTNJ7IQ.js → graph-edge-decay-EKWXRBWF.js} +5 -5
- package/dist/graph-edge-decay-EKWXRBWF.js.map +1 -0
- package/dist/graph-jsonl.js +3 -3
- package/dist/graph-snapshot.js +3 -3
- package/dist/graph.js +2 -2
- package/dist/harmonic-retrieval.js +24 -24
- package/dist/index.js +255 -251
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +5 -5
- package/dist/local-llm.js +3 -3
- package/dist/location/index.js +28 -28
- package/dist/maintenance/memory-governance.js +24 -24
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +24 -24
- package/dist/maintenance/rebuild-memory-projection-runner.js +26 -26
- package/dist/maintenance/rebuild-memory-projection.js +25 -25
- package/dist/memory-browse-http-glue.js +2 -2
- package/dist/memory-browse-operations.js +91 -89
- package/dist/memory-browse.js +5 -5
- package/dist/memory-subject.js +2 -2
- package/dist/namespaces/migrate.js +25 -25
- package/dist/namespaces/search.d.ts +9 -0
- package/dist/namespaces/search.d.ts.map +1 -1
- package/dist/namespaces/search.js +13 -7
- package/dist/namespaces/storage.js +24 -24
- package/dist/offline-sync-changeset-generations.js +3 -3
- package/dist/offline-sync-embedding-generation.js +2 -2
- package/dist/offline-sync-impression-drain.js +2 -2
- package/dist/offline-sync-snapshot-stream.js +6 -6
- package/dist/offline-sync.js +5 -5
- package/dist/operator-doctor-corpus.js +31 -31
- package/dist/operator-doctor-replica.js +4 -4
- package/dist/operator-toolkit.js +49 -49
- package/dist/orchestration/backlog-scheduler.d.ts +3 -2
- package/dist/orchestration/backlog-scheduler.d.ts.map +1 -1
- package/dist/orchestration/backlog-scheduler.js +209 -205
- package/dist/orchestration/backlog-scheduler.js.map +1 -1
- package/dist/orchestration/buffer-drain-runner.d.ts +6 -1
- package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
- package/dist/orchestration/buffer-drain-runner.js +199 -195
- package/dist/orchestration/extraction-persist.d.ts.map +1 -1
- package/dist/orchestration/fact-dedup-window.d.ts +1 -4
- package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +41 -41
- package/dist/orchestration/turn-ingestion.d.ts +2 -9
- package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
- package/dist/orchestrator.js +198 -194
- package/dist/provenance.js +2 -2
- package/dist/qmd.js +4 -4
- package/dist/recall-navigation-http-glue.js +2 -2
- package/dist/recall-navigation-operations.js +91 -89
- package/dist/recall-navigation.js +90 -88
- package/dist/recall-state-view-admission.js +5 -5
- package/dist/recall-why-service.js +5 -5
- package/dist/reconcile/manifest.js +2 -2
- package/dist/replica-divergence.js +3 -3
- package/dist/{resolution-BNWE5Y25.js → resolution-23ZBDM6A.js} +5 -5
- package/dist/resolution-23ZBDM6A.js.map +1 -0
- package/dist/resume-bundles.js +36 -36
- package/dist/salvage-envelope.js +4 -4
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +12 -12
- package/dist/semantic-consolidation.js +25 -25
- package/dist/semantic-rule-promotion.js +24 -24
- package/dist/semantic-rule-verifier.js +24 -24
- package/dist/source-agent-qualifier.d.ts +2 -0
- package/dist/source-agent-qualifier.d.ts.map +1 -1
- package/dist/source-agent-qualifier.js +36 -36
- package/dist/standup/build.js +25 -25
- package/dist/storage/buffer-drain-claims.d.ts +18 -6
- package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
- package/dist/storage/buffer-drain-claims.js +2 -2
- package/dist/storage/buffer-session-files.js +2 -2
- package/dist/storage/content-hash-index.d.ts +8 -0
- package/dist/storage/content-hash-index.d.ts.map +1 -1
- package/dist/storage.d.ts +10 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +23 -23
- package/dist/subject-config.js +3 -3
- package/dist/summarizer.js +4 -4
- package/dist/support-passport/index.js +93 -91
- package/dist/tokens.js +3 -3
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +3 -3
- package/dist/transfer/capsule-gunzip.d.ts +14 -0
- package/dist/transfer/capsule-gunzip.d.ts.map +1 -0
- package/dist/transfer/capsule-import.d.ts.map +1 -1
- package/dist/transfer/capsule-import.js +3 -2
- package/dist/transfer/capsule-merge.d.ts.map +1 -1
- package/dist/transfer/export-okf.js +24 -24
- package/dist/verified-recall.js +24 -24
- package/dist/who-knows-cli.js +26 -26
- package/dist/who-knows.js +25 -25
- package/dist/write-envelope.js +3 -3
- package/package.json +2 -2
- package/src/access-boundary.ts +15 -0
- package/src/access-errors.ts +22 -0
- package/src/access-fleet-wide-work-peer.test.ts +236 -0
- package/src/access-http-peer-fleet-wide.test.ts +192 -0
- package/src/access-http.test.ts +249 -4
- package/src/access-http.ts +114 -39
- package/src/access-mcp-frames.test.ts +47 -0
- package/src/access-mcp-frames.ts +74 -0
- package/src/access-mcp.test.ts +210 -0
- package/src/access-mcp.ts +47 -37
- package/src/access-observe-replay-durability.test.ts +40 -4
- package/src/access-observe-write-surface.ts +4 -4
- package/src/access-operations-batch.ts +23 -12
- package/src/access-proto-injection.test.ts +340 -0
- package/src/access-schema.ts +31 -5
- package/src/access-service-capsule-import.test.ts +235 -0
- package/src/access-service.ts +6 -45
- package/src/backlog-scheduler.test.ts +443 -3
- package/src/briefing-deadline.ts +5 -4
- package/src/buffer-admission.ts +45 -0
- package/src/buffer-eviction-backpressure.test.ts +211 -0
- package/src/buffer-eviction.ts +8 -14
- package/src/buffer-incremental-persistence.test.ts +25 -37
- package/src/buffer-persistence.ts +15 -6
- package/src/buffer-session.test.ts +32 -16
- package/src/buffer-turn-helpers.ts +47 -0
- package/src/buffer-types.ts +28 -1
- package/src/buffer.ts +83 -91
- package/src/capsule-import-gate.ts +109 -0
- package/src/cli.ts +4 -4
- package/src/extraction-quote-grounding.test.ts +125 -0
- package/src/extraction-quote-grounding.ts +99 -25
- package/src/extraction-source-grounding-rules.ts +6 -1
- package/src/extraction-source-grounding.ts +2 -0
- package/src/extraction.ts +3 -4
- package/src/fact-dedup-window.test.ts +103 -20
- package/src/namespaces/search.test.ts +480 -1
- package/src/namespaces/search.ts +93 -10
- package/src/orchestration/backlog-scheduler.ts +13 -12
- package/src/orchestration/buffer-drain-runner.ts +40 -16
- package/src/orchestration/extraction-persist.ts +2 -10
- package/src/orchestration/fact-dedup-window.ts +5 -12
- package/src/orchestration/turn-ingestion.ts +38 -39
- package/src/recall-navigation-operations.ts +9 -2
- package/src/source-agent-qualifier.ts +3 -0
- package/src/storage/buffer-drain-claims.test.ts +330 -6
- package/src/storage/buffer-drain-claims.ts +226 -80
- package/src/storage/buffer-session-files.ts +2 -2
- package/src/storage/content-hash-index.ts +9 -0
- package/src/storage.ts +15 -0
- package/src/transfer/capsule-gunzip.ts +39 -0
- package/src/transfer/capsule-import.test.ts +73 -0
- package/src/transfer/capsule-import.ts +2 -2
- package/src/transfer/capsule-merge.test.ts +34 -0
- package/src/transfer/capsule-merge.ts +2 -10
- package/dist/capsule-merge-XVTQJJ6R.js.map +0 -1
- package/dist/chunk-2XJZLSXE.js.map +0 -1
- package/dist/chunk-3JPTUCD7.js.map +0 -1
- package/dist/chunk-4KLQI3L2.js.map +0 -1
- package/dist/chunk-6LCWKS62.js.map +0 -1
- package/dist/chunk-77R7BQZ2.js.map +0 -1
- package/dist/chunk-7PMOCQOL.js.map +0 -1
- package/dist/chunk-B74NPUHT.js.map +0 -1
- package/dist/chunk-D5SSBCYY.js.map +0 -1
- package/dist/chunk-HLKUZW42.js.map +0 -1
- package/dist/chunk-HWHNQVRV.js.map +0 -1
- package/dist/chunk-KASJJGRG.js.map +0 -1
- package/dist/chunk-KQUSP6LB.js.map +0 -1
- package/dist/chunk-LGEH473S.js.map +0 -1
- package/dist/chunk-LU66I56Q.js.map +0 -1
- package/dist/chunk-MPDLLLOG.js.map +0 -1
- package/dist/chunk-MPGLM2VU.js.map +0 -1
- package/dist/chunk-QZBTIYKD.js.map +0 -1
- package/dist/chunk-VLMBSGQF.js.map +0 -1
- package/dist/chunk-WAD32CZG.js.map +0 -1
- package/dist/chunk-WC475JOG.js.map +0 -1
- package/dist/chunk-X2KO4FK5.js.map +0 -1
- package/dist/chunk-XYP5WBDO.js.map +0 -1
- package/dist/chunk-YK7GOPIE.js.map +0 -1
- package/dist/chunk-Z3DDQXM4.js.map +0 -1
- /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-mcp-frames.js.map} +0 -0
- /package/dist/{auto-sync-C7E6G7Y2.js.map → auto-sync-3AZW7MXI.js.map} +0 -0
- /package/dist/{graph-edge-decay-CXTNJ7IQ.js.map → buffer-admission.js.map} +0 -0
- /package/dist/{resolution-BNWE5Y25.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{chunk-MSIRVXQ3.js.map → chunk-2G4TV2ML.js.map} +0 -0
- /package/dist/{chunk-IR4C3DP2.js.map → chunk-2K3AUDH7.js.map} +0 -0
- /package/dist/{chunk-2IW5PT7U.js.map → chunk-2N4OUSIA.js.map} +0 -0
- /package/dist/{chunk-BXLOS5AJ.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-QZSIDSB2.js.map → chunk-2NUDHVYM.js.map} +0 -0
- /package/dist/{chunk-HPFVZPRH.js.map → chunk-2U4LMSEX.js.map} +0 -0
- /package/dist/{chunk-WHQXWFH7.js.map → chunk-2XOFVTTT.js.map} +0 -0
- /package/dist/{chunk-I6ORDT54.js.map → chunk-32CA7B6L.js.map} +0 -0
- /package/dist/{chunk-T77AX6DG.js.map → chunk-32YX7EJ3.js.map} +0 -0
- /package/dist/{chunk-HLWXCDUQ.js.map → chunk-3DAWHP5K.js.map} +0 -0
- /package/dist/{chunk-ZJJFUEPO.js.map → chunk-3PJEDHLD.js.map} +0 -0
- /package/dist/{chunk-PJYK7HH2.js.map → chunk-3UZ2VMKD.js.map} +0 -0
- /package/dist/{chunk-IN4OFAHX.js.map → chunk-3Y5X6YFH.js.map} +0 -0
- /package/dist/{chunk-YUTT4ZTI.js.map → chunk-45VUHDV2.js.map} +0 -0
- /package/dist/{chunk-T6T2RMWW.js.map → chunk-4GMI5HFH.js.map} +0 -0
- /package/dist/{chunk-YOEYL6CH.js.map → chunk-4LCVA2LW.js.map} +0 -0
- /package/dist/{chunk-66JOLMWS.js.map → chunk-4X7HQKAG.js.map} +0 -0
- /package/dist/{chunk-65SKM4BF.js.map → chunk-4XYAWK5H.js.map} +0 -0
- /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
- /package/dist/{chunk-TPX7XXY4.js.map → chunk-5SOSQTVE.js.map} +0 -0
- /package/dist/{chunk-G3T6YMBO.js.map → chunk-64S6X4IU.js.map} +0 -0
- /package/dist/{chunk-BK7AJUWB.js.map → chunk-6BCWFCDG.js.map} +0 -0
- /package/dist/{chunk-V27FK6D6.js.map → chunk-6XTUZKCZ.js.map} +0 -0
- /package/dist/{chunk-LECSS7EE.js.map → chunk-73UZZFJC.js.map} +0 -0
- /package/dist/{chunk-4EACCIPO.js.map → chunk-77PDJ4OE.js.map} +0 -0
- /package/dist/{chunk-GQZSGXR6.js.map → chunk-7GSL4LBG.js.map} +0 -0
- /package/dist/{chunk-B6SPPYGJ.js.map → chunk-7IWRLYFJ.js.map} +0 -0
- /package/dist/{chunk-R45L4TV6.js.map → chunk-7LCGFUMB.js.map} +0 -0
- /package/dist/{chunk-A7BM2JTH.js.map → chunk-AJHNXIQU.js.map} +0 -0
- /package/dist/{chunk-2D7BTSDW.js.map → chunk-ANZZLRUX.js.map} +0 -0
- /package/dist/{chunk-ZXF3O7P4.js.map → chunk-ARRTLPRC.js.map} +0 -0
- /package/dist/{chunk-S5CQI2KF.js.map → chunk-B3GKQ6DU.js.map} +0 -0
- /package/dist/{chunk-XRBHIR7Q.js.map → chunk-B56LORLY.js.map} +0 -0
- /package/dist/{chunk-KO3YBHOV.js.map → chunk-BIVKPQ6Q.js.map} +0 -0
- /package/dist/{chunk-54FDG7K7.js.map → chunk-C2HORX76.js.map} +0 -0
- /package/dist/{chunk-E33UJHY3.js.map → chunk-CN7Q3T2J.js.map} +0 -0
- /package/dist/{chunk-YUTG35M4.js.map → chunk-CYCEROPR.js.map} +0 -0
- /package/dist/{chunk-TFLPDNQV.js.map → chunk-D5EV6Y2L.js.map} +0 -0
- /package/dist/{chunk-BFBS7OFQ.js.map → chunk-DC55RIEZ.js.map} +0 -0
- /package/dist/{chunk-B4ZU34JQ.js.map → chunk-DT7SNJH6.js.map} +0 -0
- /package/dist/{chunk-FVRTCYZ3.js.map → chunk-EA2LA5RI.js.map} +0 -0
- /package/dist/{chunk-WBN66HPI.js.map → chunk-ELHJK7LM.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-YAUHSNPB.js.map → chunk-F74G4KIH.js.map} +0 -0
- /package/dist/{chunk-BV2BRHD4.js.map → chunk-FHGWKODN.js.map} +0 -0
- /package/dist/{chunk-KESJ5XX3.js.map → chunk-G54BOKZ6.js.map} +0 -0
- /package/dist/{chunk-K36RMINQ.js.map → chunk-GOZXQNIM.js.map} +0 -0
- /package/dist/{chunk-OQSSQOKQ.js.map → chunk-GULUWOA5.js.map} +0 -0
- /package/dist/{chunk-K6RTQZA4.js.map → chunk-HFUKXOUW.js.map} +0 -0
- /package/dist/{chunk-WV254W4V.js.map → chunk-HLN2HOXX.js.map} +0 -0
- /package/dist/{chunk-FUOAIJKQ.js.map → chunk-IBDWYRDR.js.map} +0 -0
- /package/dist/{chunk-AJG2TAMR.js.map → chunk-IEEV27BC.js.map} +0 -0
- /package/dist/{chunk-LTDLJCMQ.js.map → chunk-IJ3OEECW.js.map} +0 -0
- /package/dist/{chunk-BK4FBHZ3.js.map → chunk-IPD5GC7C.js.map} +0 -0
- /package/dist/{chunk-KFLD7ZZG.js.map → chunk-IUKYFTGW.js.map} +0 -0
- /package/dist/{chunk-GCAQ3VUJ.js.map → chunk-JM2YZKCQ.js.map} +0 -0
- /package/dist/{chunk-BNQJKWWZ.js.map → chunk-K3FH3LAQ.js.map} +0 -0
- /package/dist/{chunk-RZH5RVJC.js.map → chunk-MTTYIXSU.js.map} +0 -0
- /package/dist/{chunk-XHGYZRVW.js.map → chunk-NIVKG3DS.js.map} +0 -0
- /package/dist/{chunk-VZELUMKK.js.map → chunk-NQP4ZYGG.js.map} +0 -0
- /package/dist/{chunk-YO2JEX2Z.js.map → chunk-OGBQMY46.js.map} +0 -0
- /package/dist/{chunk-TLHF75OR.js.map → chunk-OL553AWT.js.map} +0 -0
- /package/dist/{chunk-CLVILSBR.js.map → chunk-OZNBZJ3S.js.map} +0 -0
- /package/dist/{chunk-VUROWXFL.js.map → chunk-P452SEKO.js.map} +0 -0
- /package/dist/{chunk-JX3E4XCW.js.map → chunk-PDOOUXCS.js.map} +0 -0
- /package/dist/{chunk-6QYPL6NI.js.map → chunk-PKZ274PI.js.map} +0 -0
- /package/dist/{chunk-KJCL2FZO.js.map → chunk-QD5R6U5J.js.map} +0 -0
- /package/dist/{chunk-JRVMCZE3.js.map → chunk-QEYTRFR3.js.map} +0 -0
- /package/dist/{chunk-VKDAJJQP.js.map → chunk-QEZ3DW5O.js.map} +0 -0
- /package/dist/{chunk-WYPCJK6K.js.map → chunk-QKERFEJS.js.map} +0 -0
- /package/dist/{chunk-BDN3TDGV.js.map → chunk-QKES7GU6.js.map} +0 -0
- /package/dist/{chunk-PCQWKMOL.js.map → chunk-QNSZYTSG.js.map} +0 -0
- /package/dist/{chunk-IZJQNDGI.js.map → chunk-R6HMAIKM.js.map} +0 -0
- /package/dist/{chunk-NUSAAN75.js.map → chunk-ROKDHO74.js.map} +0 -0
- /package/dist/{chunk-FF6N7BXC.js.map → chunk-S45INSV3.js.map} +0 -0
- /package/dist/{chunk-2RUTN7PR.js.map → chunk-S7GJNA3K.js.map} +0 -0
- /package/dist/{chunk-XT45WCKX.js.map → chunk-SYKJBS7S.js.map} +0 -0
- /package/dist/{chunk-5BE3FLKL.js.map → chunk-TAUHAP5J.js.map} +0 -0
- /package/dist/{chunk-RJWL3RHB.js.map → chunk-TFEHSPGQ.js.map} +0 -0
- /package/dist/{chunk-K3B72DKJ.js.map → chunk-TRMZHGIX.js.map} +0 -0
- /package/dist/{chunk-WBDIZWJQ.js.map → chunk-TSS3D7ZY.js.map} +0 -0
- /package/dist/{chunk-HQPFACXO.js.map → chunk-UH3FPX3L.js.map} +0 -0
- /package/dist/{chunk-GGJFRVRD.js.map → chunk-US7KPQA5.js.map} +0 -0
- /package/dist/{chunk-FJ2VJTGA.js.map → chunk-UWRHHRDK.js.map} +0 -0
- /package/dist/{chunk-SDESKFKT.js.map → chunk-UYHDMCY2.js.map} +0 -0
- /package/dist/{chunk-3T3VHXUH.js.map → chunk-VKILERSL.js.map} +0 -0
- /package/dist/{chunk-2TGIKMMZ.js.map → chunk-WXDTOE6F.js.map} +0 -0
- /package/dist/{chunk-KQLBXUUQ.js.map → chunk-WYG5UNDY.js.map} +0 -0
- /package/dist/{chunk-DQEMWVMT.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
- /package/dist/{chunk-QA3Y2O6H.js.map → chunk-YAVIBVCU.js.map} +0 -0
- /package/dist/{chunk-VPEIWMUT.js.map → chunk-YLTU2DC4.js.map} +0 -0
- /package/dist/{chunk-EBOHFZI4.js.map → chunk-YRZPBN46.js.map} +0 -0
- /package/dist/{chunk-FMFCQRDM.js.map → chunk-ZANNIXXE.js.map} +0 -0
- /package/dist/{chunk-HQGATKG2.js.map → chunk-ZBFLV657.js.map} +0 -0
- /package/dist/{chunk-6L63IN3M.js.map → chunk-ZOJUWVAU.js.map} +0 -0
- /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
package/src/access-mcp.test.ts
CHANGED
|
@@ -10,12 +10,16 @@ import assert from "node:assert/strict";
|
|
|
10
10
|
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
11
11
|
import os from "node:os";
|
|
12
12
|
import path from "node:path";
|
|
13
|
+
import { PassThrough } from "node:stream";
|
|
13
14
|
import test from "node:test";
|
|
14
15
|
|
|
15
16
|
import { EngramMcpServer } from "./access-mcp.js";
|
|
17
|
+
import { MCP_STDIO_MAX_FRAME_BYTES } from "./access-mcp-frames.js";
|
|
18
|
+
import { EngramAccessForbiddenError } from "./access-errors.js";
|
|
16
19
|
import { tokenCapabilityStore } from "./access-token-capabilities.js";
|
|
17
20
|
import { EngramAccessInputError, type EngramAccessService } from "./access-service.js";
|
|
18
21
|
import { parseConfig } from "./config.js";
|
|
22
|
+
import { initLogger, resetLogger } from "./logger.js";
|
|
19
23
|
import { readPair, writePair } from "./contradiction/contradiction-review.js";
|
|
20
24
|
import type { StorageManager } from "./storage.js";
|
|
21
25
|
|
|
@@ -105,6 +109,46 @@ function makeToolRequest(name: string, args: Record<string, unknown> = {}): Reco
|
|
|
105
109
|
},
|
|
106
110
|
};
|
|
107
111
|
}
|
|
112
|
+
test("MCP reports buffer admission refusal as retryable and does not return success", async () => {
|
|
113
|
+
let attempts = 0;
|
|
114
|
+
const refusal = new Error("buffer at session-entry cap");
|
|
115
|
+
refusal.name = "ReplayAdmissionRefusedError";
|
|
116
|
+
// SAFETY: the mock overrides only observe; every other MCP dispatch path is unreachable in this test.
|
|
117
|
+
const service = Object.assign({} as EngramAccessService, {
|
|
118
|
+
observe: async () => {
|
|
119
|
+
attempts += 1;
|
|
120
|
+
if (attempts === 1) throw refusal;
|
|
121
|
+
return { ok: true };
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
const server = new EngramMcpServer(service);
|
|
125
|
+
const request = makeToolRequest("engram.observe", {
|
|
126
|
+
sessionKey: "capacity-test-session",
|
|
127
|
+
messages: [{ role: "user", content: "retry me" }],
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const refused = await server.handleRequest(request);
|
|
131
|
+
// SAFETY: a tools/call request always resolves to a JSON-RPC result envelope; only isError and structuredContent are asserted.
|
|
132
|
+
const refusedResult = refused as {
|
|
133
|
+
result?: {
|
|
134
|
+
isError?: boolean;
|
|
135
|
+
structuredContent?: { code?: string; retryable?: boolean };
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
assert.equal(refusedResult.result?.isError, true, "refused admission must not be acknowledged as success");
|
|
139
|
+
// SAFETY: same tools/call result envelope as the assertion above.
|
|
140
|
+
assert.deepEqual(refusedResult.result?.structuredContent, {
|
|
141
|
+
code: "buffer_admission_refused",
|
|
142
|
+
retryable: true,
|
|
143
|
+
}, JSON.stringify(refused));
|
|
144
|
+
|
|
145
|
+
const retried = await server.handleRequest(request);
|
|
146
|
+
// SAFETY: same tools/call result envelope as refusedResult above.
|
|
147
|
+
const retriedResult = retried as typeof refusedResult;
|
|
148
|
+
assert.equal(retriedResult.result?.isError, false);
|
|
149
|
+
assert.equal(attempts, 2, "the same request can be retried after capacity is available");
|
|
150
|
+
});
|
|
151
|
+
|
|
108
152
|
|
|
109
153
|
// ──────────────────────────────────────────────────────────────────────────
|
|
110
154
|
// Finding 2 (#396 new): invalid MCP briefing format values must be rejected
|
|
@@ -1291,6 +1335,17 @@ const FLEET_WIDE_MAINTENANCE_TOOLS = [
|
|
|
1291
1335
|
"compounding_promote_candidate",
|
|
1292
1336
|
"compression_guidelines_optimize",
|
|
1293
1337
|
"compression_guidelines_activate",
|
|
1338
|
+
// Issue #3225 B3: work and peer ops run against GLOBAL stores, so a
|
|
1339
|
+
// namespace argument cannot isolate them — same fleet-wide rule.
|
|
1340
|
+
"work_task",
|
|
1341
|
+
"work_project",
|
|
1342
|
+
"work_board",
|
|
1343
|
+
"peer_list",
|
|
1344
|
+
"peer_get",
|
|
1345
|
+
"peer_set",
|
|
1346
|
+
"peer_delete",
|
|
1347
|
+
"peer_profile_get",
|
|
1348
|
+
"peer_forget",
|
|
1294
1349
|
] as const;
|
|
1295
1350
|
|
|
1296
1351
|
test("MCP fleet-wide maintenance ops: namespace-scoped token denied for every op, no side effect (issue #1850 round 10)", async () => {
|
|
@@ -1315,6 +1370,19 @@ test("MCP fleet-wide maintenance ops: namespace-scoped token denied for every op
|
|
|
1315
1370
|
compressionGuidelinesOptimize: track,
|
|
1316
1371
|
compressionGuidelinesActivate: track,
|
|
1317
1372
|
} as unknown as EngramAccessService;
|
|
1373
|
+
// Issue #3225 B3: track the global-store methods the fleet-wide guard must
|
|
1374
|
+
// block; the dispatch reads them off this same object at call time.
|
|
1375
|
+
Object.assign(service, {
|
|
1376
|
+
workTask: track,
|
|
1377
|
+
workProject: track,
|
|
1378
|
+
workBoard: track,
|
|
1379
|
+
peerList: track,
|
|
1380
|
+
peerGet: track,
|
|
1381
|
+
peerSet: track,
|
|
1382
|
+
peerDelete: track,
|
|
1383
|
+
peerProfileGet: track,
|
|
1384
|
+
peerForget: track,
|
|
1385
|
+
});
|
|
1318
1386
|
const server = new EngramMcpServer(service, { principal: "ops" });
|
|
1319
1387
|
try {
|
|
1320
1388
|
for (const tool of FLEET_WIDE_MAINTENANCE_TOOLS) {
|
|
@@ -1451,3 +1519,145 @@ test("MCP external_wiki_search rejects an empty query before service dispatch",
|
|
|
1451
1519
|
assert.equal(readMcpResult(response).isError, true);
|
|
1452
1520
|
assert.equal(configRead, false);
|
|
1453
1521
|
});
|
|
1522
|
+
|
|
1523
|
+
// ===========================================================================
|
|
1524
|
+
// Issue #3225 C5 — tools/call catch-all must not leak unexpected Error.message
|
|
1525
|
+
// (paths from fs/backend). Intended typed access errors stay readable.
|
|
1526
|
+
// ===========================================================================
|
|
1527
|
+
|
|
1528
|
+
function serviceWithRecallThrow(error: Error): EngramAccessService {
|
|
1529
|
+
const service = makeMockService();
|
|
1530
|
+
service.recall = async () => {
|
|
1531
|
+
throw error;
|
|
1532
|
+
};
|
|
1533
|
+
return service;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
test("MCP tools/call hides unexpected Error.message paths behind internal error", async () => {
|
|
1537
|
+
const secretPath = "/home/secret/vault/token.pem";
|
|
1538
|
+
const service = serviceWithRecallThrow(
|
|
1539
|
+
new Error(`ENOENT: no such file or directory, open '${secretPath}'`),
|
|
1540
|
+
);
|
|
1541
|
+
|
|
1542
|
+
const logLines: string[] = [];
|
|
1543
|
+
initLogger(
|
|
1544
|
+
{
|
|
1545
|
+
info() {},
|
|
1546
|
+
warn() {},
|
|
1547
|
+
error(msg: string) {
|
|
1548
|
+
logLines.push(msg);
|
|
1549
|
+
},
|
|
1550
|
+
},
|
|
1551
|
+
false,
|
|
1552
|
+
{ timestamps: false },
|
|
1553
|
+
);
|
|
1554
|
+
try {
|
|
1555
|
+
const server = new EngramMcpServer(service);
|
|
1556
|
+
const outcome = readCallToolOutcome(
|
|
1557
|
+
await server.handleRequest(makeToolRequest("engram.recall", { query: "anything" })),
|
|
1558
|
+
);
|
|
1559
|
+
assert.equal(outcome.isError, true);
|
|
1560
|
+
assert.equal(outcome.text, "internal error");
|
|
1561
|
+
assert.equal(outcome.text.includes(secretPath), false);
|
|
1562
|
+
assert.ok(
|
|
1563
|
+
logLines.some((line) => line.includes(secretPath)),
|
|
1564
|
+
"server log must retain the unexpected detail",
|
|
1565
|
+
);
|
|
1566
|
+
} finally {
|
|
1567
|
+
resetLogger();
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
test("MCP tools/call keeps intended EngramAccessInputError and ForbiddenError messages", async () => {
|
|
1572
|
+
const inputOutcome = readCallToolOutcome(
|
|
1573
|
+
await new EngramMcpServer(serviceWithRecallThrow(new EngramAccessInputError("memory not found: mem-42"))).handleRequest(
|
|
1574
|
+
makeToolRequest("engram.recall", { query: "anything" }),
|
|
1575
|
+
),
|
|
1576
|
+
);
|
|
1577
|
+
assert.equal(inputOutcome.isError, true);
|
|
1578
|
+
assert.equal(inputOutcome.text, "memory not found: mem-42");
|
|
1579
|
+
|
|
1580
|
+
const forbiddenOutcome = readCallToolOutcome(
|
|
1581
|
+
await new EngramMcpServer(
|
|
1582
|
+
serviceWithRecallThrow(new EngramAccessForbiddenError("token is not permitted to call operation: recall")),
|
|
1583
|
+
).handleRequest(makeToolRequest("engram.recall", { query: "anything" })),
|
|
1584
|
+
);
|
|
1585
|
+
assert.equal(forbiddenOutcome.isError, true);
|
|
1586
|
+
assert.equal(forbiddenOutcome.text, "token is not permitted to call operation: recall");
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1589
|
+
// ===========================================================================
|
|
1590
|
+
// Issue #3225 N1 — stdio framing is bounded, refuses an oversized frame,
|
|
1591
|
+
// and keeps answering the frames that follow it.
|
|
1592
|
+
// ===========================================================================
|
|
1593
|
+
|
|
1594
|
+
type StdioMessage = { id?: number | null; result?: object; error?: { message: string } };
|
|
1595
|
+
type StdioRequest = { jsonrpc: "2.0"; id: number; method: string };
|
|
1596
|
+
|
|
1597
|
+
function stdioFrame(payload: StdioRequest): string {
|
|
1598
|
+
const body = JSON.stringify(payload);
|
|
1599
|
+
return `Content-Length: ${Buffer.byteLength(body, "utf-8")}\r\n\r\n${body}`;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
function readStdioMessages(raw: string): StdioMessage[] {
|
|
1603
|
+
const messages: StdioMessage[] = [];
|
|
1604
|
+
let rest = raw;
|
|
1605
|
+
while (rest.length > 0) {
|
|
1606
|
+
const headerEnd = rest.indexOf("\r\n\r\n");
|
|
1607
|
+
assert.ok(headerEnd >= 0, "server output must be Content-Length framed");
|
|
1608
|
+
const length = Number.parseInt(rest.slice(0, headerEnd).split(":")[1] ?? "", 10);
|
|
1609
|
+
const message: StdioMessage = JSON.parse(rest.slice(headerEnd + 4, headerEnd + 4 + length));
|
|
1610
|
+
messages.push(message);
|
|
1611
|
+
rest = rest.slice(headerEnd + 4 + length);
|
|
1612
|
+
}
|
|
1613
|
+
return messages;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
/** Feeds each chunk in its own tick so the server sees separate data events. */
|
|
1617
|
+
async function runStdioChunks(chunks: string[]): Promise<StdioMessage[]> {
|
|
1618
|
+
const input = new PassThrough();
|
|
1619
|
+
const output = new PassThrough();
|
|
1620
|
+
output.setEncoding("utf8");
|
|
1621
|
+
let raw = "";
|
|
1622
|
+
output.on("data", (chunk: string) => {
|
|
1623
|
+
raw += chunk;
|
|
1624
|
+
});
|
|
1625
|
+
const running = new EngramMcpServer(makeMockService()).runStdio(input, output);
|
|
1626
|
+
for (const chunk of chunks) {
|
|
1627
|
+
input.write(chunk);
|
|
1628
|
+
await new Promise<void>((resolve) => setImmediate(resolve));
|
|
1629
|
+
}
|
|
1630
|
+
input.end();
|
|
1631
|
+
await running;
|
|
1632
|
+
return readStdioMessages(raw);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
const stdioPing = (id: number) => stdioFrame({ jsonrpc: "2.0", id, method: "ping" });
|
|
1636
|
+
|
|
1637
|
+
test("MCP stdio refuses an oversized Content-Length, discards its body, and answers the next frame", async () => {
|
|
1638
|
+
const declared = MCP_STDIO_MAX_FRAME_BYTES + 1;
|
|
1639
|
+
const messages = await runStdioChunks([
|
|
1640
|
+
`Content-Length: ${declared}\r\n\r\n`,
|
|
1641
|
+
"x".repeat(1000),
|
|
1642
|
+
"x".repeat(declared - 1000),
|
|
1643
|
+
stdioPing(7),
|
|
1644
|
+
]);
|
|
1645
|
+
assert.equal(messages.length, 2);
|
|
1646
|
+
assert.match(messages[0]?.error?.message ?? "", /frame too large/);
|
|
1647
|
+
assert.equal(messages[1]?.id, 7);
|
|
1648
|
+
assert.ok(messages[1]?.result, "the frame after the refused one is answered normally");
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
test("MCP stdio refuses a header that never terminates and answers the next frame", async () => {
|
|
1652
|
+
const messages = await runStdioChunks(["A".repeat(MCP_STDIO_MAX_FRAME_BYTES + 1), stdioPing(8)]);
|
|
1653
|
+
assert.equal(messages.length, 2);
|
|
1654
|
+
assert.match(messages[0]?.error?.message ?? "", /frame too large/);
|
|
1655
|
+
assert.equal(messages[1]?.id, 8);
|
|
1656
|
+
});
|
|
1657
|
+
|
|
1658
|
+
test("MCP stdio reassembles a frame split inside its header and inside its body", async () => {
|
|
1659
|
+
const frame = stdioPing(9);
|
|
1660
|
+
const messages = await runStdioChunks([frame.slice(0, 6), frame.slice(6, frame.length - 4), frame.slice(frame.length - 4)]);
|
|
1661
|
+
assert.equal(messages.length, 1);
|
|
1662
|
+
assert.equal(messages[0]?.id, 9);
|
|
1663
|
+
});
|
package/src/access-mcp.ts
CHANGED
|
@@ -30,7 +30,9 @@ import {
|
|
|
30
30
|
validateRequest,
|
|
31
31
|
} from "./access-schema.js";
|
|
32
32
|
import { EngramAccessInputError, type EngramAccessRecallResponse, type EngramAccessService } from "./access-service.js";
|
|
33
|
-
import { unknownToolError } from "./access-errors.js";
|
|
33
|
+
import { EngramAccessForbiddenError, unknownToolError } from "./access-errors.js";
|
|
34
|
+
import { log } from "./logger.js";
|
|
35
|
+
import { McpFrameReader } from "./access-mcp-frames.js";
|
|
34
36
|
import "./access-operations.js";
|
|
35
37
|
import { validateBriefingFormat } from "./briefing.js";
|
|
36
38
|
import { processChatMessage } from "./chat/chat-factory.js";
|
|
@@ -82,12 +84,32 @@ type JsonRpcRequest = {
|
|
|
82
84
|
params?: Record<string, unknown>;
|
|
83
85
|
};
|
|
84
86
|
|
|
87
|
+
/** tools/call failure envelope; admission backpressure adds a retryable code. */
|
|
88
|
+
type McpToolErrorResult = {
|
|
89
|
+
content: Array<{ type: string; text: string }>;
|
|
90
|
+
isError: boolean;
|
|
91
|
+
structuredContent?: { code: string; retryable: boolean };
|
|
92
|
+
};
|
|
93
|
+
|
|
85
94
|
function throwMcpAbort(signal: AbortSignal | undefined, message: string): void {
|
|
86
95
|
if (!signal?.aborted) return;
|
|
87
96
|
if (isAbortError(signal.reason)) throw signal.reason;
|
|
88
97
|
throw abortError(message);
|
|
89
98
|
}
|
|
90
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Map tools/call failures to a client-safe message. Unexpected backend/fs
|
|
102
|
+
* errors become a stable generic string; detail stays in the server log.
|
|
103
|
+
* Intended EngramAccessInputError / EngramAccessForbiddenError stay readable.
|
|
104
|
+
*/
|
|
105
|
+
function mcpToolErrorMessage(err: Error): string {
|
|
106
|
+
if (err instanceof EngramAccessInputError || err instanceof EngramAccessForbiddenError) {
|
|
107
|
+
return err.message;
|
|
108
|
+
}
|
|
109
|
+
log.error("engram MCP tools/call internal error", err);
|
|
110
|
+
return "internal error";
|
|
111
|
+
}
|
|
112
|
+
|
|
91
113
|
type McpRequestOptions = {
|
|
92
114
|
principalOverride?: string;
|
|
93
115
|
namespaceOverride?: string;
|
|
@@ -406,7 +428,7 @@ function parseMcpRequest<N extends SchemaName>(schemaName: N, args: Record<strin
|
|
|
406
428
|
function optionalNonEmptyString(value: unknown, label: string): string | undefined {
|
|
407
429
|
if (value === undefined || value === null || value === "") return undefined;
|
|
408
430
|
if (typeof value !== "string") {
|
|
409
|
-
throw new
|
|
431
|
+
throw new EngramAccessInputError(`${label} expects a string; got ${JSON.stringify(value)}`);
|
|
410
432
|
}
|
|
411
433
|
return value;
|
|
412
434
|
}
|
|
@@ -420,11 +442,11 @@ function optionalNonEmptyString(value: unknown, label: string): string | undefin
|
|
|
420
442
|
function optionalPositiveInteger(value: unknown, label: string): number | undefined {
|
|
421
443
|
if (value === undefined || value === null || value === "") return undefined;
|
|
422
444
|
if (typeof value !== "number" && typeof value !== "string") {
|
|
423
|
-
throw new
|
|
445
|
+
throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
|
|
424
446
|
}
|
|
425
447
|
const parsed = typeof value === "number" ? value : Number(value);
|
|
426
448
|
if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
|
|
427
|
-
throw new
|
|
449
|
+
throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
|
|
428
450
|
}
|
|
429
451
|
return parsed;
|
|
430
452
|
}
|
|
@@ -447,7 +469,7 @@ async function getMcpServerVersion(): Promise<string> {
|
|
|
447
469
|
}
|
|
448
470
|
|
|
449
471
|
export class EngramMcpServer {
|
|
450
|
-
private
|
|
472
|
+
private readonly frames = new McpFrameReader();
|
|
451
473
|
private flushTask: Promise<void> | null = null;
|
|
452
474
|
private readonly tools: McpTool[];
|
|
453
475
|
private readonly resources: McpResource[];
|
|
@@ -2402,15 +2424,18 @@ export class EngramMcpServer {
|
|
|
2402
2424
|
// Cancellation is transport control flow, not a JSON-RPC tool error.
|
|
2403
2425
|
// Preserve the original AbortError so HTTP can silently end a dead socket.
|
|
2404
2426
|
if (isAbortError(err)) throw err;
|
|
2405
|
-
const message = err instanceof Error ? err
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
},
|
|
2427
|
+
const message = mcpToolErrorMessage(err instanceof Error ? err : new Error(String(err)));
|
|
2428
|
+
const admissionBackpressure =
|
|
2429
|
+
err instanceof Error &&
|
|
2430
|
+
(err.name === "ReplayAdmissionRefusedError" || err.name === "BufferAdmissionRefusedError");
|
|
2431
|
+
const result: McpToolErrorResult = {
|
|
2432
|
+
content: [{ type: "text", text: message }],
|
|
2433
|
+
isError: true,
|
|
2413
2434
|
};
|
|
2435
|
+
if (admissionBackpressure) {
|
|
2436
|
+
result.structuredContent = { code: "buffer_admission_refused", retryable: true };
|
|
2437
|
+
}
|
|
2438
|
+
return { jsonrpc: "2.0", id, result };
|
|
2414
2439
|
}
|
|
2415
2440
|
}
|
|
2416
2441
|
|
|
@@ -2426,7 +2451,7 @@ export class EngramMcpServer {
|
|
|
2426
2451
|
|
|
2427
2452
|
async runStdio(input: Readable, output: Writable): Promise<void> {
|
|
2428
2453
|
input.on("data", (chunk) => {
|
|
2429
|
-
this.
|
|
2454
|
+
this.frames.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2430
2455
|
this.scheduleFlush(output);
|
|
2431
2456
|
});
|
|
2432
2457
|
await new Promise<void>((resolve, reject) => {
|
|
@@ -2455,7 +2480,7 @@ export class EngramMcpServer {
|
|
|
2455
2480
|
if (this.flushTask === task) {
|
|
2456
2481
|
this.flushTask = null;
|
|
2457
2482
|
}
|
|
2458
|
-
if (this.
|
|
2483
|
+
if (this.frames.hasUnread()) {
|
|
2459
2484
|
this.scheduleFlush(output);
|
|
2460
2485
|
}
|
|
2461
2486
|
});
|
|
@@ -2463,30 +2488,15 @@ export class EngramMcpServer {
|
|
|
2463
2488
|
}
|
|
2464
2489
|
|
|
2465
2490
|
private async flushBuffer(output: Writable): Promise<void> {
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
const headers = headerText.split("\r\n");
|
|
2471
|
-
const contentLengthHeader = headers.find((line) => line.toLowerCase().startsWith("content-length:"));
|
|
2472
|
-
if (!contentLengthHeader) {
|
|
2473
|
-
this.buffer = Buffer.alloc(0);
|
|
2474
|
-
return;
|
|
2475
|
-
}
|
|
2476
|
-
const contentLength = Number.parseInt(contentLengthHeader.split(":")[1]?.trim() ?? "0", 10);
|
|
2477
|
-
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
2478
|
-
this.buffer = Buffer.alloc(0);
|
|
2479
|
-
return;
|
|
2491
|
+
for (let frame = this.frames.next(); frame; frame = this.frames.next()) {
|
|
2492
|
+
if (frame.kind === "too-large") {
|
|
2493
|
+
this.writeMessage(output, { jsonrpc: "2.0", id: null, error: { code: -32600, message: "frame too large" } });
|
|
2494
|
+
continue;
|
|
2480
2495
|
}
|
|
2481
|
-
const messageStart = headerEnd + 4;
|
|
2482
|
-
const messageEnd = messageStart + contentLength;
|
|
2483
|
-
if (this.buffer.length < messageEnd) return;
|
|
2484
|
-
const body = this.buffer.slice(messageStart, messageEnd).toString("utf-8");
|
|
2485
|
-
this.buffer = this.buffer.slice(messageEnd);
|
|
2486
|
-
|
|
2487
2496
|
let parsed: JsonRpcRequest;
|
|
2488
2497
|
try {
|
|
2489
|
-
|
|
2498
|
+
// SAFETY: handleRequest defaults missing fields; a non-object body throws and scheduleFlush answers -32700.
|
|
2499
|
+
parsed = JSON.parse(frame.text) as JsonRpcRequest;
|
|
2490
2500
|
} catch {
|
|
2491
2501
|
this.writeMessage(output, {
|
|
2492
2502
|
jsonrpc: "2.0",
|
|
@@ -2620,7 +2630,7 @@ export class EngramMcpServer {
|
|
|
2620
2630
|
caps?.ops === undefined || capabilityAllowsOp(caps, MCP_MIGRATED_OPERATIONS[toLegacyToolName(t.name)] ?? "")
|
|
2621
2631
|
)
|
|
2622
2632
|
.map((t) => t.name);
|
|
2623
|
-
throw new
|
|
2633
|
+
throw new EngramAccessInputError(unknownToolError(name, visible));
|
|
2624
2634
|
}
|
|
2625
2635
|
const op = getOperation(migrated);
|
|
2626
2636
|
if (!op) {
|
|
@@ -32,7 +32,7 @@ import { EngramAccessService } from "./access-service.js";
|
|
|
32
32
|
import { SmartBuffer } from "./buffer.js";
|
|
33
33
|
import { ExtractionDeadlineError } from "./orchestration/extraction-run.js";
|
|
34
34
|
import { runExtractionDeferRetention } from "./orchestration/extraction-run-helpers.js";
|
|
35
|
-
import { TurnIngestionCoordinator, type TurnIngestionDeps } from "./orchestration/turn-ingestion.js";
|
|
35
|
+
import { ReplayAdmissionRefusedError, TurnIngestionCoordinator, type TurnIngestionDeps } from "./orchestration/turn-ingestion.js";
|
|
36
36
|
import { resolveScopeProfilePlan } from "./namespaces/scope-profiles.js";
|
|
37
37
|
import { resolveCodingNamespaceOverlay } from "./coding/coding-namespace.js";
|
|
38
38
|
import type { ResolvedScopeProfilePlan } from "./namespaces/scope-profiles.js";
|
|
@@ -378,6 +378,40 @@ test("#3251 buffered observe append is durable before accept: a failed save reje
|
|
|
378
378
|
await cleanupDir(memoryDir);
|
|
379
379
|
}
|
|
380
380
|
});
|
|
381
|
+
test("#3251 multi-session refusal is atomic: earlier sessions are not partially accepted", async () => {
|
|
382
|
+
const config = parseConfig({
|
|
383
|
+
bufferMaxSessions: 3,
|
|
384
|
+
bufferMaxTurns: 100,
|
|
385
|
+
triggerMode: "every_n",
|
|
386
|
+
extractionMinChars: 1_000_000,
|
|
387
|
+
extractionMinUserTurns: 1000,
|
|
388
|
+
});
|
|
389
|
+
// SAFETY: SeedStorage implements the loadBuffer/saveBuffer surface this fixture drives.
|
|
390
|
+
const buffer = new SmartBuffer(config, new SeedStorage() as StorageManager);
|
|
391
|
+
const occupied = makeReplayTurn("occupied", "existing backlog");
|
|
392
|
+
await buffer.addTurn("occupied", {
|
|
393
|
+
role: occupied.role,
|
|
394
|
+
content: occupied.content,
|
|
395
|
+
timestamp: occupied.timestamp,
|
|
396
|
+
sessionKey: occupied.sessionKey,
|
|
397
|
+
});
|
|
398
|
+
const coordinator = makeTurnIngestionDouble(config, buffer, []);
|
|
399
|
+
|
|
400
|
+
await assert.rejects(
|
|
401
|
+
coordinator.ingestReplayBatch(
|
|
402
|
+
[
|
|
403
|
+
makeReplayTurn("fresh-a", "first in batch"),
|
|
404
|
+
makeReplayTurn("fresh-b", "second in batch"),
|
|
405
|
+
],
|
|
406
|
+
bufferedObserveOptions({ writeNamespaceOverride: "default", sessionOwnerPrincipal: "alice" }),
|
|
407
|
+
),
|
|
408
|
+
ReplayAdmissionRefusedError,
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
assert.equal(buffer.getTurns("fresh-a").length, 0, "refused batch must not durably accept its first session");
|
|
412
|
+
assert.equal(buffer.getTurns("fresh-b").length, 0, "refused batch must not durably accept its second session");
|
|
413
|
+
assert.equal(buffer.getTurns("occupied").length, 1, "pre-existing backlog remains intact");
|
|
414
|
+
});
|
|
381
415
|
|
|
382
416
|
test("#3251 durable observe admission is bounded: at the entry cap a new session is refused, not evicted", async () => {
|
|
383
417
|
// Real SmartBuffer + real coordinator, NO Orchestrator background actors —
|
|
@@ -413,8 +447,10 @@ test("#3251 durable observe admission is bounded: at the entry cap a new session
|
|
|
413
447
|
// (buffer.ts), each holding an accepted, durably pending capture. The
|
|
414
448
|
// seed turns are PINNED accepted captures — cap pressure must refuse new
|
|
415
449
|
// captures, never evict these (#3251 eviction guard; the pre-existing
|
|
416
|
-
// `default` mirror entry also occupies the entries map).
|
|
417
|
-
|
|
450
|
+
// `default` mirror entry also occupies the entries map). 199 seeds +
|
|
451
|
+
// `default` = 200 entries: the reachable cap boundary (durable ingress
|
|
452
|
+
// refuses a 200th seed once pinned entries fill the cap).
|
|
453
|
+
for (let index = 0; index < 199; index += 1) {
|
|
418
454
|
const key = `seed-${index}`;
|
|
419
455
|
await buffer.addTurn(key, {
|
|
420
456
|
role: "user",
|
|
@@ -452,7 +488,7 @@ test("#3251 durable observe admission is bounded: at the entry cap a new session
|
|
|
452
488
|
assert.equal(queued.length, 0, "refused capture must not queue extraction");
|
|
453
489
|
|
|
454
490
|
const snapshot = await buffer.getBufferSnapshot();
|
|
455
|
-
assert.equal(snapshot.bufferedSessionCount,
|
|
491
|
+
assert.equal(snapshot.bufferedSessionCount, 199, "refusal must not evict accepted captures");
|
|
456
492
|
assert.ok(
|
|
457
493
|
buffer.getTurns("seed-0").some((turn) => turn.content.includes("accepted seed capture 0")),
|
|
458
494
|
);
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
queueExplicitCaptureForReview,
|
|
52
52
|
} from "./explicit-capture.js";
|
|
53
53
|
import { log } from "./logger.js";
|
|
54
|
+
import { embeddingBacklogExceeds, isSearchHealthDegraded } from "./namespaces/search.js";
|
|
54
55
|
import { recordObjectiveStateSnapshotsFromObservedMessages } from "./objective-state-writers.js";
|
|
55
56
|
import type { Orchestrator } from "./orchestrator.js";
|
|
56
57
|
import { displayErrorDetail } from "./runtime/better-sqlite.js";
|
|
@@ -289,7 +290,7 @@ export class AccessObserveWriteSurface {
|
|
|
289
290
|
/* status probe failed — non-fatal */
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
|
-
if (
|
|
293
|
+
if (embeddingBacklogExceeds(pendingEmbeddings, threshold)) {
|
|
293
294
|
degraded = true;
|
|
294
295
|
degradedReason = `embedding-backlog: ${pendingEmbeddings} pending > threshold ${threshold}`;
|
|
295
296
|
}
|
|
@@ -355,14 +356,13 @@ export class AccessObserveWriteSurface {
|
|
|
355
356
|
const pending = health.pendingEmbeddings ?? null;
|
|
356
357
|
const oldest = health.oldestPendingAgeMs ?? null;
|
|
357
358
|
const threshold = this.deps.orchestrator.config.qmdEmbeddingBacklogThreshold;
|
|
358
|
-
const backlogDegraded =
|
|
359
|
-
const degraded = !active || health.collectionState === "unknown" || backlogDegraded;
|
|
359
|
+
const backlogDegraded = embeddingBacklogExceeds(pending, threshold);
|
|
360
360
|
const mode = !active ? "fallback" : health.daemonMode === true ? "daemon" : "cli";
|
|
361
361
|
|
|
362
362
|
return {
|
|
363
363
|
enabled: true,
|
|
364
364
|
active,
|
|
365
|
-
degraded,
|
|
365
|
+
degraded: isSearchHealthDegraded({ ...health, pendingEmbeddings: pending }, threshold),
|
|
366
366
|
mode,
|
|
367
367
|
collection: health.collection || fallbackCollection,
|
|
368
368
|
collectionState: health.collectionState,
|
|
@@ -14,7 +14,7 @@ import { z } from "zod";
|
|
|
14
14
|
import type { DreamsPhase, RecallDisclosure, RecallPlanMode } from "./types.js";
|
|
15
15
|
import type { RemnicChatGptMemoryInspectorInput } from "./mcp-memory-inspector-app.js";
|
|
16
16
|
import { defineOperation } from "./access-boundary.js";
|
|
17
|
-
import { EngramAccessForbiddenError } from "./access-errors.js";
|
|
17
|
+
import { EngramAccessForbiddenError, rejectReservedArgumentKey } from "./access-errors.js";
|
|
18
18
|
import {
|
|
19
19
|
type ObserveRequest,
|
|
20
20
|
observeRequestSchema,
|
|
@@ -55,11 +55,15 @@ import { normalizeDreamsStatusWindowHours } from "./maintenance/dreams-ledger.js
|
|
|
55
55
|
// Shared helpers
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
57
|
|
|
58
|
+
// Object.create(null): a plain {} lets a copied own `__proto__` member invoke
|
|
59
|
+
// the prototype setter, so an inherited field could reach the zod parse
|
|
60
|
+
// (issue #3225 B1).
|
|
58
61
|
function stripNulls(data: unknown): unknown {
|
|
59
62
|
if (data !== null && typeof data === "object" && !Array.isArray(data)) {
|
|
60
63
|
const obj = data as Record<string, unknown>;
|
|
61
|
-
const cleaned
|
|
64
|
+
const cleaned = Object.create(null);
|
|
62
65
|
for (const [key, value] of Object.entries(obj)) {
|
|
66
|
+
rejectReservedArgumentKey(key);
|
|
63
67
|
if (value !== null) cleaned[key] = value;
|
|
64
68
|
}
|
|
65
69
|
return cleaned;
|
|
@@ -79,8 +83,9 @@ function strictSchema<T extends z.ZodRawShape>(shape: T): z.ZodType<Record<strin
|
|
|
79
83
|
*/
|
|
80
84
|
function stripNullsExcept(data: unknown, preserve: readonly string[]): unknown {
|
|
81
85
|
if (data !== null && typeof data === "object" && !Array.isArray(data)) {
|
|
82
|
-
const cleaned
|
|
86
|
+
const cleaned = Object.create(null);
|
|
83
87
|
for (const [key, value] of Object.entries(data as Record<string, unknown>)) {
|
|
88
|
+
rejectReservedArgumentKey(key);
|
|
84
89
|
if (value !== null || preserve.includes(key)) cleaned[key] = value;
|
|
85
90
|
}
|
|
86
91
|
return cleaned;
|
|
@@ -328,9 +333,15 @@ defineOperation({ name: "identity_anchor_update", description: "Update identity
|
|
|
328
333
|
defineOperation({ name: "memory_identity", description: "Memory identity.", schema: strictSchema({ namespace: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.memoryIdentity({ namespace: optStr(input.namespace), principal: ctx.authenticatedPrincipal }) }) });
|
|
329
334
|
|
|
330
335
|
// === WORK/SHARED ===
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
defineOperation({ name: "
|
|
336
|
+
// SAFETY: optStr yields undefined or a string; workTask rejects any action
|
|
337
|
+
// string outside its union with an input error before touching a store.
|
|
338
|
+
defineOperation({ name: "work_task", description: "Manage work task.", fleetWide: true, schema: strictSchema({ action: S.str, id: S.str, title: S.str, description: S.str, status: S.str, priority: S.str, owner: S.str, assignee: S.str, projectId: S.str, tags: S.strArr, dueAt: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.workTask({ action: (optStr(input.action) as "update" | "list" | "get" | "create" | "delete" | "transition") ?? "list", id: optStr(input.id), title: optStr(input.title), description: optStr(input.description), status: optStr(input.status), priority: optStr(input.priority), owner: optStr(input.owner), assignee: optStr(input.assignee), projectId: optStr(input.projectId), tags: optStrArr(input.tags), dueAt: optStr(input.dueAt) }) }) });
|
|
339
|
+
// SAFETY: optStr yields undefined or a string; workProject rejects any action
|
|
340
|
+
// string outside its union with an input error before touching a store.
|
|
341
|
+
defineOperation({ name: "work_project", description: "Manage work project.", fleetWide: true, schema: strictSchema({ action: S.str, id: S.str, name: S.str, description: S.str, status: S.str, owner: S.str, tags: S.strArr, taskId: S.str, projectId: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.workProject({ action: (optStr(input.action) as "update" | "list" | "get" | "create" | "delete" | "link_task") ?? "list", id: optStr(input.id), name: optStr(input.name), description: optStr(input.description), status: optStr(input.status), owner: optStr(input.owner), tags: optStrArr(input.tags), taskId: optStr(input.taskId), projectId: optStr(input.projectId) }) }) });
|
|
342
|
+
// SAFETY: optStr yields undefined or a string; workBoard rejects any action
|
|
343
|
+
// string outside its union with an input error before touching a store.
|
|
344
|
+
defineOperation({ name: "work_board", description: "Manage work board.", fleetWide: true, schema: strictSchema({ action: S.str, projectId: S.str, snapshotJson: S.str, linkToMemory: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.workBoard({ action: (optStr(input.action) as "export_markdown" | "export_snapshot" | "import_snapshot") ?? "export_markdown", projectId: optStr(input.projectId) ?? "", snapshotJson: optStr(input.snapshotJson) ?? "", linkToMemory: input.linkToMemory === true }) }) });
|
|
334
345
|
// Issue #2920: the envelope controls (authority/expiresAt/supersedes) are
|
|
335
346
|
// parsed by the ONE canonical surface module shared with the OpenClaw tool
|
|
336
347
|
// (write-output-controls.ts); semantics stay in composeWriteEnvelope. A
|
|
@@ -451,12 +462,12 @@ defineOperation({ name: "memory_summarize_hourly", description: "Hourly summariz
|
|
|
451
462
|
defineOperation({ name: "conversation_index_update", description: "Update conv index.", fleetWide: true, schema: strictSchema({ sessionKey: S.str, hours: S.num, embed: S.bool }), handler: async (input, ctx) => { if (input.sessionKey !== undefined && typeof input.sessionKey !== "string") throw new EngramAccessInputError("sessionKey must be a string when provided"); return { result: await ctx.service.conversationIndexUpdate({ sessionKey: optStr(input.sessionKey), hours: optNum(input.hours), embed: optBool(input.embed) }) }; } });
|
|
452
463
|
defineOperation({ name: "profiling_report", description: "Profiling report.", schema: strictSchema({ format: S.str, limit: S.num }), handler: async (input, ctx) => { if (input.format !== undefined && typeof input.format !== "string") throw new EngramAccessInputError("format must be a string when provided"); if (input.limit !== undefined && typeof input.limit !== "number") throw new EngramAccessInputError("limit must be a number when provided"); return { result: await ctx.service.profilingReport({ format: optStr(input.format), limit: optNum(input.limit) }) }; } });
|
|
453
464
|
defineOperation({ name: "live_connectors_run", description: "Run connectors.", fleetWide: true, schema: strictSchema({ force: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.liveConnectorsRun({ authenticatedPrincipal: ctx.authenticatedPrincipal, force: input.force === true }, ctx.authenticatedPrincipal) }) });
|
|
454
|
-
defineOperation({ name: "peer_list", description: "List peers.", schema: strictSchema({}), handler: async (_i, ctx) => ({ result: await ctx.service.peerList() }) });
|
|
455
|
-
defineOperation({ name: "peer_get", description: "Get peer.", schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_get: id is required"); return { result: await ctx.service.peerGet(id) }; } });
|
|
456
|
-
defineOperation({ name: "peer_set", description: "Set peer.", schema: strictSchema({ id: S.str, kind: S.str, displayName: S.str, notes: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_set: id is required");
|
|
457
|
-
defineOperation({ name: "peer_delete", description: "Delete peer.", schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_delete: id is required"); return { result: await ctx.service.peerDelete(id) }; } });
|
|
458
|
-
defineOperation({ name: "peer_profile_get", description: "Get peer profile.", schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_profile_get: id is required"); return { result: await ctx.service.peerProfileGet(id) }; } });
|
|
459
|
-
defineOperation({ name: "peer_forget", description: "Forget peer.", schema: strictSchema({ id: S.str, confirm: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_forget: id is required"); const c = optStr(input.confirm) ?? ""; if (c !== "yes") throw new EngramAccessInputError("peer_forget: confirm must be 'yes' to prevent accidental data loss"); return { result: await ctx.service.peerForget(id, { confirm: "yes" }) }; } });
|
|
465
|
+
defineOperation({ name: "peer_list", description: "List peers.", fleetWide: true, schema: strictSchema({}), handler: async (_i, ctx) => ({ result: await ctx.service.peerList() }) });
|
|
466
|
+
defineOperation({ name: "peer_get", description: "Get peer.", fleetWide: true, schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_get: id is required"); return { result: await ctx.service.peerGet(id) }; } });
|
|
467
|
+
defineOperation({ name: "peer_set", description: "Set peer.", fleetWide: true, schema: strictSchema({ id: S.str, kind: S.str, displayName: S.str, notes: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_set: id is required"); return { result: await ctx.service.peerSet({ id, kind: optStr(input.kind), displayName: optStr(input.displayName), notes: optStr(input.notes) }) }; } });
|
|
468
|
+
defineOperation({ name: "peer_delete", description: "Delete peer.", fleetWide: true, schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_delete: id is required"); return { result: await ctx.service.peerDelete(id) }; } });
|
|
469
|
+
defineOperation({ name: "peer_profile_get", description: "Get peer profile.", fleetWide: true, schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_profile_get: id is required"); return { result: await ctx.service.peerProfileGet(id) }; } });
|
|
470
|
+
defineOperation({ name: "peer_forget", description: "Forget peer.", fleetWide: true, schema: strictSchema({ id: S.str, confirm: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_forget: id is required"); const c = optStr(input.confirm) ?? ""; if (c !== "yes") throw new EngramAccessInputError("peer_forget: confirm must be 'yes' to prevent accidental data loss"); return { result: await ctx.service.peerForget(id, { confirm: "yes" }) }; } });
|
|
460
471
|
defineOperation({ name: "console_state", description: "Console state.", schema: strictSchema({ namespace: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.consoleState(optStr(input.namespace), ctx.authenticatedPrincipal) }) });
|
|
461
472
|
defineOperation({ name: "dreams_status", description: "Dreams status.", schema: strictSchema({ windowHours: S.num, namespace: S.str }), handler: async (input, ctx) => { let wh = 24; try { wh = normalizeDreamsStatusWindowHours(input.windowHours); } catch { throw new EngramAccessInputError("dreams_status: windowHours must be a positive integer. Got: " + String(input.windowHours)); } return { result: await ctx.service.dreamsStatus({ windowHours: wh, namespace: optStr(input.namespace), principal: ctx.authenticatedPrincipal }) }; } });
|
|
462
473
|
defineOperation({ name: "dreams_run", description: "Run dreams.", schema: strictSchema({ phase: S.str, dryRun: S.bool, namespace: S.str }), handler: async (input, ctx) => { const VP = ["lightSleep", "rem", "deepSleep"]; const ph = optStr(input.phase) ?? ""; if (!ph || !VP.includes(ph)) throw new EngramAccessInputError("dreams_run: phase must be one of: " + VP.join(", ")); if (input.dryRun !== undefined && typeof input.dryRun !== "boolean") throw new EngramAccessInputError("dreams_run: dryRun must be boolean"); return { result: await ctx.service.dreamsRun({ phase: ph as DreamsPhase, dryRun: input.dryRun === true, namespace: optStr(input.namespace), authenticatedPrincipal: ctx.authenticatedPrincipal }) }; } });
|