@remnic/core 9.3.723 → 9.3.725

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 (270) hide show
  1. package/dist/access-boundary.d.ts +6 -11
  2. package/dist/access-boundary.js +9 -9
  3. package/dist/access-cli.js +29 -23
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +5 -5
  6. package/dist/access-http.js +19 -14
  7. package/dist/access-mcp.d.ts +5 -5
  8. package/dist/access-mcp.js +18 -13
  9. package/dist/access-operations-batch.js +16 -10
  10. package/dist/access-operations.d.ts +5 -5
  11. package/dist/access-operations.js +17 -11
  12. package/dist/{access-service-Dj3I-VBS.d.ts → access-service-B2PlPtxh.d.ts} +3 -3
  13. package/dist/access-service.d.ts +5 -5
  14. package/dist/access-service.js +8 -8
  15. package/dist/access-surface-catalog.d.ts +5 -5
  16. package/dist/access-surface-catalog.js +82 -82
  17. package/dist/access-surface-catalog.js.map +1 -1
  18. package/dist/action-confidence.d.ts +1 -1
  19. package/dist/active-memory-bridge.d.ts +1 -1
  20. package/dist/active-recall.d.ts +1 -1
  21. package/dist/behavior-learner.d.ts +1 -1
  22. package/dist/behavior-signals.d.ts +1 -1
  23. package/dist/bootstrap.d.ts +4 -4
  24. package/dist/briefing.d.ts +1 -1
  25. package/dist/briefing.js +3 -3
  26. package/dist/buffer-surprise-report.d.ts +1 -1
  27. package/dist/buffer.d.ts +1 -1
  28. package/dist/calibration.d.ts +1 -1
  29. package/dist/capabilities.d.ts +1 -1
  30. package/dist/{catalog-F-OEVbRQ.d.ts → catalog-CzUb-GyD.d.ts} +1 -1
  31. package/dist/causal-behavior.d.ts +1 -1
  32. package/dist/causal-consolidation.d.ts +1 -1
  33. package/dist/causal-consolidation.js +4 -4
  34. package/dist/causal-trajectory-graph.d.ts +1 -1
  35. package/dist/{chunk-EVPNWN7Z.js → chunk-4R3CGCI5.js} +2 -2
  36. package/dist/{chunk-UO5ZHIIH.js → chunk-4WZ3J7M6.js} +2 -2
  37. package/dist/{chunk-PD42EYQN.js → chunk-5OPYYPJN.js} +2 -2
  38. package/dist/{chunk-EEJMD2L2.js → chunk-6CWGS2LY.js} +2 -2
  39. package/dist/{chunk-7CTNUVHZ.js → chunk-6SQQOJ4U.js} +2 -2
  40. package/dist/{chunk-ILFSEIRT.js → chunk-7AG54AF3.js} +1 -1
  41. package/dist/{chunk-ILFSEIRT.js.map → chunk-7AG54AF3.js.map} +1 -1
  42. package/dist/{chunk-UIXXE6NI.js → chunk-C6BWLYW5.js} +24 -15
  43. package/dist/chunk-C6BWLYW5.js.map +1 -0
  44. package/dist/{chunk-A5XNBK2D.js → chunk-DF3I6JGP.js} +6 -6
  45. package/dist/{chunk-GJSL4WG2.js → chunk-F3XZN24Z.js} +2 -2
  46. package/dist/{chunk-J3JPB7ZI.js → chunk-FACTVMXQ.js} +2 -2
  47. package/dist/{chunk-B7WISIAJ.js → chunk-FN6QP7JQ.js} +154 -1067
  48. package/dist/chunk-FN6QP7JQ.js.map +1 -0
  49. package/dist/{chunk-EBDA5TYA.js → chunk-FXCIOLRB.js} +2 -2
  50. package/dist/{chunk-TXQCG6SE.js → chunk-H7HUQIY7.js} +3 -3
  51. package/dist/{chunk-QD6QP5P6.js → chunk-I2HL5M6T.js} +17 -17
  52. package/dist/{chunk-M7R5R6OJ.js → chunk-IAIPNEHD.js} +20 -2
  53. package/dist/{chunk-M7R5R6OJ.js.map → chunk-IAIPNEHD.js.map} +1 -1
  54. package/dist/{chunk-Z4RU5VPM.js → chunk-J334N6QV.js} +2 -2
  55. package/dist/{chunk-NXRUUDUF.js → chunk-JJJCVCCQ.js} +2 -2
  56. package/dist/{chunk-GDUOCHCD.js → chunk-KGZF2VO3.js} +10 -10
  57. package/dist/{chunk-LA3HZKES.js → chunk-KLFYKC3K.js} +448 -49
  58. package/dist/chunk-KLFYKC3K.js.map +1 -0
  59. package/dist/{chunk-LLUGAZ3B.js → chunk-NT36GU6X.js} +30 -4
  60. package/dist/chunk-NT36GU6X.js.map +1 -0
  61. package/dist/{chunk-OHTOPFRF.js → chunk-SC2YAWRF.js} +2 -2
  62. package/dist/{chunk-YEE5KJQQ.js → chunk-T55EGZSJ.js} +2 -2
  63. package/dist/{chunk-N77ELJ2D.js → chunk-TT5U3UDO.js} +4 -4
  64. package/dist/{chunk-6FAMMBQ3.js → chunk-UBHVAGO7.js} +2 -2
  65. package/dist/chunk-VI5SGGXB.js +420 -0
  66. package/dist/chunk-VI5SGGXB.js.map +1 -0
  67. package/dist/{chunk-EDOQM4E4.js → chunk-WAFDHKES.js} +2 -2
  68. package/dist/{chunk-EXUCUPV2.js → chunk-WM7DNKBD.js} +2 -2
  69. package/dist/{dreams-ledger-3WSCI5V4.js → chunk-YF6FMLBT.js} +5 -6
  70. package/dist/{dreams-ledger-3WSCI5V4.js.map → chunk-YF6FMLBT.js.map} +1 -1
  71. package/dist/{chunk-WQYSDPMA.js → chunk-YQMPUYHE.js} +2 -2
  72. package/dist/{chunk-5O42UKXI.js → chunk-YUYPWJD3.js} +2 -2
  73. package/dist/{chunk-VCLGUIFM.js → chunk-Z3FGC3QE.js} +2 -2
  74. package/dist/{chunk-A7TJZLPK.js → chunk-ZK2L72VN.js} +2 -2
  75. package/dist/{chunk-BXEOHV7K.js → chunk-ZOHMPFOB.js} +3 -3
  76. package/dist/{graph-edge-decay-QPRJQ7DL.js → chunk-ZQNCHPTZ.js} +4 -5
  77. package/dist/{graph-edge-decay-QPRJQ7DL.js.map → chunk-ZQNCHPTZ.js.map} +1 -1
  78. package/dist/{cli-P_GaYcCY.d.ts → cli-IOlDHSBT.d.ts} +3 -3
  79. package/dist/cli.d.ts +6 -6
  80. package/dist/cli.js +31 -26
  81. package/dist/compounding/engine.d.ts +1 -1
  82. package/dist/compounding/engine.js +3 -3
  83. package/dist/compounding/preference-consolidator.d.ts +1 -1
  84. package/dist/compression-optimizer.d.ts +1 -1
  85. package/dist/config.d.ts +1 -1
  86. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  87. package/dist/connectors/codex-materialize-runner.js +3 -3
  88. package/dist/connectors/codex-materialize.d.ts +1 -1
  89. package/dist/connectors/index.d.ts +1 -1
  90. package/dist/connectors/index.js +3 -3
  91. package/dist/consolidation-provenance-check.d.ts +1 -1
  92. package/dist/consolidation-undo.d.ts +1 -1
  93. package/dist/contradiction/index.d.ts +1 -1
  94. package/dist/conversation-index/backend.d.ts +1 -1
  95. package/dist/conversation-index/chunker.d.ts +1 -1
  96. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  97. package/dist/conversation-index/indexer.d.ts +1 -1
  98. package/dist/conversation-index/search.d.ts +1 -1
  99. package/dist/day-summary.d.ts +1 -1
  100. package/dist/delinearize.d.ts +1 -1
  101. package/dist/direct-answer-wiring.d.ts +1 -1
  102. package/dist/direct-answer.d.ts +1 -1
  103. package/dist/dreams-ledger-6UODQOX6.js +22 -0
  104. package/dist/dreams-ledger-6UODQOX6.js.map +1 -0
  105. package/dist/embedding-fallback.d.ts +1 -1
  106. package/dist/enrichment/index.d.ts +1 -1
  107. package/dist/entity-retrieval.d.ts +1 -1
  108. package/dist/entity-retrieval.js +3 -3
  109. package/dist/entity-schema.d.ts +1 -1
  110. package/dist/event-time.d.ts +33 -1
  111. package/dist/event-time.js +3 -1
  112. package/dist/explicit-capture.d.ts +4 -4
  113. package/dist/extraction-faithfulness.d.ts +1 -1
  114. package/dist/extraction-judge-telemetry.d.ts +1 -1
  115. package/dist/extraction-judge-training.d.ts +1 -1
  116. package/dist/extraction-judge.d.ts +1 -1
  117. package/dist/extraction.d.ts +1 -1
  118. package/dist/fallback-llm.d.ts +1 -1
  119. package/dist/graph-dashboard-diff.d.ts +1 -1
  120. package/dist/graph-dashboard-key.d.ts +1 -1
  121. package/dist/graph-dashboard-parser.d.ts +1 -1
  122. package/dist/graph-edge-decay-RXUCJIN4.js +22 -0
  123. package/dist/graph-edge-decay-RXUCJIN4.js.map +1 -0
  124. package/dist/graph-edge-reinforcement.d.ts +1 -1
  125. package/dist/graph-snapshot.d.ts +1 -1
  126. package/dist/graph.d.ts +1 -1
  127. package/dist/identity-continuity.d.ts +1 -1
  128. package/dist/importance.d.ts +1 -1
  129. package/dist/index.d.ts +9 -9
  130. package/dist/index.js +58 -56
  131. package/dist/index.js.map +1 -1
  132. package/dist/intent.d.ts +1 -1
  133. package/dist/lcm/engine.d.ts +1 -1
  134. package/dist/lcm/index.d.ts +1 -1
  135. package/dist/lcm/tools.d.ts +1 -1
  136. package/dist/lifecycle.d.ts +1 -1
  137. package/dist/live-connectors-runner.d.ts +1 -1
  138. package/dist/local-llm.d.ts +1 -1
  139. package/dist/local-model-endpoint.d.ts +1 -1
  140. package/dist/maintenance/memory-governance.d.ts +1 -1
  141. package/dist/maintenance/memory-governance.js +3 -3
  142. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  143. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  144. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  145. package/dist/memory-action-policy.d.ts +1 -1
  146. package/dist/memory-cache.d.ts +1 -1
  147. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  148. package/dist/memory-projection-store.d.ts +1 -1
  149. package/dist/memory-provenance.d.ts +1 -1
  150. package/dist/memory-worth-outcomes.d.ts +1 -1
  151. package/dist/models-json.d.ts +1 -1
  152. package/dist/namespaces/migrate.d.ts +2 -2
  153. package/dist/namespaces/migrate.js +4 -4
  154. package/dist/namespaces/principal.d.ts +1 -1
  155. package/dist/namespaces/search.d.ts +1 -1
  156. package/dist/namespaces/storage.d.ts +2 -2
  157. package/dist/namespaces/storage.js +3 -3
  158. package/dist/native-knowledge.d.ts +1 -1
  159. package/dist/operator-toolkit.d.ts +1 -1
  160. package/dist/operator-toolkit.js +8 -8
  161. package/dist/orchestration/maintenance.d.ts +2 -2
  162. package/dist/orchestration/maintenance.js +5 -5
  163. package/dist/{orchestrator-f5xv19kp.d.ts → orchestrator-C_OMSIMZ.d.ts} +34 -3
  164. package/dist/orchestrator.d.ts +4 -4
  165. package/dist/orchestrator.js +17 -17
  166. package/dist/patterns-cli.d.ts +1 -1
  167. package/dist/policy-runtime.d.ts +1 -1
  168. package/dist/provenance.d.ts +1 -1
  169. package/dist/qmd-recall-cache.d.ts +1 -1
  170. package/dist/qmd.d.ts +1 -1
  171. package/dist/recall-disclosure-escalation.d.ts +1 -1
  172. package/dist/recall-explain-renderer.d.ts +1 -1
  173. package/dist/recall-explain-renderer.js +3 -3
  174. package/dist/recall-planner-llm.d.ts +1 -1
  175. package/dist/recall-state.d.ts +1 -1
  176. package/dist/recall-tag-filter.d.ts +1 -1
  177. package/dist/recall-xray-cli.d.ts +1 -1
  178. package/dist/recall-xray-cli.js +4 -4
  179. package/dist/recall-xray-renderer.d.ts +1 -1
  180. package/dist/recall-xray-renderer.js +3 -3
  181. package/dist/recall-xray.d.ts +1 -1
  182. package/dist/recall-xray.js +2 -2
  183. package/dist/resolve-auth-token.d.ts +1 -1
  184. package/dist/retrieval-agents.d.ts +1 -1
  185. package/dist/retrieval-tiers.d.ts +1 -1
  186. package/dist/routing/engine.d.ts +1 -1
  187. package/dist/routing/store.d.ts +1 -1
  188. package/dist/schemas.d.ts +22 -22
  189. package/dist/search/embed-helper.d.ts +1 -1
  190. package/dist/search/factory.d.ts +1 -1
  191. package/dist/search/index.d.ts +1 -1
  192. package/dist/search/lancedb-backend.d.ts +1 -1
  193. package/dist/search/meilisearch-backend.d.ts +1 -1
  194. package/dist/search/noop-backend.d.ts +1 -1
  195. package/dist/search/orama-backend.d.ts +1 -1
  196. package/dist/search/port.d.ts +1 -1
  197. package/dist/search/remote-backend.d.ts +1 -1
  198. package/dist/{semantic-consolidation-BQm1e1Pn.d.ts → semantic-consolidation-B8LiYtvx.d.ts} +1 -1
  199. package/dist/semantic-consolidation.d.ts +2 -2
  200. package/dist/semantic-consolidation.js +4 -4
  201. package/dist/semantic-rule-promotion.js +3 -3
  202. package/dist/semantic-rule-verifier.d.ts +1 -1
  203. package/dist/semantic-rule-verifier.js +3 -3
  204. package/dist/session-observer-bands.d.ts +1 -1
  205. package/dist/session-observer-state.d.ts +1 -1
  206. package/dist/shared-context/manager.d.ts +1 -1
  207. package/dist/signal.d.ts +1 -1
  208. package/dist/storage.d.ts +1 -1
  209. package/dist/storage.js +2 -2
  210. package/dist/summarizer.d.ts +1 -1
  211. package/dist/summary-snapshot.d.ts +1 -1
  212. package/dist/temporal-supersession.d.ts +1 -1
  213. package/dist/temporal-validity.d.ts +1 -1
  214. package/dist/threading.d.ts +1 -1
  215. package/dist/tier-migration.d.ts +1 -1
  216. package/dist/tier-routing.d.ts +1 -1
  217. package/dist/topics.d.ts +1 -1
  218. package/dist/transcript.d.ts +1 -1
  219. package/dist/transfer/types.d.ts +12 -12
  220. package/dist/trust-score-stage.d.ts +1 -1
  221. package/dist/trust-score.d.ts +1 -1
  222. package/dist/{types-DwO1Ot4n.d.ts → types-DmTaqob2.d.ts} +14 -0
  223. package/dist/types.d.ts +1 -1
  224. package/dist/types.js +1 -1
  225. package/dist/utility-runtime.d.ts +1 -1
  226. package/dist/verified-recall.js +3 -3
  227. package/package.json +2 -2
  228. package/src/access-boundary.ts +36 -2
  229. package/src/access-http.ts +17 -8
  230. package/src/access-mcp.ts +149 -1189
  231. package/src/access-operations-batch.ts +227 -1439
  232. package/src/access-surface-catalog.test.ts +4 -15
  233. package/src/access-surface-catalog.ts +82 -82
  234. package/src/bitemporal-dedup-backfill.test.ts +169 -0
  235. package/src/event-time.ts +57 -0
  236. package/src/extraction-eventtime-wiring.test.ts +101 -1
  237. package/src/orchestrator.ts +238 -43
  238. package/src/recall/archive-scoring.ts +526 -0
  239. package/src/types.ts +14 -0
  240. package/dist/chunk-B7WISIAJ.js.map +0 -1
  241. package/dist/chunk-EM2LDXJ2.js +0 -1315
  242. package/dist/chunk-EM2LDXJ2.js.map +0 -1
  243. package/dist/chunk-LA3HZKES.js.map +0 -1
  244. package/dist/chunk-LLUGAZ3B.js.map +0 -1
  245. package/dist/chunk-UIXXE6NI.js.map +0 -1
  246. /package/dist/{chunk-EVPNWN7Z.js.map → chunk-4R3CGCI5.js.map} +0 -0
  247. /package/dist/{chunk-UO5ZHIIH.js.map → chunk-4WZ3J7M6.js.map} +0 -0
  248. /package/dist/{chunk-PD42EYQN.js.map → chunk-5OPYYPJN.js.map} +0 -0
  249. /package/dist/{chunk-EEJMD2L2.js.map → chunk-6CWGS2LY.js.map} +0 -0
  250. /package/dist/{chunk-7CTNUVHZ.js.map → chunk-6SQQOJ4U.js.map} +0 -0
  251. /package/dist/{chunk-A5XNBK2D.js.map → chunk-DF3I6JGP.js.map} +0 -0
  252. /package/dist/{chunk-GJSL4WG2.js.map → chunk-F3XZN24Z.js.map} +0 -0
  253. /package/dist/{chunk-J3JPB7ZI.js.map → chunk-FACTVMXQ.js.map} +0 -0
  254. /package/dist/{chunk-EBDA5TYA.js.map → chunk-FXCIOLRB.js.map} +0 -0
  255. /package/dist/{chunk-TXQCG6SE.js.map → chunk-H7HUQIY7.js.map} +0 -0
  256. /package/dist/{chunk-QD6QP5P6.js.map → chunk-I2HL5M6T.js.map} +0 -0
  257. /package/dist/{chunk-Z4RU5VPM.js.map → chunk-J334N6QV.js.map} +0 -0
  258. /package/dist/{chunk-NXRUUDUF.js.map → chunk-JJJCVCCQ.js.map} +0 -0
  259. /package/dist/{chunk-GDUOCHCD.js.map → chunk-KGZF2VO3.js.map} +0 -0
  260. /package/dist/{chunk-OHTOPFRF.js.map → chunk-SC2YAWRF.js.map} +0 -0
  261. /package/dist/{chunk-YEE5KJQQ.js.map → chunk-T55EGZSJ.js.map} +0 -0
  262. /package/dist/{chunk-N77ELJ2D.js.map → chunk-TT5U3UDO.js.map} +0 -0
  263. /package/dist/{chunk-6FAMMBQ3.js.map → chunk-UBHVAGO7.js.map} +0 -0
  264. /package/dist/{chunk-EDOQM4E4.js.map → chunk-WAFDHKES.js.map} +0 -0
  265. /package/dist/{chunk-EXUCUPV2.js.map → chunk-WM7DNKBD.js.map} +0 -0
  266. /package/dist/{chunk-WQYSDPMA.js.map → chunk-YQMPUYHE.js.map} +0 -0
  267. /package/dist/{chunk-5O42UKXI.js.map → chunk-YUYPWJD3.js.map} +0 -0
  268. /package/dist/{chunk-VCLGUIFM.js.map → chunk-Z3FGC3QE.js.map} +0 -0
  269. /package/dist/{chunk-A7TJZLPK.js.map → chunk-ZK2L72VN.js.map} +0 -0
  270. /package/dist/{chunk-BXEOHV7K.js.map → chunk-ZOHMPFOB.js.map} +0 -0
