@remnic/core 9.3.652 → 9.3.654

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 +17 -17
  2. package/dist/access-http.d.ts +4 -4
  3. package/dist/access-http.js +11 -11
  4. package/dist/access-mcp.d.ts +4 -4
  5. package/dist/access-mcp.js +10 -10
  6. package/dist/access-schema.d.ts +15 -12
  7. package/dist/access-schema.js +1 -1
  8. package/dist/{access-service-CdJFd3_b.d.ts → access-service-C8A5hoXJ.d.ts} +11 -2
  9. package/dist/access-service.d.ts +4 -4
  10. package/dist/access-service.js +8 -8
  11. package/dist/action-confidence.d.ts +1 -1
  12. package/dist/active-memory-bridge.d.ts +1 -1
  13. package/dist/active-recall.d.ts +1 -1
  14. package/dist/active-recall.js +1 -1
  15. package/dist/behavior-learner.d.ts +1 -1
  16. package/dist/behavior-signals.d.ts +1 -1
  17. package/dist/bootstrap.d.ts +3 -3
  18. package/dist/briefing.d.ts +1 -1
  19. package/dist/briefing.js +3 -3
  20. package/dist/buffer-surprise-report.d.ts +1 -1
  21. package/dist/buffer.d.ts +1 -1
  22. package/dist/calibration.d.ts +1 -1
  23. package/dist/causal-behavior.d.ts +1 -1
  24. package/dist/causal-consolidation.d.ts +1 -1
  25. package/dist/causal-consolidation.js +4 -4
  26. package/dist/{chunk-KJDKZVF3.js → chunk-2DSTAWNZ.js} +3 -3
  27. package/dist/chunk-3RACUBII.js +212 -0
  28. package/dist/chunk-3RACUBII.js.map +1 -0
  29. package/dist/{chunk-Y7NWBBHV.js → chunk-6CVI6BP6.js} +2 -2
  30. package/dist/{chunk-R3PQUPQ4.js → chunk-6IMKOIZ6.js} +85 -3
  31. package/dist/chunk-6IMKOIZ6.js.map +1 -0
  32. package/dist/{chunk-WTI35CVJ.js → chunk-BJA6DQOC.js} +5 -5
  33. package/dist/{chunk-GI45G4BK.js → chunk-BP2EV6W5.js} +3 -3
  34. package/dist/{chunk-WLGE6KEO.js → chunk-DBM2BD22.js} +3 -3
  35. package/dist/{chunk-IENGGY2C.js → chunk-ENV6RDTD.js} +2 -2
  36. package/dist/{chunk-BEMWL2FZ.js → chunk-FVRBLJP6.js} +2 -2
  37. package/dist/{chunk-H3PHZLMF.js → chunk-GKKAXVAJ.js} +20 -11
  38. package/dist/chunk-GKKAXVAJ.js.map +1 -0
  39. package/dist/{chunk-MGGNV3H2.js → chunk-GPW2E4LN.js} +23 -8
  40. package/dist/chunk-GPW2E4LN.js.map +1 -0
  41. package/dist/{chunk-KWM33SPU.js → chunk-JMQSYGXS.js} +2 -2
  42. package/dist/{chunk-WSFNYPAT.js → chunk-JYN7QNTA.js} +87 -18
  43. package/dist/chunk-JYN7QNTA.js.map +1 -0
  44. package/dist/{chunk-AJE7FJVE.js → chunk-K6X553JB.js} +2 -2
  45. package/dist/{chunk-5V3TAB7D.js → chunk-LJCEWTG3.js} +19 -8
  46. package/dist/{chunk-5V3TAB7D.js.map → chunk-LJCEWTG3.js.map} +1 -1
  47. package/dist/{chunk-YOVKPOMD.js → chunk-NAZWHTYV.js} +13 -6
  48. package/dist/chunk-NAZWHTYV.js.map +1 -0
  49. package/dist/{chunk-XMN6MMTU.js → chunk-NCGWXCSW.js} +2 -2
  50. package/dist/{chunk-C43KEWEV.js → chunk-NE2JBMLN.js} +1 -1
  51. package/dist/chunk-NE2JBMLN.js.map +1 -0
  52. package/dist/{chunk-TCX4WLKK.js → chunk-OL2364SB.js} +2020 -368
  53. package/dist/chunk-OL2364SB.js.map +1 -0
  54. package/dist/{chunk-JF7SFXTG.js → chunk-QKK64Z6M.js} +2 -2
  55. package/dist/{chunk-IVYSVAC6.js → chunk-QW6JZO5P.js} +2 -2
  56. package/dist/{chunk-EHQLDFSH.js → chunk-RGPUQ66K.js} +2 -2
  57. package/dist/{chunk-CFOCZPIQ.js → chunk-T2C6QJG2.js} +2 -2
  58. package/dist/{chunk-4HYSMH7D.js → chunk-UAU5U5ML.js} +3 -2
  59. package/dist/chunk-UAU5U5ML.js.map +1 -0
  60. package/dist/{chunk-V4UDXYGG.js → chunk-XWQ6ERUG.js} +2 -2
  61. package/dist/{chunk-IJHLC5CH.js → chunk-Y2RIIF6H.js} +32 -22
  62. package/dist/{chunk-IJHLC5CH.js.map → chunk-Y2RIIF6H.js.map} +1 -1
  63. package/dist/{chunk-C63WC454.js → chunk-YLZLPVKK.js} +22 -1
  64. package/dist/chunk-YLZLPVKK.js.map +1 -0
  65. package/dist/{chunk-RZOBQ23O.js → chunk-Z5MQI7K2.js} +2 -2
  66. package/dist/{chunk-PRQXUSQV.js → chunk-ZCORQM74.js} +2 -2
  67. package/dist/{cli-DDo7Qgs-.d.ts → cli-uQgvDFNE.d.ts} +3 -3
  68. package/dist/cli.d.ts +5 -5
  69. package/dist/cli.js +23 -23
  70. package/dist/compounding/engine.d.ts +1 -1
  71. package/dist/compounding/engine.js +3 -3
  72. package/dist/compounding/preference-consolidator.d.ts +1 -1
  73. package/dist/compression-optimizer.d.ts +1 -1
  74. package/dist/config.d.ts +1 -1
  75. package/dist/config.js +1 -1
  76. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  77. package/dist/connectors/codex-materialize-runner.js +3 -3
  78. package/dist/connectors/codex-materialize.d.ts +1 -1
  79. package/dist/connectors/index.d.ts +1 -1
  80. package/dist/connectors/index.js +3 -3
  81. package/dist/consolidation-provenance-check.d.ts +1 -1
  82. package/dist/consolidation-undo.d.ts +1 -1
  83. package/dist/contradiction/index.d.ts +19 -1
  84. package/dist/contradiction/index.js +1 -1
  85. package/dist/conversation-index/backend.d.ts +1 -1
  86. package/dist/conversation-index/chunker.d.ts +1 -1
  87. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  88. package/dist/conversation-index/indexer.d.ts +1 -1
  89. package/dist/conversation-index/search.d.ts +1 -1
  90. package/dist/day-summary.d.ts +1 -1
  91. package/dist/delinearize.d.ts +1 -1
  92. package/dist/direct-answer-wiring.d.ts +1 -1
  93. package/dist/direct-answer.d.ts +1 -1
  94. package/dist/embedding-fallback.d.ts +1 -1
  95. package/dist/enrichment/index.d.ts +1 -1
  96. package/dist/entity-retrieval.d.ts +1 -1
  97. package/dist/entity-retrieval.js +3 -3
  98. package/dist/entity-schema.d.ts +1 -1
  99. package/dist/explicit-capture.d.ts +3 -3
  100. package/dist/explicit-capture.js +1 -1
  101. package/dist/extraction-judge-telemetry.d.ts +1 -1
  102. package/dist/extraction-judge-training.d.ts +1 -1
  103. package/dist/extraction-judge.d.ts +1 -1
  104. package/dist/extraction.d.ts +1 -1
  105. package/dist/fallback-llm.d.ts +1 -1
  106. package/dist/identity-continuity.d.ts +1 -1
  107. package/dist/importance.d.ts +1 -1
  108. package/dist/index.d.ts +8 -8
  109. package/dist/index.js +31 -29
  110. package/dist/index.js.map +1 -1
  111. package/dist/intent.d.ts +1 -1
  112. package/dist/lcm/engine.d.ts +1 -1
  113. package/dist/lcm/index.d.ts +1 -1
  114. package/dist/lcm/tools.d.ts +1 -1
  115. package/dist/lifecycle.d.ts +1 -1
  116. package/dist/live-connectors-runner.d.ts +1 -1
  117. package/dist/local-llm.d.ts +1 -1
  118. package/dist/maintenance/memory-governance.d.ts +1 -1
  119. package/dist/maintenance/memory-governance.js +3 -3
  120. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  121. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  122. package/dist/mcp-memory-inspector-app.d.ts +4 -4
  123. package/dist/memory-action-policy.d.ts +1 -1
  124. package/dist/memory-cache.d.ts +1 -1
  125. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  126. package/dist/memory-projection-store.d.ts +1 -1
  127. package/dist/memory-provenance.d.ts +1 -1
  128. package/dist/memory-worth-outcomes.d.ts +1 -1
  129. package/dist/models-json.d.ts +1 -1
  130. package/dist/namespaces/migrate.d.ts +1 -1
  131. package/dist/namespaces/migrate.js +4 -4
  132. package/dist/namespaces/principal.d.ts +1 -1
  133. package/dist/namespaces/search.d.ts +1 -1
  134. package/dist/namespaces/storage.d.ts +52 -3
  135. package/dist/namespaces/storage.js +9 -5
  136. package/dist/native-knowledge.d.ts +1 -1
  137. package/dist/operator-toolkit.d.ts +1 -1
  138. package/dist/operator-toolkit.js +7 -7
  139. package/dist/{orchestrator-8fTZsa0y.d.ts → orchestrator-B4Y4sWQH.d.ts} +503 -3
  140. package/dist/orchestrator.d.ts +3 -3
  141. package/dist/orchestrator.js +13 -13
  142. package/dist/patterns-cli.d.ts +1 -1
  143. package/dist/policy-runtime.d.ts +1 -1
  144. package/dist/qmd-recall-cache.d.ts +1 -1
  145. package/dist/qmd.d.ts +1 -1
  146. package/dist/recall-disclosure-escalation.d.ts +1 -1
  147. package/dist/recall-explain-renderer.d.ts +1 -1
  148. package/dist/recall-explain-renderer.js +3 -3
  149. package/dist/recall-planner-llm.d.ts +1 -1
  150. package/dist/recall-state.d.ts +1 -1
  151. package/dist/recall-tag-filter.d.ts +1 -1
  152. package/dist/recall-xray-cli.d.ts +1 -1
  153. package/dist/recall-xray-cli.js +4 -4
  154. package/dist/recall-xray-renderer.d.ts +1 -1
  155. package/dist/recall-xray-renderer.js +3 -3
  156. package/dist/recall-xray.d.ts +1 -1
  157. package/dist/recall-xray.js +2 -2
  158. package/dist/{resolution-3SAP4SH2.js → resolution-IDTEBJFS.js} +2 -2
  159. package/dist/resolve-auth-token.d.ts +1 -1
  160. package/dist/resume-bundles.js +2 -2
  161. package/dist/retrieval-agents.d.ts +1 -1
  162. package/dist/retrieval-tiers.d.ts +1 -1
  163. package/dist/routing/engine.d.ts +1 -1
  164. package/dist/routing/store.d.ts +1 -1
  165. package/dist/search/embed-helper.d.ts +1 -1
  166. package/dist/search/factory.d.ts +1 -1
  167. package/dist/search/index.d.ts +1 -1
  168. package/dist/search/lancedb-backend.d.ts +1 -1
  169. package/dist/search/meilisearch-backend.d.ts +1 -1
  170. package/dist/search/noop-backend.d.ts +1 -1
  171. package/dist/search/orama-backend.d.ts +1 -1
  172. package/dist/search/port.d.ts +1 -1
  173. package/dist/search/remote-backend.d.ts +1 -1
  174. package/dist/{semantic-consolidation-DKdYzQOg.d.ts → semantic-consolidation-BKd0Pype.d.ts} +1 -1
  175. package/dist/semantic-consolidation.d.ts +2 -2
  176. package/dist/semantic-consolidation.js +4 -4
  177. package/dist/semantic-rule-promotion.js +3 -3
  178. package/dist/semantic-rule-verifier.d.ts +1 -1
  179. package/dist/semantic-rule-verifier.js +3 -3
  180. package/dist/session-observer-bands.d.ts +1 -1
  181. package/dist/session-observer-state.d.ts +1 -1
  182. package/dist/shared-context/manager.d.ts +1 -1
  183. package/dist/signal.d.ts +1 -1
  184. package/dist/storage.d.ts +1 -1
  185. package/dist/storage.js +2 -2
  186. package/dist/summarizer.d.ts +1 -1
  187. package/dist/summary-snapshot.d.ts +1 -1
  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/{types-D8yUmSik.d.ts → types-BgChEr0M.d.ts} +11 -0
  196. package/dist/types.d.ts +1 -1
  197. package/dist/types.js +1 -1
  198. package/dist/utility-runtime.d.ts +1 -1
  199. package/dist/verified-recall.js +3 -3
  200. package/package.json +1 -1
  201. package/src/access-http.ts +7 -0
  202. package/src/access-mcp.test.ts +70 -1
  203. package/src/access-mcp.ts +19 -2
  204. package/src/access-schema.ts +1 -0
  205. package/src/access-service.ts +12 -0
  206. package/src/briefing.test.ts +70 -0
  207. package/src/briefing.ts +30 -20
  208. package/src/cli.ts +104 -0
  209. package/src/config.test.ts +40 -0
  210. package/src/config.ts +29 -0
  211. package/src/contradiction/contradiction.test.ts +284 -0
  212. package/src/contradiction/resolution.ts +151 -4
  213. package/src/explicit-capture.ts +31 -10
  214. package/src/index.ts +10 -0
  215. package/src/namespaces/catalog.test.ts +3356 -0
  216. package/src/namespaces/catalog.ts +2123 -0
  217. package/src/namespaces/storage.ts +210 -30
  218. package/src/orchestrator-flush.test.ts +300 -0
  219. package/src/orchestrator.ts +851 -240
  220. package/src/types.ts +11 -0
  221. package/dist/chunk-4HYSMH7D.js.map +0 -1
  222. package/dist/chunk-C43KEWEV.js.map +0 -1
  223. package/dist/chunk-C63WC454.js.map +0 -1
  224. package/dist/chunk-H3PHZLMF.js.map +0 -1
  225. package/dist/chunk-MGGNV3H2.js.map +0 -1
  226. package/dist/chunk-ORGWWNJG.js +0 -131
  227. package/dist/chunk-ORGWWNJG.js.map +0 -1
  228. package/dist/chunk-R3PQUPQ4.js.map +0 -1
  229. package/dist/chunk-TCX4WLKK.js.map +0 -1
  230. package/dist/chunk-WSFNYPAT.js.map +0 -1
  231. package/dist/chunk-YOVKPOMD.js.map +0 -1
  232. /package/dist/{chunk-KJDKZVF3.js.map → chunk-2DSTAWNZ.js.map} +0 -0
  233. /package/dist/{chunk-Y7NWBBHV.js.map → chunk-6CVI6BP6.js.map} +0 -0
  234. /package/dist/{chunk-WTI35CVJ.js.map → chunk-BJA6DQOC.js.map} +0 -0
  235. /package/dist/{chunk-GI45G4BK.js.map → chunk-BP2EV6W5.js.map} +0 -0
  236. /package/dist/{chunk-WLGE6KEO.js.map → chunk-DBM2BD22.js.map} +0 -0
  237. /package/dist/{chunk-IENGGY2C.js.map → chunk-ENV6RDTD.js.map} +0 -0
  238. /package/dist/{chunk-BEMWL2FZ.js.map → chunk-FVRBLJP6.js.map} +0 -0
  239. /package/dist/{chunk-KWM33SPU.js.map → chunk-JMQSYGXS.js.map} +0 -0
  240. /package/dist/{chunk-AJE7FJVE.js.map → chunk-K6X553JB.js.map} +0 -0
  241. /package/dist/{chunk-XMN6MMTU.js.map → chunk-NCGWXCSW.js.map} +0 -0
  242. /package/dist/{chunk-JF7SFXTG.js.map → chunk-QKK64Z6M.js.map} +0 -0
  243. /package/dist/{chunk-IVYSVAC6.js.map → chunk-QW6JZO5P.js.map} +0 -0
  244. /package/dist/{chunk-EHQLDFSH.js.map → chunk-RGPUQ66K.js.map} +0 -0
  245. /package/dist/{chunk-CFOCZPIQ.js.map → chunk-T2C6QJG2.js.map} +0 -0
  246. /package/dist/{chunk-V4UDXYGG.js.map → chunk-XWQ6ERUG.js.map} +0 -0
  247. /package/dist/{chunk-RZOBQ23O.js.map → chunk-Z5MQI7K2.js.map} +0 -0
  248. /package/dist/{chunk-PRQXUSQV.js.map → chunk-ZCORQM74.js.map} +0 -0
  249. /package/dist/{resolution-3SAP4SH2.js.map → resolution-IDTEBJFS.js.map} +0 -0
