@remnic/core 9.3.754 → 9.3.756
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +5 -5
- package/dist/access-admin-ops-surface.js +34 -34
- package/dist/access-boundary.d.ts +5 -5
- package/dist/access-boundary.js +35 -35
- package/dist/access-cli.js +83 -83
- package/dist/access-http.d.ts +5 -5
- package/dist/access-http.js +44 -44
- package/dist/access-lcm-surface.d.ts +5 -5
- package/dist/access-lcm-surface.js +34 -34
- package/dist/access-mcp.d.ts +5 -5
- package/dist/access-mcp.js +41 -41
- package/dist/access-observe-write-surface.d.ts +225 -0
- package/dist/access-observe-write-surface.js +128 -0
- package/dist/access-operations-batch.js +38 -38
- package/dist/access-operations.d.ts +5 -5
- package/dist/access-operations.js +40 -40
- package/dist/access-recall-surface.d.ts +5 -5
- package/dist/access-recall-surface.js +34 -34
- package/dist/access-schema.d.ts +10 -10
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DKRV1zxh.d.ts → access-service-CJwcDoQp.d.ts} +7 -8
- package/dist/access-service.d.ts +5 -5
- package/dist/access-service.js +34 -34
- package/dist/access-surface-catalog.d.ts +5 -5
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-G4YKBPOU.js → auto-sync-RZO5ZPHR.js} +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +4 -4
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +7 -7
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-FFWJ4X4A.js} +2 -2
- package/dist/{catalog-BEmNVRN0.d.ts → catalog-DaKAFbSJ.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +15 -15
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-M5QKGHCR.js → chunk-3I2IASNK.js} +2 -2
- package/dist/{chunk-JBO6UBIE.js → chunk-4AC57RCM.js} +4 -4
- package/dist/{chunk-3DOD6ICJ.js → chunk-4I45UZ4V.js} +5 -5
- package/dist/{chunk-TAPSNIMA.js → chunk-5CEJH5ZN.js} +4 -4
- package/dist/{chunk-CXCZ4LAI.js → chunk-5VWRYFQE.js} +7 -7
- package/dist/{chunk-TXALJRYO.js → chunk-5ZKT7WTR.js} +7 -7
- package/dist/{chunk-PYVZKEZZ.js → chunk-6DA5BBLS.js} +120 -120
- package/dist/{chunk-WS6GLJ7P.js → chunk-6DXXIXED.js} +4 -4
- package/dist/{chunk-THZ4WR5L.js → chunk-7APVFQ34.js} +1182 -1009
- package/dist/chunk-7APVFQ34.js.map +1 -0
- package/dist/{chunk-OH66EBCS.js → chunk-7CFGXWYX.js} +2 -2
- package/dist/{chunk-TJATYB3F.js → chunk-A6XCHI53.js} +5 -5
- package/dist/{chunk-2MHCWXX6.js → chunk-BTH6MQJT.js} +7 -7
- package/dist/{chunk-G7LCG56O.js → chunk-CXW6Y7SI.js} +4 -4
- package/dist/{chunk-L4AS7TIG.js → chunk-DWDDQLSZ.js} +2300 -2239
- package/dist/chunk-DWDDQLSZ.js.map +1 -0
- package/dist/{chunk-DS4QM6I7.js → chunk-EEKDRAWX.js} +4 -4
- package/dist/{chunk-GAH5755F.js → chunk-EF3VFQC4.js} +2 -2
- package/dist/{chunk-437S3G37.js → chunk-EJJQDP3H.js} +1 -1
- package/dist/{chunk-V4C4SFZB.js → chunk-EVVLGYHR.js} +3 -3
- package/dist/{chunk-ZJMSTFOR.js → chunk-GBXP62CL.js} +10 -10
- package/dist/{chunk-7AL5WNPW.js → chunk-GG6G2RYR.js} +9 -9
- package/dist/{chunk-BERWPOXU.js → chunk-H2T4OJNI.js} +4 -4
- package/dist/{chunk-2MR3MFQB.js → chunk-JTYM4HPA.js} +2 -2
- package/dist/{chunk-LD53WPMU.js → chunk-LQ6JI4VH.js} +4 -4
- package/dist/{chunk-GPFXPC2V.js → chunk-NG2NHB4C.js} +7 -7
- package/dist/{chunk-COZEVVXZ.js → chunk-NLXKSM46.js} +2 -2
- package/dist/{chunk-VSU2NZGC.js → chunk-OLAVQS6S.js} +6 -6
- package/dist/{chunk-ER3CCWK2.js → chunk-ORAK3LNZ.js} +2 -2
- package/dist/{chunk-WUVUTOUV.js → chunk-PBZQVVLG.js} +5 -5
- package/dist/{chunk-PJQMISPD.js → chunk-PPAEJXVF.js} +5 -5
- package/dist/{chunk-KURUULFF.js → chunk-PRX77NPC.js} +5 -5
- package/dist/{chunk-24463PIV.js → chunk-PS572XYO.js} +2 -2
- package/dist/{chunk-U2SMR57B.js → chunk-REZ4TUD2.js} +10 -10
- package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/{chunk-V5UV7BOA.js → chunk-SQNED75R.js} +1 -1
- package/dist/{chunk-FLVL4L3L.js → chunk-TCKP4EHY.js} +2 -2
- package/dist/{chunk-YRBXBHU7.js → chunk-TXV3I2O6.js} +7 -7
- package/dist/{chunk-CJ276MDI.js → chunk-UNV6ZGTS.js} +2 -2
- package/dist/{chunk-2BFLDH5F.js → chunk-WEPWY4HB.js} +2 -2
- package/dist/{chunk-YQGHW5ML.js → chunk-WRFKZEO6.js} +4 -4
- package/dist/{chunk-7CAJLUNW.js → chunk-Y4TAFMUE.js} +6 -6
- package/dist/{chunk-TIFF32QK.js → chunk-YMGQXBPS.js} +2 -2
- package/dist/{chunk-T422SYM6.js → chunk-ZUDM75KG.js} +5 -5
- package/dist/{chunk-4DVYRPLL.js → chunk-ZUHD6XZZ.js} +43 -43
- package/dist/{chunk-BQ6J6P3K.js → chunk-ZW5SZ3TH.js} +12 -12
- package/dist/{cli-D-xw8Sp1.d.ts → cli-cZyANcmY.d.ts} +3 -3
- package/dist/cli.d.ts +6 -6
- package/dist/cli.js +59 -59
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +8 -8
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +8 -8
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +9 -9
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/contradiction/index.js +2 -2
- package/dist/{contradiction-scan-2SODRNUF.js → contradiction-scan-3FJYWS3G.js} +3 -3
- package/dist/contradiction-scan-3FJYWS3G.js.map +1 -0
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +8 -8
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +4 -4
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +2 -2
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +126 -126
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +2 -2
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +8 -8
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.js +9 -9
- package/dist/mcp-memory-inspector-app.d.ts +5 -5
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +18 -18
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +9 -9
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +24 -24
- package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
- package/dist/orchestration/maintenance.d.ts +2 -2
- package/dist/orchestration/maintenance.js +12 -12
- package/dist/{orchestrator-D7ZGOQLX.d.ts → orchestrator-Xm6sK5jw.d.ts} +3 -3
- package/dist/orchestrator.d.ts +4 -4
- package/dist/orchestrator.js +82 -82
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +1 -1
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +8 -8
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-7Sndms2M.d.ts → semantic-consolidation-bfXJNKVf.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +11 -11
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +8 -8
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +3 -3
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +69 -97
- package/dist/storage.js +22 -6
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +2 -2
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- 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/types.d.ts +12 -12
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-DTEFxFuL.d.ts → types-D1iBqhJA.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +8 -8
- package/package.json +2 -2
- package/src/access-observe-write-surface.ts +1007 -0
- package/src/access-service.ts +48 -794
- package/src/orchestration/self-deps.ts +9 -1
- package/src/storage/entity-store.ts +674 -0
- package/src/storage/memory-read-store.ts +756 -0
- package/src/storage.ts +111 -1191
- package/dist/chunk-L4AS7TIG.js.map +0 -1
- package/dist/chunk-THZ4WR5L.js.map +0 -1
- /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-observe-write-surface.js.map} +0 -0
- /package/dist/{auto-sync-G4YKBPOU.js.map → auto-sync-RZO5ZPHR.js.map} +0 -0
- /package/dist/{contradiction-scan-2SODRNUF.js.map → capsule-crypto-FFWJ4X4A.js.map} +0 -0
- /package/dist/{chunk-M5QKGHCR.js.map → chunk-3I2IASNK.js.map} +0 -0
- /package/dist/{chunk-JBO6UBIE.js.map → chunk-4AC57RCM.js.map} +0 -0
- /package/dist/{chunk-3DOD6ICJ.js.map → chunk-4I45UZ4V.js.map} +0 -0
- /package/dist/{chunk-TAPSNIMA.js.map → chunk-5CEJH5ZN.js.map} +0 -0
- /package/dist/{chunk-CXCZ4LAI.js.map → chunk-5VWRYFQE.js.map} +0 -0
- /package/dist/{chunk-TXALJRYO.js.map → chunk-5ZKT7WTR.js.map} +0 -0
- /package/dist/{chunk-PYVZKEZZ.js.map → chunk-6DA5BBLS.js.map} +0 -0
- /package/dist/{chunk-WS6GLJ7P.js.map → chunk-6DXXIXED.js.map} +0 -0
- /package/dist/{chunk-OH66EBCS.js.map → chunk-7CFGXWYX.js.map} +0 -0
- /package/dist/{chunk-TJATYB3F.js.map → chunk-A6XCHI53.js.map} +0 -0
- /package/dist/{chunk-2MHCWXX6.js.map → chunk-BTH6MQJT.js.map} +0 -0
- /package/dist/{chunk-G7LCG56O.js.map → chunk-CXW6Y7SI.js.map} +0 -0
- /package/dist/{chunk-DS4QM6I7.js.map → chunk-EEKDRAWX.js.map} +0 -0
- /package/dist/{chunk-GAH5755F.js.map → chunk-EF3VFQC4.js.map} +0 -0
- /package/dist/{chunk-437S3G37.js.map → chunk-EJJQDP3H.js.map} +0 -0
- /package/dist/{chunk-V4C4SFZB.js.map → chunk-EVVLGYHR.js.map} +0 -0
- /package/dist/{chunk-ZJMSTFOR.js.map → chunk-GBXP62CL.js.map} +0 -0
- /package/dist/{chunk-7AL5WNPW.js.map → chunk-GG6G2RYR.js.map} +0 -0
- /package/dist/{chunk-BERWPOXU.js.map → chunk-H2T4OJNI.js.map} +0 -0
- /package/dist/{chunk-2MR3MFQB.js.map → chunk-JTYM4HPA.js.map} +0 -0
- /package/dist/{chunk-LD53WPMU.js.map → chunk-LQ6JI4VH.js.map} +0 -0
- /package/dist/{chunk-GPFXPC2V.js.map → chunk-NG2NHB4C.js.map} +0 -0
- /package/dist/{chunk-COZEVVXZ.js.map → chunk-NLXKSM46.js.map} +0 -0
- /package/dist/{chunk-VSU2NZGC.js.map → chunk-OLAVQS6S.js.map} +0 -0
- /package/dist/{chunk-ER3CCWK2.js.map → chunk-ORAK3LNZ.js.map} +0 -0
- /package/dist/{chunk-WUVUTOUV.js.map → chunk-PBZQVVLG.js.map} +0 -0
- /package/dist/{chunk-PJQMISPD.js.map → chunk-PPAEJXVF.js.map} +0 -0
- /package/dist/{chunk-KURUULFF.js.map → chunk-PRX77NPC.js.map} +0 -0
- /package/dist/{chunk-24463PIV.js.map → chunk-PS572XYO.js.map} +0 -0
- /package/dist/{chunk-U2SMR57B.js.map → chunk-REZ4TUD2.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-V5UV7BOA.js.map → chunk-SQNED75R.js.map} +0 -0
- /package/dist/{chunk-FLVL4L3L.js.map → chunk-TCKP4EHY.js.map} +0 -0
- /package/dist/{chunk-YRBXBHU7.js.map → chunk-TXV3I2O6.js.map} +0 -0
- /package/dist/{chunk-CJ276MDI.js.map → chunk-UNV6ZGTS.js.map} +0 -0
- /package/dist/{chunk-2BFLDH5F.js.map → chunk-WEPWY4HB.js.map} +0 -0
- /package/dist/{chunk-YQGHW5ML.js.map → chunk-WRFKZEO6.js.map} +0 -0
- /package/dist/{chunk-7CAJLUNW.js.map → chunk-Y4TAFMUE.js.map} +0 -0
- /package/dist/{chunk-TIFF32QK.js.map → chunk-YMGQXBPS.js.map} +0 -0
- /package/dist/{chunk-T422SYM6.js.map → chunk-ZUDM75KG.js.map} +0 -0
- /package/dist/{chunk-4DVYRPLL.js.map → chunk-ZUHD6XZZ.js.map} +0 -0
- /package/dist/{chunk-BQ6J6P3K.js.map → chunk-ZW5SZ3TH.js.map} +0 -0
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-GDB4J2H3.js";
|
|
9
9
|
import {
|
|
10
10
|
importCapsule
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-3I2IASNK.js";
|
|
12
12
|
import {
|
|
13
13
|
getTrustZoneStoreStatus,
|
|
14
14
|
isTrustZoneName,
|
|
@@ -29,6 +29,9 @@ import {
|
|
|
29
29
|
normalizeTags,
|
|
30
30
|
parseTagMatch
|
|
31
31
|
} from "./chunk-BT7NVCML.js";
|
|
32
|
+
import {
|
|
33
|
+
decideDisclosureEscalation
|
|
34
|
+
} from "./chunk-H7XKCNR6.js";
|
|
32
35
|
import {
|
|
33
36
|
toRecallExplainJson
|
|
34
37
|
} from "./chunk-2KK5WNQB.js";
|
|
@@ -36,14 +39,8 @@ import {
|
|
|
36
39
|
estimateRecallTokens
|
|
37
40
|
} from "./chunk-6MMONPHM.js";
|
|
38
41
|
import {
|
|
39
|
-
|
|
40
|
-
} from "./chunk-
|
|
41
|
-
import {
|
|
42
|
-
recordObjectiveStateSnapshotsFromObservedMessages
|
|
43
|
-
} from "./chunk-ICP53SKG.js";
|
|
44
|
-
import {
|
|
45
|
-
objectiveStateStoreOverrideForNamespace
|
|
46
|
-
} from "./chunk-6HMYUWXR.js";
|
|
42
|
+
namespaceCollectionName
|
|
43
|
+
} from "./chunk-PS572XYO.js";
|
|
47
44
|
import {
|
|
48
45
|
combineNamespaces,
|
|
49
46
|
defaultGitInvoker,
|
|
@@ -57,9 +54,6 @@ import {
|
|
|
57
54
|
resolveWritableNamespaceValue,
|
|
58
55
|
sanitizeFragment
|
|
59
56
|
} from "./chunk-W3M34XT4.js";
|
|
60
|
-
import {
|
|
61
|
-
namespaceCollectionName
|
|
62
|
-
} from "./chunk-24463PIV.js";
|
|
63
57
|
import {
|
|
64
58
|
namespaceIdentityFromToken
|
|
65
59
|
} from "./chunk-ZFXCQPNO.js";
|
|
@@ -70,13 +64,24 @@ import {
|
|
|
70
64
|
listMemoryGovernanceRuns,
|
|
71
65
|
readMemoryGovernanceRunArtifact,
|
|
72
66
|
runMemoryGovernance
|
|
73
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-PRX77NPC.js";
|
|
68
|
+
import {
|
|
69
|
+
recordObjectiveStateSnapshotsFromObservedMessages
|
|
70
|
+
} from "./chunk-ICP53SKG.js";
|
|
71
|
+
import {
|
|
72
|
+
objectiveStateStoreOverrideForNamespace
|
|
73
|
+
} from "./chunk-6HMYUWXR.js";
|
|
74
74
|
import {
|
|
75
75
|
recordMemoryOutcome
|
|
76
76
|
} from "./chunk-EIR5VLIH.js";
|
|
77
77
|
import {
|
|
78
78
|
buildGraphSnapshot
|
|
79
79
|
} from "./chunk-TQT3USCG.js";
|
|
80
|
+
import {
|
|
81
|
+
persistExplicitCapture,
|
|
82
|
+
queueExplicitCaptureForReview,
|
|
83
|
+
validateExplicitCaptureInput
|
|
84
|
+
} from "./chunk-K5LLP4CY.js";
|
|
80
85
|
import {
|
|
81
86
|
wrapWorkLayerContext
|
|
82
87
|
} from "./chunk-EI6V5UXY.js";
|
|
@@ -87,53 +92,20 @@ import {
|
|
|
87
92
|
buildProcedurePersistBody,
|
|
88
93
|
normalizeProcedureSteps
|
|
89
94
|
} from "./chunk-QDW3E4RD.js";
|
|
90
|
-
import {
|
|
91
|
-
persistExplicitCapture,
|
|
92
|
-
queueExplicitCaptureForReview,
|
|
93
|
-
validateExplicitCaptureInput
|
|
94
|
-
} from "./chunk-K5LLP4CY.js";
|
|
95
95
|
import {
|
|
96
96
|
CrossNamespaceBudget
|
|
97
97
|
} from "./chunk-ODPLEWB6.js";
|
|
98
|
-
import {
|
|
99
|
-
defaultCapsulesDir
|
|
100
|
-
} from "./chunk-ZY2MNJR6.js";
|
|
101
|
-
import {
|
|
102
|
-
filterTrajectoriesByLookbackDays,
|
|
103
|
-
readCausalTrajectoryRecords
|
|
104
|
-
} from "./chunk-V5UV7BOA.js";
|
|
105
|
-
import {
|
|
106
|
-
canReadNamespace,
|
|
107
|
-
canWriteNamespace,
|
|
108
|
-
defaultNamespaceForPrincipal,
|
|
109
|
-
recallNamespacesForPrincipal,
|
|
110
|
-
resolvePrincipal
|
|
111
|
-
} from "./chunk-PE6XR4GG.js";
|
|
112
|
-
import {
|
|
113
|
-
resolveAccessSetupCapabilities,
|
|
114
|
-
resolveCompressionCapabilities,
|
|
115
|
-
resolveConsolidationCapabilities,
|
|
116
|
-
resolveGraphConstructionCapabilities,
|
|
117
|
-
resolveIdentityContinuityCapabilities,
|
|
118
|
-
resolveIndexingCapabilities,
|
|
119
|
-
resolveMemoryLifecycleCapabilities,
|
|
120
|
-
resolveNamespaceCapabilities,
|
|
121
|
-
resolveObjectiveStateCapabilities,
|
|
122
|
-
resolveQmdCapabilities,
|
|
123
|
-
resolveRecallAuxiliaryCapabilities,
|
|
124
|
-
resolveRecallEnhancementCapabilities,
|
|
125
|
-
resolveSecurityCapabilities
|
|
126
|
-
} from "./chunk-VSF7AAGE.js";
|
|
127
98
|
import {
|
|
128
99
|
FileCalendarSource,
|
|
129
100
|
buildBriefing,
|
|
130
101
|
enqueuePassiveCorrectionNotification,
|
|
131
102
|
parseBriefingFocus,
|
|
132
103
|
parseBriefingWindow
|
|
133
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-YMGQXBPS.js";
|
|
134
105
|
import {
|
|
135
|
-
parseEntityFile
|
|
136
|
-
|
|
106
|
+
parseEntityFile,
|
|
107
|
+
selfDeps
|
|
108
|
+
} from "./chunk-7APVFQ34.js";
|
|
137
109
|
import {
|
|
138
110
|
supersessionKeysForFact
|
|
139
111
|
} from "./chunk-TKZ3AL5V.js";
|
|
@@ -147,6 +119,10 @@ import {
|
|
|
147
119
|
DEFAULT_RECALL_DISCLOSURE,
|
|
148
120
|
isRecallDisclosure
|
|
149
121
|
} from "./chunk-JS2HKKOK.js";
|
|
122
|
+
import {
|
|
123
|
+
inferMemoryStatus,
|
|
124
|
+
toMemoryPathRel
|
|
125
|
+
} from "./chunk-RULE4VG5.js";
|
|
150
126
|
import {
|
|
151
127
|
normalizeProjectionPreview,
|
|
152
128
|
normalizeProjectionTags
|
|
@@ -158,15 +134,40 @@ import {
|
|
|
158
134
|
displayErrorDetail
|
|
159
135
|
} from "./chunk-6KYMPV2O.js";
|
|
160
136
|
import {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
} from "./chunk-RULE4VG5.js";
|
|
137
|
+
computeContentHash
|
|
138
|
+
} from "./chunk-W5H6ZRPT.js";
|
|
164
139
|
import {
|
|
165
140
|
createVersion
|
|
166
141
|
} from "./chunk-VF4XKTX3.js";
|
|
167
142
|
import {
|
|
168
|
-
|
|
169
|
-
} from "./chunk-
|
|
143
|
+
defaultCapsulesDir
|
|
144
|
+
} from "./chunk-ZY2MNJR6.js";
|
|
145
|
+
import {
|
|
146
|
+
filterTrajectoriesByLookbackDays,
|
|
147
|
+
readCausalTrajectoryRecords
|
|
148
|
+
} from "./chunk-SQNED75R.js";
|
|
149
|
+
import {
|
|
150
|
+
canReadNamespace,
|
|
151
|
+
canWriteNamespace,
|
|
152
|
+
defaultNamespaceForPrincipal,
|
|
153
|
+
recallNamespacesForPrincipal,
|
|
154
|
+
resolvePrincipal
|
|
155
|
+
} from "./chunk-PE6XR4GG.js";
|
|
156
|
+
import {
|
|
157
|
+
resolveAccessSetupCapabilities,
|
|
158
|
+
resolveCompressionCapabilities,
|
|
159
|
+
resolveConsolidationCapabilities,
|
|
160
|
+
resolveGraphConstructionCapabilities,
|
|
161
|
+
resolveIdentityContinuityCapabilities,
|
|
162
|
+
resolveIndexingCapabilities,
|
|
163
|
+
resolveMemoryLifecycleCapabilities,
|
|
164
|
+
resolveNamespaceCapabilities,
|
|
165
|
+
resolveObjectiveStateCapabilities,
|
|
166
|
+
resolveQmdCapabilities,
|
|
167
|
+
resolveRecallAuxiliaryCapabilities,
|
|
168
|
+
resolveRecallEnhancementCapabilities,
|
|
169
|
+
resolveSecurityCapabilities
|
|
170
|
+
} from "./chunk-VSF7AAGE.js";
|
|
170
171
|
import {
|
|
171
172
|
sanitizeMemoryContent
|
|
172
173
|
} from "./chunk-5GKPFL3S.js";
|
|
@@ -181,7 +182,7 @@ import {
|
|
|
181
182
|
} from "./chunk-NU3CSQ4H.js";
|
|
182
183
|
import {
|
|
183
184
|
exportCapsule
|
|
184
|
-
} from "./chunk-
|
|
185
|
+
} from "./chunk-TCKP4EHY.js";
|
|
185
186
|
import {
|
|
186
187
|
evaluateActionConfidence
|
|
187
188
|
} from "./chunk-Z2YFLXHU.js";
|
|
@@ -207,6 +208,9 @@ import {
|
|
|
207
208
|
hashAccessIdempotencyPayload
|
|
208
209
|
} from "./chunk-VFUEZZBS.js";
|
|
209
210
|
|
|
211
|
+
// src/access-observe-write-surface.ts
|
|
212
|
+
import { createHash as createHash6 } from "crypto";
|
|
213
|
+
|
|
210
214
|
// src/access-service.ts
|
|
211
215
|
import { stat as stat2 } from "fs/promises";
|
|
212
216
|
import * as nodeFs2 from "fs/promises";
|
|
@@ -4748,1266 +4752,1574 @@ async function promoteMemory(options) {
|
|
|
4748
4752
|
// src/access-service.ts
|
|
4749
4753
|
import * as nodePath3 from "path";
|
|
4750
4754
|
|
|
4751
|
-
// src/access-
|
|
4752
|
-
|
|
4753
|
-
import * as nodePath from "path";
|
|
4754
|
-
var AccessAdminOpsSurface = class {
|
|
4755
|
+
// src/access-lcm-surface.ts
|
|
4756
|
+
var AccessLcmSurface = class {
|
|
4755
4757
|
constructor(deps) {
|
|
4756
4758
|
this.deps = deps;
|
|
4757
4759
|
}
|
|
4758
4760
|
deps;
|
|
4759
|
-
async
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
} catch {
|
|
4778
|
-
return null;
|
|
4779
|
-
}
|
|
4780
|
-
})();
|
|
4781
|
-
const metrics = projected.metrics;
|
|
4782
|
-
const fallbackTransitionReport = {
|
|
4783
|
-
proposed: groupActionsByStatus(projectedProposedActions),
|
|
4784
|
-
applied: groupActionsByStatus(projectedAppliedActions)
|
|
4761
|
+
async lcmSearch(request) {
|
|
4762
|
+
if (!request.query || typeof request.query !== "string" || request.query.trim().length === 0) {
|
|
4763
|
+
throw new EngramAccessInputError("query is required and must be a non-empty string");
|
|
4764
|
+
}
|
|
4765
|
+
const principal = this.deps.resolveRequestPrincipal(request.sessionKey, request.authenticatedPrincipal);
|
|
4766
|
+
const hasExplicitNamespace = typeof request.namespace === "string" && request.namespace.trim().length > 0;
|
|
4767
|
+
const profileLcmReadNamespaces = hasExplicitNamespace ? null : this.deps.resolveScopeProfileLcmReadNamespaces(
|
|
4768
|
+
request.sessionKey,
|
|
4769
|
+
request.authenticatedPrincipal
|
|
4770
|
+
);
|
|
4771
|
+
const namespace = hasExplicitNamespace ? this.deps.resolveReadableNamespace(request.namespace, principal) : profileLcmReadNamespaces !== null ? profileLcmReadNamespaces[0] : this.deps.resolveImplicitLcmReadFallbackNamespace(principal);
|
|
4772
|
+
if (!this.deps.orchestrator.lcmEngine || !this.deps.orchestrator.lcmEngine.enabled) {
|
|
4773
|
+
return {
|
|
4774
|
+
query: request.query,
|
|
4775
|
+
namespace: namespace ?? this.deps.orchestrator.config.defaultNamespace,
|
|
4776
|
+
results: [],
|
|
4777
|
+
count: 0,
|
|
4778
|
+
lcmEnabled: false
|
|
4785
4779
|
};
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
applied: hasGroupedGovernanceActions(projectedArtifact.transitionReport.applied) || projectedAppliedActions.length === 0 ? projectedArtifact.transitionReport.applied : fallbackTransitionReport.applied
|
|
4789
|
-
} : fallbackTransitionReport;
|
|
4790
|
-
const qualityScore = projectedArtifact?.qualityScore ?? metrics?.qualityScore ?? buildQualityScore(metrics?.reviewReasons ?? {
|
|
4791
|
-
exact_duplicate: 0,
|
|
4792
|
-
semantic_duplicate_candidate: 0,
|
|
4793
|
-
disputed_memory: 0,
|
|
4794
|
-
speculative_low_confidence: 0,
|
|
4795
|
-
archive_candidate: 0,
|
|
4796
|
-
explicit_capture_review: 0,
|
|
4797
|
-
malformed_import: 0
|
|
4798
|
-
});
|
|
4799
|
-
const effectiveMetrics = metrics ? { ...metrics, qualityScore: metrics.qualityScore ?? qualityScore } : metrics;
|
|
4780
|
+
}
|
|
4781
|
+
if (profileLcmReadNamespaces !== null && profileLcmReadNamespaces.length === 0) {
|
|
4800
4782
|
return {
|
|
4801
|
-
|
|
4802
|
-
namespace:
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
qualityScore,
|
|
4807
|
-
reviewQueue: projected.reviewQueueRows.map((row) => ({
|
|
4808
|
-
entryId: row.entryId,
|
|
4809
|
-
memoryId: row.memoryId,
|
|
4810
|
-
path: row.path,
|
|
4811
|
-
reasonCode: row.reasonCode,
|
|
4812
|
-
severity: row.severity,
|
|
4813
|
-
suggestedAction: row.suggestedAction,
|
|
4814
|
-
suggestedStatus: row.suggestedStatus,
|
|
4815
|
-
relatedMemoryIds: row.relatedMemoryIds
|
|
4816
|
-
})),
|
|
4817
|
-
appliedActions: projectedAppliedActions,
|
|
4818
|
-
transitionReport,
|
|
4819
|
-
report: projected.report
|
|
4783
|
+
query: request.query,
|
|
4784
|
+
namespace: this.deps.orchestrator.config.defaultNamespace,
|
|
4785
|
+
results: [],
|
|
4786
|
+
count: 0,
|
|
4787
|
+
lcmEnabled: true
|
|
4820
4788
|
};
|
|
4821
4789
|
}
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
metrics: artifact.metrics,
|
|
4831
|
-
qualityScore: artifact.qualityScore,
|
|
4832
|
-
reviewQueue: artifact.reviewQueue,
|
|
4833
|
-
appliedActions: artifact.appliedActions,
|
|
4834
|
-
transitionReport: artifact.transitionReport,
|
|
4835
|
-
report: artifact.report
|
|
4836
|
-
};
|
|
4837
|
-
}
|
|
4838
|
-
async governanceRun(request, principal) {
|
|
4839
|
-
const deepSleep = this.deps.orchestrator.config.dreamsPhases.deepSleep;
|
|
4840
|
-
if (deepSleep.enabled === false && deepSleep.enabledExplicitlySet === true) {
|
|
4841
|
-
throw new Error("memory governance is disabled by dreams.phases.deepSleep.enabled=false");
|
|
4790
|
+
if (namespace === void 0) {
|
|
4791
|
+
return {
|
|
4792
|
+
query: request.query,
|
|
4793
|
+
namespace: this.deps.orchestrator.config.defaultNamespace,
|
|
4794
|
+
results: [],
|
|
4795
|
+
count: 0,
|
|
4796
|
+
lcmEnabled: true
|
|
4797
|
+
};
|
|
4842
4798
|
}
|
|
4843
|
-
const
|
|
4799
|
+
const limit = Math.max(1, Math.min(request.limit ?? 10, 100));
|
|
4800
|
+
const lcmReadNamespace = profileLcmReadNamespaces !== null ? profileLcmReadNamespaces[0] ?? this.deps.orchestrator.config.defaultNamespace : this.deps.resolveLcmReadNamespace(
|
|
4844
4801
|
request.namespace,
|
|
4845
|
-
|
|
4846
|
-
request.
|
|
4802
|
+
namespace,
|
|
4803
|
+
request.sessionKey,
|
|
4804
|
+
request.authenticatedPrincipal
|
|
4847
4805
|
);
|
|
4848
|
-
const
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
namespace: resolvedNamespace,
|
|
4870
|
-
runId: result.runId,
|
|
4871
|
-
traceId: result.traceId,
|
|
4872
|
-
mode: result.mode,
|
|
4873
|
-
reviewQueueCount: result.reviewQueue.length,
|
|
4874
|
-
proposedActionCount: result.proposedActions.length,
|
|
4875
|
-
appliedActionCount: result.appliedActions.length,
|
|
4876
|
-
summaryPath: result.summaryPath,
|
|
4877
|
-
reportPath: result.reportPath
|
|
4878
|
-
};
|
|
4879
|
-
}
|
|
4880
|
-
async conversationIndexUpdate(request = {}) {
|
|
4881
|
-
if (!resolveIndexingCapabilities(this.deps.orchestrator.config).conversationIndex) {
|
|
4882
|
-
return {
|
|
4883
|
-
enabled: false,
|
|
4884
|
-
sessions: 0,
|
|
4885
|
-
chunks: 0,
|
|
4886
|
-
skipped: 0,
|
|
4887
|
-
skippedSessionKeys: [],
|
|
4888
|
-
embeddedRuns: 0,
|
|
4889
|
-
reason: "disabled"
|
|
4890
|
-
};
|
|
4891
|
-
}
|
|
4892
|
-
const hours = typeof request.hours === "number" && Number.isFinite(request.hours) ? Math.max(1, Math.floor(request.hours)) : 24;
|
|
4893
|
-
let sessionKey;
|
|
4894
|
-
if (request.sessionKey !== void 0) {
|
|
4895
|
-
if (typeof request.sessionKey !== "string" || request.sessionKey.trim().length === 0) {
|
|
4896
|
-
throw new EngramAccessInputError("sessionKey must be a non-empty string when provided");
|
|
4897
|
-
}
|
|
4898
|
-
sessionKey = request.sessionKey.trim();
|
|
4899
|
-
}
|
|
4900
|
-
if (sessionKey) {
|
|
4901
|
-
const result = await this.deps.orchestrator.updateConversationIndex(
|
|
4902
|
-
sessionKey,
|
|
4903
|
-
hours,
|
|
4904
|
-
{ embed: request.embed }
|
|
4905
|
-
);
|
|
4806
|
+
const lcmSessionKeyIds = request.sessionKey ? profileLcmReadNamespaces !== null ? this.deps.lcmSessionIdsForNamespaces(
|
|
4807
|
+
profileLcmReadNamespaces,
|
|
4808
|
+
request.sessionKey
|
|
4809
|
+
) : this.deps.resolveLcmReadSessionIds(
|
|
4810
|
+
request.namespace,
|
|
4811
|
+
namespace,
|
|
4812
|
+
request.sessionKey,
|
|
4813
|
+
request.authenticatedPrincipal
|
|
4814
|
+
) : [void 0];
|
|
4815
|
+
const lcmSessionPrefixes = request.sessionPrefix ? profileLcmReadNamespaces !== null && !request.sessionKey ? this.deps.lcmSessionIdsForNamespaces(
|
|
4816
|
+
profileLcmReadNamespaces,
|
|
4817
|
+
request.sessionPrefix
|
|
4818
|
+
) : [
|
|
4819
|
+
lcmSessionKeyForNamespace(
|
|
4820
|
+
lcmReadNamespace,
|
|
4821
|
+
request.sessionPrefix,
|
|
4822
|
+
this.deps.orchestrator.config.defaultNamespace
|
|
4823
|
+
) ?? request.sessionPrefix
|
|
4824
|
+
] : [void 0];
|
|
4825
|
+
const hasScopedSession = typeof request.sessionKey === "string" && request.sessionKey.length > 0 || lcmSessionPrefixes.some((prefix) => typeof prefix === "string" && prefix.length > 0);
|
|
4826
|
+
if (!hasScopedSession && resolveNamespaceCapabilities(this.deps.orchestrator.config).namespaces === true) {
|
|
4906
4827
|
return {
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
skippedSessionKeys: result.skipped ? [sessionKey] : [],
|
|
4913
|
-
embeddedRuns: result.embedded ? 1 : 0,
|
|
4914
|
-
reason: result.reason,
|
|
4915
|
-
retryAfterMs: result.retryAfterMs
|
|
4828
|
+
query: request.query,
|
|
4829
|
+
namespace,
|
|
4830
|
+
results: [],
|
|
4831
|
+
count: 0,
|
|
4832
|
+
lcmEnabled: true
|
|
4916
4833
|
};
|
|
4917
4834
|
}
|
|
4918
|
-
const
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
const
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
}
|
|
4934
|
-
if (result.embedded) {
|
|
4935
|
-
embeddedRuns += 1;
|
|
4835
|
+
const seenRows = /* @__PURE__ */ new Set();
|
|
4836
|
+
const results = [];
|
|
4837
|
+
const lcmSearches = [];
|
|
4838
|
+
for (const lcmSessionKey of lcmSessionKeyIds) {
|
|
4839
|
+
for (const lcmSessionPrefix of lcmSessionPrefixes) {
|
|
4840
|
+
lcmSearches.push({
|
|
4841
|
+
key: lcmSessionKey,
|
|
4842
|
+
prefix: lcmSessionPrefix,
|
|
4843
|
+
promise: this.deps.orchestrator.lcmEngine.searchContextFull(
|
|
4844
|
+
request.query,
|
|
4845
|
+
limit,
|
|
4846
|
+
lcmSessionKey,
|
|
4847
|
+
lcmSessionPrefix
|
|
4848
|
+
)
|
|
4849
|
+
});
|
|
4936
4850
|
}
|
|
4937
4851
|
}
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
return {
|
|
4951
|
-
enabled: false,
|
|
4952
|
-
reason: "disabled",
|
|
4953
|
-
message: "Profiling is disabled. Set profilingEnabled: true in your plugin config to enable."
|
|
4954
|
-
};
|
|
4955
|
-
}
|
|
4956
|
-
const format = request.format ?? "ascii";
|
|
4957
|
-
if (format !== "ascii" && format !== "json") {
|
|
4958
|
-
throw new EngramAccessInputError("format must be one of: ascii, json");
|
|
4959
|
-
}
|
|
4960
|
-
const limit = request.limit ?? 5;
|
|
4961
|
-
if (!Number.isInteger(limit) || limit < 1 || limit > 20) {
|
|
4962
|
-
throw new EngramAccessInputError("limit must be an integer between 1 and 20");
|
|
4963
|
-
}
|
|
4964
|
-
const traces = profiler.getRecentTraces(limit);
|
|
4965
|
-
const stats = profiler.getStats();
|
|
4966
|
-
const bottleneck = profiler.identifyBottleneck();
|
|
4967
|
-
if (format === "json") {
|
|
4968
|
-
return {
|
|
4969
|
-
enabled: true,
|
|
4970
|
-
format,
|
|
4971
|
-
traces,
|
|
4972
|
-
stats,
|
|
4973
|
-
bottleneck
|
|
4974
|
-
};
|
|
4975
|
-
}
|
|
4976
|
-
const lines = [];
|
|
4977
|
-
lines.push("Engram Profiling Report");
|
|
4978
|
-
lines.push("=".repeat(60));
|
|
4979
|
-
lines.push("");
|
|
4980
|
-
const allBuckets = [
|
|
4981
|
-
["byKind", stats.byKind],
|
|
4982
|
-
["bySpan", stats.bySpan]
|
|
4983
|
-
];
|
|
4984
|
-
const hasStats = allBuckets.some(([, entries]) => Object.keys(entries).length > 0);
|
|
4985
|
-
if (hasStats) {
|
|
4986
|
-
lines.push("Aggregate Stats (all retained traces):");
|
|
4987
|
-
for (const [bucket, entries] of allBuckets) {
|
|
4988
|
-
for (const [key, summary] of Object.entries(entries)) {
|
|
4989
|
-
lines.push(
|
|
4990
|
-
` ${bucket}/${key}: avg=${summary.avgMs}ms p50=${summary.p50Ms}ms p95=${summary.p95Ms}ms max=${summary.maxMs}ms (n=${summary.count})`
|
|
4991
|
-
);
|
|
4992
|
-
}
|
|
4852
|
+
const settledSearches = await Promise.allSettled(
|
|
4853
|
+
lcmSearches.map((search) => search.promise)
|
|
4854
|
+
);
|
|
4855
|
+
for (let i = 0; i < settledSearches.length; i += 1) {
|
|
4856
|
+
if (results.length >= limit) break;
|
|
4857
|
+
const settled = settledSearches[i];
|
|
4858
|
+
if (!settled || settled.status === "rejected") {
|
|
4859
|
+
const failed = lcmSearches[i];
|
|
4860
|
+
log.warn(
|
|
4861
|
+
`lcmSearch: failed for key=${failed?.key ?? "<none>"} prefix=${failed?.prefix ?? "<none>"}: ${settled?.status === "rejected" ? settled.reason : "missing result"}`
|
|
4862
|
+
);
|
|
4863
|
+
continue;
|
|
4993
4864
|
}
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
lines.push(formatProfileTraceAscii(trace));
|
|
5005
|
-
lines.push("");
|
|
4865
|
+
for (const r of settled.value) {
|
|
4866
|
+
const dedupeKey = `${r.session_id}\0${r.turn_index}`;
|
|
4867
|
+
if (seenRows.has(dedupeKey)) continue;
|
|
4868
|
+
seenRows.add(dedupeKey);
|
|
4869
|
+
results.push({
|
|
4870
|
+
sessionId: r.session_id,
|
|
4871
|
+
content: r.content,
|
|
4872
|
+
turnIndex: r.turn_index
|
|
4873
|
+
});
|
|
4874
|
+
if (results.length >= limit) break;
|
|
5006
4875
|
}
|
|
5007
4876
|
}
|
|
5008
4877
|
return {
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
4878
|
+
query: request.query,
|
|
4879
|
+
namespace,
|
|
4880
|
+
results,
|
|
4881
|
+
count: results.length,
|
|
4882
|
+
lcmEnabled: true
|
|
5012
4883
|
};
|
|
5013
4884
|
}
|
|
5014
4885
|
/**
|
|
5015
|
-
*
|
|
4886
|
+
* Resolve the effective LCM NAMESPACE a same-session operation must prefix
|
|
4887
|
+
* with (the namespace half of {@link resolveLcmReadSessionKey}). Split out so
|
|
4888
|
+
* `lcmSearch` can apply ONE namespace to BOTH its `sessionKey` and its
|
|
4889
|
+
* `sessionPrefix` — the prefix is a search fragment, not a real session, so its
|
|
4890
|
+
* own coding context can't be looked up; it must inherit the namespace resolved
|
|
4891
|
+
* from the real session (`sessionKeyForOverlay`).
|
|
5016
4892
|
*
|
|
5017
|
-
*
|
|
5018
|
-
*
|
|
5019
|
-
*
|
|
5020
|
-
* `
|
|
5021
|
-
*
|
|
4893
|
+
* `purpose` selects the AUTHORIZATION gate applied before honouring the
|
|
4894
|
+
* coding overlay (#1505 round 3 + round 4, codex P2):
|
|
4895
|
+
*
|
|
4896
|
+
* - `"read"` (`lcmSearch` / raw-excerpt recall): the overlay rows are only
|
|
4897
|
+
* visible when the principal SELF base is in the READABLE RECALL SET — the
|
|
4898
|
+
* same gate the orchestrator's `lcmReadNamespaceForSession` and the recall
|
|
4899
|
+
* namespace set use (`recallNamespacesForPrincipal`, gated by both
|
|
4900
|
+
* `defaultRecallNamespaces.includes("self")` AND `canReadNamespace`). A
|
|
4901
|
+
* caller that passed the default read check must NOT receive
|
|
4902
|
+
* `<principal>-project-*` rows the policy never granted (cross-tenant read
|
|
4903
|
+
* leak). When the self base is not readable, keep the just-authorized
|
|
4904
|
+
* namespace (collapses to the raw key on the default store).
|
|
4905
|
+
*
|
|
4906
|
+
* - `"write"` (`lcmCompactionFlush` / `lcmCompactionRecord`): these are
|
|
4907
|
+
* write/maintenance operations on the SAME queue `observe` just wrote, so
|
|
4908
|
+
* the gate must mirror observe's WRITE authorization (`canWriteNamespace`
|
|
4909
|
+
* on the self base), NOT readability. A principal that can WRITE but not
|
|
4910
|
+
* READ its self namespace (or whose `defaultRecallNamespaces` omits `self`)
|
|
4911
|
+
* archived under the overlay key via `observe`; gating compaction by
|
|
4912
|
+
* readability would fall back to the default/raw key and leave that queue
|
|
4913
|
+
* never flushed/recorded (round-4 codex P2). Write-authorized ⇒ overlay
|
|
4914
|
+
* key, matching the observe write key (rule 42 read/write parity; rule 39
|
|
4915
|
+
* identical gates across paths).
|
|
5022
4916
|
*/
|
|
5023
|
-
|
|
5024
|
-
const
|
|
5025
|
-
|
|
4917
|
+
resolveLcmReadNamespace(explicitNamespace, resolvedNamespace, sessionKeyForOverlay, authenticatedPrincipal, purpose = "read") {
|
|
4918
|
+
const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
|
|
4919
|
+
if (hasExplicitNamespace) return resolvedNamespace;
|
|
4920
|
+
const principal = this.deps.resolveRequestPrincipal(
|
|
4921
|
+
sessionKeyForOverlay,
|
|
5026
4922
|
authenticatedPrincipal
|
|
5027
4923
|
);
|
|
5028
|
-
const
|
|
5029
|
-
const
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
`graphSnapshot: rejected absolute edge endpoint (len=${relPath.length}) outside namespace root`
|
|
5042
|
-
);
|
|
5043
|
-
return null;
|
|
5044
|
-
}
|
|
5045
|
-
const candidate = nodePath.resolve(namespaceRootReal, relPath);
|
|
5046
|
-
if (candidate !== namespaceRootReal && !candidate.startsWith(namespaceRootWithSep)) {
|
|
5047
|
-
log.warn(
|
|
5048
|
-
`graphSnapshot: rejected traversing edge endpoint (len=${relPath.length}) outside namespace root`
|
|
5049
|
-
);
|
|
5050
|
-
return null;
|
|
5051
|
-
}
|
|
5052
|
-
let canonical;
|
|
5053
|
-
try {
|
|
5054
|
-
canonical = await nodeFs.realpath(candidate);
|
|
5055
|
-
} catch {
|
|
5056
|
-
canonical = candidate;
|
|
5057
|
-
}
|
|
5058
|
-
if (canonical !== namespaceRootReal && !canonical.startsWith(namespaceRootWithSep)) {
|
|
5059
|
-
log.warn(
|
|
5060
|
-
`graphSnapshot: rejected symlinked edge endpoint (len=${relPath.length}) that resolved outside namespace root`
|
|
5061
|
-
);
|
|
5062
|
-
return null;
|
|
5063
|
-
}
|
|
5064
|
-
const memory = await storage.readMemoryByPath(canonical);
|
|
5065
|
-
if (!memory) return null;
|
|
5066
|
-
const fm = memory.frontmatter;
|
|
5067
|
-
return {
|
|
5068
|
-
category: fm.category ?? "unknown",
|
|
5069
|
-
label: fm.id ?? nodePath.basename(canonical, nodePath.extname(canonical)),
|
|
5070
|
-
updated: fm.updated
|
|
5071
|
-
};
|
|
5072
|
-
};
|
|
5073
|
-
return buildGraphSnapshot({
|
|
5074
|
-
memoryDir: namespaceRootReal,
|
|
5075
|
-
graphConfig: {
|
|
5076
|
-
entityGraph: graphCaps.entityGraph,
|
|
5077
|
-
timeGraph: graphCaps.timeGraph,
|
|
5078
|
-
causalGraph: graphCaps.causalGraph
|
|
5079
|
-
},
|
|
5080
|
-
request: {
|
|
5081
|
-
limit: request.limit,
|
|
5082
|
-
since: request.since,
|
|
5083
|
-
focusNodeId: request.focusNodeId,
|
|
5084
|
-
categories: request.categories
|
|
5085
|
-
},
|
|
5086
|
-
loadNode
|
|
5087
|
-
});
|
|
4924
|
+
const base = defaultNamespaceForPrincipal(principal, this.deps.orchestrator.config);
|
|
4925
|
+
const overlaid = this.deps.orchestrator.applyCodingNamespaceOverlay(
|
|
4926
|
+
sessionKeyForOverlay,
|
|
4927
|
+
base
|
|
4928
|
+
);
|
|
4929
|
+
if (overlaid === base) return this.deps.orchestrator.config.defaultNamespace;
|
|
4930
|
+
const authorized = purpose === "write" ? canWriteNamespace(principal, base, this.deps.orchestrator.config) : recallNamespacesForPrincipal(
|
|
4931
|
+
principal,
|
|
4932
|
+
this.deps.orchestrator.config
|
|
4933
|
+
).includes(base);
|
|
4934
|
+
if (authorized) return overlaid;
|
|
4935
|
+
if (purpose === "read") return this.deps.orchestrator.config.defaultNamespace;
|
|
4936
|
+
return resolvedNamespace;
|
|
5088
4937
|
}
|
|
5089
4938
|
/**
|
|
5090
|
-
*
|
|
4939
|
+
* Resolve the namespace the raw-disclosure excerpt lookup
|
|
4940
|
+
* ({@link fetchRawExcerpts}) must prefix its LCM `session_id` with (#1505
|
|
4941
|
+
* thread 2f7). Raw disclosure reads the SAME LCM archive `lcmSearch` and the
|
|
4942
|
+
* in-prompt LCM sections read, so it MUST pass through the identical
|
|
4943
|
+
* read-authorization gate — NOT `snapshot.namespace`, which records the
|
|
4944
|
+
* effective WRITE/overlay namespace (`<principal>-project-*`) even when the
|
|
4945
|
+
* principal can WRITE but not READ its self base (or `defaultRecallNamespaces`
|
|
4946
|
+
* omits `self`). Routing through `resolveLcmReadNamespace(..., "read")` makes
|
|
4947
|
+
* raw disclosure fall back to the default store exactly like normal recall +
|
|
4948
|
+
* `lcmSearch`, so it never attaches overlay transcript rows the read gate
|
|
4949
|
+
* excludes (cross-tenant read leak). Collapses to the default store / raw
|
|
4950
|
+
* sessionKey for single-store / no-overlay / explicit-default flows, so
|
|
4951
|
+
* single-user recall is byte-for-byte unchanged.
|
|
5091
4952
|
*
|
|
5092
|
-
*
|
|
5093
|
-
*
|
|
5094
|
-
*
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
const capsulesDir = defaultCapsulesDir(storage.dir);
|
|
5100
|
-
let dirEntries;
|
|
5101
|
-
try {
|
|
5102
|
-
const capsulesDirStat = await nodeFs.lstat(capsulesDir);
|
|
5103
|
-
if (capsulesDirStat.isSymbolicLink()) {
|
|
5104
|
-
throw new EngramAccessInputError("capsule list failed: capsule store directory must not be a symlink");
|
|
5105
|
-
}
|
|
5106
|
-
if (!capsulesDirStat.isDirectory()) {
|
|
5107
|
-
throw new EngramAccessInputError("capsule list failed: capsule store path must be a directory");
|
|
5108
|
-
}
|
|
5109
|
-
dirEntries = await nodeFs.readdir(capsulesDir, { withFileTypes: true });
|
|
5110
|
-
} catch (err) {
|
|
5111
|
-
const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
|
|
5112
|
-
if (code === "ENOENT") {
|
|
5113
|
-
return { namespace: resolvedNamespace, capsulesDir, capsules: [] };
|
|
5114
|
-
}
|
|
5115
|
-
throw err;
|
|
5116
|
-
}
|
|
5117
|
-
const archiveNames = dirEntries.filter(
|
|
5118
|
-
(entry) => entry.isFile() && (entry.name.endsWith(".capsule.json.gz") || entry.name.endsWith(".capsule.json.gz.enc"))
|
|
5119
|
-
).map((entry) => entry.name).sort();
|
|
5120
|
-
const capsules = [];
|
|
5121
|
-
for (const archiveName of archiveNames) {
|
|
5122
|
-
const archivePath = nodePath.join(capsulesDir, archiveName);
|
|
5123
|
-
const id = archiveName.replace(/\.capsule\.json\.gz\.enc$/, "").replace(/\.capsule\.json\.gz$/, "");
|
|
5124
|
-
const manifestPath = nodePath.join(capsulesDir, `${id}.manifest.json`);
|
|
5125
|
-
let createdAt = null;
|
|
5126
|
-
let pluginVersion = null;
|
|
5127
|
-
let fileCount = null;
|
|
5128
|
-
let description = null;
|
|
5129
|
-
let manifestPathOrNull = manifestPath;
|
|
5130
|
-
try {
|
|
5131
|
-
const manifestStat = await nodeFs.lstat(manifestPath);
|
|
5132
|
-
if (manifestStat.isSymbolicLink() || !manifestStat.isFile()) {
|
|
5133
|
-
capsules.push({
|
|
5134
|
-
id,
|
|
5135
|
-
archivePath,
|
|
5136
|
-
manifestPath: manifestPathOrNull,
|
|
5137
|
-
createdAt,
|
|
5138
|
-
pluginVersion,
|
|
5139
|
-
fileCount,
|
|
5140
|
-
description
|
|
5141
|
-
});
|
|
5142
|
-
continue;
|
|
5143
|
-
}
|
|
5144
|
-
const raw = await nodeFs.readFile(manifestPath, "utf-8");
|
|
5145
|
-
const sidecar = JSON.parse(raw);
|
|
5146
|
-
createdAt = typeof sidecar.createdAt === "string" ? sidecar.createdAt : null;
|
|
5147
|
-
pluginVersion = typeof sidecar.pluginVersion === "string" ? sidecar.pluginVersion : null;
|
|
5148
|
-
fileCount = Array.isArray(sidecar.files) ? sidecar.files.length : null;
|
|
5149
|
-
const capsule = sidecar.capsule;
|
|
5150
|
-
description = capsule && typeof capsule.description === "string" ? capsule.description : null;
|
|
5151
|
-
} catch (err) {
|
|
5152
|
-
const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
|
|
5153
|
-
if (code === "ENOENT") {
|
|
5154
|
-
manifestPathOrNull = null;
|
|
5155
|
-
}
|
|
5156
|
-
}
|
|
5157
|
-
capsules.push({
|
|
5158
|
-
id,
|
|
5159
|
-
archivePath,
|
|
5160
|
-
manifestPath: manifestPathOrNull,
|
|
5161
|
-
createdAt,
|
|
5162
|
-
pluginVersion,
|
|
5163
|
-
fileCount,
|
|
5164
|
-
description
|
|
5165
|
-
});
|
|
5166
|
-
}
|
|
5167
|
-
return { namespace: resolvedNamespace, capsulesDir, capsules };
|
|
5168
|
-
}
|
|
5169
|
-
/**
|
|
5170
|
-
* Manually invoke a single Dreams phase pass (PR 4/4).
|
|
4953
|
+
* Returns `undefined` when NO readable LCM namespace exists for an IMPLICIT
|
|
4954
|
+
* (no explicit `namespace`) raw recall — i.e. a restrictive `default` READ
|
|
4955
|
+
* policy denies the principal `default` AND no overlay/self namespace is
|
|
4956
|
+
* readable. In that case the caller emits NO excerpts rather than throwing
|
|
4957
|
+
* `namespace is not readable: default` (#1505 thread NBHWz): normal recall
|
|
4958
|
+
* still succeeds via `recallNamespacesForPrincipal`, so `disclosure: "raw"`
|
|
4959
|
+
* must degrade gracefully (empty excerpts), never pre-authorize `default`.
|
|
5171
4960
|
*
|
|
5172
|
-
*
|
|
5173
|
-
*
|
|
5174
|
-
*
|
|
4961
|
+
* IMPLICIT-namespace fallback selection derives from the ALREADY
|
|
4962
|
+
* read-authorized recall namespace set (`recallNamespacesForPrincipal` +
|
|
4963
|
+
* `canReadNamespace`) — the principal's self base when it is in the readable
|
|
4964
|
+
* recall set, else `config.defaultNamespace` ONLY when the principal may read
|
|
4965
|
+
* it. It NEVER pre-authorizes `default`. An EXPLICIT `namespace` is still
|
|
4966
|
+
* authorized strictly via `resolveReadableNamespace` (explicit reads must pass
|
|
4967
|
+
* the ACL — no behavior change).
|
|
5175
4968
|
*/
|
|
5176
|
-
|
|
5177
|
-
const
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
4969
|
+
resolveRawExcerptReadNamespace(explicitNamespace, sessionKey, authenticatedPrincipal) {
|
|
4970
|
+
const principal = this.deps.resolveRequestPrincipal(
|
|
4971
|
+
sessionKey,
|
|
4972
|
+
authenticatedPrincipal
|
|
4973
|
+
);
|
|
4974
|
+
const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
|
|
4975
|
+
if (hasExplicitNamespace) {
|
|
4976
|
+
const resolvedNamespace = this.deps.resolveReadableNamespace(
|
|
4977
|
+
explicitNamespace,
|
|
4978
|
+
principal
|
|
5182
4979
|
);
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
4980
|
+
return this.deps.resolveLcmReadNamespace(
|
|
4981
|
+
explicitNamespace,
|
|
4982
|
+
resolvedNamespace,
|
|
4983
|
+
sessionKey,
|
|
4984
|
+
authenticatedPrincipal,
|
|
4985
|
+
"read"
|
|
5188
4986
|
);
|
|
5189
4987
|
}
|
|
5190
|
-
const
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
void 0,
|
|
5194
|
-
options.authenticatedPrincipal
|
|
4988
|
+
const profileReadNamespaces = this.deps.resolveScopeProfileLcmReadNamespaces(
|
|
4989
|
+
sessionKey,
|
|
4990
|
+
authenticatedPrincipal
|
|
5195
4991
|
);
|
|
5196
|
-
|
|
5197
|
-
const
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
}
|
|
5206
|
-
const result2 = await this.deps.orchestrator.runSemanticConsolidationNow({
|
|
5207
|
-
dryRun: false,
|
|
5208
|
-
storage
|
|
5209
|
-
});
|
|
5210
|
-
const itemsProcessed = result2.clusters.reduce(
|
|
5211
|
-
(sum, cluster) => sum + cluster.memories.length,
|
|
5212
|
-
0
|
|
5213
|
-
);
|
|
5214
|
-
return {
|
|
5215
|
-
itemsProcessed,
|
|
5216
|
-
notes: `REM consolidation found ${result2.clustersFound} clusters`
|
|
5217
|
-
};
|
|
5218
|
-
};
|
|
5219
|
-
const governanceRunner = options.phase === "deepSleep" ? async (_opts) => {
|
|
5220
|
-
return this.deps.orchestrator.runDeepSleepGovernanceNow({
|
|
5221
|
-
storage,
|
|
5222
|
-
dryRun: _opts.dryRun
|
|
5223
|
-
});
|
|
5224
|
-
} : void 0;
|
|
5225
|
-
const result = await runDreamsPhase(
|
|
5226
|
-
{ memoryDir, phase: options.phase, dryRun },
|
|
5227
|
-
governanceRunner,
|
|
5228
|
-
phaseRunner
|
|
4992
|
+
if (profileReadNamespaces !== null) return profileReadNamespaces[0];
|
|
4993
|
+
const fallbackNamespace = this.deps.resolveImplicitLcmReadFallbackNamespace(principal);
|
|
4994
|
+
if (fallbackNamespace === void 0) return void 0;
|
|
4995
|
+
return this.deps.resolveLcmReadNamespace(
|
|
4996
|
+
explicitNamespace,
|
|
4997
|
+
fallbackNamespace,
|
|
4998
|
+
sessionKey,
|
|
4999
|
+
authenticatedPrincipal,
|
|
5000
|
+
"read"
|
|
5229
5001
|
);
|
|
5230
|
-
return {
|
|
5231
|
-
phase: result.phase,
|
|
5232
|
-
dryRun: result.dryRun,
|
|
5233
|
-
durationMs: result.durationMs,
|
|
5234
|
-
itemsProcessed: result.itemsProcessed,
|
|
5235
|
-
notes: result.notes
|
|
5236
|
-
};
|
|
5237
5002
|
}
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
const
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
const codingContext2 = request.sessionKey ? this.deps.orchestrator.getCodingContextForSession(request.sessionKey) ?? null : null;
|
|
5255
|
-
const plan = resolveScopePlan({
|
|
5256
|
-
config,
|
|
5257
|
-
sessionKey: request.sessionKey,
|
|
5258
|
-
principalOverride: request.principal,
|
|
5259
|
-
codingContext: codingContext2,
|
|
5260
|
-
namespacesEnabled
|
|
5261
|
-
});
|
|
5262
|
-
sourceNamespace = this.deps.resolveReadableNamespace(
|
|
5263
|
-
plan.baseNamespace,
|
|
5264
|
-
request.principal
|
|
5265
|
-
);
|
|
5003
|
+
resolveLcmReadSessionIds(explicitNamespace, resolvedNamespace, sessionKey, authenticatedPrincipal) {
|
|
5004
|
+
const primary = this.deps.resolveLcmReadSessionKey(
|
|
5005
|
+
explicitNamespace,
|
|
5006
|
+
resolvedNamespace,
|
|
5007
|
+
sessionKey,
|
|
5008
|
+
authenticatedPrincipal,
|
|
5009
|
+
"read"
|
|
5010
|
+
);
|
|
5011
|
+
const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
|
|
5012
|
+
if (hasExplicitNamespace) return [primary];
|
|
5013
|
+
const profileReadNamespaces = this.deps.resolveScopeProfileLcmReadNamespaces(
|
|
5014
|
+
sessionKey,
|
|
5015
|
+
authenticatedPrincipal
|
|
5016
|
+
);
|
|
5017
|
+
if (profileReadNamespaces !== null) {
|
|
5018
|
+
return this.deps.lcmSessionIdsForNamespaces(profileReadNamespaces, sessionKey);
|
|
5266
5019
|
}
|
|
5267
|
-
const
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
}
|
|
5305
|
-
);
|
|
5306
|
-
if (tombstoneBlocked) {
|
|
5307
|
-
throw new Error(
|
|
5308
|
-
"target namespace tombstone-blocked the promoted content (pending_review)"
|
|
5309
|
-
);
|
|
5310
|
-
}
|
|
5311
|
-
return id;
|
|
5312
|
-
}
|
|
5313
|
-
};
|
|
5314
|
-
try {
|
|
5315
|
-
return await promoteMemory({
|
|
5316
|
-
config,
|
|
5317
|
-
sourceMemoryId: request.sourceMemoryId,
|
|
5318
|
-
sourceNamespace,
|
|
5319
|
-
principal: request.principal,
|
|
5320
|
-
targets: request.targets,
|
|
5321
|
-
reason: request.reason,
|
|
5322
|
-
actor: request.principal ?? "admin-console",
|
|
5323
|
-
storage,
|
|
5324
|
-
scopeProfilePlan
|
|
5325
|
-
});
|
|
5326
|
-
} catch (err) {
|
|
5327
|
-
if (err instanceof AdminPromotionError) {
|
|
5328
|
-
throw new EngramAccessInputError(err.message);
|
|
5020
|
+
const principal = this.deps.resolveRequestPrincipal(
|
|
5021
|
+
sessionKey,
|
|
5022
|
+
authenticatedPrincipal
|
|
5023
|
+
);
|
|
5024
|
+
const base = defaultNamespaceForPrincipal(
|
|
5025
|
+
principal,
|
|
5026
|
+
this.deps.orchestrator.config
|
|
5027
|
+
);
|
|
5028
|
+
const overlaid = this.deps.orchestrator.applyCodingNamespaceOverlay(
|
|
5029
|
+
sessionKey,
|
|
5030
|
+
base
|
|
5031
|
+
);
|
|
5032
|
+
if (overlaid === base) return [primary];
|
|
5033
|
+
const selfReadableInRecall = recallNamespacesForPrincipal(
|
|
5034
|
+
principal,
|
|
5035
|
+
this.deps.orchestrator.config
|
|
5036
|
+
).includes(base);
|
|
5037
|
+
if (!selfReadableInRecall) return [primary];
|
|
5038
|
+
const overlay = resolveCodingNamespaceOverlay(
|
|
5039
|
+
this.deps.orchestrator.getCodingContextForSession(sessionKey),
|
|
5040
|
+
this.deps.orchestrator.config.codingMode,
|
|
5041
|
+
this.deps.orchestrator.config.defaultNamespace
|
|
5042
|
+
);
|
|
5043
|
+
const fallbackNamespaces = (overlay?.readFallbacks ?? []).map(
|
|
5044
|
+
(fallback) => combineNamespaces(base, fallback)
|
|
5045
|
+
);
|
|
5046
|
+
const out = [primary];
|
|
5047
|
+
const seen = /* @__PURE__ */ new Set([primary]);
|
|
5048
|
+
for (const ns of fallbackNamespaces) {
|
|
5049
|
+
const key = lcmSessionKeyForNamespace(
|
|
5050
|
+
ns,
|
|
5051
|
+
sessionKey,
|
|
5052
|
+
this.deps.orchestrator.config.defaultNamespace
|
|
5053
|
+
) ?? sessionKey;
|
|
5054
|
+
if (!seen.has(key)) {
|
|
5055
|
+
seen.add(key);
|
|
5056
|
+
out.push(key);
|
|
5329
5057
|
}
|
|
5330
|
-
throw err;
|
|
5331
5058
|
}
|
|
5059
|
+
return out;
|
|
5332
5060
|
}
|
|
5333
5061
|
};
|
|
5334
5062
|
|
|
5335
|
-
// src/access-
|
|
5336
|
-
import
|
|
5337
|
-
import * as
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
function qmdCollectionPathParts(resultPath) {
|
|
5342
|
-
if (!resultPath || path8.isAbsolute(resultPath)) return null;
|
|
5343
|
-
const normalized = resultPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
5344
|
-
const slashIndex = normalized.indexOf("/");
|
|
5345
|
-
if (slashIndex <= 0 || slashIndex >= normalized.length - 1) return null;
|
|
5346
|
-
const collection = normalized.slice(0, slashIndex);
|
|
5347
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(collection)) return null;
|
|
5348
|
-
return {
|
|
5349
|
-
collection,
|
|
5350
|
-
relativePath: normalized.slice(slashIndex + 1)
|
|
5351
|
-
};
|
|
5352
|
-
}
|
|
5353
|
-
function qmdResultPathCandidates(storageDir, resultPath) {
|
|
5354
|
-
const candidates = /* @__PURE__ */ new Set();
|
|
5355
|
-
const storageRoot = path8.resolve(storageDir);
|
|
5356
|
-
const addCandidate = (candidate) => {
|
|
5357
|
-
const resolved = path8.resolve(candidate);
|
|
5358
|
-
if (isPathInsideStorageRoot(storageRoot, resolved)) {
|
|
5359
|
-
candidates.add(resolved);
|
|
5360
|
-
}
|
|
5361
|
-
};
|
|
5362
|
-
const addRelativeCandidates = (relativePath) => {
|
|
5363
|
-
const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
5364
|
-
if (!normalized) return;
|
|
5365
|
-
addCandidate(path8.join(storageRoot, normalized));
|
|
5366
|
-
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
5367
|
-
addCandidate(path8.join(storageRoot, "facts", normalized));
|
|
5368
|
-
}
|
|
5369
|
-
};
|
|
5370
|
-
if (path8.isAbsolute(resultPath)) {
|
|
5371
|
-
addCandidate(resultPath);
|
|
5372
|
-
} else {
|
|
5373
|
-
addRelativeCandidates(resultPath);
|
|
5374
|
-
}
|
|
5375
|
-
return [...candidates];
|
|
5376
|
-
}
|
|
5377
|
-
function storageDirOrNull(storage) {
|
|
5378
|
-
const dir = storage.dir;
|
|
5379
|
-
return typeof dir === "string" && dir.length > 0 ? dir : null;
|
|
5380
|
-
}
|
|
5381
|
-
var QmdResultResolver = class {
|
|
5382
|
-
getConfig;
|
|
5383
|
-
storageFor;
|
|
5384
|
-
storageDirNamespace;
|
|
5385
|
-
qmdCollectionNamespaceFromPrefix;
|
|
5386
|
-
namespaceFromPath;
|
|
5387
|
-
constructor(options) {
|
|
5388
|
-
this.getConfig = options.getConfig;
|
|
5389
|
-
this.storageFor = options.storageFor;
|
|
5390
|
-
this.storageDirNamespace = options.storageDirNamespace;
|
|
5391
|
-
this.qmdCollectionNamespaceFromPrefix = options.qmdCollectionNamespaceFromPrefix;
|
|
5392
|
-
this.namespaceFromPath = options.namespaceFromPath;
|
|
5063
|
+
// src/access-admin-ops-surface.ts
|
|
5064
|
+
import * as nodeFs from "fs/promises";
|
|
5065
|
+
import * as nodePath from "path";
|
|
5066
|
+
var AccessAdminOpsSurface = class {
|
|
5067
|
+
constructor(deps) {
|
|
5068
|
+
this.deps = deps;
|
|
5393
5069
|
}
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
const
|
|
5397
|
-
const
|
|
5398
|
-
const
|
|
5399
|
-
if (
|
|
5400
|
-
const
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
};
|
|
5409
|
-
const
|
|
5410
|
-
|
|
5411
|
-
addStorage(fallbackStorage);
|
|
5412
|
-
}
|
|
5413
|
-
if (recallNamespaces.length > 0) {
|
|
5414
|
-
for (const namespace of recallNamespaces) {
|
|
5415
|
-
try {
|
|
5416
|
-
addStorage(await this.storageFor(namespace));
|
|
5417
|
-
} catch (err) {
|
|
5418
|
-
log.debug("qmd cold result namespace storage lookup skipped", {
|
|
5419
|
-
path: resultPath,
|
|
5420
|
-
namespace,
|
|
5421
|
-
error: err.message
|
|
5422
|
-
});
|
|
5423
|
-
}
|
|
5424
|
-
}
|
|
5425
|
-
}
|
|
5426
|
-
for (const storage of storages) {
|
|
5427
|
-
const storageDir = storageDirOrNull(storage);
|
|
5428
|
-
if (!storageDir) {
|
|
5429
|
-
const memory = await storage.readMemoryByPath(resultPath);
|
|
5430
|
-
if (memory) return memory;
|
|
5431
|
-
continue;
|
|
5432
|
-
}
|
|
5070
|
+
deps;
|
|
5071
|
+
async reviewQueue(runId, namespace, principal) {
|
|
5072
|
+
const resolvedNamespace = this.deps.resolveReadableNamespace(namespace, principal);
|
|
5073
|
+
const storage = await this.deps.orchestrator.getStorage(resolvedNamespace);
|
|
5074
|
+
const projected = await storage.getProjectedGovernanceRecord();
|
|
5075
|
+
if (projected && (!runId || projected.runId === runId.trim())) {
|
|
5076
|
+
const projectedAppliedActions = projected.appliedActionRows.map((row) => ({
|
|
5077
|
+
action: row.action,
|
|
5078
|
+
memoryId: row.memoryId,
|
|
5079
|
+
reasonCode: row.reasonCode,
|
|
5080
|
+
beforeStatus: row.beforeStatus,
|
|
5081
|
+
afterStatus: row.afterStatus,
|
|
5082
|
+
originalPath: row.originalPath,
|
|
5083
|
+
currentPath: row.currentPath
|
|
5084
|
+
}));
|
|
5085
|
+
const projectedProposedActions = await buildProjectedGovernanceProposedActions(storage, projected);
|
|
5086
|
+
const projectedArtifact = await (async () => {
|
|
5433
5087
|
try {
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
parts.relativePath
|
|
5438
|
-
)) {
|
|
5439
|
-
const memory = await storage.readMemoryByPath(candidate);
|
|
5440
|
-
if (memory) return memory;
|
|
5441
|
-
}
|
|
5442
|
-
} catch (err) {
|
|
5443
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
5444
|
-
log.debug("qmd cold result path lookup failed open", {
|
|
5445
|
-
path: resultPath,
|
|
5446
|
-
collection: coldCollection,
|
|
5447
|
-
error: err.message
|
|
5448
|
-
});
|
|
5088
|
+
return await readMemoryGovernanceRunArtifact(storage.dir, projected.runId);
|
|
5089
|
+
} catch {
|
|
5090
|
+
return null;
|
|
5449
5091
|
}
|
|
5450
|
-
}
|
|
5451
|
-
|
|
5092
|
+
})();
|
|
5093
|
+
const metrics = projected.metrics;
|
|
5094
|
+
const fallbackTransitionReport = {
|
|
5095
|
+
proposed: groupActionsByStatus(projectedProposedActions),
|
|
5096
|
+
applied: groupActionsByStatus(projectedAppliedActions)
|
|
5097
|
+
};
|
|
5098
|
+
const transitionReport = projectedArtifact?.transitionReport ? {
|
|
5099
|
+
proposed: hasGroupedGovernanceActions(projectedArtifact.transitionReport.proposed) || projectedProposedActions.length === 0 ? projectedArtifact.transitionReport.proposed : fallbackTransitionReport.proposed,
|
|
5100
|
+
applied: hasGroupedGovernanceActions(projectedArtifact.transitionReport.applied) || projectedAppliedActions.length === 0 ? projectedArtifact.transitionReport.applied : fallbackTransitionReport.applied
|
|
5101
|
+
} : fallbackTransitionReport;
|
|
5102
|
+
const qualityScore = projectedArtifact?.qualityScore ?? metrics?.qualityScore ?? buildQualityScore(metrics?.reviewReasons ?? {
|
|
5103
|
+
exact_duplicate: 0,
|
|
5104
|
+
semantic_duplicate_candidate: 0,
|
|
5105
|
+
disputed_memory: 0,
|
|
5106
|
+
speculative_low_confidence: 0,
|
|
5107
|
+
archive_candidate: 0,
|
|
5108
|
+
explicit_capture_review: 0,
|
|
5109
|
+
malformed_import: 0
|
|
5110
|
+
});
|
|
5111
|
+
const effectiveMetrics = metrics ? { ...metrics, qualityScore: metrics.qualityScore ?? qualityScore } : metrics;
|
|
5112
|
+
return {
|
|
5113
|
+
found: true,
|
|
5114
|
+
namespace: resolvedNamespace,
|
|
5115
|
+
runId: projected.runId,
|
|
5116
|
+
summary: projected.summary,
|
|
5117
|
+
metrics: effectiveMetrics,
|
|
5118
|
+
qualityScore,
|
|
5119
|
+
reviewQueue: projected.reviewQueueRows.map((row) => ({
|
|
5120
|
+
entryId: row.entryId,
|
|
5121
|
+
memoryId: row.memoryId,
|
|
5122
|
+
path: row.path,
|
|
5123
|
+
reasonCode: row.reasonCode,
|
|
5124
|
+
severity: row.severity,
|
|
5125
|
+
suggestedAction: row.suggestedAction,
|
|
5126
|
+
suggestedStatus: row.suggestedStatus,
|
|
5127
|
+
relatedMemoryIds: row.relatedMemoryIds
|
|
5128
|
+
})),
|
|
5129
|
+
appliedActions: projectedAppliedActions,
|
|
5130
|
+
transitionReport,
|
|
5131
|
+
report: projected.report
|
|
5132
|
+
};
|
|
5452
5133
|
}
|
|
5453
|
-
const
|
|
5454
|
-
if (
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
}
|
|
5134
|
+
const resolvedRunId = runId?.trim() || (await listMemoryGovernanceRuns(storage.dir))[0];
|
|
5135
|
+
if (!resolvedRunId) return { found: false, namespace: resolvedNamespace };
|
|
5136
|
+
const artifact = await readMemoryGovernanceRunArtifact(storage.dir, resolvedRunId);
|
|
5137
|
+
return {
|
|
5138
|
+
found: true,
|
|
5139
|
+
namespace: resolvedNamespace,
|
|
5140
|
+
runId: resolvedRunId,
|
|
5141
|
+
summary: artifact.summary,
|
|
5142
|
+
metrics: artifact.metrics,
|
|
5143
|
+
qualityScore: artifact.qualityScore,
|
|
5144
|
+
reviewQueue: artifact.reviewQueue,
|
|
5145
|
+
appliedActions: artifact.appliedActions,
|
|
5146
|
+
transitionReport: artifact.transitionReport,
|
|
5147
|
+
report: artifact.report
|
|
5148
|
+
};
|
|
5149
|
+
}
|
|
5150
|
+
async governanceRun(request, principal) {
|
|
5151
|
+
const deepSleep = this.deps.orchestrator.config.dreamsPhases.deepSleep;
|
|
5152
|
+
if (deepSleep.enabled === false && deepSleep.enabledExplicitlySet === true) {
|
|
5153
|
+
throw new Error("memory governance is disabled by dreams.phases.deepSleep.enabled=false");
|
|
5474
5154
|
}
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5155
|
+
const resolvedNamespace = this.deps.writableNamespaceFor(
|
|
5156
|
+
request.namespace,
|
|
5157
|
+
void 0,
|
|
5158
|
+
request.authenticatedPrincipal ?? principal
|
|
5159
|
+
);
|
|
5160
|
+
const storage = await this.deps.orchestrator.getStorage(resolvedNamespace);
|
|
5161
|
+
const mode = request.mode === "apply" ? "apply" : "shadow";
|
|
5162
|
+
const boundedBatchSize = typeof request.batchSize === "number" && Number.isFinite(request.batchSize) ? Math.max(1, Math.floor(request.batchSize)) : void 0;
|
|
5163
|
+
const result = await runMemoryGovernance({
|
|
5164
|
+
memoryDir: storage.dir,
|
|
5165
|
+
mode,
|
|
5166
|
+
recentDays: typeof request.recentDays === "number" && Number.isFinite(request.recentDays) ? Math.max(1, Math.floor(request.recentDays)) : void 0,
|
|
5167
|
+
maxMemories: typeof request.maxMemories === "number" && Number.isFinite(request.maxMemories) ? Math.max(1, Math.floor(request.maxMemories)) : void 0,
|
|
5168
|
+
batchSize: boundedBatchSize
|
|
5169
|
+
});
|
|
5170
|
+
if (mode === "apply") {
|
|
5171
|
+
try {
|
|
5172
|
+
await this.deps.orchestrator.processEntitySynthesisQueue(
|
|
5173
|
+
resolvedNamespace,
|
|
5174
|
+
Math.min(boundedBatchSize ?? 5, 5)
|
|
5175
|
+
);
|
|
5176
|
+
} catch (error) {
|
|
5177
|
+
log.debug(`governanceRun: entity synthesis refresh failed after governance apply: ${error}`);
|
|
5491
5178
|
}
|
|
5492
|
-
return null;
|
|
5493
5179
|
}
|
|
5494
|
-
|
|
5495
|
-
|
|
5180
|
+
return {
|
|
5181
|
+
namespace: resolvedNamespace,
|
|
5182
|
+
runId: result.runId,
|
|
5183
|
+
traceId: result.traceId,
|
|
5184
|
+
mode: result.mode,
|
|
5185
|
+
reviewQueueCount: result.reviewQueue.length,
|
|
5186
|
+
proposedActionCount: result.proposedActions.length,
|
|
5187
|
+
appliedActionCount: result.appliedActions.length,
|
|
5188
|
+
summaryPath: result.summaryPath,
|
|
5189
|
+
reportPath: result.reportPath
|
|
5190
|
+
};
|
|
5191
|
+
}
|
|
5192
|
+
async conversationIndexUpdate(request = {}) {
|
|
5193
|
+
if (!resolveIndexingCapabilities(this.deps.orchestrator.config).conversationIndex) {
|
|
5194
|
+
return {
|
|
5195
|
+
enabled: false,
|
|
5196
|
+
sessions: 0,
|
|
5197
|
+
chunks: 0,
|
|
5198
|
+
skipped: 0,
|
|
5199
|
+
skippedSessionKeys: [],
|
|
5200
|
+
embeddedRuns: 0,
|
|
5201
|
+
reason: "disabled"
|
|
5202
|
+
};
|
|
5496
5203
|
}
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5204
|
+
const hours = typeof request.hours === "number" && Number.isFinite(request.hours) ? Math.max(1, Math.floor(request.hours)) : 24;
|
|
5205
|
+
let sessionKey;
|
|
5206
|
+
if (request.sessionKey !== void 0) {
|
|
5207
|
+
if (typeof request.sessionKey !== "string" || request.sessionKey.trim().length === 0) {
|
|
5208
|
+
throw new EngramAccessInputError("sessionKey must be a non-empty string when provided");
|
|
5209
|
+
}
|
|
5210
|
+
sessionKey = request.sessionKey.trim();
|
|
5503
5211
|
}
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
fallbackStorage,
|
|
5510
|
-
recallNamespaces
|
|
5511
|
-
);
|
|
5512
|
-
if (!memory) return null;
|
|
5513
|
-
let ownerNamespace = null;
|
|
5514
|
-
if (path8.isAbsolute(memory.path)) {
|
|
5515
|
-
const ownerStorage = await this.storageForAbsoluteQmdResultPath(
|
|
5516
|
-
memory.path,
|
|
5517
|
-
fallbackStorage,
|
|
5518
|
-
recallNamespaces
|
|
5212
|
+
if (sessionKey) {
|
|
5213
|
+
const result = await this.deps.orchestrator.updateConversationIndex(
|
|
5214
|
+
sessionKey,
|
|
5215
|
+
hours,
|
|
5216
|
+
{ embed: request.embed }
|
|
5519
5217
|
);
|
|
5520
|
-
|
|
5521
|
-
|
|
5218
|
+
return {
|
|
5219
|
+
enabled: true,
|
|
5220
|
+
sessionKey,
|
|
5221
|
+
sessions: 1,
|
|
5222
|
+
chunks: result.chunks,
|
|
5223
|
+
skipped: result.skipped ? 1 : 0,
|
|
5224
|
+
skippedSessionKeys: result.skipped ? [sessionKey] : [],
|
|
5225
|
+
embeddedRuns: result.embedded ? 1 : 0,
|
|
5226
|
+
reason: result.reason,
|
|
5227
|
+
retryAfterMs: result.retryAfterMs
|
|
5228
|
+
};
|
|
5522
5229
|
}
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5230
|
+
const sessionKeys = await this.deps.orchestrator.transcript.listSessionKeys();
|
|
5231
|
+
let chunks = 0;
|
|
5232
|
+
let skipped = 0;
|
|
5233
|
+
const skippedSessionKeys = [];
|
|
5234
|
+
let embeddedRuns = 0;
|
|
5235
|
+
for (const sessionKey2 of sessionKeys) {
|
|
5236
|
+
const result = await this.deps.orchestrator.updateConversationIndex(
|
|
5237
|
+
sessionKey2,
|
|
5238
|
+
hours,
|
|
5239
|
+
{ embed: request.embed }
|
|
5240
|
+
);
|
|
5241
|
+
chunks += result.chunks;
|
|
5242
|
+
if (result.skipped) {
|
|
5243
|
+
skipped += 1;
|
|
5244
|
+
skippedSessionKeys.push(sessionKey2);
|
|
5245
|
+
}
|
|
5246
|
+
if (result.embedded) {
|
|
5247
|
+
embeddedRuns += 1;
|
|
5248
|
+
}
|
|
5526
5249
|
}
|
|
5527
5250
|
return {
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
}
|
|
5251
|
+
enabled: true,
|
|
5252
|
+
sessions: sessionKeys.length,
|
|
5253
|
+
chunks,
|
|
5254
|
+
skipped,
|
|
5255
|
+
skippedSessionKeys,
|
|
5256
|
+
embeddedRuns
|
|
5535
5257
|
};
|
|
5536
5258
|
}
|
|
5537
|
-
async
|
|
5538
|
-
const
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
const maybeAddStorage = (storage, namespace) => {
|
|
5546
|
-
const storageDir = storageDirOrNull(storage);
|
|
5547
|
-
if (!storageDir) return;
|
|
5548
|
-
const candidateRoot = path8.resolve(storageDir);
|
|
5549
|
-
if (seenDirs.has(candidateRoot)) return;
|
|
5550
|
-
if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
|
|
5551
|
-
if (candidateRoot === memoryRoot && isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
5552
|
-
return;
|
|
5553
|
-
}
|
|
5554
|
-
seenDirs.add(candidateRoot);
|
|
5555
|
-
matches.push({ storage, dir: candidateRoot, namespace });
|
|
5556
|
-
};
|
|
5557
|
-
const fallbackNamespace = fallbackStorageDir !== null ? this.storageDirNamespace(fallbackStorageDir) : config.defaultNamespace;
|
|
5558
|
-
maybeAddStorage(fallbackStorage, fallbackNamespace);
|
|
5559
|
-
const candidateNamespaces = /* @__PURE__ */ new Set();
|
|
5560
|
-
candidateNamespaces.add(config.defaultNamespace);
|
|
5561
|
-
candidateNamespaces.add(config.sharedNamespace);
|
|
5562
|
-
for (const ns of recallNamespaces) {
|
|
5563
|
-
candidateNamespaces.add(ns);
|
|
5259
|
+
async profilingReport(request = {}) {
|
|
5260
|
+
const profiler = this.deps.orchestrator.profiler;
|
|
5261
|
+
if (!profiler.isEnabled) {
|
|
5262
|
+
return {
|
|
5263
|
+
enabled: false,
|
|
5264
|
+
reason: "disabled",
|
|
5265
|
+
message: "Profiling is disabled. Set profilingEnabled: true in your plugin config to enable."
|
|
5266
|
+
};
|
|
5564
5267
|
}
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
if (namespaceSegment) {
|
|
5569
|
-
candidateNamespaces.add(
|
|
5570
|
-
namespaceIdentityFromToken(namespaceSegment) ?? namespaceSegment
|
|
5571
|
-
);
|
|
5572
|
-
}
|
|
5268
|
+
const format = request.format ?? "ascii";
|
|
5269
|
+
if (format !== "ascii" && format !== "json") {
|
|
5270
|
+
throw new EngramAccessInputError("format must be one of: ascii, json");
|
|
5573
5271
|
}
|
|
5574
|
-
|
|
5575
|
-
|
|
5272
|
+
const limit = request.limit ?? 5;
|
|
5273
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 20) {
|
|
5274
|
+
throw new EngramAccessInputError("limit must be an integer between 1 and 20");
|
|
5576
5275
|
}
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5276
|
+
const traces = profiler.getRecentTraces(limit);
|
|
5277
|
+
const stats = profiler.getStats();
|
|
5278
|
+
const bottleneck = profiler.identifyBottleneck();
|
|
5279
|
+
if (format === "json") {
|
|
5280
|
+
return {
|
|
5281
|
+
enabled: true,
|
|
5282
|
+
format,
|
|
5283
|
+
traces,
|
|
5284
|
+
stats,
|
|
5285
|
+
bottleneck
|
|
5286
|
+
};
|
|
5287
|
+
}
|
|
5288
|
+
const lines = [];
|
|
5289
|
+
lines.push("Engram Profiling Report");
|
|
5290
|
+
lines.push("=".repeat(60));
|
|
5291
|
+
lines.push("");
|
|
5292
|
+
const allBuckets = [
|
|
5293
|
+
["byKind", stats.byKind],
|
|
5294
|
+
["bySpan", stats.bySpan]
|
|
5295
|
+
];
|
|
5296
|
+
const hasStats = allBuckets.some(([, entries]) => Object.keys(entries).length > 0);
|
|
5297
|
+
if (hasStats) {
|
|
5298
|
+
lines.push("Aggregate Stats (all retained traces):");
|
|
5299
|
+
for (const [bucket, entries] of allBuckets) {
|
|
5300
|
+
for (const [key, summary] of Object.entries(entries)) {
|
|
5301
|
+
lines.push(
|
|
5302
|
+
` ${bucket}/${key}: avg=${summary.avgMs}ms p50=${summary.p50Ms}ms p95=${summary.p95Ms}ms max=${summary.maxMs}ms (n=${summary.count})`
|
|
5303
|
+
);
|
|
5304
|
+
}
|
|
5583
5305
|
}
|
|
5306
|
+
lines.push("");
|
|
5584
5307
|
}
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
}
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
deps;
|
|
5596
|
-
async buildRecallResponseFromXraySnapshot(options) {
|
|
5597
|
-
const memoryIds = options.snapshot.results.map((result) => result.memoryId);
|
|
5598
|
-
const resultPaths = options.snapshot.results.map((result) => result.path);
|
|
5599
|
-
const namespace = options.snapshot.namespace ? this.deps.resolveNamespace(options.snapshot.namespace) : this.deps.orchestrator.config.defaultNamespace;
|
|
5600
|
-
const sourcesUsed = Array.from(
|
|
5601
|
-
new Set(options.snapshot.results.map((result) => result.servedBy))
|
|
5602
|
-
);
|
|
5603
|
-
const snapshotForSerialization = {
|
|
5604
|
-
sessionKey: options.sessionKey ?? "",
|
|
5605
|
-
recordedAt: new Date(options.snapshot.capturedAt).toISOString(),
|
|
5606
|
-
queryHash: createHash4("sha256").update(options.query).digest("hex"),
|
|
5607
|
-
queryLen: options.query.length,
|
|
5608
|
-
memoryIds,
|
|
5609
|
-
namespace,
|
|
5610
|
-
recallNamespaces: [namespace],
|
|
5611
|
-
traceId: options.snapshot.traceId,
|
|
5612
|
-
plannerMode: options.normalizedMode,
|
|
5613
|
-
requestedMode: options.requestedMode && options.requestedMode !== "auto" ? options.requestedMode : void 0,
|
|
5614
|
-
sourcesUsed,
|
|
5615
|
-
budgetsApplied: {
|
|
5616
|
-
appliedTopK: memoryIds.length,
|
|
5617
|
-
recallBudgetChars: options.snapshot.budget.chars,
|
|
5618
|
-
maxMemoryTokens: this.deps.orchestrator.config.maxMemoryTokens,
|
|
5619
|
-
finalContextChars: options.snapshot.budget.used
|
|
5620
|
-
},
|
|
5621
|
-
latencyMs: Date.now() - options.startedAt,
|
|
5622
|
-
resultPaths
|
|
5623
|
-
};
|
|
5624
|
-
const results = await this.deps.serializeRecallResults(
|
|
5625
|
-
snapshotForSerialization,
|
|
5626
|
-
options.disclosure,
|
|
5627
|
-
{
|
|
5628
|
-
query: options.query,
|
|
5629
|
-
...options.sessionKey ? { sessionKey: options.sessionKey } : {},
|
|
5630
|
-
...options.rawExcerptNamespace ? { rawExcerptNamespace: options.rawExcerptNamespace } : {},
|
|
5631
|
-
...options.rawExcerptSessionIds !== void 0 ? { rawExcerptSessionIds: options.rawExcerptSessionIds } : {},
|
|
5632
|
-
...options.rawExcerptsSuppressed ? { rawExcerptsSuppressed: options.rawExcerptsSuppressed } : {}
|
|
5308
|
+
if (bottleneck) {
|
|
5309
|
+
lines.push(`Bottleneck: ${bottleneck}`);
|
|
5310
|
+
lines.push("");
|
|
5311
|
+
}
|
|
5312
|
+
if (traces.length === 0) {
|
|
5313
|
+
lines.push("No traces recorded yet. Trigger a recall or extraction to see timing data.");
|
|
5314
|
+
} else {
|
|
5315
|
+
for (const trace of traces) {
|
|
5316
|
+
lines.push(formatProfileTraceAscii(trace));
|
|
5317
|
+
lines.push("");
|
|
5633
5318
|
}
|
|
5634
|
-
|
|
5635
|
-
const context = results.map((result) => {
|
|
5636
|
-
const content = typeof result.content === "string" && result.content.length > 0 ? result.content : "";
|
|
5637
|
-
return content || result.preview;
|
|
5638
|
-
}).filter((text) => text.length > 0).join("\n\n");
|
|
5319
|
+
}
|
|
5639
5320
|
return {
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
context,
|
|
5644
|
-
count: memoryIds.length,
|
|
5645
|
-
memoryIds,
|
|
5646
|
-
results,
|
|
5647
|
-
recordedAt: snapshotForSerialization.recordedAt,
|
|
5648
|
-
traceId: options.snapshot.traceId,
|
|
5649
|
-
plannerMode: options.normalizedMode,
|
|
5650
|
-
fallbackUsed: sourcesUsed.some((source) => source !== "hybrid"),
|
|
5651
|
-
sourcesUsed,
|
|
5652
|
-
disclosure: options.disclosure,
|
|
5653
|
-
budgetsApplied: snapshotForSerialization.budgetsApplied,
|
|
5654
|
-
latencyMs: snapshotForSerialization.latencyMs
|
|
5321
|
+
enabled: true,
|
|
5322
|
+
format,
|
|
5323
|
+
report: lines.join("\n")
|
|
5655
5324
|
};
|
|
5656
5325
|
}
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
].filter((ns) => typeof ns === "string" && ns.length > 0)
|
|
5671
|
-
)
|
|
5326
|
+
/**
|
|
5327
|
+
* Read-only graph snapshot for the admin pane (issue #691 PR 2/5).
|
|
5328
|
+
*
|
|
5329
|
+
* Reads adjacency from the JSONL edge store written by `GraphIndex` and
|
|
5330
|
+
* resolves node metadata via the namespaced storage manager. Namespace
|
|
5331
|
+
* resolution mirrors the read-side path used by `recall` /
|
|
5332
|
+
* `procedureStats`, so multi-principal deployments can't leak edges from
|
|
5333
|
+
* a peer namespace (CLAUDE.md rule 42).
|
|
5334
|
+
*/
|
|
5335
|
+
async graphSnapshot(request, authenticatedPrincipal) {
|
|
5336
|
+
const namespace = this.deps.resolveReadableNamespace(
|
|
5337
|
+
request.namespace,
|
|
5338
|
+
authenticatedPrincipal
|
|
5672
5339
|
);
|
|
5673
|
-
const
|
|
5674
|
-
const
|
|
5675
|
-
const
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5340
|
+
const storage = await this.deps.orchestrator.getStorage(namespace);
|
|
5341
|
+
const cfg = this.deps.orchestrator.config;
|
|
5342
|
+
const graphCaps = resolveGraphConstructionCapabilities(cfg);
|
|
5343
|
+
let namespaceRootReal;
|
|
5344
|
+
try {
|
|
5345
|
+
namespaceRootReal = await nodeFs.realpath(storage.dir);
|
|
5346
|
+
} catch {
|
|
5347
|
+
namespaceRootReal = nodePath.resolve(storage.dir);
|
|
5348
|
+
}
|
|
5349
|
+
const namespaceRootWithSep = namespaceRootReal.endsWith(nodePath.sep) ? namespaceRootReal : namespaceRootReal + nodePath.sep;
|
|
5350
|
+
const loadNode = async (relPath) => {
|
|
5351
|
+
if (nodePath.isAbsolute(relPath)) {
|
|
5352
|
+
log.warn(
|
|
5353
|
+
`graphSnapshot: rejected absolute edge endpoint (len=${relPath.length}) outside namespace root`
|
|
5354
|
+
);
|
|
5355
|
+
return null;
|
|
5685
5356
|
}
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
if (parts && parts.collection === coldCollection) {
|
|
5692
|
-
const storages = [];
|
|
5693
|
-
const seenStorageDirs = /* @__PURE__ */ new Set();
|
|
5694
|
-
const addStorage = (candidateStorage) => {
|
|
5695
|
-
const candidateDir = nodePath2.resolve(candidateStorage.dir);
|
|
5696
|
-
if (seenStorageDirs.has(candidateDir)) return;
|
|
5697
|
-
seenStorageDirs.add(candidateDir);
|
|
5698
|
-
storages.push({ storage: candidateStorage, dir: candidateDir });
|
|
5699
|
-
};
|
|
5700
|
-
addStorage(storage);
|
|
5701
|
-
for (const recallNamespace of recallNamespaces) {
|
|
5702
|
-
try {
|
|
5703
|
-
addStorage(await this.deps.orchestrator.getStorage(recallNamespace));
|
|
5704
|
-
} catch {
|
|
5705
|
-
continue;
|
|
5706
|
-
}
|
|
5707
|
-
}
|
|
5708
|
-
for (const candidateStorage of storages) {
|
|
5709
|
-
try {
|
|
5710
|
-
const coldRoot = nodePath2.join(candidateStorage.dir, "cold");
|
|
5711
|
-
for (const candidatePath of qmdResultPathCandidates(
|
|
5712
|
-
coldRoot,
|
|
5713
|
-
parts.relativePath
|
|
5714
|
-
)) {
|
|
5715
|
-
const memory = await candidateStorage.storage.readMemoryByPath(candidatePath);
|
|
5716
|
-
if (memory) return { memory, baseDir: candidateStorage.dir };
|
|
5717
|
-
}
|
|
5718
|
-
} catch (err) {
|
|
5719
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
5720
|
-
continue;
|
|
5721
|
-
}
|
|
5722
|
-
}
|
|
5357
|
+
const candidate = nodePath.resolve(namespaceRootReal, relPath);
|
|
5358
|
+
if (candidate !== namespaceRootReal && !candidate.startsWith(namespaceRootWithSep)) {
|
|
5359
|
+
log.warn(
|
|
5360
|
+
`graphSnapshot: rejected traversing edge endpoint (len=${relPath.length}) outside namespace root`
|
|
5361
|
+
);
|
|
5723
5362
|
return null;
|
|
5724
5363
|
}
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
collectionStorage.dir,
|
|
5731
|
-
parts.relativePath
|
|
5732
|
-
)) {
|
|
5733
|
-
const memory = await collectionStorage.readMemoryByPath(candidate);
|
|
5734
|
-
if (memory) return { memory, baseDir: collectionStorage.dir };
|
|
5735
|
-
}
|
|
5736
|
-
return null;
|
|
5737
|
-
} catch (err) {
|
|
5738
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
5739
|
-
return null;
|
|
5740
|
-
}
|
|
5364
|
+
let canonical;
|
|
5365
|
+
try {
|
|
5366
|
+
canonical = await nodeFs.realpath(candidate);
|
|
5367
|
+
} catch {
|
|
5368
|
+
canonical = candidate;
|
|
5741
5369
|
}
|
|
5742
|
-
if (
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
namespace,
|
|
5746
|
-
recallNamespaces
|
|
5370
|
+
if (canonical !== namespaceRootReal && !canonical.startsWith(namespaceRootWithSep)) {
|
|
5371
|
+
log.warn(
|
|
5372
|
+
`graphSnapshot: rejected symlinked edge endpoint (len=${relPath.length}) that resolved outside namespace root`
|
|
5747
5373
|
);
|
|
5748
|
-
if (!ownerStorage) return null;
|
|
5749
|
-
for (const candidate of qmdResultPathCandidates(
|
|
5750
|
-
ownerStorage.dir,
|
|
5751
|
-
memoryPath
|
|
5752
|
-
)) {
|
|
5753
|
-
const memory = await ownerStorage.storage.readMemoryByPath(candidate);
|
|
5754
|
-
if (memory) return { memory, baseDir: ownerStorage.dir };
|
|
5755
|
-
}
|
|
5756
5374
|
return null;
|
|
5757
5375
|
}
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5376
|
+
const memory = await storage.readMemoryByPath(canonical);
|
|
5377
|
+
if (!memory) return null;
|
|
5378
|
+
const fm = memory.frontmatter;
|
|
5379
|
+
return {
|
|
5380
|
+
category: fm.category ?? "unknown",
|
|
5381
|
+
label: fm.id ?? nodePath.basename(canonical, nodePath.extname(canonical)),
|
|
5382
|
+
updated: fm.updated
|
|
5383
|
+
};
|
|
5763
5384
|
};
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
}
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
for (const memoryPath of snapshot.resultPaths ?? []) {
|
|
5780
|
-
if (!memoryPath || seen.has(memoryPath)) continue;
|
|
5781
|
-
const resolved = await readResultPath(memoryPath);
|
|
5782
|
-
if (!resolved) continue;
|
|
5783
|
-
const { memory, baseDir } = resolved;
|
|
5784
|
-
seen.add(memoryPath);
|
|
5785
|
-
results.push(
|
|
5786
|
-
this.deps.serializeMemorySummary(
|
|
5787
|
-
memory,
|
|
5788
|
-
baseDir,
|
|
5789
|
-
disclosure,
|
|
5790
|
-
// Attach the (possibly empty) raw excerpts to the first raw
|
|
5791
|
-
// result; subsequent results do not duplicate the array.
|
|
5792
|
-
results.length === 0 ? rawExcerpts : void 0
|
|
5793
|
-
)
|
|
5794
|
-
);
|
|
5795
|
-
}
|
|
5796
|
-
if (results.length > 0) return results;
|
|
5797
|
-
for (const memoryId of snapshot.memoryIds) {
|
|
5798
|
-
const memory = await storage.getMemoryById(memoryId);
|
|
5799
|
-
if (!memory || seen.has(memory.path)) continue;
|
|
5800
|
-
seen.add(memory.path);
|
|
5801
|
-
results.push(
|
|
5802
|
-
this.deps.serializeMemorySummary(
|
|
5803
|
-
memory,
|
|
5804
|
-
storageDir,
|
|
5805
|
-
disclosure,
|
|
5806
|
-
results.length === 0 ? rawExcerpts : void 0
|
|
5807
|
-
)
|
|
5808
|
-
);
|
|
5809
|
-
}
|
|
5810
|
-
return results;
|
|
5385
|
+
return buildGraphSnapshot({
|
|
5386
|
+
memoryDir: namespaceRootReal,
|
|
5387
|
+
graphConfig: {
|
|
5388
|
+
entityGraph: graphCaps.entityGraph,
|
|
5389
|
+
timeGraph: graphCaps.timeGraph,
|
|
5390
|
+
causalGraph: graphCaps.causalGraph
|
|
5391
|
+
},
|
|
5392
|
+
request: {
|
|
5393
|
+
limit: request.limit,
|
|
5394
|
+
since: request.since,
|
|
5395
|
+
focusNodeId: request.focusNodeId,
|
|
5396
|
+
categories: request.categories
|
|
5397
|
+
},
|
|
5398
|
+
loadNode
|
|
5399
|
+
});
|
|
5811
5400
|
}
|
|
5812
5401
|
/**
|
|
5813
|
-
*
|
|
5814
|
-
* "raw"` recalls (issue #677 PR 2/4). Returns `null` for non-raw recall
|
|
5815
|
-
* depths, an empty array when LCM is disabled / not initialized / has no
|
|
5816
|
-
* matches, and an array of LCM-side excerpts otherwise. Errors are
|
|
5817
|
-
* swallowed and treated as "no excerpts" so a failing LCM never breaks
|
|
5818
|
-
* the recall response.
|
|
5402
|
+
* List capsule archives in the namespace-scoped capsule store.
|
|
5819
5403
|
*
|
|
5820
|
-
*
|
|
5821
|
-
*
|
|
5822
|
-
*
|
|
5823
|
-
* non-default namespaces miss their own excerpts.
|
|
5404
|
+
* MCP uses this access-layer method instead of reading arbitrary paths so
|
|
5405
|
+
* capsule discovery remains bound to the same namespace ACLs as export and
|
|
5406
|
+
* import.
|
|
5824
5407
|
*/
|
|
5825
|
-
async
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
if (!lcm || !lcm.enabled) return [];
|
|
5408
|
+
async capsuleList(options) {
|
|
5409
|
+
const resolvedNamespace = this.deps.resolveReadableNamespace(options?.namespace, options?.principal);
|
|
5410
|
+
const storage = await this.deps.orchestrator.getStorage(resolvedNamespace);
|
|
5411
|
+
const capsulesDir = defaultCapsulesDir(storage.dir);
|
|
5412
|
+
let dirEntries;
|
|
5831
5413
|
try {
|
|
5832
|
-
const
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
const seenRows = /* @__PURE__ */ new Set();
|
|
5836
|
-
const excerpts = [];
|
|
5837
|
-
const settledRows = await Promise.allSettled(
|
|
5838
|
-
lcmSessionIds.map(
|
|
5839
|
-
async (lcmSessionKey) => lcm.searchContextFull(context.query, limit, lcmSessionKey)
|
|
5840
|
-
)
|
|
5841
|
-
);
|
|
5842
|
-
for (const result of settledRows) {
|
|
5843
|
-
if (excerpts.length >= limit) break;
|
|
5844
|
-
if (result.status !== "fulfilled") continue;
|
|
5845
|
-
for (const r of result.value) {
|
|
5846
|
-
const dedupeKey = `${r.session_id} ${r.turn_index}`;
|
|
5847
|
-
if (seenRows.has(dedupeKey)) continue;
|
|
5848
|
-
seenRows.add(dedupeKey);
|
|
5849
|
-
excerpts.push({
|
|
5850
|
-
turnIndex: r.turn_index,
|
|
5851
|
-
role: r.role,
|
|
5852
|
-
content: r.content,
|
|
5853
|
-
sessionId: r.session_id
|
|
5854
|
-
});
|
|
5855
|
-
if (excerpts.length >= limit) break;
|
|
5856
|
-
}
|
|
5414
|
+
const capsulesDirStat = await nodeFs.lstat(capsulesDir);
|
|
5415
|
+
if (capsulesDirStat.isSymbolicLink()) {
|
|
5416
|
+
throw new EngramAccessInputError("capsule list failed: capsule store directory must not be a symlink");
|
|
5857
5417
|
}
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
return [];
|
|
5861
|
-
}
|
|
5862
|
-
}
|
|
5863
|
-
async executeRecall(request) {
|
|
5864
|
-
const query = request.query;
|
|
5865
|
-
const callerProvidedDisclosure = request.disclosure !== void 0 && request.disclosure !== null;
|
|
5866
|
-
const requestedDisclosure = (() => {
|
|
5867
|
-
if (!callerProvidedDisclosure) {
|
|
5868
|
-
return DEFAULT_RECALL_DISCLOSURE;
|
|
5418
|
+
if (!capsulesDirStat.isDirectory()) {
|
|
5419
|
+
throw new EngramAccessInputError("capsule list failed: capsule store path must be a directory");
|
|
5869
5420
|
}
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5421
|
+
dirEntries = await nodeFs.readdir(capsulesDir, { withFileTypes: true });
|
|
5422
|
+
} catch (err) {
|
|
5423
|
+
const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
|
|
5424
|
+
if (code === "ENOENT") {
|
|
5425
|
+
return { namespace: resolvedNamespace, capsulesDir, capsules: [] };
|
|
5874
5426
|
}
|
|
5875
|
-
|
|
5876
|
-
})();
|
|
5877
|
-
if (request.codingContext !== void 0 && request.sessionKey) {
|
|
5878
|
-
this.deps.setCodingContext({
|
|
5879
|
-
sessionKey: request.sessionKey,
|
|
5880
|
-
codingContext: request.codingContext
|
|
5881
|
-
});
|
|
5427
|
+
throw err;
|
|
5882
5428
|
}
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5429
|
+
const archiveNames = dirEntries.filter(
|
|
5430
|
+
(entry) => entry.isFile() && (entry.name.endsWith(".capsule.json.gz") || entry.name.endsWith(".capsule.json.gz.enc"))
|
|
5431
|
+
).map((entry) => entry.name).sort();
|
|
5432
|
+
const capsules = [];
|
|
5433
|
+
for (const archiveName of archiveNames) {
|
|
5434
|
+
const archivePath = nodePath.join(capsulesDir, archiveName);
|
|
5435
|
+
const id = archiveName.replace(/\.capsule\.json\.gz\.enc$/, "").replace(/\.capsule\.json\.gz$/, "");
|
|
5436
|
+
const manifestPath = nodePath.join(capsulesDir, `${id}.manifest.json`);
|
|
5437
|
+
let createdAt = null;
|
|
5438
|
+
let pluginVersion = null;
|
|
5439
|
+
let fileCount = null;
|
|
5440
|
+
let description = null;
|
|
5441
|
+
let manifestPathOrNull = manifestPath;
|
|
5442
|
+
try {
|
|
5443
|
+
const manifestStat = await nodeFs.lstat(manifestPath);
|
|
5444
|
+
if (manifestStat.isSymbolicLink() || !manifestStat.isFile()) {
|
|
5445
|
+
capsules.push({
|
|
5446
|
+
id,
|
|
5447
|
+
archivePath,
|
|
5448
|
+
manifestPath: manifestPathOrNull,
|
|
5449
|
+
createdAt,
|
|
5450
|
+
pluginVersion,
|
|
5451
|
+
fileCount,
|
|
5452
|
+
description
|
|
5453
|
+
});
|
|
5454
|
+
continue;
|
|
5455
|
+
}
|
|
5456
|
+
const raw = await nodeFs.readFile(manifestPath, "utf-8");
|
|
5457
|
+
const sidecar = JSON.parse(raw);
|
|
5458
|
+
createdAt = typeof sidecar.createdAt === "string" ? sidecar.createdAt : null;
|
|
5459
|
+
pluginVersion = typeof sidecar.pluginVersion === "string" ? sidecar.pluginVersion : null;
|
|
5460
|
+
fileCount = Array.isArray(sidecar.files) ? sidecar.files.length : null;
|
|
5461
|
+
const capsule = sidecar.capsule;
|
|
5462
|
+
description = capsule && typeof capsule.description === "string" ? capsule.description : null;
|
|
5463
|
+
} catch (err) {
|
|
5464
|
+
const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
|
|
5465
|
+
if (code === "ENOENT") {
|
|
5466
|
+
manifestPathOrNull = null;
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
capsules.push({
|
|
5470
|
+
id,
|
|
5471
|
+
archivePath,
|
|
5472
|
+
manifestPath: manifestPathOrNull,
|
|
5473
|
+
createdAt,
|
|
5474
|
+
pluginVersion,
|
|
5475
|
+
fileCount,
|
|
5476
|
+
description
|
|
5887
5477
|
});
|
|
5888
5478
|
}
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5479
|
+
return { namespace: resolvedNamespace, capsulesDir, capsules };
|
|
5480
|
+
}
|
|
5481
|
+
/**
|
|
5482
|
+
* Manually invoke a single Dreams phase pass (PR 4/4).
|
|
5483
|
+
*
|
|
5484
|
+
* Deep-sleep delegates to memory governance (shadow → dry-run, apply → live).
|
|
5485
|
+
* Light-sleep and REM scan the observation ledger and memory corpus
|
|
5486
|
+
* respectively, returning the same telemetry shape as a scheduled run.
|
|
5487
|
+
*/
|
|
5488
|
+
async dreamsRun(options) {
|
|
5489
|
+
const { runDreamsPhase } = await import("./dreams-ledger-6UODQOX6.js");
|
|
5490
|
+
const validPhases = ["lightSleep", "rem", "deepSleep"];
|
|
5491
|
+
if (!validPhases.includes(options.phase)) {
|
|
5899
5492
|
throw new EngramAccessInputError(
|
|
5900
|
-
|
|
5493
|
+
`Invalid phase: ${String(options.phase)}. Must be one of: ${validPhases.join(", ")}`
|
|
5901
5494
|
);
|
|
5902
5495
|
}
|
|
5903
|
-
const
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
const
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
softLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetSoftLimit ?? 10,
|
|
5937
|
-
hardLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetHardLimit ?? 30,
|
|
5938
|
-
windowMs: this.deps.orchestrator.config.recallCrossNamespaceBudgetWindowMs ?? 6e4
|
|
5939
|
-
}
|
|
5496
|
+
const deepSleep = this.deps.orchestrator.config.dreamsPhases.deepSleep;
|
|
5497
|
+
if (options.phase === "deepSleep" && deepSleep.enabled === false && deepSleep.enabledExplicitlySet === true) {
|
|
5498
|
+
throw new EngramAccessInputError(
|
|
5499
|
+
"memory governance is disabled by dreams.phases.deepSleep.enabled=false"
|
|
5500
|
+
);
|
|
5501
|
+
}
|
|
5502
|
+
const dryRun = options.dryRun === true;
|
|
5503
|
+
const resolvedNamespace = this.deps.writableNamespaceFor(
|
|
5504
|
+
options.namespace,
|
|
5505
|
+
void 0,
|
|
5506
|
+
options.authenticatedPrincipal
|
|
5507
|
+
);
|
|
5508
|
+
const storage = await this.deps.orchestrator.getStorage(resolvedNamespace);
|
|
5509
|
+
const memoryDir = storage.dir;
|
|
5510
|
+
const phaseRunner = dryRun || options.phase === "deepSleep" ? void 0 : async (_opts) => {
|
|
5511
|
+
if (_opts.phase === "lightSleep") {
|
|
5512
|
+
const result3 = await this.deps.orchestrator.runLifecyclePolicyNow(storage);
|
|
5513
|
+
return {
|
|
5514
|
+
itemsProcessed: result3.memoriesAssessed,
|
|
5515
|
+
notes: `scored ${result3.memoriesAssessed} memories`
|
|
5516
|
+
};
|
|
5517
|
+
}
|
|
5518
|
+
const result2 = await this.deps.orchestrator.runSemanticConsolidationNow({
|
|
5519
|
+
dryRun: false,
|
|
5520
|
+
storage
|
|
5521
|
+
});
|
|
5522
|
+
const itemsProcessed = result2.clusters.reduce(
|
|
5523
|
+
(sum, cluster) => sum + cluster.memories.length,
|
|
5524
|
+
0
|
|
5525
|
+
);
|
|
5526
|
+
return {
|
|
5527
|
+
itemsProcessed,
|
|
5528
|
+
notes: `REM consolidation found ${result2.clustersFound} clusters`
|
|
5940
5529
|
};
|
|
5530
|
+
};
|
|
5531
|
+
const governanceRunner = options.phase === "deepSleep" ? async (_opts) => {
|
|
5532
|
+
return this.deps.orchestrator.runDeepSleepGovernanceNow({
|
|
5533
|
+
storage,
|
|
5534
|
+
dryRun: _opts.dryRun
|
|
5535
|
+
});
|
|
5536
|
+
} : void 0;
|
|
5537
|
+
const result = await runDreamsPhase(
|
|
5538
|
+
{ memoryDir, phase: options.phase, dryRun },
|
|
5539
|
+
governanceRunner,
|
|
5540
|
+
phaseRunner
|
|
5541
|
+
);
|
|
5542
|
+
return {
|
|
5543
|
+
phase: result.phase,
|
|
5544
|
+
dryRun: result.dryRun,
|
|
5545
|
+
durationMs: result.durationMs,
|
|
5546
|
+
itemsProcessed: result.itemsProcessed,
|
|
5547
|
+
notes: result.notes
|
|
5548
|
+
};
|
|
5549
|
+
}
|
|
5550
|
+
/**
|
|
5551
|
+
* Manually promote a memory into one or more authorized targets. Requires
|
|
5552
|
+
* a non-empty reason (audit-logged). Reuses the scope-profile promotion
|
|
5553
|
+
* resolution and `canWriteNamespace` gate — there is no dashboard-only
|
|
5554
|
+
* write path.
|
|
5555
|
+
*/
|
|
5556
|
+
async adminPromoteMemory(request) {
|
|
5557
|
+
const config = this.deps.orchestrator.config;
|
|
5558
|
+
const namespacesEnabled = resolveNamespaceCapabilities(config).namespaces === true;
|
|
5559
|
+
let sourceNamespace;
|
|
5560
|
+
if (request.namespace) {
|
|
5561
|
+
sourceNamespace = this.deps.resolveReadableNamespace(
|
|
5562
|
+
request.namespace,
|
|
5563
|
+
request.principal
|
|
5564
|
+
);
|
|
5941
5565
|
} else {
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5566
|
+
const codingContext2 = request.sessionKey ? this.deps.orchestrator.getCodingContextForSession(request.sessionKey) ?? null : null;
|
|
5567
|
+
const plan = resolveScopePlan({
|
|
5568
|
+
config,
|
|
5569
|
+
sessionKey: request.sessionKey,
|
|
5570
|
+
principalOverride: request.principal,
|
|
5571
|
+
codingContext: codingContext2,
|
|
5572
|
+
namespacesEnabled
|
|
5573
|
+
});
|
|
5574
|
+
sourceNamespace = this.deps.resolveReadableNamespace(
|
|
5575
|
+
plan.baseNamespace,
|
|
5576
|
+
request.principal
|
|
5577
|
+
);
|
|
5578
|
+
}
|
|
5579
|
+
const codingContext = request.sessionKey ? this.deps.orchestrator.getCodingContextForSession(request.sessionKey) ?? null : null;
|
|
5580
|
+
const codingOverlay = codingContext ? resolveCodingNamespaceOverlay(
|
|
5581
|
+
codingContext,
|
|
5582
|
+
config.codingMode,
|
|
5583
|
+
config.defaultNamespace
|
|
5584
|
+
) : null;
|
|
5585
|
+
const scopeProfilePlan = resolveScopeProfilePlan({
|
|
5586
|
+
config,
|
|
5587
|
+
principal: request.principal,
|
|
5588
|
+
codingContext,
|
|
5589
|
+
codingOverlay
|
|
5590
|
+
});
|
|
5591
|
+
const storage = {
|
|
5592
|
+
readMemory: async (namespace, memoryId) => {
|
|
5593
|
+
const resolved = await this.deps.orchestrator.getStorage(namespace);
|
|
5594
|
+
const memory = await resolved.getMemoryById(memoryId);
|
|
5595
|
+
if (!memory) return null;
|
|
5596
|
+
return {
|
|
5597
|
+
category: memory.frontmatter.category,
|
|
5598
|
+
content: memory.content,
|
|
5599
|
+
frontmatter: memory.frontmatter
|
|
5972
5600
|
};
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5601
|
+
},
|
|
5602
|
+
writePromotedMemory: async (namespace, memory) => {
|
|
5603
|
+
const resolved = await this.deps.orchestrator.getStorage(namespace);
|
|
5604
|
+
const { id, tombstoneBlocked } = await resolved.writeMemory(
|
|
5605
|
+
memory.category,
|
|
5606
|
+
memory.content,
|
|
5607
|
+
{
|
|
5608
|
+
confidence: memory.confidence,
|
|
5609
|
+
tags: memory.tags,
|
|
5610
|
+
entityRef: memory.entityRef,
|
|
5611
|
+
source: `admin-promotion:${memory.sourceNamespace}:${memory.reason.slice(0, 120)}`,
|
|
5612
|
+
lineage: memory.lineage,
|
|
5613
|
+
sourceMemoryId: memory.sourceMemoryId,
|
|
5614
|
+
actor: memory.actor,
|
|
5615
|
+
validAt: memory.validAt
|
|
5983
5616
|
}
|
|
5984
|
-
};
|
|
5985
|
-
}
|
|
5986
|
-
if (!budgetDecision.allowed) {
|
|
5987
|
-
throw new EngramAccessInputError(
|
|
5988
|
-
`recall denied: cross-namespace budget exceeded (${budgetDecision.count}/${budgetDecision.limit.hardLimit} in ${budgetDecision.limit.windowMs}ms window)`
|
|
5989
|
-
);
|
|
5990
|
-
}
|
|
5991
|
-
this.deps.budget.gc();
|
|
5992
|
-
}
|
|
5993
|
-
const topK = Number.isFinite(request.topK) ? Math.max(0, Math.floor(request.topK ?? 0)) : void 0;
|
|
5994
|
-
let asOf;
|
|
5995
|
-
if (request.asOf !== void 0 && request.asOf !== null) {
|
|
5996
|
-
if (typeof request.asOf !== "string" || request.asOf.trim().length === 0) {
|
|
5997
|
-
throw new EngramAccessInputError(
|
|
5998
|
-
"asOf must be a non-empty ISO 8601 timestamp string"
|
|
5999
5617
|
);
|
|
5618
|
+
if (tombstoneBlocked) {
|
|
5619
|
+
throw new Error(
|
|
5620
|
+
"target namespace tombstone-blocked the promoted content (pending_review)"
|
|
5621
|
+
);
|
|
5622
|
+
}
|
|
5623
|
+
return id;
|
|
6000
5624
|
}
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
5625
|
+
};
|
|
5626
|
+
try {
|
|
5627
|
+
return await promoteMemory({
|
|
5628
|
+
config,
|
|
5629
|
+
sourceMemoryId: request.sourceMemoryId,
|
|
5630
|
+
sourceNamespace,
|
|
5631
|
+
principal: request.principal,
|
|
5632
|
+
targets: request.targets,
|
|
5633
|
+
reason: request.reason,
|
|
5634
|
+
actor: request.principal ?? "admin-console",
|
|
5635
|
+
storage,
|
|
5636
|
+
scopeProfilePlan
|
|
5637
|
+
});
|
|
5638
|
+
} catch (err) {
|
|
5639
|
+
if (err instanceof AdminPromotionError) {
|
|
5640
|
+
throw new EngramAccessInputError(err.message);
|
|
6006
5641
|
}
|
|
6007
|
-
|
|
5642
|
+
throw err;
|
|
6008
5643
|
}
|
|
6009
|
-
|
|
6010
|
-
|
|
5644
|
+
}
|
|
5645
|
+
};
|
|
5646
|
+
|
|
5647
|
+
// src/access-recall-surface.ts
|
|
5648
|
+
import { createHash as createHash4 } from "crypto";
|
|
5649
|
+
import * as nodePath2 from "path";
|
|
5650
|
+
|
|
5651
|
+
// src/orchestration/qmd-result-resolver.ts
|
|
5652
|
+
import path8 from "path";
|
|
5653
|
+
function qmdCollectionPathParts(resultPath) {
|
|
5654
|
+
if (!resultPath || path8.isAbsolute(resultPath)) return null;
|
|
5655
|
+
const normalized = resultPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
5656
|
+
const slashIndex = normalized.indexOf("/");
|
|
5657
|
+
if (slashIndex <= 0 || slashIndex >= normalized.length - 1) return null;
|
|
5658
|
+
const collection = normalized.slice(0, slashIndex);
|
|
5659
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(collection)) return null;
|
|
5660
|
+
return {
|
|
5661
|
+
collection,
|
|
5662
|
+
relativePath: normalized.slice(slashIndex + 1)
|
|
5663
|
+
};
|
|
5664
|
+
}
|
|
5665
|
+
function qmdResultPathCandidates(storageDir, resultPath) {
|
|
5666
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
5667
|
+
const storageRoot = path8.resolve(storageDir);
|
|
5668
|
+
const addCandidate = (candidate) => {
|
|
5669
|
+
const resolved = path8.resolve(candidate);
|
|
5670
|
+
if (isPathInsideStorageRoot(storageRoot, resolved)) {
|
|
5671
|
+
candidates.add(resolved);
|
|
5672
|
+
}
|
|
5673
|
+
};
|
|
5674
|
+
const addRelativeCandidates = (relativePath) => {
|
|
5675
|
+
const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
5676
|
+
if (!normalized) return;
|
|
5677
|
+
addCandidate(path8.join(storageRoot, normalized));
|
|
5678
|
+
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
5679
|
+
addCandidate(path8.join(storageRoot, "facts", normalized));
|
|
5680
|
+
}
|
|
5681
|
+
};
|
|
5682
|
+
if (path8.isAbsolute(resultPath)) {
|
|
5683
|
+
addCandidate(resultPath);
|
|
5684
|
+
} else {
|
|
5685
|
+
addRelativeCandidates(resultPath);
|
|
5686
|
+
}
|
|
5687
|
+
return [...candidates];
|
|
5688
|
+
}
|
|
5689
|
+
function storageDirOrNull(storage) {
|
|
5690
|
+
const dir = storage.dir;
|
|
5691
|
+
return typeof dir === "string" && dir.length > 0 ? dir : null;
|
|
5692
|
+
}
|
|
5693
|
+
var QmdResultResolver = class {
|
|
5694
|
+
getConfig;
|
|
5695
|
+
storageFor;
|
|
5696
|
+
storageDirNamespace;
|
|
5697
|
+
qmdCollectionNamespaceFromPrefix;
|
|
5698
|
+
namespaceFromPath;
|
|
5699
|
+
constructor(options) {
|
|
5700
|
+
this.getConfig = options.getConfig;
|
|
5701
|
+
this.storageFor = options.storageFor;
|
|
5702
|
+
this.storageDirNamespace = options.storageDirNamespace;
|
|
5703
|
+
this.qmdCollectionNamespaceFromPrefix = options.qmdCollectionNamespaceFromPrefix;
|
|
5704
|
+
this.namespaceFromPath = options.namespaceFromPath;
|
|
5705
|
+
}
|
|
5706
|
+
async readQmdResultMemory(resultPath, fallbackStorage, recallNamespaces = []) {
|
|
5707
|
+
const parts = qmdCollectionPathParts(resultPath);
|
|
5708
|
+
const fallbackStorageDir = storageDirOrNull(fallbackStorage);
|
|
5709
|
+
const config = this.getConfig();
|
|
5710
|
+
const coldCollection = config.qmdColdCollection ?? "openclaw-engram-cold";
|
|
5711
|
+
if (parts && parts.collection === coldCollection) {
|
|
5712
|
+
const storages = [];
|
|
5713
|
+
const seenStorageDirs = /* @__PURE__ */ new Set();
|
|
5714
|
+
const addStorage = (storage) => {
|
|
5715
|
+
const storageDir = storageDirOrNull(storage);
|
|
5716
|
+
const storageKey = storageDir ? path8.resolve(storageDir) : `storage-without-dir-${storages.length}`;
|
|
5717
|
+
if (seenStorageDirs.has(storageKey)) return;
|
|
5718
|
+
seenStorageDirs.add(storageKey);
|
|
5719
|
+
storages.push(storage);
|
|
5720
|
+
};
|
|
5721
|
+
const fallbackNamespace = fallbackStorageDir !== null ? this.storageDirNamespace(fallbackStorageDir) : config.defaultNamespace;
|
|
5722
|
+
if (recallNamespaces.length === 0 || !resolveNamespaceCapabilities(config).namespaces || recallNamespaces.includes(fallbackNamespace)) {
|
|
5723
|
+
addStorage(fallbackStorage);
|
|
5724
|
+
}
|
|
5725
|
+
if (recallNamespaces.length > 0) {
|
|
5726
|
+
for (const namespace of recallNamespaces) {
|
|
5727
|
+
try {
|
|
5728
|
+
addStorage(await this.storageFor(namespace));
|
|
5729
|
+
} catch (err) {
|
|
5730
|
+
log.debug("qmd cold result namespace storage lookup skipped", {
|
|
5731
|
+
path: resultPath,
|
|
5732
|
+
namespace,
|
|
5733
|
+
error: err.message
|
|
5734
|
+
});
|
|
5735
|
+
}
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
|
+
for (const storage of storages) {
|
|
5739
|
+
const storageDir = storageDirOrNull(storage);
|
|
5740
|
+
if (!storageDir) {
|
|
5741
|
+
const memory = await storage.readMemoryByPath(resultPath);
|
|
5742
|
+
if (memory) return memory;
|
|
5743
|
+
continue;
|
|
5744
|
+
}
|
|
5745
|
+
try {
|
|
5746
|
+
const coldRoot = path8.join(storageDir, "cold");
|
|
5747
|
+
for (const candidate of qmdResultPathCandidates(
|
|
5748
|
+
coldRoot,
|
|
5749
|
+
parts.relativePath
|
|
5750
|
+
)) {
|
|
5751
|
+
const memory = await storage.readMemoryByPath(candidate);
|
|
5752
|
+
if (memory) return memory;
|
|
5753
|
+
}
|
|
5754
|
+
} catch (err) {
|
|
5755
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
5756
|
+
log.debug("qmd cold result path lookup failed open", {
|
|
5757
|
+
path: resultPath,
|
|
5758
|
+
collection: coldCollection,
|
|
5759
|
+
error: err.message
|
|
5760
|
+
});
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
return null;
|
|
5764
|
+
}
|
|
5765
|
+
const collectionNamespace = parts ? this.qmdCollectionNamespaceFromPrefix(parts.collection) : null;
|
|
5766
|
+
if (parts && collectionNamespace) {
|
|
5767
|
+
try {
|
|
5768
|
+
const collectionStorage = await this.storageFor(collectionNamespace);
|
|
5769
|
+
for (const candidate of qmdResultPathCandidates(
|
|
5770
|
+
collectionStorage.dir,
|
|
5771
|
+
parts.relativePath
|
|
5772
|
+
)) {
|
|
5773
|
+
const memory = await collectionStorage.readMemoryByPath(candidate);
|
|
5774
|
+
if (memory) return memory;
|
|
5775
|
+
}
|
|
5776
|
+
return null;
|
|
5777
|
+
} catch (err) {
|
|
5778
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
5779
|
+
log.debug("qmd result namespace path lookup failed open", {
|
|
5780
|
+
path: resultPath,
|
|
5781
|
+
namespace: collectionNamespace,
|
|
5782
|
+
error: err.message
|
|
5783
|
+
});
|
|
5784
|
+
return null;
|
|
5785
|
+
}
|
|
5786
|
+
}
|
|
5787
|
+
if (path8.isAbsolute(resultPath)) {
|
|
5788
|
+
if (!fallbackStorageDir) {
|
|
5789
|
+
return await fallbackStorage.readMemoryByPath(resultPath);
|
|
5790
|
+
}
|
|
5791
|
+
const ownerStorage = await this.storageForAbsoluteQmdResultPath(
|
|
5792
|
+
resultPath,
|
|
5793
|
+
fallbackStorage,
|
|
5794
|
+
recallNamespaces
|
|
5795
|
+
);
|
|
5796
|
+
if (!ownerStorage) return null;
|
|
5797
|
+
for (const candidate of qmdResultPathCandidates(
|
|
5798
|
+
ownerStorage.dir,
|
|
5799
|
+
resultPath
|
|
5800
|
+
)) {
|
|
5801
|
+
const memory = await ownerStorage.storage.readMemoryByPath(candidate);
|
|
5802
|
+
if (memory) return memory;
|
|
5803
|
+
}
|
|
5804
|
+
return null;
|
|
5805
|
+
}
|
|
5806
|
+
if (!fallbackStorageDir) {
|
|
5807
|
+
return await fallbackStorage.readMemoryByPath(resultPath);
|
|
5808
|
+
}
|
|
5809
|
+
for (const candidate of qmdResultPathCandidates(
|
|
5810
|
+
fallbackStorageDir,
|
|
5811
|
+
resultPath
|
|
5812
|
+
)) {
|
|
5813
|
+
const memory = await fallbackStorage.readMemoryByPath(candidate);
|
|
5814
|
+
if (memory) return memory;
|
|
5815
|
+
}
|
|
5816
|
+
return null;
|
|
5817
|
+
}
|
|
5818
|
+
async resolveColdQmdResultForRecall(result, fallbackStorage, recallNamespaces = []) {
|
|
5819
|
+
const memory = await this.readQmdResultMemory(
|
|
5820
|
+
result.path,
|
|
5821
|
+
fallbackStorage,
|
|
5822
|
+
recallNamespaces
|
|
5823
|
+
);
|
|
5824
|
+
if (!memory) return null;
|
|
5825
|
+
let ownerNamespace = null;
|
|
5826
|
+
if (path8.isAbsolute(memory.path)) {
|
|
5827
|
+
const ownerStorage = await this.storageForAbsoluteQmdResultPath(
|
|
5828
|
+
memory.path,
|
|
5829
|
+
fallbackStorage,
|
|
5830
|
+
recallNamespaces
|
|
5831
|
+
);
|
|
5832
|
+
ownerNamespace = ownerStorage?.namespace ?? null;
|
|
5833
|
+
if (!ownerNamespace && resolveNamespaceCapabilities(this.getConfig()).namespaces) return null;
|
|
5834
|
+
}
|
|
5835
|
+
ownerNamespace ??= this.namespaceFromPath(memory.path);
|
|
5836
|
+
if (recallNamespaces.length > 0 && !recallNamespaces.includes(ownerNamespace)) {
|
|
5837
|
+
return null;
|
|
5838
|
+
}
|
|
5839
|
+
return {
|
|
5840
|
+
namespace: ownerNamespace,
|
|
5841
|
+
result: {
|
|
5842
|
+
...result,
|
|
5843
|
+
docid: result.docid || memory.frontmatter.id,
|
|
5844
|
+
path: memory.path,
|
|
5845
|
+
snippet: result.snippet || memory.content.slice(0, 400)
|
|
5846
|
+
}
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5849
|
+
async storageForAbsoluteQmdResultPath(resultPath, fallbackStorage, recallNamespaces = []) {
|
|
5850
|
+
const resolvedPath = path8.resolve(resultPath);
|
|
5851
|
+
const config = this.getConfig();
|
|
5852
|
+
const memoryRoot = path8.resolve(config.memoryDir);
|
|
5853
|
+
const namespacesRoot = path8.join(memoryRoot, "namespaces");
|
|
5854
|
+
const fallbackStorageDir = storageDirOrNull(fallbackStorage);
|
|
5855
|
+
const matches = [];
|
|
5856
|
+
const seenDirs = /* @__PURE__ */ new Set();
|
|
5857
|
+
const maybeAddStorage = (storage, namespace) => {
|
|
5858
|
+
const storageDir = storageDirOrNull(storage);
|
|
5859
|
+
if (!storageDir) return;
|
|
5860
|
+
const candidateRoot = path8.resolve(storageDir);
|
|
5861
|
+
if (seenDirs.has(candidateRoot)) return;
|
|
5862
|
+
if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
|
|
5863
|
+
if (candidateRoot === memoryRoot && isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
5864
|
+
return;
|
|
5865
|
+
}
|
|
5866
|
+
seenDirs.add(candidateRoot);
|
|
5867
|
+
matches.push({ storage, dir: candidateRoot, namespace });
|
|
5868
|
+
};
|
|
5869
|
+
const fallbackNamespace = fallbackStorageDir !== null ? this.storageDirNamespace(fallbackStorageDir) : config.defaultNamespace;
|
|
5870
|
+
maybeAddStorage(fallbackStorage, fallbackNamespace);
|
|
5871
|
+
const candidateNamespaces = /* @__PURE__ */ new Set();
|
|
5872
|
+
candidateNamespaces.add(config.defaultNamespace);
|
|
5873
|
+
candidateNamespaces.add(config.sharedNamespace);
|
|
5874
|
+
for (const ns of recallNamespaces) {
|
|
5875
|
+
candidateNamespaces.add(ns);
|
|
5876
|
+
}
|
|
5877
|
+
if (isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
5878
|
+
const relativeToNamespaces = path8.relative(namespacesRoot, resolvedPath);
|
|
5879
|
+
const [namespaceSegment] = relativeToNamespaces.split(/[\\/]/);
|
|
5880
|
+
if (namespaceSegment) {
|
|
5881
|
+
candidateNamespaces.add(
|
|
5882
|
+
namespaceIdentityFromToken(namespaceSegment) ?? namespaceSegment
|
|
5883
|
+
);
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
for (const policy of config.namespacePolicies ?? []) {
|
|
5887
|
+
candidateNamespaces.add(policy.name);
|
|
5888
|
+
}
|
|
5889
|
+
for (const ns of candidateNamespaces) {
|
|
5890
|
+
if (!ns) continue;
|
|
5891
|
+
try {
|
|
5892
|
+
maybeAddStorage(await this.storageFor(ns), ns);
|
|
5893
|
+
} catch {
|
|
5894
|
+
continue;
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
matches.sort((a, b) => b.dir.length - a.dir.length);
|
|
5898
|
+
return matches[0] ?? null;
|
|
5899
|
+
}
|
|
5900
|
+
};
|
|
5901
|
+
|
|
5902
|
+
// src/access-recall-surface.ts
|
|
5903
|
+
var AccessRecallSurface = class {
|
|
5904
|
+
constructor(deps) {
|
|
5905
|
+
this.deps = deps;
|
|
5906
|
+
}
|
|
5907
|
+
deps;
|
|
5908
|
+
async buildRecallResponseFromXraySnapshot(options) {
|
|
5909
|
+
const memoryIds = options.snapshot.results.map((result) => result.memoryId);
|
|
5910
|
+
const resultPaths = options.snapshot.results.map((result) => result.path);
|
|
5911
|
+
const namespace = options.snapshot.namespace ? this.deps.resolveNamespace(options.snapshot.namespace) : this.deps.orchestrator.config.defaultNamespace;
|
|
5912
|
+
const sourcesUsed = Array.from(
|
|
5913
|
+
new Set(options.snapshot.results.map((result) => result.servedBy))
|
|
5914
|
+
);
|
|
5915
|
+
const snapshotForSerialization = {
|
|
5916
|
+
sessionKey: options.sessionKey ?? "",
|
|
5917
|
+
recordedAt: new Date(options.snapshot.capturedAt).toISOString(),
|
|
5918
|
+
queryHash: createHash4("sha256").update(options.query).digest("hex"),
|
|
5919
|
+
queryLen: options.query.length,
|
|
5920
|
+
memoryIds,
|
|
5921
|
+
namespace,
|
|
5922
|
+
recallNamespaces: [namespace],
|
|
5923
|
+
traceId: options.snapshot.traceId,
|
|
5924
|
+
plannerMode: options.normalizedMode,
|
|
5925
|
+
requestedMode: options.requestedMode && options.requestedMode !== "auto" ? options.requestedMode : void 0,
|
|
5926
|
+
sourcesUsed,
|
|
5927
|
+
budgetsApplied: {
|
|
5928
|
+
appliedTopK: memoryIds.length,
|
|
5929
|
+
recallBudgetChars: options.snapshot.budget.chars,
|
|
5930
|
+
maxMemoryTokens: this.deps.orchestrator.config.maxMemoryTokens,
|
|
5931
|
+
finalContextChars: options.snapshot.budget.used
|
|
5932
|
+
},
|
|
5933
|
+
latencyMs: Date.now() - options.startedAt,
|
|
5934
|
+
resultPaths
|
|
5935
|
+
};
|
|
5936
|
+
const results = await this.deps.serializeRecallResults(
|
|
5937
|
+
snapshotForSerialization,
|
|
5938
|
+
options.disclosure,
|
|
5939
|
+
{
|
|
5940
|
+
query: options.query,
|
|
5941
|
+
...options.sessionKey ? { sessionKey: options.sessionKey } : {},
|
|
5942
|
+
...options.rawExcerptNamespace ? { rawExcerptNamespace: options.rawExcerptNamespace } : {},
|
|
5943
|
+
...options.rawExcerptSessionIds !== void 0 ? { rawExcerptSessionIds: options.rawExcerptSessionIds } : {},
|
|
5944
|
+
...options.rawExcerptsSuppressed ? { rawExcerptsSuppressed: options.rawExcerptsSuppressed } : {}
|
|
5945
|
+
}
|
|
5946
|
+
);
|
|
5947
|
+
const context = results.map((result) => {
|
|
5948
|
+
const content = typeof result.content === "string" && result.content.length > 0 ? result.content : "";
|
|
5949
|
+
return content || result.preview;
|
|
5950
|
+
}).filter((text) => text.length > 0).join("\n\n");
|
|
5951
|
+
return {
|
|
5952
|
+
query: options.query,
|
|
5953
|
+
...options.sessionKey ? { sessionKey: options.sessionKey } : {},
|
|
5954
|
+
namespace,
|
|
5955
|
+
context,
|
|
5956
|
+
count: memoryIds.length,
|
|
5957
|
+
memoryIds,
|
|
5958
|
+
results,
|
|
5959
|
+
recordedAt: snapshotForSerialization.recordedAt,
|
|
5960
|
+
traceId: options.snapshot.traceId,
|
|
5961
|
+
plannerMode: options.normalizedMode,
|
|
5962
|
+
fallbackUsed: sourcesUsed.some((source) => source !== "hybrid"),
|
|
5963
|
+
sourcesUsed,
|
|
5964
|
+
disclosure: options.disclosure,
|
|
5965
|
+
budgetsApplied: snapshotForSerialization.budgetsApplied,
|
|
5966
|
+
latencyMs: snapshotForSerialization.latencyMs
|
|
5967
|
+
};
|
|
5968
|
+
}
|
|
5969
|
+
async serializeRecallResults(snapshot, disclosure, rawContext = null) {
|
|
5970
|
+
if (!snapshot) return [];
|
|
5971
|
+
const namespace = snapshot.namespace ? this.deps.resolveNamespace(snapshot.namespace) : this.deps.orchestrator.config.defaultNamespace;
|
|
5972
|
+
const storage = await this.deps.orchestrator.getStorage(namespace);
|
|
5973
|
+
const storageDir = storage.dir;
|
|
5974
|
+
const recallNamespaces = Array.from(
|
|
5975
|
+
new Set(
|
|
5976
|
+
[
|
|
5977
|
+
namespace,
|
|
5978
|
+
...Array.isArray(snapshot.recallNamespaces) ? snapshot.recallNamespaces.map((ns) => this.deps.resolveNamespace(ns)) : [],
|
|
5979
|
+
this.deps.orchestrator.config.defaultNamespace,
|
|
5980
|
+
this.deps.orchestrator.config.sharedNamespace,
|
|
5981
|
+
...(this.deps.orchestrator.config.namespacePolicies ?? []).map((p) => p.name)
|
|
5982
|
+
].filter((ns) => typeof ns === "string" && ns.length > 0)
|
|
5983
|
+
)
|
|
5984
|
+
);
|
|
5985
|
+
const results = [];
|
|
5986
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5987
|
+
const collectionNamespaceFromPrefix = (collectionPrefix) => {
|
|
5988
|
+
const baseCollection = this.deps.orchestrator.config.qmdCollection;
|
|
5989
|
+
if (collectionPrefix === baseCollection) return this.deps.orchestrator.config.defaultNamespace;
|
|
5990
|
+
const namespaceSuffix = collectionPrefix.startsWith(`${baseCollection}--`) ? collectionPrefix.slice(baseCollection.length + 2) : "";
|
|
5991
|
+
if (!namespaceSuffix) return null;
|
|
5992
|
+
const decoded = namespaceIdentityFromToken(namespaceSuffix);
|
|
5993
|
+
if (decoded !== null) return decoded || this.deps.orchestrator.config.defaultNamespace;
|
|
5994
|
+
if (namespaceSuffix.startsWith("ns--")) {
|
|
5995
|
+
const legacyNamespace = namespaceSuffix.slice("ns--".length).trim();
|
|
5996
|
+
return legacyNamespace || null;
|
|
5997
|
+
}
|
|
5998
|
+
return null;
|
|
5999
|
+
};
|
|
6000
|
+
const readResultPath = async (memoryPath) => {
|
|
6001
|
+
const parts = qmdCollectionPathParts(memoryPath);
|
|
6002
|
+
const coldCollection = this.deps.orchestrator.config.qmdColdCollection ?? "openclaw-engram-cold";
|
|
6003
|
+
if (parts && parts.collection === coldCollection) {
|
|
6004
|
+
const storages = [];
|
|
6005
|
+
const seenStorageDirs = /* @__PURE__ */ new Set();
|
|
6006
|
+
const addStorage = (candidateStorage) => {
|
|
6007
|
+
const candidateDir = nodePath2.resolve(candidateStorage.dir);
|
|
6008
|
+
if (seenStorageDirs.has(candidateDir)) return;
|
|
6009
|
+
seenStorageDirs.add(candidateDir);
|
|
6010
|
+
storages.push({ storage: candidateStorage, dir: candidateDir });
|
|
6011
|
+
};
|
|
6012
|
+
addStorage(storage);
|
|
6013
|
+
for (const recallNamespace of recallNamespaces) {
|
|
6014
|
+
try {
|
|
6015
|
+
addStorage(await this.deps.orchestrator.getStorage(recallNamespace));
|
|
6016
|
+
} catch {
|
|
6017
|
+
continue;
|
|
6018
|
+
}
|
|
6019
|
+
}
|
|
6020
|
+
for (const candidateStorage of storages) {
|
|
6021
|
+
try {
|
|
6022
|
+
const coldRoot = nodePath2.join(candidateStorage.dir, "cold");
|
|
6023
|
+
for (const candidatePath of qmdResultPathCandidates(
|
|
6024
|
+
coldRoot,
|
|
6025
|
+
parts.relativePath
|
|
6026
|
+
)) {
|
|
6027
|
+
const memory = await candidateStorage.storage.readMemoryByPath(candidatePath);
|
|
6028
|
+
if (memory) return { memory, baseDir: candidateStorage.dir };
|
|
6029
|
+
}
|
|
6030
|
+
} catch (err) {
|
|
6031
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
6032
|
+
continue;
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
return null;
|
|
6036
|
+
}
|
|
6037
|
+
const collectionNamespace = parts ? collectionNamespaceFromPrefix(parts.collection) : null;
|
|
6038
|
+
if (parts && collectionNamespace) {
|
|
6039
|
+
try {
|
|
6040
|
+
const collectionStorage = await this.deps.orchestrator.getStorage(collectionNamespace);
|
|
6041
|
+
for (const candidate of qmdResultPathCandidates(
|
|
6042
|
+
collectionStorage.dir,
|
|
6043
|
+
parts.relativePath
|
|
6044
|
+
)) {
|
|
6045
|
+
const memory = await collectionStorage.readMemoryByPath(candidate);
|
|
6046
|
+
if (memory) return { memory, baseDir: collectionStorage.dir };
|
|
6047
|
+
}
|
|
6048
|
+
return null;
|
|
6049
|
+
} catch (err) {
|
|
6050
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
6051
|
+
return null;
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
if (nodePath2.isAbsolute(memoryPath)) {
|
|
6055
|
+
const ownerStorage = await this.deps.storageForAbsoluteRecallPath(
|
|
6056
|
+
memoryPath,
|
|
6057
|
+
namespace,
|
|
6058
|
+
recallNamespaces
|
|
6059
|
+
);
|
|
6060
|
+
if (!ownerStorage) return null;
|
|
6061
|
+
for (const candidate of qmdResultPathCandidates(
|
|
6062
|
+
ownerStorage.dir,
|
|
6063
|
+
memoryPath
|
|
6064
|
+
)) {
|
|
6065
|
+
const memory = await ownerStorage.storage.readMemoryByPath(candidate);
|
|
6066
|
+
if (memory) return { memory, baseDir: ownerStorage.dir };
|
|
6067
|
+
}
|
|
6068
|
+
return null;
|
|
6069
|
+
}
|
|
6070
|
+
for (const candidate of qmdResultPathCandidates(storageDir, memoryPath)) {
|
|
6071
|
+
const memory = await storage.readMemoryByPath(candidate);
|
|
6072
|
+
if (memory) return { memory, baseDir: storageDir };
|
|
6073
|
+
}
|
|
6074
|
+
return null;
|
|
6075
|
+
};
|
|
6076
|
+
const rawExcerptsResult = rawContext?.rawExcerptsSuppressed === true ? (
|
|
6077
|
+
// Implicit raw recall with NO readable LCM namespace (#1505 thread
|
|
6078
|
+
// NBHWz): emit empty excerpts rather than falling back to the
|
|
6079
|
+
// write/overlay `namespace` the read gate excludes.
|
|
6080
|
+
[]
|
|
6081
|
+
) : await this.deps.fetchRawExcerpts(
|
|
6082
|
+
disclosure,
|
|
6083
|
+
rawContext ? {
|
|
6084
|
+
query: rawContext.query,
|
|
6085
|
+
...rawContext.sessionKey ? { sessionKey: rawContext.sessionKey } : {},
|
|
6086
|
+
namespace: rawContext.rawExcerptNamespace ?? namespace,
|
|
6087
|
+
...rawContext.rawExcerptSessionIds !== void 0 ? { lcmSessionIds: rawContext.rawExcerptSessionIds } : {}
|
|
6088
|
+
} : null
|
|
6089
|
+
);
|
|
6090
|
+
const rawExcerpts = rawExcerptsResult ?? void 0;
|
|
6091
|
+
for (const memoryPath of snapshot.resultPaths ?? []) {
|
|
6092
|
+
if (!memoryPath || seen.has(memoryPath)) continue;
|
|
6093
|
+
const resolved = await readResultPath(memoryPath);
|
|
6094
|
+
if (!resolved) continue;
|
|
6095
|
+
const { memory, baseDir } = resolved;
|
|
6096
|
+
seen.add(memoryPath);
|
|
6097
|
+
results.push(
|
|
6098
|
+
this.deps.serializeMemorySummary(
|
|
6099
|
+
memory,
|
|
6100
|
+
baseDir,
|
|
6101
|
+
disclosure,
|
|
6102
|
+
// Attach the (possibly empty) raw excerpts to the first raw
|
|
6103
|
+
// result; subsequent results do not duplicate the array.
|
|
6104
|
+
results.length === 0 ? rawExcerpts : void 0
|
|
6105
|
+
)
|
|
6106
|
+
);
|
|
6107
|
+
}
|
|
6108
|
+
if (results.length > 0) return results;
|
|
6109
|
+
for (const memoryId of snapshot.memoryIds) {
|
|
6110
|
+
const memory = await storage.getMemoryById(memoryId);
|
|
6111
|
+
if (!memory || seen.has(memory.path)) continue;
|
|
6112
|
+
seen.add(memory.path);
|
|
6113
|
+
results.push(
|
|
6114
|
+
this.deps.serializeMemorySummary(
|
|
6115
|
+
memory,
|
|
6116
|
+
storageDir,
|
|
6117
|
+
disclosure,
|
|
6118
|
+
results.length === 0 ? rawExcerpts : void 0
|
|
6119
|
+
)
|
|
6120
|
+
);
|
|
6121
|
+
}
|
|
6122
|
+
return results;
|
|
6123
|
+
}
|
|
6124
|
+
/**
|
|
6125
|
+
* Fetch raw transcript excerpts from the LCM archive for `disclosure ===
|
|
6126
|
+
* "raw"` recalls (issue #677 PR 2/4). Returns `null` for non-raw recall
|
|
6127
|
+
* depths, an empty array when LCM is disabled / not initialized / has no
|
|
6128
|
+
* matches, and an array of LCM-side excerpts otherwise. Errors are
|
|
6129
|
+
* swallowed and treated as "no excerpts" so a failing LCM never breaks
|
|
6130
|
+
* the recall response.
|
|
6131
|
+
*
|
|
6132
|
+
* Namespace handling: LCM archival prefixes non-default-namespace
|
|
6133
|
+
* sessions with `${namespace}:${sessionKey}` (see `observe()` around
|
|
6134
|
+
* line 2498), so the lookup must mirror that prefix or raw recalls in
|
|
6135
|
+
* non-default namespaces miss their own excerpts.
|
|
6136
|
+
*/
|
|
6137
|
+
async fetchRawExcerpts(disclosure, context) {
|
|
6138
|
+
if (disclosure !== "raw") return null;
|
|
6139
|
+
if (!context || !context.query) return [];
|
|
6140
|
+
if (!context.sessionKey) return [];
|
|
6141
|
+
const lcm = this.deps.orchestrator.lcmEngine;
|
|
6142
|
+
if (!lcm || !lcm.enabled) return [];
|
|
6143
|
+
try {
|
|
6144
|
+
const legacyKey = context.namespace && context.namespace !== this.deps.orchestrator.config.defaultNamespace ? `${context.namespace}:${context.sessionKey}` : context.sessionKey;
|
|
6145
|
+
const lcmSessionIds = context.lcmSessionIds !== void 0 ? context.lcmSessionIds : [legacyKey];
|
|
6146
|
+
const limit = 5;
|
|
6147
|
+
const seenRows = /* @__PURE__ */ new Set();
|
|
6148
|
+
const excerpts = [];
|
|
6149
|
+
const settledRows = await Promise.allSettled(
|
|
6150
|
+
lcmSessionIds.map(
|
|
6151
|
+
async (lcmSessionKey) => lcm.searchContextFull(context.query, limit, lcmSessionKey)
|
|
6152
|
+
)
|
|
6153
|
+
);
|
|
6154
|
+
for (const result of settledRows) {
|
|
6155
|
+
if (excerpts.length >= limit) break;
|
|
6156
|
+
if (result.status !== "fulfilled") continue;
|
|
6157
|
+
for (const r of result.value) {
|
|
6158
|
+
const dedupeKey = `${r.session_id} ${r.turn_index}`;
|
|
6159
|
+
if (seenRows.has(dedupeKey)) continue;
|
|
6160
|
+
seenRows.add(dedupeKey);
|
|
6161
|
+
excerpts.push({
|
|
6162
|
+
turnIndex: r.turn_index,
|
|
6163
|
+
role: r.role,
|
|
6164
|
+
content: r.content,
|
|
6165
|
+
sessionId: r.session_id
|
|
6166
|
+
});
|
|
6167
|
+
if (excerpts.length >= limit) break;
|
|
6168
|
+
}
|
|
6169
|
+
}
|
|
6170
|
+
return excerpts;
|
|
6171
|
+
} catch {
|
|
6172
|
+
return [];
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
async executeRecall(request) {
|
|
6176
|
+
const query = request.query;
|
|
6177
|
+
const callerProvidedDisclosure = request.disclosure !== void 0 && request.disclosure !== null;
|
|
6178
|
+
const requestedDisclosure = (() => {
|
|
6179
|
+
if (!callerProvidedDisclosure) {
|
|
6180
|
+
return DEFAULT_RECALL_DISCLOSURE;
|
|
6181
|
+
}
|
|
6182
|
+
if (!isRecallDisclosure(request.disclosure)) {
|
|
6183
|
+
throw new EngramAccessInputError(
|
|
6184
|
+
`disclosure must be one of: chunk, section, raw (got: ${String(request.disclosure)})`
|
|
6185
|
+
);
|
|
6186
|
+
}
|
|
6187
|
+
return request.disclosure;
|
|
6188
|
+
})();
|
|
6189
|
+
if (request.codingContext !== void 0 && request.sessionKey) {
|
|
6190
|
+
this.deps.setCodingContext({
|
|
6191
|
+
sessionKey: request.sessionKey,
|
|
6192
|
+
codingContext: request.codingContext
|
|
6193
|
+
});
|
|
6194
|
+
}
|
|
6195
|
+
if (request.codingContext === void 0 && request.sessionKey) {
|
|
6196
|
+
await this.deps.maybeAttachCodingContext(request.sessionKey, {
|
|
6197
|
+
cwd: request.cwd,
|
|
6198
|
+
projectTag: request.projectTag
|
|
6199
|
+
});
|
|
6200
|
+
}
|
|
6201
|
+
const authenticatedPrincipal = request.authenticatedPrincipal?.trim();
|
|
6202
|
+
const namespaceOverride = this.deps.resolveRecallNamespace(
|
|
6203
|
+
request.namespace,
|
|
6204
|
+
request.sessionKey,
|
|
6205
|
+
authenticatedPrincipal
|
|
6206
|
+
);
|
|
6207
|
+
const namespace = namespaceOverride ?? this.deps.orchestrator.config.defaultNamespace;
|
|
6208
|
+
const mode = this.deps.normalizeRecallMode(request.mode);
|
|
6209
|
+
const maybePrincipal = this.deps.resolveRequestPrincipal(request.sessionKey, authenticatedPrincipal);
|
|
6210
|
+
if (resolveNamespaceCapabilities(this.deps.orchestrator.config).namespaces && !maybePrincipal) {
|
|
6211
|
+
throw new EngramAccessInputError(
|
|
6212
|
+
"authentication required: namespaces are enabled and no principal was supplied"
|
|
6213
|
+
);
|
|
6214
|
+
}
|
|
6215
|
+
const principal = maybePrincipal ?? "default";
|
|
6216
|
+
const principalNamespace = defaultNamespaceForPrincipal(principal, this.deps.orchestrator.config);
|
|
6217
|
+
const profileCodingContext = request.sessionKey && typeof this.deps.orchestrator.getCodingContextForSession === "function" ? this.deps.orchestrator.getCodingContextForSession(request.sessionKey) : null;
|
|
6218
|
+
const profileCodingOverlay = !namespaceOverride && profileCodingContext && resolveNamespaceCapabilities(this.deps.orchestrator.config).namespaces && this.deps.orchestrator.config.codingMode?.projectScope ? resolveCodingNamespaceOverlay(
|
|
6219
|
+
profileCodingContext,
|
|
6220
|
+
this.deps.orchestrator.config.codingMode,
|
|
6221
|
+
this.deps.orchestrator.config.defaultNamespace
|
|
6222
|
+
) : null;
|
|
6223
|
+
const profilePlan = namespaceOverride ? null : resolveScopeProfilePlan({
|
|
6224
|
+
config: this.deps.orchestrator.config,
|
|
6225
|
+
principal,
|
|
6226
|
+
codingContext: profileCodingContext,
|
|
6227
|
+
codingOverlay: profileCodingOverlay
|
|
6228
|
+
});
|
|
6229
|
+
const modeSkipsBudget = mode === "no_recall";
|
|
6230
|
+
const legacyRecallNamespaces = Array.isArray(this.deps.orchestrator.config.defaultRecallNamespaces) ? recallNamespacesForPrincipal(principal, this.deps.orchestrator.config) : [];
|
|
6231
|
+
const effectiveNamespaces = namespaceOverride ? [namespaceOverride] : profilePlan ? expandScopeProfileReadNamespaces({
|
|
6232
|
+
profilePlan,
|
|
6233
|
+
principalSelfNamespace: profilePlan.baseNamespace,
|
|
6234
|
+
config: this.deps.orchestrator.config,
|
|
6235
|
+
principal,
|
|
6236
|
+
codingOverlay: profileCodingOverlay,
|
|
6237
|
+
legacyRecallNamespaces
|
|
6238
|
+
}) : legacyRecallNamespaces;
|
|
6239
|
+
const budgetPrincipalNamespace = profilePlan?.baseNamespace ?? principalNamespace;
|
|
6240
|
+
let budgetDecision;
|
|
6241
|
+
let recordBudgetAfterSuccess = false;
|
|
6242
|
+
if (modeSkipsBudget) {
|
|
6243
|
+
budgetDecision = {
|
|
6244
|
+
allowed: true,
|
|
6245
|
+
reason: "allowed-same-namespace",
|
|
6246
|
+
count: 0,
|
|
6247
|
+
limit: {
|
|
6248
|
+
softLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetSoftLimit ?? 10,
|
|
6249
|
+
hardLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetHardLimit ?? 30,
|
|
6250
|
+
windowMs: this.deps.orchestrator.config.recallCrossNamespaceBudgetWindowMs ?? 6e4
|
|
6251
|
+
}
|
|
6252
|
+
};
|
|
6253
|
+
} else {
|
|
6254
|
+
let anyCrossNamespace = false;
|
|
6255
|
+
let denied = null;
|
|
6256
|
+
let crossNamespaceDecision = null;
|
|
6257
|
+
for (const ns of effectiveNamespaces) {
|
|
6258
|
+
const peek = this.deps.budget.peek({
|
|
6259
|
+
principal,
|
|
6260
|
+
principalNamespace: budgetPrincipalNamespace,
|
|
6261
|
+
queryNamespace: ns
|
|
6262
|
+
});
|
|
6263
|
+
if (peek.reason !== "allowed-same-namespace") {
|
|
6264
|
+
anyCrossNamespace = true;
|
|
6265
|
+
crossNamespaceDecision ??= peek;
|
|
6266
|
+
}
|
|
6267
|
+
if (!peek.allowed) {
|
|
6268
|
+
denied = peek;
|
|
6269
|
+
break;
|
|
6270
|
+
}
|
|
6271
|
+
}
|
|
6272
|
+
if (denied) {
|
|
6273
|
+
budgetDecision = denied;
|
|
6274
|
+
} else if (anyCrossNamespace) {
|
|
6275
|
+
budgetDecision = crossNamespaceDecision ?? {
|
|
6276
|
+
allowed: true,
|
|
6277
|
+
reason: "allowed-under-soft",
|
|
6278
|
+
count: 0,
|
|
6279
|
+
limit: {
|
|
6280
|
+
softLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetSoftLimit ?? 10,
|
|
6281
|
+
hardLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetHardLimit ?? 30,
|
|
6282
|
+
windowMs: this.deps.orchestrator.config.recallCrossNamespaceBudgetWindowMs ?? 6e4
|
|
6283
|
+
}
|
|
6284
|
+
};
|
|
6285
|
+
recordBudgetAfterSuccess = true;
|
|
6286
|
+
} else {
|
|
6287
|
+
budgetDecision = {
|
|
6288
|
+
allowed: true,
|
|
6289
|
+
reason: "allowed-same-namespace",
|
|
6290
|
+
count: 0,
|
|
6291
|
+
limit: {
|
|
6292
|
+
softLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetSoftLimit ?? 10,
|
|
6293
|
+
hardLimit: this.deps.orchestrator.config.recallCrossNamespaceBudgetHardLimit ?? 30,
|
|
6294
|
+
windowMs: this.deps.orchestrator.config.recallCrossNamespaceBudgetWindowMs ?? 6e4
|
|
6295
|
+
}
|
|
6296
|
+
};
|
|
6297
|
+
}
|
|
6298
|
+
if (!budgetDecision.allowed) {
|
|
6299
|
+
throw new EngramAccessInputError(
|
|
6300
|
+
`recall denied: cross-namespace budget exceeded (${budgetDecision.count}/${budgetDecision.limit.hardLimit} in ${budgetDecision.limit.windowMs}ms window)`
|
|
6301
|
+
);
|
|
6302
|
+
}
|
|
6303
|
+
this.deps.budget.gc();
|
|
6304
|
+
}
|
|
6305
|
+
const topK = Number.isFinite(request.topK) ? Math.max(0, Math.floor(request.topK ?? 0)) : void 0;
|
|
6306
|
+
let asOf;
|
|
6307
|
+
if (request.asOf !== void 0 && request.asOf !== null) {
|
|
6308
|
+
if (typeof request.asOf !== "string" || request.asOf.trim().length === 0) {
|
|
6309
|
+
throw new EngramAccessInputError(
|
|
6310
|
+
"asOf must be a non-empty ISO 8601 timestamp string"
|
|
6311
|
+
);
|
|
6312
|
+
}
|
|
6313
|
+
const parsed = Date.parse(request.asOf);
|
|
6314
|
+
if (!Number.isFinite(parsed)) {
|
|
6315
|
+
throw new EngramAccessInputError(
|
|
6316
|
+
`asOf must be a parseable ISO 8601 timestamp (got: "${request.asOf}")`
|
|
6317
|
+
);
|
|
6318
|
+
}
|
|
6319
|
+
asOf = request.asOf;
|
|
6320
|
+
}
|
|
6321
|
+
const recallOptions = {
|
|
6322
|
+
namespace: namespaceOverride,
|
|
6011
6323
|
topK,
|
|
6012
6324
|
mode,
|
|
6013
6325
|
...authenticatedPrincipal ? { principalOverride: authenticatedPrincipal } : {},
|
|
@@ -6370,25 +6682,6 @@ var AccessRecallSurface = class {
|
|
|
6370
6682
|
}
|
|
6371
6683
|
};
|
|
6372
6684
|
|
|
6373
|
-
// src/orchestration/self-deps.ts
|
|
6374
|
-
function selfDeps(self) {
|
|
6375
|
-
const source = self;
|
|
6376
|
-
return new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
6377
|
-
get(_target, prop) {
|
|
6378
|
-
const value = source[prop];
|
|
6379
|
-
return typeof value === "function" ? value.bind(self) : value;
|
|
6380
|
-
},
|
|
6381
|
-
set(_target, prop, value) {
|
|
6382
|
-
if (!(prop in source)) return false;
|
|
6383
|
-
source[prop] = value;
|
|
6384
|
-
return true;
|
|
6385
|
-
},
|
|
6386
|
-
has(_target, prop) {
|
|
6387
|
-
return prop in source;
|
|
6388
|
-
}
|
|
6389
|
-
});
|
|
6390
|
-
}
|
|
6391
|
-
|
|
6392
6685
|
// src/access-service.ts
|
|
6393
6686
|
var EngramAccessInputError = class extends Error {
|
|
6394
6687
|
};
|
|
@@ -6572,6 +6865,16 @@ var EngramAccessService = class {
|
|
|
6572
6865
|
budgetLocks = /* @__PURE__ */ new Map();
|
|
6573
6866
|
budget;
|
|
6574
6867
|
auditAdapter;
|
|
6868
|
+
/** AccessObserveWriteSurface (access-service decomposition). Lazy; selfDeps live wiring. */
|
|
6869
|
+
_accessObserveWriteSurface;
|
|
6870
|
+
get accessObserveWriteSurface() {
|
|
6871
|
+
if (!this._accessObserveWriteSurface) {
|
|
6872
|
+
this._accessObserveWriteSurface = new AccessObserveWriteSurface(
|
|
6873
|
+
selfDeps(this)
|
|
6874
|
+
);
|
|
6875
|
+
}
|
|
6876
|
+
return this._accessObserveWriteSurface;
|
|
6877
|
+
}
|
|
6575
6878
|
/** AccessLcmSurface (access-service decomposition). Lazy; selfDeps live wiring. */
|
|
6576
6879
|
_accessLcmSurface;
|
|
6577
6880
|
get accessLcmSurface() {
|
|
@@ -7309,114 +7612,20 @@ var EngramAccessService = class {
|
|
|
7309
7612
|
});
|
|
7310
7613
|
}
|
|
7311
7614
|
async qmdHealth(searchBackend, qmdEnabled, namespace, collection) {
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
collection,
|
|
7319
|
-
collectionState: "skipped",
|
|
7320
|
-
installedVersion: null,
|
|
7321
|
-
supportedVersion: null,
|
|
7322
|
-
supported: null,
|
|
7323
|
-
upgradeAvailable: null,
|
|
7324
|
-
doctorAvailable: null,
|
|
7325
|
-
debugStatus: searchBackend !== "qmd" ? `backend=${searchBackend}` : "backend=disabled"
|
|
7326
|
-
};
|
|
7327
|
-
}
|
|
7328
|
-
if (resolveNamespaceCapabilities(this.orchestrator.config).namespaces === true) {
|
|
7329
|
-
const namespaceHealth = await this.namespaceQmdHealth(searchBackend, qmdEnabled, namespace, collection);
|
|
7330
|
-
if (namespaceHealth) return namespaceHealth;
|
|
7331
|
-
}
|
|
7332
|
-
const qmd = this.orchestrator.qmd;
|
|
7333
|
-
if (!qmd) {
|
|
7334
|
-
return {
|
|
7335
|
-
enabled: true,
|
|
7336
|
-
active: false,
|
|
7337
|
-
degraded: true,
|
|
7338
|
-
mode: "fallback",
|
|
7339
|
-
collection,
|
|
7340
|
-
collectionState: "unknown",
|
|
7341
|
-
installedVersion: null,
|
|
7342
|
-
supportedVersion: null,
|
|
7343
|
-
supported: null,
|
|
7344
|
-
upgradeAvailable: null,
|
|
7345
|
-
doctorAvailable: null,
|
|
7346
|
-
debugStatus: "backend=unavailable"
|
|
7347
|
-
};
|
|
7348
|
-
}
|
|
7349
|
-
const diagnosticAvailable = await this.qmdProbeAvailable(searchBackend, qmdEnabled);
|
|
7350
|
-
const operationalAvailable = diagnosticAvailable || qmd.isAvailable();
|
|
7351
|
-
const collectionState = diagnosticAvailable ? await this.qmdCollectionState(searchBackend, qmdEnabled, collection) : "unknown";
|
|
7352
|
-
const active = operationalAvailable && collectionState !== "missing";
|
|
7353
|
-
const degraded = searchBackend === "qmd" && qmdEnabled && (!active || !diagnosticAvailable || collectionState === "unknown");
|
|
7354
|
-
const debugStatus = qmd.debugStatus();
|
|
7355
|
-
const versionStatus = "getVersionStatus" in qmd && typeof qmd.getVersionStatus === "function" ? qmd.getVersionStatus() : null;
|
|
7356
|
-
const daemonMode = "isDaemonMode" in qmd && typeof qmd.isDaemonMode === "function" ? qmd.isDaemonMode() === true : false;
|
|
7357
|
-
const mode = searchBackend !== "qmd" ? "not-selected" : !qmdEnabled ? "disabled" : !active ? "fallback" : daemonMode ? "daemon" : "cli";
|
|
7358
|
-
return {
|
|
7359
|
-
enabled: qmdEnabled,
|
|
7360
|
-
active,
|
|
7361
|
-
degraded,
|
|
7362
|
-
mode,
|
|
7363
|
-
collection,
|
|
7364
|
-
collectionState,
|
|
7365
|
-
installedVersion: versionStatus?.installedVersion ?? null,
|
|
7366
|
-
supportedVersion: versionStatus?.supportedVersion ?? null,
|
|
7367
|
-
supported: versionStatus?.supported ?? null,
|
|
7368
|
-
upgradeAvailable: versionStatus?.upgradeAvailable ?? null,
|
|
7369
|
-
doctorAvailable: versionStatus?.capabilities?.doctor ?? null,
|
|
7370
|
-
debugStatus
|
|
7371
|
-
};
|
|
7615
|
+
return this.accessObserveWriteSurface.qmdHealth(
|
|
7616
|
+
searchBackend,
|
|
7617
|
+
qmdEnabled,
|
|
7618
|
+
namespace,
|
|
7619
|
+
collection
|
|
7620
|
+
);
|
|
7372
7621
|
}
|
|
7373
7622
|
async namespaceQmdHealth(searchBackend, qmdEnabled, namespace, fallbackCollection) {
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
try {
|
|
7381
|
-
const health = await searchHealthForNamespace.call(this.orchestrator, namespace, {
|
|
7382
|
-
signal: controller.signal
|
|
7383
|
-
});
|
|
7384
|
-
const active = health.available && health.collectionState !== "missing";
|
|
7385
|
-
const degraded = !active || health.collectionState === "unknown";
|
|
7386
|
-
const mode = !active ? "fallback" : health.daemonMode === true ? "daemon" : "cli";
|
|
7387
|
-
return {
|
|
7388
|
-
enabled: true,
|
|
7389
|
-
active,
|
|
7390
|
-
degraded,
|
|
7391
|
-
mode,
|
|
7392
|
-
collection: health.collection || fallbackCollection,
|
|
7393
|
-
collectionState: health.collectionState,
|
|
7394
|
-
installedVersion: health.installedVersion,
|
|
7395
|
-
supportedVersion: health.supportedVersion,
|
|
7396
|
-
supported: health.supported,
|
|
7397
|
-
upgradeAvailable: health.upgradeAvailable,
|
|
7398
|
-
doctorAvailable: health.doctorAvailable,
|
|
7399
|
-
debugStatus: health.debugStatus
|
|
7400
|
-
};
|
|
7401
|
-
} catch (error) {
|
|
7402
|
-
const detail = displayErrorDetail(error) || "unknown";
|
|
7403
|
-
return {
|
|
7404
|
-
enabled: true,
|
|
7405
|
-
active: false,
|
|
7406
|
-
degraded: true,
|
|
7407
|
-
mode: "fallback",
|
|
7408
|
-
collection: fallbackCollection,
|
|
7409
|
-
collectionState: "unknown",
|
|
7410
|
-
installedVersion: null,
|
|
7411
|
-
supportedVersion: null,
|
|
7412
|
-
supported: null,
|
|
7413
|
-
upgradeAvailable: null,
|
|
7414
|
-
doctorAvailable: null,
|
|
7415
|
-
debugStatus: `backend=namespace-unavailable error=${detail}`
|
|
7416
|
-
};
|
|
7417
|
-
} finally {
|
|
7418
|
-
clearTimeout(timer);
|
|
7419
|
-
}
|
|
7623
|
+
return this.accessObserveWriteSurface.namespaceQmdHealth(
|
|
7624
|
+
searchBackend,
|
|
7625
|
+
qmdEnabled,
|
|
7626
|
+
namespace,
|
|
7627
|
+
fallbackCollection
|
|
7628
|
+
);
|
|
7420
7629
|
}
|
|
7421
7630
|
async qmdCollectionState(searchBackend, qmdEnabled, collection) {
|
|
7422
7631
|
if (searchBackend !== "qmd" || !qmdEnabled) return "skipped";
|
|
@@ -7471,70 +7680,14 @@ var EngramAccessService = class {
|
|
|
7471
7680
|
if (memories.length === 0) {
|
|
7472
7681
|
return this.orchestrator.generateDaySummaryAuto(namespace, {
|
|
7473
7682
|
timeZone: request.timeZone
|
|
7474
|
-
});
|
|
7475
|
-
}
|
|
7476
|
-
return this.orchestrator.generateDaySummary(memories);
|
|
7477
|
-
}
|
|
7478
|
-
/**
|
|
7479
|
-
* Build a daily context briefing. Gracefully degrades when the OpenAI key
|
|
7480
|
-
* or Responses API is unavailable — never throws for LLM-related problems.
|
|
7481
|
-
*/
|
|
7482
|
-
async briefing(request) {
|
|
7483
|
-
const config = this.orchestrator.config;
|
|
7484
|
-
if (!config.briefing.enabled) {
|
|
7485
|
-
throw new EngramAccessInputError("briefing is disabled");
|
|
7486
|
-
}
|
|
7487
|
-
const namespace = this.resolveReadableNamespace(request.namespace, request.principal);
|
|
7488
|
-
const storage = await this.orchestrator.getStorage(namespace);
|
|
7489
|
-
const token = typeof request.since === "string" && request.since.trim().length > 0 ? request.since.trim() : config.briefing.defaultWindow;
|
|
7490
|
-
const window = parseBriefingWindow(token);
|
|
7491
|
-
if (!window) {
|
|
7492
|
-
throw new EngramAccessInputError(`invalid briefing window: ${token}`);
|
|
7493
|
-
}
|
|
7494
|
-
const rawFocus = typeof request.focus === "string" ? request.focus.trim() : "";
|
|
7495
|
-
let focus = null;
|
|
7496
|
-
if (rawFocus.length > 0) {
|
|
7497
|
-
focus = parseBriefingFocus(rawFocus);
|
|
7498
|
-
if (!focus) {
|
|
7499
|
-
throw new EngramAccessInputError(
|
|
7500
|
-
`invalid briefing focus filter: ${request.focus}`
|
|
7501
|
-
);
|
|
7502
|
-
}
|
|
7503
|
-
}
|
|
7504
|
-
const SUPPORTED_FORMATS = ["markdown", "json"];
|
|
7505
|
-
if (typeof request.format === "string" && !SUPPORTED_FORMATS.includes(request.format)) {
|
|
7506
|
-
throw new EngramAccessInputError(
|
|
7507
|
-
`unsupported briefing format: "${request.format}". Accepted: ${SUPPORTED_FORMATS.join(", ")}.`
|
|
7508
|
-
);
|
|
7683
|
+
});
|
|
7509
7684
|
}
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
window,
|
|
7517
|
-
focus,
|
|
7518
|
-
calendarSource,
|
|
7519
|
-
maxFollowups,
|
|
7520
|
-
allowLlm: config.briefing.llmFollowups,
|
|
7521
|
-
openaiApiKey: config.openaiApiKey,
|
|
7522
|
-
openaiBaseUrl: config.openaiBaseUrl,
|
|
7523
|
-
model: config.model,
|
|
7524
|
-
// Without a direct OpenAI key, route follow-ups through the configured
|
|
7525
|
-
// LLM chain (gateway model source or local LLM) — same fallback every
|
|
7526
|
-
// other LLM feature uses. A configured key keeps its precedence so
|
|
7527
|
-
// existing deployments are unchanged.
|
|
7528
|
-
followupGenerator: config.openaiApiKey ? void 0 : this.orchestrator.briefingChainFollowupGenerator
|
|
7529
|
-
});
|
|
7530
|
-
return {
|
|
7531
|
-
format,
|
|
7532
|
-
window: result.window,
|
|
7533
|
-
namespace,
|
|
7534
|
-
markdown: result.markdown,
|
|
7535
|
-
json: result.json,
|
|
7536
|
-
followupsUnavailableReason: result.followupsUnavailableReason
|
|
7537
|
-
};
|
|
7685
|
+
return this.orchestrator.generateDaySummary(memories);
|
|
7686
|
+
}
|
|
7687
|
+
async briefing(request) {
|
|
7688
|
+
return this.accessObserveWriteSurface.briefing(
|
|
7689
|
+
request
|
|
7690
|
+
);
|
|
7538
7691
|
}
|
|
7539
7692
|
/**
|
|
7540
7693
|
* Attach a coding context to a session (issue #569). Used by the Claude
|
|
@@ -7760,63 +7913,10 @@ var EngramAccessService = class {
|
|
|
7760
7913
|
// per-tenant) do not block each other.
|
|
7761
7914
|
xrayQueue = Promise.resolve();
|
|
7762
7915
|
async memoryStore(request, hooks) {
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
}
|
|
7768
|
-
const execute = async () => {
|
|
7769
|
-
const candidate = this.validateWriteCandidate(request, namespace);
|
|
7770
|
-
if (request.dryRun === true) {
|
|
7771
|
-
return {
|
|
7772
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
7773
|
-
operation: "memory_store",
|
|
7774
|
-
namespace,
|
|
7775
|
-
dryRun: true,
|
|
7776
|
-
accepted: true,
|
|
7777
|
-
queued: false,
|
|
7778
|
-
status: "validated",
|
|
7779
|
-
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
7780
|
-
};
|
|
7781
|
-
}
|
|
7782
|
-
const result = await persistExplicitCapture(this.orchestrator, candidate, "memory_store");
|
|
7783
|
-
await this.attachCodingContextAfterScopedWrite(request);
|
|
7784
|
-
const blocked = result.tombstoneBlocked === true;
|
|
7785
|
-
const response = {
|
|
7786
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
7787
|
-
operation: "memory_store",
|
|
7788
|
-
namespace,
|
|
7789
|
-
dryRun: false,
|
|
7790
|
-
accepted: true,
|
|
7791
|
-
queued: blocked,
|
|
7792
|
-
status: blocked ? "queued_for_review" : result.duplicateOf ? "duplicate" : "stored",
|
|
7793
|
-
memoryId: result.id,
|
|
7794
|
-
duplicateOf: result.duplicateOf,
|
|
7795
|
-
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
7796
|
-
};
|
|
7797
|
-
log.info(
|
|
7798
|
-
`access-write op=memory_store namespace=${namespace} dryRun=false status=${response.status} memoryId=${response.memoryId ?? "-"} idempotency=${response.idempotencyKey ? "yes" : "no"}`
|
|
7799
|
-
);
|
|
7800
|
-
return response;
|
|
7801
|
-
};
|
|
7802
|
-
return this.handleIdempotentWrite({
|
|
7803
|
-
operation: "memory_store",
|
|
7804
|
-
idempotencyKey: request.idempotencyKey,
|
|
7805
|
-
requestFingerprint: {
|
|
7806
|
-
schemaVersion,
|
|
7807
|
-
content: request.content,
|
|
7808
|
-
category: request.category,
|
|
7809
|
-
confidence: request.confidence,
|
|
7810
|
-
namespace,
|
|
7811
|
-
tags: request.tags,
|
|
7812
|
-
entityRef: request.entityRef,
|
|
7813
|
-
ttl: request.ttl,
|
|
7814
|
-
sourceReason: request.sourceReason
|
|
7815
|
-
},
|
|
7816
|
-
skip: request.dryRun === true,
|
|
7817
|
-
beforeExecute: hooks?.enforceWriteQuota,
|
|
7818
|
-
execute
|
|
7819
|
-
});
|
|
7916
|
+
return this.accessObserveWriteSurface.memoryStore(
|
|
7917
|
+
request,
|
|
7918
|
+
hooks
|
|
7919
|
+
);
|
|
7820
7920
|
}
|
|
7821
7921
|
async peekMemoryStoreIdempotency(request) {
|
|
7822
7922
|
const namespace = await this.resolveCodingScopedWriteNamespace(request);
|
|
@@ -7842,67 +7942,10 @@ var EngramAccessService = class {
|
|
|
7842
7942
|
});
|
|
7843
7943
|
}
|
|
7844
7944
|
async suggestionSubmit(request, hooks) {
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
}
|
|
7850
|
-
const execute = async () => {
|
|
7851
|
-
const candidate = this.validateWriteCandidate(request, namespace);
|
|
7852
|
-
if (request.dryRun === true) {
|
|
7853
|
-
return {
|
|
7854
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
7855
|
-
operation: "suggestion_submit",
|
|
7856
|
-
namespace,
|
|
7857
|
-
dryRun: true,
|
|
7858
|
-
accepted: true,
|
|
7859
|
-
queued: true,
|
|
7860
|
-
status: "validated",
|
|
7861
|
-
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
7862
|
-
};
|
|
7863
|
-
}
|
|
7864
|
-
const result = await queueExplicitCaptureForReview(
|
|
7865
|
-
this.orchestrator,
|
|
7866
|
-
candidate,
|
|
7867
|
-
"suggestion_submit",
|
|
7868
|
-
new Error(request.sourceReason?.trim() || "submitted via engram suggestion_submit")
|
|
7869
|
-
);
|
|
7870
|
-
await this.attachCodingContextAfterScopedWrite(request);
|
|
7871
|
-
const response = {
|
|
7872
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
7873
|
-
operation: "suggestion_submit",
|
|
7874
|
-
namespace,
|
|
7875
|
-
dryRun: false,
|
|
7876
|
-
accepted: true,
|
|
7877
|
-
queued: true,
|
|
7878
|
-
status: "queued_for_review",
|
|
7879
|
-
memoryId: result.id,
|
|
7880
|
-
duplicateOf: result.duplicateOf,
|
|
7881
|
-
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
7882
|
-
};
|
|
7883
|
-
log.info(
|
|
7884
|
-
`access-write op=suggestion_submit namespace=${namespace} dryRun=false status=${response.status} memoryId=${response.memoryId ?? "-"} idempotency=${response.idempotencyKey ? "yes" : "no"}`
|
|
7885
|
-
);
|
|
7886
|
-
return response;
|
|
7887
|
-
};
|
|
7888
|
-
return this.handleIdempotentWrite({
|
|
7889
|
-
operation: "suggestion_submit",
|
|
7890
|
-
idempotencyKey: request.idempotencyKey,
|
|
7891
|
-
requestFingerprint: {
|
|
7892
|
-
schemaVersion,
|
|
7893
|
-
content: request.content,
|
|
7894
|
-
category: request.category,
|
|
7895
|
-
confidence: request.confidence,
|
|
7896
|
-
namespace,
|
|
7897
|
-
tags: request.tags,
|
|
7898
|
-
entityRef: request.entityRef,
|
|
7899
|
-
ttl: request.ttl,
|
|
7900
|
-
sourceReason: request.sourceReason
|
|
7901
|
-
},
|
|
7902
|
-
skip: request.dryRun === true,
|
|
7903
|
-
beforeExecute: hooks?.enforceWriteQuota,
|
|
7904
|
-
execute
|
|
7905
|
-
});
|
|
7945
|
+
return this.accessObserveWriteSurface.suggestionSubmit(
|
|
7946
|
+
request,
|
|
7947
|
+
hooks
|
|
7948
|
+
);
|
|
7906
7949
|
}
|
|
7907
7950
|
async peekSuggestionSubmitIdempotency(request) {
|
|
7908
7951
|
const namespace = await this.resolveCodingScopedWriteNamespace(request);
|
|
@@ -8716,145 +8759,34 @@ var EngramAccessService = class {
|
|
|
8716
8759
|
}
|
|
8717
8760
|
serializeMemorySummary(memory, baseDir, disclosure, rawExcerpts) {
|
|
8718
8761
|
return shapeMemorySummary(memory, baseDir, disclosure, rawExcerpts);
|
|
8719
|
-
}
|
|
8720
|
-
async observe(request, hooks) {
|
|
8721
|
-
const idempotencyKey = request.idempotencyKey?.trim();
|
|
8722
|
-
let effectiveCodingContext;
|
|
8723
|
-
if (idempotencyKey && !(typeof request.namespace === "string" && request.namespace.trim().length > 0)) {
|
|
8724
|
-
effectiveCodingContext = (typeof this.orchestrator.getCodingContextForSession === "function" ? this.orchestrator.getCodingContextForSession(request.sessionKey) : null) ?? await this.resolveCodingContextFromOptions(request);
|
|
8725
|
-
}
|
|
8726
|
-
return this.handleIdempotentWrite({
|
|
8727
|
-
operation: "observe",
|
|
8728
|
-
idempotencyKey: request.idempotencyKey,
|
|
8729
|
-
requestFingerprint: {
|
|
8730
|
-
sessionKey: request.sessionKey,
|
|
8731
|
-
messages: request.messages,
|
|
8732
|
-
namespace: request.namespace,
|
|
8733
|
-
skipExtraction: request.skipExtraction,
|
|
8734
|
-
authenticatedPrincipal: request.authenticatedPrincipal,
|
|
8735
|
-
cwd: request.cwd,
|
|
8736
|
-
projectTag: request.projectTag,
|
|
8737
|
-
effectiveCodingContext: effectiveCodingContext ?? null
|
|
8738
|
-
},
|
|
8739
|
-
beforeExecute: hooks?.enforceWriteQuota,
|
|
8740
|
-
execute: () => this.runObserve(request)
|
|
8741
|
-
});
|
|
8742
|
-
}
|
|
8743
|
-
async runObserve(request) {
|
|
8744
|
-
if (!request.sessionKey || typeof request.sessionKey !== "string" || request.sessionKey.trim().length === 0) {
|
|
8745
|
-
throw new EngramAccessInputError("sessionKey is required and must be a non-empty string");
|
|
8746
|
-
}
|
|
8747
|
-
if (!Array.isArray(request.messages) || request.messages.length === 0) {
|
|
8748
|
-
throw new EngramAccessInputError("messages is required and must be a non-empty array");
|
|
8749
|
-
}
|
|
8750
|
-
for (const msg of request.messages) {
|
|
8751
|
-
if (!msg || typeof msg !== "object" || typeof msg.role !== "string" || typeof msg.content !== "string") {
|
|
8752
|
-
throw new EngramAccessInputError("each message must have a string 'role' and 'content'");
|
|
8753
|
-
}
|
|
8754
|
-
if (msg.role !== "user" && msg.role !== "assistant") {
|
|
8755
|
-
throw new EngramAccessInputError(`invalid message role: ${msg.role} (expected 'user' or 'assistant')`);
|
|
8756
|
-
}
|
|
8757
|
-
}
|
|
8758
|
-
const scope = await this.resolveMemoryScopePlan(request);
|
|
8759
|
-
const writeNamespace = scope.writeNamespace;
|
|
8760
|
-
const namespace = this.legacyResponseNamespaceForScope(scope);
|
|
8761
|
-
const shouldWriteObjectiveState = resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateMemory === true && resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateSnapshotWrites === true;
|
|
8762
|
-
await this.maybeAttachCodingContext(request.sessionKey, {
|
|
8763
|
-
cwd: request.cwd,
|
|
8764
|
-
projectTag: request.projectTag
|
|
8765
|
-
});
|
|
8766
|
-
const lcmSessionKey = lcmSessionKeyForNamespace(
|
|
8767
|
-
writeNamespace,
|
|
8768
|
-
request.sessionKey,
|
|
8769
|
-
this.orchestrator.config.defaultNamespace
|
|
8770
|
-
) ?? request.sessionKey;
|
|
8771
|
-
if (shouldWriteObjectiveState) {
|
|
8772
|
-
try {
|
|
8773
|
-
const objectiveStateLocation = await this.objectiveStateStoreLocationForNamespace(
|
|
8774
|
-
scope.objectiveStateNamespace
|
|
8775
|
-
);
|
|
8776
|
-
await recordObjectiveStateSnapshotsFromObservedMessages({
|
|
8777
|
-
memoryDir: objectiveStateLocation.memoryDir,
|
|
8778
|
-
objectiveStateStoreDir: objectiveStateLocation.objectiveStateStoreDir,
|
|
8779
|
-
objectiveStateMemoryEnabled: resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateMemory,
|
|
8780
|
-
objectiveStateSnapshotWritesEnabled: resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateSnapshotWrites,
|
|
8781
|
-
sessionKey: request.sessionKey,
|
|
8782
|
-
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8783
|
-
messages: request.messages
|
|
8784
|
-
});
|
|
8785
|
-
} catch (err) {
|
|
8786
|
-
log.error(`access-observe objective-state snapshot write failed: ${err}`);
|
|
8787
|
-
}
|
|
8788
|
-
}
|
|
8789
|
-
let lcmArchived = false;
|
|
8790
|
-
if (this.orchestrator.lcmEngine && this.orchestrator.lcmEngine.enabled) {
|
|
8791
|
-
try {
|
|
8792
|
-
this.orchestrator.lcmEngine.enqueueObserveMessages(lcmSessionKey, request.messages);
|
|
8793
|
-
lcmArchived = true;
|
|
8794
|
-
} catch (err) {
|
|
8795
|
-
log.error(`access-observe LCM enqueue failed: ${err}`);
|
|
8796
|
-
}
|
|
8797
|
-
}
|
|
8798
|
-
let extractionQueued = false;
|
|
8799
|
-
if (request.skipExtraction !== true) {
|
|
8800
|
-
const turns = request.messages.map((m) => ({
|
|
8801
|
-
source: "openclaw",
|
|
8802
|
-
// Identity-vs-routing separation (#1505 thread 1, cursor): extraction
|
|
8803
|
-
// derives the provenance principal via `resolvePrincipal(turn.sessionKey)`
|
|
8804
|
-
// and threads `turn.sessionKey` into conversation threading. Feeding the
|
|
8805
|
-
// namespace-PREFIXED `lcmSessionKey` here mis-derived the principal to
|
|
8806
|
-
// `default` (a `<ns>:<key>` string matches no prefix/map rule and fails
|
|
8807
|
-
// the `agent:` heuristic). Pass the ORIGINAL sessionKey so identity is
|
|
8808
|
-
// correct; storage routing is pinned separately via
|
|
8809
|
-
// writeNamespaceOverride below, and the authenticated principal is pinned
|
|
8810
|
-
// via principalOverride.
|
|
8811
|
-
sessionKey: request.sessionKey,
|
|
8812
|
-
role: m.role,
|
|
8813
|
-
content: m.content,
|
|
8814
|
-
parts: m.parts,
|
|
8815
|
-
rawContent: m.rawContent,
|
|
8816
|
-
sourceFormat: m.sourceFormat,
|
|
8817
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8818
|
-
}));
|
|
8819
|
-
const writeNamespaceOverride = resolveNamespaceCapabilities(this.orchestrator.config).namespaces === true ? writeNamespace : void 0;
|
|
8820
|
-
const principalOverride = typeof scope.principal === "string" && scope.principal.length > 0 ? scope.principal : void 0;
|
|
8821
|
-
try {
|
|
8822
|
-
const extractionPromise = this.orchestrator.ingestReplayBatch(turns, {
|
|
8823
|
-
archiveLcm: false,
|
|
8824
|
-
writeNamespaceOverride,
|
|
8825
|
-
principalOverride
|
|
8826
|
-
});
|
|
8827
|
-
extractionPromise.catch((err) => {
|
|
8828
|
-
log.error(`access-observe background extraction failed: ${err}`);
|
|
8829
|
-
});
|
|
8830
|
-
extractionQueued = true;
|
|
8831
|
-
} catch (err) {
|
|
8832
|
-
log.error(`access-observe extraction enqueue failed: ${err}`);
|
|
8833
|
-
}
|
|
8762
|
+
}
|
|
8763
|
+
async observe(request, hooks) {
|
|
8764
|
+
const idempotencyKey = request.idempotencyKey?.trim();
|
|
8765
|
+
let effectiveCodingContext;
|
|
8766
|
+
if (idempotencyKey && !(typeof request.namespace === "string" && request.namespace.trim().length > 0)) {
|
|
8767
|
+
effectiveCodingContext = (typeof this.orchestrator.getCodingContextForSession === "function" ? this.orchestrator.getCodingContextForSession(request.sessionKey) : null) ?? await this.resolveCodingContextFromOptions(request);
|
|
8834
8768
|
}
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8839
|
-
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
writeNamespace: scope.writeNamespace,
|
|
8848
|
-
codingOverlayApplied: scope.codingOverlayApplied,
|
|
8849
|
-
readNamespaces: scope.readNamespaces,
|
|
8850
|
-
scopeProfile: scope.scopeProfile,
|
|
8851
|
-
writeLayer: scope.writeLayer,
|
|
8852
|
-
layers: scope.layers,
|
|
8853
|
-
promotionTargets: scope.promotionTargets
|
|
8769
|
+
return this.handleIdempotentWrite({
|
|
8770
|
+
operation: "observe",
|
|
8771
|
+
idempotencyKey: request.idempotencyKey,
|
|
8772
|
+
requestFingerprint: {
|
|
8773
|
+
sessionKey: request.sessionKey,
|
|
8774
|
+
messages: request.messages,
|
|
8775
|
+
namespace: request.namespace,
|
|
8776
|
+
skipExtraction: request.skipExtraction,
|
|
8777
|
+
authenticatedPrincipal: request.authenticatedPrincipal,
|
|
8778
|
+
cwd: request.cwd,
|
|
8779
|
+
projectTag: request.projectTag,
|
|
8780
|
+
effectiveCodingContext: effectiveCodingContext ?? null
|
|
8854
8781
|
},
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
};
|
|
8782
|
+
beforeExecute: hooks?.enforceWriteQuota,
|
|
8783
|
+
execute: () => this.runObserve(request)
|
|
8784
|
+
});
|
|
8785
|
+
}
|
|
8786
|
+
async runObserve(request) {
|
|
8787
|
+
return this.accessObserveWriteSurface.runObserve(
|
|
8788
|
+
request
|
|
8789
|
+
);
|
|
8858
8790
|
}
|
|
8859
8791
|
async lcmSearch(request) {
|
|
8860
8792
|
return this.accessLcmSurface.lcmSearch(
|
|
@@ -9251,137 +9183,19 @@ var EngramAccessService = class {
|
|
|
9251
9183
|
}
|
|
9252
9184
|
// ── Work Layer ──────────────────────────────────────────────────────────
|
|
9253
9185
|
async workTask(request) {
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
const asPriority = (v) => v && PRIORITIES.has(v) ? v : void 0;
|
|
9258
|
-
const storage = new WorkStorage(this.orchestrator.config.memoryDir);
|
|
9259
|
-
await storage.ensureDirectories();
|
|
9260
|
-
const action = request.action;
|
|
9261
|
-
if (action === "create") {
|
|
9262
|
-
if (!request.title?.trim()) throw new EngramAccessInputError("title is required for create");
|
|
9263
|
-
const task = await storage.createTask({
|
|
9264
|
-
title: request.title,
|
|
9265
|
-
description: request.description,
|
|
9266
|
-
status: asStatus(request.status),
|
|
9267
|
-
priority: asPriority(request.priority),
|
|
9268
|
-
owner: request.owner?.trim() || void 0,
|
|
9269
|
-
assignee: request.assignee?.trim() || void 0,
|
|
9270
|
-
projectId: request.projectId?.trim() || void 0,
|
|
9271
|
-
tags: request.tags,
|
|
9272
|
-
dueAt: request.dueAt?.trim() || void 0
|
|
9273
|
-
});
|
|
9274
|
-
return { action, task };
|
|
9275
|
-
}
|
|
9276
|
-
if (action === "get") {
|
|
9277
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for get");
|
|
9278
|
-
return { action, task: await storage.getTask(request.id) };
|
|
9279
|
-
}
|
|
9280
|
-
if (action === "list") {
|
|
9281
|
-
const tasks = await storage.listTasks({
|
|
9282
|
-
status: asStatus(request.status),
|
|
9283
|
-
owner: request.owner?.trim() || void 0,
|
|
9284
|
-
assignee: request.assignee?.trim() || void 0,
|
|
9285
|
-
projectId: request.projectId?.trim() || void 0
|
|
9286
|
-
});
|
|
9287
|
-
return { action, count: tasks.length, tasks };
|
|
9288
|
-
}
|
|
9289
|
-
if (action === "update") {
|
|
9290
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for update");
|
|
9291
|
-
const patch = {};
|
|
9292
|
-
if (request.title !== void 0) patch.title = request.title;
|
|
9293
|
-
if (request.description !== void 0) patch.description = request.description;
|
|
9294
|
-
const st = asStatus(request.status);
|
|
9295
|
-
if (st) patch.status = st;
|
|
9296
|
-
const pr = asPriority(request.priority);
|
|
9297
|
-
if (pr) patch.priority = pr;
|
|
9298
|
-
if (request.owner !== void 0) patch.owner = request.owner || null;
|
|
9299
|
-
if (request.assignee !== void 0) patch.assignee = request.assignee || null;
|
|
9300
|
-
if (request.projectId !== void 0) patch.projectId = request.projectId || null;
|
|
9301
|
-
if (request.tags) patch.tags = request.tags;
|
|
9302
|
-
if (request.dueAt !== void 0) patch.dueAt = request.dueAt || null;
|
|
9303
|
-
return { action, task: await storage.updateTask(request.id, patch) };
|
|
9304
|
-
}
|
|
9305
|
-
if (action === "transition") {
|
|
9306
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for transition");
|
|
9307
|
-
const st = asStatus(request.status);
|
|
9308
|
-
if (!st) throw new EngramAccessInputError("valid status is required for transition");
|
|
9309
|
-
return { action, task: await storage.transitionTask(request.id, st) };
|
|
9310
|
-
}
|
|
9311
|
-
if (action === "delete") {
|
|
9312
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for delete");
|
|
9313
|
-
return { action, deleted: await storage.deleteTask(request.id) };
|
|
9314
|
-
}
|
|
9315
|
-
throw new EngramAccessInputError(`Unsupported work_task action: ${action}`);
|
|
9186
|
+
return this.accessObserveWriteSurface.workTask(
|
|
9187
|
+
request
|
|
9188
|
+
);
|
|
9316
9189
|
}
|
|
9317
9190
|
async workProject(request) {
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
await storage.ensureDirectories();
|
|
9322
|
-
const action = request.action;
|
|
9323
|
-
if (action === "create") {
|
|
9324
|
-
if (!request.name?.trim()) throw new EngramAccessInputError("name is required for create");
|
|
9325
|
-
const project = await storage.createProject({
|
|
9326
|
-
name: request.name,
|
|
9327
|
-
description: request.description,
|
|
9328
|
-
status: asStatus(request.status),
|
|
9329
|
-
owner: request.owner?.trim() || void 0,
|
|
9330
|
-
tags: request.tags
|
|
9331
|
-
});
|
|
9332
|
-
return { action, project };
|
|
9333
|
-
}
|
|
9334
|
-
if (action === "get") {
|
|
9335
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for get");
|
|
9336
|
-
return { action, project: await storage.getProject(request.id) };
|
|
9337
|
-
}
|
|
9338
|
-
if (action === "list") {
|
|
9339
|
-
const projects = await storage.listProjects();
|
|
9340
|
-
return { action, count: projects.length, projects };
|
|
9341
|
-
}
|
|
9342
|
-
if (action === "update") {
|
|
9343
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for update");
|
|
9344
|
-
const patch = {};
|
|
9345
|
-
if (request.name !== void 0) patch.name = request.name;
|
|
9346
|
-
if (request.description !== void 0) patch.description = request.description;
|
|
9347
|
-
const st = asStatus(request.status);
|
|
9348
|
-
if (st) patch.status = st;
|
|
9349
|
-
if (request.owner !== void 0) patch.owner = request.owner || null;
|
|
9350
|
-
if (request.tags) patch.tags = request.tags;
|
|
9351
|
-
return { action, project: await storage.updateProject(request.id, patch) };
|
|
9352
|
-
}
|
|
9353
|
-
if (action === "delete") {
|
|
9354
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for delete");
|
|
9355
|
-
return { action, deleted: await storage.deleteProject(request.id) };
|
|
9356
|
-
}
|
|
9357
|
-
if (action === "link_task") {
|
|
9358
|
-
if (!request.taskId?.trim() || !request.projectId?.trim()) {
|
|
9359
|
-
throw new EngramAccessInputError("taskId and projectId are required for link_task");
|
|
9360
|
-
}
|
|
9361
|
-
return { action, linked: await storage.linkTaskToProject(request.taskId, request.projectId) };
|
|
9362
|
-
}
|
|
9363
|
-
throw new EngramAccessInputError(`Unsupported work_project action: ${action}`);
|
|
9191
|
+
return this.accessObserveWriteSurface.workProject(
|
|
9192
|
+
request
|
|
9193
|
+
);
|
|
9364
9194
|
}
|
|
9365
9195
|
async workBoard(request) {
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
const projectId = request.projectId?.trim() || void 0;
|
|
9370
|
-
if (action === "export_markdown") {
|
|
9371
|
-
const markdown = await exportWorkBoardMarkdown({ memoryDir, projectId });
|
|
9372
|
-
return { action, markdown: wrapWorkLayerContext(markdown, { linkToMemory: request.linkToMemory === true }) };
|
|
9373
|
-
}
|
|
9374
|
-
if (action === "export_snapshot") {
|
|
9375
|
-
const snapshot = await exportWorkBoardSnapshot({ memoryDir, projectId });
|
|
9376
|
-
return { action, snapshot };
|
|
9377
|
-
}
|
|
9378
|
-
if (action === "import_snapshot") {
|
|
9379
|
-
if (!request.snapshotJson?.trim()) throw new EngramAccessInputError("snapshotJson is required for import_snapshot");
|
|
9380
|
-
const snapshot = JSON.parse(request.snapshotJson);
|
|
9381
|
-
const result = await importWorkBoardSnapshot({ memoryDir, snapshot, projectId });
|
|
9382
|
-
return { action, result };
|
|
9383
|
-
}
|
|
9384
|
-
throw new EngramAccessInputError(`Unsupported work_board action: ${action}`);
|
|
9196
|
+
return this.accessObserveWriteSurface.workBoard(
|
|
9197
|
+
request
|
|
9198
|
+
);
|
|
9385
9199
|
}
|
|
9386
9200
|
// ── Shared Context / Compounding ────────────────────────────────────────
|
|
9387
9201
|
async sharedContextWriteOutput(request) {
|
|
@@ -9665,90 +9479,35 @@ ${next}`);
|
|
|
9665
9479
|
*/
|
|
9666
9480
|
async memoryOutcome(request) {
|
|
9667
9481
|
if (request.memoryId.includes("/") || request.memoryId.includes("\\")) {
|
|
9668
|
-
throw new EngramAccessInputError(
|
|
9669
|
-
"memoryId must not contain path separators"
|
|
9670
|
-
);
|
|
9671
|
-
}
|
|
9672
|
-
const resolvedNs = this.writableNamespaceFor(
|
|
9673
|
-
request.namespace,
|
|
9674
|
-
request.sessionKey,
|
|
9675
|
-
request.principal
|
|
9676
|
-
);
|
|
9677
|
-
const storage = await this.orchestrator.getStorage(resolvedNs);
|
|
9678
|
-
return recordMemoryOutcome(storage, {
|
|
9679
|
-
memoryPath: `${request.memoryId}.md`,
|
|
9680
|
-
outcome: request.outcome,
|
|
9681
|
-
timestamp: request.timestamp
|
|
9682
|
-
});
|
|
9683
|
-
}
|
|
9684
|
-
async memoryPromote(request) {
|
|
9685
|
-
const resolvedNs = this.writableNamespaceFor(request.namespace, request.sessionKey, request.principal);
|
|
9686
|
-
const storage = await this.orchestrator.getStorage(resolvedNs);
|
|
9687
|
-
await storage.updateMemoryFrontmatter(request.memoryId, {
|
|
9688
|
-
lifecycleState: "active",
|
|
9689
|
-
updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
9690
|
-
});
|
|
9691
|
-
return { promoted: true, memoryId: request.memoryId };
|
|
9692
|
-
}
|
|
9693
|
-
async memoryActionApply(request) {
|
|
9694
|
-
const actionTypes = /* @__PURE__ */ new Set([
|
|
9695
|
-
"store_episode",
|
|
9696
|
-
"store_note",
|
|
9697
|
-
"update_note",
|
|
9698
|
-
"create_artifact",
|
|
9699
|
-
"summarize_node",
|
|
9700
|
-
"discard",
|
|
9701
|
-
"link_graph"
|
|
9702
|
-
]);
|
|
9703
|
-
if (!actionTypes.has(request.action)) {
|
|
9704
|
-
throw new EngramAccessInputError(
|
|
9705
|
-
`memory_action_apply: invalid action ${JSON.stringify(request.action)}`
|
|
9706
|
-
);
|
|
9707
|
-
}
|
|
9708
|
-
if (resolveCompressionCapabilities(this.orchestrator.config).contextCompressionActions !== true) {
|
|
9709
|
-
throw new EngramAccessInputError(
|
|
9710
|
-
"memory_action_apply is disabled; enable contextCompressionActionsEnabled to use this tool"
|
|
9711
|
-
);
|
|
9712
|
-
}
|
|
9713
|
-
const outcome = request.outcome ?? "skipped";
|
|
9714
|
-
if (outcome !== "applied" && outcome !== "skipped" && outcome !== "failed") {
|
|
9715
|
-
throw new EngramAccessInputError(
|
|
9716
|
-
`memory_action_apply: outcome must be "applied", "skipped", or "failed"; got ${JSON.stringify(outcome)}`
|
|
9717
|
-
);
|
|
9718
|
-
}
|
|
9719
|
-
const resolvedNs = this.writableNamespaceFor(
|
|
9720
|
-
request.namespace,
|
|
9721
|
-
request.sessionKey,
|
|
9722
|
-
request.principal
|
|
9723
|
-
);
|
|
9724
|
-
const inputSummaryParts = [
|
|
9725
|
-
request.content,
|
|
9726
|
-
request.category ? `category=${request.category}` : void 0,
|
|
9727
|
-
request.linkTargetId ? `linkTargetId=${request.linkTargetId}` : void 0,
|
|
9728
|
-
request.linkType ? `linkType=${request.linkType}` : void 0,
|
|
9729
|
-
typeof request.linkStrength === "number" ? `linkStrength=${request.linkStrength}` : void 0,
|
|
9730
|
-
request.artifactType ? `artifactType=${request.artifactType}` : void 0,
|
|
9731
|
-
typeof request.execute === "boolean" ? `execute=${request.execute}` : void 0
|
|
9732
|
-
].filter((part) => typeof part === "string" && part.length > 0);
|
|
9733
|
-
const event = {
|
|
9734
|
-
action: request.action,
|
|
9735
|
-
outcome,
|
|
9736
|
-
namespace: resolvedNs,
|
|
9737
|
-
actor: "access.memory_action_apply",
|
|
9738
|
-
subsystem: "access.memory_action_apply",
|
|
9739
|
-
reason: request.reason,
|
|
9740
|
-
memoryId: request.memoryId,
|
|
9741
|
-
sourceSessionKey: request.sessionKey,
|
|
9742
|
-
inputSummary: inputSummaryParts.length > 0 ? inputSummaryParts.join(" | ").slice(0, 500) : void 0,
|
|
9743
|
-
dryRun: request.dryRun === true,
|
|
9744
|
-
promptHash: typeof request.sourcePrompt === "string" && request.sourcePrompt.length > 0 ? createHash5("sha256").update(request.sourcePrompt).digest("hex") : void 0
|
|
9745
|
-
};
|
|
9746
|
-
const preview = this.orchestrator.previewMemoryActionEvent(event);
|
|
9747
|
-
if (request.dryRun === true) {
|
|
9748
|
-
return { recorded: false, dryRun: true, event: preview };
|
|
9482
|
+
throw new EngramAccessInputError(
|
|
9483
|
+
"memoryId must not contain path separators"
|
|
9484
|
+
);
|
|
9749
9485
|
}
|
|
9750
|
-
const
|
|
9751
|
-
|
|
9486
|
+
const resolvedNs = this.writableNamespaceFor(
|
|
9487
|
+
request.namespace,
|
|
9488
|
+
request.sessionKey,
|
|
9489
|
+
request.principal
|
|
9490
|
+
);
|
|
9491
|
+
const storage = await this.orchestrator.getStorage(resolvedNs);
|
|
9492
|
+
return recordMemoryOutcome(storage, {
|
|
9493
|
+
memoryPath: `${request.memoryId}.md`,
|
|
9494
|
+
outcome: request.outcome,
|
|
9495
|
+
timestamp: request.timestamp
|
|
9496
|
+
});
|
|
9497
|
+
}
|
|
9498
|
+
async memoryPromote(request) {
|
|
9499
|
+
const resolvedNs = this.writableNamespaceFor(request.namespace, request.sessionKey, request.principal);
|
|
9500
|
+
const storage = await this.orchestrator.getStorage(resolvedNs);
|
|
9501
|
+
await storage.updateMemoryFrontmatter(request.memoryId, {
|
|
9502
|
+
lifecycleState: "active",
|
|
9503
|
+
updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
9504
|
+
});
|
|
9505
|
+
return { promoted: true, memoryId: request.memoryId };
|
|
9506
|
+
}
|
|
9507
|
+
async memoryActionApply(request) {
|
|
9508
|
+
return this.accessObserveWriteSurface.memoryActionApply(
|
|
9509
|
+
request
|
|
9510
|
+
);
|
|
9752
9511
|
}
|
|
9753
9512
|
async contextCheckpoint(request) {
|
|
9754
9513
|
const resolvedNs = this.writableNamespaceFor(request.namespace, request.sessionKey, request.principal);
|
|
@@ -10365,397 +10124,699 @@ ${next}`);
|
|
|
10365
10124
|
limit: request.limit
|
|
10366
10125
|
});
|
|
10367
10126
|
}
|
|
10368
|
-
async wearablesTranscriptMemories(request) {
|
|
10369
|
-
return this.orchestrator.getWearablesService().transcriptMemories({
|
|
10370
|
-
source: request.source,
|
|
10371
|
-
date: request.date,
|
|
10372
|
-
limit: request.limit
|
|
10127
|
+
async wearablesTranscriptMemories(request) {
|
|
10128
|
+
return this.orchestrator.getWearablesService().transcriptMemories({
|
|
10129
|
+
source: request.source,
|
|
10130
|
+
date: request.date,
|
|
10131
|
+
limit: request.limit
|
|
10132
|
+
});
|
|
10133
|
+
}
|
|
10134
|
+
// ── Admin console surfaces (issue #1502) ────────────────────────────────
|
|
10135
|
+
//
|
|
10136
|
+
// Thin delegators. Every method threads `this.orchestrator` references
|
|
10137
|
+
// into the pure `admin/admin-surfaces.ts` functions so the dashboard
|
|
10138
|
+
// never re-resolves scope, re-derives promotion targets, or re-lists
|
|
10139
|
+
// namespaces.
|
|
10140
|
+
/**
|
|
10141
|
+
* Resolve an effective scope inspection for the console. Inputs are
|
|
10142
|
+
* redacted before return so an operator-supplied principal preview /
|
|
10143
|
+
* namespace override never echoes a pasted credential back.
|
|
10144
|
+
*/
|
|
10145
|
+
async adminInspectScope(options) {
|
|
10146
|
+
const config = this.orchestrator.config;
|
|
10147
|
+
const codingContext = options.sessionKey ? this.orchestrator.getCodingContextForSession(options.sessionKey) ?? null : null;
|
|
10148
|
+
const inspection = inspectScope({
|
|
10149
|
+
config,
|
|
10150
|
+
sessionKey: options.sessionKey,
|
|
10151
|
+
namespace: options.namespace,
|
|
10152
|
+
principalOverride: options.principalOverride,
|
|
10153
|
+
codingContext,
|
|
10154
|
+
operation: options.operation
|
|
10155
|
+
});
|
|
10156
|
+
return redactSensitive(inspection);
|
|
10157
|
+
}
|
|
10158
|
+
/**
|
|
10159
|
+
* List configured + discovered namespaces from the catalog with the
|
|
10160
|
+
* admin filters the console exposes.
|
|
10161
|
+
*/
|
|
10162
|
+
async adminListNamespaces(filter) {
|
|
10163
|
+
const result = await listAdminNamespaces({
|
|
10164
|
+
catalog: this.orchestrator.namespaceCatalog,
|
|
10165
|
+
filter
|
|
10166
|
+
});
|
|
10167
|
+
return redactSensitive(result);
|
|
10168
|
+
}
|
|
10169
|
+
/**
|
|
10170
|
+
* Per-namespace maintenance + QMD health. QMD diagnostics are probed via
|
|
10171
|
+
* the orchestrator's search health accessor (one probe per namespace).
|
|
10172
|
+
*/
|
|
10173
|
+
async adminMaintenanceHealth() {
|
|
10174
|
+
const report = await gatherMaintenanceHealth({
|
|
10175
|
+
catalog: this.orchestrator.namespaceCatalog,
|
|
10176
|
+
qmdHealthProvider: async (namespace) => {
|
|
10177
|
+
try {
|
|
10178
|
+
const health = await this.orchestrator.searchHealthForNamespace(namespace);
|
|
10179
|
+
return {
|
|
10180
|
+
namespace,
|
|
10181
|
+
collection: health.collection,
|
|
10182
|
+
available: health.available,
|
|
10183
|
+
collectionState: health.collectionState,
|
|
10184
|
+
debugStatus: health.debugStatus,
|
|
10185
|
+
installedVersion: health.installedVersion,
|
|
10186
|
+
supportedVersion: health.supportedVersion,
|
|
10187
|
+
supported: health.supported,
|
|
10188
|
+
upgradeAvailable: health.upgradeAvailable,
|
|
10189
|
+
daemonMode: health.daemonMode
|
|
10190
|
+
};
|
|
10191
|
+
} catch (err) {
|
|
10192
|
+
throw err;
|
|
10193
|
+
}
|
|
10194
|
+
}
|
|
10195
|
+
});
|
|
10196
|
+
return redactSensitive(report);
|
|
10197
|
+
}
|
|
10198
|
+
/**
|
|
10199
|
+
* Dry-run transcript/session audit. Detects mixed `other/default` data
|
|
10200
|
+
* and legacy stranded directories. Never applies a migration.
|
|
10201
|
+
*/
|
|
10202
|
+
async adminTranscriptAudit() {
|
|
10203
|
+
const result = await auditTranscripts(this.orchestrator.config.memoryDir);
|
|
10204
|
+
return redactSensitive(result);
|
|
10205
|
+
}
|
|
10206
|
+
async adminPromoteMemory(request) {
|
|
10207
|
+
return this.accessAdminOpsSurface.adminPromoteMemory(
|
|
10208
|
+
request
|
|
10209
|
+
);
|
|
10210
|
+
}
|
|
10211
|
+
};
|
|
10212
|
+
|
|
10213
|
+
// src/access-observe-write-surface.ts
|
|
10214
|
+
var AccessObserveWriteSurface = class {
|
|
10215
|
+
constructor(deps) {
|
|
10216
|
+
this.deps = deps;
|
|
10217
|
+
}
|
|
10218
|
+
deps;
|
|
10219
|
+
async qmdHealth(searchBackend, qmdEnabled, namespace, collection) {
|
|
10220
|
+
if (searchBackend !== "qmd" || !qmdEnabled) {
|
|
10221
|
+
return {
|
|
10222
|
+
enabled: qmdEnabled,
|
|
10223
|
+
active: false,
|
|
10224
|
+
degraded: false,
|
|
10225
|
+
mode: searchBackend !== "qmd" ? "not-selected" : "disabled",
|
|
10226
|
+
collection,
|
|
10227
|
+
collectionState: "skipped",
|
|
10228
|
+
installedVersion: null,
|
|
10229
|
+
supportedVersion: null,
|
|
10230
|
+
supported: null,
|
|
10231
|
+
upgradeAvailable: null,
|
|
10232
|
+
doctorAvailable: null,
|
|
10233
|
+
debugStatus: searchBackend !== "qmd" ? `backend=${searchBackend}` : "backend=disabled"
|
|
10234
|
+
};
|
|
10235
|
+
}
|
|
10236
|
+
if (resolveNamespaceCapabilities(this.deps.orchestrator.config).namespaces === true) {
|
|
10237
|
+
const namespaceHealth = await this.deps.namespaceQmdHealth(searchBackend, qmdEnabled, namespace, collection);
|
|
10238
|
+
if (namespaceHealth) return namespaceHealth;
|
|
10239
|
+
}
|
|
10240
|
+
const qmd = this.deps.orchestrator.qmd;
|
|
10241
|
+
if (!qmd) {
|
|
10242
|
+
return {
|
|
10243
|
+
enabled: true,
|
|
10244
|
+
active: false,
|
|
10245
|
+
degraded: true,
|
|
10246
|
+
mode: "fallback",
|
|
10247
|
+
collection,
|
|
10248
|
+
collectionState: "unknown",
|
|
10249
|
+
installedVersion: null,
|
|
10250
|
+
supportedVersion: null,
|
|
10251
|
+
supported: null,
|
|
10252
|
+
upgradeAvailable: null,
|
|
10253
|
+
doctorAvailable: null,
|
|
10254
|
+
debugStatus: "backend=unavailable"
|
|
10255
|
+
};
|
|
10256
|
+
}
|
|
10257
|
+
const diagnosticAvailable = await this.deps.qmdProbeAvailable(searchBackend, qmdEnabled);
|
|
10258
|
+
const operationalAvailable = diagnosticAvailable || qmd.isAvailable();
|
|
10259
|
+
const collectionState = diagnosticAvailable ? await this.deps.qmdCollectionState(searchBackend, qmdEnabled, collection) : "unknown";
|
|
10260
|
+
const active = operationalAvailable && collectionState !== "missing";
|
|
10261
|
+
const degraded = searchBackend === "qmd" && qmdEnabled && (!active || !diagnosticAvailable || collectionState === "unknown");
|
|
10262
|
+
const debugStatus = qmd.debugStatus();
|
|
10263
|
+
const versionStatus = "getVersionStatus" in qmd && typeof qmd.getVersionStatus === "function" ? qmd.getVersionStatus() : null;
|
|
10264
|
+
const daemonMode = "isDaemonMode" in qmd && typeof qmd.isDaemonMode === "function" ? qmd.isDaemonMode() === true : false;
|
|
10265
|
+
const mode = searchBackend !== "qmd" ? "not-selected" : !qmdEnabled ? "disabled" : !active ? "fallback" : daemonMode ? "daemon" : "cli";
|
|
10266
|
+
return {
|
|
10267
|
+
enabled: qmdEnabled,
|
|
10268
|
+
active,
|
|
10269
|
+
degraded,
|
|
10270
|
+
mode,
|
|
10271
|
+
collection,
|
|
10272
|
+
collectionState,
|
|
10273
|
+
installedVersion: versionStatus?.installedVersion ?? null,
|
|
10274
|
+
supportedVersion: versionStatus?.supportedVersion ?? null,
|
|
10275
|
+
supported: versionStatus?.supported ?? null,
|
|
10276
|
+
upgradeAvailable: versionStatus?.upgradeAvailable ?? null,
|
|
10277
|
+
doctorAvailable: versionStatus?.capabilities?.doctor ?? null,
|
|
10278
|
+
debugStatus
|
|
10279
|
+
};
|
|
10280
|
+
}
|
|
10281
|
+
async namespaceQmdHealth(searchBackend, qmdEnabled, namespace, fallbackCollection) {
|
|
10282
|
+
if (searchBackend !== "qmd" || !qmdEnabled) return null;
|
|
10283
|
+
const searchHealthForNamespace = this.deps.orchestrator.searchHealthForNamespace;
|
|
10284
|
+
if (typeof searchHealthForNamespace !== "function") return null;
|
|
10285
|
+
const controller = new AbortController();
|
|
10286
|
+
const timer = setTimeout(() => controller.abort(), 2e3);
|
|
10287
|
+
timer.unref?.();
|
|
10288
|
+
try {
|
|
10289
|
+
const health = await searchHealthForNamespace.call(this.deps.orchestrator, namespace, {
|
|
10290
|
+
signal: controller.signal
|
|
10291
|
+
});
|
|
10292
|
+
const active = health.available && health.collectionState !== "missing";
|
|
10293
|
+
const degraded = !active || health.collectionState === "unknown";
|
|
10294
|
+
const mode = !active ? "fallback" : health.daemonMode === true ? "daemon" : "cli";
|
|
10295
|
+
return {
|
|
10296
|
+
enabled: true,
|
|
10297
|
+
active,
|
|
10298
|
+
degraded,
|
|
10299
|
+
mode,
|
|
10300
|
+
collection: health.collection || fallbackCollection,
|
|
10301
|
+
collectionState: health.collectionState,
|
|
10302
|
+
installedVersion: health.installedVersion,
|
|
10303
|
+
supportedVersion: health.supportedVersion,
|
|
10304
|
+
supported: health.supported,
|
|
10305
|
+
upgradeAvailable: health.upgradeAvailable,
|
|
10306
|
+
doctorAvailable: health.doctorAvailable,
|
|
10307
|
+
debugStatus: health.debugStatus
|
|
10308
|
+
};
|
|
10309
|
+
} catch (error) {
|
|
10310
|
+
const detail = displayErrorDetail(error) || "unknown";
|
|
10311
|
+
return {
|
|
10312
|
+
enabled: true,
|
|
10313
|
+
active: false,
|
|
10314
|
+
degraded: true,
|
|
10315
|
+
mode: "fallback",
|
|
10316
|
+
collection: fallbackCollection,
|
|
10317
|
+
collectionState: "unknown",
|
|
10318
|
+
installedVersion: null,
|
|
10319
|
+
supportedVersion: null,
|
|
10320
|
+
supported: null,
|
|
10321
|
+
upgradeAvailable: null,
|
|
10322
|
+
doctorAvailable: null,
|
|
10323
|
+
debugStatus: `backend=namespace-unavailable error=${detail}`
|
|
10324
|
+
};
|
|
10325
|
+
} finally {
|
|
10326
|
+
clearTimeout(timer);
|
|
10327
|
+
}
|
|
10328
|
+
}
|
|
10329
|
+
/**
|
|
10330
|
+
* Build a daily context briefing. Gracefully degrades when the OpenAI key
|
|
10331
|
+
* or Responses API is unavailable — never throws for LLM-related problems.
|
|
10332
|
+
*/
|
|
10333
|
+
async briefing(request) {
|
|
10334
|
+
const config = this.deps.orchestrator.config;
|
|
10335
|
+
if (!config.briefing.enabled) {
|
|
10336
|
+
throw new EngramAccessInputError("briefing is disabled");
|
|
10337
|
+
}
|
|
10338
|
+
const namespace = this.deps.resolveReadableNamespace(request.namespace, request.principal);
|
|
10339
|
+
const storage = await this.deps.orchestrator.getStorage(namespace);
|
|
10340
|
+
const token = typeof request.since === "string" && request.since.trim().length > 0 ? request.since.trim() : config.briefing.defaultWindow;
|
|
10341
|
+
const window = parseBriefingWindow(token);
|
|
10342
|
+
if (!window) {
|
|
10343
|
+
throw new EngramAccessInputError(`invalid briefing window: ${token}`);
|
|
10344
|
+
}
|
|
10345
|
+
const rawFocus = typeof request.focus === "string" ? request.focus.trim() : "";
|
|
10346
|
+
let focus = null;
|
|
10347
|
+
if (rawFocus.length > 0) {
|
|
10348
|
+
focus = parseBriefingFocus(rawFocus);
|
|
10349
|
+
if (!focus) {
|
|
10350
|
+
throw new EngramAccessInputError(
|
|
10351
|
+
`invalid briefing focus filter: ${request.focus}`
|
|
10352
|
+
);
|
|
10353
|
+
}
|
|
10354
|
+
}
|
|
10355
|
+
const SUPPORTED_FORMATS = ["markdown", "json"];
|
|
10356
|
+
if (typeof request.format === "string" && !SUPPORTED_FORMATS.includes(request.format)) {
|
|
10357
|
+
throw new EngramAccessInputError(
|
|
10358
|
+
`unsupported briefing format: "${request.format}". Accepted: ${SUPPORTED_FORMATS.join(", ")}.`
|
|
10359
|
+
);
|
|
10360
|
+
}
|
|
10361
|
+
const format = request.format === "json" ? "json" : request.format === "markdown" ? "markdown" : config.briefing.defaultFormat;
|
|
10362
|
+
const maxFollowups = typeof request.maxFollowups === "number" && Number.isFinite(request.maxFollowups) ? Math.max(0, Math.min(10, Math.floor(request.maxFollowups))) : config.briefing.maxFollowups;
|
|
10363
|
+
const calendarSource = config.briefing.calendarSource ? new FileCalendarSource(config.briefing.calendarSource) : void 0;
|
|
10364
|
+
const result = await buildBriefing({
|
|
10365
|
+
storage,
|
|
10366
|
+
namespace,
|
|
10367
|
+
window,
|
|
10368
|
+
focus,
|
|
10369
|
+
calendarSource,
|
|
10370
|
+
maxFollowups,
|
|
10371
|
+
allowLlm: config.briefing.llmFollowups,
|
|
10372
|
+
openaiApiKey: config.openaiApiKey,
|
|
10373
|
+
openaiBaseUrl: config.openaiBaseUrl,
|
|
10374
|
+
model: config.model,
|
|
10375
|
+
// Without a direct OpenAI key, route follow-ups through the configured
|
|
10376
|
+
// LLM chain (gateway model source or local LLM) — same fallback every
|
|
10377
|
+
// other LLM feature uses. A configured key keeps its precedence so
|
|
10378
|
+
// existing deployments are unchanged.
|
|
10379
|
+
followupGenerator: config.openaiApiKey ? void 0 : this.deps.orchestrator.briefingChainFollowupGenerator
|
|
10380
|
+
});
|
|
10381
|
+
return {
|
|
10382
|
+
format,
|
|
10383
|
+
window: result.window,
|
|
10384
|
+
namespace,
|
|
10385
|
+
markdown: result.markdown,
|
|
10386
|
+
json: result.json,
|
|
10387
|
+
followupsUnavailableReason: result.followupsUnavailableReason
|
|
10388
|
+
};
|
|
10389
|
+
}
|
|
10390
|
+
async memoryStore(request, hooks) {
|
|
10391
|
+
const namespace = await this.deps.resolveCodingScopedWriteNamespace(request);
|
|
10392
|
+
const schemaVersion = request.schemaVersion ?? ENGRAM_ACCESS_WRITE_SCHEMA_VERSION;
|
|
10393
|
+
if (schemaVersion !== ENGRAM_ACCESS_WRITE_SCHEMA_VERSION) {
|
|
10394
|
+
throw new EngramAccessInputError(`unsupported schemaVersion: ${schemaVersion}`);
|
|
10395
|
+
}
|
|
10396
|
+
const execute = async () => {
|
|
10397
|
+
const candidate = this.deps.validateWriteCandidate(request, namespace);
|
|
10398
|
+
if (request.dryRun === true) {
|
|
10399
|
+
return {
|
|
10400
|
+
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
10401
|
+
operation: "memory_store",
|
|
10402
|
+
namespace,
|
|
10403
|
+
dryRun: true,
|
|
10404
|
+
accepted: true,
|
|
10405
|
+
queued: false,
|
|
10406
|
+
status: "validated",
|
|
10407
|
+
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
10408
|
+
};
|
|
10409
|
+
}
|
|
10410
|
+
const result = await persistExplicitCapture(this.deps.orchestrator, candidate, "memory_store");
|
|
10411
|
+
await this.deps.attachCodingContextAfterScopedWrite(request);
|
|
10412
|
+
const blocked = result.tombstoneBlocked === true;
|
|
10413
|
+
const response = {
|
|
10414
|
+
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
10415
|
+
operation: "memory_store",
|
|
10416
|
+
namespace,
|
|
10417
|
+
dryRun: false,
|
|
10418
|
+
accepted: true,
|
|
10419
|
+
queued: blocked,
|
|
10420
|
+
status: blocked ? "queued_for_review" : result.duplicateOf ? "duplicate" : "stored",
|
|
10421
|
+
memoryId: result.id,
|
|
10422
|
+
duplicateOf: result.duplicateOf,
|
|
10423
|
+
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
10424
|
+
};
|
|
10425
|
+
log.info(
|
|
10426
|
+
`access-write op=memory_store namespace=${namespace} dryRun=false status=${response.status} memoryId=${response.memoryId ?? "-"} idempotency=${response.idempotencyKey ? "yes" : "no"}`
|
|
10427
|
+
);
|
|
10428
|
+
return response;
|
|
10429
|
+
};
|
|
10430
|
+
return this.deps.handleIdempotentWrite({
|
|
10431
|
+
operation: "memory_store",
|
|
10432
|
+
idempotencyKey: request.idempotencyKey,
|
|
10433
|
+
requestFingerprint: {
|
|
10434
|
+
schemaVersion,
|
|
10435
|
+
content: request.content,
|
|
10436
|
+
category: request.category,
|
|
10437
|
+
confidence: request.confidence,
|
|
10438
|
+
namespace,
|
|
10439
|
+
tags: request.tags,
|
|
10440
|
+
entityRef: request.entityRef,
|
|
10441
|
+
ttl: request.ttl,
|
|
10442
|
+
sourceReason: request.sourceReason
|
|
10443
|
+
},
|
|
10444
|
+
skip: request.dryRun === true,
|
|
10445
|
+
beforeExecute: hooks?.enforceWriteQuota,
|
|
10446
|
+
execute
|
|
10373
10447
|
});
|
|
10374
10448
|
}
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10449
|
+
async suggestionSubmit(request, hooks) {
|
|
10450
|
+
const namespace = await this.deps.resolveCodingScopedWriteNamespace(request);
|
|
10451
|
+
const schemaVersion = request.schemaVersion ?? ENGRAM_ACCESS_WRITE_SCHEMA_VERSION;
|
|
10452
|
+
if (schemaVersion !== ENGRAM_ACCESS_WRITE_SCHEMA_VERSION) {
|
|
10453
|
+
throw new EngramAccessInputError(`unsupported schemaVersion: ${schemaVersion}`);
|
|
10454
|
+
}
|
|
10455
|
+
const execute = async () => {
|
|
10456
|
+
const candidate = this.deps.validateWriteCandidate(request, namespace);
|
|
10457
|
+
if (request.dryRun === true) {
|
|
10458
|
+
return {
|
|
10459
|
+
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
10460
|
+
operation: "suggestion_submit",
|
|
10461
|
+
namespace,
|
|
10462
|
+
dryRun: true,
|
|
10463
|
+
accepted: true,
|
|
10464
|
+
queued: true,
|
|
10465
|
+
status: "validated",
|
|
10466
|
+
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
10467
|
+
};
|
|
10468
|
+
}
|
|
10469
|
+
const result = await queueExplicitCaptureForReview(
|
|
10470
|
+
this.deps.orchestrator,
|
|
10471
|
+
candidate,
|
|
10472
|
+
"suggestion_submit",
|
|
10473
|
+
new Error(request.sourceReason?.trim() || "submitted via engram suggestion_submit")
|
|
10474
|
+
);
|
|
10475
|
+
await this.deps.attachCodingContextAfterScopedWrite(request);
|
|
10476
|
+
const response = {
|
|
10477
|
+
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
10478
|
+
operation: "suggestion_submit",
|
|
10479
|
+
namespace,
|
|
10480
|
+
dryRun: false,
|
|
10481
|
+
accepted: true,
|
|
10482
|
+
queued: true,
|
|
10483
|
+
status: "queued_for_review",
|
|
10484
|
+
memoryId: result.id,
|
|
10485
|
+
duplicateOf: result.duplicateOf,
|
|
10486
|
+
idempotencyKey: request.idempotencyKey?.trim() || void 0
|
|
10487
|
+
};
|
|
10488
|
+
log.info(
|
|
10489
|
+
`access-write op=suggestion_submit namespace=${namespace} dryRun=false status=${response.status} memoryId=${response.memoryId ?? "-"} idempotency=${response.idempotencyKey ? "yes" : "no"}`
|
|
10490
|
+
);
|
|
10491
|
+
return response;
|
|
10492
|
+
};
|
|
10493
|
+
return this.deps.handleIdempotentWrite({
|
|
10494
|
+
operation: "suggestion_submit",
|
|
10495
|
+
idempotencyKey: request.idempotencyKey,
|
|
10496
|
+
requestFingerprint: {
|
|
10497
|
+
schemaVersion,
|
|
10498
|
+
content: request.content,
|
|
10499
|
+
category: request.category,
|
|
10500
|
+
confidence: request.confidence,
|
|
10501
|
+
namespace,
|
|
10502
|
+
tags: request.tags,
|
|
10503
|
+
entityRef: request.entityRef,
|
|
10504
|
+
ttl: request.ttl,
|
|
10505
|
+
sourceReason: request.sourceReason
|
|
10506
|
+
},
|
|
10507
|
+
skip: request.dryRun === true,
|
|
10508
|
+
beforeExecute: hooks?.enforceWriteQuota,
|
|
10509
|
+
execute
|
|
10396
10510
|
});
|
|
10397
|
-
return redactSensitive(inspection);
|
|
10398
10511
|
}
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10512
|
+
async runObserve(request) {
|
|
10513
|
+
if (!request.sessionKey || typeof request.sessionKey !== "string" || request.sessionKey.trim().length === 0) {
|
|
10514
|
+
throw new EngramAccessInputError("sessionKey is required and must be a non-empty string");
|
|
10515
|
+
}
|
|
10516
|
+
if (!Array.isArray(request.messages) || request.messages.length === 0) {
|
|
10517
|
+
throw new EngramAccessInputError("messages is required and must be a non-empty array");
|
|
10518
|
+
}
|
|
10519
|
+
for (const msg of request.messages) {
|
|
10520
|
+
if (!msg || typeof msg !== "object" || typeof msg.role !== "string" || typeof msg.content !== "string") {
|
|
10521
|
+
throw new EngramAccessInputError("each message must have a string 'role' and 'content'");
|
|
10522
|
+
}
|
|
10523
|
+
if (msg.role !== "user" && msg.role !== "assistant") {
|
|
10524
|
+
throw new EngramAccessInputError(`invalid message role: ${msg.role} (expected 'user' or 'assistant')`);
|
|
10525
|
+
}
|
|
10526
|
+
}
|
|
10527
|
+
const scope = await this.deps.resolveMemoryScopePlan(request);
|
|
10528
|
+
const writeNamespace = scope.writeNamespace;
|
|
10529
|
+
const namespace = this.deps.legacyResponseNamespaceForScope(scope);
|
|
10530
|
+
const shouldWriteObjectiveState = resolveObjectiveStateCapabilities(this.deps.orchestrator.config).objectiveStateMemory === true && resolveObjectiveStateCapabilities(this.deps.orchestrator.config).objectiveStateSnapshotWrites === true;
|
|
10531
|
+
await this.deps.maybeAttachCodingContext(request.sessionKey, {
|
|
10532
|
+
cwd: request.cwd,
|
|
10533
|
+
projectTag: request.projectTag
|
|
10407
10534
|
});
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10535
|
+
const lcmSessionKey = lcmSessionKeyForNamespace(
|
|
10536
|
+
writeNamespace,
|
|
10537
|
+
request.sessionKey,
|
|
10538
|
+
this.deps.orchestrator.config.defaultNamespace
|
|
10539
|
+
) ?? request.sessionKey;
|
|
10540
|
+
if (shouldWriteObjectiveState) {
|
|
10541
|
+
try {
|
|
10542
|
+
const objectiveStateLocation = await this.deps.objectiveStateStoreLocationForNamespace(
|
|
10543
|
+
scope.objectiveStateNamespace
|
|
10544
|
+
);
|
|
10545
|
+
await recordObjectiveStateSnapshotsFromObservedMessages({
|
|
10546
|
+
memoryDir: objectiveStateLocation.memoryDir,
|
|
10547
|
+
objectiveStateStoreDir: objectiveStateLocation.objectiveStateStoreDir,
|
|
10548
|
+
objectiveStateMemoryEnabled: resolveObjectiveStateCapabilities(this.deps.orchestrator.config).objectiveStateMemory,
|
|
10549
|
+
objectiveStateSnapshotWritesEnabled: resolveObjectiveStateCapabilities(this.deps.orchestrator.config).objectiveStateSnapshotWrites,
|
|
10550
|
+
sessionKey: request.sessionKey,
|
|
10551
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10552
|
+
messages: request.messages
|
|
10553
|
+
});
|
|
10554
|
+
} catch (err) {
|
|
10555
|
+
log.error(`access-observe objective-state snapshot write failed: ${err}`);
|
|
10556
|
+
}
|
|
10557
|
+
}
|
|
10558
|
+
let lcmArchived = false;
|
|
10559
|
+
if (this.deps.orchestrator.lcmEngine && this.deps.orchestrator.lcmEngine.enabled) {
|
|
10560
|
+
try {
|
|
10561
|
+
this.deps.orchestrator.lcmEngine.enqueueObserveMessages(lcmSessionKey, request.messages);
|
|
10562
|
+
lcmArchived = true;
|
|
10563
|
+
} catch (err) {
|
|
10564
|
+
log.error(`access-observe LCM enqueue failed: ${err}`);
|
|
10565
|
+
}
|
|
10566
|
+
}
|
|
10567
|
+
let extractionQueued = false;
|
|
10568
|
+
if (request.skipExtraction !== true) {
|
|
10569
|
+
const turns = request.messages.map((m) => ({
|
|
10570
|
+
source: "openclaw",
|
|
10571
|
+
// Identity-vs-routing separation (#1505 thread 1, cursor): extraction
|
|
10572
|
+
// derives the provenance principal via `resolvePrincipal(turn.sessionKey)`
|
|
10573
|
+
// and threads `turn.sessionKey` into conversation threading. Feeding the
|
|
10574
|
+
// namespace-PREFIXED `lcmSessionKey` here mis-derived the principal to
|
|
10575
|
+
// `default` (a `<ns>:<key>` string matches no prefix/map rule and fails
|
|
10576
|
+
// the `agent:` heuristic). Pass the ORIGINAL sessionKey so identity is
|
|
10577
|
+
// correct; storage routing is pinned separately via
|
|
10578
|
+
// writeNamespaceOverride below, and the authenticated principal is pinned
|
|
10579
|
+
// via principalOverride.
|
|
10580
|
+
sessionKey: request.sessionKey,
|
|
10581
|
+
role: m.role,
|
|
10582
|
+
content: m.content,
|
|
10583
|
+
parts: m.parts,
|
|
10584
|
+
rawContent: m.rawContent,
|
|
10585
|
+
sourceFormat: m.sourceFormat,
|
|
10586
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10587
|
+
}));
|
|
10588
|
+
const writeNamespaceOverride = resolveNamespaceCapabilities(this.deps.orchestrator.config).namespaces === true ? writeNamespace : void 0;
|
|
10589
|
+
const principalOverride = typeof scope.principal === "string" && scope.principal.length > 0 ? scope.principal : void 0;
|
|
10590
|
+
try {
|
|
10591
|
+
const extractionPromise = this.deps.orchestrator.ingestReplayBatch(turns, {
|
|
10592
|
+
archiveLcm: false,
|
|
10593
|
+
writeNamespaceOverride,
|
|
10594
|
+
principalOverride
|
|
10595
|
+
});
|
|
10596
|
+
extractionPromise.catch((err) => {
|
|
10597
|
+
log.error(`access-observe background extraction failed: ${err}`);
|
|
10598
|
+
});
|
|
10599
|
+
extractionQueued = true;
|
|
10600
|
+
} catch (err) {
|
|
10601
|
+
log.error(`access-observe extraction enqueue failed: ${err}`);
|
|
10435
10602
|
}
|
|
10436
|
-
}
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
/**
|
|
10440
|
-
* Dry-run transcript/session audit. Detects mixed `other/default` data
|
|
10441
|
-
* and legacy stranded directories. Never applies a migration.
|
|
10442
|
-
*/
|
|
10443
|
-
async adminTranscriptAudit() {
|
|
10444
|
-
const result = await auditTranscripts(this.orchestrator.config.memoryDir);
|
|
10445
|
-
return redactSensitive(result);
|
|
10446
|
-
}
|
|
10447
|
-
async adminPromoteMemory(request) {
|
|
10448
|
-
return this.accessAdminOpsSurface.adminPromoteMemory(
|
|
10449
|
-
request
|
|
10603
|
+
}
|
|
10604
|
+
log.info(
|
|
10605
|
+
`access-observe namespace=${namespace} effectiveNamespace=${writeNamespace} sessionKey=${request.sessionKey} messages=${request.messages.length} lcm=${lcmArchived} extraction=${extractionQueued}`
|
|
10450
10606
|
);
|
|
10607
|
+
return {
|
|
10608
|
+
accepted: request.messages.length,
|
|
10609
|
+
sessionKey: request.sessionKey,
|
|
10610
|
+
namespace,
|
|
10611
|
+
effectiveNamespace: writeNamespace,
|
|
10612
|
+
scopeDebug: {
|
|
10613
|
+
principal: scope.principal,
|
|
10614
|
+
explicitNamespace: scope.explicitNamespace,
|
|
10615
|
+
baseNamespace: scope.baseNamespace,
|
|
10616
|
+
writeNamespace: scope.writeNamespace,
|
|
10617
|
+
codingOverlayApplied: scope.codingOverlayApplied,
|
|
10618
|
+
readNamespaces: scope.readNamespaces,
|
|
10619
|
+
scopeProfile: scope.scopeProfile,
|
|
10620
|
+
writeLayer: scope.writeLayer,
|
|
10621
|
+
layers: scope.layers,
|
|
10622
|
+
promotionTargets: scope.promotionTargets
|
|
10623
|
+
},
|
|
10624
|
+
lcmArchived,
|
|
10625
|
+
extractionQueued
|
|
10626
|
+
};
|
|
10451
10627
|
}
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
10628
|
+
async workTask(request) {
|
|
10629
|
+
const STATUSES = /* @__PURE__ */ new Set(["todo", "in_progress", "blocked", "done", "cancelled"]);
|
|
10630
|
+
const PRIORITIES = /* @__PURE__ */ new Set(["low", "medium", "high"]);
|
|
10631
|
+
const asStatus = (v) => v && STATUSES.has(v) ? v : void 0;
|
|
10632
|
+
const asPriority = (v) => v && PRIORITIES.has(v) ? v : void 0;
|
|
10633
|
+
const storage = new WorkStorage(this.deps.orchestrator.config.memoryDir);
|
|
10634
|
+
await storage.ensureDirectories();
|
|
10635
|
+
const action = request.action;
|
|
10636
|
+
if (action === "create") {
|
|
10637
|
+
if (!request.title?.trim()) throw new EngramAccessInputError("title is required for create");
|
|
10638
|
+
const task = await storage.createTask({
|
|
10639
|
+
title: request.title,
|
|
10640
|
+
description: request.description,
|
|
10641
|
+
status: asStatus(request.status),
|
|
10642
|
+
priority: asPriority(request.priority),
|
|
10643
|
+
owner: request.owner?.trim() || void 0,
|
|
10644
|
+
assignee: request.assignee?.trim() || void 0,
|
|
10645
|
+
projectId: request.projectId?.trim() || void 0,
|
|
10646
|
+
tags: request.tags,
|
|
10647
|
+
dueAt: request.dueAt?.trim() || void 0
|
|
10648
|
+
});
|
|
10649
|
+
return { action, task };
|
|
10463
10650
|
}
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
request.sessionKey,
|
|
10468
|
-
request.authenticatedPrincipal
|
|
10469
|
-
);
|
|
10470
|
-
const namespace = hasExplicitNamespace ? this.deps.resolveReadableNamespace(request.namespace, principal) : profileLcmReadNamespaces !== null ? profileLcmReadNamespaces[0] : this.deps.resolveImplicitLcmReadFallbackNamespace(principal);
|
|
10471
|
-
if (!this.deps.orchestrator.lcmEngine || !this.deps.orchestrator.lcmEngine.enabled) {
|
|
10472
|
-
return {
|
|
10473
|
-
query: request.query,
|
|
10474
|
-
namespace: namespace ?? this.deps.orchestrator.config.defaultNamespace,
|
|
10475
|
-
results: [],
|
|
10476
|
-
count: 0,
|
|
10477
|
-
lcmEnabled: false
|
|
10478
|
-
};
|
|
10651
|
+
if (action === "get") {
|
|
10652
|
+
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for get");
|
|
10653
|
+
return { action, task: await storage.getTask(request.id) };
|
|
10479
10654
|
}
|
|
10480
|
-
if (
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
};
|
|
10655
|
+
if (action === "list") {
|
|
10656
|
+
const tasks = await storage.listTasks({
|
|
10657
|
+
status: asStatus(request.status),
|
|
10658
|
+
owner: request.owner?.trim() || void 0,
|
|
10659
|
+
assignee: request.assignee?.trim() || void 0,
|
|
10660
|
+
projectId: request.projectId?.trim() || void 0
|
|
10661
|
+
});
|
|
10662
|
+
return { action, count: tasks.length, tasks };
|
|
10488
10663
|
}
|
|
10489
|
-
if (
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10664
|
+
if (action === "update") {
|
|
10665
|
+
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for update");
|
|
10666
|
+
const patch = {};
|
|
10667
|
+
if (request.title !== void 0) patch.title = request.title;
|
|
10668
|
+
if (request.description !== void 0) patch.description = request.description;
|
|
10669
|
+
const st = asStatus(request.status);
|
|
10670
|
+
if (st) patch.status = st;
|
|
10671
|
+
const pr = asPriority(request.priority);
|
|
10672
|
+
if (pr) patch.priority = pr;
|
|
10673
|
+
if (request.owner !== void 0) patch.owner = request.owner || null;
|
|
10674
|
+
if (request.assignee !== void 0) patch.assignee = request.assignee || null;
|
|
10675
|
+
if (request.projectId !== void 0) patch.projectId = request.projectId || null;
|
|
10676
|
+
if (request.tags) patch.tags = request.tags;
|
|
10677
|
+
if (request.dueAt !== void 0) patch.dueAt = request.dueAt || null;
|
|
10678
|
+
return { action, task: await storage.updateTask(request.id, patch) };
|
|
10497
10679
|
}
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
request.
|
|
10501
|
-
|
|
10502
|
-
request.
|
|
10503
|
-
request.authenticatedPrincipal
|
|
10504
|
-
);
|
|
10505
|
-
const lcmSessionKeyIds = request.sessionKey ? profileLcmReadNamespaces !== null ? this.deps.lcmSessionIdsForNamespaces(
|
|
10506
|
-
profileLcmReadNamespaces,
|
|
10507
|
-
request.sessionKey
|
|
10508
|
-
) : this.deps.resolveLcmReadSessionIds(
|
|
10509
|
-
request.namespace,
|
|
10510
|
-
namespace,
|
|
10511
|
-
request.sessionKey,
|
|
10512
|
-
request.authenticatedPrincipal
|
|
10513
|
-
) : [void 0];
|
|
10514
|
-
const lcmSessionPrefixes = request.sessionPrefix ? profileLcmReadNamespaces !== null && !request.sessionKey ? this.deps.lcmSessionIdsForNamespaces(
|
|
10515
|
-
profileLcmReadNamespaces,
|
|
10516
|
-
request.sessionPrefix
|
|
10517
|
-
) : [
|
|
10518
|
-
lcmSessionKeyForNamespace(
|
|
10519
|
-
lcmReadNamespace,
|
|
10520
|
-
request.sessionPrefix,
|
|
10521
|
-
this.deps.orchestrator.config.defaultNamespace
|
|
10522
|
-
) ?? request.sessionPrefix
|
|
10523
|
-
] : [void 0];
|
|
10524
|
-
const hasScopedSession = typeof request.sessionKey === "string" && request.sessionKey.length > 0 || lcmSessionPrefixes.some((prefix) => typeof prefix === "string" && prefix.length > 0);
|
|
10525
|
-
if (!hasScopedSession && resolveNamespaceCapabilities(this.deps.orchestrator.config).namespaces === true) {
|
|
10526
|
-
return {
|
|
10527
|
-
query: request.query,
|
|
10528
|
-
namespace,
|
|
10529
|
-
results: [],
|
|
10530
|
-
count: 0,
|
|
10531
|
-
lcmEnabled: true
|
|
10532
|
-
};
|
|
10680
|
+
if (action === "transition") {
|
|
10681
|
+
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for transition");
|
|
10682
|
+
const st = asStatus(request.status);
|
|
10683
|
+
if (!st) throw new EngramAccessInputError("valid status is required for transition");
|
|
10684
|
+
return { action, task: await storage.transitionTask(request.id, st) };
|
|
10533
10685
|
}
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
for (const lcmSessionKey of lcmSessionKeyIds) {
|
|
10538
|
-
for (const lcmSessionPrefix of lcmSessionPrefixes) {
|
|
10539
|
-
lcmSearches.push({
|
|
10540
|
-
key: lcmSessionKey,
|
|
10541
|
-
prefix: lcmSessionPrefix,
|
|
10542
|
-
promise: this.deps.orchestrator.lcmEngine.searchContextFull(
|
|
10543
|
-
request.query,
|
|
10544
|
-
limit,
|
|
10545
|
-
lcmSessionKey,
|
|
10546
|
-
lcmSessionPrefix
|
|
10547
|
-
)
|
|
10548
|
-
});
|
|
10549
|
-
}
|
|
10686
|
+
if (action === "delete") {
|
|
10687
|
+
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for delete");
|
|
10688
|
+
return { action, deleted: await storage.deleteTask(request.id) };
|
|
10550
10689
|
}
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10690
|
+
throw new EngramAccessInputError(`Unsupported work_task action: ${action}`);
|
|
10691
|
+
}
|
|
10692
|
+
async workProject(request) {
|
|
10693
|
+
const STATUSES = /* @__PURE__ */ new Set(["active", "on_hold", "completed", "archived"]);
|
|
10694
|
+
const asStatus = (v) => v && STATUSES.has(v) ? v : void 0;
|
|
10695
|
+
const storage = new WorkStorage(this.deps.orchestrator.config.memoryDir);
|
|
10696
|
+
await storage.ensureDirectories();
|
|
10697
|
+
const action = request.action;
|
|
10698
|
+
if (action === "create") {
|
|
10699
|
+
if (!request.name?.trim()) throw new EngramAccessInputError("name is required for create");
|
|
10700
|
+
const project = await storage.createProject({
|
|
10701
|
+
name: request.name,
|
|
10702
|
+
description: request.description,
|
|
10703
|
+
status: asStatus(request.status),
|
|
10704
|
+
owner: request.owner?.trim() || void 0,
|
|
10705
|
+
tags: request.tags
|
|
10706
|
+
});
|
|
10707
|
+
return { action, project };
|
|
10708
|
+
}
|
|
10709
|
+
if (action === "get") {
|
|
10710
|
+
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for get");
|
|
10711
|
+
return { action, project: await storage.getProject(request.id) };
|
|
10712
|
+
}
|
|
10713
|
+
if (action === "list") {
|
|
10714
|
+
const projects = await storage.listProjects();
|
|
10715
|
+
return { action, count: projects.length, projects };
|
|
10716
|
+
}
|
|
10717
|
+
if (action === "update") {
|
|
10718
|
+
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for update");
|
|
10719
|
+
const patch = {};
|
|
10720
|
+
if (request.name !== void 0) patch.name = request.name;
|
|
10721
|
+
if (request.description !== void 0) patch.description = request.description;
|
|
10722
|
+
const st = asStatus(request.status);
|
|
10723
|
+
if (st) patch.status = st;
|
|
10724
|
+
if (request.owner !== void 0) patch.owner = request.owner || null;
|
|
10725
|
+
if (request.tags) patch.tags = request.tags;
|
|
10726
|
+
return { action, project: await storage.updateProject(request.id, patch) };
|
|
10727
|
+
}
|
|
10728
|
+
if (action === "delete") {
|
|
10729
|
+
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for delete");
|
|
10730
|
+
return { action, deleted: await storage.deleteProject(request.id) };
|
|
10731
|
+
}
|
|
10732
|
+
if (action === "link_task") {
|
|
10733
|
+
if (!request.taskId?.trim() || !request.projectId?.trim()) {
|
|
10734
|
+
throw new EngramAccessInputError("taskId and projectId are required for link_task");
|
|
10574
10735
|
}
|
|
10736
|
+
return { action, linked: await storage.linkTaskToProject(request.taskId, request.projectId) };
|
|
10575
10737
|
}
|
|
10576
|
-
|
|
10577
|
-
query: request.query,
|
|
10578
|
-
namespace,
|
|
10579
|
-
results,
|
|
10580
|
-
count: results.length,
|
|
10581
|
-
lcmEnabled: true
|
|
10582
|
-
};
|
|
10738
|
+
throw new EngramAccessInputError(`Unsupported work_project action: ${action}`);
|
|
10583
10739
|
}
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
*
|
|
10605
|
-
* - `"write"` (`lcmCompactionFlush` / `lcmCompactionRecord`): these are
|
|
10606
|
-
* write/maintenance operations on the SAME queue `observe` just wrote, so
|
|
10607
|
-
* the gate must mirror observe's WRITE authorization (`canWriteNamespace`
|
|
10608
|
-
* on the self base), NOT readability. A principal that can WRITE but not
|
|
10609
|
-
* READ its self namespace (or whose `defaultRecallNamespaces` omits `self`)
|
|
10610
|
-
* archived under the overlay key via `observe`; gating compaction by
|
|
10611
|
-
* readability would fall back to the default/raw key and leave that queue
|
|
10612
|
-
* never flushed/recorded (round-4 codex P2). Write-authorized ⇒ overlay
|
|
10613
|
-
* key, matching the observe write key (rule 42 read/write parity; rule 39
|
|
10614
|
-
* identical gates across paths).
|
|
10615
|
-
*/
|
|
10616
|
-
resolveLcmReadNamespace(explicitNamespace, resolvedNamespace, sessionKeyForOverlay, authenticatedPrincipal, purpose = "read") {
|
|
10617
|
-
const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
|
|
10618
|
-
if (hasExplicitNamespace) return resolvedNamespace;
|
|
10619
|
-
const principal = this.deps.resolveRequestPrincipal(
|
|
10620
|
-
sessionKeyForOverlay,
|
|
10621
|
-
authenticatedPrincipal
|
|
10622
|
-
);
|
|
10623
|
-
const base = defaultNamespaceForPrincipal(principal, this.deps.orchestrator.config);
|
|
10624
|
-
const overlaid = this.deps.orchestrator.applyCodingNamespaceOverlay(
|
|
10625
|
-
sessionKeyForOverlay,
|
|
10626
|
-
base
|
|
10627
|
-
);
|
|
10628
|
-
if (overlaid === base) return this.deps.orchestrator.config.defaultNamespace;
|
|
10629
|
-
const authorized = purpose === "write" ? canWriteNamespace(principal, base, this.deps.orchestrator.config) : recallNamespacesForPrincipal(
|
|
10630
|
-
principal,
|
|
10631
|
-
this.deps.orchestrator.config
|
|
10632
|
-
).includes(base);
|
|
10633
|
-
if (authorized) return overlaid;
|
|
10634
|
-
if (purpose === "read") return this.deps.orchestrator.config.defaultNamespace;
|
|
10635
|
-
return resolvedNamespace;
|
|
10740
|
+
async workBoard(request) {
|
|
10741
|
+
const memoryDir = this.deps.orchestrator.config.memoryDir;
|
|
10742
|
+
await new WorkStorage(memoryDir).ensureDirectories();
|
|
10743
|
+
const action = request.action;
|
|
10744
|
+
const projectId = request.projectId?.trim() || void 0;
|
|
10745
|
+
if (action === "export_markdown") {
|
|
10746
|
+
const markdown = await exportWorkBoardMarkdown({ memoryDir, projectId });
|
|
10747
|
+
return { action, markdown: wrapWorkLayerContext(markdown, { linkToMemory: request.linkToMemory === true }) };
|
|
10748
|
+
}
|
|
10749
|
+
if (action === "export_snapshot") {
|
|
10750
|
+
const snapshot = await exportWorkBoardSnapshot({ memoryDir, projectId });
|
|
10751
|
+
return { action, snapshot };
|
|
10752
|
+
}
|
|
10753
|
+
if (action === "import_snapshot") {
|
|
10754
|
+
if (!request.snapshotJson?.trim()) throw new EngramAccessInputError("snapshotJson is required for import_snapshot");
|
|
10755
|
+
const snapshot = JSON.parse(request.snapshotJson);
|
|
10756
|
+
const result = await importWorkBoardSnapshot({ memoryDir, snapshot, projectId });
|
|
10757
|
+
return { action, result };
|
|
10758
|
+
}
|
|
10759
|
+
throw new EngramAccessInputError(`Unsupported work_board action: ${action}`);
|
|
10636
10760
|
}
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
* single-user recall is byte-for-byte unchanged.
|
|
10651
|
-
*
|
|
10652
|
-
* Returns `undefined` when NO readable LCM namespace exists for an IMPLICIT
|
|
10653
|
-
* (no explicit `namespace`) raw recall — i.e. a restrictive `default` READ
|
|
10654
|
-
* policy denies the principal `default` AND no overlay/self namespace is
|
|
10655
|
-
* readable. In that case the caller emits NO excerpts rather than throwing
|
|
10656
|
-
* `namespace is not readable: default` (#1505 thread NBHWz): normal recall
|
|
10657
|
-
* still succeeds via `recallNamespacesForPrincipal`, so `disclosure: "raw"`
|
|
10658
|
-
* must degrade gracefully (empty excerpts), never pre-authorize `default`.
|
|
10659
|
-
*
|
|
10660
|
-
* IMPLICIT-namespace fallback selection derives from the ALREADY
|
|
10661
|
-
* read-authorized recall namespace set (`recallNamespacesForPrincipal` +
|
|
10662
|
-
* `canReadNamespace`) — the principal's self base when it is in the readable
|
|
10663
|
-
* recall set, else `config.defaultNamespace` ONLY when the principal may read
|
|
10664
|
-
* it. It NEVER pre-authorizes `default`. An EXPLICIT `namespace` is still
|
|
10665
|
-
* authorized strictly via `resolveReadableNamespace` (explicit reads must pass
|
|
10666
|
-
* the ACL — no behavior change).
|
|
10667
|
-
*/
|
|
10668
|
-
resolveRawExcerptReadNamespace(explicitNamespace, sessionKey, authenticatedPrincipal) {
|
|
10669
|
-
const principal = this.deps.resolveRequestPrincipal(
|
|
10670
|
-
sessionKey,
|
|
10671
|
-
authenticatedPrincipal
|
|
10672
|
-
);
|
|
10673
|
-
const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
|
|
10674
|
-
if (hasExplicitNamespace) {
|
|
10675
|
-
const resolvedNamespace = this.deps.resolveReadableNamespace(
|
|
10676
|
-
explicitNamespace,
|
|
10677
|
-
principal
|
|
10761
|
+
async memoryActionApply(request) {
|
|
10762
|
+
const actionTypes = /* @__PURE__ */ new Set([
|
|
10763
|
+
"store_episode",
|
|
10764
|
+
"store_note",
|
|
10765
|
+
"update_note",
|
|
10766
|
+
"create_artifact",
|
|
10767
|
+
"summarize_node",
|
|
10768
|
+
"discard",
|
|
10769
|
+
"link_graph"
|
|
10770
|
+
]);
|
|
10771
|
+
if (!actionTypes.has(request.action)) {
|
|
10772
|
+
throw new EngramAccessInputError(
|
|
10773
|
+
`memory_action_apply: invalid action ${JSON.stringify(request.action)}`
|
|
10678
10774
|
);
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
authenticatedPrincipal,
|
|
10684
|
-
"read"
|
|
10775
|
+
}
|
|
10776
|
+
if (resolveCompressionCapabilities(this.deps.orchestrator.config).contextCompressionActions !== true) {
|
|
10777
|
+
throw new EngramAccessInputError(
|
|
10778
|
+
"memory_action_apply is disabled; enable contextCompressionActionsEnabled to use this tool"
|
|
10685
10779
|
);
|
|
10686
10780
|
}
|
|
10687
|
-
const
|
|
10688
|
-
|
|
10689
|
-
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
const fallbackNamespace = this.deps.resolveImplicitLcmReadFallbackNamespace(principal);
|
|
10693
|
-
if (fallbackNamespace === void 0) return void 0;
|
|
10694
|
-
return this.deps.resolveLcmReadNamespace(
|
|
10695
|
-
explicitNamespace,
|
|
10696
|
-
fallbackNamespace,
|
|
10697
|
-
sessionKey,
|
|
10698
|
-
authenticatedPrincipal,
|
|
10699
|
-
"read"
|
|
10700
|
-
);
|
|
10701
|
-
}
|
|
10702
|
-
resolveLcmReadSessionIds(explicitNamespace, resolvedNamespace, sessionKey, authenticatedPrincipal) {
|
|
10703
|
-
const primary = this.deps.resolveLcmReadSessionKey(
|
|
10704
|
-
explicitNamespace,
|
|
10705
|
-
resolvedNamespace,
|
|
10706
|
-
sessionKey,
|
|
10707
|
-
authenticatedPrincipal,
|
|
10708
|
-
"read"
|
|
10709
|
-
);
|
|
10710
|
-
const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
|
|
10711
|
-
if (hasExplicitNamespace) return [primary];
|
|
10712
|
-
const profileReadNamespaces = this.deps.resolveScopeProfileLcmReadNamespaces(
|
|
10713
|
-
sessionKey,
|
|
10714
|
-
authenticatedPrincipal
|
|
10715
|
-
);
|
|
10716
|
-
if (profileReadNamespaces !== null) {
|
|
10717
|
-
return this.deps.lcmSessionIdsForNamespaces(profileReadNamespaces, sessionKey);
|
|
10781
|
+
const outcome = request.outcome ?? "skipped";
|
|
10782
|
+
if (outcome !== "applied" && outcome !== "skipped" && outcome !== "failed") {
|
|
10783
|
+
throw new EngramAccessInputError(
|
|
10784
|
+
`memory_action_apply: outcome must be "applied", "skipped", or "failed"; got ${JSON.stringify(outcome)}`
|
|
10785
|
+
);
|
|
10718
10786
|
}
|
|
10719
|
-
const
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
const base = defaultNamespaceForPrincipal(
|
|
10724
|
-
principal,
|
|
10725
|
-
this.deps.orchestrator.config
|
|
10726
|
-
);
|
|
10727
|
-
const overlaid = this.deps.orchestrator.applyCodingNamespaceOverlay(
|
|
10728
|
-
sessionKey,
|
|
10729
|
-
base
|
|
10730
|
-
);
|
|
10731
|
-
if (overlaid === base) return [primary];
|
|
10732
|
-
const selfReadableInRecall = recallNamespacesForPrincipal(
|
|
10733
|
-
principal,
|
|
10734
|
-
this.deps.orchestrator.config
|
|
10735
|
-
).includes(base);
|
|
10736
|
-
if (!selfReadableInRecall) return [primary];
|
|
10737
|
-
const overlay = resolveCodingNamespaceOverlay(
|
|
10738
|
-
this.deps.orchestrator.getCodingContextForSession(sessionKey),
|
|
10739
|
-
this.deps.orchestrator.config.codingMode,
|
|
10740
|
-
this.deps.orchestrator.config.defaultNamespace
|
|
10741
|
-
);
|
|
10742
|
-
const fallbackNamespaces = (overlay?.readFallbacks ?? []).map(
|
|
10743
|
-
(fallback) => combineNamespaces(base, fallback)
|
|
10787
|
+
const resolvedNs = this.deps.writableNamespaceFor(
|
|
10788
|
+
request.namespace,
|
|
10789
|
+
request.sessionKey,
|
|
10790
|
+
request.principal
|
|
10744
10791
|
);
|
|
10745
|
-
const
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10792
|
+
const inputSummaryParts = [
|
|
10793
|
+
request.content,
|
|
10794
|
+
request.category ? `category=${request.category}` : void 0,
|
|
10795
|
+
request.linkTargetId ? `linkTargetId=${request.linkTargetId}` : void 0,
|
|
10796
|
+
request.linkType ? `linkType=${request.linkType}` : void 0,
|
|
10797
|
+
typeof request.linkStrength === "number" ? `linkStrength=${request.linkStrength}` : void 0,
|
|
10798
|
+
request.artifactType ? `artifactType=${request.artifactType}` : void 0,
|
|
10799
|
+
typeof request.execute === "boolean" ? `execute=${request.execute}` : void 0
|
|
10800
|
+
].filter((part) => typeof part === "string" && part.length > 0);
|
|
10801
|
+
const event = {
|
|
10802
|
+
action: request.action,
|
|
10803
|
+
outcome,
|
|
10804
|
+
namespace: resolvedNs,
|
|
10805
|
+
actor: "access.memory_action_apply",
|
|
10806
|
+
subsystem: "access.memory_action_apply",
|
|
10807
|
+
reason: request.reason,
|
|
10808
|
+
memoryId: request.memoryId,
|
|
10809
|
+
sourceSessionKey: request.sessionKey,
|
|
10810
|
+
inputSummary: inputSummaryParts.length > 0 ? inputSummaryParts.join(" | ").slice(0, 500) : void 0,
|
|
10811
|
+
dryRun: request.dryRun === true,
|
|
10812
|
+
promptHash: typeof request.sourcePrompt === "string" && request.sourcePrompt.length > 0 ? createHash6("sha256").update(request.sourcePrompt).digest("hex") : void 0
|
|
10813
|
+
};
|
|
10814
|
+
const preview = this.deps.orchestrator.previewMemoryActionEvent(event);
|
|
10815
|
+
if (request.dryRun === true) {
|
|
10816
|
+
return { recorded: false, dryRun: true, event: preview };
|
|
10757
10817
|
}
|
|
10758
|
-
|
|
10818
|
+
const recorded = await this.deps.orchestrator.appendMemoryActionEvent(event);
|
|
10819
|
+
return { recorded, event: preview };
|
|
10759
10820
|
}
|
|
10760
10821
|
};
|
|
10761
10822
|
|
|
@@ -10770,11 +10831,11 @@ export {
|
|
|
10770
10831
|
clusterByKey,
|
|
10771
10832
|
computeProcedureStats,
|
|
10772
10833
|
formatProcedureStatsText,
|
|
10834
|
+
AccessObserveWriteSurface,
|
|
10773
10835
|
AccessLcmSurface,
|
|
10774
10836
|
qmdCollectionPathParts,
|
|
10775
10837
|
QmdResultResolver,
|
|
10776
10838
|
AccessRecallSurface,
|
|
10777
|
-
selfDeps,
|
|
10778
10839
|
EngramAccessInputError,
|
|
10779
10840
|
ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
10780
10841
|
buildProjectedGovernanceProposedActions,
|
|
@@ -10783,4 +10844,4 @@ export {
|
|
|
10783
10844
|
EngramAccessService,
|
|
10784
10845
|
AccessAdminOpsSurface
|
|
10785
10846
|
};
|
|
10786
|
-
//# sourceMappingURL=chunk-
|
|
10847
|
+
//# sourceMappingURL=chunk-DWDDQLSZ.js.map
|