package/src/access-mcp.ts CHANGED
@@ -137,6 +137,88 @@ const MCP_MIGRATED_OPERATIONS: Readonly<Record<string, OperationName>> = {
137
137
  // Correction Contract (issue #1580) — one plan/apply pipeline.
138
138
  "engram.memory_correct_plan": "memory_correct_plan",
139
139
  "engram.memory_correct_apply": "memory_correct_apply",
140
+ // Fully migrated through the strict-schema boundary (#1668).
141
+ "engram.recall": "recall",
142
+ "engram.recall_explain": "recall_explain",
143
+ "engram.set_coding_context": "set_coding_context",
144
+ "engram.recall_tier_explain": "recall_tier_explain",
145
+ "engram.recall_xray": "recall_xray",
146
+ "engram.wearables_status": "wearables_status",
147
+ "engram.wearables_sync": "wearables_sync",
148
+ "engram.transcript_day": "transcript_day",
149
+ "engram.transcript_search": "transcript_search",
150
+ "engram.transcript_memories": "transcript_memories",
151
+ "engram.action_confidence": "action_confidence",
152
+ "engram.chatgpt_memory_inspector": "chatgpt_memory_inspector",
153
+ "engram.day_summary": "day_summary",
154
+ "engram.capsule_export": "capsule_export",
155
+ "engram.capsule_import": "capsule_import",
156
+ "engram.capsule_list": "capsule_list",
157
+ "engram.memory_governance_run": "memory_governance_run",
158
+ "engram.procedure_mining_run": "procedure_mining_run",
159
+ "engram.pattern_reinforcement_run": "pattern_reinforcement_run",
160
+ "engram.procedural_stats": "procedural_stats",
161
+ "engram.memory_timeline": "memory_timeline",
162
+ "engram.suggestion_submit": "suggestion_submit",
163
+ "engram.entity_get": "entity_get",
164
+ "engram.review_queue_list": "review_queue_list",
165
+ "engram.observe": "observe",
166
+ "engram.lcm_search": "lcm_search",
167
+ "engram.lcm_compaction_flush": "lcm_compaction_flush",
168
+ "engram.lcm_compaction_record": "lcm_compaction_record",
169
+ "engram.continuity_audit_generate": "continuity_audit_generate",
170
+ "engram.continuity_incident_open": "continuity_incident_open",
171
+ "engram.continuity_incident_close": "continuity_incident_close",
172
+ "engram.continuity_incident_list": "continuity_incident_list",
173
+ "engram.continuity_loop_add_or_update": "continuity_loop_add_or_update",
174
+ "engram.continuity_loop_review": "continuity_loop_review",
175
+ "engram.identity_anchor_get": "identity_anchor_get",
176
+ "engram.identity_anchor_update": "identity_anchor_update",
177
+ "engram.memory_identity": "memory_identity",
178
+ "engram.work_task": "work_task",
179
+ "engram.work_project": "work_project",
180
+ "engram.work_board": "work_board",
181
+ "engram.shared_context_write_output": "shared_context_write_output",
182
+ "engram.shared_feedback_record": "shared_feedback_record",
183
+ "engram.shared_priorities_append": "shared_priorities_append",
184
+ "engram.shared_context_cross_signals_run": "shared_context_cross_signals_run",
185
+ "engram.shared_context_curate_daily": "shared_context_curate_daily",
186
+ "engram.compounding_weekly_synthesize": "compounding_weekly_synthesize",
187
+ "engram.compounding_promote_candidate": "compounding_promote_candidate",
188
+ "engram.compression_guidelines_optimize": "compression_guidelines_optimize",
189
+ "engram.compression_guidelines_activate": "compression_guidelines_activate",
190
+ "engram.memory_profile": "memory_profile",
191
+ "engram.memory_entities_list": "memory_entities_list",
192
+ "engram.memory_questions": "memory_questions",
193
+ "engram.memory_last_recall": "memory_last_recall",
194
+ "engram.memory_intent_debug": "memory_intent_debug",
195
+ "engram.memory_qmd_debug": "memory_qmd_debug",
196
+ "engram.memory_graph_explain": "memory_graph_explain",
197
+ "engram.graph_snapshot": "graph_snapshot",
198
+ "engram.memory_feedback": "memory_feedback",
199
+ "engram.memory_promote": "memory_promote",
200
+ "engram.memory_outcome": "memory_outcome",
201
+ "engram.memory_action_apply": "memory_action_apply",
202
+ "engram.context_checkpoint": "context_checkpoint",
203
+ "engram.briefing": "briefing",
204
+ "engram.review_list": "review_list",
205
+ "engram.review_resolve": "review_resolve",
206
+ "engram.contradiction_scan_run": "contradiction_scan_run",
207
+ "engram.memory_summarize_hourly": "memory_summarize_hourly",
208
+ "engram.conversation_index_update": "conversation_index_update",
209
+ "engram.profiling_report": "profiling_report",
210
+ "engram.graph_edge_decay_run": "graph_edge_decay_run",
211
+ "engram.live_connectors_run": "live_connectors_run",
212
+ "engram.peer_list": "peer_list",
213
+ "engram.peer_get": "peer_get",
214
+ "engram.peer_set": "peer_set",
215
+ "engram.peer_delete": "peer_delete",
216
+ "engram.peer_profile_get": "peer_profile_get",
217
+ "engram.peer_forget": "peer_forget",
218
+ "engram.console_state": "console_state",
219
+ "engram.dreams_status": "dreams_status",
220
+ "engram.dreams_run": "dreams_run",
221
+ "engram.memory_chat": "chat_message",
140
222
  };