package/dist/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import './types-ByK7T3L6.js';
2
2
  import './index-DJ9QWMw-.js';
3
- export { y as AccessTrackingEntry, A as ActiveRecallChatType, d as ActiveRecallModelFallbackPolicy, b as ActiveRecallPromptStyle, a as ActiveRecallQueryMode, c as ActiveRecallThinking, a7 as AgentAccessAuthToken, b2 as AgentAccessHttpConfig, b3 as AgentDefaultsConfig, b4 as AgentPersona, f as AgentPersonaModelConfig, b5 as BehaviorLoopAdjustment, e as BehaviorLoopPolicyState, b6 as BehaviorSignalDirection, B as BehaviorSignalEvent, b7 as BehaviorSignalType, aH as BootstrapOptions, aI as BootstrapResult, V as BriefingActiveThread, b8 as BriefingCalendarSourceError, aO as BriefingConfig, Q as BriefingFocus, O as BriefingFollowup, aP as BriefingOpenCommitment, X as BriefingRecentEntity, W as BriefingResult, N as BriefingSections, aQ as BriefingWindow, b9 as BufferEntryState, k as BufferState, o as BufferSurpriseEvent, Y as BufferTurn, U as CalendarEvent, S as CalendarSource, ba as CaptureMode, $ as Checkpoint, bb as CodexCliReasoningEffort, bc as CodexCompactionFlushMode, aR as CodexCompatConfig, bd as CodexConnectorConfig, aG as CodingContext, aL as CodingModeConfig, be as CompressionGuidelineActivationState, a9 as CompressionGuidelineOptimizerActionSummary, a8 as CompressionGuidelineOptimizerEventCounts, aa as CompressionGuidelineOptimizerRuleUpdate, bf as CompressionGuidelineOptimizerSourceWindow, C as CompressionGuidelineOptimizerState, bg as ConfidenceTier, bh as ConsolidationAction, bi as ConsolidationItem, aJ as ConsolidationObservation, ag as ConsolidationResult, s as ContinuityImprovementLoop, r as ContinuityIncidentCloseInput, p as ContinuityIncidentOpenInput, q as ContinuityIncidentRecord, bj as ContinuityIncidentState, bk as ContinuityLoopCadence, u as ContinuityLoopReviewInput, bl as ContinuityLoopStatus, t as ContinuityLoopUpsertInput, bm as ContradictionScanConfig, as as ConversationThread, bn as CronConversationRecallMode, bo as CronRecallMode, aS as DEFAULT_RECALL_DISCLOSURE, ah as DaySummaryResult, bp as DreamingConfig, bq as DreamingNarrativePromptStyle, br as DreamsDeepSleepConfig, bs as DreamsLightSleepConfig, au as DreamsPhase, bt as DreamsPhaseStatus, bu as DreamsPhasesConfig, bv as DreamsRemConfig, av as DreamsRunResult, at as DreamsStatusResult, bw as EngramTraceEvent, w as EntityActivityEntry, x as EntityFile, ac as EntityMention, v as EntityRelationship, ad as EntitySchemaDefinition, ae as EntitySchemaSectionDefinition, E as EntityStructuredSection, aK as EntityTimelineEntry, aT as ExtractedFact, bx as ExtractedProcedureStep, by as ExtractedQuestion, bz as ExtractedReasoningTrace, bA as ExtractedReasoningTraceStep, bB as ExtractedRelationship, bC as ExtractionPassSource, af as ExtractionResult, F as FileHygieneConfig, G as GatewayConfig, bD as GitHubLiveConnectorConfig, bE as GmailLiveConnectorConfig, bF as GoogleDriveLiveConnectorConfig, bG as HeartbeatConfig, bH as HeartbeatDetectionMode, a0 as HourlySummary, J as IdentityInjectionMode, a5 as ImportanceLevel, I as ImportanceScore, a2 as LifecycleState, L as LiveConnectorsConfig, bI as LlmTraceCallback, bJ as LlmTraceEvent, ak as MemoryActionEligibilityContext, aU as MemoryActionEligibilitySource, m as MemoryActionEvent, bK as MemoryActionOutcome, bL as MemoryActionPolicyDecision, al as MemoryActionPolicyResult, bM as MemoryActionStatus, ab as MemoryActionType, M as MemoryCategory, g as MemoryFile, j as MemoryFrontmatter, ai as MemoryIntent, n as MemoryLifecycleEvent, am as MemoryLifecycleEventType, an as MemoryLifecycleStateSummary, i as MemoryLink, bN as MemoryLinkType, aV as MemoryObservation, bO as MemoryOsPresetName, z as MemoryProjectionCurrentState, aW as MemoryScope, h as MemoryStatus, D as MemorySummary, l as MetaState, bP as ModelApi, bQ as ModelDefinitionConfig, bR as ModelProviderAuthMode, ao as ModelProviderConfig, bS as NamespacePolicy, ap as NativeKnowledgeConfig, bT as NativeKnowledgeFolderRuleConfig, bU as NativeKnowledgeObsidianVaultConfig, bV as NativeKnowledgeOpenClawWorkspaceConfig, bW as NotionLiveConnectorConfig, P as PluginConfig, a4 as PolicyClass, bX as PrincipalFromSessionKeyMode, bY as PrincipalRule, bZ as ProceduralConfig, aj as QmdSearchExplain, Z as QmdSearchResult, b_ as QuestionEntry, aX as RECALL_DISCLOSURE_LEVELS, b$ as ReasoningEffort, K as RecallDisclosure, c0 as RecallPipelineConfig, H as RecallPlanMode, c1 as RecallSectionConfig, R as RecallTierExplain, c2 as RecallTraceEvent, c3 as RelevanceFeedback, aq as RetrievalTier, c4 as SPECULATIVE_TTL_DAYS, c5 as ScoredEntity, a6 as SecretRef, c6 as SemanticChunkingConfigShape, a1 as SessionObserverBandConfig, c7 as SignalLevel, ar as SignalScanResult, c8 as SlotBehaviorConfig, c9 as SlotMismatchMode, T as TopicScore, _ as TranscriptEntry, ca as TriggerMode, a3 as VerificationState, cb as confidenceTier, b1 as isRecallDisclosure } from './types-D8yUmSik.js';
3
+ export { y as AccessTrackingEntry, A as ActiveRecallChatType, d as ActiveRecallModelFallbackPolicy, b as ActiveRecallPromptStyle, a as ActiveRecallQueryMode, c as ActiveRecallThinking, a7 as AgentAccessAuthToken, b2 as AgentAccessHttpConfig, b3 as AgentDefaultsConfig, b4 as AgentPersona, f as AgentPersonaModelConfig, b5 as BehaviorLoopAdjustment, e as BehaviorLoopPolicyState, b6 as BehaviorSignalDirection, B as BehaviorSignalEvent, b7 as BehaviorSignalType, aH as BootstrapOptions, aI as BootstrapResult, V as BriefingActiveThread, b8 as BriefingCalendarSourceError, aO as BriefingConfig, Q as BriefingFocus, O as BriefingFollowup, aP as BriefingOpenCommitment, X as BriefingRecentEntity, W as BriefingResult, N as BriefingSections, aQ as BriefingWindow, b9 as BufferEntryState, k as BufferState, o as BufferSurpriseEvent, Y as BufferTurn, U as CalendarEvent, S as CalendarSource, ba as CaptureMode, $ as Checkpoint, bb as CodexCliReasoningEffort, bc as CodexCompactionFlushMode, aR as CodexCompatConfig, bd as CodexConnectorConfig, aG as CodingContext, aL as CodingModeConfig, be as CompressionGuidelineActivationState, a9 as CompressionGuidelineOptimizerActionSummary, a8 as CompressionGuidelineOptimizerEventCounts, aa as CompressionGuidelineOptimizerRuleUpdate, bf as CompressionGuidelineOptimizerSourceWindow, C as CompressionGuidelineOptimizerState, bg as ConfidenceTier, bh as ConsolidationAction, bi as ConsolidationItem, aJ as ConsolidationObservation, ag as ConsolidationResult, s as ContinuityImprovementLoop, r as ContinuityIncidentCloseInput, p as ContinuityIncidentOpenInput, q as ContinuityIncidentRecord, bj as ContinuityIncidentState, bk as ContinuityLoopCadence, u as ContinuityLoopReviewInput, bl as ContinuityLoopStatus, t as ContinuityLoopUpsertInput, bm as ContradictionScanConfig, as as ConversationThread, bn as CronConversationRecallMode, bo as CronRecallMode, aS as DEFAULT_RECALL_DISCLOSURE, ah as DaySummaryResult, bp as DreamingConfig, bq as DreamingNarrativePromptStyle, br as DreamsDeepSleepConfig, bs as DreamsLightSleepConfig, au as DreamsPhase, bt as DreamsPhaseStatus, bu as DreamsPhasesConfig, bv as DreamsRemConfig, av as DreamsRunResult, at as DreamsStatusResult, bw as EngramTraceEvent, w as EntityActivityEntry, x as EntityFile, ac as EntityMention, v as EntityRelationship, ad as EntitySchemaDefinition, ae as EntitySchemaSectionDefinition, E as EntityStructuredSection, aK as EntityTimelineEntry, aT as ExtractedFact, bx as ExtractedProcedureStep, by as ExtractedQuestion, bz as ExtractedReasoningTrace, bA as ExtractedReasoningTraceStep, bB as ExtractedRelationship, bC as ExtractionPassSource, af as ExtractionResult, F as FileHygieneConfig, G as GatewayConfig, bD as GitHubLiveConnectorConfig, bE as GmailLiveConnectorConfig, bF as GoogleDriveLiveConnectorConfig, bG as HeartbeatConfig, bH as HeartbeatDetectionMode, a0 as HourlySummary, J as IdentityInjectionMode, a5 as ImportanceLevel, I as ImportanceScore, a2 as LifecycleState, L as LiveConnectorsConfig, bI as LlmTraceCallback, bJ as LlmTraceEvent, ak as MemoryActionEligibilityContext, aU as MemoryActionEligibilitySource, m as MemoryActionEvent, bK as MemoryActionOutcome, bL as MemoryActionPolicyDecision, al as MemoryActionPolicyResult, bM as MemoryActionStatus, ab as MemoryActionType, M as MemoryCategory, g as MemoryFile, j as MemoryFrontmatter, ai as MemoryIntent, n as MemoryLifecycleEvent, am as MemoryLifecycleEventType, an as MemoryLifecycleStateSummary, i as MemoryLink, bN as MemoryLinkType, aV as MemoryObservation, bO as MemoryOsPresetName, z as MemoryProjectionCurrentState, aW as MemoryScope, h as MemoryStatus, D as MemorySummary, l as MetaState, bP as ModelApi, bQ as ModelDefinitionConfig, bR as ModelProviderAuthMode, ao as ModelProviderConfig, bS as NamespacePolicy, ap as NativeKnowledgeConfig, bT as NativeKnowledgeFolderRuleConfig, bU as NativeKnowledgeObsidianVaultConfig, bV as NativeKnowledgeOpenClawWorkspaceConfig, bW as NotionLiveConnectorConfig, P as PluginConfig, a4 as PolicyClass, bX as PrincipalFromSessionKeyMode, bY as PrincipalRule, bZ as ProceduralConfig, aj as QmdSearchExplain, Z as QmdSearchResult, b_ as QuestionEntry, aX as RECALL_DISCLOSURE_LEVELS, b$ as ReasoningEffort, K as RecallDisclosure, c0 as RecallPipelineConfig, H as RecallPlanMode, c1 as RecallSectionConfig, R as RecallTierExplain, c2 as RecallTraceEvent, c3 as RelevanceFeedback, aq as RetrievalTier, c4 as SPECULATIVE_TTL_DAYS, c5 as ScoredEntity, a6 as SecretRef, c6 as SemanticChunkingConfigShape, a1 as SessionObserverBandConfig, c7 as SignalLevel, ar as SignalScanResult, c8 as SlotBehaviorConfig, c9 as SlotMismatchMode, T as TopicScore, _ as TranscriptEntry, ca as TriggerMode, a3 as VerificationState, cb as confidenceTier, b1 as isRecallDisclosure } from './types-BgChEr0M.js';
package/dist/types.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  SPECULATIVE_TTL_DAYS,
5
5
  confidenceTier,
