@remnic/core 9.3.649 → 9.3.651

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.
Files changed (176) hide show
  1. package/dist/access-cli.js +36 -35
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +2 -2
  4. package/dist/access-http.js +16 -16
  5. package/dist/access-mcp.d.ts +2 -2
  6. package/dist/access-mcp.js +15 -15
  7. package/dist/access-schema.js +3 -3
  8. package/dist/{access-service-DFXIlGvZ.d.ts → access-service-DIZRHQ7Q.d.ts} +255 -2
  9. package/dist/access-service.d.ts +2 -2
  10. package/dist/access-service.js +13 -13
  11. package/dist/{auto-sync-54QQHOG5.js → auto-sync-5CJBJMPZ.js} +5 -5
  12. package/dist/bootstrap.d.ts +1 -1
  13. package/dist/briefing.js +3 -3
  14. package/dist/calibration.js +2 -2
  15. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  16. package/dist/causal-consolidation.js +6 -6
  17. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  18. package/dist/{chunk-OAZ5MFUB.js → chunk-3XGWCZ63.js} +45 -28
  19. package/dist/chunk-3XGWCZ63.js.map +1 -0
  20. package/dist/{chunk-QKE4LHNR.js → chunk-4HYSMH7D.js} +2 -2
  21. package/dist/{chunk-NMIOW7XG.js → chunk-4PTKFBST.js} +2 -2
  22. package/dist/{chunk-DDRNDPX4.js → chunk-4SKKVWLQ.js} +2 -2
  23. package/dist/chunk-5FOCXX5E.js +34 -0
  24. package/dist/chunk-5FOCXX5E.js.map +1 -0
  25. package/dist/{chunk-XUGVP7ZU.js → chunk-5WSDHTBO.js} +166 -47
  26. package/dist/chunk-5WSDHTBO.js.map +1 -0
  27. package/dist/{chunk-WPCCNSWO.js → chunk-6UKL6IXM.js} +4 -4
  28. package/dist/{chunk-DB5A3NHS.js → chunk-7LWRCOP7.js} +9 -2
  29. package/dist/chunk-7LWRCOP7.js.map +1 -0
  30. package/dist/{chunk-APJQ6UEA.js → chunk-AGNBY3VG.js} +4 -4
  31. package/dist/{chunk-4BISW7RX.js → chunk-AJE7FJVE.js} +2 -2
  32. package/dist/{chunk-ZXWAQFDE.js → chunk-CFOCZPIQ.js} +2 -2
  33. package/dist/{chunk-NT5TINK5.js → chunk-DHGSZ3UD.js} +2 -2
  34. package/dist/{chunk-OTC2KOZ2.js → chunk-EHQLDFSH.js} +2 -2
  35. package/dist/{chunk-AMACWKM4.js → chunk-IJHLC5CH.js} +2 -2
  36. package/dist/{chunk-OR7R6M5Z.js → chunk-IVYSVAC6.js} +2 -2
  37. package/dist/{chunk-UMKPSD35.js → chunk-JF7SFXTG.js} +2 -2
  38. package/dist/{chunk-MCYT2RNT.js → chunk-KJDKZVF3.js} +3 -3
  39. package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
  40. package/dist/{chunk-PQFUUXWK.js → chunk-KWM33SPU.js} +2 -2
  41. package/dist/{chunk-A3BS64GV.js → chunk-LCC5EZTT.js} +4 -4
  42. package/dist/{chunk-ZT6R3WR3.js → chunk-LFTLXOFX.js} +4 -4
  43. package/dist/{chunk-CNRZ6WJU.js → chunk-MF32AL7N.js} +5 -5
  44. package/dist/{chunk-6GIKAUTN.js → chunk-MMJANTJX.js} +33 -2
  45. package/dist/{chunk-6GIKAUTN.js.map → chunk-MMJANTJX.js.map} +1 -1
  46. package/dist/{chunk-D6WVJIS3.js → chunk-ORGWWNJG.js} +2 -2
  47. package/dist/{chunk-Z3PZRDLW.js → chunk-PRQXUSQV.js} +2 -2
  48. package/dist/{chunk-VWT3F4IV.js → chunk-PS3SYNHP.js} +12 -4
  49. package/dist/chunk-PS3SYNHP.js.map +1 -0
  50. package/dist/{chunk-IMWFHBG2.js → chunk-QWRC7GIO.js} +2 -2
  51. package/dist/{chunk-FQYFMIKG.js → chunk-RKN5J4RO.js} +26 -26
  52. package/dist/{chunk-FUXV6HSO.js → chunk-RSS2KWN6.js} +5 -5
  53. package/dist/{chunk-U3GQ33JC.js → chunk-SLTKP5WJ.js} +2 -2
  54. package/dist/{chunk-5ETA6OAS.js → chunk-SLYD3AH4.js} +617 -89
  55. package/dist/chunk-SLYD3AH4.js.map +1 -0
  56. package/dist/{chunk-6NKAQ74D.js → chunk-UU6MVCJ6.js} +1 -1
  57. package/dist/chunk-UU6MVCJ6.js.map +1 -0
  58. package/dist/{chunk-WEPMT6SC.js → chunk-V25ZAOSB.js} +5 -5
  59. package/dist/{chunk-UMTG2BN2.js → chunk-V4UDXYGG.js} +2 -2
  60. package/dist/{chunk-RRRCNIPK.js → chunk-WJK75OCH.js} +4 -4
  61. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  62. package/dist/{chunk-OZKZ2TRP.js → chunk-XBIACVCO.js} +9 -2
  63. package/dist/chunk-XBIACVCO.js.map +1 -0
  64. package/dist/{chunk-ALUZN7BE.js → chunk-XMN6MMTU.js} +2 -2
  65. package/dist/{chunk-A4BTPHIN.js → chunk-Y7NWBBHV.js} +6 -6
  66. package/dist/{chunk-M75TBFKQ.js → chunk-Z2OXSMZK.js} +2 -2
  67. package/dist/{cli-DrL2Nv4j.d.ts → cli-BG4ybtJr.d.ts} +2 -2
  68. package/dist/cli.d.ts +3 -3
  69. package/dist/cli.js +31 -31
  70. package/dist/compounding/engine.js +3 -3
  71. package/dist/connectors/codex-materialize-runner.js +3 -3
  72. package/dist/connectors/index.js +3 -3
  73. package/dist/entity-retrieval.js +3 -3
  74. package/dist/event-order-recall.js +1 -1
  75. package/dist/explicit-capture.d.ts +1 -1
  76. package/dist/explicit-cue-recall.d.ts +7 -0
  77. package/dist/explicit-cue-recall.js +2 -1
  78. package/dist/extraction-judge.js +3 -3
  79. package/dist/extraction.js +3 -3
  80. package/dist/fallback-llm.js +2 -2
  81. package/dist/focused-list-recall.d.ts +6 -0
  82. package/dist/focused-list-recall.js +2 -1
  83. package/dist/index.d.ts +4 -4
  84. package/dist/index.js +84 -83
  85. package/dist/index.js.map +1 -1
  86. package/dist/lcm/engine.js +2 -2
  87. package/dist/lcm/index.js +5 -5
  88. package/dist/lcm-fallback-read.d.ts +71 -0
  89. package/dist/lcm-fallback-read.js +10 -0
  90. package/dist/lcm-fallback-read.js.map +1 -0
  91. package/dist/maintenance/memory-governance.js +3 -3
  92. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  93. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  94. package/dist/mcp-memory-inspector-app.d.ts +2 -2
  95. package/dist/namespaces/migrate.js +7 -7
  96. package/dist/namespaces/search.js +3 -3
  97. package/dist/namespaces/storage.js +3 -3
  98. package/dist/operator-toolkit.js +9 -9
  99. package/dist/{orchestrator-DEQW9j0Z.d.ts → orchestrator-CX-oqwJq.d.ts} +58 -0
  100. package/dist/orchestrator.d.ts +1 -1
  101. package/dist/orchestrator.js +30 -29
  102. package/dist/recall-planner-llm.js +2 -2
  103. package/dist/response-guidance-recall.d.ts +6 -0
  104. package/dist/response-guidance-recall.js +2 -1
  105. package/dist/schemas.d.ts +22 -22
  106. package/dist/search/factory.js +2 -2
  107. package/dist/search/index.js +4 -4
  108. package/dist/semantic-consolidation.js +4 -4
  109. package/dist/semantic-rule-promotion.js +3 -3
  110. package/dist/semantic-rule-verifier.js +3 -3
  111. package/dist/storage.js +2 -2
  112. package/dist/summarizer.js +3 -3
  113. package/dist/targeted-fact-recall.d.ts +6 -0
  114. package/dist/targeted-fact-recall.js +2 -1
  115. package/dist/transfer/backup.js +2 -2
  116. package/dist/transfer/capsule-export.js +2 -2
  117. package/dist/transfer/capsule-import.js +2 -2
  118. package/dist/transfer/import-sqlite.js +2 -2
  119. package/dist/transfer/types.d.ts +12 -12
  120. package/dist/verified-recall.js +3 -3
  121. package/package.json +1 -1
  122. package/src/access-service-lcm-forgery.test.ts +410 -0
  123. package/src/access-service-observe-lcm-parity.test.ts +1397 -0
  124. package/src/access-service-observe-scope.test.ts +599 -0
  125. package/src/access-service-raw-excerpt-read-gate.test.ts +443 -0
  126. package/src/access-service.ts +1270 -113
  127. package/src/coding/coding-namespace.test.ts +44 -0
  128. package/src/coding/coding-namespace.ts +163 -0
  129. package/src/event-order-recall.ts +8 -0
  130. package/src/explicit-cue-recall.ts +70 -29
  131. package/src/focused-list-recall.ts +23 -1
  132. package/src/lcm-fallback-read.ts +113 -0
  133. package/src/orchestrator.ts +331 -26
  134. package/src/response-guidance-recall.ts +21 -1
  135. package/src/targeted-fact-recall.ts +24 -3
  136. package/dist/chunk-5ETA6OAS.js.map +0 -1
  137. package/dist/chunk-6NKAQ74D.js.map +0 -1
  138. package/dist/chunk-DB5A3NHS.js.map +0 -1
  139. package/dist/chunk-OAZ5MFUB.js.map +0 -1
  140. package/dist/chunk-OZKZ2TRP.js.map +0 -1
  141. package/dist/chunk-VWT3F4IV.js.map +0 -1
  142. package/dist/chunk-XUGVP7ZU.js.map +0 -1
  143. /package/dist/{auto-sync-54QQHOG5.js.map → auto-sync-5CJBJMPZ.js.map} +0 -0
  144. /package/dist/{capsule-crypto-GWVG7LGC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  145. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  146. /package/dist/{chunk-QKE4LHNR.js.map → chunk-4HYSMH7D.js.map} +0 -0
  147. /package/dist/{chunk-NMIOW7XG.js.map → chunk-4PTKFBST.js.map} +0 -0
  148. /package/dist/{chunk-DDRNDPX4.js.map → chunk-4SKKVWLQ.js.map} +0 -0
  149. /package/dist/{chunk-WPCCNSWO.js.map → chunk-6UKL6IXM.js.map} +0 -0
  150. /package/dist/{chunk-APJQ6UEA.js.map → chunk-AGNBY3VG.js.map} +0 -0
  151. /package/dist/{chunk-4BISW7RX.js.map → chunk-AJE7FJVE.js.map} +0 -0
  152. /package/dist/{chunk-ZXWAQFDE.js.map → chunk-CFOCZPIQ.js.map} +0 -0
  153. /package/dist/{chunk-NT5TINK5.js.map → chunk-DHGSZ3UD.js.map} +0 -0
  154. /package/dist/{chunk-OTC2KOZ2.js.map → chunk-EHQLDFSH.js.map} +0 -0
  155. /package/dist/{chunk-AMACWKM4.js.map → chunk-IJHLC5CH.js.map} +0 -0
  156. /package/dist/{chunk-OR7R6M5Z.js.map → chunk-IVYSVAC6.js.map} +0 -0
  157. /package/dist/{chunk-UMKPSD35.js.map → chunk-JF7SFXTG.js.map} +0 -0
  158. /package/dist/{chunk-MCYT2RNT.js.map → chunk-KJDKZVF3.js.map} +0 -0
  159. /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
  160. /package/dist/{chunk-PQFUUXWK.js.map → chunk-KWM33SPU.js.map} +0 -0
  161. /package/dist/{chunk-A3BS64GV.js.map → chunk-LCC5EZTT.js.map} +0 -0
  162. /package/dist/{chunk-ZT6R3WR3.js.map → chunk-LFTLXOFX.js.map} +0 -0
  163. /package/dist/{chunk-CNRZ6WJU.js.map → chunk-MF32AL7N.js.map} +0 -0
  164. /package/dist/{chunk-D6WVJIS3.js.map → chunk-ORGWWNJG.js.map} +0 -0
  165. /package/dist/{chunk-Z3PZRDLW.js.map → chunk-PRQXUSQV.js.map} +0 -0
  166. /package/dist/{chunk-IMWFHBG2.js.map → chunk-QWRC7GIO.js.map} +0 -0
  167. /package/dist/{chunk-FQYFMIKG.js.map → chunk-RKN5J4RO.js.map} +0 -0
  168. /package/dist/{chunk-FUXV6HSO.js.map → chunk-RSS2KWN6.js.map} +0 -0
  169. /package/dist/{chunk-U3GQ33JC.js.map → chunk-SLTKP5WJ.js.map} +0 -0
  170. /package/dist/{chunk-WEPMT6SC.js.map → chunk-V25ZAOSB.js.map} +0 -0
  171. /package/dist/{chunk-UMTG2BN2.js.map → chunk-V4UDXYGG.js.map} +0 -0
  172. /package/dist/{chunk-RRRCNIPK.js.map → chunk-WJK75OCH.js.map} +0 -0
  173. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
  174. /package/dist/{chunk-ALUZN7BE.js.map → chunk-XMN6MMTU.js.map} +0 -0
  175. /package/dist/{chunk-A4BTPHIN.js.map → chunk-Y7NWBBHV.js.map} +0 -0
  176. /package/dist/{chunk-M75TBFKQ.js.map → chunk-Z2OXSMZK.js.map} +0 -0
@@ -8,7 +8,10 @@ import {
8
8
  } from "./chunk-GDB4J2H3.js";
9
9
  import {
10
10
  importCapsule
11
- } from "./chunk-NT5TINK5.js";
11
+ } from "./chunk-DHGSZ3UD.js";
12
+ import {
13
+ decideDisclosureEscalation
14
+ } from "./chunk-H7XKCNR6.js";
12
15
  import {
13
16
  toRecallExplainJson
14
17
  } from "./chunk-IENGGY2C.js";
