@remnic/core 10.0.69 → 10.0.70
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 +80 -77
- package/dist/access-authorization-probe.js +81 -78
- package/dist/access-boundary.js +80 -77
- package/dist/access-cli.js +194 -191
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +80 -77
- package/dist/access-http-briefing.js +81 -78
- package/dist/access-http-query.js +82 -79
- package/dist/access-http.js +104 -101
- package/dist/access-identity-continuity-surface.js +80 -77
- package/dist/access-lcm-surface.js +80 -77
- package/dist/access-mcp.js +93 -90
- package/dist/access-namespace-preflight.js +80 -77
- package/dist/access-observe-lcm-archive.d.ts +5 -0
- package/dist/access-observe-lcm-archive.d.ts.map +1 -0
- package/dist/access-observe-lcm-archive.js +9 -0
- package/dist/access-observe-objective-state.d.ts +8 -0
- package/dist/access-observe-objective-state.d.ts.map +1 -0
- package/dist/access-observe-objective-state.js +17 -0
- package/dist/access-observe-stage-timing.d.ts +11 -0
- package/dist/access-observe-stage-timing.d.ts.map +1 -0
- package/dist/access-observe-stage-timing.js +9 -0
- package/dist/access-observe-stage-timing.js.map +1 -0
- package/dist/access-observe-write-category.js +13 -13
- package/dist/access-observe-write-surface.d.ts.map +1 -1
- package/dist/access-observe-write-surface.js +80 -77
- package/dist/access-offline-manifest.js +8 -8
- package/dist/access-operations-batch.js +83 -80
- package/dist/access-operations.js +90 -87
- package/dist/access-recall-concurrency.js +80 -77
- package/dist/access-recall-response.js +80 -77
- package/dist/access-recall-surface.js +80 -77
- package/dist/access-schema.js +12 -12
- package/dist/access-service.d.ts +1 -0
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +80 -77
- package/dist/access-surface-catalog.js +82 -79
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-recall.js +24 -24
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/briefing.js +15 -15
- package/dist/buffer.js +8 -8
- package/dist/calibration.js +4 -4
- package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/capsule-crypto-7FJQINUR.js.map +1 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +20 -20
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +3 -3
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-NCZGGGWI.js → chunk-2BBMCMJS.js} +12 -12
- package/dist/{chunk-UVVFPCPX.js → chunk-2GV24KFD.js} +2 -2
- package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-QDZWQBLU.js → chunk-2R7OCBRR.js} +2 -2
- package/dist/{chunk-U2CAGMUD.js → chunk-2YPIFJHL.js} +2 -2
- package/dist/{chunk-4SOWMA55.js → chunk-3AASGFR4.js} +3 -3
- package/dist/{chunk-CI3MAAIY.js → chunk-3F7N4I5I.js} +2 -2
- package/dist/{chunk-PLCYKAWH.js → chunk-3ICAWZUU.js} +4 -4
- package/dist/{chunk-B4DXREWC.js → chunk-3W37NITP.js} +2 -2
- package/dist/{chunk-R4ZHF4K2.js → chunk-43FLJLT2.js} +4 -4
- package/dist/{chunk-RLVI2M5Q.js → chunk-4BOBW6H4.js} +2 -2
- package/dist/{chunk-HXFIILGF.js → chunk-4RASUAN3.js} +4 -4
- package/dist/{chunk-JLMOH5JA.js → chunk-5FMGPV34.js} +2 -2
- package/dist/{chunk-EI6M5NVK.js → chunk-5JBVPJCT.js} +2 -2
- package/dist/{chunk-74AY3S7U.js → chunk-5RW247F5.js} +10 -10
- package/dist/{chunk-P4TV3TQ2.js → chunk-5VQSKBNF.js} +5 -5
- package/dist/{chunk-H5DIRCZP.js → chunk-64U42JL5.js} +18 -18
- package/dist/{chunk-4XINO77L.js → chunk-66JOLMWS.js} +2 -2
- package/dist/{chunk-WHROVQ5Q.js → chunk-66PEACZP.js} +3 -3
- package/dist/{chunk-GKL6QYZO.js → chunk-6L63IN3M.js} +1 -1
- package/dist/{chunk-Y3XPSGNF.js → chunk-6QS5NQJH.js} +2 -2
- package/dist/{chunk-KOLJJN5G.js → chunk-AQNHEDXP.js} +11 -4
- package/dist/chunk-AQNHEDXP.js.map +1 -0
- package/dist/{chunk-6WKWL53V.js → chunk-B7YAIHAV.js} +127 -121
- package/dist/chunk-B7YAIHAV.js.map +1 -0
- package/dist/{chunk-M4IEZGCK.js → chunk-BFBS7OFQ.js} +7 -7
- package/dist/{chunk-4PG5R6BJ.js → chunk-BNU5TQLR.js} +2 -2
- package/dist/{chunk-B3MWELZ3.js → chunk-BW33YTF3.js} +2 -2
- package/dist/{chunk-H6VJSITJ.js → chunk-CIL4GHMD.js} +3 -3
- package/dist/{chunk-5UM4H64E.js → chunk-D3AGBN6G.js} +2 -2
- package/dist/{chunk-IG3EZ3TF.js → chunk-DM3UZUCN.js} +2 -2
- package/dist/{chunk-PMDOGXA2.js → chunk-EF3HNVL4.js} +2 -2
- package/dist/{chunk-TVE7W67N.js → chunk-EIZ4MK33.js} +2 -2
- package/dist/{chunk-AUFPPBC3.js → chunk-ENPJX3BB.js} +3 -3
- package/dist/{chunk-63A2BU2B.js → chunk-F4ZHKDBW.js} +5 -5
- package/dist/{chunk-3LBDEGQA.js → chunk-FDUCIUQ5.js} +2 -2
- package/dist/{chunk-CTH2CQCB.js → chunk-FM6UKZAE.js} +2 -2
- package/dist/{chunk-XVZA6VXC.js → chunk-FZVROZKC.js} +4 -4
- package/dist/{chunk-IAGVM7EY.js → chunk-GMSRLDVR.js} +2 -2
- package/dist/{chunk-HPB5TF55.js → chunk-GT4G2S2Y.js} +3 -3
- package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
- package/dist/{chunk-5MGKGEL7.js → chunk-HNF7IMYQ.js} +2 -2
- package/dist/{chunk-W7K5BP3M.js → chunk-HQPFACXO.js} +2 -2
- package/dist/{chunk-TECK5IVY.js → chunk-HZNURRDS.js} +2 -2
- package/dist/{chunk-SXTVZ6JL.js → chunk-I6HRHJRA.js} +2 -2
- package/dist/{chunk-NHWDTOBN.js → chunk-ILD7N3XC.js} +20 -20
- package/dist/{chunk-4NV7O76Q.js → chunk-J5TVHCDG.js} +2 -2
- package/dist/{chunk-AL3TCNSJ.js → chunk-J6GZ2254.js} +4 -4
- package/dist/{chunk-OH55F76C.js → chunk-JEX2JL3U.js} +2 -2
- package/dist/{chunk-AVGDYYMA.js → chunk-JGQEF33K.js} +3 -3
- package/dist/{chunk-6P7U3HWG.js → chunk-K36RMINQ.js} +3 -3
- package/dist/{chunk-UW6LBFYN.js → chunk-K47G2CBP.js} +6 -6
- package/dist/{chunk-3RHU5MD7.js → chunk-K7LLGCPH.js} +2 -2
- package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
- package/dist/chunk-L3E7MLMK.js +20 -0
- package/dist/chunk-L3E7MLMK.js.map +1 -0
- package/dist/{chunk-R2BIVU2Z.js → chunk-LTST4R7U.js} +2 -2
- package/dist/{chunk-IZBIDPMY.js → chunk-LYTKNK5O.js} +2 -2
- package/dist/{chunk-HH73M65H.js → chunk-MCFI6G7U.js} +3 -3
- package/dist/{chunk-V6SBA5O7.js → chunk-MFKBYXBY.js} +5 -5
- package/dist/{chunk-AYJQ644F.js → chunk-MK7HXGM6.js} +2 -2
- package/dist/{chunk-MOIJWWNA.js → chunk-MKMIY3E7.js} +13 -13
- package/dist/{chunk-EXQJMFRF.js → chunk-MRN2MKB7.js} +2 -2
- package/dist/{chunk-W26D2USW.js → chunk-MUH7RR6T.js} +6 -6
- package/dist/{chunk-64JLBFCQ.js → chunk-N7NAGECH.js} +2 -2
- package/dist/{chunk-75DCOS6I.js → chunk-OAKOAOEG.js} +67 -67
- package/dist/{chunk-DDBB55L2.js → chunk-OAQO5AZJ.js} +121 -121
- package/dist/{chunk-OPFVXM2D.js → chunk-OX6GY22Q.js} +2 -2
- package/dist/chunk-QCFZDYYG.js +34 -0
- package/dist/chunk-QCFZDYYG.js.map +1 -0
- package/dist/{chunk-MMCO4SRG.js → chunk-QNN4FYBD.js} +19 -11
- package/dist/chunk-QNN4FYBD.js.map +1 -0
- package/dist/{chunk-XWE3EOWX.js → chunk-RZED7FAU.js} +2 -2
- package/dist/{chunk-YSATDEBY.js → chunk-SBRUTD5O.js} +5 -5
- package/dist/{chunk-WVP2I6C5.js → chunk-SDL22RRW.js} +50 -11
- package/dist/chunk-SDL22RRW.js.map +1 -0
- package/dist/{chunk-ZORS5BG5.js → chunk-SQO5SEKA.js} +10 -10
- package/dist/{chunk-PRKNAIGZ.js → chunk-T6T2RMWW.js} +5 -5
- package/dist/{chunk-O5ZINWW3.js → chunk-THFSCUFC.js} +2 -2
- package/dist/{chunk-CIEEYVUB.js → chunk-TPX7XXY4.js} +2 -2
- package/dist/{chunk-OCK4D5PC.js → chunk-TSZP7GGJ.js} +2 -2
- package/dist/{chunk-L334PSLW.js → chunk-U2A56LLF.js} +8 -8
- package/dist/{chunk-TGQW3AMK.js → chunk-UIFWZMO7.js} +23 -23
- package/dist/{chunk-LN4X2NDA.js → chunk-UJ2FP2RV.js} +10 -10
- package/dist/{chunk-4MPAKYZU.js → chunk-VDYRXI3E.js} +5 -5
- package/dist/{chunk-IHCXMYSE.js → chunk-VPEIWMUT.js} +2 -2
- package/dist/{chunk-ELHJK7LM.js → chunk-WBN66HPI.js} +6 -6
- package/dist/{chunk-BJLCV2P3.js → chunk-WGRAB3KB.js} +2 -2
- package/dist/{chunk-IVUVQPTQ.js → chunk-WIW4LOZL.js} +2 -2
- package/dist/{chunk-5KDDAQ6K.js → chunk-WN2DASTS.js} +10 -10
- package/dist/chunk-WZU5AGO6.js +38 -0
- package/dist/chunk-WZU5AGO6.js.map +1 -0
- package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-5Y6VA6X2.js → chunk-XDUP6SWG.js} +2 -2
- package/dist/{chunk-GHHQLSSY.js → chunk-XR4QR3JT.js} +3 -3
- package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
- package/dist/{chunk-QGFR73SN.js → chunk-YIG4HPY4.js} +2 -2
- package/dist/{chunk-UYUFDTIX.js → chunk-YIXWY4OZ.js} +2 -2
- package/dist/{chunk-GJ4HU6LK.js → chunk-YLMTFRZR.js} +2 -2
- package/dist/{chunk-GGD2YDS6.js → chunk-YRMSHA7H.js} +2 -2
- package/dist/{chunk-Y7427SL7.js → chunk-ZG4GNPRE.js} +216 -50
- package/dist/chunk-ZG4GNPRE.js.map +1 -0
- package/dist/{chunk-KZZ5EZ7O.js → chunk-ZS5LREUZ.js} +2 -2
- package/dist/{chunk-JLGBJ4AX.js → chunk-ZYEWTVNQ.js} +10 -10
- package/dist/cli.js +131 -128
- package/dist/coding/export-okf-codegraph.js +14 -14
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +16 -16
- package/dist/config.js +24 -24
- package/dist/connectors/codex-materialize-runner.js +17 -17
- package/dist/connectors/codex-materialize.js +2 -2
- package/dist/connectors/index.js +18 -18
- package/dist/consolidation-provenance-check.js +2 -2
- package/dist/convergence-refresh.js +18 -18
- package/dist/corpus-watermark.js +18 -18
- package/dist/deep-recall-http-glue.js +13 -13
- package/dist/deep-recall.js +2 -2
- package/dist/embedding-fallback.js +2 -2
- package/dist/entity-retrieval.js +18 -18
- package/dist/event-order-recall.js +2 -2
- package/dist/external-wiki-access.js +81 -78
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction.js +30 -30
- package/dist/{graph-edge-decay-OEX5OGMI.js → graph-edge-decay-CXTNJ7IQ.js} +6 -6
- package/dist/graph-edge-decay-CXTNJ7IQ.js.map +1 -0
- package/dist/graph-jsonl.js +4 -4
- package/dist/graph-snapshot.js +4 -4
- package/dist/graph.js +3 -3
- package/dist/harmonic-retrieval.js +15 -15
- package/dist/index.js +279 -276
- package/dist/lcm/archive.d.ts +12 -2
- package/dist/lcm/archive.d.ts.map +1 -1
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.d.ts +59 -8
- package/dist/lcm/engine.d.ts.map +1 -1
- package/dist/lcm/engine.js +6 -6
- package/dist/lcm/index.js +16 -16
- package/dist/lcm/queue.d.ts +7 -3
- package/dist/lcm/queue.d.ts.map +1 -1
- package/dist/lcm/queue.js +1 -1
- package/dist/lcm/summarizer.d.ts +8 -1
- package/dist/lcm/summarizer.d.ts.map +1 -1
- package/dist/lcm/summarizer.js +3 -3
- package/dist/local-llm.js +4 -4
- package/dist/location/index.js +17 -17
- package/dist/maintenance/memory-governance.js +16 -16
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +15 -15
- package/dist/maintenance/rebuild-memory-projection-runner.js +18 -18
- package/dist/maintenance/rebuild-memory-projection.js +17 -17
- package/dist/memory-browse-operations.js +81 -78
- package/dist/memory-browse.js +6 -6
- package/dist/namespaces/migrate.js +16 -16
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.js +15 -15
- package/dist/offline-sync-changeset-generations.js +5 -5
- package/dist/offline-sync-embedding-generation.js +4 -4
- package/dist/offline-sync-snapshot-stream.js +7 -7
- package/dist/offline-sync.js +9 -9
- package/dist/operator-doctor-corpus.js +19 -19
- package/dist/operator-toolkit.js +39 -39
- package/dist/orchestration/buffer-drain-runner.js +194 -191
- package/dist/orchestration/buffer-drain-runner.js.map +1 -1
- package/dist/orchestration/maintenance.js +31 -31
- package/dist/orchestrator.js +195 -192
- package/dist/qmd.js +4 -4
- package/dist/recall-navigation-operations.js +81 -78
- package/dist/recall-navigation.js +80 -77
- package/dist/recall-state-view-admission.js +6 -6
- package/dist/recall-why-service.js +6 -6
- package/dist/resume-bundles.js +27 -27
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +12 -12
- package/dist/semantic-consolidation.js +23 -23
- package/dist/semantic-rule-promotion.js +15 -15
- package/dist/semantic-rule-verifier.js +15 -15
- package/dist/source-agent-qualifier.js +22 -22
- package/dist/standup/build.js +15 -15
- package/dist/storage.js +14 -14
- package/dist/summarizer.js +6 -6
- package/dist/support-passport/index.js +83 -80
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-okf.js +15 -15
- package/dist/verified-recall.js +15 -15
- package/dist/who-knows-cli.js +16 -16
- package/dist/who-knows.js +15 -15
- package/package.json +2 -2
- package/src/access-observe-lcm-archive.ts +30 -0
- package/src/access-observe-lcm-fail-closed.test.ts +118 -0
- package/src/access-observe-objective-state.ts +40 -0
- package/src/access-observe-stage-timing.test.ts +246 -0
- package/src/access-observe-stage-timing.ts +58 -0
- package/src/access-observe-write-surface.ts +45 -45
- package/src/access-service.ts +1 -0
- package/src/lcm/archive.ts +59 -7
- package/src/lcm/engine.ts +256 -53
- package/src/lcm/queue.test.ts +32 -14
- package/src/lcm/queue.ts +24 -16
- package/src/lcm/summarizer.ts +15 -2
- package/src/lcm-durability.test.ts +856 -0
- package/src/lcm-engine.test.ts +12 -3
- package/dist/chunk-6WKWL53V.js.map +0 -1
- package/dist/chunk-KOLJJN5G.js.map +0 -1
- package/dist/chunk-MMCO4SRG.js.map +0 -1
- package/dist/chunk-WVP2I6C5.js.map +0 -1
- package/dist/chunk-Y7427SL7.js.map +0 -1
- /package/dist/{capsule-crypto-GWVG7LGC.js.map → access-observe-lcm-archive.js.map} +0 -0
- /package/dist/{graph-edge-decay-OEX5OGMI.js.map → access-observe-objective-state.js.map} +0 -0
- /package/dist/{chunk-NCZGGGWI.js.map → chunk-2BBMCMJS.js.map} +0 -0
- /package/dist/{chunk-UVVFPCPX.js.map → chunk-2GV24KFD.js.map} +0 -0
- /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-QDZWQBLU.js.map → chunk-2R7OCBRR.js.map} +0 -0
- /package/dist/{chunk-U2CAGMUD.js.map → chunk-2YPIFJHL.js.map} +0 -0
- /package/dist/{chunk-4SOWMA55.js.map → chunk-3AASGFR4.js.map} +0 -0
- /package/dist/{chunk-CI3MAAIY.js.map → chunk-3F7N4I5I.js.map} +0 -0
- /package/dist/{chunk-PLCYKAWH.js.map → chunk-3ICAWZUU.js.map} +0 -0
- /package/dist/{chunk-B4DXREWC.js.map → chunk-3W37NITP.js.map} +0 -0
- /package/dist/{chunk-R4ZHF4K2.js.map → chunk-43FLJLT2.js.map} +0 -0
- /package/dist/{chunk-RLVI2M5Q.js.map → chunk-4BOBW6H4.js.map} +0 -0
- /package/dist/{chunk-HXFIILGF.js.map → chunk-4RASUAN3.js.map} +0 -0
- /package/dist/{chunk-JLMOH5JA.js.map → chunk-5FMGPV34.js.map} +0 -0
- /package/dist/{chunk-EI6M5NVK.js.map → chunk-5JBVPJCT.js.map} +0 -0
- /package/dist/{chunk-74AY3S7U.js.map → chunk-5RW247F5.js.map} +0 -0
- /package/dist/{chunk-P4TV3TQ2.js.map → chunk-5VQSKBNF.js.map} +0 -0
- /package/dist/{chunk-H5DIRCZP.js.map → chunk-64U42JL5.js.map} +0 -0
- /package/dist/{chunk-4XINO77L.js.map → chunk-66JOLMWS.js.map} +0 -0
- /package/dist/{chunk-WHROVQ5Q.js.map → chunk-66PEACZP.js.map} +0 -0
- /package/dist/{chunk-GKL6QYZO.js.map → chunk-6L63IN3M.js.map} +0 -0
- /package/dist/{chunk-Y3XPSGNF.js.map → chunk-6QS5NQJH.js.map} +0 -0
- /package/dist/{chunk-M4IEZGCK.js.map → chunk-BFBS7OFQ.js.map} +0 -0
- /package/dist/{chunk-4PG5R6BJ.js.map → chunk-BNU5TQLR.js.map} +0 -0
- /package/dist/{chunk-B3MWELZ3.js.map → chunk-BW33YTF3.js.map} +0 -0
- /package/dist/{chunk-H6VJSITJ.js.map → chunk-CIL4GHMD.js.map} +0 -0
- /package/dist/{chunk-5UM4H64E.js.map → chunk-D3AGBN6G.js.map} +0 -0
- /package/dist/{chunk-IG3EZ3TF.js.map → chunk-DM3UZUCN.js.map} +0 -0
- /package/dist/{chunk-PMDOGXA2.js.map → chunk-EF3HNVL4.js.map} +0 -0
- /package/dist/{chunk-TVE7W67N.js.map → chunk-EIZ4MK33.js.map} +0 -0
- /package/dist/{chunk-AUFPPBC3.js.map → chunk-ENPJX3BB.js.map} +0 -0
- /package/dist/{chunk-63A2BU2B.js.map → chunk-F4ZHKDBW.js.map} +0 -0
- /package/dist/{chunk-3LBDEGQA.js.map → chunk-FDUCIUQ5.js.map} +0 -0
- /package/dist/{chunk-CTH2CQCB.js.map → chunk-FM6UKZAE.js.map} +0 -0
- /package/dist/{chunk-XVZA6VXC.js.map → chunk-FZVROZKC.js.map} +0 -0
- /package/dist/{chunk-IAGVM7EY.js.map → chunk-GMSRLDVR.js.map} +0 -0
- /package/dist/{chunk-HPB5TF55.js.map → chunk-GT4G2S2Y.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
- /package/dist/{chunk-5MGKGEL7.js.map → chunk-HNF7IMYQ.js.map} +0 -0
- /package/dist/{chunk-W7K5BP3M.js.map → chunk-HQPFACXO.js.map} +0 -0
- /package/dist/{chunk-TECK5IVY.js.map → chunk-HZNURRDS.js.map} +0 -0
- /package/dist/{chunk-SXTVZ6JL.js.map → chunk-I6HRHJRA.js.map} +0 -0
- /package/dist/{chunk-NHWDTOBN.js.map → chunk-ILD7N3XC.js.map} +0 -0
- /package/dist/{chunk-4NV7O76Q.js.map → chunk-J5TVHCDG.js.map} +0 -0
- /package/dist/{chunk-AL3TCNSJ.js.map → chunk-J6GZ2254.js.map} +0 -0
- /package/dist/{chunk-OH55F76C.js.map → chunk-JEX2JL3U.js.map} +0 -0
- /package/dist/{chunk-AVGDYYMA.js.map → chunk-JGQEF33K.js.map} +0 -0
- /package/dist/{chunk-6P7U3HWG.js.map → chunk-K36RMINQ.js.map} +0 -0
- /package/dist/{chunk-UW6LBFYN.js.map → chunk-K47G2CBP.js.map} +0 -0
- /package/dist/{chunk-3RHU5MD7.js.map → chunk-K7LLGCPH.js.map} +0 -0
- /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
- /package/dist/{chunk-R2BIVU2Z.js.map → chunk-LTST4R7U.js.map} +0 -0
- /package/dist/{chunk-IZBIDPMY.js.map → chunk-LYTKNK5O.js.map} +0 -0
- /package/dist/{chunk-HH73M65H.js.map → chunk-MCFI6G7U.js.map} +0 -0
- /package/dist/{chunk-V6SBA5O7.js.map → chunk-MFKBYXBY.js.map} +0 -0
- /package/dist/{chunk-AYJQ644F.js.map → chunk-MK7HXGM6.js.map} +0 -0
- /package/dist/{chunk-MOIJWWNA.js.map → chunk-MKMIY3E7.js.map} +0 -0
- /package/dist/{chunk-EXQJMFRF.js.map → chunk-MRN2MKB7.js.map} +0 -0
- /package/dist/{chunk-W26D2USW.js.map → chunk-MUH7RR6T.js.map} +0 -0
- /package/dist/{chunk-64JLBFCQ.js.map → chunk-N7NAGECH.js.map} +0 -0
- /package/dist/{chunk-75DCOS6I.js.map → chunk-OAKOAOEG.js.map} +0 -0
- /package/dist/{chunk-DDBB55L2.js.map → chunk-OAQO5AZJ.js.map} +0 -0
- /package/dist/{chunk-OPFVXM2D.js.map → chunk-OX6GY22Q.js.map} +0 -0
- /package/dist/{chunk-XWE3EOWX.js.map → chunk-RZED7FAU.js.map} +0 -0
- /package/dist/{chunk-YSATDEBY.js.map → chunk-SBRUTD5O.js.map} +0 -0
- /package/dist/{chunk-ZORS5BG5.js.map → chunk-SQO5SEKA.js.map} +0 -0
- /package/dist/{chunk-PRKNAIGZ.js.map → chunk-T6T2RMWW.js.map} +0 -0
- /package/dist/{chunk-O5ZINWW3.js.map → chunk-THFSCUFC.js.map} +0 -0
- /package/dist/{chunk-CIEEYVUB.js.map → chunk-TPX7XXY4.js.map} +0 -0
- /package/dist/{chunk-OCK4D5PC.js.map → chunk-TSZP7GGJ.js.map} +0 -0
- /package/dist/{chunk-L334PSLW.js.map → chunk-U2A56LLF.js.map} +0 -0
- /package/dist/{chunk-TGQW3AMK.js.map → chunk-UIFWZMO7.js.map} +0 -0
- /package/dist/{chunk-LN4X2NDA.js.map → chunk-UJ2FP2RV.js.map} +0 -0
- /package/dist/{chunk-4MPAKYZU.js.map → chunk-VDYRXI3E.js.map} +0 -0
- /package/dist/{chunk-IHCXMYSE.js.map → chunk-VPEIWMUT.js.map} +0 -0
- /package/dist/{chunk-ELHJK7LM.js.map → chunk-WBN66HPI.js.map} +0 -0
- /package/dist/{chunk-BJLCV2P3.js.map → chunk-WGRAB3KB.js.map} +0 -0
- /package/dist/{chunk-IVUVQPTQ.js.map → chunk-WIW4LOZL.js.map} +0 -0
- /package/dist/{chunk-5KDDAQ6K.js.map → chunk-WN2DASTS.js.map} +0 -0
- /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-5Y6VA6X2.js.map → chunk-XDUP6SWG.js.map} +0 -0
- /package/dist/{chunk-GHHQLSSY.js.map → chunk-XR4QR3JT.js.map} +0 -0
- /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
- /package/dist/{chunk-QGFR73SN.js.map → chunk-YIG4HPY4.js.map} +0 -0
- /package/dist/{chunk-UYUFDTIX.js.map → chunk-YIXWY4OZ.js.map} +0 -0
- /package/dist/{chunk-GJ4HU6LK.js.map → chunk-YLMTFRZR.js.map} +0 -0
- /package/dist/{chunk-GGD2YDS6.js.map → chunk-YRMSHA7H.js.map} +0 -0
- /package/dist/{chunk-KZZ5EZ7O.js.map → chunk-ZS5LREUZ.js.map} +0 -0
- /package/dist/{chunk-JLGBJ4AX.js.map → chunk-ZYEWTVNQ.js.map} +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Noncontent stage timing for slow/failed observe requests (25s incident).
|
|
3
|
+
* A failed request must record which stage blocked it (phase attribution) in
|
|
4
|
+
* the `access-observe-failed` log line without leaking message content,
|
|
5
|
+
* session keys, namespaces or principals, and must still throw unchanged.
|
|
6
|
+
*/
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import test from "node:test";
|
|
12
|
+
|
|
13
|
+
import { EngramAccessService, NamespaceNotWritableError } from "./access-service.js";
|
|
14
|
+
import { initLogger, resetLogger, type LoggerBackend } from "./logger.js";
|
|
15
|
+
import { Orchestrator } from "./orchestrator.js";
|
|
16
|
+
import { parseConfig } from "./config.js";
|
|
17
|
+
import type { EngramAccessObserveRequest } from "./access-service.js";
|
|
18
|
+
import type { CodingContext, PluginConfig } from "./types.js";
|
|
19
|
+
|
|
20
|
+
interface OrchestratorInternals {
|
|
21
|
+
config: PluginConfig;
|
|
22
|
+
_codingContextBySession: Map<string, CodingContext>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function makeService(memoryDir: string): EngramAccessService {
|
|
26
|
+
// SAFETY: the probe drives only the observe scope path on this orchestrator.
|
|
27
|
+
const orch = Object.create(Orchestrator.prototype) as Orchestrator;
|
|
28
|
+
// SAFETY: the probe sets exactly these two internal fields.
|
|
29
|
+
const internals: OrchestratorInternals = {
|
|
30
|
+
config: parseConfig({
|
|
31
|
+
namespacesEnabled: true,
|
|
32
|
+
defaultNamespace: "default",
|
|
33
|
+
sharedNamespace: "shared",
|
|
34
|
+
namespacePolicies: [],
|
|
35
|
+
codingMode: { projectScope: true },
|
|
36
|
+
transcriptEnabled: false,
|
|
37
|
+
memoryDir,
|
|
38
|
+
}),
|
|
39
|
+
_codingContextBySession: new Map(),
|
|
40
|
+
};
|
|
41
|
+
Object.assign(orch, internals);
|
|
42
|
+
return new EngramAccessService(orch);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test("a rejected observe logs phase attribution with stage durations and still throws", async () => {
|
|
46
|
+
const dir = await mkdtemp(path.join(tmpdir(), "remnic-observe-timing-"));
|
|
47
|
+
const lines: string[] = [];
|
|
48
|
+
const backend: LoggerBackend = {
|
|
49
|
+
info(msg) {
|
|
50
|
+
lines.push(msg);
|
|
51
|
+
},
|
|
52
|
+
warn(msg) {
|
|
53
|
+
lines.push(msg);
|
|
54
|
+
},
|
|
55
|
+
error(msg) {
|
|
56
|
+
lines.push(msg);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
initLogger(backend, false, { timestamps: false });
|
|
60
|
+
try {
|
|
61
|
+
const service = makeService(dir);
|
|
62
|
+
// SAFETY: the literal sets the request fields the scope path reads.
|
|
63
|
+
const rejectedRequest = {
|
|
64
|
+
sessionKey: "sess-timing-1",
|
|
65
|
+
authenticatedPrincipal: "alice",
|
|
66
|
+
namespace: "victim-secret",
|
|
67
|
+
messages: [
|
|
68
|
+
{ role: "user", content: "first secret message body" },
|
|
69
|
+
{ role: "assistant", content: "second secret message body" },
|
|
70
|
+
],
|
|
71
|
+
} as EngramAccessObserveRequest;
|
|
72
|
+
await assert.rejects(
|
|
73
|
+
service.observe(rejectedRequest),
|
|
74
|
+
NamespaceNotWritableError
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const failed = lines.filter((l) => l.includes("access-observe-failed"));
|
|
78
|
+
assert.equal(failed.length, 1, `expected exactly one access-observe-failed line, got: ${failed}`);
|
|
79
|
+
const line = failed[0] ?? "";
|
|
80
|
+
// Phase attribution: the blocked stage (scope) plus total and message count.
|
|
81
|
+
assert.match(line, /totalMs=\d+/);
|
|
82
|
+
assert.match(line, /messages=2/);
|
|
83
|
+
assert.match(line, /scopeMs=\d+/);
|
|
84
|
+
// Noncontent: no message bodies, session key, namespace or principal.
|
|
85
|
+
assert.ok(!line.includes("secret"), `leaked content in: ${line}`);
|
|
86
|
+
assert.ok(!line.includes("sess-timing-1"), `leaked session key in: ${line}`);
|
|
87
|
+
assert.ok(!line.includes("victim-secret"), `leaked namespace in: ${line}`);
|
|
88
|
+
assert.ok(!line.includes("alice"), `leaked principal in: ${line}`);
|
|
89
|
+
} finally {
|
|
90
|
+
resetLogger();
|
|
91
|
+
await rm(dir, { recursive: true, force: true });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Plain-object orchestrator for full observe success/failure paths (mirrors
|
|
96
|
+
// the access-service-observe-lcm-parity harness).
|
|
97
|
+
function makeProbe(options: {
|
|
98
|
+
enqueueObserveMessages: (sessionKey: string, messages: unknown[]) => Promise<void> | void;
|
|
99
|
+
}) {
|
|
100
|
+
const config = parseConfig({
|
|
101
|
+
namespacesEnabled: true,
|
|
102
|
+
defaultNamespace: "default",
|
|
103
|
+
sharedNamespace: "shared",
|
|
104
|
+
namespacePolicies: [],
|
|
105
|
+
defaultRecallNamespaces: ["self", "shared"],
|
|
106
|
+
codingMode: { projectScope: true },
|
|
107
|
+
transcriptEnabled: false,
|
|
108
|
+
memoryDir: "/synthetic/remnic-observe-timing",
|
|
109
|
+
objectiveStateMemoryEnabled: false,
|
|
110
|
+
objectiveStateSnapshotWritesEnabled: false,
|
|
111
|
+
principalFromSessionKeyMode: "prefix",
|
|
112
|
+
principalFromSessionKeyRules: [],
|
|
113
|
+
recallCrossNamespaceBudgetEnabled: false,
|
|
114
|
+
recallCrossNamespaceBudgetWindowMs: 60_000,
|
|
115
|
+
recallCrossNamespaceBudgetSoftLimit: 10,
|
|
116
|
+
recallCrossNamespaceBudgetHardLimit: 30,
|
|
117
|
+
});
|
|
118
|
+
const contexts = new Map<string, CodingContext>();
|
|
119
|
+
const extractionCalls: string[][] = [];
|
|
120
|
+
// SAFETY: the probe exercises the observe surface; the rest of Orchestrator
|
|
121
|
+
// is unused on this path.
|
|
122
|
+
const orch = {
|
|
123
|
+
config,
|
|
124
|
+
getCodingContextForSession: (sk: string | undefined) => (sk ? contexts.get(sk) : null) ?? null,
|
|
125
|
+
setCodingContextForSession: (sk: string, ctx: CodingContext | null) => {
|
|
126
|
+
if (ctx === null) contexts.delete(sk);
|
|
127
|
+
else contexts.set(sk, ctx);
|
|
128
|
+
},
|
|
129
|
+
applyCodingNamespaceOverlay: (sk: string | undefined, base: string) =>
|
|
130
|
+
Orchestrator.prototype.applyCodingNamespaceOverlay.call(orch, sk, base),
|
|
131
|
+
resolvePrincipal: (sk?: string) => Orchestrator.prototype.resolvePrincipal.call(orch, sk),
|
|
132
|
+
resolveSelfNamespace: (sk?: string) => Orchestrator.prototype.resolveSelfNamespace.call(orch, sk),
|
|
133
|
+
lcmEngine: {
|
|
134
|
+
enabled: true,
|
|
135
|
+
enqueueObserveMessages: options.enqueueObserveMessages,
|
|
136
|
+
},
|
|
137
|
+
ingestReplayBatch: async (turns: Array<{ sessionKey: string }>) => {
|
|
138
|
+
extractionCalls.push(turns.map((t) => t.sessionKey));
|
|
139
|
+
},
|
|
140
|
+
} as any;
|
|
141
|
+
return { orch, extractionCalls };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
test("a slow successful observe logs access-observe-slow with per-stage durations", async () => {
|
|
145
|
+
const lines: string[] = [];
|
|
146
|
+
const backend: LoggerBackend = {
|
|
147
|
+
info(msg) {
|
|
148
|
+
lines.push(msg);
|
|
149
|
+
},
|
|
150
|
+
warn(msg) {
|
|
151
|
+
lines.push(msg);
|
|
152
|
+
},
|
|
153
|
+
error(msg) {
|
|
154
|
+
lines.push(msg);
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
initLogger(backend, false, { timestamps: false });
|
|
158
|
+
// Deterministic monotonic clock: every read advances 6s, so each timed
|
|
159
|
+
// stage records exactly 6000ms and the total exceeds the slow threshold.
|
|
160
|
+
let now = 0;
|
|
161
|
+
const realPerformanceNow = performance.now;
|
|
162
|
+
performance.now = () => (now += 6000);
|
|
163
|
+
try {
|
|
164
|
+
const { orch } = makeProbe({
|
|
165
|
+
enqueueObserveMessages: (_sessionKey) => {},
|
|
166
|
+
});
|
|
167
|
+
const service = new EngramAccessService(orch);
|
|
168
|
+
// SAFETY: the literal sets the request fields the observe path reads.
|
|
169
|
+
const slowRequest = {
|
|
170
|
+
sessionKey: "alice:sess-slow",
|
|
171
|
+
authenticatedPrincipal: "alice",
|
|
172
|
+
messages: [
|
|
173
|
+
{ role: "user", content: "slow path message body" },
|
|
174
|
+
{ role: "assistant", content: "reply body" },
|
|
175
|
+
],
|
|
176
|
+
} as EngramAccessObserveRequest;
|
|
177
|
+
const res = await service.observe(slowRequest);
|
|
178
|
+
assert.equal(res.accepted, 2);
|
|
179
|
+
|
|
180
|
+
const slow = lines.filter((l) => l.includes("access-observe-slow"));
|
|
181
|
+
assert.equal(slow.length, 1, `expected one access-observe-slow line, got: ${lines}`);
|
|
182
|
+
const line = slow[0] ?? "";
|
|
183
|
+
assert.match(line, /totalMs=(\d+)/);
|
|
184
|
+
const totalMs = Number(/totalMs=(\d+)/.exec(line)?.[1]);
|
|
185
|
+
assert.ok(totalMs >= 10_000, `slow line must exceed threshold, got totalMs=${totalMs}`);
|
|
186
|
+
assert.match(line, /messages=2/);
|
|
187
|
+
// Real stages are attributed, including the durable-buffer terminal stage.
|
|
188
|
+
assert.match(line, /scopeMs=6000/);
|
|
189
|
+
assert.match(line, /durable-bufferMs=6000/);
|
|
190
|
+
assert.ok(!line.includes("sess-slow") && !line.includes("alice") && !line.includes("body"));
|
|
191
|
+
} finally {
|
|
192
|
+
performance.now = realPerformanceNow;
|
|
193
|
+
resetLogger();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("a failed awaited LCM archival records the lcm-accept phase and still throws", async () => {
|
|
198
|
+
const lines: string[] = [];
|
|
199
|
+
const backend: LoggerBackend = {
|
|
200
|
+
info(msg) {
|
|
201
|
+
lines.push(msg);
|
|
202
|
+
},
|
|
203
|
+
warn(msg) {
|
|
204
|
+
lines.push(msg);
|
|
205
|
+
},
|
|
206
|
+
error(msg) {
|
|
207
|
+
lines.push(msg);
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
initLogger(backend, false, { timestamps: false });
|
|
211
|
+
try {
|
|
212
|
+
const { orch } = makeProbe({
|
|
213
|
+
enqueueObserveMessages: async () => {
|
|
214
|
+
throw new Error("lcm archival append failed");
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
const service = new EngramAccessService(orch);
|
|
218
|
+
// SAFETY: the literal sets the request fields the observe path reads.
|
|
219
|
+
const failedLcmRequest = {
|
|
220
|
+
sessionKey: "alice:sess-lcmfail",
|
|
221
|
+
authenticatedPrincipal: "alice",
|
|
222
|
+
messages: [
|
|
223
|
+
{ role: "user", content: "lcm failure message body" },
|
|
224
|
+
{ role: "assistant", content: "reply body" },
|
|
225
|
+
],
|
|
226
|
+
} as EngramAccessObserveRequest;
|
|
227
|
+
await assert.rejects(
|
|
228
|
+
service.observe(failedLcmRequest),
|
|
229
|
+
/lcm archival append failed/
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const failed = lines.filter((l) => l.includes("access-observe-failed"));
|
|
233
|
+
assert.equal(failed.length, 1, `expected exactly one access-observe-failed line, got: ${failed}`);
|
|
234
|
+
const line = failed[0] ?? "";
|
|
235
|
+
assert.match(line, /totalMs=\d+/);
|
|
236
|
+
assert.match(line, /messages=2/);
|
|
237
|
+
// The rejected LCM accept is attributed with its duration, alongside earlier stages.
|
|
238
|
+
assert.match(line, /scopeMs=\d+/);
|
|
239
|
+
assert.match(line, /lcm-acceptMs=\d+/);
|
|
240
|
+
assert.ok(!line.includes("sess-lcmfail"), `leaked session key in: ${line}`);
|
|
241
|
+
assert.ok(!line.includes("alice"), `leaked principal in: ${line}`);
|
|
242
|
+
assert.ok(!line.includes("body"), `leaked content in: ${line}`);
|
|
243
|
+
} finally {
|
|
244
|
+
resetLogger();
|
|
245
|
+
}
|
|
246
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Noncontent stage timing for the observe surface.
|
|
3
|
+
*
|
|
4
|
+
* Durations only — never message content, session keys, namespaces or
|
|
5
|
+
* principals. A monotonic clock (`performance.now`) keeps phase durations
|
|
6
|
+
* sane under wall-clock corrections.
|
|
7
|
+
*
|
|
8
|
+
* A slow observe logs `access-observe-slow`; a failed observe always logs
|
|
9
|
+
* `access-observe-failed`, so a 25s-class stall attributes to a phase
|
|
10
|
+
* instead of a guess.
|
|
11
|
+
*/
|
|
12
|
+
import { log } from "./logger.js";
|
|
13
|
+
|
|
14
|
+
/** Observes slower than this log `access-observe-slow` with per-stage durations. */
|
|
15
|
+
const OBSERVE_SLOW_LOG_MS = 10_000;
|
|
16
|
+
|
|
17
|
+
export interface ObserveStageTimer {
|
|
18
|
+
/** Run one stage, recording its duration even when the stage rejects. */
|
|
19
|
+
timed<T>(stage: string, fn: () => Promise<T>): Promise<T>;
|
|
20
|
+
/** Log a slow successful observe with per-stage durations. */
|
|
21
|
+
logSlow(messages: number): void;
|
|
22
|
+
/** Log a failed observe with per-stage durations. */
|
|
23
|
+
logFailed(messages: number): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Timer for one observe request. */
|
|
27
|
+
export function createObserveStageTimer(): ObserveStageTimer {
|
|
28
|
+
const startedAtMs = performance.now();
|
|
29
|
+
const stageMs: Record<string, number> = {};
|
|
30
|
+
|
|
31
|
+
const formatStages = (): string =>
|
|
32
|
+
Object.entries(stageMs)
|
|
33
|
+
.map(([stage, ms]) => `${stage}Ms=${Math.round(ms)}`)
|
|
34
|
+
.join(" ");
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
async timed<T>(stage: string, fn: () => Promise<T>): Promise<T> {
|
|
38
|
+
const stageStartMs = performance.now();
|
|
39
|
+
try {
|
|
40
|
+
return await fn();
|
|
41
|
+
} finally {
|
|
42
|
+
stageMs[stage] = (stageMs[stage] ?? 0) + (performance.now() - stageStartMs);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
logSlow(messages: number): void {
|
|
46
|
+
const totalMs = Math.round(performance.now() - startedAtMs);
|
|
47
|
+
if (totalMs < OBSERVE_SLOW_LOG_MS) return;
|
|
48
|
+
log.warn(
|
|
49
|
+
`access-observe-slow totalMs=${totalMs} messages=${messages} ${formatStages()}`,
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
logFailed(messages: number): void {
|
|
53
|
+
log.warn(
|
|
54
|
+
`access-observe-failed totalMs=${Math.round(performance.now() - startedAtMs)} messages=${messages} ${formatStages()}`,
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -34,6 +34,9 @@ import { abortError } from "./abort-error.js";
|
|
|
34
34
|
import { withBriefingDeadline } from "./briefing-deadline.js";
|
|
35
35
|
import { extractionForceFlush } from "./access-extraction-force-flush.js";
|
|
36
36
|
import { ObserveTranscriptPersister, observeTranscriptSessionKey } from "./access-observe-transcript.js";
|
|
37
|
+
import { createObserveStageTimer } from "./access-observe-stage-timing.js";
|
|
38
|
+
import { archiveObservedTurns } from "./access-observe-lcm-archive.js";
|
|
39
|
+
import { recordObserveObjectiveState } from "./access-observe-objective-state.js";
|
|
37
40
|
import { FileCalendarSource, buildBriefing, parseBriefingFocus, parseBriefingWindow } from "./briefing.js";
|
|
38
41
|
import {
|
|
39
42
|
resolveCompressionCapabilities,
|
|
@@ -708,10 +711,16 @@ export class AccessObserveWriteSurface {
|
|
|
708
711
|
request.sessionKey,
|
|
709
712
|
pendingObserveScopeHint(request)
|
|
710
713
|
);
|
|
714
|
+
// Noncontent stage timing for slow/failed observes: durations only, logged
|
|
715
|
+
// in the `access-observe-failed` line (always) and `access-observe-slow`
|
|
716
|
+
// (successes at or above the slow threshold). Never message content,
|
|
717
|
+
// session keys, namespaces or principals.
|
|
718
|
+
const stageTimer = createObserveStageTimer();
|
|
719
|
+
const timed = stageTimer.timed.bind(stageTimer);
|
|
711
720
|
try {
|
|
712
721
|
let scope: MemoryScopePlan;
|
|
713
722
|
try {
|
|
714
|
-
scope = await this.deps.resolveMemoryScopePlan(request);
|
|
723
|
+
scope = await timed("scope", () => this.deps.resolveMemoryScopePlan(request));
|
|
715
724
|
} catch (err) {
|
|
716
725
|
// A replay re-submit sets suppressQuarantine so a still-unwritable target
|
|
717
726
|
// propagates instead of re-parking (#1888); observe has no dryRun path.
|
|
@@ -749,10 +758,12 @@ export class AccessObserveWriteSurface {
|
|
|
749
758
|
// on the same session is project-scoped (rule 42: same namespace layer as
|
|
750
759
|
// recall). Done AFTER the scope plan authorized the write, so a rejected
|
|
751
760
|
// request never leaves orphaned context on the session.
|
|
752
|
-
await
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
761
|
+
await timed("context", () =>
|
|
762
|
+
this.deps.maybeAttachCodingContext(request.sessionKey, {
|
|
763
|
+
cwd: request.cwd,
|
|
764
|
+
projectTag: request.projectTag,
|
|
765
|
+
})
|
|
766
|
+
);
|
|
756
767
|
|
|
757
768
|
// Prefix sessionKey with the EFFECTIVE write namespace for LCM archival so
|
|
758
769
|
// observed turns are scoped to the same namespace project-scoped recall
|
|
@@ -771,51 +782,35 @@ export class AccessObserveWriteSurface {
|
|
|
771
782
|
// (#928 contract, already auth-checked inside the scope plan), not the
|
|
772
783
|
// general default-store write namespace.
|
|
773
784
|
if (shouldWriteObjectiveState) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
.objectiveStateMemory,
|
|
783
|
-
objectiveStateSnapshotWritesEnabled: resolveObjectiveStateCapabilities(this.deps.orchestrator.config)
|
|
784
|
-
.objectiveStateSnapshotWrites,
|
|
785
|
-
sessionKey: request.sessionKey,
|
|
786
|
-
recordedAt: new Date().toISOString(),
|
|
787
|
-
messages: request.messages,
|
|
788
|
-
});
|
|
789
|
-
} catch (err) {
|
|
790
|
-
log.error(`access-observe objective-state snapshot write failed: ${err}`);
|
|
791
|
-
}
|
|
785
|
+
await timed("objective", () =>
|
|
786
|
+
recordObserveObjectiveState(
|
|
787
|
+
this.deps.orchestrator,
|
|
788
|
+
this.deps.objectiveStateStoreLocationForNamespace,
|
|
789
|
+
scope,
|
|
790
|
+
request,
|
|
791
|
+
),
|
|
792
|
+
);
|
|
792
793
|
}
|
|
793
794
|
|
|
794
795
|
// 5. LCM archival → effective write namespace.
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
// block the HTTP response — the caller only needs acknowledgment.
|
|
802
|
-
try {
|
|
803
|
-
this.deps.orchestrator.lcmEngine.enqueueObserveMessages(lcmSessionKey, request.messages);
|
|
804
|
-
lcmArchived = true;
|
|
805
|
-
} catch (err) {
|
|
806
|
-
log.error(`access-observe LCM enqueue failed: ${err}`);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
796
|
+
// The archival append is durable before this returns: the queue holds
|
|
797
|
+
// only a session reference, and summary DAGs are built in the
|
|
798
|
+
// background, so the caller is never blocked on inference.
|
|
799
|
+
const lcmArchived = await timed("lcm-accept", () =>
|
|
800
|
+
archiveObservedTurns(this.deps.orchestrator.lcmEngine, lcmSessionKey, request.messages),
|
|
801
|
+
);
|
|
809
802
|
|
|
810
803
|
// 5b. Transcript persistence → the per-session transcript store the
|
|
811
|
-
// hourly summarizer reads (issue #2783). Best-effort
|
|
812
|
-
//
|
|
804
|
+
// hourly summarizer reads (issue #2783). Best-effort: a transcript
|
|
805
|
+
// failure must not fail the observe.
|
|
813
806
|
const transcriptPersisted = resolvePresentationCapabilities(this.deps.orchestrator.config).transcript
|
|
814
|
-
? await
|
|
815
|
-
this.
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
807
|
+
? await timed("transcript", () =>
|
|
808
|
+
this.observeTranscriptPersister.persist(
|
|
809
|
+
this.deps.orchestrator,
|
|
810
|
+
observeTranscriptSessionKey(request.sessionKey, writeNamespace, this.deps.orchestrator.config),
|
|
811
|
+
request.messages,
|
|
812
|
+
request.sessionKey
|
|
813
|
+
)
|
|
819
814
|
)
|
|
820
815
|
: false;
|
|
821
816
|
|
|
@@ -895,10 +890,11 @@ export class AccessObserveWriteSurface {
|
|
|
895
890
|
extractionQueued = true;
|
|
896
891
|
};
|
|
897
892
|
}
|
|
898
|
-
await this.deps.orchestrator.ingestReplayBatch(turns, ingestOptions);
|
|
893
|
+
await timed("durable-buffer", () => this.deps.orchestrator.ingestReplayBatch(turns, ingestOptions));
|
|
899
894
|
// A native trigger may leave the durable capture pending rather than queued.
|
|
900
895
|
}
|
|
901
896
|
|
|
897
|
+
stageTimer.logSlow(request.messages.length);
|
|
902
898
|
log.info(
|
|
903
899
|
`access-observe namespace=${namespace} effectiveNamespace=${writeNamespace} sessionKey=${request.sessionKey} messages=${request.messages.length} lcm=${lcmArchived} extraction=${extractionQueued} transcript=${transcriptPersisted}`
|
|
904
900
|
);
|
|
@@ -924,6 +920,10 @@ export class AccessObserveWriteSurface {
|
|
|
924
920
|
extractionQueued,
|
|
925
921
|
transcriptPersisted,
|
|
926
922
|
};
|
|
923
|
+
} catch (err) {
|
|
924
|
+
// Failed requests always get phase attribution (noncontent), regardless of duration.
|
|
925
|
+
stageTimer.logFailed(request.messages.length);
|
|
926
|
+
throw err;
|
|
927
927
|
} finally {
|
|
928
928
|
observePreparation.release();
|
|
929
929
|
}
|
package/src/access-service.ts
CHANGED
|
@@ -1079,6 +1079,7 @@ export interface EngramAccessObserveResponse {
|
|
|
1079
1079
|
effectiveNamespace: string;
|
|
1080
1080
|
/** Additive diagnostic view of the resolved scope plan (#1495). */
|
|
1081
1081
|
scopeDebug?: EngramAccessScopeDebug;
|
|
1082
|
+
/** True when the observed turns are durably archived in LCM — the archive append completed before the response was produced. */
|
|
1082
1083
|
lcmArchived: boolean;
|
|
1083
1084
|
extractionQueued: boolean;
|
|
1084
1085
|
/** True when at least one observe-derived turn was appended to the transcript store (issue #2783). False when the transcript capability is off, every message was filtered/deduped, or every append failed. */
|
package/src/lcm/archive.ts
CHANGED
|
@@ -188,20 +188,72 @@ export class LcmArchive {
|
|
|
188
188
|
.all(sessionId, fromTurn, toTurn) as LcmMessage[];
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
/**
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
/** Highest turn index covered by a leaf summary (-1 when none exist). */
|
|
192
|
+
private lastLeafSummaryEnd(sessionId: string): number {
|
|
193
|
+
// SAFETY: the query returns exactly one aggregate column, `last_end`.
|
|
194
|
+
const row = this.db
|
|
194
195
|
.prepare(
|
|
195
196
|
"SELECT MAX(msg_end) as last_end FROM lcm_summary_nodes WHERE session_id = ? AND depth = 0",
|
|
196
197
|
)
|
|
197
198
|
.get(sessionId) as { last_end: number | null } | undefined;
|
|
199
|
+
return row?.last_end ?? -1;
|
|
200
|
+
}
|
|
198
201
|
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
/** Retrieve unsummarized messages (after last leaf summary), optionally bounded. */
|
|
203
|
+
getUnsummarizedMessages(sessionId: string, limit?: number): LcmMessage[] {
|
|
204
|
+
const lastSummarized = this.lastLeafSummaryEnd(sessionId);
|
|
205
|
+
if (limit !== undefined && limit <= 0) return [];
|
|
206
|
+
|
|
207
|
+
const stmt = this.db.prepare(
|
|
208
|
+
limit === undefined
|
|
209
|
+
? "SELECT * FROM lcm_messages WHERE session_id = ? AND turn_index > ? ORDER BY turn_index"
|
|
210
|
+
: "SELECT * FROM lcm_messages WHERE session_id = ? AND turn_index > ? ORDER BY turn_index LIMIT ?",
|
|
211
|
+
);
|
|
212
|
+
// SAFETY: the statement selects the lcm_messages row shape.
|
|
213
|
+
const rows = (
|
|
214
|
+
limit === undefined ? stmt.all(sessionId, lastSummarized) : stmt.all(sessionId, lastSummarized, limit)
|
|
215
|
+
) as LcmMessage[];
|
|
216
|
+
return rows;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Count archived turns that no leaf summary covers yet. */
|
|
220
|
+
countUnsummarizedMessages(sessionId: string): number {
|
|
221
|
+
const lastSummarized = this.lastLeafSummaryEnd(sessionId);
|
|
222
|
+
// SAFETY: the query returns exactly one aggregate column, `n`.
|
|
223
|
+
const row = this.db
|
|
224
|
+
.prepare(
|
|
225
|
+
"SELECT COUNT(*) as n FROM lcm_messages WHERE session_id = ? AND turn_index > ?",
|
|
226
|
+
)
|
|
227
|
+
.get(sessionId, lastSummarized) as { n: number };
|
|
228
|
+
return row.n;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Sessions whose durable archive still holds at least `minTurns`
|
|
233
|
+
* unsummarized turns. Metadata only — used to resume outstanding summary
|
|
234
|
+
* work after a restart.
|
|
235
|
+
*/
|
|
236
|
+
listSessionsWithUnsummarizedTurns(minTurns: number): string[] {
|
|
237
|
+
if (minTurns <= 0) return [];
|
|
238
|
+
// SAFETY: the query returns one `session_id` column per session.
|
|
239
|
+
const rows = this.db
|
|
201
240
|
.prepare(
|
|
202
|
-
|
|
241
|
+
`
|
|
242
|
+
SELECT m.session_id AS session_id
|
|
243
|
+
FROM lcm_messages m
|
|
244
|
+
LEFT JOIN (
|
|
245
|
+
SELECT session_id, MAX(msg_end) AS last_end
|
|
246
|
+
FROM lcm_summary_nodes
|
|
247
|
+
WHERE depth = 0
|
|
248
|
+
GROUP BY session_id
|
|
249
|
+
) leaves ON leaves.session_id = m.session_id
|
|
250
|
+
WHERE m.turn_index > COALESCE(leaves.last_end, -1)
|
|
251
|
+
GROUP BY m.session_id
|
|
252
|
+
HAVING COUNT(*) >= ?
|
|
253
|
+
`,
|
|
203
254
|
)
|
|
204
|
-
.all(
|
|
255
|
+
.all(minTurns) as Array<{ session_id: string }>;
|
|
256
|
+
return rows.map((row) => row.session_id);
|
|
205
257
|
}
|
|
206
258
|
|
|
207
259
|
/** Full-text search across all messages. */
|