6
6
  isRecallDisclosure
7
- } from "./chunk-C43KEWEV.js";
7
+ } from "./chunk-NE2JBMLN.js";
8
8
  import "./chunk-PZ5AY32C.js";
9
9
  export {
10
10
  DEFAULT_RECALL_DISCLOSURE,
@@ -1,5 +1,5 @@
1
1
  import { TierRoutingPolicy } from './tier-routing.js';
2
- import './types-D8yUmSik.js';
2
+ import './types-BgChEr0M.js';
3
3
  import './types-ByK7T3L6.js';
4
4
  import './index-DJ9QWMw-.js';
5
5
  import './lifecycle.js';
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  searchVerifiedEpisodes
3
- } from "./chunk-JF7SFXTG.js";
3
+ } from "./chunk-QKK64Z6M.js";
4
4
  import "./chunk-HQ6NIBL6.js";
5
- import "./chunk-Y7NWBBHV.js";
5
+ import "./chunk-6CVI6BP6.js";
6
6
  import "./chunk-M7XQSUBB.js";
7
7
  import "./chunk-5UZXUTVO.js";
8
8
  import "./chunk-J6A3CX5N.js";
9
- import "./chunk-C43KEWEV.js";
9
+ import "./chunk-NE2JBMLN.js";
10
10
  import "./chunk-CPPS65WS.js";