@@ -17,9 +20,6 @@ import {
17
20
  normalizeTags,
18
21
  parseTagMatch
19
22
  } from "./chunk-BT7NVCML.js";
20
- import {
21
- decideDisclosureEscalation
22
- } from "./chunk-H7XKCNR6.js";
23
23
  import {
24
24
  buildProposedActions,
25
25
  buildQualityScore,
@@ -27,14 +27,15 @@ import {
27
27
  listMemoryGovernanceRuns,
28
28
  readMemoryGovernanceRunArtifact,
29
29
  runMemoryGovernance
30
- } from "./chunk-UMTG2BN2.js";
30
+ } from "./chunk-V4UDXYGG.js";
31
31
  import {
32
32
  clusterByKey,
33
33
  combineNamespaces,
34
+ lcmSessionKeyForNamespace,
34
35
  projectTagProjectId,
35
36
  resolveCodingNamespaceOverlay,
36
37
  resolveGitContext
37
- } from "./chunk-6GIKAUTN.js";
38
+ } from "./chunk-MMJANTJX.js";
38
39
  import {
39
40
  getTrustZoneStoreStatus,
40
41
  isTrustZoneName,
@@ -52,7 +53,7 @@ import {
52
53
  } from "./chunk-BEMWL2FZ.js";
53
54
  import {
54
55
  namespaceCollectionName
55
- } from "./chunk-NMIOW7XG.js";
56
+ } from "./chunk-4PTKFBST.js";
56
57
  import {
57
58
  namespaceIdentityFromToken
58
59
  } from "./chunk-ZFXCQPNO.js";
