@remnic/core 9.3.655 → 9.3.657

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 (249) hide show
  1. package/dist/access-cli.js +22 -22
  2. package/dist/access-http.d.ts +4 -4
  3. package/dist/access-http.js +10 -10
  4. package/dist/access-mcp.d.ts +4 -4
  5. package/dist/access-mcp.js +9 -9
  6. package/dist/access-schema.d.ts +10 -10
  7. package/dist/{access-service-BEJvriUt.d.ts → access-service-D_nbpexW.d.ts} +33 -2
  8. package/dist/access-service.d.ts +4 -4
  9. package/dist/access-service.js +8 -8
  10. package/dist/action-confidence.d.ts +1 -1
  11. package/dist/active-memory-bridge.d.ts +1 -1
  12. package/dist/active-recall.d.ts +1 -1
  13. package/dist/active-recall.js +1 -1
  14. package/dist/behavior-learner.d.ts +1 -1
  15. package/dist/behavior-signals.d.ts +1 -1
  16. package/dist/bootstrap.d.ts +3 -3
  17. package/dist/briefing.d.ts +1 -1
  18. package/dist/briefing.js +3 -3
  19. package/dist/buffer-surprise-report.d.ts +1 -1
  20. package/dist/buffer.d.ts +1 -1
  21. package/dist/calibration.d.ts +1 -1
  22. package/dist/causal-behavior.d.ts +1 -1
  23. package/dist/causal-consolidation.d.ts +1 -1
  24. package/dist/causal-consolidation.js +4 -4
  25. package/dist/{chunk-PVE7KSQP.js → chunk-2BD7DG37.js} +2 -2
  26. package/dist/{chunk-54LOUIBE.js → chunk-2MXEVL75.js} +2 -2
  27. package/dist/{chunk-55ZMNKMQ.js → chunk-4UL7VPTD.js} +276 -57
  28. package/dist/chunk-4UL7VPTD.js.map +1 -0
  29. package/dist/{chunk-COVZLGMR.js → chunk-54XF2FY7.js} +17 -17
  30. package/dist/{chunk-UYNFWZWG.js → chunk-AGJKWOKV.js} +2 -2
  31. package/dist/{chunk-TDZSSJV4.js → chunk-AZBV4RRY.js} +1 -1
  32. package/dist/chunk-AZBV4RRY.js.map +1 -0
  33. package/dist/{chunk-KOI765XP.js → chunk-CTAV55JM.js} +241 -1
  34. package/dist/chunk-CTAV55JM.js.map +1 -0
  35. package/dist/{chunk-A3Y37UWI.js → chunk-DIBWFCLA.js} +3 -3
  36. package/dist/{chunk-QDVQ4AN2.js → chunk-DR67OK4E.js} +5 -5
  37. package/dist/{chunk-XBIACVCO.js → chunk-EC2AYKRX.js} +2 -2
  38. package/dist/{chunk-IQ53ZSXV.js → chunk-GCYFUTUC.js} +2 -2
  39. package/dist/{chunk-YYN3LIYA.js → chunk-GSHW5VVD.js} +5 -5
  40. package/dist/chunk-GYSYLGNE.js +650 -0
  41. package/dist/chunk-GYSYLGNE.js.map +1 -0
  42. package/dist/{chunk-NRBGRZW4.js → chunk-IOZ5WBWD.js} +2 -2
  43. package/dist/{chunk-NCSJKK23.js → chunk-JSVFEHLL.js} +7 -5
  44. package/dist/chunk-JSVFEHLL.js.map +1 -0
  45. package/dist/{chunk-7LWRCOP7.js → chunk-LZTFCAKE.js} +2 -2
  46. package/dist/{chunk-TEO46GMM.js → chunk-NXCK7DO7.js} +2 -2
  47. package/dist/{chunk-XOFXKASO.js → chunk-PEPHBH2W.js} +2 -2
  48. package/dist/{chunk-WDTUYOLS.js → chunk-QZRKNA5F.js} +2 -2
  49. package/dist/{chunk-PS3SYNHP.js → chunk-R5DB26G6.js} +2 -2
  50. package/dist/{chunk-5QD3QD76.js → chunk-RDW5G6DO.js} +659 -123
  51. package/dist/chunk-RDW5G6DO.js.map +1 -0
  52. package/dist/{chunk-BGKXTVNG.js → chunk-SWDHVH2P.js} +2 -2
  53. package/dist/{chunk-67G4T7KI.js → chunk-SXYCVRLK.js} +3 -3
  54. package/dist/{chunk-UCEABZZN.js → chunk-TFFZUFEP.js} +7 -5
  55. package/dist/chunk-TFFZUFEP.js.map +1 -0
  56. package/dist/{chunk-UCEDY5M7.js → chunk-TIJYQXDI.js} +2 -2
  57. package/dist/{chunk-2RCGZ67B.js → chunk-VAEAGTEQ.js} +3 -3
  58. package/dist/{chunk-XRKQOQLY.js → chunk-WIKMCJUR.js} +2 -2
  59. package/dist/{chunk-KZZ4YAEC.js → chunk-WWMHAMAY.js} +2 -2
  60. package/dist/{chunk-OKW6F5S5.js → chunk-YEZHZCUO.js} +4 -4
  61. package/dist/{chunk-5FOCXX5E.js → chunk-YVVQUAOO.js} +3 -3
  62. package/dist/{chunk-5FOCXX5E.js.map → chunk-YVVQUAOO.js.map} +1 -1
  63. package/dist/{chunk-3XGWCZ63.js → chunk-YXLT4EMM.js} +2 -2
  64. package/dist/{chunk-PTMJ2FH2.js → chunk-Z6UDTNY6.js} +2 -2
  65. package/dist/{cli-BGahB_d3.d.ts → cli-aYxSuPvP.d.ts} +3 -3
  66. package/dist/cli.d.ts +5 -5
  67. package/dist/cli.js +22 -22
  68. package/dist/compounding/engine.d.ts +1 -1
  69. package/dist/compounding/engine.js +3 -3
  70. package/dist/compounding/preference-consolidator.d.ts +1 -1
  71. package/dist/compression-optimizer.d.ts +1 -1
  72. package/dist/config.d.ts +1 -1
  73. package/dist/config.js +1 -1
  74. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  75. package/dist/connectors/codex-materialize-runner.js +3 -3
  76. package/dist/connectors/codex-materialize.d.ts +1 -1
  77. package/dist/connectors/index.d.ts +1 -1
  78. package/dist/connectors/index.js +3 -3
  79. package/dist/consolidation-provenance-check.d.ts +1 -1
  80. package/dist/consolidation-undo.d.ts +1 -1
  81. package/dist/contradiction/index.d.ts +1 -1
  82. package/dist/conversation-index/backend.d.ts +1 -1
  83. package/dist/conversation-index/chunker.d.ts +1 -1
  84. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  85. package/dist/conversation-index/indexer.d.ts +1 -1
  86. package/dist/conversation-index/search.d.ts +1 -1
  87. package/dist/day-summary.d.ts +1 -1
  88. package/dist/delinearize.d.ts +1 -1
  89. package/dist/direct-answer-wiring.d.ts +1 -1
  90. package/dist/direct-answer.d.ts +1 -1
  91. package/dist/embedding-fallback.d.ts +1 -1
  92. package/dist/enrichment/index.d.ts +1 -1
  93. package/dist/entity-retrieval.d.ts +1 -1
  94. package/dist/entity-retrieval.js +3 -3
  95. package/dist/entity-schema.d.ts +1 -1
  96. package/dist/explicit-capture.d.ts +3 -3
  97. package/dist/explicit-cue-recall.js +2 -2
  98. package/dist/extraction-judge-telemetry.d.ts +1 -1
  99. package/dist/extraction-judge-training.d.ts +1 -1
  100. package/dist/extraction-judge.d.ts +1 -1
  101. package/dist/extraction.d.ts +1 -1
  102. package/dist/fallback-llm.d.ts +1 -1
  103. package/dist/focused-list-recall.js +2 -2
  104. package/dist/identity-continuity.d.ts +1 -1
  105. package/dist/importance.d.ts +1 -1
  106. package/dist/index.d.ts +121 -121
  107. package/dist/index.js +32 -32
  108. package/dist/intent.d.ts +1 -1
  109. package/dist/lcm/engine.d.ts +1 -1
  110. package/dist/lcm/index.d.ts +1 -1
  111. package/dist/lcm/tools.d.ts +1 -1
  112. package/dist/lcm-fallback-read.js +1 -1
  113. package/dist/lifecycle.d.ts +1 -1
  114. package/dist/live-connectors-runner.d.ts +1 -1
  115. package/dist/local-llm.d.ts +1 -1
  116. package/dist/maintenance/memory-governance.d.ts +1 -1
  117. package/dist/maintenance/memory-governance.js +3 -3
  118. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  119. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  120. package/dist/mcp-memory-inspector-app.d.ts +4 -4
  121. package/dist/memory-action-policy.d.ts +1 -1
  122. package/dist/memory-cache.d.ts +1 -1
  123. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  124. package/dist/memory-projection-store.d.ts +1 -1
  125. package/dist/memory-provenance.d.ts +1 -1
  126. package/dist/memory-worth-outcomes.d.ts +1 -1
  127. package/dist/models-json.d.ts +1 -1
  128. package/dist/namespaces/migrate.d.ts +1 -1
  129. package/dist/namespaces/migrate.js +4 -4
  130. package/dist/namespaces/principal.d.ts +1 -1
  131. package/dist/namespaces/search.d.ts +1 -1
  132. package/dist/namespaces/storage.d.ts +1 -1
  133. package/dist/namespaces/storage.js +3 -3
  134. package/dist/native-knowledge.d.ts +1 -1
  135. package/dist/operator-toolkit.d.ts +1 -1
  136. package/dist/operator-toolkit.js +7 -7
  137. package/dist/{orchestrator-BgzZlWxH.d.ts → orchestrator-D1wcmPNj.d.ts} +8 -2
  138. package/dist/orchestrator.d.ts +3 -3
  139. package/dist/orchestrator.js +18 -18
  140. package/dist/patterns-cli.d.ts +1 -1
  141. package/dist/policy-runtime.d.ts +1 -1
  142. package/dist/qmd-recall-cache.d.ts +1 -1
  143. package/dist/qmd.d.ts +1 -1
  144. package/dist/recall-disclosure-escalation.d.ts +1 -1
  145. package/dist/recall-explain-renderer.d.ts +1 -1
  146. package/dist/recall-explain-renderer.js +3 -3
  147. package/dist/recall-planner-llm.d.ts +1 -1
  148. package/dist/recall-state.d.ts +1 -1
  149. package/dist/recall-tag-filter.d.ts +1 -1
  150. package/dist/recall-xray-cli.d.ts +1 -1
  151. package/dist/recall-xray-cli.js +4 -4
  152. package/dist/recall-xray-renderer.d.ts +1 -1
  153. package/dist/recall-xray-renderer.js +3 -3
  154. package/dist/recall-xray.d.ts +1 -1
  155. package/dist/recall-xray.js +2 -2
  156. package/dist/resolve-auth-token.d.ts +1 -1
  157. package/dist/response-guidance-recall.js +2 -2
  158. package/dist/resume-bundles.js +2 -2
  159. package/dist/retrieval-agents.d.ts +1 -1
  160. package/dist/retrieval-tiers.d.ts +1 -1
  161. package/dist/routing/engine.d.ts +1 -1
  162. package/dist/routing/store.d.ts +1 -1
  163. package/dist/schemas.d.ts +22 -22
  164. package/dist/search/embed-helper.d.ts +1 -1
  165. package/dist/search/factory.d.ts +1 -1
  166. package/dist/search/index.d.ts +1 -1
  167. package/dist/search/lancedb-backend.d.ts +1 -1
  168. package/dist/search/meilisearch-backend.d.ts +1 -1
  169. package/dist/search/noop-backend.d.ts +1 -1
  170. package/dist/search/orama-backend.d.ts +1 -1
  171. package/dist/search/port.d.ts +1 -1
  172. package/dist/search/remote-backend.d.ts +1 -1
  173. package/dist/{semantic-consolidation-Z8d_uMq8.d.ts → semantic-consolidation-MWOdNtSE.d.ts} +1 -1
  174. package/dist/semantic-consolidation.d.ts +2 -2
  175. package/dist/semantic-consolidation.js +4 -4
  176. package/dist/semantic-rule-promotion.js +3 -3
  177. package/dist/semantic-rule-verifier.d.ts +3 -2
  178. package/dist/semantic-rule-verifier.js +5 -3
  179. package/dist/session-observer-bands.d.ts +1 -1
  180. package/dist/session-observer-state.d.ts +1 -1
  181. package/dist/shared-context/manager.d.ts +1 -1
  182. package/dist/signal.d.ts +1 -1
  183. package/dist/storage.d.ts +1 -1
  184. package/dist/storage.js +2 -2
  185. package/dist/summarizer.d.ts +1 -1
  186. package/dist/summary-snapshot.d.ts +1 -1
  187. package/dist/targeted-fact-recall.js +2 -2
  188. package/dist/temporal-supersession.d.ts +1 -1
  189. package/dist/temporal-validity.d.ts +1 -1
  190. package/dist/threading.d.ts +1 -1
  191. package/dist/tier-migration.d.ts +1 -1
  192. package/dist/tier-routing.d.ts +1 -1
  193. package/dist/topics.d.ts +1 -1
  194. package/dist/transcript.d.ts +1 -1
  195. package/dist/transfer/types.d.ts +12 -12
  196. package/dist/{types-2OPlQWJG.d.ts → types-CgcCpUrf.d.ts} +39 -1
  197. package/dist/types.d.ts +1 -1
  198. package/dist/types.js +1 -1
  199. package/dist/utility-runtime.d.ts +1 -1
  200. package/dist/verified-recall.d.ts +2 -1
  201. package/dist/verified-recall.js +5 -3
  202. package/package.json +1 -1
  203. package/src/access-service-observe-lcm-parity.test.ts +86 -1
  204. package/src/access-service-observe-scope.test.ts +283 -1
  205. package/src/access-service-raw-excerpt-read-gate.test.ts +53 -0
  206. package/src/access-service.ts +391 -93
  207. package/src/coding/coding-namespace.ts +0 -3
  208. package/src/config.ts +282 -0
  209. package/src/lcm-fallback-read.ts +2 -6
  210. package/src/namespaces/scope-profiles.test.ts +1074 -0
  211. package/src/namespaces/scope-profiles.ts +456 -0
  212. package/src/orchestrator-flush.test.ts +142 -0
  213. package/src/orchestrator-source-attribution.test.ts +73 -0
  214. package/src/orchestrator.ts +835 -163
  215. package/src/semantic-rule-verifier.ts +13 -6
  216. package/src/types.ts +52 -0
  217. package/src/verified-recall.ts +10 -6
  218. package/dist/chunk-55ZMNKMQ.js.map +0 -1
  219. package/dist/chunk-5QD3QD76.js.map +0 -1
  220. package/dist/chunk-KOI765XP.js.map +0 -1
  221. package/dist/chunk-MMJANTJX.js +0 -339
  222. package/dist/chunk-MMJANTJX.js.map +0 -1
  223. package/dist/chunk-NCSJKK23.js.map +0 -1
  224. package/dist/chunk-TDZSSJV4.js.map +0 -1
  225. package/dist/chunk-UCEABZZN.js.map +0 -1
  226. /package/dist/{chunk-PVE7KSQP.js.map → chunk-2BD7DG37.js.map} +0 -0
  227. /package/dist/{chunk-54LOUIBE.js.map → chunk-2MXEVL75.js.map} +0 -0
  228. /package/dist/{chunk-COVZLGMR.js.map → chunk-54XF2FY7.js.map} +0 -0
  229. /package/dist/{chunk-UYNFWZWG.js.map → chunk-AGJKWOKV.js.map} +0 -0
  230. /package/dist/{chunk-A3Y37UWI.js.map → chunk-DIBWFCLA.js.map} +0 -0
  231. /package/dist/{chunk-QDVQ4AN2.js.map → chunk-DR67OK4E.js.map} +0 -0
  232. /package/dist/{chunk-XBIACVCO.js.map → chunk-EC2AYKRX.js.map} +0 -0
  233. /package/dist/{chunk-IQ53ZSXV.js.map → chunk-GCYFUTUC.js.map} +0 -0
  234. /package/dist/{chunk-YYN3LIYA.js.map → chunk-GSHW5VVD.js.map} +0 -0
  235. /package/dist/{chunk-NRBGRZW4.js.map → chunk-IOZ5WBWD.js.map} +0 -0
  236. /package/dist/{chunk-7LWRCOP7.js.map → chunk-LZTFCAKE.js.map} +0 -0
  237. /package/dist/{chunk-TEO46GMM.js.map → chunk-NXCK7DO7.js.map} +0 -0
  238. /package/dist/{chunk-XOFXKASO.js.map → chunk-PEPHBH2W.js.map} +0 -0
  239. /package/dist/{chunk-WDTUYOLS.js.map → chunk-QZRKNA5F.js.map} +0 -0
  240. /package/dist/{chunk-PS3SYNHP.js.map → chunk-R5DB26G6.js.map} +0 -0
  241. /package/dist/{chunk-BGKXTVNG.js.map → chunk-SWDHVH2P.js.map} +0 -0
  242. /package/dist/{chunk-67G4T7KI.js.map → chunk-SXYCVRLK.js.map} +0 -0
  243. /package/dist/{chunk-UCEDY5M7.js.map → chunk-TIJYQXDI.js.map} +0 -0
  244. /package/dist/{chunk-2RCGZ67B.js.map → chunk-VAEAGTEQ.js.map} +0 -0
  245. /package/dist/{chunk-XRKQOQLY.js.map → chunk-WIKMCJUR.js.map} +0 -0
  246. /package/dist/{chunk-KZZ4YAEC.js.map → chunk-WWMHAMAY.js.map} +0 -0
  247. /package/dist/{chunk-OKW6F5S5.js.map → chunk-YEZHZCUO.js.map} +0 -0
  248. /package/dist/{chunk-3XGWCZ63.js.map → chunk-YXLT4EMM.js.map} +0 -0
  249. /package/dist/{chunk-PTMJ2FH2.js.map → chunk-Z6UDTNY6.js.map} +0 -0