11
11
  import "./chunk-RULE4VG5.js";
12
12
  import "./chunk-SCU65EZI.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.652",
3
+ "version": "9.3.654",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1692,6 +1692,13 @@ export class EngramAccessHttpServer {
1692
1692
  const resolved = await this.service.getWritableStorageForNamespace(namespace, principal);
1693
1693
  return resolved.storage;
1694
1694
  },
1695
+ // Catalog write touch (issue #1499 sweep): a contradiction merge writes a
1696
+ // new memory directly to the resolved namespace storage, bypassing the
1697
+ // extraction write path. Record it so QMD maintenance / writtenSince
1698
+ // don't miss the write. Best-effort and failure-tolerant.
1699
+ onMergedMemoryWritten: (namespace, storageDir) => {
1700
+ this.service.recordCatalogWrite(namespace, storageDir);
1701
+ },
1695
1702
  });
1696
1703
  this.respondJson(res, 200, result);
1697
1704
  return;
@@ -371,11 +371,80 @@ test("MCP capsule tools tolerate client-injected cwd/projectTag (#1434)", async
371
371
  }
372
372
  });
373
373
 
374
+ test("MCP capsule list tolerates client-injected sessionKey (#1513)", async () => {
375
+ let received: Record<string, unknown> | undefined;
376
+ const service = {
377
+ ...makeMockService(),
378
+ capsuleList: async (args: Record<string, unknown>) => {
379
+ received = args;
380
+ return { capsules: [] };
381
+ },
382
+ } as unknown as EngramAccessService;
383
+ const server = new EngramMcpServer(service);
384
+
385
+ const response = await server.handleRequest(
386
+ makeToolRequest("engram.capsule_list", {
387
+ namespace: "team",
388
+ sessionKey: "pi-injected-session",
389
+ cwd: "/x",
390
+ projectTag: "t",
391
+ }),
392
+ );
393
+
394
+ assert.deepEqual(received, {
395
+ namespace: "team",
396
+ principal: undefined,
397
+ });
398
+ assert.equal((response as Record<string, unknown> & { result?: { isError?: boolean } }).result?.isError, false);
399
+ });
400
+
401
+ test("MCP capsule list derives namespace principal from client-injected sessionKey (#1513)", async () => {
402
+ let received: Record<string, unknown> | undefined;
403
+ const service = {
404
+ ...makeMockService(),
405
+ configRef: parseConfig({
406
+ memoryDir: "/tmp/remnic-mcp-capsule-list-session-principal",
407
+ namespacesEnabled: true,
408
+ defaultNamespace: "default",
409
+ principalFromSessionKeyMode: "map",
410
+ principalFromSessionKeyRules: [{ match: "pi-session", principal: "pi-agent" }],
411
+ namespacePolicies: [
412
+ { name: "team", readPrincipals: ["pi-agent"], writePrincipals: [] },
413
+ ],
414
+ }),
415
+ capsuleList: async (args: Record<string, unknown>) => {
416
+ received = args;
417
+ return { capsules: [] };
418
+ },
419
+ } as unknown as EngramAccessService;
420
+ const server = new EngramMcpServer(service);
421
+
422
+ const response = await server.handleRequest(
423
+ makeToolRequest("engram.capsule_list", {
424
+ namespace: "team",
425
+ sessionKey: "pi-session",
426
+ }),
427
+ );
428
+
429
+ assert.deepEqual(received, {
430
+ namespace: "team",
431
+ principal: "pi-agent",
432
+ });
433
+ assert.equal((response as Record<string, unknown> & { result?: { isError?: boolean } }).result?.isError, false);
434
+ });
435
+
374
436
  test("MCP session override is injected only into tools that accept sessionKey", async () => {
375
437
  let capsuleListArgs: Record<string, unknown> | undefined;
376
438
  let observeArgs: Record<string, unknown> | undefined;
377
439
  const service = {
378
440
  ...makeMockService(),
441
+ configRef: parseConfig({
442
+ memoryDir: "/tmp/remnic-mcp-session-override-test",
443
+ namespacesEnabled: true,
444
+ defaultNamespace: "default",
445
+ principalFromSessionKeyMode: "map",
446
+ principalFromSessionKeyRules: [{ match: "adapter-session", principal: "adapter-agent" }],
447
+ }),
379
448
  capsuleList: async (args: Record<string, unknown>) => {
380
449
  capsuleListArgs = args;
381
450
  return { capsules: [] };
@@ -400,7 +469,7 @@ test("MCP session override is injected only into tools that accept sessionKey",
400
469
 
401
470
  assert.deepEqual(capsuleListArgs, {
402
471
  namespace: undefined,
403
- principal: undefined,
472
+ principal: "adapter-agent",
404
473
  });
405
474
  assert.deepEqual(observeArgs, {
406
475
  sessionKey: "adapter-session",
package/src/access-mcp.ts CHANGED
@@ -20,6 +20,7 @@ import { validateBriefingFormat } from "./briefing.js";
20
20
  import { buildCitationGuidance, type CitationMetadata } from "./citations.js";
21
21
  import { projectTagProjectId } from "./coding/coding-namespace.js";
22
22
  import { expandTildePath } from "./utils/path.js";
23
+ import { resolvePrincipal } from "./namespaces/principal.js";
23
24
  import {
24
25
  REMNIC_CHATGPT_MEMORY_INSPECTOR_MIME_TYPE,
25
26
  REMNIC_CHATGPT_MEMORY_INSPECTOR_TOOL,
@@ -155,7 +156,7 @@ const STRICT_MCP_SCHEMA_KEYS: Partial<Record<SchemaName, readonly string[]>> = {
155
156
  "projectTag",
156
157
  ],
157
158
  capsuleImport: ["archivePath", "namespace", "mode", "passphrase", "cwd", "projectTag"],
158
- capsuleList: ["namespace", "cwd", "projectTag"],
159
+ capsuleList: ["namespace", "sessionKey", "cwd", "projectTag"],
159
160
  };
160
161
 
161
162
  // Shared JSON-schema fragments for the client-injected git/project context
@@ -852,6 +853,10 @@ export class EngramMcpServer {
852
853
  type: "object",
853
854
  properties: {
854
855
  namespace: { type: "string" },
856
+ sessionKey: {
857
+ type: "string",
858
+ description: "Optional session key used to derive namespace principal when no trusted transport principal is present.",
859
+ },
855
860
  ...MCP_GIT_CONTEXT_SCHEMA_PROPS_IGNORED,
856
861
  },
857
862
  additionalProperties: false,
@@ -2694,9 +2699,14 @@ export class EngramMcpServer {
2694
2699
  }
2695
2700
  case "engram.capsule_list": {
2696
2701
  const body: CapsuleListRequest = parseMcpRequest("capsuleList", args);
2702
+ const requestPrincipal =
2703
+ effectivePrincipal ??
2704
+ (body.sessionKey && this.service.configRef
2705
+ ? resolvePrincipal(body.sessionKey, this.service.configRef)
2706
+ : undefined);
2697
2707
  return this.service.capsuleList({
2698
2708
  namespace: body.namespace,
2699
- principal: effectivePrincipal,
2709
+ principal: requestPrincipal,
2700
2710
  });
2701
2711
  }
2702
2712
  case "engram.memory_governance_run":
@@ -3210,6 +3220,13 @@ export class EngramMcpServer {
3210
3220
  const resolved = await this.service.getWritableStorageForNamespace(namespace, effectivePrincipal);
3211
3221
  return resolved.storage;
3212
3222
  },
3223
+ // Catalog write touch (issue #1499 sweep): a contradiction merge writes
3224
+ // a new memory directly to the resolved namespace storage, bypassing the
3225
+ // extraction write path. Record it so QMD maintenance / writtenSince
3226
+ // don't miss the write. Best-effort and failure-tolerant.
3227
+ onMergedMemoryWritten: (namespace, storageDir) => {
3228
+ this.service.recordCatalogWrite(namespace, storageDir);
3229
+ },
3213
3230
  });
3214
3231
  }
3215
3232
  case "engram.contradiction_scan_run":
@@ -410,6 +410,7 @@ export const capsuleImportRequestSchema = z
410
410
  export const capsuleListRequestSchema = z
411
411
  .object({
412
412
  namespace: namespaceSchema,
413
+ sessionKey: sessionKeySchema,
413
414
  });
414
415
 
415
416
  // ---------------------------------------------------------------------------
@@ -7348,6 +7348,18 @@ export class EngramAccessService {
7348
7348
  return this.orchestrator.storage;
7349
7349
  }
7350
7350
 
7351
+ /**
7352
+ * Best-effort catalog write touch delegate (issue #1499 sweep). HTTP/MCP
7353
+ * surfaces resolve a per-namespace storage and write through it directly (e.g.
7354
+ * a contradiction merge), bypassing the extraction write path that owns
7355
+ * `markCatalogWrite`. They call this to record the write so a (possibly
7356
+ * dynamic) namespace's `lastWriteAt` stays accurate and QMD maintenance does
7357
+ * not miss it. Fire-and-forget and failure-tolerant on the orchestrator side.
7358
+ */
7359
+ recordCatalogWrite(namespace?: string, storageDir?: string): void {
7360
+ this.orchestrator.recordCatalogWrite(namespace, storageDir);
7361
+ }
7362
+
7351
7363
  get configRef(): PluginConfig {
7352
7364
  return this.orchestrator.config;
7353
7365
  }
@@ -16,6 +16,7 @@ import {
16
16
  focusMatchesMemory,
17
17
  buildActiveThreads,
18
18
  buildBriefing,
19
+ buildChainFollowupGenerator,
19
20
  BRIEFING_FOLLOWUP_DEFAULT_MODEL,
20
21
  } from "./briefing.js";
21
22
  import type {
@@ -976,6 +977,75 @@ test("buildBriefing: unrelated LLM errors still produce generic message (no fals
976
977
  );
977
978
  });
978
979
 
980
+ test("buildChainFollowupGenerator accepts markdown-fenced followup JSON", async () => {
981
+ const generator = buildChainFollowupGenerator({
982
+ chatCompletion: async () => ({
983
+ content: [
984
+ "```json",
985
+ "{",
986
+ ' "followups": [',
987
+ ' { "text": "Check the launch plan", "rationale": "Open commitment found" }',
988
+ " ]",
989
+ "}",
990
+ "```",
991
+ ].join("\n"),
992
+ }),
993
+ });
994
+
995
+ const followups = await generator({
996
+ sections: {
997
+ activeThreads: [],
998
+ recentEntities: [],
999
+ openCommitments: [{ id: "commit-1", kind: "commitment", text: "Finish launch plan" }],
1000
+ suggestedFollowups: [],
1001
+ },
1002
+ windowLabel: "today",
1003
+ maxFollowups: 3,
1004
+ });
1005
+
1006
+ assert.deepEqual(followups, [
1007
+ {
1008
+ text: "Check the launch plan",
1009
+ rationale: "Open commitment found",
1010
+ },
1011
+ ]);
1012
+ });
1013
+
1014
+ test("buildChainFollowupGenerator skips parseable non-followup JSON candidates", async () => {
1015
+ const generator = buildChainFollowupGenerator({
1016
+ chatCompletion: async () => ({
1017
+ content: [
1018
+ '{ "metadata": "not followups" }',
1019
+ "```json",
1020
+ "{",
1021
+ ' "followups": [',
1022
+ ' { "text": "Review the launch plan", "rationale": "Actual follow-up block" }',
1023
+ " ]",
1024
+ "}",
1025
+ "```",
1026
+ ].join("\n"),
1027
+ }),
1028
+ });
1029
+
1030
+ const followups = await generator({
1031
+ sections: {
1032
+ activeThreads: [],
1033
+ recentEntities: [],
1034
+ openCommitments: [{ id: "commit-1", kind: "commitment", text: "Finish launch plan" }],
1035
+ suggestedFollowups: [],
1036
+ },
1037
+ windowLabel: "today",
1038
+ maxFollowups: 3,
1039
+ });
1040
+
1041
+ assert.deepEqual(followups, [
1042
+ {
1043
+ text: "Review the launch plan",
1044
+ rationale: "Actual follow-up block",
1045
+ },
1046
+ ]);
1047
+ });
1048
+
979
1049
  // ──────────────────────────────────────────────────────────────────────────
980
1050
  // Round 8 Finding UXE4: buildActiveThreads must recompute reason from newer memory
981
1051
  // ──────────────────────────────────────────────────────────────────────────
package/src/briefing.ts CHANGED
@@ -1298,27 +1298,37 @@ function parseFollowupResponse(raw: string, max: number): BriefingFollowup[] {
1298
1298
  // JSON.parse throws on invalid JSON — let the caller catch it so the outer
1299
1299
  // try/catch in buildBriefing can set followupsUnavailableReason rather than
1300
1300
  // silently returning an empty array that masks the parse failure.
1301
- const parsed = JSON.parse(raw) as unknown;
1302
- if (!parsed || typeof parsed !== "object") {
1303
- throw new Error(`LLM returned non-object JSON: ${typeof parsed}`);
1304
- }
1305
- const arr = (parsed as { followups?: unknown }).followups;
1306
- if (!Array.isArray(arr)) {
1307
- throw new Error(`LLM response missing "followups" array`);
1308
- }
1309
- const out: BriefingFollowup[] = [];
1310
- for (const entry of arr) {
1311
- if (!entry || typeof entry !== "object") continue;
1312
- const text = (entry as Record<string, unknown>).text;
1313
- if (typeof text !== "string" || text.trim().length === 0) continue;
1314
- const rationale = (entry as Record<string, unknown>).rationale;
1315
- out.push({
1316
- text: text.trim(),
1317
- rationale: typeof rationale === "string" ? rationale.trim() : undefined,
1318
- });
1319
- if (out.length >= max) break;
1301
+ const candidates = extractJsonCandidates(raw);
1302
+ let lastError: unknown;
1303
+ for (const candidate of candidates) {
1304
+ try {
1305
+ const parsed = JSON.parse(candidate) as unknown;
1306
+ if (!parsed || typeof parsed !== "object") {
1307
+ throw new Error(`LLM returned non-object JSON: ${typeof parsed}`);
1308
+ }
1309
+ const arr = (parsed as { followups?: unknown }).followups;
1310
+ if (!Array.isArray(arr)) {
1311
+ throw new Error(`LLM response missing "followups" array`);
1312
+ }
1313
+ const out: BriefingFollowup[] = [];
1314
+ for (const entry of arr) {
1315
+ if (!entry || typeof entry !== "object") continue;
1316
+ const text = (entry as Record<string, unknown>).text;
1317
+ if (typeof text !== "string" || text.trim().length === 0) continue;
1318
+ const rationale = (entry as Record<string, unknown>).rationale;
1319
+ out.push({
1320
+ text: text.trim(),
1321
+ rationale: typeof rationale === "string" ? rationale.trim() : undefined,
1322
+ });
1323
+ if (out.length >= max) break;
1324
+ }
1325
+ return out;
1326
+ } catch (err) {
1327
+ lastError = err;
1328
+ }
1320
1329
  }
1321
- return out;
1330
+ if (lastError) throw lastError;
1331
+ throw new Error("LLM response contained no JSON candidates");
1322
1332
  }
1323
1333
 
1324
1334
  function stringifyError(err: unknown): string {
package/src/cli.ts CHANGED
@@ -4155,6 +4155,103 @@ export function registerCli(
4155
4155
  console.log("\nOK");
4156
4156
  });
4157
4157
 
4158
+ namespacesCmd
4159
+ .command("rebuild")
4160
+ .description(
4161
+ "Rebuild the namespace catalog from disk (issue #1499). Scans configured + dynamic namespace roots safely and rewrites <memoryDir>/state/namespaces.jsonl",
4162
+ )
4163
+ .option("--dry-run", "Show the rebuilt catalog without writing it")
4164
+ .option("--apply", "Write the rebuilt catalog to disk")
4165
+ .option("--json", "Emit the rebuild result as JSON")
4166
+ .action(async (optionsRaw: unknown) => {
4167
+ const options =
4168
+ optionsRaw && typeof optionsRaw === "object"
4169
+ ? (optionsRaw as { dryRun?: boolean; apply?: boolean; json?: boolean })
4170
+ : {};
4171
+ // Default to a non-destructive dry run unless --apply is given.
4172
+ // Reject contradictory flags rather than silently choosing one.
4173
+ if (options.dryRun === true && options.apply === true) {
4174
+ throw new Error("Pass only one of --dry-run or --apply, not both.");
4175
+ }
4176
+ const dryRun = options.apply !== true;
4177
+
4178
+ if (!orchestrator.namespaceCatalog.enabled) {
4179
+ const note =
4180
+ "Namespace catalog is inert: it only runs when namespacesEnabled is true and namespaceCatalogEnabled is not false.";
4181
+ if (options.json === true) {
4182
+ // Include `applied: false` so the inert JSON payload carries the SAME
4183
+ // shape as the enabled path (cursor Low — NFDBM). An inert catalog
4184
+ // rewrote nothing, so a `--apply` did NOT apply; automation that keys
4185
+ // on `applied` must not misread a missing field as a completed rebuild.
4186
+ console.log(
4187
+ JSON.stringify(
4188
+ { dryRun, enabled: false, applied: false, records: [], skipped: [], note },
4189
+ null,
4190
+ 2,
4191
+ ),
4192
+ );
4193
+ } else {
4194
+ console.log(note);
4195
+ }
4196
+ // Exit-code parity with the enabled path (cursor Medium — NFb5W): an
4197
+ // inert catalog rewrote NOTHING, so a `--apply` (dryRun=false) did NOT
4198
+ // apply. The enabled path sets `exitCode = 1` whenever a non-dry-run
4199
+ // rebuild reports `applied: false`; an inert `--apply` is exactly that
4200
+ // case, so exit-code-only automation must see a non-zero exit rather
4201
+ // than read a no-op apply as a completed rebuild. A `--dry-run` (or the
4202
+ // default dry run) inert call stays exit 0 — it never promised to write.
4203
+ if (!dryRun) {
4204
+ process.exitCode = 1;
4205
+ }
4206
+ return;
4207
+ }
4208
+
4209
+ const result = await orchestrator.namespaceCatalog.rebuildFromDisk({ dryRun });
4210
+
4211
+ if (options.json === true) {
4212
+ console.log(JSON.stringify({ enabled: true, ...result }, null, 2));
4213
+ // Mirror the non-JSON failure signal (round 6, codex P2 — NEFoa): an
4214
+ // `--apply` that could not acquire the lock returns `applied: false`
4215
+ // and rewrote nothing, so automation must see a non-zero exit even in
4216
+ // JSON mode rather than treating a no-op apply as a successful rebuild.
4217
+ if (!dryRun && result.applied === false) {
4218
+ process.exitCode = 1;
4219
+ }
4220
+ return;
4221
+ }
4222
+
4223
+ console.log("=== Namespace Catalog Rebuild ===\n");
4224
+ console.log(`mode: ${dryRun ? "dry-run" : "apply"}`);
4225
+ console.log(`namespaces: ${result.records.length}`);
4226
+ for (const record of result.records) {
4227
+ console.log(
4228
+ `${record.namespace}\n kind: ${record.kind}\n storage: ${record.storageDir}\n discovered-by: ${record.discoveredBy}`,
4229
+ );
4230
+ }
4231
+
4232
+ if (result.skipped.length > 0) {
4233
+ console.log("\nSkipped / ambiguous roots:");
4234
+ for (const skip of result.skipped) {
4235
+ console.log(`- ${skip.token} (${skip.reason})${skip.detail ? `: ${skip.detail}` : ""}`);
4236
+ }
4237
+ }
4238
+
4239
+ if (dryRun) {
4240
+ console.log("\nDRY RUN");
4241
+ } else if (result.applied) {
4242
+ console.log("\nOK");
4243
+ } else {
4244
+ // An --apply that could not acquire the cross-process rebuild lock
4245
+ // ran compute-only and did NOT rewrite the catalog (NBn3n/NBsGG).
4246
+ // Report the non-mutation explicitly and exit non-zero so scripts
4247
+ // and operators retry rather than assume the rebuild persisted.
4248
+ console.log(
4249
+ "\nNOT APPLIED: another rebuild holds the lock; the catalog was NOT rewritten. Retry shortly.",
4250
+ );
4251
+ process.exitCode = 1;
4252
+ }
4253
+ });
4254
+
4158
4255
  cmd
4159
4256
  .command("export")
4160
4257
  .description("Export Remnic memory to JSON, Markdown bundle, or SQLite")
@@ -8858,6 +8955,13 @@ export function registerCli(
8858
8955
  }
8859
8956
  return orchestrator.getStorageForNamespace(requested || orchestrator.config.defaultNamespace);
8860
8957
  },
8958
+ // Catalog write touch (issue #1499 sweep): a contradiction merge can
8959
+ // write a new memory to a dynamic namespace via the CLI resolve path,
8960
+ // bypassing the extraction write path. Record it so QMD maintenance /
8961
+ // writtenSince don't miss the write. Best-effort and failure-tolerant.
8962
+ onMergedMemoryWritten: (namespace, storageDir) => {
8963
+ orchestrator.recordCatalogWrite(namespace, storageDir);
8964
+ },
8861
8965
  });
8862
8966
  console.log(result.message);
8863
8967
  if (result.affectedIds.length > 0) {
@@ -1291,3 +1291,43 @@ test("parseConfig rejects connectors.gmail.pollIntervalMs above maximum (25h)",
1291
1291
  /pollIntervalMs/,
1292
1292
  );
1293
1293
  });
1294
+
1295
+ // ── issue #1499 / CLAUDE.md rule #36: namespaceCatalogEnabled opt-out coercion.
1296
+ // A string "false"/"0"/"no"/"off" from CLI/env/JSON config must disable the
1297
+ // catalog, not stay truthy. Defaults to enabled when absent/unrecognized.
1298
+ test("parseConfig namespaceCatalogEnabled defaults to true when absent", () => {
1299
+ assert.equal(parseConfig({}).namespaceCatalogEnabled, true);
1300
+ });
1301
+
1302
+ test('parseConfig namespaceCatalogEnabled="false" (string) → false (rule #36)', () => {
1303
+ assert.equal(parseConfig({ namespaceCatalogEnabled: "false" }).namespaceCatalogEnabled, false);
1304
+ });
1305
+
1306
+ test('parseConfig namespaceCatalogEnabled="0" (string) → false', () => {
1307
+ assert.equal(parseConfig({ namespaceCatalogEnabled: "0" }).namespaceCatalogEnabled, false);
1308
+ });
1309
+
1310
+ test('parseConfig namespaceCatalogEnabled="no"/"off" (strings) → false', () => {
1311
+ assert.equal(parseConfig({ namespaceCatalogEnabled: "no" }).namespaceCatalogEnabled, false);
1312
+ assert.equal(parseConfig({ namespaceCatalogEnabled: "off" }).namespaceCatalogEnabled, false);
1313
+ });
1314
+
1315
+ test("parseConfig namespaceCatalogEnabled=false (boolean) → false", () => {
1316
+ assert.equal(parseConfig({ namespaceCatalogEnabled: false }).namespaceCatalogEnabled, false);
1317
+ });
1318
+
1319
+ test('parseConfig namespaceCatalogEnabled="true" (string) → true', () => {
1320
+ assert.equal(parseConfig({ namespaceCatalogEnabled: "true" }).namespaceCatalogEnabled, true);
1321
+ });
1322
+
1323
+ test("parseConfig namespaceCatalogEnabled present-but-unrecognized → throws (rule #51, codex NI42R)", () => {
1324
+ // A PRESENT but unrecognized value is rejected, not silently defaulted to
1325
+ // enabled — mirrors resolveEmitLegacyTools (CLAUDE.md rule #51: reject invalid
1326
+ // input instead of silently defaulting). Absent still defaults to true.
1327
+ assert.throws(
1328
+ () => parseConfig({ namespaceCatalogEnabled: "maybe" }),
1329
+ /namespaceCatalogEnabled must be a boolean-like value/,
1330
+ );
1331
+ assert.throws(() => parseConfig({ namespaceCatalogEnabled: "flase" }), /boolean-like value/);
1332
+ assert.throws(() => parseConfig({ namespaceCatalogEnabled: 2 }), /boolean-like value/);
1333
+ });
package/src/config.ts CHANGED
@@ -384,6 +384,27 @@ function resolveEmitLegacyTools(configValue: unknown): boolean {
384
384
  return true;
385
385
  }
386
386
 
387
+ /**
388
+ * Resolve the `namespaceCatalogEnabled` opt-out (issue #1499). A boolean-like
389
+ * value ("false"/"0"/"no"/"off" etc.) is honored; a PRESENT but unrecognized
390
+ * value ("flase", 2) is REJECTED rather than silently defaulting to enabled
391
+ * (CLAUDE.md rule #51 — reject invalid input instead of silently defaulting),
392
+ * mirroring `resolveEmitLegacyTools`. Defaults to enabled only when absent.
393
+ */
394
+ function resolveNamespaceCatalogEnabled(configValue: unknown): boolean {
395
+ const ACCEPTED = "true/false/1/0/yes/no/on/off";
396
+ if (configValue !== undefined && configValue !== null) {
397
+ const coerced = coerceBooleanLike(configValue);
398
+ if (coerced === undefined) {
399
+ throw new Error(
400
+ `namespaceCatalogEnabled must be a boolean-like value (${ACCEPTED}); got ${JSON.stringify(configValue)}`,
401
+ );
402
+ }
403
+ return coerced;
404
+ }
405
+ return true;
406
+ }
407
+
387
408
  export function isOpenaiApiKeyDisabled(value: unknown): boolean {
388
409
  return value === false || (typeof value === "string" && value.trim().toLowerCase() === "false");
389
410
  }
@@ -2588,6 +2609,14 @@ export function parseConfig(raw: unknown): PluginConfig {
2588
2609
 
2589
2610
  // v3.0 namespaces (default off)
2590
2611
  namespacesEnabled: cfg.namespacesEnabled === true,
2612
+ // Namespace catalog (issue #1499): default ON, but only does anything when
2613
+ // namespacesEnabled is also true (the catalog is inert otherwise). Operators
2614
+ // opt out with namespaceCatalogEnabled: false. Boolean-like opt-outs from
2615
+ // CLI/env/JSON ("false", "0", "no", "off") are honored (CLAUDE.md rule #36);
2616
+ // a PRESENT but unrecognized value ("flase", 2) is REJECTED rather than
2617
+ // silently defaulting to enabled (CLAUDE.md rule #51); default to enabled
2618
+ // only when the value is absent.
2619
+ namespaceCatalogEnabled: resolveNamespaceCatalogEnabled(cfg.namespaceCatalogEnabled),
2591
2620
  // NOTE: namespace identifiers are intentionally NOT sanitized here — the
2592
2621
  // codebase rejects unsafe namespaces at the point of use (see
2593
2622
  // codex-materialize-runner and NamespaceStorageRouter / resolveNamespaceDir),