141
223
 
142
224
  function resolveChatGptInspectorRecallSessionKey(
@@ -2615,1200 +2697,78 @@ export class EngramMcpServer {
2615
2697
  }
2616
2698
 
2617
2699
  private async callTool(name: string, args: Record<string, unknown>, effectivePrincipal?: string, mcpSessionId?: string, scope?: { namespace?: string; sessionKey?: string }, enforceWriteQuota?: () => void | Promise<void>): Promise<unknown> {
2618
- // Migrated operations dispatch through the access boundary (issue #1525):
2619
- // one registry entry owns schema validation, normalization (rules
2620
- // 17/28/36/48/51), and error mapping for every surface. The switch
2621
- // below still serves the not-yet-migrated tools. memory_store keeps its
2622
- // parseMcpRequest strict-keys guard (MCP transport contract) before the
2623
- // boundary re-validates the cleaned body.
2624
2700
  const migrated = MCP_MIGRATED_OPERATIONS[toLegacyToolName(name)];
2625
- if (migrated) {
2626
- const op = getOperation(migrated);
2627
- if (!op) {
2628
- throw new EngramAccessInputError(`access-boundary: operation not registered: ${migrated}`);
2629
- }
2630
- let envelope: Record<string, unknown>;
2631
- if (migrated === "memory_store") {
2632
- envelope = parseMcpRequest("memoryStore", args);
2633
- } else if (migrated.startsWith("codegraph_")) {
2634
- // codegraph parity tools (issue #1554): the surface handler dispatches
2635
- // on the `tool` field. The MCP tool name encodes it (e.g.
2636
- // codegraph_search_graph → tool: "search_graph"); inject it so the
2637
- // client payload does not need to carry the internal dispatch field.
2638
- envelope = { ...args, tool: migrated.slice("codegraph_".length) };
2639
- } else {
2640
- envelope = args;
2641
- }
2642
- // The registry erases In/Out at the Map boundary; the caller knows the
2643
- // concrete output shape ({ result: <service response> }) so the cast is
2644
- // the type-erasure seam, not a leap of faith.
2645
- const output = (await op.run(envelope, {
2646
- service: this.service,
2647
- authenticatedPrincipal: effectivePrincipal,
2648
- })) as { result: unknown };
2649
- return output.result;
2701
+ if (!migrated) {
2702
+ throw new Error("unknown tool: " + name);
2650
2703
  }
2651
- switch (toLegacyToolName(name)) {
2652
- case "engram.recall": {
2653
- // Forward `disclosure` only when the caller actually supplied it,
2654
- // so the service layer's default-application path stays the
2655
- // single source of truth. We must distinguish "absent" from
2656
- // "present-but-wrong-type": absence forwards as `undefined`
2657
- // (service applies the chunk default), while a present-but-
2658
- // non-string value (e.g. `1`, `true`) is rejected here as a
2659
- // structured input error instead of silently being coerced to
2660
- // `undefined`. CLAUDE.md rule 51: never silently default on
2661
- // malformed input. String values are forwarded as-is; the
2662
- // service's `isRecallDisclosure` guard rejects unknown enum
2663
- // strings (e.g. `"verbose"`) with the same error class.
2664
- let disclosure: RecallDisclosure | undefined;
2665
- if ("disclosure" in args && args.disclosure !== undefined && args.disclosure !== null) {
2666
- if (typeof args.disclosure !== "string") {
2667
- throw new EngramAccessInputError(
2668
- "disclosure must be a string (one of: chunk, section, raw)",
2669
- );
2670
- }
2671
- disclosure = args.disclosure as RecallDisclosure;
2672
- }
2673
- // Reject non-string cwd/projectTag (CLAUDE.md #51) — these control
2674
- // namespace routing so silent coercion to undefined would mix memories.
2675
- if ("cwd" in args && args.cwd !== undefined && args.cwd !== null && typeof args.cwd !== "string") {
2676
- throw new EngramAccessInputError("cwd must be a string");
2677
- }
2678
- if ("projectTag" in args && args.projectTag !== undefined && args.projectTag !== null && typeof args.projectTag !== "string") {
2679
- throw new EngramAccessInputError("projectTag must be a string");
2680
- }
2681
- // Issue #680 historical recall pin. Reject non-string asOf
2682
- // values up-front so malformed payloads surface as structured
2683
- // input errors. The service layer performs Date.parse on the
2684
- // string value.
2685
- if ("asOf" in args && args.asOf !== undefined && args.asOf !== null && typeof args.asOf !== "string") {
2686
- throw new EngramAccessInputError("asOf must be a string (ISO 8601 timestamp)");
2687
- }
2688
- // Tag filter (issue #689). Reject malformed tags / tagMatch
2689
- // up front rather than silently dropping (CLAUDE.md rule 51).
2690
- let tags: string[] | undefined;
2691
- if ("tags" in args && args.tags !== undefined && args.tags !== null) {
2692
- if (!Array.isArray(args.tags) || !args.tags.every((t) => typeof t === "string")) {
2693
- throw new EngramAccessInputError("tags must be an array of strings");
2694
- }
2695
- tags = args.tags;
2696
- }
2697
- let tagMatch: "any" | "all" | undefined;
2698
- if ("tagMatch" in args && args.tagMatch !== undefined && args.tagMatch !== null) {
2699
- if (typeof args.tagMatch !== "string" || (args.tagMatch !== "any" && args.tagMatch !== "all")) {
2700
- throw new EngramAccessInputError(
2701
- `tagMatch must be one of: any, all (got: ${String(args.tagMatch)})`,
2702
- );
2703
- }
2704
- tagMatch = args.tagMatch;
2705
- }
2706
- const response = await this.service.recall({
2707
- query: typeof args.query === "string" ? args.query : "",
2708
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : undefined,
2709
- authenticatedPrincipal: effectivePrincipal,
2710
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
2711
- topK: typeof args.topK === "number" && Number.isFinite(args.topK) ? args.topK : undefined,
2712
- mode: typeof args.mode === "string" ? args.mode as RecallPlanMode | "auto" : undefined,
2713
- includeDebug: args.includeDebug === true,
2714
- disclosure,
2715
- cwd: typeof args.cwd === "string" ? args.cwd : undefined,
2716
- projectTag: typeof args.projectTag === "string" ? args.projectTag : undefined,
2717
- asOf: typeof args.asOf === "string" ? args.asOf : undefined,
2718
- ...(tags !== undefined ? { tags } : {}),
2719
- ...(tagMatch !== undefined ? { tagMatch } : {}),
2720
- });
2721
-
2722
- if (this.shouldEmitCitations(mcpSessionId)) {
2723
- const citations = this.buildRecallCitations(response);
2724
- const guidance = buildCitationGuidance(citations);
2725
- if (guidance.length > 0) {
2726
- return {
2727
- ...response,
2728
- context: response.context + guidance,
2729
- citations,
2730
- };
2731
- }
2732
- }
2733
- return response;
2734
- }
2735
- case "engram.recall_explain":
2736
- return this.service.recallExplain({
2737
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : undefined,
2738
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
2739
- authenticatedPrincipal: effectivePrincipal,
2740
- });
2741
- case "engram.set_coding_context": {
2742
- // Issue #569 PR 7 — MCP tool for clients that don't ship cwd.
2743
- // Validation lives in EngramAccessService.setCodingContext; any
2744
- // EngramAccessInputError surfaces as a structured tool-call error.
2745
- const sessionKey = typeof args.sessionKey === "string" ? args.sessionKey : "";
2746
- // Support projectTag as an alternative to full codingContext.
2747
- // When projectTag is provided and codingContext is absent, create
2748
- // a tag-based context (issue #569 wiring).
2749
- const hasProjectTag = typeof args.projectTag === "string" && args.projectTag.trim().length > 0;
2750
- const hasCodingContext = "codingContext" in args;
2751
- if (!hasCodingContext && hasProjectTag) {
2752
- const tag = (args.projectTag as string).trim();
2753
- const projectId = projectTagProjectId(tag);
2754
- this.service.setCodingContext({
2755
- sessionKey,
2756
- codingContext: {
2757
- projectId,
2758
- branch: null,
2759
- rootPath: projectId,
2760
- defaultBranch: null,
2761
- },
2762
- });
2763
- return { ok: true };
2764
- }
2765
- // Require at least one of codingContext or projectTag (CLAUDE.md #51).
2766
- if (!hasCodingContext && !hasProjectTag) {
2767
- throw new EngramAccessInputError(
2768
- "set_coding_context requires either codingContext or projectTag",
2769
- );
2770
- }
2771
- const rawCtx = args.codingContext;
2772
- let codingContext: {
2773
- projectId: string;
2774
- branch: string | null;
2775
- rootPath: string;
2776
- defaultBranch: string | null;
2777
- } | null = null;
2778
- if (rawCtx !== null) {
2779
- if (typeof rawCtx !== "object" || rawCtx === undefined) {
2780
- throw new EngramAccessInputError("codingContext must be an object or null");
2781
- }
2782
- const obj = rawCtx as Record<string, unknown>;
2783
- const projectId = typeof obj.projectId === "string" ? obj.projectId : "";
2784
- const rootPath = typeof obj.rootPath === "string" ? obj.rootPath : "";
2785
- const branch = obj.branch === null
2786
- ? null
2787
- : typeof obj.branch === "string" ? obj.branch : undefined;
2788
- const defaultBranch = obj.defaultBranch === null
2789
- ? null
2790
- : typeof obj.defaultBranch === "string" ? obj.defaultBranch : undefined;
2791
- if (branch === undefined) {
2792
- throw new EngramAccessInputError("codingContext.branch must be a string or null");
2793
- }
2794
- if (defaultBranch === undefined) {
2795
- throw new EngramAccessInputError("codingContext.defaultBranch must be a string or null");
2796
- }
2797
- codingContext = { projectId, branch, rootPath, defaultBranch };
2798
- }
2799
- this.service.setCodingContext({ sessionKey, codingContext });
2800
- return { ok: true };
2801
- }
2802
- case "engram.recall_tier_explain":
2803
- return this.service.recallTierExplain(
2804
- typeof args.sessionKey === "string" && args.sessionKey.length > 0
2805
- ? args.sessionKey
2806
- : undefined,
2807
- typeof args.namespace === "string" && args.namespace.length > 0
2808
- ? args.namespace
2809
- : undefined,
2810
- effectivePrincipal,
2811
- );
2812
- case "engram.recall_xray": {
2813
- // `recallXray` throws on empty query / invalid budget; surface
2814
- // those as MCP errors with a listed-options message rather
2815
- // than silently returning `snapshotFound: false` (CLAUDE.md
2816
- // rule 51). Namespace scope is enforced inside the service
2817
- // via `canReadNamespace`.
2818
- const query = typeof args.query === "string" ? args.query : "";
2819
- const sessionKey =
2820
- typeof args.sessionKey === "string" && args.sessionKey.length > 0
2821
- ? args.sessionKey
2822
- : undefined;
2823
- const namespace =
2824
- typeof args.namespace === "string" && args.namespace.length > 0
2825
- ? args.namespace
2826
- : undefined;
2827
- // `budget` may arrive as a JSON number or a string ('4096')
2828
- // from loosely-typed MCP clients; coerce + validate here so
2829
- // the service sees a number. Reject booleans, objects, and
2830
- // other non-string-non-number inputs explicitly — `Number()`
2831
- // otherwise coerces `true` to `1`, which would silently force
2832
- // an extremely small recall budget (CLAUDE.md rule 51).
2833
- let budget: number | undefined;
2834
- if (args.budget !== undefined && args.budget !== null) {
2835
- if (typeof args.budget !== "number" && typeof args.budget !== "string") {
2836
- throw new Error(
2837
- `engram.recall_xray: budget expects a positive integer; got ${JSON.stringify(args.budget)}`,
2838
- );
2839
- }
2840
- const parsed =
2841
- typeof args.budget === "number"
2842
- ? args.budget
2843
- : Number(args.budget);
2844
- if (
2845
- !Number.isFinite(parsed)
2846
- || parsed <= 0
2847
- || !Number.isInteger(parsed)
2848
- ) {
2849
- throw new Error(
2850
- `engram.recall_xray: budget expects a positive integer; got ${JSON.stringify(args.budget)}`,
2851
- );
2852
- }
2853
- budget = parsed;
2854
- }
2855
- // Forward disclosure depth so the recallXray telemetry table is
2856
- // populated for MCP callers (issue #677 PR 3/4). Reject
2857
- // non-string types explicitly (matches the strict input
2858
- // contract used elsewhere in this handler — see `budget` /
2859
- // `disclosure` in engram.recall around line 1198 — and the
2860
- // HTTP path's 400-on-bad-disclosure handling). Treat empty
2861
- // string as absent so HTTP `?disclosure=` and MCP align on
2862
- // the same observable contract for that pathological input.
2863
- // Non-empty strings flow through to the service's strict
2864
- // allow-list validator (which throws on unknown values).
2865
- let disclosure: string | undefined;
2866
- if (
2867
- "disclosure" in args &&
2868
- args.disclosure !== undefined &&
2869
- args.disclosure !== null &&
2870
- args.disclosure !== ""
2871
- ) {
2872
- if (typeof args.disclosure !== "string") {
2873
- throw new Error(
2874
- "engram.recall_xray: disclosure must be a string (one of: chunk, section, raw)",
2875
- );
2876
- }
2877
- disclosure = args.disclosure;
2878
- }
2879
- return this.service.recallXray({
2880
- query,
2881
- sessionKey,
2882
- namespace,
2883
- budget,
2884
- authenticatedPrincipal: effectivePrincipal,
2885
- ...(disclosure !== undefined
2886
- ? { disclosure: disclosure as import("./types.js").RecallDisclosure }
2887
- : {}),
2888
- });
2889
- }
2890
- case "engram.wearables_status":
2891
- return this.service.wearablesStatus();
2892
- case "engram.wearables_sync": {
2893
- const source = optionalNonEmptyString(args.source, "engram.wearables_sync: source");
2894
- const date = optionalNonEmptyString(args.date, "engram.wearables_sync: date");
2895
- const days = optionalPositiveInteger(args.days, "engram.wearables_sync: days");
2896
- let forceMemories: boolean | undefined;
2897
- if (args.forceMemories !== undefined && args.forceMemories !== null) {
2898
- if (typeof args.forceMemories !== "boolean") {
2899
- throw new Error(
2900
- `engram.wearables_sync: forceMemories expects a boolean; got ${JSON.stringify(args.forceMemories)}`,
2901
- );
2902
- }
2903
- forceMemories = args.forceMemories;
2904
- }
2905
- // Date/days/source validation beyond shape lives in the shared
2906
- // WearablesService so CLI/HTTP/MCP reject identically.
2907
- return this.service.wearablesSync({ source, date, days, forceMemories });
2908
- }
2909
- case "engram.transcript_day": {
2910
- const date = typeof args.date === "string" ? args.date : "";
2911
- if (date.trim().length === 0) {
2912
- throw new Error(
2913
- "engram.transcript_day: date is required and must be YYYY-MM-DD",
2914
- );
2915
- }
2916
- const source = optionalNonEmptyString(args.source, "engram.transcript_day: source");
2917
- return this.service.wearablesTranscriptDay({ date, source });
2918
- }
2919
- case "engram.transcript_search": {
2920
- const query = typeof args.query === "string" ? args.query : "";
2921
- if (query.trim().length === 0) {
2922
- throw new Error(
2923
- "engram.transcript_search: query is required and must be non-empty",
2924
- );
2925
- }
2926
- return this.service.wearablesTranscriptSearch({
2927
- query,
2928
- source: optionalNonEmptyString(args.source, "engram.transcript_search: source"),
2929
- from: optionalNonEmptyString(args.from, "engram.transcript_search: from"),
2930
- to: optionalNonEmptyString(args.to, "engram.transcript_search: to"),
2931
- limit: optionalPositiveInteger(args.limit, "engram.transcript_search: limit"),
2932
- });
2933
- }
2934
- case "engram.transcript_memories":
2935
- return this.service.wearablesTranscriptMemories({
2936
- source: optionalNonEmptyString(args.source, "engram.transcript_memories: source"),
2937
- date: optionalNonEmptyString(args.date, "engram.transcript_memories: date"),
2938
- limit: optionalPositiveInteger(args.limit, "engram.transcript_memories: limit"),
2939
- });
2940
- case "engram.action_confidence": {
2941
- const body: ActionConfidenceRequest = parseMcpRequest("actionConfidence", args);
2942
- return this.service.actionConfidence(body);
2943
- }
2944
- case REMNIC_CHATGPT_MEMORY_INSPECTOR_TOOL: {
2945
- if (typeof args.query !== "string" || args.query.trim().length === 0) {
2946
- throw new EngramAccessInputError(
2947
- "chatgpt_memory_inspector requires a non-empty query string",
2948
- );
2949
- }
2950
- if (
2951
- "sessionKey" in args &&
2952
- args.sessionKey !== undefined &&
2953
- args.sessionKey !== null &&
2954
- typeof args.sessionKey !== "string"
2955
- ) {
2956
- throw new EngramAccessInputError("sessionKey must be a string");
2957
- }
2958
- if (
2959
- "namespace" in args &&
2960
- args.namespace !== undefined &&
2961
- args.namespace !== null &&
2962
- typeof args.namespace !== "string"
2963
- ) {
2964
- throw new EngramAccessInputError("namespace must be a string");
2965
- }
2966
- let currentContextScopes: string[] | undefined;
2967
- if (args.currentContextScopes !== undefined && args.currentContextScopes !== null) {
2968
- if (
2969
- !Array.isArray(args.currentContextScopes) ||
2970
- !args.currentContextScopes.every((scope) => typeof scope === "string")
2971
- ) {
2972
- throw new EngramAccessInputError(
2973
- "currentContextScopes must be an array of strings",
2974
- );
2975
- }
2976
- currentContextScopes = args.currentContextScopes;
2977
- }
2978
-
2979
- const input: RemnicChatGptMemoryInspectorInput = {
2980
- query: args.query.trim(),
2981
- };
2982
- if (typeof args.sessionKey === "string" && args.sessionKey.trim().length > 0) {
2983
- input.sessionKey = args.sessionKey;
2984
- }
2985
- if (typeof args.namespace === "string" && args.namespace.trim().length > 0) {
2986
- input.namespace = args.namespace;
2987
- }
2988
- if (currentContextScopes !== undefined) {
2989
- input.currentContextScopes = currentContextScopes;
2990
- }
2991
- if (
2992
- args.allowUnverifiedPreview !== undefined &&
2993
- args.allowUnverifiedPreview !== null
2994
- ) {
2995
- if (typeof args.allowUnverifiedPreview !== "boolean") {
2996
- throw new EngramAccessInputError(
2997
- "allowUnverifiedPreview must be a boolean",
2998
- );
2999
- }
3000
- input.allowUnverifiedPreview = args.allowUnverifiedPreview;
3001
- }
3002
- const recallSessionKey = resolveChatGptInspectorRecallSessionKey(
3003
- input.sessionKey,
3004
- effectivePrincipal,
3005
- );
3006
- const xrayResponse = await this.service.recallXray({
3007
- query: input.query,
3008
- sessionKey: recallSessionKey,
3009
- namespace: input.namespace,
3010
- currentContextScopes: input.currentContextScopes,
3011
- authenticatedPrincipal: effectivePrincipal,
3012
- mode: "full",
3013
- disclosure: "chunk",
3014
- includeRecall: true,
3015
- });
3016
- const xray = xrayResponse.snapshotFound === true
3017
- ? xrayResponse.snapshot ?? null
3018
- : null;
3019
- const recall = xrayResponse.recall ?? {
3020
- query: input.query,
3021
- namespace: input.namespace ?? xray?.namespace ?? "global",
3022
- context: "",
3023
- count: 0,
3024
- memoryIds: [],
3025
- results: [],
3026
- fallbackUsed: false,
3027
- sourcesUsed: [],
3028
- disclosure: "chunk",
3029
- };
3030
- const actionRequest = buildChatGptMemoryInspectorActionRequest(
3031
- input,
3032
- recall,
3033
- xray,
3034
- );
3035
- const actionConfidence = await this.service.actionConfidence(actionRequest);
3036
- return buildChatGptMemoryInspectorResult(
3037
- input,
3038
- recall,
3039
- xray,
3040
- actionConfidence,
3041
- );
3042
- }
3043
- case "engram.day_summary": {
3044
- const body: DaySummaryRequest = parseMcpRequest("daySummary", args);
3045
- return this.service.daySummary(body);
3046
- }
3047
- case "engram.capsule_export": {
3048
- const body: CapsuleExportRequest = parseMcpRequest("capsuleExport", args);
3049
- return this.service.capsuleExport({
3050
- name: body.name,
3051
- namespace: body.namespace,
3052
- principal: effectivePrincipal,
3053
- since: body.since,
3054
- includeKinds: body.includeKinds,
3055
- peerIds: body.peerIds,
3056
- includeTranscripts: body.includeTranscripts,
3057
- encrypt: body.encrypt,
3058
- });
3059
- }
3060
- case "engram.capsule_import": {
3061
- const body: CapsuleImportRequest = parseMcpRequest("capsuleImport", args);
3062
- return this.service.capsuleImport({
3063
- archivePath: expandTildePath(body.archivePath),
3064
- namespace: body.namespace,
3065
- principal: effectivePrincipal,
3066
- mode: body.mode,
3067
- passphrase: body.passphrase,
3068
- });
3069
- }
3070
- case "engram.capsule_list": {
3071
- const body: CapsuleListRequest = parseMcpRequest("capsuleList", args);
3072
- const requestPrincipal =
3073
- effectivePrincipal ??
3074
- (body.sessionKey && this.service.configRef
3075
- ? resolvePrincipal(body.sessionKey, this.service.configRef)
3076
- : undefined);
3077
- return this.service.capsuleList({
3078
- namespace: body.namespace,
3079
- principal: requestPrincipal,
3080
- });
3081
- }
3082
- case "engram.memory_governance_run":
3083
- return this.service.governanceRun({
3084
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3085
- mode: args.mode === "apply" ? "apply" : "shadow",
3086
- recentDays: typeof args.recentDays === "number" && Number.isFinite(args.recentDays) ? args.recentDays : undefined,
3087
- maxMemories: typeof args.maxMemories === "number" && Number.isFinite(args.maxMemories) ? args.maxMemories : undefined,
3088
- batchSize: typeof args.batchSize === "number" && Number.isFinite(args.batchSize) ? args.batchSize : undefined,
3089
- authenticatedPrincipal: effectivePrincipal,
3090
- }, effectivePrincipal);
3091
- case "engram.procedure_mining_run":
3092
- return this.service.procedureMiningRun(
3093
- {
3094
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3095
- authenticatedPrincipal: effectivePrincipal,
3096
- },
3097
- effectivePrincipal,
3098
- );
3099
- case "engram.pattern_reinforcement_run":
3100
- return this.service.patternReinforcementRun(
3101
- {
3102
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3103
- authenticatedPrincipal: effectivePrincipal,
3104
- force: args.force === true,
3105
- },
3106
- effectivePrincipal,
3107
- );
3108
- case "remnic.procedural_stats":
3109
- case "engram.procedural_stats":
3110
- return this.service.procedureStats(
3111
- {
3112
- namespace:
3113
- typeof args.namespace === "string" ? args.namespace : undefined,
3114
- },
3115
- effectivePrincipal,
3116
- );
3117
- case "engram.memory_timeline": {
3118
- const limit = typeof args.limit === "number" && Number.isFinite(args.limit) ? args.limit : 200;
3119
- return this.service.memoryTimeline(
3120
- typeof args.memoryId === "string" ? args.memoryId : "",
3121
- typeof args.namespace === "string" ? args.namespace : undefined,
3122
- limit,
3123
- effectivePrincipal,
3124
- );
3125
- }
3126
- case "engram.suggestion_submit": {
3127
- const body: SuggestionSubmitRequest = parseMcpRequest("suggestionSubmit", args);
3128
- return this.service.suggestionSubmit({
3129
- schemaVersion: body.schemaVersion,
3130
- idempotencyKey: body.idempotencyKey,
3131
- dryRun: body.dryRun,
3132
- sessionKey: body.sessionKey,
3133
- authenticatedPrincipal: effectivePrincipal,
3134
- content: body.content,
3135
- category: body.category,
3136
- confidence: body.confidence,
3137
- namespace: body.namespace,
3138
- tags: body.tags,
3139
- entityRef: body.entityRef,
3140
- ttl: body.ttl,
3141
- sourceReason: body.sourceReason,
3142
- cwd: body.cwd,
3143
- projectTag: body.projectTag,
3144
- });
3145
- }
3146
- case "engram.entity_get":
3147
- return this.service.entityGet(
3148
- typeof args.name === "string" ? args.name : "",
3149
- typeof args.namespace === "string" ? args.namespace : undefined,
3150
- );
3151
- case "engram.review_queue_list":
3152
- return this.service.reviewQueue(
3153
- typeof args.runId === "string" ? args.runId : undefined,
3154
- typeof args.namespace === "string" ? args.namespace : undefined,
3155
- effectivePrincipal,
3156
- );
3157
- case "engram.observe": {
3158
- const body = parseMcpRequest("observe", args);
3159
- return this.service.observe(
3160
- {
3161
- sessionKey: body.sessionKey,
3162
- messages: body.messages.map((message) => ({
3163
- role: message.role,
3164
- content: message.content,
3165
- parts: message.parts ?? undefined,
3166
- rawContent: message.rawContent ?? undefined,
3167
- sourceFormat: message.sourceFormat ?? undefined,
3168
- })),
3169
- skipExtraction: body.skipExtraction === true,
3170
- idempotencyKey: body.idempotencyKey,
3171
- namespace: body.namespace,
3172
- authenticatedPrincipal: effectivePrincipal,
3173
- cwd: body.cwd,
3174
- projectTag: body.projectTag,
3175
- },
3176
- // Forward the transport's write-quota enforcer into observe's
3177
- // idempotency lock so a replay is never 429'd (issue #1649).
3178
- enforceWriteQuota ? { enforceWriteQuota } : undefined,
3179
- );
3180
- }
3181
- case "engram.lcm_search":
3182
- return this.service.lcmSearch({
3183
- query: typeof args.query === "string" ? args.query : "",
3184
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : undefined,
3185
- sessionPrefix: typeof args.sessionPrefix === "string" ? args.sessionPrefix : undefined,
3186
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3187
- limit: typeof args.limit === "number" && Number.isFinite(args.limit) ? args.limit : undefined,
3188
- authenticatedPrincipal: effectivePrincipal,
3189
- });
3190
- case "engram.lcm_compaction_flush": {
3191
- const body = parseMcpRequest("lcmCompactionFlush", args);
3192
- return this.service.lcmCompactionFlush({
3193
- sessionKey: body.sessionKey,
3194
- namespace: body.namespace,
3195
- authenticatedPrincipal: effectivePrincipal,
3196
- });
3197
- }
3198
- case "engram.lcm_compaction_record": {
3199
- const body = parseMcpRequest("lcmCompactionRecord", args);
3200
- return this.service.lcmCompactionRecord({
3201
- sessionKey: body.sessionKey,
3202
- namespace: body.namespace,
3203
- tokensBefore: body.tokensBefore,
3204
- tokensAfter: body.tokensAfter,
3205
- authenticatedPrincipal: effectivePrincipal,
3206
- });
3207
- }
3208
- // ── Continuity / Identity tools ───────────────────────────────────
3209
- case "engram.continuity_audit_generate":
3210
- return this.service.continuityAuditGenerate({
3211
- period: args.period === "monthly" ? "monthly" : args.period === "weekly" ? "weekly" : undefined,
3212
- key: typeof args.key === "string" ? args.key : undefined,
3213
- });
3214
- case "engram.continuity_incident_open":
3215
- return this.service.continuityIncidentOpen({
3216
- symptom: typeof args.symptom === "string" ? args.symptom : "",
3217
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3218
- principal: effectivePrincipal,
3219
- triggerWindow: typeof args.triggerWindow === "string" ? args.triggerWindow : undefined,
3220
- suspectedCause: typeof args.suspectedCause === "string" ? args.suspectedCause : undefined,
3221
- });
3222
- case "engram.continuity_incident_close":
3223
- return this.service.continuityIncidentClose({
3224
- id: typeof args.id === "string" ? args.id : "",
3225
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3226
- principal: effectivePrincipal,
3227
- fixApplied: typeof args.fixApplied === "string" ? args.fixApplied : "",
3228
- verificationResult: typeof args.verificationResult === "string" ? args.verificationResult : "",
3229
- preventiveRule: typeof args.preventiveRule === "string" ? args.preventiveRule : undefined,
3230
- });
3231
- case "engram.continuity_incident_list":
3232
- return this.service.continuityIncidentList({
3233
- state: args.state === "closed" ? "closed" : args.state === "all" ? "all" : args.state === "open" ? "open" : undefined,
3234
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3235
- principal: effectivePrincipal,
3236
- limit: typeof args.limit === "number" ? args.limit : undefined,
3237
- });
3238
- case "engram.continuity_loop_add_or_update":
3239
- return this.service.continuityLoopAddOrUpdate({
3240
- id: typeof args.id === "string" ? args.id : "",
3241
- cadence: (args.cadence as "daily" | "weekly" | "monthly" | "quarterly") ?? "weekly",
3242
- purpose: typeof args.purpose === "string" ? args.purpose : "",
3243
- status: (args.status as "active" | "paused" | "retired") ?? "active",
3244
- killCondition: typeof args.killCondition === "string" ? args.killCondition : "",
3245
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3246
- principal: effectivePrincipal,
3247
- lastReviewed: typeof args.lastReviewed === "string" ? args.lastReviewed : undefined,
3248
- notes: typeof args.notes === "string" ? args.notes : undefined,
3249
- });
3250
- case "engram.continuity_loop_review":
3251
- return this.service.continuityLoopReview({
3252
- id: typeof args.id === "string" ? args.id : "",
3253
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3254
- principal: effectivePrincipal,
3255
- status: args.status === "active" || args.status === "paused" || args.status === "retired" ? args.status : undefined,
3256
- notes: typeof args.notes === "string" ? args.notes : undefined,
3257
- reviewedAt: typeof args.reviewedAt === "string" ? args.reviewedAt : undefined,
3258
- });
3259
- case "engram.identity_anchor_get":
3260
- return this.service.identityAnchorGet({
3261
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3262
- principal: effectivePrincipal,
3263
- });
3264
- case "engram.identity_anchor_update":
3265
- return this.service.identityAnchorUpdate({
3266
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3267
- principal: effectivePrincipal,
3268
- identityTraits: typeof args.identityTraits === "string" ? args.identityTraits : undefined,
3269
- communicationPreferences: typeof args.communicationPreferences === "string" ? args.communicationPreferences : undefined,
3270
- operatingPrinciples: typeof args.operatingPrinciples === "string" ? args.operatingPrinciples : undefined,
3271
- continuityNotes: typeof args.continuityNotes === "string" ? args.continuityNotes : undefined,
3272
- });
3273
- case "engram.memory_identity":
3274
- return this.service.memoryIdentity({
3275
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3276
- principal: effectivePrincipal,
3277
- });
3278
- // ── Work Layer tools ──────────────────────────────────────────────
3279
- case "engram.work_task":
3280
- return this.service.workTask({
3281
- action: (args.action as any) ?? "list",
3282
- id: typeof args.id === "string" ? args.id : undefined,
3283
- title: typeof args.title === "string" ? args.title : undefined,
3284
- description: typeof args.description === "string" ? args.description : undefined,
3285
- status: typeof args.status === "string" ? args.status : undefined,
3286
- priority: typeof args.priority === "string" ? args.priority : undefined,
3287
- owner: typeof args.owner === "string" ? args.owner : undefined,
3288
- assignee: typeof args.assignee === "string" ? args.assignee : undefined,
3289
- projectId: typeof args.projectId === "string" ? args.projectId : undefined,
3290
- tags: Array.isArray(args.tags) ? args.tags.filter((x: unknown): x is string => typeof x === "string") : undefined,
3291
- dueAt: typeof args.dueAt === "string" ? args.dueAt : undefined,
3292
- });
3293
- case "engram.work_project":
3294
- return this.service.workProject({
3295
- action: (args.action as any) ?? "list",
3296
- id: typeof args.id === "string" ? args.id : undefined,
3297
- name: typeof args.name === "string" ? args.name : undefined,
3298
- description: typeof args.description === "string" ? args.description : undefined,
3299
- status: typeof args.status === "string" ? args.status : undefined,
3300
- owner: typeof args.owner === "string" ? args.owner : undefined,
3301
- tags: Array.isArray(args.tags) ? args.tags.filter((x: unknown): x is string => typeof x === "string") : undefined,
3302
- taskId: typeof args.taskId === "string" ? args.taskId : undefined,
3303
- projectId: typeof args.projectId === "string" ? args.projectId : undefined,
3304
- });
3305
- case "engram.work_board":
3306
- return this.service.workBoard({
3307
- action: (args.action as any) ?? "export_markdown",
3308
- projectId: typeof args.projectId === "string" ? args.projectId : undefined,
3309
- snapshotJson: typeof args.snapshotJson === "string" ? args.snapshotJson : undefined,
3310
- linkToMemory: args.linkToMemory === true,
3311
- });
3312
- // ── Shared Context / Compounding tools ─────────────────────────
3313
- case "engram.shared_context_write_output":
3314
- return this.service.sharedContextWriteOutput({
3315
- agentId: typeof args.agentId === "string" ? args.agentId : "",
3316
- title: typeof args.title === "string" ? args.title : "",
3317
- content: typeof args.content === "string" ? args.content : "",
3318
- });
3319
- case "engram.shared_feedback_record":
3320
- return this.service.sharedFeedbackRecord({
3321
- agent: typeof args.agent === "string" ? args.agent : "",
3322
- decision: (args.decision as any) ?? "approved",
3323
- reason: typeof args.reason === "string" ? args.reason : "",
3324
- date: typeof args.date === "string" ? args.date : undefined,
3325
- learning: typeof args.learning === "string" ? args.learning : undefined,
3326
- outcome: typeof args.outcome === "string" ? args.outcome : undefined,
3327
- severity: args.severity === "low" || args.severity === "medium" || args.severity === "high" ? args.severity : undefined,
3328
- confidence: typeof args.confidence === "number" ? args.confidence : undefined,
3329
- workflow: typeof args.workflow === "string" ? args.workflow : undefined,
3330
- tags: Array.isArray(args.tags) ? args.tags.filter((x: unknown): x is string => typeof x === "string") : undefined,
3331
- evidenceWindowStart: typeof args.evidenceWindowStart === "string" ? args.evidenceWindowStart : undefined,
3332
- evidenceWindowEnd: typeof args.evidenceWindowEnd === "string" ? args.evidenceWindowEnd : undefined,
3333
- refs: Array.isArray(args.refs) ? args.refs.filter((x: unknown): x is string => typeof x === "string") : undefined,
3334
- });
3335
- case "engram.shared_priorities_append":
3336
- return this.service.sharedPrioritiesAppend({
3337
- agentId: typeof args.agentId === "string" ? args.agentId : "",
3338
- text: typeof args.text === "string" ? args.text : "",
3339
- });
3340
- case "engram.shared_context_cross_signals_run":
3341
- return this.service.sharedContextCrossSignalsRun({
3342
- date: typeof args.date === "string" ? args.date : undefined,
3343
- });
3344
- case "engram.shared_context_curate_daily":
3345
- return this.service.sharedContextCurateDaily({
3346
- date: typeof args.date === "string" ? args.date : undefined,
3347
- });
3348
- case "engram.compounding_weekly_synthesize":
3349
- return this.service.compoundingWeeklySynthesize({
3350
- weekId: typeof args.weekId === "string" ? args.weekId : undefined,
3351
- });
3352
- case "engram.compounding_promote_candidate":
3353
- return this.service.compoundingPromoteCandidate({
3354
- weekId: typeof args.weekId === "string" ? args.weekId : "",
3355
- candidateId: typeof args.candidateId === "string" ? args.candidateId : "",
3356
- dryRun: args.dryRun === true,
3357
- });
3358
- // ── Compression Guidelines tools ───────────────────────────────────
3359
- case "engram.compression_guidelines_optimize":
3360
- return this.service.compressionGuidelinesOptimize({
3361
- dryRun: args.dryRun === true,
3362
- eventLimit: typeof args.eventLimit === "number" ? args.eventLimit : undefined,
3363
- });
3364
- case "engram.compression_guidelines_activate":
3365
- return this.service.compressionGuidelinesActivate({
3366
- expectedContentHash: typeof args.expectedContentHash === "string" ? args.expectedContentHash : undefined,
3367
- expectedGuidelineVersion: typeof args.expectedGuidelineVersion === "number" ? args.expectedGuidelineVersion : undefined,
3368
- });
3369
- // ── Memory search & debug tools ──────────────────────────────────
3370
- case "engram.memory_profile":
3371
- return this.service.memoryProfile(
3372
- typeof args.namespace === "string" ? args.namespace : undefined,
3373
- effectivePrincipal,
3374
- );
3375
- case "engram.memory_entities_list":
3376
- return this.service.memoryEntitiesList(
3377
- typeof args.namespace === "string" ? args.namespace : undefined,
3378
- effectivePrincipal,
3379
- );
3380
- case "engram.memory_questions":
3381
- return this.service.memoryQuestions(
3382
- typeof args.namespace === "string" ? args.namespace : undefined,
3383
- effectivePrincipal,
3384
- );
3385
- case "engram.memory_last_recall":
3386
- return this.service.lastRecallSnapshot(
3387
- typeof args.sessionKey === "string" ? args.sessionKey : undefined,
3388
- );
3389
- case "engram.memory_intent_debug":
3390
- return this.service.intentDebug(
3391
- typeof args.namespace === "string" ? args.namespace : undefined,
3392
- );
3393
- case "engram.memory_qmd_debug":
3394
- return this.service.qmdDebug(
3395
- typeof args.namespace === "string" ? args.namespace : undefined,
3396
- );
3397
- case "engram.memory_graph_explain":
3398
- return this.service.graphExplainLastRecall(
3399
- typeof args.namespace === "string" ? args.namespace : undefined,
3400
- );
3401
- case "engram.graph_snapshot": {
3402
- // Validate the typed inputs at the boundary — silently coercing
3403
- // unknown shapes (e.g. `limit: "200"`) would defeat the access
3404
- // service's tighter parser (CLAUDE.md rule 28 + 51).
3405
- if (args.limit !== undefined && typeof args.limit !== "number") {
3406
- throw new Error("engram.graph_snapshot: limit must be a number");
3407
- }
3408
- if (args.since !== undefined && typeof args.since !== "string") {
3409
- throw new Error("engram.graph_snapshot: since must be a string");
3410
- }
3411
- if (
3412
- args.focusNodeId !== undefined
3413
- && typeof args.focusNodeId !== "string"
3414
- ) {
3415
- throw new Error("engram.graph_snapshot: focusNodeId must be a string");
3416
- }
3417
- let categories: string[] | undefined;
3418
- if (args.categories !== undefined) {
3419
- if (!Array.isArray(args.categories)) {
3420
- throw new Error(
3421
- "engram.graph_snapshot: categories must be an array of strings",
3422
- );
3423
- }
3424
- categories = args.categories.map((value, index) => {
3425
- if (typeof value !== "string") {
3426
- throw new Error(
3427
- `engram.graph_snapshot: categories[${index}] must be a string`,
3428
- );
3429
- }
3430
- return value;
3431
- });
3432
- }
3433
- return this.service.graphSnapshot(
3434
- {
3435
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3436
- limit: typeof args.limit === "number" ? args.limit : undefined,
3437
- since: typeof args.since === "string" ? args.since : undefined,
3438
- focusNodeId: typeof args.focusNodeId === "string"
3439
- ? args.focusNodeId
3440
- : undefined,
3441
- ...(categories !== undefined ? { categories } : {}),
3442
- },
3443
- effectivePrincipal,
3444
- );
3445
- }
3446
- case "engram.memory_feedback":
3447
- return this.service.memoryFeedback({
3448
- memoryId: typeof args.memoryId === "string" ? args.memoryId : "",
3449
- vote: args.vote === "down" ? "down" : "up",
3450
- note: typeof args.note === "string" ? args.note : undefined,
3451
- });
3452
- case "engram.memory_promote":
3453
- return this.service.memoryPromote({
3454
- memoryId: typeof args.memoryId === "string" ? args.memoryId : "",
3455
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3456
- principal: effectivePrincipal,
3457
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : undefined,
3458
- });
3459
- case "engram.memory_outcome": {
3460
- // Validate `outcome` up front — silently defaulting unknown values
3461
- // to "success" or "failure" would poison the counters a downstream
3462
- // recall filter (PR 4) will trust.
3463
- const outcome = args.outcome;
3464
- if (outcome !== "success" && outcome !== "failure") {
3465
- throw new Error(
3466
- `engram.memory_outcome: outcome must be "success" or "failure"; got ${JSON.stringify(outcome)}`,
3467
- );
3468
- }
3469
- return this.service.memoryOutcome({
3470
- memoryId: typeof args.memoryId === "string" ? args.memoryId : "",
3471
- outcome,
3472
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3473
- principal: effectivePrincipal,
3474
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : undefined,
3475
- timestamp: typeof args.timestamp === "string" ? args.timestamp : undefined,
3476
- });
3477
- }
3478
- case "engram.memory_action_apply":
3479
- return this.service.memoryActionApply({
3480
- action: typeof args.action === "string" ? args.action : "",
3481
- outcome: typeof args.outcome === "string" ? args.outcome : undefined,
3482
- reason: typeof args.reason === "string" ? args.reason : undefined,
3483
- memoryId: typeof args.memoryId === "string" ? args.memoryId : undefined,
3484
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3485
- principal: effectivePrincipal,
3486
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : undefined,
3487
- content: typeof args.content === "string" ? args.content : undefined,
3488
- category: typeof args.category === "string" ? args.category : undefined,
3489
- linkTargetId: typeof args.linkTargetId === "string" ? args.linkTargetId : undefined,
3490
- linkType: typeof args.linkType === "string" ? args.linkType : undefined,
3491
- linkStrength: typeof args.linkStrength === "number" ? args.linkStrength : undefined,
3492
- artifactType: typeof args.artifactType === "string" ? args.artifactType : undefined,
3493
- execute: typeof args.execute === "boolean" ? args.execute : undefined,
3494
- sourcePrompt: typeof args.sourcePrompt === "string" ? args.sourcePrompt : undefined,
3495
- dryRun: args.dryRun === true,
3496
- });
3497
- case "engram.context_checkpoint":
3498
- return this.service.contextCheckpoint({
3499
- sessionKey: typeof args.sessionKey === "string" ? args.sessionKey : "",
3500
- context: typeof args.context === "string" ? args.context : "",
3501
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3502
- principal: effectivePrincipal,
3503
- });
3504
- // ── Daily Context Briefing (#370) ───────────────────────────────────
3505
- case "engram.briefing": {
3506
- // Validate the format value upfront — unsupported values (e.g. "xml")
3507
- // must be rejected with a descriptive error rather than silently
3508
- // falling back to the default format.
3509
- const rawFormat = typeof args.format === "string" ? args.format : undefined;
3510
- const formatErr = validateBriefingFormat(rawFormat);
3511
- if (formatErr) throw new Error(formatErr);
3512
- return this.service.briefing({
3513
- since: typeof args.since === "string" ? args.since : undefined,
3514
- focus: typeof args.focus === "string" ? args.focus : undefined,
3515
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3516
- format: rawFormat as "json" | "markdown" | undefined,
3517
- maxFollowups:
3518
- typeof args.maxFollowups === "number" ? args.maxFollowups : undefined,
3519
- principal: effectivePrincipal,
3520
- });
3521
- }
3522
- // ── Contradiction Review (issue #520) ──────────────────────────────────
3523
- case "engram.review_list":
3524
- case "remnic.review_list": {
3525
- const {
3526
- isDefaultReviewNamespace,
3527
- listPairs,
3528
- } = await import("./contradiction/contradiction-review.js");
3529
- const VALID_REVIEW_FILTERS = new Set(["all", "unresolved", "contradicts", "independent", "duplicates", "needs-user"]);
3530
- const rawFilter = typeof args.filter === "string" ? args.filter : "unresolved";
3531
- if (!VALID_REVIEW_FILTERS.has(rawFilter)) {
3532
- throw new Error(`Invalid filter '${rawFilter}'. Valid: ${[...VALID_REVIEW_FILTERS].join(", ")}`);
3533
- }
3534
- const filter = rawFilter as "all" | "unresolved" | "contradicts" | "independent" | "duplicates" | "needs-user";
3535
- const ns = typeof args.namespace === "string" ? args.namespace : undefined;
3536
- const limit = typeof args.limit === "number" ? args.limit : 50;
3537
- const resolved = await this.service.getReadableStorageForNamespace(ns, effectivePrincipal);
3538
- const reviewNamespace = this.service.configRef.namespacesEnabled ? resolved.namespace : undefined;
3539
- const includeUnscopedForNamespace = Boolean(
3540
- reviewNamespace && isDefaultReviewNamespace(this.service.configRef.defaultNamespace, ns, reviewNamespace),
3541
- );
3542
- return listPairs(this.service.memoryDir, {
3543
- filter,
3544
- namespace: reviewNamespace,
3545
- includeUnscopedForNamespace,
3546
- limit,
3547
- });
3548
- }
3549
- case "engram.review_resolve":
3550
- case "remnic.review_resolve": {
3551
- const pairId = typeof args.pairId === "string" ? args.pairId : "";
3552
- const verb = typeof args.verb === "string" ? args.verb : "";
3553
- if (!pairId) throw new Error("pairId is required");
3554
- if (!verb) throw new Error("verb is required");
3555
- const { isValidResolutionVerb } = await import("./contradiction/resolution.js");
3556
- if (!isValidResolutionVerb(verb)) throw new Error(`Invalid verb: ${verb}. Must be one of: keep-a, keep-b, merge, both-valid, needs-more-context`);
3557
- const { executeResolution } = await import("./contradiction/resolution.js");
3558
- return executeResolution(this.service.memoryDir, this.service.storageRef, pairId, verb, {
3559
- mergedMemoryId: typeof args.mergedMemoryId === "string" ? args.mergedMemoryId : undefined,
3560
- mergedContent: typeof args.mergedContent === "string" ? args.mergedContent : undefined,
3561
- storageForNamespace: async (namespace) => {
3562
- const resolved = await this.service.getWritableStorageForNamespace(namespace, effectivePrincipal);
3563
- return resolved.storage;
3564
- },
3565
- // Catalog write touch (issue #1499 sweep): a contradiction merge writes
3566
- // a new memory directly to the resolved namespace storage, bypassing the
3567
- // extraction write path. Record it so QMD maintenance / writtenSince
3568
- // don't miss the write. Best-effort and failure-tolerant.
3569
- onMergedMemoryWritten: (namespace, storageDir) => {
3570
- // #1522: catalog touch handled at the storage chokepoint.
3571
- },
3572
- });
3573
- }
3574
- case "engram.contradiction_scan_run":
3575
- case "remnic.contradiction_scan_run": {
3576
- const { runContradictionScan } = await import("./contradiction/contradiction-scan.js");
3577
- return runContradictionScan({
3578
- storage: this.service.storageRef,
3579
- config: this.service.configRef,
3580
- memoryDir: this.service.memoryDir,
3581
- embeddingLookupFactory: this.service.embeddingLookupFactoryRef,
3582
- storageForNamespace: (namespace) =>
3583
- this.service.getWritableStorageForNamespace(namespace, effectivePrincipal),
3584
- localLlm: this.service.localLlmRef,
3585
- fallbackLlm: this.service.fallbackLlmRef,
3586
- namespace: typeof args.namespace === "string" ? args.namespace : undefined,
3587
- });
3588
- }
3589
- case "engram.memory_summarize_hourly":
3590
- case "remnic.memory_summarize_hourly":
3591
- return this.service.memorySummarizeHourly();
3592
- case "engram.conversation_index_update":
3593
- case "remnic.conversation_index_update": {
3594
- if ("sessionKey" in args && args.sessionKey !== undefined && typeof args.sessionKey !== "string") {
3595
- throw new Error("sessionKey must be a string when provided");
3596
- }
3597
- const sessionKey = typeof args.sessionKey === "string" ? args.sessionKey : undefined;
3598
- return this.service.conversationIndexUpdate({
3599
- sessionKey,
3600
- hours: typeof args.hours === "number" && Number.isFinite(args.hours) ? args.hours : undefined,
3601
- embed: typeof args.embed === "boolean" ? args.embed : undefined,
3602
- });
3603
- }
3604
- case "engram.profiling_report":
3605
- case "remnic.profiling_report": {
3606
- if ("format" in args && args.format !== undefined && typeof args.format !== "string") {
3607
- throw new EngramAccessInputError("format must be a string when provided");
3608
- }
3609
- if ("limit" in args && args.limit !== undefined && typeof args.limit !== "number") {
3610
- throw new EngramAccessInputError("limit must be a number when provided");
3611
- }
3612
- return this.service.profilingReport({
3613
- format: typeof args.format === "string" ? args.format : undefined,
3614
- limit: typeof args.limit === "number" ? args.limit : undefined,
3615
- });
3616
- }
3617
- case "engram.graph_edge_decay_run":
3618
- case "remnic.graph_edge_decay_run": {
3619
- // Issue #681 PR 2/3 — gated by config; tool always callable, but
3620
- // the job is a no-op when disabled (and the response indicates it).
3621
- const cfg = this.service.configRef;
3622
- if (!cfg.graphEdgeDecayEnabled) {
3623
- return {
3624
- ranAt: new Date().toISOString(),
3625
- disabled: true,
3626
- reason: "graphEdgeDecayEnabled is false",
3627
- };
3628
- }
3629
- const { runGraphEdgeDecayMaintenanceAcrossNamespaces } = await import(
3630
- "./maintenance/graph-edge-decay.js"
3631
- );
3632
- const dryRun = args.dryRun === true;
3633
- // Codex P2 / gotcha #42: enumerate every namespace storage root
3634
- // so non-default namespaces (memoryDir/namespaces/<ns>) also get
3635
- // confidence decay applied. Returns a list of per-namespace
3636
- // results — each with telemetry or an error string.
3637
- const results = await runGraphEdgeDecayMaintenanceAcrossNamespaces(
3638
- this.service.memoryDir,
3639
- {
3640
- windowMs: cfg.graphEdgeDecayWindowMs,
3641
- perWindow: cfg.graphEdgeDecayPerWindow,
3642
- floor: cfg.graphEdgeDecayFloor,
3643
- visibilityThreshold: cfg.graphEdgeDecayVisibilityThreshold,
3644
- dryRun,
3645
- namespacesEnabled: cfg.namespacesEnabled === true,
3646
- defaultNamespace: cfg.defaultNamespace,
3647
- },
3648
- );
3649
- return { results };
3650
- }
3651
- case "engram.live_connectors_run":
3652
- case "remnic.live_connectors_run":
3653
- return this.service.liveConnectorsRun(
3654
- {
3655
- authenticatedPrincipal: effectivePrincipal,
3656
- force: args.force === true,
3657
- },
3658
- effectivePrincipal,
3659
- );
3660
- // ── Peer Registry dispatchers (issue #679 PR 4/5) ─────────────────
3661
- case "engram.peer_list":
3662
- case "remnic.peer_list":
3663
- return this.service.peerList();
3664
- case "engram.peer_get":
3665
- case "remnic.peer_get": {
3666
- const id = typeof args.id === "string" ? args.id : "";
3667
- if (!id) throw new Error("engram.peer_get: id is required");
3668
- return this.service.peerGet(id);
3669
- }
3670
- case "engram.peer_set":
3671
- case "remnic.peer_set": {
3672
- const id = typeof args.id === "string" ? args.id : "";
3673
- if (!id) throw new Error("engram.peer_set: id is required");
3674
- // Codex P2 (PR #756 round 2): mirror the HTTP surface — reject
3675
- // non-string `kind`/`displayName`/`notes` rather than silently
3676
- // coercing to `undefined` and letting peerSet fall back to its
3677
- // "human" default. Symmetry across access surfaces (CLAUDE.md
3678
- // rule 39) and no-silent-defaults on bad input (rule 51).
3679
- if (args.kind !== undefined && typeof args.kind !== "string") {
3680
- throw new Error("engram.peer_set: kind must be a string when provided");
3681
- }
3682
- if (args.displayName !== undefined && typeof args.displayName !== "string") {
3683
- throw new Error("engram.peer_set: displayName must be a string when provided");
3684
- }
3685
- if (args.notes !== undefined && typeof args.notes !== "string") {
3686
- throw new Error("engram.peer_set: notes must be a string when provided");
3687
- }
3688
- return this.service.peerSet({
3689
- id,
3690
- kind: typeof args.kind === "string" ? args.kind : undefined,
3691
- displayName: typeof args.displayName === "string" ? args.displayName : undefined,
3692
- notes: typeof args.notes === "string" ? args.notes : undefined,
3693
- });
3694
- }
3695
- case "engram.peer_delete":
3696
- case "remnic.peer_delete": {
3697
- const id = typeof args.id === "string" ? args.id : "";
3698
- if (!id) throw new Error("engram.peer_delete: id is required");
3699
- return this.service.peerDelete(id);
3700
- }
3701
- case "engram.peer_profile_get":
3702
- case "remnic.peer_profile_get": {
3703
- const id = typeof args.id === "string" ? args.id : "";
3704
- if (!id) throw new Error("engram.peer_profile_get: id is required");
3705
- return this.service.peerProfileGet(id);
3706
- }
3707
- case "engram.peer_forget":
3708
- case "remnic.peer_forget": {
3709
- const id = typeof args.id === "string" ? args.id : "";
3710
- if (!id) throw new Error("engram.peer_forget: id is required");
3711
- const confirm = typeof args.confirm === "string" ? args.confirm : "";
3712
- if (confirm !== "yes") {
3713
- throw new Error(
3714
- "engram.peer_forget: confirm must be 'yes' to prevent accidental data loss",
3715
- );
3716
- }
3717
- return this.service.peerForget(id, { confirm: "yes" });
3718
- }
3719
- // ── Operator Console state (issue #688 PR 2/3) ──────────────────────────
3720
- case "engram.console_state":
3721
- case "remnic.console_state":
3722
- return this.service.consoleState(
3723
- typeof args.namespace === "string" ? args.namespace : undefined,
3724
- effectivePrincipal,
3725
- );
3726
- // ── Dreams telemetry (issue #678 PR 3+4) ──────────────────────────────
3727
- case "engram.dreams_status":
3728
- case "remnic.dreams_status": {
3729
- const { normalizeDreamsStatusWindowHours } = await import("./maintenance/dreams-ledger.js");
3730
- let windowHours = 24;
3731
- try {
3732
- windowHours = normalizeDreamsStatusWindowHours(args.windowHours);
3733
- } catch {
3734
- throw new Error(
3735
- `engram.dreams_status: windowHours must be a positive integer (e.g. 24). Got: ${String(args.windowHours)}`,
3736
- );
3737
- }
3738
- if (
3739
- "namespace" in args &&
3740
- args.namespace !== undefined &&
3741
- typeof args.namespace !== "string"
3742
- ) {
3743
- throw new Error("engram.dreams_status: namespace must be a string when provided");
3744
- }
3745
- const namespace =
3746
- typeof args.namespace === "string" ? args.namespace : undefined;
3747
- return this.service.dreamsStatus({
3748
- windowHours,
3749
- namespace,
3750
- principal: effectivePrincipal,
3751
- });
3752
- }
3753
- case "engram.dreams_run":
3754
- case "remnic.dreams_run": {
3755
- const VALID_PHASES = ["lightSleep", "rem", "deepSleep"];
3756
- const phase = typeof args.phase === "string" ? args.phase : "";
3757
- if (!phase || !VALID_PHASES.includes(phase)) {
3758
- throw new Error(
3759
- `engram.dreams_run: phase is required and must be one of: ${VALID_PHASES.join(", ")}`,
3760
- );
3761
- }
3762
- if (
3763
- "dryRun" in args &&
3764
- args.dryRun !== undefined &&
3765
- typeof args.dryRun !== "boolean"
3766
- ) {
3767
- throw new Error("engram.dreams_run: dryRun must be a boolean when provided");
3768
- }
3769
- if (
3770
- "namespace" in args &&
3771
- args.namespace !== undefined &&
3772
- typeof args.namespace !== "string"
3773
- ) {
3774
- throw new Error("engram.dreams_run: namespace must be a string when provided");
3775
- }
3776
- const namespace =
3777
- typeof args.namespace === "string" ? args.namespace : undefined;
3778
- const dryRun = args.dryRun === true;
3779
- return this.service.dreamsRun({
3780
- phase: phase as import("./types.js").DreamsPhase,
3781
- dryRun,
3782
- namespace,
3783
- authenticatedPrincipal: effectivePrincipal,
3784
- });
2704
+ const op = getOperation(migrated);
2705
+ if (!op) {
2706
+ throw new EngramAccessInputError("access-boundary: operation not registered: " + migrated);
2707
+ }
2708
+ let envelope: Record<string, unknown>;
2709
+ if (migrated === "memory_store") {
2710
+ envelope = parseMcpRequest("memoryStore", args);
2711
+ } else if (migrated === "suggestion_submit") {
2712
+ envelope = parseMcpRequest("suggestionSubmit", args);
2713
+ } else if (migrated === "action_confidence") {
2714
+ envelope = parseMcpRequest("actionConfidence", args);
2715
+ } else if (migrated === "day_summary") {
2716
+ envelope = parseMcpRequest("daySummary", args);
2717
+ } else if (migrated === "capsule_export") {
2718
+ envelope = parseMcpRequest("capsuleExport", args);
2719
+ } else if (migrated === "capsule_import") {
2720
+ envelope = parseMcpRequest("capsuleImport", args);
2721
+ } else if (migrated === "capsule_list") {
2722
+ envelope = parseMcpRequest("capsuleList", args);
2723
+ } else if (migrated === "observe") {
2724
+ envelope = parseMcpRequest("observe", args);
2725
+ } else if (migrated === "lcm_compaction_flush") {
2726
+ envelope = parseMcpRequest("lcmCompactionFlush", args);
2727
+ } else if (migrated === "lcm_compaction_record") {
2728
+ envelope = parseMcpRequest("lcmCompactionRecord", args);
2729
+ } else if (migrated.startsWith("codegraph_")) {
2730
+ envelope = { ...args, tool: migrated.slice("codegraph_".length) };
2731
+ } else if (migrated === "chat_message") {
2732
+ // memory_chat dispatches to processChatMessage, not a pure service call.
2733
+ const message = typeof args.message === "string" ? args.message : "";
2734
+ if (!message) throw new EngramAccessInputError("message is required");
2735
+ const chatSessionId = typeof args.chatSessionId === "string" ? args.chatSessionId : undefined;
2736
+ const chatResult = await processChatMessage({
2737
+ service: this.service,
2738
+ config: this.service.configRef?.chat,
2739
+ memoryDir: this.service.memoryDir,
2740
+ message,
2741
+ ...(chatSessionId ? { chatSessionId } : {}),
2742
+ ...(effectivePrincipal ? { principal: effectivePrincipal } : {}),
2743
+ ...(scope?.namespace ? { namespace: scope.namespace } : {}),
2744
+ ...(scope?.sessionKey ? { sessionKey: scope.sessionKey } : {}),
2745
+ });
2746
+ if (chatResult && typeof chatResult === "object" && "error" in chatResult) {
2747
+ const { error: _stripped, ...wireResult } = chatResult as unknown as Record<string, unknown>;
2748
+ return wireResult;
3785
2749
  }
3786
- case "engram.memory_chat": {
3787
- const message = typeof args.message === "string" ? args.message : "";
3788
- if (!message) {
3789
- throw new EngramAccessInputError("message is required");
3790
- }
3791
- const chatSessionId = typeof args.chatSessionId === "string" ? args.chatSessionId : undefined;
3792
- const chatResult = await processChatMessage({
3793
- service: this.service,
3794
- config: this.service.configRef?.chat,
3795
- memoryDir: this.service.memoryDir,
3796
- message,
3797
- ...(chatSessionId ? { chatSessionId } : {}),
3798
- ...(effectivePrincipal ? { principal: effectivePrincipal } : {}),
3799
- ...(scope?.namespace ? { namespace: scope.namespace } : {}),
3800
- ...(scope?.sessionKey ? { sessionKey: scope.sessionKey } : {}),
3801
- });
3802
- // Strip internal error details — MCP clients get the tagged reply only,
3803
- // matching the HTTP handler (cursor security review, Thread 17).
3804
- if (chatResult && typeof chatResult === "object" && "error" in chatResult) {
3805
- const { error: _stripped, ...wireResult } = chatResult as unknown as Record<string, unknown>;
3806
- return wireResult;
2750
+ return chatResult;
2751
+ } else {
2752
+ envelope = args;
2753
+ }
2754
+ // recall citation guidance (MCP-specific post-processing).
2755
+ if (migrated === "recall") {
2756
+ const result = await op.run(envelope, { service: this.service, authenticatedPrincipal: effectivePrincipal }) as { result: unknown };
2757
+ const response = result.result as Record<string, unknown>;
2758
+ if (this.shouldEmitCitations(mcpSessionId)) {
2759
+ const citations = this.buildRecallCitations(response as unknown as EngramAccessRecallResponse);
2760
+ const guidance = buildCitationGuidance(citations);
2761
+ if (guidance.length > 0) {
2762
+ return { ...response, context: ((response as Record<string, unknown>).context as string ?? "") + guidance, citations };
3807
2763
  }
3808
- return chatResult;
3809
2764
  }
3810
- default:
3811
- throw new Error(`unknown tool: ${name}`);
2765
+ return response;
3812
2766
  }
2767
+ const output = (await op.run(envelope, {
2768
+ service: this.service,
2769
+ authenticatedPrincipal: effectivePrincipal,
2770
+ ...(enforceWriteQuota ? { hooks: { enforceWriteQuota } } : {}),
2771
+ })) as { result: unknown };
2772
+ return output.result;
3813
2773
  }
3814
2774
  }