@remnic/core 9.69.60 → 9.69.63
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 +38 -38
- package/dist/access-authorization-probe.js +39 -39
- package/dist/access-boundary.js +38 -38
- package/dist/access-cli.js +95 -95
- package/dist/access-extraction-force-flush.js +38 -38
- package/dist/access-http-briefing.js +39 -39
- package/dist/access-http-query.js +40 -40
- package/dist/access-http.js +56 -56
- package/dist/access-identity-continuity-surface.js +38 -38
- package/dist/access-lcm-surface.js +38 -38
- package/dist/access-mcp.js +49 -49
- package/dist/access-memory-search-fanout.js +3 -3
- package/dist/access-namespace-preflight.js +38 -38
- package/dist/access-observe-write-surface.js +38 -38
- package/dist/access-offline-manifest.js +6 -6
- package/dist/access-operations-batch.js +40 -40
- package/dist/access-operations.js +47 -47
- package/dist/access-recall-concurrency.js +38 -38
- package/dist/access-recall-diagnostics.js +5 -5
- package/dist/access-recall-response.js +38 -38
- package/dist/access-recall-surface.js +38 -38
- package/dist/access-service.js +38 -38
- package/dist/access-surface-catalog.js +40 -40
- package/dist/active-memory-bridge.js +3 -3
- package/dist/active-recall.js +9 -9
- package/dist/{auto-sync-CWSZDLLW.js → auto-sync-QJNIKZHV.js} +5 -5
- package/dist/briefing.js +8 -8
- package/dist/buffer-turn-helpers.d.ts.map +1 -1
- package/dist/buffer-turn-helpers.js +1 -1
- package/dist/buffer.d.ts.map +1 -1
- package/dist/buffer.js +7 -7
- package/dist/calibration.js +2 -2
- package/dist/capabilities.d.ts +6 -7
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +1 -1
- package/dist/causal-consolidation.js +9 -9
- package/dist/{chunk-6Q3ZTEF6.js → chunk-23G5P7TA.js} +9 -1
- package/dist/chunk-23G5P7TA.js.map +1 -0
- package/dist/{chunk-RP7TZEI6.js → chunk-24TJ77TT.js} +2 -2
- package/dist/{chunk-ZLVUWROW.js → chunk-2OUSWDY6.js} +2 -2
- package/dist/{chunk-M5IJP3YW.js → chunk-2PKGJPQL.js} +2 -2
- package/dist/{chunk-FJMYMJLR.js → chunk-3JDA3XF5.js} +2 -2
- package/dist/{chunk-JGT7M3BA.js → chunk-3MSMBVA3.js} +6 -6
- package/dist/{chunk-6JYYSDHH.js → chunk-4EEDRKRT.js} +4 -4
- package/dist/{chunk-YUHGJBQK.js → chunk-5LWVTSF7.js} +2 -2
- package/dist/{chunk-WLZYGLJ4.js → chunk-5MA7ZLPD.js} +5 -7
- package/dist/chunk-5MA7ZLPD.js.map +1 -0
- package/dist/{chunk-2T2RF5ZK.js → chunk-5QZHDS4X.js} +5 -5
- package/dist/{chunk-64JK4TL7.js → chunk-5WXJE5TV.js} +4 -4
- package/dist/{chunk-DJ4CKWAD.js → chunk-5Y7VCE3A.js} +2 -2
- package/dist/{chunk-G2XZFGYN.js → chunk-62JTOLEX.js} +2 -2
- package/dist/{chunk-LVXBC6YB.js → chunk-65I2LZEK.js} +2 -2
- package/dist/{chunk-NZLFVLLJ.js → chunk-7CRUDH7G.js} +2 -2
- package/dist/{chunk-B4EGAHHS.js → chunk-7DE2NOON.js} +2 -2
- package/dist/{chunk-BSDBDFKZ.js → chunk-7JFQI6DQ.js} +3 -3
- package/dist/{chunk-BOADKFN4.js → chunk-7LNHRQKR.js} +2 -2
- package/dist/{chunk-IV6APZJS.js → chunk-7OC3Q5X4.js} +2 -2
- package/dist/{chunk-X2QPKLPL.js → chunk-ACX55QDO.js} +3 -3
- package/dist/{chunk-YLPCEUXP.js → chunk-AEGQ5PJ7.js} +2 -2
- package/dist/{chunk-GK6NOQE6.js → chunk-AERXJCOG.js} +2 -2
- package/dist/{chunk-HW2QYZEV.js → chunk-B5GMOOY2.js} +5 -5
- package/dist/{chunk-NLE6TSLG.js → chunk-BLVHDY2U.js} +5 -7
- package/dist/chunk-BLVHDY2U.js.map +1 -0
- package/dist/{chunk-JUOZ62Y7.js → chunk-BMXEBWBT.js} +2 -1
- package/dist/chunk-BMXEBWBT.js.map +1 -0
- package/dist/{chunk-YHGRA7CK.js → chunk-CDQO5DCD.js} +2 -2
- package/dist/{chunk-ENUI3IO7.js → chunk-D433RXFJ.js} +2 -2
- package/dist/{chunk-IK63WA5A.js → chunk-D4SPMHEW.js} +2 -2
- package/dist/{chunk-42DJ235G.js → chunk-DINRT75P.js} +2 -2
- package/dist/{chunk-LELJRLPY.js → chunk-DQFZOPDL.js} +2 -2
- package/dist/{chunk-WN4HFL4K.js → chunk-DQISIT63.js} +2 -2
- package/dist/{chunk-Q7KQ5YHT.js → chunk-EIU642TN.js} +2 -2
- package/dist/{chunk-EN2YX5SF.js → chunk-F4PEETM4.js} +3 -3
- package/dist/{chunk-FNQEXI3D.js → chunk-F5O6WEDO.js} +1 -1
- package/dist/{chunk-FNQEXI3D.js.map → chunk-F5O6WEDO.js.map} +1 -1
- package/dist/{chunk-Q6C5PIBW.js → chunk-F5ZCNJ3V.js} +3 -3
- package/dist/{chunk-KR5SZNNF.js → chunk-FEHVG4C4.js} +2 -2
- package/dist/{chunk-42G4FIKQ.js → chunk-FG5QU7A2.js} +4 -4
- package/dist/{chunk-IRGQYQQ7.js → chunk-FM5HN7LL.js} +4 -4
- package/dist/{chunk-FF2XYADH.js → chunk-FQGY7XB2.js} +6 -6
- package/dist/{chunk-FF2XYADH.js.map → chunk-FQGY7XB2.js.map} +1 -1
- package/dist/{chunk-IDBLAGIM.js → chunk-FTNV5476.js} +14 -14
- package/dist/{chunk-R4Y5PF25.js → chunk-FWVDTLRK.js} +1 -1
- package/dist/{chunk-C6FQJ52H.js → chunk-G2XTGEGL.js} +65 -18
- package/dist/chunk-G2XTGEGL.js.map +1 -0
- package/dist/{chunk-Q5FL3MEH.js → chunk-G4WZDQ32.js} +3 -3
- package/dist/{chunk-4HNY6ORZ.js → chunk-GMHWCPLP.js} +2 -2
- package/dist/{chunk-ZWWKKAJL.js → chunk-GYNCJSQ7.js} +4 -4
- package/dist/{chunk-BJI2D4SU.js → chunk-GZBNEZIU.js} +3 -3
- package/dist/{chunk-NYVECYCF.js → chunk-HNOKNHFO.js} +2 -2
- package/dist/{chunk-XLXYARWC.js → chunk-HXCJF4XU.js} +2 -2
- package/dist/{chunk-RMF763SX.js → chunk-IDRSTHVR.js} +5 -5
- package/dist/{chunk-UXREWVW4.js → chunk-IW3RMBPB.js} +2 -2
- package/dist/{chunk-W6YK672C.js → chunk-JTTLH646.js} +3 -3
- package/dist/{chunk-BQB5ZMYO.js → chunk-KKNZ7PUZ.js} +2 -2
- package/dist/{chunk-AAMFQHKI.js → chunk-KRPMISWO.js} +3 -3
- package/dist/{chunk-XX2IGMCB.js → chunk-KV44HTMJ.js} +2 -2
- package/dist/{chunk-FKFTPFUG.js → chunk-KXJ7XYUT.js} +2 -2
- package/dist/{chunk-2NZ7KWRW.js → chunk-LAEW4S43.js} +2 -2
- package/dist/{chunk-5SV2GFZX.js → chunk-LDP6LBDS.js} +2 -2
- package/dist/{chunk-F6WCOXTF.js → chunk-LMLPLIT4.js} +2 -2
- package/dist/{chunk-QJTFMH5Y.js → chunk-LVGVXG22.js} +2 -2
- package/dist/{chunk-OINFRHLR.js → chunk-NRTVZSPO.js} +2 -2
- package/dist/{chunk-IAZPYX6W.js → chunk-NRVGODSE.js} +3 -3
- package/dist/{chunk-IHOXK2I6.js → chunk-NVKO5YKP.js} +2 -2
- package/dist/{chunk-OQQXGYMX.js → chunk-OWYHHW67.js} +4 -4
- package/dist/{chunk-KPKGDQR3.js → chunk-PZEXA6CM.js} +2 -2
- package/dist/{chunk-DXODYQ7J.js → chunk-Q3THVX75.js} +2 -2
- package/dist/{chunk-TAC5WFJC.js → chunk-QEGKMZNQ.js} +4 -4
- package/dist/{chunk-VRF2KFI2.js → chunk-RA3C46VX.js} +2 -2
- package/dist/{chunk-V2HHAAPI.js → chunk-RB776UH3.js} +2 -2
- package/dist/{chunk-TAIIC5OM.js → chunk-SEHKTE4F.js} +3 -3
- package/dist/{chunk-3JLWSFTV.js → chunk-SVWYZXIL.js} +8 -5
- package/dist/{chunk-3JLWSFTV.js.map → chunk-SVWYZXIL.js.map} +1 -1
- package/dist/{chunk-G6E7XJYL.js → chunk-SZAWZPY6.js} +10 -10
- package/dist/{chunk-2AD5SXO4.js → chunk-T5IF2ZCE.js} +5 -2
- package/dist/chunk-T5IF2ZCE.js.map +1 -0
- package/dist/{chunk-TYZK56OA.js → chunk-TPRBJF46.js} +9 -9
- package/dist/{chunk-7DNMX7SA.js → chunk-TQC6ELJO.js} +2 -2
- package/dist/{chunk-OVJFS5P5.js → chunk-U6HXB2OG.js} +2 -2
- package/dist/{chunk-7WSQZJPC.js → chunk-V3JM4XEI.js} +4 -4
- package/dist/{chunk-36S7UHAW.js → chunk-VEBYLZQQ.js} +2 -2
- package/dist/{chunk-IMJPJY7V.js → chunk-VHF73MGU.js} +2 -2
- package/dist/{chunk-TDULASPE.js → chunk-VKDWYDFQ.js} +7 -7
- package/dist/{chunk-NKXHCCLB.js → chunk-VTPASHTO.js} +2 -2
- package/dist/{chunk-4B5S3VWD.js → chunk-VXGB766B.js} +29 -29
- package/dist/{chunk-ADP2VCP6.js → chunk-W5UAB6EH.js} +5 -5
- package/dist/{chunk-L3R24OLZ.js → chunk-W65KK3X6.js} +2 -2
- package/dist/{chunk-BJX6G726.js → chunk-WAR75CRY.js} +67 -64
- package/dist/chunk-WAR75CRY.js.map +1 -0
- package/dist/{chunk-MNIPH6LD.js → chunk-WBYQA3VN.js} +3 -3
- package/dist/{chunk-WD2JD2UB.js → chunk-WEQZ5OYK.js} +2 -2
- package/dist/{chunk-AJW2K7K3.js → chunk-WKNWC46C.js} +2 -2
- package/dist/{chunk-UD6J2LIA.js → chunk-XDH5MO6L.js} +2 -2
- package/dist/{chunk-MRV7VSQK.js → chunk-Y23L4YJV.js} +2 -2
- package/dist/{chunk-6ISP2EN2.js → chunk-YF3JERFZ.js} +2 -2
- package/dist/{chunk-C5CPYMLX.js → chunk-YR6A6ZK7.js} +2 -2
- package/dist/{chunk-EJO2NWW7.js → chunk-Z3YQOV45.js} +2 -2
- package/dist/{chunk-6KTXE6A3.js → chunk-Z4TXB4K7.js} +2 -2
- package/dist/{chunk-Y76HXF3F.js → chunk-ZDZJ2PG4.js} +393 -41
- package/dist/{chunk-Y76HXF3F.js.map → chunk-ZDZJ2PG4.js.map} +1 -1
- package/dist/{chunk-6VITOSPL.js → chunk-ZFUPRODV.js} +2 -2
- package/dist/cli.js +70 -70
- package/dist/coding/export-okf-codegraph.js +7 -7
- package/dist/compounding/engine.js +9 -9
- package/dist/config-security.d.ts +4 -0
- package/dist/config-security.d.ts.map +1 -1
- package/dist/config-security.js +2 -2
- package/dist/config.js +9 -9
- package/dist/connectors/codex-materialize-runner.js +8 -8
- package/dist/connectors/index.js +9 -9
- package/dist/contradiction/index.js +3 -3
- package/dist/{contradiction-scan-GVD4JJ7B.js → contradiction-scan-5EJYRKD3.js} +3 -3
- package/dist/convergence-refresh.js +10 -10
- package/dist/corpus-watermark.js +10 -10
- package/dist/day-summary.js +2 -2
- package/dist/embedding-fallback.js +2 -2
- package/dist/entity-retrieval.js +9 -9
- package/dist/explicit-capture.js +2 -2
- package/dist/external-wiki-access.js +39 -39
- package/dist/extraction-judge.js +3 -3
- package/dist/extraction-normalization.js +3 -3
- package/dist/extraction-prompt.js +2 -2
- package/dist/extraction.js +21 -21
- package/dist/{first-start-migration-L3UFX5CU.js → first-start-migration-7OATHJKB.js} +2 -2
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +99 -95
- package/dist/local-llm.js +2 -2
- package/dist/location/index.js +10 -10
- package/dist/maintenance/memory-governance.js +8 -8
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +8 -8
- package/dist/maintenance/rebuild-memory-projection.js +9 -9
- package/dist/memory-browse-operations.js +39 -39
- package/dist/model-registry.d.ts.map +1 -1
- package/dist/model-registry.js +1 -1
- package/dist/namespaces/migrate.js +9 -9
- package/dist/namespaces/principal.js +2 -2
- package/dist/namespaces/search.js +4 -4
- package/dist/namespaces/storage.js +8 -8
- package/dist/offline-sync-impression-drain.js +5 -5
- package/dist/operator-doctor-corpus.js +11 -11
- package/dist/operator-toolkit.js +21 -21
- package/dist/orchestration/compression-guideline-coordinator.js +2 -2
- package/dist/orchestration/extraction-injection-gate.d.ts +5 -4
- package/dist/orchestration/extraction-injection-gate.d.ts.map +1 -1
- package/dist/orchestration/maintenance.js +20 -20
- package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
- package/dist/orchestrator.js +95 -95
- package/dist/profiling.d.ts.map +1 -1
- package/dist/profiling.js +1 -1
- package/dist/recall-composition-decision.js +3 -3
- package/dist/recall-context-composition.js +2 -2
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-navigate-access.js +2 -2
- package/dist/recall-navigate-disclosure.js +2 -2
- package/dist/recall-navigate.js +2 -2
- package/dist/recall-navigation-operations.js +39 -39
- package/dist/recall-navigation.js +38 -38
- package/dist/recall-why-service.js +5 -5
- package/dist/recall-why.js +3 -3
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.js +2 -2
- package/dist/{resolution-CHNISQJB.js → resolution-UYJU5JUA.js} +4 -4
- package/dist/resume-bundles.js +10 -10
- package/dist/search/embed-helper.js +2 -2
- package/dist/search/factory.js +3 -3
- package/dist/search/index.js +3 -3
- package/dist/security/audit-memory-cli.d.ts.map +1 -1
- package/dist/security/audit-memory.d.ts +13 -0
- package/dist/security/audit-memory.d.ts.map +1 -1
- package/dist/security/injection-screen.d.ts +2 -2
- package/dist/security/injection-screen.d.ts.map +1 -1
- package/dist/security/origin-authority.d.ts +3 -0
- package/dist/security/origin-authority.d.ts.map +1 -1
- package/dist/security/types.d.ts +5 -0
- package/dist/security/types.d.ts.map +1 -1
- package/dist/semantic-consolidation.js +9 -9
- package/dist/semantic-rule-promotion.js +8 -8
- package/dist/semantic-rule-verifier.js +8 -8
- package/dist/shared-context/manager.js +2 -2
- package/dist/source-agent-qualifier.js +14 -14
- package/dist/standup/build.js +8 -8
- package/dist/storage.js +7 -7
- package/dist/summarizer.js +4 -4
- package/dist/support-passport/index.js +41 -41
- package/dist/temporal-timeline-recall.js +3 -3
- package/dist/{tier-stats-VK5PCO2L.js → tier-stats-VF6DS52E.js} +2 -2
- package/dist/transfer/export-okf.js +8 -8
- package/dist/types.d.ts +4 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/verified-recall.js +8 -8
- package/dist/who-knows-cli.js +9 -9
- package/dist/who-knows.js +8 -8
- package/package.json +12 -2
- package/src/buffer-turn-helpers.ts +4 -0
- package/src/buffer.ts +3 -0
- package/src/capabilities.test.ts +4 -1
- package/src/capabilities.ts +7 -7
- package/src/config-security.ts +103 -16
- package/src/config.test.ts +58 -1
- package/src/connector-provenance-lifecycle.test.ts +19 -0
- package/src/index.ts +3 -3
- package/src/model-registry.test.ts +22 -1
- package/src/model-registry.ts +7 -6
- package/src/orchestration/extraction-injection-gate.ts +11 -7
- package/src/orchestration/extraction-origin-context.ts +1 -1
- package/src/orchestration/extraction-persist.ts +8 -8
- package/src/orchestration/extraction-security.test.ts +12 -0
- package/src/orchestration/turn-ingestion.ts +2 -0
- package/src/profiling.test.ts +20 -1
- package/src/profiling.ts +7 -7
- package/src/security/audit-memory-cli.test.ts +90 -0
- package/src/security/audit-memory-cli.ts +7 -0
- package/src/security/audit-memory.test.ts +21 -10
- package/src/security/audit-memory.ts +20 -2
- package/src/security/injection-screen.test.ts +301 -0
- package/src/security/injection-screen.ts +414 -11
- package/src/security/origin-authority.ts +10 -0
- package/src/security/types.ts +11 -0
- package/src/types.ts +4 -3
- package/dist/chunk-2AD5SXO4.js.map +0 -1
- package/dist/chunk-6Q3ZTEF6.js.map +0 -1
- package/dist/chunk-BJX6G726.js.map +0 -1
- package/dist/chunk-C6FQJ52H.js.map +0 -1
- package/dist/chunk-JUOZ62Y7.js.map +0 -1
- package/dist/chunk-NLE6TSLG.js.map +0 -1
- package/dist/chunk-WLZYGLJ4.js.map +0 -1
- /package/dist/{auto-sync-CWSZDLLW.js.map → auto-sync-QJNIKZHV.js.map} +0 -0
- /package/dist/{chunk-RP7TZEI6.js.map → chunk-24TJ77TT.js.map} +0 -0
- /package/dist/{chunk-ZLVUWROW.js.map → chunk-2OUSWDY6.js.map} +0 -0
- /package/dist/{chunk-M5IJP3YW.js.map → chunk-2PKGJPQL.js.map} +0 -0
- /package/dist/{chunk-FJMYMJLR.js.map → chunk-3JDA3XF5.js.map} +0 -0
- /package/dist/{chunk-JGT7M3BA.js.map → chunk-3MSMBVA3.js.map} +0 -0
- /package/dist/{chunk-6JYYSDHH.js.map → chunk-4EEDRKRT.js.map} +0 -0
- /package/dist/{chunk-YUHGJBQK.js.map → chunk-5LWVTSF7.js.map} +0 -0
- /package/dist/{chunk-2T2RF5ZK.js.map → chunk-5QZHDS4X.js.map} +0 -0
- /package/dist/{chunk-64JK4TL7.js.map → chunk-5WXJE5TV.js.map} +0 -0
- /package/dist/{chunk-DJ4CKWAD.js.map → chunk-5Y7VCE3A.js.map} +0 -0
- /package/dist/{chunk-G2XZFGYN.js.map → chunk-62JTOLEX.js.map} +0 -0
- /package/dist/{chunk-LVXBC6YB.js.map → chunk-65I2LZEK.js.map} +0 -0
- /package/dist/{chunk-NZLFVLLJ.js.map → chunk-7CRUDH7G.js.map} +0 -0
- /package/dist/{chunk-B4EGAHHS.js.map → chunk-7DE2NOON.js.map} +0 -0
- /package/dist/{chunk-BSDBDFKZ.js.map → chunk-7JFQI6DQ.js.map} +0 -0
- /package/dist/{chunk-BOADKFN4.js.map → chunk-7LNHRQKR.js.map} +0 -0
- /package/dist/{chunk-IV6APZJS.js.map → chunk-7OC3Q5X4.js.map} +0 -0
- /package/dist/{chunk-X2QPKLPL.js.map → chunk-ACX55QDO.js.map} +0 -0
- /package/dist/{chunk-YLPCEUXP.js.map → chunk-AEGQ5PJ7.js.map} +0 -0
- /package/dist/{chunk-GK6NOQE6.js.map → chunk-AERXJCOG.js.map} +0 -0
- /package/dist/{chunk-HW2QYZEV.js.map → chunk-B5GMOOY2.js.map} +0 -0
- /package/dist/{chunk-YHGRA7CK.js.map → chunk-CDQO5DCD.js.map} +0 -0
- /package/dist/{chunk-ENUI3IO7.js.map → chunk-D433RXFJ.js.map} +0 -0
- /package/dist/{chunk-IK63WA5A.js.map → chunk-D4SPMHEW.js.map} +0 -0
- /package/dist/{chunk-42DJ235G.js.map → chunk-DINRT75P.js.map} +0 -0
- /package/dist/{chunk-LELJRLPY.js.map → chunk-DQFZOPDL.js.map} +0 -0
- /package/dist/{chunk-WN4HFL4K.js.map → chunk-DQISIT63.js.map} +0 -0
- /package/dist/{chunk-Q7KQ5YHT.js.map → chunk-EIU642TN.js.map} +0 -0
- /package/dist/{chunk-EN2YX5SF.js.map → chunk-F4PEETM4.js.map} +0 -0
- /package/dist/{chunk-Q6C5PIBW.js.map → chunk-F5ZCNJ3V.js.map} +0 -0
- /package/dist/{chunk-KR5SZNNF.js.map → chunk-FEHVG4C4.js.map} +0 -0
- /package/dist/{chunk-42G4FIKQ.js.map → chunk-FG5QU7A2.js.map} +0 -0
- /package/dist/{chunk-IRGQYQQ7.js.map → chunk-FM5HN7LL.js.map} +0 -0
- /package/dist/{chunk-IDBLAGIM.js.map → chunk-FTNV5476.js.map} +0 -0
- /package/dist/{chunk-R4Y5PF25.js.map → chunk-FWVDTLRK.js.map} +0 -0
- /package/dist/{chunk-Q5FL3MEH.js.map → chunk-G4WZDQ32.js.map} +0 -0
- /package/dist/{chunk-4HNY6ORZ.js.map → chunk-GMHWCPLP.js.map} +0 -0
- /package/dist/{chunk-ZWWKKAJL.js.map → chunk-GYNCJSQ7.js.map} +0 -0
- /package/dist/{chunk-BJI2D4SU.js.map → chunk-GZBNEZIU.js.map} +0 -0
- /package/dist/{chunk-NYVECYCF.js.map → chunk-HNOKNHFO.js.map} +0 -0
- /package/dist/{chunk-XLXYARWC.js.map → chunk-HXCJF4XU.js.map} +0 -0
- /package/dist/{chunk-RMF763SX.js.map → chunk-IDRSTHVR.js.map} +0 -0
- /package/dist/{chunk-UXREWVW4.js.map → chunk-IW3RMBPB.js.map} +0 -0
- /package/dist/{chunk-W6YK672C.js.map → chunk-JTTLH646.js.map} +0 -0
- /package/dist/{chunk-BQB5ZMYO.js.map → chunk-KKNZ7PUZ.js.map} +0 -0
- /package/dist/{chunk-AAMFQHKI.js.map → chunk-KRPMISWO.js.map} +0 -0
- /package/dist/{chunk-XX2IGMCB.js.map → chunk-KV44HTMJ.js.map} +0 -0
- /package/dist/{chunk-FKFTPFUG.js.map → chunk-KXJ7XYUT.js.map} +0 -0
- /package/dist/{chunk-2NZ7KWRW.js.map → chunk-LAEW4S43.js.map} +0 -0
- /package/dist/{chunk-5SV2GFZX.js.map → chunk-LDP6LBDS.js.map} +0 -0
- /package/dist/{chunk-F6WCOXTF.js.map → chunk-LMLPLIT4.js.map} +0 -0
- /package/dist/{chunk-QJTFMH5Y.js.map → chunk-LVGVXG22.js.map} +0 -0
- /package/dist/{chunk-OINFRHLR.js.map → chunk-NRTVZSPO.js.map} +0 -0
- /package/dist/{chunk-IAZPYX6W.js.map → chunk-NRVGODSE.js.map} +0 -0
- /package/dist/{chunk-IHOXK2I6.js.map → chunk-NVKO5YKP.js.map} +0 -0
- /package/dist/{chunk-OQQXGYMX.js.map → chunk-OWYHHW67.js.map} +0 -0
- /package/dist/{chunk-KPKGDQR3.js.map → chunk-PZEXA6CM.js.map} +0 -0
- /package/dist/{chunk-DXODYQ7J.js.map → chunk-Q3THVX75.js.map} +0 -0
- /package/dist/{chunk-TAC5WFJC.js.map → chunk-QEGKMZNQ.js.map} +0 -0
- /package/dist/{chunk-VRF2KFI2.js.map → chunk-RA3C46VX.js.map} +0 -0
- /package/dist/{chunk-V2HHAAPI.js.map → chunk-RB776UH3.js.map} +0 -0
- /package/dist/{chunk-TAIIC5OM.js.map → chunk-SEHKTE4F.js.map} +0 -0
- /package/dist/{chunk-G6E7XJYL.js.map → chunk-SZAWZPY6.js.map} +0 -0
- /package/dist/{chunk-TYZK56OA.js.map → chunk-TPRBJF46.js.map} +0 -0
- /package/dist/{chunk-7DNMX7SA.js.map → chunk-TQC6ELJO.js.map} +0 -0
- /package/dist/{chunk-OVJFS5P5.js.map → chunk-U6HXB2OG.js.map} +0 -0
- /package/dist/{chunk-7WSQZJPC.js.map → chunk-V3JM4XEI.js.map} +0 -0
- /package/dist/{chunk-36S7UHAW.js.map → chunk-VEBYLZQQ.js.map} +0 -0
- /package/dist/{chunk-IMJPJY7V.js.map → chunk-VHF73MGU.js.map} +0 -0
- /package/dist/{chunk-TDULASPE.js.map → chunk-VKDWYDFQ.js.map} +0 -0
- /package/dist/{chunk-NKXHCCLB.js.map → chunk-VTPASHTO.js.map} +0 -0
- /package/dist/{chunk-4B5S3VWD.js.map → chunk-VXGB766B.js.map} +0 -0
- /package/dist/{chunk-ADP2VCP6.js.map → chunk-W5UAB6EH.js.map} +0 -0
- /package/dist/{chunk-L3R24OLZ.js.map → chunk-W65KK3X6.js.map} +0 -0
- /package/dist/{chunk-MNIPH6LD.js.map → chunk-WBYQA3VN.js.map} +0 -0
- /package/dist/{chunk-WD2JD2UB.js.map → chunk-WEQZ5OYK.js.map} +0 -0
- /package/dist/{chunk-AJW2K7K3.js.map → chunk-WKNWC46C.js.map} +0 -0
- /package/dist/{chunk-UD6J2LIA.js.map → chunk-XDH5MO6L.js.map} +0 -0
- /package/dist/{chunk-MRV7VSQK.js.map → chunk-Y23L4YJV.js.map} +0 -0
- /package/dist/{chunk-6ISP2EN2.js.map → chunk-YF3JERFZ.js.map} +0 -0
- /package/dist/{chunk-C5CPYMLX.js.map → chunk-YR6A6ZK7.js.map} +0 -0
- /package/dist/{chunk-EJO2NWW7.js.map → chunk-Z3YQOV45.js.map} +0 -0
- /package/dist/{chunk-6KTXE6A3.js.map → chunk-Z4TXB4K7.js.map} +0 -0
- /package/dist/{chunk-6VITOSPL.js.map → chunk-ZFUPRODV.js.map} +0 -0
- /package/dist/{contradiction-scan-GVD4JJ7B.js.map → contradiction-scan-5EJYRKD3.js.map} +0 -0
- /package/dist/{first-start-migration-L3UFX5CU.js.map → first-start-migration-7OATHJKB.js.map} +0 -0
- /package/dist/{resolution-CHNISQJB.js.map → resolution-UYJU5JUA.js.map} +0 -0
- /package/dist/{tier-stats-VK5PCO2L.js.map → tier-stats-VF6DS52E.js.map} +0 -0
|
@@ -2,6 +2,7 @@ import { parseOriginClass } from "../security/origin-authority.js";
|
|
|
2
2
|
import { normalizeAttributePairs } from "../structured-attributes.js";
|
|
3
3
|
import { buildProcedurePersistBody } from "../procedural/procedure-types.js";
|
|
4
4
|
import { screenCandidateFact } from "../security/injection-screen.js";
|
|
5
|
+
import type { InjectionScreenProfile } from "../security/injection-screen.js";
|
|
5
6
|
import type { EntityStructuredSection } from "../types.js";
|
|
6
7
|
|
|
7
8
|
export interface InjectionScreenCandidate {
|
|
@@ -41,11 +42,12 @@ export function serializeInjectionScreenCandidate(candidate: InjectionScreenCand
|
|
|
41
42
|
export function evaluateInjectionScreen(
|
|
42
43
|
candidate: InjectionScreenCandidate | string,
|
|
43
44
|
enabled: boolean,
|
|
45
|
+
profile: InjectionScreenProfile = "default",
|
|
44
46
|
): InjectionScreenGateResult {
|
|
45
47
|
if (!enabled) return { tags: [] };
|
|
46
48
|
const content =
|
|
47
49
|
typeof candidate === "string" ? candidate : serializeInjectionScreenCandidate(candidate);
|
|
48
|
-
const result = screenCandidateFact(content);
|
|
50
|
+
const result = screenCandidateFact(content, profile);
|
|
49
51
|
return {
|
|
50
52
|
status: result.quarantine === true ? "pending_review" : undefined,
|
|
51
53
|
tags: result.quarantine === true
|
|
@@ -63,6 +65,7 @@ export function evaluateInjectionScreen(
|
|
|
63
65
|
export function withholdScreenedStrings(
|
|
64
66
|
values: readonly unknown[],
|
|
65
67
|
enabled: boolean,
|
|
68
|
+
profile: InjectionScreenProfile = "default",
|
|
66
69
|
): { kept: string[]; withheldRules: string[] } {
|
|
67
70
|
const kept: string[] = [];
|
|
68
71
|
const withheldRules: string[] = [];
|
|
@@ -72,7 +75,7 @@ export function withholdScreenedStrings(
|
|
|
72
75
|
kept.push(value);
|
|
73
76
|
continue;
|
|
74
77
|
}
|
|
75
|
-
const screened = screenCandidateFact(value);
|
|
78
|
+
const screened = screenCandidateFact(value, profile);
|
|
76
79
|
if (screened.quarantine) {
|
|
77
80
|
withheldRules.push(...screened.findings.map((finding) => `injection-screen:${finding.rule}`));
|
|
78
81
|
} else {
|
|
@@ -99,20 +102,20 @@ export interface ScreenedEntityWrite {
|
|
|
99
102
|
* `withheldRules` (entities carry no review status; routing is #2397).
|
|
100
103
|
* Returns null for entities without a usable name/type.
|
|
101
104
|
*/
|
|
102
|
-
export function screenEntityForIndex(entity: unknown, enabled: boolean): ScreenedEntityWrite | null {
|
|
105
|
+
export function screenEntityForIndex(entity: unknown, enabled: boolean, profile: InjectionScreenProfile = "default"): ScreenedEntityWrite | null {
|
|
103
106
|
const record = entity as { name?: unknown; type?: unknown; source?: unknown; facts?: unknown; structuredSections?: unknown } | null;
|
|
104
107
|
const name = record?.name;
|
|
105
108
|
const type = record?.type;
|
|
106
109
|
if (typeof name !== "string" || !name.trim() || typeof type !== "string" || !type.trim()) return null;
|
|
107
110
|
// #1955 review: the raw name is emitted as the entity target outside the
|
|
108
111
|
// snippet fence — a flagged name withholds the whole entity from the index.
|
|
109
|
-
const nameScreen = withholdScreenedStrings([name], enabled);
|
|
110
|
-
const factScreen = withholdScreenedStrings(Array.isArray(record?.facts) ? record.facts : [], enabled);
|
|
112
|
+
const nameScreen = withholdScreenedStrings([name], enabled, profile);
|
|
113
|
+
const factScreen = withholdScreenedStrings(Array.isArray(record?.facts) ? record.facts : [], enabled, profile);
|
|
111
114
|
const rawSections: EntityStructuredSection[] | undefined = Array.isArray(record?.structuredSections)
|
|
112
115
|
? (record.structuredSections as EntityStructuredSection[])
|
|
113
116
|
: undefined;
|
|
114
117
|
const sectionScreens = enabled && rawSections
|
|
115
|
-
? rawSections.map((section) => ({ section, screen: withholdScreenedStrings(section?.facts ?? [], true) }))
|
|
118
|
+
? rawSections.map((section) => ({ section, screen: withholdScreenedStrings(section?.facts ?? [], true, profile) }))
|
|
116
119
|
: undefined;
|
|
117
120
|
return {
|
|
118
121
|
name,
|
|
@@ -155,8 +158,9 @@ export function screenEntityForIndex(entity: unknown, enabled: boolean): Screene
|
|
|
155
158
|
export function screenPersistStrings(
|
|
156
159
|
values: readonly string[],
|
|
157
160
|
enabled: boolean,
|
|
161
|
+
profile: InjectionScreenProfile = "default",
|
|
158
162
|
): { kept: string[]; warning?: string } {
|
|
159
|
-
const screen = withholdScreenedStrings(values, enabled);
|
|
163
|
+
const screen = withholdScreenedStrings(values, enabled, profile);
|
|
160
164
|
return {
|
|
161
165
|
kept: screen.kept,
|
|
162
166
|
...(screen.withheldRules.length > 0
|
|
@@ -20,7 +20,7 @@ export function deriveExtractionOriginContext(
|
|
|
20
20
|
targetTurns: readonly BufferTurn[],
|
|
21
21
|
fallbackConnector?: string,
|
|
22
22
|
): Pick<ExtractionSourceContext, "turnRole" | "importAdapter" | "sourceConnector" | "originConflict"> {
|
|
23
|
-
const originRoles = new Set(targetTurns.map((turn) => turn.role));
|
|
23
|
+
const originRoles = new Set(targetTurns.map((turn) => turn.originRole ?? turn.role));
|
|
24
24
|
const turnRole = originRoles.size === 1 ? [...originRoles][0] : undefined;
|
|
25
25
|
const originConnectors = new Set(
|
|
26
26
|
targetTurns
|
|
@@ -1623,7 +1623,7 @@ export class ExtractionPersistCoordinator {
|
|
|
1623
1623
|
entityRef: fact.entityRef,
|
|
1624
1624
|
validAt: biTemporal?.validFrom ?? sourceContext?.validAt,
|
|
1625
1625
|
};
|
|
1626
|
-
const { status: injectionScreenStatus, tags: injectionScreenTags } = evaluateInjectionScreen({ content: fact.content, category: writeCategory, structuredAttributes: fact.structuredAttributes, procedureSteps: fact.procedureSteps }, resolveSecurityCapabilities(this.deps.config).injectionScreen);
|
|
1626
|
+
const { status: injectionScreenStatus, tags: injectionScreenTags } = evaluateInjectionScreen({ content: fact.content, category: writeCategory, structuredAttributes: fact.structuredAttributes, procedureSteps: fact.procedureSteps }, resolveSecurityCapabilities(this.deps.config).injectionScreen, resolveSecurityCapabilities(this.deps.config).injectionScreenProfile);
|
|
1627
1627
|
// #1669 redaction-rule gate: consult BOTH source and target namespace
|
|
1628
1628
|
// rules before any write. A never-store pattern registered under the
|
|
1629
1629
|
// source namespace must survive scope-routing to a different target
|
|
@@ -2841,12 +2841,12 @@ export class ExtractionPersistCoordinator {
|
|
|
2841
2841
|
durableNonFactTouchRecorded = true;
|
|
2842
2842
|
touchBaseNonFactNamespace();
|
|
2843
2843
|
};
|
|
2844
|
-
// #1955 review: entity fields
|
|
2845
|
-
// entering the index (screenEntityForIndex); withheld fields are logged.
|
|
2844
|
+
// #1955 review: entity fields are screened before entering the index (screenEntityForIndex).
|
|
2846
2845
|
const entityScreenOn = resolveSecurityCapabilities(this.deps.config).injectionScreen;
|
|
2846
|
+
const entityScreenProfile = resolveSecurityCapabilities(this.deps.config).injectionScreenProfile;
|
|
2847
2847
|
for (const entity of entities) {
|
|
2848
2848
|
try {
|
|
2849
|
-
const screened = screenEntityForIndex(entity, entityScreenOn);
|
|
2849
|
+
const screened = screenEntityForIndex(entity, entityScreenOn, entityScreenProfile);
|
|
2850
2850
|
if (!screened) continue;
|
|
2851
2851
|
if (screened.withheldRules.length > 0) log.warn(`persistExtraction: injection screen withheld ${screened.withheldRules.length} entity field(s) for "${screened.name}" [${screened.withheldRules.join(", ")}]`);
|
|
2852
2852
|
if (screened.withheld) continue;
|
|
@@ -2874,7 +2874,7 @@ export class ExtractionPersistCoordinator {
|
|
|
2874
2874
|
for (const rel of result.relationships.slice(0, 5)) {
|
|
2875
2875
|
if (!rel.source || !rel.target || !rel.label) continue;
|
|
2876
2876
|
const relScreen = screenPersistStrings([`${rel.source}\n${rel.label}\n${rel.target}`,
|
|
2877
|
-
`${rel.target}\n${rel.label} (reverse)\n${rel.source}`], entityScreenOn);
|
|
2877
|
+
`${rel.target}\n${rel.label} (reverse)\n${rel.source}`], entityScreenOn, entityScreenProfile);
|
|
2878
2878
|
if (relScreen.warning) {
|
|
2879
2879
|
log.warn(`persistExtraction(relationship): ${relScreen.warning}`);
|
|
2880
2880
|
continue;
|
|
@@ -2911,7 +2911,7 @@ export class ExtractionPersistCoordinator {
|
|
|
2911
2911
|
}
|
|
2912
2912
|
// #1955 review: profile + questions render verbatim into model context —
|
|
2913
2913
|
// screened; questions screen question+context JOINED (routing: #2397).
|
|
2914
|
-
const profileScreen = screenPersistStrings(profileUpdates, entityScreenOn);
|
|
2914
|
+
const profileScreen = screenPersistStrings(profileUpdates, entityScreenOn, entityScreenProfile);
|
|
2915
2915
|
if (profileScreen.warning) log.warn(`persistExtraction(profile): ${profileScreen.warning}`);
|
|
2916
2916
|
if (profileScreen.kept.length > 0) {
|
|
2917
2917
|
await storage.appendToProfile(profileScreen.kept);
|
|
@@ -2919,7 +2919,7 @@ export class ExtractionPersistCoordinator {
|
|
|
2919
2919
|
}
|
|
2920
2920
|
|
|
2921
2921
|
for (const q of questions) {
|
|
2922
|
-
const qScreen = screenPersistStrings([`${q.question}\n${q.context ?? ""}`], entityScreenOn);
|
|
2922
|
+
const qScreen = screenPersistStrings([`${q.question}\n${q.context ?? ""}`], entityScreenOn, entityScreenProfile);
|
|
2923
2923
|
if (qScreen.warning) {
|
|
2924
2924
|
log.warn(`persistExtraction(question): ${qScreen.warning}`);
|
|
2925
2925
|
continue;
|
|
@@ -2934,7 +2934,7 @@ export class ExtractionPersistCoordinator {
|
|
|
2934
2934
|
// Persist identity reflection (screened — #1955): durable namespace-local
|
|
2935
2935
|
// state; identity-ONLY extraction still counts as durable non-fact (NIIly).
|
|
2936
2936
|
if (resolveRecallEnhancementCapabilities(this.deps.config).identity && result.identityReflection) {
|
|
2937
|
-
const idScreen = screenPersistStrings([result.identityReflection], entityScreenOn);
|
|
2937
|
+
const idScreen = screenPersistStrings([result.identityReflection], entityScreenOn, entityScreenProfile);
|
|
2938
2938
|
if (idScreen.warning) log.warn(`persistExtraction(identity): ${idScreen.warning}`);
|
|
2939
2939
|
else {
|
|
2940
2940
|
try {
|
|
@@ -187,6 +187,18 @@ test("mixed connector identities resolve to unknown origin", () => {
|
|
|
187
187
|
assert.equal(classifyExtractionOrigin(context), "unknown");
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
+
test("trusted originRole preserves tool authority independently of conversation role", () => {
|
|
191
|
+
const turns: BufferTurn[] = [{
|
|
192
|
+
role: "user",
|
|
193
|
+
originRole: "tool",
|
|
194
|
+
content: "Tool result rendered into conversation context.",
|
|
195
|
+
timestamp: "2026-01-01T00:00:00Z",
|
|
196
|
+
}];
|
|
197
|
+
const context = deriveExtractionOriginContext(turns);
|
|
198
|
+
assert.equal(context.turnRole, "tool");
|
|
199
|
+
assert.equal(classifyExtractionOrigin(context), "tool_output");
|
|
200
|
+
});
|
|
201
|
+
|
|
190
202
|
test("empty import source labels do not create adapter origins", () => {
|
|
191
203
|
const turns: BufferTurn[] = [{
|
|
192
204
|
role: "user",
|
|
@@ -274,6 +274,7 @@ export class TurnIngestionCoordinator {
|
|
|
274
274
|
rawContent: turn.rawContent,
|
|
275
275
|
sourceFormat: turn.sourceFormat,
|
|
276
276
|
sourceConnector: turn.sourceConnector,
|
|
277
|
+
...(turn.originRole ? { originRole: turn.originRole } : {}),
|
|
277
278
|
});
|
|
278
279
|
bySession.set(key, list);
|
|
279
280
|
}
|
|
@@ -461,6 +462,7 @@ export class TurnIngestionCoordinator {
|
|
|
461
462
|
turnFingerprint: turn.turnFingerprint,
|
|
462
463
|
persistProcessedFingerprint: turn.persistProcessedFingerprint === true,
|
|
463
464
|
...(turn.sourceConnector ? { sourceConnector: turn.sourceConnector } : {}),
|
|
465
|
+
...(turn.originRole ? { originRole: turn.originRole } : {}),
|
|
464
466
|
});
|
|
465
467
|
}
|
|
466
468
|
if (sessionTurns.length === 0) {
|
package/src/profiling.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import test, { describe } from "node:test";
|
|
@@ -283,4 +283,23 @@ describe("ProfilingCollector", () => {
|
|
|
283
283
|
cleanup(dir);
|
|
284
284
|
}
|
|
285
285
|
});
|
|
286
|
+
|
|
287
|
+
test("construction never creates the storage dir; the first persist does", () => {
|
|
288
|
+
const dir = tempDir();
|
|
289
|
+
const storageDir = join(dir, "profiling");
|
|
290
|
+
try {
|
|
291
|
+
const collector = new ProfilingCollector({ enabled: true, storageDir, maxTraces: 10 });
|
|
292
|
+
// Constructing a collector happens on every plugin registration, including
|
|
293
|
+
// read-only discovery passes, so it must not touch the memory dir.
|
|
294
|
+
assert.equal(existsSync(storageDir), false, "no dir before a trace is written");
|
|
295
|
+
collector.startTrace("recall", "test-session");
|
|
296
|
+
collector.startSpan("qmd-search");
|
|
297
|
+
collector.endSpan("qmd-search");
|
|
298
|
+
collector.endTrace();
|
|
299
|
+
assert.equal(existsSync(storageDir), true, "the first persisted trace creates it");
|
|
300
|
+
assert.equal(readdirSync(storageDir).length, 1);
|
|
301
|
+
} finally {
|
|
302
|
+
cleanup(dir);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
286
305
|
});
|
package/src/profiling.ts
CHANGED
|
@@ -118,13 +118,6 @@ export class ProfilingCollector {
|
|
|
118
118
|
this.enabled = config.enabled;
|
|
119
119
|
this.storageDir = config.storageDir;
|
|
120
120
|
this.maxTraces = Math.max(0, config.maxTraces);
|
|
121
|
-
|
|
122
|
-
if (this.enabled) {
|
|
123
|
-
if (!existsSync(this.storageDir)) {
|
|
124
|
-
mkdirSync(this.storageDir, { recursive: true });
|
|
125
|
-
log.debug(`profiling: created storage dir ${this.storageDir}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
121
|
}
|
|
129
122
|
|
|
130
123
|
get isEnabled(): boolean {
|
|
@@ -347,6 +340,13 @@ export class ProfilingCollector {
|
|
|
347
340
|
const filename = `${trace.kind}-${trace.traceId}.jsonl`;
|
|
348
341
|
const filepath = join(this.storageDir, filename);
|
|
349
342
|
try {
|
|
343
|
+
// Created on first persist, never at construction: constructing a
|
|
344
|
+
// collector (plugin registration, a discovery pass, CLI metadata) must
|
|
345
|
+
// not touch the operator's memory dir when no trace is ever written.
|
|
346
|
+
if (!existsSync(this.storageDir)) {
|
|
347
|
+
mkdirSync(this.storageDir, { recursive: true });
|
|
348
|
+
log.debug(`profiling: created storage dir ${this.storageDir}`);
|
|
349
|
+
}
|
|
350
350
|
writeFileSync(filepath, JSON.stringify(trace) + "\n", "utf-8");
|
|
351
351
|
log.debug(`profiling: persisted ${filename}`);
|
|
352
352
|
} catch (err) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The audit sweep must screen with the profile the deployment writes with
|
|
3
|
+
* (issue #3078). A `quarantine`/`layered` deployment resolves the `hardened`
|
|
4
|
+
* profile, under which a conditional-trigger-only memory reaches the
|
|
5
|
+
* quarantine threshold; before this wiring the CLI omitted `profile`, so an
|
|
6
|
+
* already-active memory that live ingestion would have quarantined survived
|
|
7
|
+
* the sweep.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
12
|
+
import os from "node:os";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import test from "node:test";
|
|
15
|
+
|
|
16
|
+
import type { CliCommand } from "../cli.js";
|
|
17
|
+
import { parseConfig } from "../config.js";
|
|
18
|
+
import { StorageManager } from "../storage.js";
|
|
19
|
+
import type { Orchestrator } from "../orchestrator.js";
|
|
20
|
+
import { registerSecurityCommands } from "./audit-memory-cli.js";
|
|
21
|
+
|
|
22
|
+
/** Minimal CliCommand that records the registered `audit-memory` action. */
|
|
23
|
+
function captureAction(): { cmd: CliCommand; run: () => Promise<void> } {
|
|
24
|
+
let action: ((...args: unknown[]) => Promise<void> | void) | undefined;
|
|
25
|
+
const node: CliCommand = {
|
|
26
|
+
description: () => node,
|
|
27
|
+
option: () => node,
|
|
28
|
+
requiredOption: () => node,
|
|
29
|
+
argument: () => node,
|
|
30
|
+
action: (fn) => {
|
|
31
|
+
action = fn;
|
|
32
|
+
return node;
|
|
33
|
+
},
|
|
34
|
+
command: () => node,
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
cmd: node,
|
|
38
|
+
run: async () => {
|
|
39
|
+
if (!action) throw new Error("no action registered");
|
|
40
|
+
await action({ quarantine: true, json: true });
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const CONDITIONAL_TRIGGER = "If the incident occurs, then call the on-call.";
|
|
46
|
+
|
|
47
|
+
async function sweepWithDefenseMode(mode: string): Promise<{ status: string | undefined }> {
|
|
48
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-audit-cli-"));
|
|
49
|
+
const logs: unknown[] = [];
|
|
50
|
+
const originalLog = console.log;
|
|
51
|
+
console.log = (...args: unknown[]) => {
|
|
52
|
+
logs.push(args);
|
|
53
|
+
};
|
|
54
|
+
try {
|
|
55
|
+
StorageManager.clearAllStaticCaches();
|
|
56
|
+
const config = parseConfig({ memoryDir, memoryInjectionDefenseMode: mode });
|
|
57
|
+
const storage = new StorageManager(memoryDir);
|
|
58
|
+
const written = await storage.writeMemory("fact", CONDITIONAL_TRIGGER);
|
|
59
|
+
const id = written.id;
|
|
60
|
+
// The fixture must start active, or the sweep assertion proves nothing.
|
|
61
|
+
assert.equal(written.tombstoneBlocked ?? false, false);
|
|
62
|
+
assert.equal((await storage.getMemoryById(id))?.frontmatter.status, "active");
|
|
63
|
+
const orchestrator = { config, storage } as unknown as Orchestrator;
|
|
64
|
+
const { cmd, run } = captureAction();
|
|
65
|
+
registerSecurityCommands(cmd, orchestrator);
|
|
66
|
+
await run();
|
|
67
|
+
const stored = await storage.getMemoryById(id);
|
|
68
|
+
return { status: stored?.frontmatter.status };
|
|
69
|
+
} finally {
|
|
70
|
+
console.log = originalLog;
|
|
71
|
+
StorageManager.clearAllStaticCaches();
|
|
72
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
test("the plugin audit-memory command screens with the configured profile", async () => {
|
|
77
|
+
// `layered` resolves the hardened profile: weight 4 for a conditional
|
|
78
|
+
// trigger alone, so the sweep retro-quarantines the memory.
|
|
79
|
+
const hardened = await sweepWithDefenseMode("layered");
|
|
80
|
+
assert.equal(hardened.status, "pending_review");
|
|
81
|
+
// `custom` resolves the default profile: weight 3, below the threshold,
|
|
82
|
+
// so the same memory stays active. This proves the profile is read from
|
|
83
|
+
// the configuration rather than hard-coded either way.
|
|
84
|
+
const lenient = await sweepWithDefenseMode("custom");
|
|
85
|
+
assert.equal(lenient.status, "active");
|
|
86
|
+
// `off`/`fencing` disable live screening, so the sweep keeps the previous
|
|
87
|
+
// `default` weighting instead of becoming stricter than the write path.
|
|
88
|
+
const screenDisabled = await sweepWithDefenseMode("off");
|
|
89
|
+
assert.equal(screenDisabled.status, "active");
|
|
90
|
+
});
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { CliCommand } from "../cli.js";
|
|
8
8
|
import { runAuditMemoryCliCommand } from "../cli.js";
|
|
9
9
|
import type { Orchestrator } from "../orchestrator.js";
|
|
10
|
+
import { auditScreenProfile } from "./audit-memory.js";
|
|
10
11
|
import { formatAuditMemoryReport } from "./audit-memory.js";
|
|
11
12
|
|
|
12
13
|
export function registerSecurityCommands(
|
|
@@ -32,6 +33,12 @@ export function registerSecurityCommands(
|
|
|
32
33
|
? options.since.trim()
|
|
33
34
|
: undefined,
|
|
34
35
|
quarantine: options.quarantine === true,
|
|
36
|
+
// Screen with the profile live ingestion uses, or a memory this
|
|
37
|
+
// deployment would quarantine on write stays active once stored
|
|
38
|
+
// (#3078). A deployment with the screen DISABLED keeps the previous
|
|
39
|
+
// `default` weighting: the sweep must not be stricter than the
|
|
40
|
+
// write path the operator configured.
|
|
41
|
+
profile: auditScreenProfile(orchestrator.config),
|
|
35
42
|
});
|
|
36
43
|
if (options.json === true) {
|
|
37
44
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -162,27 +162,38 @@ test("auditMemoryStore audits legacy missing-status memories and excludes archiv
|
|
|
162
162
|
}
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
test("auditMemoryStore
|
|
165
|
+
test("auditMemoryStore quarantines a single-rule finding and leaves benign prose untouched", async () => {
|
|
166
|
+
// The hardened profile keeps every rule at weight >= INJECTION_SCREEN_THRESHOLD,
|
|
167
|
+
// so one finding is sufficient to quarantine; the default profile keeps
|
|
168
|
+
// conditional triggers sub-threshold (#1962).
|
|
166
169
|
const root = await mkdtemp(path.join(tmpdir(), "remnic-audit-memory-threshold-"));
|
|
167
170
|
try {
|
|
168
|
-
const
|
|
171
|
+
const trigger = makeMemory(
|
|
169
172
|
root,
|
|
170
|
-
"
|
|
171
|
-
"session-
|
|
173
|
+
"trigger",
|
|
174
|
+
"session-trigger",
|
|
172
175
|
"If the incident occurs, then call the on-call.",
|
|
173
176
|
);
|
|
177
|
+
const benign = makeMemory(
|
|
178
|
+
root,
|
|
179
|
+
"benign",
|
|
180
|
+
"session-benign",
|
|
181
|
+
"Meeting notes: if the build fails, the release is delayed.",
|
|
182
|
+
);
|
|
174
183
|
const report = await auditMemoryStore({
|
|
175
184
|
memoryDir: root,
|
|
176
|
-
storage: memoryStorage([
|
|
185
|
+
storage: memoryStorage([trigger, benign]),
|
|
177
186
|
quarantine: true,
|
|
187
|
+
profile: "hardened",
|
|
178
188
|
});
|
|
179
189
|
assert.deepEqual(
|
|
180
|
-
report.findings.map(({ category, rule }) => ({ category, rule })),
|
|
181
|
-
[{ category: "injection-signature", rule: "conditional-trigger" }],
|
|
190
|
+
report.findings.map(({ memoryId, category, rule }) => ({ memoryId, category, rule })),
|
|
191
|
+
[{ memoryId: "trigger", category: "injection-signature", rule: "conditional-trigger" }],
|
|
182
192
|
);
|
|
183
|
-
assert.deepEqual(report.quarantinedMemoryIds, []);
|
|
184
|
-
assert.equal(report.transitions,
|
|
185
|
-
assert.equal(
|
|
193
|
+
assert.deepEqual(report.quarantinedMemoryIds, ["trigger"]);
|
|
194
|
+
assert.equal(report.transitions, 1);
|
|
195
|
+
assert.equal(trigger.frontmatter.status, "pending_review");
|
|
196
|
+
assert.equal(benign.frontmatter.status, "active");
|
|
186
197
|
} finally {
|
|
187
198
|
await rm(root, { recursive: true, force: true });
|
|
188
199
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { MemoryFile, MemoryFrontmatter } from "../types.js";
|
|
2
2
|
import { screenCandidateFact } from "./injection-screen.js";
|
|
3
|
+
import type { InjectionScreenProfile } from "./injection-screen.js";
|
|
4
|
+
import { resolveSecurityCapabilities, type SecurityConfigProjection } from "../capabilities.js";
|
|
3
5
|
import { inferMemoryStatus, isArchivedMemoryPath, toMemoryPathRel } from "../memory-lifecycle-ledger-utils.js";
|
|
4
6
|
import { parseStrictCliDate } from "../training-export/date-parse.js";
|
|
5
7
|
|
|
@@ -36,6 +38,8 @@ export interface AuditMemoryStoreOptions {
|
|
|
36
38
|
memoryDir: string;
|
|
37
39
|
since?: string | Date;
|
|
38
40
|
quarantine?: boolean;
|
|
41
|
+
/** Screen profile; defaults to "default" (named defense modes pass "hardened"). */
|
|
42
|
+
profile?: InjectionScreenProfile;
|
|
39
43
|
now?: Date;
|
|
40
44
|
storage: AuditMemoryStorage;
|
|
41
45
|
}
|
|
@@ -203,6 +207,19 @@ function writeBurstStats(groups: Map<string, MemoryFile[]>): AuditMemoryReport["
|
|
|
203
207
|
};
|
|
204
208
|
}
|
|
205
209
|
|
|
210
|
+
/**
|
|
211
|
+
* The screen profile an audit sweep must use, resolved through the shared
|
|
212
|
+
* security capability plan (#1523) rather than read off config (#3078). The
|
|
213
|
+
* sweep matches the live write path, so a memory this deployment would
|
|
214
|
+
* quarantine on write does not survive once stored; a deployment with the
|
|
215
|
+
* screen DISABLED keeps the `default` weighting, because the sweep must
|
|
216
|
+
* never be stricter than the configured write path.
|
|
217
|
+
*/
|
|
218
|
+
export function auditScreenProfile(config: SecurityConfigProjection): InjectionScreenProfile {
|
|
219
|
+
const capabilities = resolveSecurityCapabilities(config);
|
|
220
|
+
return capabilities.injectionScreen ? capabilities.injectionScreenProfile : "default";
|
|
221
|
+
}
|
|
222
|
+
|
|
206
223
|
export async function auditMemoryStore(options: AuditMemoryStoreOptions): Promise<AuditMemoryReport> {
|
|
207
224
|
const since = parseSince(options.since);
|
|
208
225
|
const storage = options.storage;
|
|
@@ -214,9 +231,10 @@ export async function auditMemoryStore(options: AuditMemoryStoreOptions): Promis
|
|
|
214
231
|
);
|
|
215
232
|
const findings: AuditMemoryFinding[] = [];
|
|
216
233
|
const quarantinePaths = new Map<string, { injection: boolean; burst: boolean }>();
|
|
234
|
+
const profile = options.profile ?? "default";
|
|
217
235
|
|
|
218
236
|
for (const memory of activeMemories) {
|
|
219
|
-
const screened = screenCandidateFact(memory.content);
|
|
237
|
+
const screened = screenCandidateFact(memory.content, profile);
|
|
220
238
|
if (screened.quarantine === true) quarantinePaths.set(memory.path, { injection: true, burst: false });
|
|
221
239
|
for (const screenFinding of screened.findings) {
|
|
222
240
|
findings.push({
|
|
@@ -268,7 +286,7 @@ export async function auditMemoryStore(options: AuditMemoryStoreOptions): Promis
|
|
|
268
286
|
// the live record first.
|
|
269
287
|
const memory = await storage.readMemoryByPath(staleMemory.path);
|
|
270
288
|
if (!memory || !isActive(memory, options.memoryDir)) continue;
|
|
271
|
-
if (qInfo.injection && !qInfo.burst && !screenCandidateFact(memory.content).quarantine) continue;
|
|
289
|
+
if (qInfo.injection && !qInfo.burst && !screenCandidateFact(memory.content, profile).quarantine) continue;
|
|
272
290
|
const changed = await storage.writeMemoryFrontmatterIfUnchanged(memory, {
|
|
273
291
|
status: "pending_review",
|
|
274
292
|
updated: now,
|