@@ -91,10 +92,10 @@ import {
91
92
  buildBriefing,
92
93
  parseBriefingFocus,
93
94
  parseBriefingWindow
94
- } from "./chunk-AMACWKM4.js";
95
+ } from "./chunk-IJHLC5CH.js";
95
96
  import {
96
97
  parseEntityFile
97
- } from "./chunk-A4BTPHIN.js";
98
+ } from "./chunk-Y7NWBBHV.js";
98
99
  import {
99
100
  DEFAULT_RECALL_DISCLOSURE,
100
101
  isRecallDisclosure
@@ -136,7 +137,7 @@ import {
136
137
  } from "./chunk-VFUEZZBS.js";
137
138
  import {
138
139
  exportCapsule
139
- } from "./chunk-BUKK5SWA.js";
140
+ } from "./chunk-KQAFEZQX.js";
140
141
  import {
141
142
  OFFLINE_SYNC_SNAPSHOT_FORMAT,
142
143
  applyOfflineSyncChangeset,
@@ -807,6 +808,140 @@ var EngramAccessService = class {
807
808
  }
808
809
  return combineNamespaces(base, overlay.namespace);
809
810
  }
811
+ /**
812
+ * Resolve ONE effective memory scope plan for a write-producing request
813
+ * (#1495 / seed for epic #1494). The returned {@link MemoryScopePlan} is the
814
+ * single source of truth `observe` (and, later, other write surfaces) consume
815
+ * so every side effect lands in `plan.writeNamespace`.
816
+ *
817
+ * Authorization mirrors {@link resolveCodingScopedWriteNamespace} EXACTLY so
818
+ * `observe`'s scoping is identical to `memory_store`/`suggestion_submit`
819
+ * (rule 39 — feature gates identical across code paths):
820
+ * - an explicit `namespace` always wins and is authorized strictly through
821
+ * `resolveWritableNamespace` → `canWriteNamespace`; an overlay-shaped string
822
+ * is never a writable target (rule 42 / 47 / 48);
823
+ * - with NO overlay, the base stays on `config.defaultNamespace` (pre-#1434
824
+ * behavior), auth-checked;
825
+ * - WITH an overlay, the base is the principal self namespace and the overlay
826
+ * is REBUILT from that authorized base — never accepted as a caller string.
827
+ *
828
+ * READ-ONLY: this never mutates session coding context. Callers that need the
829
+ * `cwd`/`projectTag` bound to the session (so a later bare recall is scoped)
830
+ * must attach it via `maybeAttachCodingContext` BEFORE calling this, which
831
+ * also preserves the no-orphan-context guard (attach only after auth passes).
832
+ * The overlay here reads the session's attached context first (matching recall
833
+ * precedence), falling back to the per-call `cwd`/`projectTag`.
834
+ */
835
+ async resolveMemoryScopePlan(request) {
836
+ const warnings = [];
837
+ const principal = this.resolveRequestPrincipal(
838
+ request.sessionKey,
839
+ request.authenticatedPrincipal
840
+ );
841
+ const hasExplicitNamespace = typeof request.namespace === "string" && request.namespace.trim().length > 0;
842
+ if (hasExplicitNamespace) {
843
+ const writeNamespace2 = this.resolveWritableNamespace(
844
+ request.namespace,
845
+ request.sessionKey,
846
+ request.authenticatedPrincipal
847
+ );
848
+ return {
849
+ principal,
850
+ explicitNamespace: request.namespace.trim(),
851
+ baseNamespace: writeNamespace2,
852
+ writeNamespace: writeNamespace2,
853
+ objectiveStateNamespace: writeNamespace2,
854
+ readNamespaces: [writeNamespace2],
855
+ codingOverlayApplied: false,
856
+ warnings
857
+ };
858
+ }
859
+ const baseNamespace = defaultNamespaceForPrincipal(
860
+ principal,
861
+ this.orchestrator.config
862
+ );
863
+ const hasSession = typeof request.sessionKey === "string" && request.sessionKey.length > 0;
864
+ const overlayEligible = hasSession && this.orchestrator.config.namespacesEnabled === true && this.orchestrator.config.codingMode?.projectScope === true;
865
+ let attachedContext = hasSession ? this.orchestrator.getCodingContextForSession(request.sessionKey) : null;
866
+ let seededContext = false;
867
+ if (overlayEligible && !attachedContext) {
868
+ attachedContext = await this.resolveCodingContextFromOptions(request);
869
+ if (attachedContext) {
870
+ this.orchestrator.setCodingContextForSession(
871
+ request.sessionKey,
872
+ attachedContext
873
+ );
874
+ seededContext = true;
875
+ }
876
+ }
877
+ const clearSeededContext = () => {
878
+ if (seededContext && hasSession) {
879
+ this.orchestrator.setCodingContextForSession(request.sessionKey, null);
880
+ }
881
+ };
882
+ const overlaidBase = this.orchestrator.applyCodingNamespaceOverlay(
883
+ request.sessionKey,
884
+ baseNamespace
885
+ );
886
+ const codingOverlayApplied = overlaidBase !== baseNamespace;
887
+ if (!codingOverlayApplied) {
888
+ const writeNamespace2 = this.resolveWritableNamespace(
889
+ void 0,
890
+ request.sessionKey,
891
+ request.authenticatedPrincipal
892
+ );
893
+ const willWriteObjectiveState = this.orchestrator.config.objectiveStateMemoryEnabled === true && this.orchestrator.config.objectiveStateSnapshotWritesEnabled === true;
894
+ if (willWriteObjectiveState && this.orchestrator.config.namespacesEnabled === true && !canWriteNamespace(principal, baseNamespace, this.orchestrator.config)) {
895
+ clearSeededContext();
896
+ throw new EngramAccessInputError(
897
+ `namespace is not writable: ${baseNamespace}`
898
+ );
899
+ }
900
+ return {
901
+ principal,
902
+ // scopeDebug.baseNamespace must report the principal SELF base
903
+ // (`defaultNamespaceForPrincipal`), NOT the general write namespace —
904
+ // which collapses to config.defaultNamespace on this implicit no-overlay
905
+ // path (#1505 cursor "Wrong scopeDebug base namespace"). It already
906
+ // matches `objectiveStateNamespace` below; `writeNamespace` is unchanged.
907
+ baseNamespace,
908
+ writeNamespace: writeNamespace2,
909
+ // Implicit objective-state stays on the principal self base, NOT the
910
+ // (possibly default) general write namespace — preserving the #928
911
+ // semantics the objective-state suite asserts.
912
+ objectiveStateNamespace: baseNamespace,
913
+ readNamespaces: [writeNamespace2],
914
+ codingOverlayApplied: false,
915
+ warnings
916
+ };
917
+ }
918
+ if (!canWriteNamespace(principal, baseNamespace, this.orchestrator.config)) {
919
+ clearSeededContext();
920
+ throw new EngramAccessInputError(
921
+ `namespace is not writable: ${baseNamespace}`
922
+ );
923
+ }
924
+ const writeNamespace = overlaidBase;
925
+ const readNamespaces = [writeNamespace];
926
+ const overlay = resolveCodingNamespaceOverlay(
927
+ attachedContext,
928
+ this.orchestrator.config.codingMode,
929
+ this.orchestrator.config.defaultNamespace
930
+ );
931
+ for (const fallback of overlay?.readFallbacks ?? []) {
932
+ const ns = combineNamespaces(baseNamespace, fallback);
933
+ if (!readNamespaces.includes(ns)) readNamespaces.push(ns);
934
+ }
935
+ return {
936
+ principal,
937
+ baseNamespace,
938
+ writeNamespace,
939
+ objectiveStateNamespace: writeNamespace,
940
+ readNamespaces,
941
+ codingOverlayApplied: true,
942
+ warnings
943
+ };
944
+ }
810
945
  async objectiveStateStoreLocationForNamespace(namespace) {
811
946
  if (!this.orchestrator.config.namespacesEnabled) {
812
947
  return {
@@ -938,7 +1073,10 @@ var EngramAccessService = class {
938
1073
  options.disclosure,
939
1074
  {
940
1075
  query: options.query,
941
- ...options.sessionKey ? { sessionKey: options.sessionKey } : {}
1076
+ ...options.sessionKey ? { sessionKey: options.sessionKey } : {},
1077
+ ...options.rawExcerptNamespace ? { rawExcerptNamespace: options.rawExcerptNamespace } : {},
1078
+ ...options.rawExcerptSessionIds ? { rawExcerptSessionIds: options.rawExcerptSessionIds } : {},
1079
+ ...options.rawExcerptsSuppressed ? { rawExcerptsSuppressed: options.rawExcerptsSuppressed } : {}
942
1080
  }
943
1081
  );
944
1082
  const context = results.map((result) => {
@@ -1070,9 +1208,19 @@ var EngramAccessService = class {
1070
1208
  }
1071
1209
  return null;
1072
1210
  };
1073
- const rawExcerptsResult = await this.fetchRawExcerpts(
1211
+ const rawExcerptsResult = rawContext?.rawExcerptsSuppressed === true ? (
1212
+ // Implicit raw recall with NO readable LCM namespace (#1505 thread
1213
+ // NBHWz): emit empty excerpts rather than falling back to the
1214
+ // write/overlay `namespace` the read gate excludes.
1215
+ []
1216
+ ) : await this.fetchRawExcerpts(
1074
1217
  disclosure,
1075
- rawContext ? { ...rawContext, namespace } : null
1218
+ rawContext ? {
1219
+ query: rawContext.query,
1220
+ ...rawContext.sessionKey ? { sessionKey: rawContext.sessionKey } : {},
1221
+ namespace: rawContext.rawExcerptNamespace ?? namespace,
1222
+ ...rawContext.rawExcerptSessionIds ? { lcmSessionIds: rawContext.rawExcerptSessionIds } : {}
1223
+ } : null
1076
1224
  );
1077
1225
  const rawExcerpts = rawExcerptsResult ?? void 0;
1078
1226
  for (const memoryPath of snapshot.resultPaths ?? []) {
@@ -1170,22 +1318,32 @@ var EngramAccessService = class {
1170
1318
  const lcm = this.orchestrator.lcmEngine;
1171
1319
  if (!lcm || !lcm.enabled) return [];
1172
1320
  try {
1173
- const lcmSessionKey = context.namespace && context.namespace !== this.orchestrator.config.defaultNamespace ? `${context.namespace}:${context.sessionKey}` : context.sessionKey;
1174
- const rows = await lcm.searchContextFull(
1175
- context.query,
1176
- // Cap the excerpt fanout so recall responses stay bounded. Five
1177
- // matches is enough to anchor the model in the raw transcript
1178
- // without ballooning token spend; raw is meant as the escape
1179
- // hatch, not the default.
1180
- 5,
1181
- lcmSessionKey
1182
- );
1183
- return rows.map((r) => ({
1184
- turnIndex: r.turn_index,
1185
- role: r.role,
1186
- content: r.content,
1187
- sessionId: r.session_id
1188
- }));
1321
+ const legacyKey = context.namespace && context.namespace !== this.orchestrator.config.defaultNamespace ? `${context.namespace}:${context.sessionKey}` : context.sessionKey;
1322
+ const lcmSessionIds = context.lcmSessionIds && context.lcmSessionIds.length > 0 ? context.lcmSessionIds : [legacyKey];
1323
+ const limit = 5;
1324
+ const seenRows = /* @__PURE__ */ new Set();
1325
+ const excerpts = [];
1326
+ for (const lcmSessionKey of lcmSessionIds) {
1327
+ if (excerpts.length >= limit) break;
1328
+ const rows = await lcm.searchContextFull(
1329
+ context.query,
1330
+ limit,
1331
+ lcmSessionKey
1332
+ );
1333
+ for (const r of rows) {
1334
+ const dedupeKey = `${r.session_id} ${r.turn_index}`;
1335
+ if (seenRows.has(dedupeKey)) continue;
1336
+ seenRows.add(dedupeKey);
1337
+ excerpts.push({
1338
+ turnIndex: r.turn_index,
1339
+ role: r.role,
1340
+ content: r.content,
1341
+ sessionId: r.session_id
1342
+ });
1343
+ if (excerpts.length >= limit) break;
1344
+ }
1345
+ }
1346
+ return excerpts;
1189
1347
  } catch {
1190
1348
  return [];
1191
1349
  }
@@ -1701,9 +1859,26 @@ var EngramAccessService = class {
1701
1859
  topKConfidence
1702
1860
  });
1703
1861
  const disclosure = escalationDecision.effective;
1862
+ const trimmedSessionKey = request.sessionKey?.trim() || void 0;
1863
+ const rawExcerptNamespace = disclosure === "raw" ? this.resolveRawExcerptReadNamespace(
1864
+ request.namespace,
1865
+ trimmedSessionKey,
1866
+ authenticatedPrincipal
1867
+ ) : void 0;
1868
+ const hasExplicitNamespace = typeof request.namespace === "string" && request.namespace.trim().length > 0;
1869
+ const rawExcerptsSuppressed = disclosure === "raw" && !hasExplicitNamespace && rawExcerptNamespace === void 0;
1870
+ const rawExcerptSessionIds = disclosure === "raw" && rawExcerptNamespace && trimmedSessionKey ? this.resolveLcmReadSessionIds(
1871
+ request.namespace,
1872
+ rawExcerptNamespace,
1873
+ trimmedSessionKey,
1874
+ authenticatedPrincipal
1875
+ ) : void 0;
1704
1876
  let results = await this.serializeRecallResults(snapshot, disclosure, {
1705
1877
  query,
1706
- sessionKey: request.sessionKey
1878
+ sessionKey: trimmedSessionKey,
1879
+ ...rawExcerptNamespace ? { rawExcerptNamespace } : {},
1880
+ ...rawExcerptSessionIds ? { rawExcerptSessionIds } : {},
1881
+ ...rawExcerptsSuppressed ? { rawExcerptsSuppressed } : {}
1707
1882
  });
1708
1883
  const filterTags = normalizeTags(request.tags);
1709
1884
  let tagMatchMode;
@@ -1983,11 +2158,25 @@ var EngramAccessService = class {
1983
2158
  const disclosure2 = request.disclosure;
1984
2159
  const namespace = snapshot.namespace ? this.resolveNamespace(snapshot.namespace) : this.orchestrator.config.defaultNamespace;
1985
2160
  const trimmedSessionKey = request.sessionKey?.trim() || void 0;
1986
- const rawExcerpts = disclosure2 === "raw" ? await this.fetchRawExcerpts(disclosure2, {
2161
+ const rawExcerptNamespace = disclosure2 === "raw" ? this.resolveRawExcerptReadNamespace(
2162
+ request.namespace,
2163
+ trimmedSessionKey,
2164
+ authenticatedPrincipal
2165
+ ) : namespace;
2166
+ const xrayHasExplicitNamespace = typeof request.namespace === "string" && request.namespace.trim().length > 0;
2167
+ const rawExcerptsSuppressed = disclosure2 === "raw" && !xrayHasExplicitNamespace && rawExcerptNamespace === void 0;
2168
+ const rawExcerptSessionIds = disclosure2 === "raw" && trimmedSessionKey && rawExcerptNamespace ? this.resolveLcmReadSessionIds(
2169
+ request.namespace,
2170
+ rawExcerptNamespace,
2171
+ trimmedSessionKey,
2172
+ authenticatedPrincipal
2173
+ ) : void 0;
2174
+ const rawExcerpts = disclosure2 === "raw" && !rawExcerptsSuppressed ? await this.fetchRawExcerpts(disclosure2, {
1987
2175
  query,
1988
2176
  ...trimmedSessionKey ? { sessionKey: trimmedSessionKey } : {},
1989
- namespace
1990
- }) : null;
2177
+ ...rawExcerptNamespace ? { namespace: rawExcerptNamespace } : {},
2178
+ ...rawExcerptSessionIds ? { lcmSessionIds: rawExcerptSessionIds } : {}
2179
+ }) : disclosure2 === "raw" ? [] : null;
1991
2180
  const rawExcerptText = rawExcerpts && rawExcerpts.length > 0 ? rawExcerpts.map((e) => e.content).join("\n") : "";
1992
2181
  const memoryByIndex = await Promise.all(
1993
2182
  snapshot.results.map(async (result) => {
@@ -2041,6 +2230,19 @@ var EngramAccessService = class {
2041
2230
  release();
2042
2231
  }
2043
2232
  if (request.includeRecall === true && xrayResponse.snapshotFound === true && xrayResponse.snapshot) {
2233
+ const xrayRawExcerptNamespace = disclosure === "raw" ? this.resolveRawExcerptReadNamespace(
2234
+ request.namespace,
2235
+ recallSessionKey,
2236
+ authenticatedPrincipal
2237
+ ) : void 0;
2238
+ const xrayHasExplicitNamespace = typeof request.namespace === "string" && request.namespace.trim().length > 0;
2239
+ const xrayRawExcerptsSuppressed = disclosure === "raw" && !xrayHasExplicitNamespace && xrayRawExcerptNamespace === void 0;
2240
+ const xrayRawExcerptSessionIds = disclosure === "raw" && xrayRawExcerptNamespace && recallSessionKey ? this.resolveLcmReadSessionIds(
2241
+ request.namespace,
2242
+ xrayRawExcerptNamespace,
2243
+ recallSessionKey,
2244
+ authenticatedPrincipal
2245
+ ) : void 0;
2044
2246
  return {
2045
2247
  ...xrayResponse,
2046
2248
  recall: await this.buildRecallResponseFromXraySnapshot({
@@ -2050,7 +2252,10 @@ var EngramAccessService = class {
2050
2252
  disclosure,
2051
2253
  startedAt: recallStartedAt,
2052
2254
  requestedMode: request.mode,
2053
- normalizedMode: mode
2255
+ normalizedMode: mode,
2256
+ ...xrayRawExcerptNamespace ? { rawExcerptNamespace: xrayRawExcerptNamespace } : {},
2257
+ ...xrayRawExcerptSessionIds ? { rawExcerptSessionIds: xrayRawExcerptSessionIds } : {},
2258
+ ...xrayRawExcerptsSuppressed ? { rawExcerptsSuppressed: xrayRawExcerptsSuppressed } : {}
2054
2259
  })
2055
2260
  };
2056
2261
  }
@@ -2987,40 +3192,23 @@ var EngramAccessService = class {
2987
3192
  throw new EngramAccessInputError(`invalid message role: ${msg.role} (expected 'user' or 'assistant')`);
2988
3193
  }
2989
3194
  }
2990
- const hasExplicitNamespace = typeof request.namespace === "string" && request.namespace.trim().length > 0;
2991
- const principal = this.resolveRequestPrincipal(
2992
- request.sessionKey,
2993
- request.authenticatedPrincipal
2994
- );
2995
- const namespace = this.resolveWritableNamespace(
2996
- request.namespace,
2997
- request.sessionKey,
2998
- request.authenticatedPrincipal
2999
- );
3195
+ const scope = await this.resolveMemoryScopePlan(request);
3196
+ const writeNamespace = scope.writeNamespace;
3197
+ const namespace = scope.explicitNamespace ? scope.writeNamespace : scope.codingOverlayApplied ? this.orchestrator.config.defaultNamespace : scope.writeNamespace;
3000
3198
  const shouldWriteObjectiveState = this.orchestrator.config.objectiveStateMemoryEnabled === true && this.orchestrator.config.objectiveStateSnapshotWritesEnabled === true;
3001
- const objectiveStateBaseNamespace = hasExplicitNamespace ? namespace : defaultNamespaceForPrincipal(principal, this.orchestrator.config);
3002
- if (shouldWriteObjectiveState && !hasExplicitNamespace && !canWriteNamespace(
3003
- principal,
3004
- objectiveStateBaseNamespace,
3005
- this.orchestrator.config
3006
- )) {
3007
- throw new EngramAccessInputError(
3008
- `namespace is not writable: ${objectiveStateBaseNamespace}`
3009
- );
3010
- }
3011
3199
  await this.maybeAttachCodingContext(request.sessionKey, {
3012
3200
  cwd: request.cwd,
3013
3201
  projectTag: request.projectTag
3014
3202
  });
3015
- const objectiveStateNamespace = hasExplicitNamespace ? namespace : this.orchestrator.applyCodingNamespaceOverlay(
3203
+ const lcmSessionKey = lcmSessionKeyForNamespace(
3204
+ writeNamespace,
3016
3205
  request.sessionKey,
3017
- objectiveStateBaseNamespace
3018
- );
3019
- const lcmSessionKey = namespace !== this.orchestrator.config.defaultNamespace ? `${namespace}:${request.sessionKey}` : request.sessionKey;
3206
+ this.orchestrator.config.defaultNamespace
3207
+ ) ?? request.sessionKey;
3020
3208
  if (shouldWriteObjectiveState) {
3021
3209
  try {
3022
3210
  const objectiveStateLocation = await this.objectiveStateStoreLocationForNamespace(
3023
- objectiveStateNamespace
3211
+ scope.objectiveStateNamespace
3024
3212
  );
3025
3213
  await recordObjectiveStateSnapshotsFromObservedMessages({
3026
3214
  memoryDir: objectiveStateLocation.memoryDir,
@@ -3048,7 +3236,16 @@ var EngramAccessService = class {
3048
3236
  if (request.skipExtraction !== true) {
3049
3237
  const turns = request.messages.map((m) => ({
3050
3238
  source: "openclaw",
3051
- sessionKey: lcmSessionKey,
3239
+ // Identity-vs-routing separation (#1505 thread 1, cursor): extraction
3240
+ // derives the provenance principal via `resolvePrincipal(turn.sessionKey)`
3241
+ // and threads `turn.sessionKey` into conversation threading. Feeding the
3242
+ // namespace-PREFIXED `lcmSessionKey` here mis-derived the principal to
3243
+ // `default` (a `<ns>:<key>` string matches no prefix/map rule and fails
3244
+ // the `agent:` heuristic). Pass the ORIGINAL sessionKey so identity is
3245
+ // correct; storage routing is pinned separately via
3246
+ // writeNamespaceOverride below, and the authenticated principal is pinned
3247
+ // via principalOverride.
3248
+ sessionKey: request.sessionKey,
3052
3249
  role: m.role,
3053
3250
  content: m.content,
3054
3251
  parts: m.parts,
@@ -3056,9 +3253,13 @@ var EngramAccessService = class {
3056
3253
  sourceFormat: m.sourceFormat,
3057
3254
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3058
3255
  }));
3256
+ const writeNamespaceOverride = this.orchestrator.config.namespacesEnabled === true ? writeNamespace : void 0;
3257
+ const principalOverride = typeof scope.principal === "string" && scope.principal.length > 0 ? scope.principal : void 0;
3059
3258
  try {
3060
3259
  const extractionPromise = this.orchestrator.ingestReplayBatch(turns, {
3061
- archiveLcm: false
3260
+ archiveLcm: false,
3261
+ writeNamespaceOverride,
3262
+ principalOverride
3062
3263
  });
3063
3264
  extractionPromise.catch((err) => {
3064
3265
  log.error(`access-observe background extraction failed: ${err}`);
@@ -3069,12 +3270,21 @@ var EngramAccessService = class {
3069
3270
  }
3070
3271
  }
3071
3272
  log.info(
3072
- `access-observe namespace=${namespace} sessionKey=${request.sessionKey} messages=${request.messages.length} lcm=${lcmArchived} extraction=${extractionQueued}`
3273
+ `access-observe namespace=${namespace} effectiveNamespace=${writeNamespace} sessionKey=${request.sessionKey} messages=${request.messages.length} lcm=${lcmArchived} extraction=${extractionQueued}`
3073
3274
  );
3074
3275
  return {
3075
3276
  accepted: request.messages.length,
3076
3277
  sessionKey: request.sessionKey,
3077
3278
  namespace,
3279
+ effectiveNamespace: writeNamespace,
3280
+ scopeDebug: {
3281
+ principal: scope.principal,
3282
+ explicitNamespace: scope.explicitNamespace,
3283
+ baseNamespace: scope.baseNamespace,
3284
+ writeNamespace: scope.writeNamespace,
3285
+ codingOverlayApplied: scope.codingOverlayApplied,
3286
+ readNamespaces: scope.readNamespaces
3287
+ },
3078
3288
  lcmArchived,
3079
3289
  extractionQueued
3080
3290
  };
@@ -3084,30 +3294,76 @@ var EngramAccessService = class {
3084
3294
  throw new EngramAccessInputError("query is required and must be a non-empty string");
3085
3295
  }
3086
3296
  const principal = this.resolveRequestPrincipal(request.sessionKey, request.authenticatedPrincipal);
3087
- const namespace = this.resolveReadableNamespace(request.namespace, principal);
3297
+ const hasExplicitNamespace = typeof request.namespace === "string" && request.namespace.trim().length > 0;
3298
+ const namespace = hasExplicitNamespace ? this.resolveReadableNamespace(request.namespace, principal) : this.resolveImplicitLcmReadFallbackNamespace(principal);
3088
3299
  if (!this.orchestrator.lcmEngine || !this.orchestrator.lcmEngine.enabled) {
3089
3300
  return {
3090
3301
  query: request.query,
3091
- namespace,
3302
+ namespace: namespace ?? this.orchestrator.config.defaultNamespace,
3092
3303
  results: [],
3093
3304
  count: 0,
3094
3305
  lcmEnabled: false
3095
3306
  };
3096
3307
  }
3308
+ if (namespace === void 0) {
3309
+ return {
3310
+ query: request.query,
3311
+ namespace: this.orchestrator.config.defaultNamespace,
3312
+ results: [],
3313
+ count: 0,
3314
+ lcmEnabled: true
3315
+ };
3316
+ }
3097
3317
  const limit = Math.max(1, Math.min(request.limit ?? 10, 100));
3098
- const lcmSessionKey = request.sessionKey && namespace !== this.orchestrator.config.defaultNamespace ? `${namespace}:${request.sessionKey}` : request.sessionKey;
3099
- const lcmSessionPrefix = request.sessionPrefix && namespace !== this.orchestrator.config.defaultNamespace ? `${namespace}:${request.sessionPrefix}` : request.sessionPrefix;
3100
- const rawResults = await this.orchestrator.lcmEngine.searchContextFull(
3101
- request.query,
3102
- limit,
3103
- lcmSessionKey,
3104
- lcmSessionPrefix
3318
+ const lcmReadNamespace = this.resolveLcmReadNamespace(
3319
+ request.namespace,
3320
+ namespace,
3321
+ request.sessionKey,
3322
+ request.authenticatedPrincipal
3105
3323
  );
3106
- const results = rawResults.map((r) => ({
3107
- sessionId: r.session_id,
3108
- content: r.content,
3109
- turnIndex: r.turn_index
3110
- }));
3324
+ const lcmSessionKeyIds = request.sessionKey ? this.resolveLcmReadSessionIds(
3325
+ request.namespace,
3326
+ namespace,
3327
+ request.sessionKey,
3328
+ request.authenticatedPrincipal
3329
+ ) : [void 0];
3330
+ const lcmSessionPrefix = request.sessionPrefix ? lcmSessionKeyForNamespace(
3331
+ lcmReadNamespace,
3332
+ request.sessionPrefix,
3333
+ this.orchestrator.config.defaultNamespace
3334
+ ) ?? request.sessionPrefix : request.sessionPrefix;
3335
+ const hasScopedSession = typeof request.sessionKey === "string" && request.sessionKey.length > 0 || typeof lcmSessionPrefix === "string" && lcmSessionPrefix.length > 0;
3336
+ if (!hasScopedSession && this.orchestrator.config.namespacesEnabled === true) {
3337
+ return {
3338
+ query: request.query,
3339
+ namespace,
3340
+ results: [],
3341
+ count: 0,
3342
+ lcmEnabled: true
3343
+ };
3344
+ }
3345
+ const seenRows = /* @__PURE__ */ new Set();
3346
+ const results = [];
3347
+ for (const lcmSessionKey of lcmSessionKeyIds) {
3348
+ if (results.length >= limit) break;
3349
+ const rawResults = await this.orchestrator.lcmEngine.searchContextFull(
3350
+ request.query,
3351
+ limit,
3352
+ lcmSessionKey,
3353
+ lcmSessionPrefix
3354
+ );
3355
+ for (const r of rawResults) {
3356
+ const dedupeKey = `${r.session_id}\0${r.turn_index}`;
3357
+ if (seenRows.has(dedupeKey)) continue;
3358
+ seenRows.add(dedupeKey);
3359
+ results.push({
3360
+ sessionId: r.session_id,
3361
+ content: r.content,
3362
+ turnIndex: r.turn_index
3363
+ });
3364
+ if (results.length >= limit) break;
3365
+ }
3366
+ }
3111
3367
  return {
3112
3368
  query: request.query,
3113
3369
  namespace,
@@ -3116,15 +3372,282 @@ var EngramAccessService = class {
3116
3372
  lcmEnabled: true
3117
3373
  };
3118
3374
  }
3375
+ /**
3376
+ * Resolve the LCM `session_id` a same-session READER (compaction flush/record,
3377
+ * `lcmSearch`, raw-excerpt lookup) must target so it matches the key `observe`
3378
+ * archived under (#1495 thread 2 + #1505 round 3, rule 42). One helper for
3379
+ * EVERY access-surface LCM read so the read key cannot drift from the write key
3380
+ * (rule 22).
3381
+ *
3382
+ * Precedence mirrors `observe`'s effective write namespace:
3383
+ * - With an explicit `request.namespace`, use the already-authorized
3384
+ * `resolvedNamespace` (the overlay never applies to an explicit write).
3385
+ * - With NO explicit namespace, an auto-scoped session was archived under
3386
+ * its coding-overlay namespace, so overlay the session's bound coding
3387
+ * context onto the principal self base — the SAME resolution
3388
+ * `resolveMemoryScopePlan`/recall use. `applyCodingNamespaceOverlay`
3389
+ * returns the base unchanged when projectScope/namespaces are off or no
3390
+ * context is bound, so single-store / no-overlay flows collapse to the raw
3391
+ * sessionKey exactly as before.
3392
+ *
3393
+ * Then encode the `${namespace}:${sessionKey}` prefix via the shared helper
3394
+ * so the read key is byte-for-byte what the LCM write and the recall readers
3395
+ * use.
3396
+ */
3397
+ /**
3398
+ * Resolve the effective LCM NAMESPACE a same-session operation must prefix
3399
+ * with (the namespace half of {@link resolveLcmReadSessionKey}). Split out so
3400
+ * `lcmSearch` can apply ONE namespace to BOTH its `sessionKey` and its
3401
+ * `sessionPrefix` — the prefix is a search fragment, not a real session, so its
3402
+ * own coding context can't be looked up; it must inherit the namespace resolved
3403
+ * from the real session (`sessionKeyForOverlay`).
3404
+ *
3405
+ * `purpose` selects the AUTHORIZATION gate applied before honouring the
3406
+ * coding overlay (#1505 round 3 + round 4, codex P2):
3407
+ *
3408
+ * - `"read"` (`lcmSearch` / raw-excerpt recall): the overlay rows are only
3409
+ * visible when the principal SELF base is in the READABLE RECALL SET — the
3410
+ * same gate the orchestrator's `lcmReadNamespaceForSession` and the recall
3411
+ * namespace set use (`recallNamespacesForPrincipal`, gated by both
3412
+ * `defaultRecallNamespaces.includes("self")` AND `canReadNamespace`). A
3413
+ * caller that passed the default read check must NOT receive
3414
+ * `<principal>-project-*` rows the policy never granted (cross-tenant read
3415
+ * leak). When the self base is not readable, keep the just-authorized
3416
+ * namespace (collapses to the raw key on the default store).
3417
+ *
3418
+ * - `"write"` (`lcmCompactionFlush` / `lcmCompactionRecord`): these are
3419
+ * write/maintenance operations on the SAME queue `observe` just wrote, so
3420
+ * the gate must mirror observe's WRITE authorization (`canWriteNamespace`
3421
+ * on the self base), NOT readability. A principal that can WRITE but not
3422
+ * READ its self namespace (or whose `defaultRecallNamespaces` omits `self`)
3423
+ * archived under the overlay key via `observe`; gating compaction by
3424
+ * readability would fall back to the default/raw key and leave that queue
3425
+ * never flushed/recorded (round-4 codex P2). Write-authorized ⇒ overlay
3426
+ * key, matching the observe write key (rule 42 read/write parity; rule 39
3427
+ * identical gates across paths).
3428
+ */
3429
+ resolveLcmReadNamespace(explicitNamespace, resolvedNamespace, sessionKeyForOverlay, authenticatedPrincipal, purpose = "read") {
3430
+ const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
3431
+ if (hasExplicitNamespace) return resolvedNamespace;
3432
+ const principal = this.resolveRequestPrincipal(
3433
+ sessionKeyForOverlay,
3434
+ authenticatedPrincipal
3435
+ );
3436
+ const base = defaultNamespaceForPrincipal(principal, this.orchestrator.config);
3437
+ const overlaid = this.orchestrator.applyCodingNamespaceOverlay(
3438
+ sessionKeyForOverlay,
3439
+ base
3440
+ );
3441
+ if (overlaid === base) return this.orchestrator.config.defaultNamespace;
3442
+ const authorized = purpose === "write" ? canWriteNamespace(principal, base, this.orchestrator.config) : recallNamespacesForPrincipal(
3443
+ principal,
3444
+ this.orchestrator.config
3445
+ ).includes(base);
3446
+ if (authorized) return overlaid;
3447
+ if (purpose === "read") return this.orchestrator.config.defaultNamespace;
3448
+ return resolvedNamespace;
3449
+ }
3450
+ /**
3451
+ * Resolve the namespace the raw-disclosure excerpt lookup
3452
+ * ({@link fetchRawExcerpts}) must prefix its LCM `session_id` with (#1505
3453
+ * thread 2f7). Raw disclosure reads the SAME LCM archive `lcmSearch` and the
3454
+ * in-prompt LCM sections read, so it MUST pass through the identical
3455
+ * read-authorization gate — NOT `snapshot.namespace`, which records the
3456
+ * effective WRITE/overlay namespace (`<principal>-project-*`) even when the
3457
+ * principal can WRITE but not READ its self base (or `defaultRecallNamespaces`
3458
+ * omits `self`). Routing through `resolveLcmReadNamespace(..., "read")` makes
3459
+ * raw disclosure fall back to the default store exactly like normal recall +
3460
+ * `lcmSearch`, so it never attaches overlay transcript rows the read gate
3461
+ * excludes (cross-tenant read leak). Collapses to the default store / raw
3462
+ * sessionKey for single-store / no-overlay / explicit-default flows, so
3463
+ * single-user recall is byte-for-byte unchanged.
3464
+ *
3465
+ * Returns `undefined` when NO readable LCM namespace exists for an IMPLICIT
3466
+ * (no explicit `namespace`) raw recall — i.e. a restrictive `default` READ
3467
+ * policy denies the principal `default` AND no overlay/self namespace is
3468
+ * readable. In that case the caller emits NO excerpts rather than throwing
3469
+ * `namespace is not readable: default` (#1505 thread NBHWz): normal recall
3470
+ * still succeeds via `recallNamespacesForPrincipal`, so `disclosure: "raw"`
3471
+ * must degrade gracefully (empty excerpts), never pre-authorize `default`.
3472
+ *
3473
+ * IMPLICIT-namespace fallback selection derives from the ALREADY
3474
+ * read-authorized recall namespace set (`recallNamespacesForPrincipal` +
3475
+ * `canReadNamespace`) — the principal's self base when it is in the readable
3476
+ * recall set, else `config.defaultNamespace` ONLY when the principal may read
3477
+ * it. It NEVER pre-authorizes `default`. An EXPLICIT `namespace` is still
3478
+ * authorized strictly via `resolveReadableNamespace` (explicit reads must pass
3479
+ * the ACL — no behavior change).
3480
+ */
3481
+ resolveRawExcerptReadNamespace(explicitNamespace, sessionKey, authenticatedPrincipal) {
3482
+ const principal = this.resolveRequestPrincipal(
3483
+ sessionKey,
3484
+ authenticatedPrincipal
3485
+ );
3486
+ const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
3487
+ if (hasExplicitNamespace) {
3488
+ const resolvedNamespace = this.resolveReadableNamespace(
3489
+ explicitNamespace,
3490
+ principal
3491
+ );
3492
+ return this.resolveLcmReadNamespace(
3493
+ explicitNamespace,
3494
+ resolvedNamespace,
3495
+ sessionKey,
3496
+ authenticatedPrincipal,
3497
+ "read"
3498
+ );
3499
+ }
3500
+ const fallbackNamespace = this.resolveImplicitLcmReadFallbackNamespace(principal);
3501
+ if (fallbackNamespace === void 0) return void 0;
3502
+ return this.resolveLcmReadNamespace(
3503
+ explicitNamespace,
3504
+ fallbackNamespace,
3505
+ sessionKey,
3506
+ authenticatedPrincipal,
3507
+ "read"
3508
+ );
3509
+ }
3510
+ /**
3511
+ * The base `resolvedNamespace` an IMPLICIT (no explicit `namespace`)
3512
+ * same-session LCM READER (`resolveRawExcerptReadNamespace`, `lcmSearch`)
3513
+ * passes into {@link resolveLcmReadNamespace} — WITHOUT pre-authorizing
3514
+ * `default` (#1505 thread NBHWz). It decides PROCEED vs SUPPRESS only; the
3515
+ * actual LCM prefix is then resolved by `resolveLcmReadNamespace`, which
3516
+ * mirrors the orchestrator's `lcmReadNamespaceForSession` EXACTLY (rule 39 /
3517
+ * 42): the coding overlay when the principal SELF base is in the readable
3518
+ * recall set, else `config.defaultNamespace` (the raw key).
3519
+ *
3520
+ * Returns `config.defaultNamespace` (PROCEED) whenever the principal has ANY
3521
+ * readable LCM access — either `default` itself is readable, OR a coding
3522
+ * overlay / self base is in the readable recall set. The returned value is
3523
+ * ALWAYS `config.defaultNamespace`, NEVER an arbitrary readable recall
3524
+ * namespace (e.g. `shared`): `resolveLcmReadNamespace` returns this fallback
3525
+ * verbatim only on the overlay-applies-but-self-unreadable branch, where the
3526
+ * orchestrator collapses to the default store — so returning anything but the
3527
+ * default store there would prefix LCM reads with `shared:sessionKey` while
3528
+ * in-prompt recall uses the raw `sessionKey`, diverging the two (cursor
3529
+ * "LCM read gate wrong fallback").
3530
+ *
3531
+ * Returns `undefined` (SUPPRESS) only when NO readable LCM namespace exists —
3532
+ * a restrictive `default` READ policy AND no readable overlay/self — so the
3533
+ * caller emits NO rows instead of throwing `namespace is not readable:
3534
+ * default`. Normal recall still succeeds through the readable self namespace.
3535
+ *
3536
+ * Single-store / namespaces-disabled deployments resolve to
3537
+ * `config.defaultNamespace`, keeping single-user recall byte-for-byte
3538
+ * unchanged.
3539
+ */
3540
+ resolveImplicitLcmReadFallbackNamespace(principal) {
3541
+ const config = this.orchestrator.config;
3542
+ if (!config.namespacesEnabled) return config.defaultNamespace;
3543
+ if (canReadNamespace(principal, config.defaultNamespace, config)) {
3544
+ return config.defaultNamespace;
3545
+ }
3546
+ const selfBase = defaultNamespaceForPrincipal(principal, config);
3547
+ const selfReadableInRecall = recallNamespacesForPrincipal(
3548
+ principal,
3549
+ config
3550
+ ).includes(selfBase);
3551
+ return selfReadableInRecall ? config.defaultNamespace : void 0;
3552
+ }
3553
+ resolveLcmReadSessionKey(explicitNamespace, resolvedNamespace, sessionKey, authenticatedPrincipal, purpose = "read") {
3554
+ const effectiveNamespace = this.resolveLcmReadNamespace(
3555
+ explicitNamespace,
3556
+ resolvedNamespace,
3557
+ sessionKey,
3558
+ authenticatedPrincipal,
3559
+ purpose
3560
+ );
3561
+ return lcmSessionKeyForNamespace(
3562
+ effectiveNamespace,
3563
+ sessionKey,
3564
+ this.orchestrator.config.defaultNamespace
3565
+ ) ?? sessionKey;
3566
+ }
3567
+ /**
3568
+ * Resolve the ORDERED, read-authorized set of LCM `session_id`s a same-session
3569
+ * READER (`lcmSearch`, raw-excerpt disclosure) must query so it matches every
3570
+ * key `observe` archived under across the coding scope (#1505 thread "Include
3571
+ * coding fallback namespaces in LCM reads").
3572
+ *
3573
+ * Mirrors the orchestrator recall path exactly (rule 39): `observe` archives
3574
+ * each turn under `${effectiveNamespace}:${sessionKey}` for whichever namespace
3575
+ * was effective at write time, and normal QMD/file recall searches the primary
3576
+ * coding-overlay namespace AND `codingOverlay.readFallbacks` (project → root).
3577
+ * A single overlay key therefore MISSES rows a branch-scoped session archived at
3578
+ * project/root scope. This returns the primary overlay LCM key first, then one
3579
+ * per read fallback, deduped + ordered so the caller can short-circuit on the
3580
+ * first hit.
3581
+ *
3582
+ * READ-AUTHORIZATION (preserved from the round-3..5 `resolveLcmReadNamespace`
3583
+ * "read" gate; rule 42 / 48): the overlay + fallbacks are `<principal>-project-*`
3584
+ * sub-namespaces authorized transitively by the principal SELF base. They are
3585
+ * included ONLY when the self base is in the readable recall set
3586
+ * (`recallNamespacesForPrincipal`). When the self base is NOT readable (write-
3587
+ * only / self-omitted principal), or when an explicit namespace was supplied,
3588
+ * or no overlay applies, this collapses to the single key
3589
+ * {@link resolveLcmReadSessionKey} returns — byte-for-byte the prior behavior
3590
+ * (single-store / no-overlay flows stay the raw `sessionKey`). No
3591
+ * `<principal>-project-*` key is ever searched for an unauthorized reader (no
3592
+ * cross-tenant read leak).
3593
+ */
3594
+ resolveLcmReadSessionIds(explicitNamespace, resolvedNamespace, sessionKey, authenticatedPrincipal) {
3595
+ const primary = this.resolveLcmReadSessionKey(
3596
+ explicitNamespace,
3597
+ resolvedNamespace,
3598
+ sessionKey,
3599
+ authenticatedPrincipal,
3600
+ "read"
3601
+ );
3602
+ const hasExplicitNamespace = typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
3603
+ if (hasExplicitNamespace) return [primary];
3604
+ const principal = this.resolveRequestPrincipal(
3605
+ sessionKey,
3606
+ authenticatedPrincipal
3607
+ );
3608
+ const base = defaultNamespaceForPrincipal(
3609
+ principal,
3610
+ this.orchestrator.config
3611
+ );
3612
+ const overlaid = this.orchestrator.applyCodingNamespaceOverlay(
3613
+ sessionKey,
3614
+ base
3615
+ );
3616
+ if (overlaid === base) return [primary];
3617
+ const selfReadableInRecall = recallNamespacesForPrincipal(
3618
+ principal,
3619
+ this.orchestrator.config
3620
+ ).includes(base);
3621
+ if (!selfReadableInRecall) return [primary];
3622
+ const overlay = resolveCodingNamespaceOverlay(
3623
+ this.orchestrator.getCodingContextForSession(sessionKey),
3624
+ this.orchestrator.config.codingMode,
3625
+ this.orchestrator.config.defaultNamespace
3626
+ );
3627
+ const fallbackNamespaces = (overlay?.readFallbacks ?? []).map(
3628
+ (fallback) => combineNamespaces(base, fallback)
3629
+ );
3630
+ const out = [primary];
3631
+ const seen = /* @__PURE__ */ new Set([primary]);
3632
+ for (const ns of fallbackNamespaces) {
3633
+ const key = lcmSessionKeyForNamespace(
3634
+ ns,
3635
+ sessionKey,
3636
+ this.orchestrator.config.defaultNamespace
3637
+ ) ?? sessionKey;
3638
+ if (!seen.has(key)) {
3639
+ seen.add(key);
3640
+ out.push(key);
3641
+ }
3642
+ }
3643
+ return out;
3644
+ }
3119
3645
  async lcmCompactionFlush(request) {
3120
3646
  if (!request.sessionKey || typeof request.sessionKey !== "string" || request.sessionKey.trim().length === 0) {
3121
3647
  throw new EngramAccessInputError("sessionKey is required and must be a non-empty string");
3122
3648
  }
3123
- const namespace = this.resolveWritableNamespace(
3124
- request.namespace,
3125
- request.sessionKey,
3126
- request.authenticatedPrincipal
3127
- );
3649
+ const scope = await this.resolveMemoryScopePlan(request);
3650
+ const namespace = scope.explicitNamespace ? scope.writeNamespace : scope.codingOverlayApplied ? this.orchestrator.config.defaultNamespace : scope.writeNamespace;
3128
3651
  if (!this.orchestrator.lcmEngine || !this.orchestrator.lcmEngine.enabled) {
3129
3652
  return {
3130
3653
  enabled: false,
@@ -3134,7 +3657,11 @@ var EngramAccessService = class {
3134
3657
  reason: "LCM is disabled"
3135
3658
  };
3136
3659
  }
3137
- const lcmSessionKey = namespace !== this.orchestrator.config.defaultNamespace ? `${namespace}:${request.sessionKey}` : request.sessionKey;
3660
+ const lcmSessionKey = lcmSessionKeyForNamespace(
3661
+ scope.writeNamespace,
3662
+ request.sessionKey,
3663
+ this.orchestrator.config.defaultNamespace
3664
+ ) ?? request.sessionKey;
3138
3665
  await this.orchestrator.lcmEngine.waitForSessionObserveIdle(lcmSessionKey);
3139
3666
  await this.orchestrator.lcmEngine.preCompactionFlush(lcmSessionKey);
3140
3667
  return {
@@ -3154,11 +3681,8 @@ var EngramAccessService = class {
3154
3681
  if (!Number.isInteger(request.tokensAfter) || request.tokensAfter < 0) {
3155
3682
  throw new EngramAccessInputError("tokensAfter must be a non-negative integer");
3156
3683
  }
3157
- const namespace = this.resolveWritableNamespace(
3158
- request.namespace,
3159
- request.sessionKey,
3160
- request.authenticatedPrincipal
3161
- );
3684
+ const scope = await this.resolveMemoryScopePlan(request);
3685
+ const namespace = scope.explicitNamespace ? scope.writeNamespace : scope.codingOverlayApplied ? this.orchestrator.config.defaultNamespace : scope.writeNamespace;
3162
3686
  if (!this.orchestrator.lcmEngine || !this.orchestrator.lcmEngine.enabled) {
3163
3687
  return {
3164
3688
  enabled: false,
@@ -3168,7 +3692,11 @@ var EngramAccessService = class {
3168
3692
  reason: "LCM is disabled"
3169
3693
  };
3170
3694
  }
3171
- const lcmSessionKey = namespace !== this.orchestrator.config.defaultNamespace ? `${namespace}:${request.sessionKey}` : request.sessionKey;
3695
+ const lcmSessionKey = lcmSessionKeyForNamespace(
3696
+ scope.writeNamespace,
3697
+ request.sessionKey,
3698
+ this.orchestrator.config.defaultNamespace
3699
+ ) ?? request.sessionKey;
3172
3700
  await this.orchestrator.lcmEngine.waitForSessionObserveIdle(lcmSessionKey);
3173
3701
  await this.orchestrator.lcmEngine.recordCompaction(
3174
3702
  lcmSessionKey,
@@ -4649,4 +5177,4 @@ export {
4649
5177
  shapeMemorySummary,
4650
5178
  EngramAccessService
4651
5179
  };
4652
- //# sourceMappingURL=chunk-5ETA6OAS.js.map
5180
+ //# sourceMappingURL=chunk-SLYD3AH4.js.map