@@ -576,8 +576,5 @@ export function lcmReadSessionIdsForNamespaces(
576
576
  out.push(key);
577
577
  }
578
578
  }
579
- if (out.length === 0) {
580
- out.push(sessionKey);
581
- }
582
579
  return out;
583
580
  }
package/src/config.ts CHANGED
@@ -19,6 +19,10 @@ import type {
19
19
  RecallPipelineConfig,
20
20
  RecallSectionConfig,
21
21
  ReasoningEffort,
22
+ ScopeProfileConfig,
23
+ ScopeProfileLayerId,
24
+ ScopeProfilePromotionTarget,
25
+ ScopeTeamConfig,
22
26
  SemanticChunkingConfigShape,
23
27
  SessionObserverBandConfig,
24
28
  SlotBehaviorConfig,
@@ -59,6 +63,263 @@ const LEGACY_ACTIVE_RECALL_CUSTOM_FIELD = [
59
63
  "Prompt",
60
64
  "Override",
61
65
  ].join("") as "activeRecallPromptOverride";
66
+ const SCOPE_PROFILE_LAYER_IDS = [
67
+ "userProject",
68
+ "teamProject",
69
+ "userGlobal",
70
+ "serverShared",
71
+ ] as const satisfies readonly ScopeProfileLayerId[];
72
+ const SCOPE_PROFILE_PROMOTION_TARGETS = [
73
+ ...SCOPE_PROFILE_LAYER_IDS,
74
+ ] as const satisfies readonly ScopeProfilePromotionTarget[];
75
+ const SCOPE_PROFILE_AUTO_PROMOTE_CATEGORIES = [
76
+ "fact",
77
+ "correction",
78
+ "decision",
79
+ "preference",
80
+ "rule",
81
+ "procedure",
82
+ ] as const;
83
+ const CONFIDENCE_TIERS = [
84
+ "explicit",
85
+ "implied",
86
+ "inferred",
87
+ "speculative",
88
+ ] as const;
89
+
90
+ function isRecord(value: unknown): value is Record<string, unknown> {
91
+ return typeof value === "object" && value !== null && !Array.isArray(value);
92
+ }
93
+
94
+ function parseStringList(value: unknown, keyName: string): string[] {
95
+ if (value === undefined || value === null) return [];
96
+ if (!Array.isArray(value)) {
97
+ throw new Error(keyName + " must be an array");
98
+ }
99
+ const out: string[] = [];
100
+ for (const entry of value) {
101
+ if (typeof entry !== "string" || entry.length === 0) {
102
+ throw new Error(keyName + " must contain only non-empty strings");
103
+ }
104
+ out.push(entry);
105
+ }
106
+ return out;
107
+ }
108
+
109
+ function parseScopeProfileLayerList(
110
+ value: unknown,
111
+ keyName: string,
112
+ fallback: ScopeProfileLayerId[],
113
+ ): ScopeProfileLayerId[] {
114
+ if (value === undefined || value === null) return [...fallback];
115
+ if (!Array.isArray(value)) {
116
+ throw new Error(`${keyName} must be an array`);
117
+ }
118
+ const out: ScopeProfileLayerId[] = [];
119
+ for (const entry of value) {
120
+ if (!SCOPE_PROFILE_LAYER_IDS.includes(entry as ScopeProfileLayerId)) {
121
+ throw new Error(`${keyName} contains unsupported layer: ${String(entry)}`);
122
+ }
123
+ if (!out.includes(entry as ScopeProfileLayerId)) {
124
+ out.push(entry as ScopeProfileLayerId);
125
+ }
126
+ }
127
+ return out;
128
+ }
129
+
130
+ function parseScopeProfilePromotionTargets(
131
+ value: unknown,
132
+ keyName: string,
133
+ ): ScopeProfilePromotionTarget[] {
134
+ if (value === undefined || value === null) return [];
135
+ if (!Array.isArray(value)) {
136
+ throw new Error(`${keyName} must be an array`);
137
+ }
138
+ const out: ScopeProfilePromotionTarget[] = [];
139
+ for (const entry of value) {
140
+ if (!SCOPE_PROFILE_PROMOTION_TARGETS.includes(entry as ScopeProfilePromotionTarget)) {
141
+ throw new Error(`${keyName} contains unsupported target: ${String(entry)}`);
142
+ }
143
+ if (!out.includes(entry as ScopeProfilePromotionTarget)) {
144
+ out.push(entry as ScopeProfilePromotionTarget);
145
+ }
146
+ }
147
+ return out;
148
+ }
149
+
150
+ function parseScopeProfiles(value: unknown): Record<string, ScopeProfileConfig> {
151
+ if (value === undefined || value === null) return {};
152
+ if (!isRecord(value)) {
153
+ throw new Error("scopeProfiles must be an object");
154
+ }
155
+ const profiles: Record<string, ScopeProfileConfig> = {};
156
+ for (const [profileId, rawProfile] of Object.entries(value)) {
157
+ if (profileId.trim().length === 0) {
158
+ throw new Error("scopeProfiles keys must not be empty");
159
+ }
160
+ if (!isRecord(rawProfile)) {
161
+ throw new Error(`scopeProfiles.${profileId} must be an object`);
162
+ }
163
+ const readOrder = parseScopeProfileLayerList(
164
+ rawProfile.readOrder,
165
+ `scopeProfiles.${profileId}.readOrder`,
166
+ ["userProject", "userGlobal", "serverShared"],
167
+ );
168
+ const writeDefault =
169
+ rawProfile.writeDefault === undefined || rawProfile.writeDefault === null
170
+ ? "userProject"
171
+ : rawProfile.writeDefault;
172
+ if (!SCOPE_PROFILE_LAYER_IDS.includes(writeDefault as ScopeProfileLayerId)) {
173
+ throw new Error(`scopeProfiles.${profileId}.writeDefault contains unsupported layer: ${String(writeDefault)}`);
174
+ }
175
+ if (
176
+ rawProfile.teamProject !== undefined &&
177
+ rawProfile.teamProject !== null &&
178
+ !isRecord(rawProfile.teamProject)
179
+ ) {
180
+ throw new Error("scopeProfiles." + profileId + ".teamProject must be an object");
181
+ }
182
+ const teamProject = (() => {
183
+ if (!isRecord(rawProfile.teamProject)) return undefined;
184
+ const out: NonNullable<ScopeProfileConfig["teamProject"]> = {};
185
+ if (rawProfile.teamProject.namespaceTemplate !== undefined) {
186
+ if (
187
+ typeof rawProfile.teamProject.namespaceTemplate !== "string" ||
188
+ rawProfile.teamProject.namespaceTemplate.length === 0
189
+ ) {
190
+ throw new Error(
191
+ `scopeProfiles.${profileId}.teamProject.namespaceTemplate must be a non-empty string`,
192
+ );
193
+ }
194
+ out.namespaceTemplate = rawProfile.teamProject.namespaceTemplate;
195
+ }
196
+ if (rawProfile.teamProject.teamId !== undefined) {
197
+ if (
198
+ typeof rawProfile.teamProject.teamId !== "string" ||
199
+ rawProfile.teamProject.teamId.length === 0
200
+ ) {
201
+ throw new Error(
202
+ `scopeProfiles.${profileId}.teamProject.teamId must be a non-empty string`,
203
+ );
204
+ }
205
+ out.teamId = rawProfile.teamProject.teamId;
206
+ }
207
+ return out;
208
+ })();
209
+ if (
210
+ rawProfile.autoPromote !== undefined &&
211
+ rawProfile.autoPromote !== null &&
212
+ !isRecord(rawProfile.autoPromote)
213
+ ) {
214
+ throw new Error(`scopeProfiles.${profileId}.autoPromote must be an object`);
215
+ }
216
+ const rawAutoPromote = isRecord(rawProfile.autoPromote) ? rawProfile.autoPromote : {};
217
+ const hasAutoPromoteEnabled = Object.prototype.hasOwnProperty.call(
218
+ rawAutoPromote,
219
+ "enabled",
220
+ );
221
+ const autoPromoteEnabled = coerceBool(rawAutoPromote.enabled);
222
+ if (hasAutoPromoteEnabled && autoPromoteEnabled === undefined) {
223
+ throw new Error(
224
+ `scopeProfiles.${profileId}.autoPromote.enabled must be a boolean or boolean-like string`,
225
+ );
226
+ }
227
+ const minConfidenceTier = (() => {
228
+ const rawTier = rawAutoPromote.minConfidenceTier;
229
+ if (rawTier === undefined || rawTier === null) return "explicit";
230
+ if (typeof rawTier !== "string" || !CONFIDENCE_TIERS.includes(rawTier as any)) {
231
+ throw new Error(
232
+ `scopeProfiles.${profileId}.autoPromote.minConfidenceTier must be one of: ${CONFIDENCE_TIERS.join(", ")}`,
233
+ );
234
+ }
235
+ return rawTier as typeof CONFIDENCE_TIERS[number];
236
+ })();
237
+ const autoPromoteCategories: Array<typeof SCOPE_PROFILE_AUTO_PROMOTE_CATEGORIES[number]> = (() => {
238
+ if (rawAutoPromote.categories === undefined || rawAutoPromote.categories === null) {
239
+ return ["fact", "correction", "decision", "preference"];
240
+ }
241
+ if (!Array.isArray(rawAutoPromote.categories)) {
242
+ throw new Error(
243
+ `scopeProfiles.${profileId}.autoPromote.categories must be an array`,
244
+ );
245
+ }
246
+ const categories: Array<typeof SCOPE_PROFILE_AUTO_PROMOTE_CATEGORIES[number]> = [];
247
+ for (const entry of rawAutoPromote.categories) {
248
+ if (typeof entry !== "string" || !SCOPE_PROFILE_AUTO_PROMOTE_CATEGORIES.includes(entry as any)) {
249
+ throw new Error(
250
+ "scopeProfiles." + profileId + ".autoPromote.categories must contain only: " +
251
+ SCOPE_PROFILE_AUTO_PROMOTE_CATEGORIES.join(", "),
252
+ );
253
+ }
254
+ categories.push(entry as typeof SCOPE_PROFILE_AUTO_PROMOTE_CATEGORIES[number]);
255
+ }
256
+ return categories;
257
+ })();
258
+ profiles[profileId] = {
259
+ readOrder,
260
+ writeDefault: writeDefault as ScopeProfileLayerId,
261
+ promotionTargets: parseScopeProfilePromotionTargets(
262
+ rawProfile.promotionTargets,
263
+ `scopeProfiles.${profileId}.promotionTargets`,
264
+ ),
265
+ ...(teamProject && Object.keys(teamProject).length > 0 ? { teamProject } : {}),
266
+ autoPromote: {
267
+ enabled: autoPromoteEnabled === true,
268
+ targets: parseScopeProfilePromotionTargets(
269
+ rawAutoPromote.targets,
270
+ `scopeProfiles.${profileId}.autoPromote.targets`,
271
+ ),
272
+ categories: autoPromoteCategories,
273
+ minConfidenceTier,
274
+ },
275
+ };
276
+ }
277
+ return profiles;
278
+ }
279
+
280
+ function parseScopeTeams(value: unknown): Record<string, ScopeTeamConfig> {
281
+ if (value === undefined || value === null) return {};
282
+ if (!isRecord(value)) {
283
+ throw new Error("teams must be an object");
284
+ }
285
+ const teams: Record<string, ScopeTeamConfig> = {};
286
+ for (const [teamId, rawTeam] of Object.entries(value)) {
287
+ if (teamId.trim().length === 0) {
288
+ throw new Error("teams keys must not be empty");
289
+ }
290
+ if (!isRecord(rawTeam)) {
291
+ throw new Error(`teams.${teamId} must be an object`);
292
+ }
293
+ const projectNamespaceTemplate = rawTeam.projectNamespaceTemplate;
294
+ if (projectNamespaceTemplate !== undefined) {
295
+ if (typeof projectNamespaceTemplate !== "string" || projectNamespaceTemplate.length === 0) {
296
+ throw new Error(`teams.${teamId}.projectNamespaceTemplate must be a non-empty string`);
297
+ }
298
+ }
299
+ teams[teamId] = {
300
+ principals: parseStringList(rawTeam.principals, `teams.${teamId}.principals`),
301
+ ...(projectNamespaceTemplate !== undefined ? { projectNamespaceTemplate } : {}),
302
+ read: parseStringList(rawTeam.read, `teams.${teamId}.read`),
303
+ write: parseStringList(rawTeam.write, `teams.${teamId}.write`),
304
+ promote: parseStringList(rawTeam.promote, `teams.${teamId}.promote`),
305
+ };
306
+ }
307
+ return teams;
308
+ }
309
+
310
+ function validateScopeProfileTeamReferences(
311
+ profiles: Record<string, ScopeProfileConfig>,
312
+ teams: Record<string, ScopeTeamConfig>,
313
+ ): void {
314
+ for (const [profileId, profile] of Object.entries(profiles)) {
315
+ const teamId = profile.teamProject?.teamId;
316
+ if (teamId && !Object.prototype.hasOwnProperty.call(teams, teamId)) {
317
+ throw new Error(
318
+ `scopeProfiles.${profileId}.teamProject.teamId references unknown team: ${teamId}`,
319
+ );
320
+ }
321
+ }
322
+ }
62
323
 
63
324
  function parseBoundedIntegerMs(
64
325
  value: unknown,
@@ -1281,6 +1542,24 @@ export function parseConfig(raw: unknown): PluginConfig {
1281
1542
  typeof cfg.memoryDir === "string" && cfg.memoryDir.length > 0
1282
1543
  ? expandTildePath(cfg.memoryDir)
1283
1544
  : DEFAULT_MEMORY_DIR;
1545
+ const scopeProfiles = parseScopeProfiles(cfg.scopeProfiles);
1546
+ const teams = parseScopeTeams(cfg.teams);
1547
+ validateScopeProfileTeamReferences(scopeProfiles, teams);
1548
+ const defaultScopeProfile = (() => {
1549
+ if (cfg.defaultScopeProfile === undefined || cfg.defaultScopeProfile === null) {
1550
+ return undefined;
1551
+ }
1552
+ if (typeof cfg.defaultScopeProfile !== "string" || cfg.defaultScopeProfile.trim().length === 0) {
1553
+ throw new Error("defaultScopeProfile must be a non-empty string");
1554
+ }
1555
+ return cfg.defaultScopeProfile.trim();
1556
+ })();
1557
+ if (
1558
+ defaultScopeProfile !== undefined &&
1559
+ scopeProfiles[defaultScopeProfile] === undefined
1560
+ ) {
1561
+ throw new Error(`defaultScopeProfile references unknown scope profile: ${defaultScopeProfile}`);
1562
+ }
1284
1563
  const rawIdentityInjectionMode = cfg.identityInjectionMode as string | undefined;
1285
1564
  const identityInjectionMode: IdentityInjectionMode =
1286
1565
  rawIdentityInjectionMode
@@ -2777,6 +3056,9 @@ export function parseConfig(raw: unknown): PluginConfig {
2777
3056
  })).filter((p) => p.name.length > 0)
2778
3057
  : [],
2779
3058
  defaultRecallNamespaces: Array.isArray(cfg.defaultRecallNamespaces) ? ["self", "shared"].filter((x) => (cfg.defaultRecallNamespaces as any[]).includes(x)) as any : ["self", "shared"],
3059
+ scopeProfiles,
3060
+ defaultScopeProfile,
3061
+ teams,
2780
3062
  cronRecallMode:
2781
3063
  cfg.cronRecallMode === "none"
2782
3064
  ? "none"
@@ -57,10 +57,7 @@ const UNDEFINED_SESSION_SENTINEL = " <lcm-sessionless>";
57
57
  export function resolveLcmReadSessionIds(
58
58
  target: LcmReadSessionTarget,
59
59
  ): Array<string | undefined> {
60
- const source =
61
- target.sessionIds && target.sessionIds.length > 0
62
- ? target.sessionIds
63
- : [target.sessionId];
60
+ const source = target.sessionIds !== undefined ? target.sessionIds : [target.sessionId];
64
61
  const seen = new Set<string>();
65
62
  const out: Array<string | undefined> = [];
66
63
  for (const sessionId of source) {
@@ -69,8 +66,7 @@ export function resolveLcmReadSessionIds(
69
66
  seen.add(key);
70
67
  out.push(sessionId);
71
68
  }
72
- // Defensive: an all-empty `sessionIds` still collapses to the single-key path.
73
- return out.length > 0 ? out : [target.sessionId];
69
+ return out;
74
70
  }
75
71
 
76
72
  /**