@datasynx/agentic-crm 0.1.0 → 1.0.0

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 (286) hide show
  1. package/README.md +264 -670
  2. package/dist/{approvals-DpjxGHFp.js → approvals-CmDT2eUg.js} +7 -24
  3. package/dist/approvals-CmDT2eUg.js.map +1 -0
  4. package/dist/{ask-CID3jnuL.js → ask-D8iYqDAr.js} +6 -6
  5. package/dist/{ask-CID3jnuL.js.map → ask-D8iYqDAr.js.map} +1 -1
  6. package/dist/atomic-write-8yjqqLtS.js +29 -0
  7. package/dist/atomic-write-8yjqqLtS.js.map +1 -0
  8. package/dist/atomic-write-BYmF-ThH.cjs +37 -0
  9. package/dist/atomic-write-BYmF-ThH.cjs.map +1 -0
  10. package/dist/auth-B5DcjJ_6.js +2 -0
  11. package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
  12. package/dist/auth-DDXZTwS0.js.map +1 -0
  13. package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
  14. package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
  15. package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
  16. package/dist/backup-CTlIxUdO.js.map +1 -0
  17. package/dist/backup-LFnC09oV.js +2 -0
  18. package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
  19. package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
  20. package/dist/cli.js +282 -184
  21. package/dist/cli.js.map +1 -1
  22. package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
  23. package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
  24. package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
  25. package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
  26. package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
  27. package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
  28. package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
  29. package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
  30. package/dist/context-builder-7Uab5-G4.js.map +1 -0
  31. package/dist/context-builder-hmOPvgso.js +2 -0
  32. package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
  33. package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
  34. package/dist/custom-fields-GzpOHW_2.js.map +1 -0
  35. package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
  36. package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
  37. package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
  38. package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
  39. package/dist/customer-dir-CkMMXhb0.js.map +1 -0
  40. package/dist/daemon/worker.js +66 -40
  41. package/dist/daemon/worker.js.map +1 -1
  42. package/dist/doctor-C14-vnJ1.js +103 -0
  43. package/dist/doctor-C14-vnJ1.js.map +1 -0
  44. package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
  45. package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
  46. package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
  47. package/dist/file-lock-CcHotQkZ.js.map +1 -0
  48. package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-C-NmibzS.js} +13 -9
  49. package/dist/gmail-sync-C-NmibzS.js.map +1 -0
  50. package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-DueE6tl5.js} +14 -10
  51. package/dist/gmail-sync-DueE6tl5.js.map +1 -0
  52. package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-GEy3oVvw.cjs} +13 -9
  53. package/dist/gmail-sync-GEy3oVvw.cjs.map +1 -0
  54. package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-B26COilD.js} +2 -2
  55. package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-kGKpbY9h.js} +4 -4
  56. package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-kGKpbY9h.js.map} +1 -1
  57. package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
  58. package/dist/goal-engine-BbroPhqm.js.map +1 -0
  59. package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
  60. package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-D1n7WKZn.js} +3 -3
  61. package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-D1n7WKZn.js.map} +1 -1
  62. package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
  63. package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
  64. package/dist/identity-CB7j-Zr1.js +2 -0
  65. package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
  66. package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
  67. package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-DB4n89jy.js} +51 -45
  68. package/dist/import-hubspot-DB4n89jy.js.map +1 -0
  69. package/dist/{index-V8BFaH-b.d.ts → index-B0IMMrp_.d.ts} +8 -4
  70. package/dist/{index-V8BFaH-b.d.ts.map → index-B0IMMrp_.d.ts.map} +1 -1
  71. package/dist/{index-YqwMd6aQ.d.cts → index-pY7tYXwH.d.cts} +8 -4
  72. package/dist/{index-YqwMd6aQ.d.cts.map → index-pY7tYXwH.d.cts.map} +1 -1
  73. package/dist/index.cjs +19 -21
  74. package/dist/index.cjs.map +1 -1
  75. package/dist/index.d.cts +8 -4
  76. package/dist/index.d.cts.map +1 -1
  77. package/dist/index.d.ts +8 -4
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +19 -21
  80. package/dist/index.js.map +1 -1
  81. package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-BZzUIgJd.js} +5 -2
  82. package/dist/interactions-writer-BZzUIgJd.js.map +1 -0
  83. package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-DbSyI2rt.js} +32 -3
  84. package/dist/interactions-writer-DbSyI2rt.js.map +1 -0
  85. package/dist/interactions-writer-RJB8SWf2.js +2 -0
  86. package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-a2yzBd7T.cjs} +5 -2
  87. package/dist/interactions-writer-a2yzBd7T.cjs.map +1 -0
  88. package/dist/json-store-WWsFzXub.js +43 -0
  89. package/dist/json-store-WWsFzXub.js.map +1 -0
  90. package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base-DHNc4hVj.js} +43 -16
  91. package/dist/knowledge-base-DHNc4hVj.js.map +1 -0
  92. package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
  93. package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
  94. package/dist/lancedb-CuHKNsNZ.js.map +1 -0
  95. package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
  96. package/dist/lead-model-CEmx7te7.js.map +1 -0
  97. package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
  98. package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
  99. package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
  100. package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
  101. package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
  102. package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
  103. package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
  104. package/dist/logger-BkInaGoV.cjs +167 -0
  105. package/dist/logger-BkInaGoV.cjs.map +1 -0
  106. package/dist/logger-Dyl4VcLO.js +147 -0
  107. package/dist/logger-Dyl4VcLO.js.map +1 -0
  108. package/dist/logger-UaF5p9d1.js +147 -0
  109. package/dist/logger-UaF5p9d1.js.map +1 -0
  110. package/dist/logger-vKQS34w9.js +2 -0
  111. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  112. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  113. package/dist/mcp.cjs +327 -303
  114. package/dist/mcp.cjs.map +1 -1
  115. package/dist/mcp.d.cts.map +1 -1
  116. package/dist/mcp.d.ts.map +1 -1
  117. package/dist/mcp.js +327 -303
  118. package/dist/mcp.js.map +1 -1
  119. package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
  120. package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
  121. package/dist/memory-Dzr9dXSM.js.map +1 -0
  122. package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-jIu9K5zX.js} +4 -4
  123. package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-jIu9K5zX.js.map} +1 -1
  124. package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-R_r8HL-B.js} +5 -5
  125. package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-R_r8HL-B.js.map} +1 -1
  126. package/dist/{nba-3wanmJ0U.js → nba-mTJ4yEqD.js} +3 -3
  127. package/dist/{nba-3wanmJ0U.js.map → nba-mTJ4yEqD.js.map} +1 -1
  128. package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
  129. package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
  130. package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
  131. package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
  132. package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
  133. package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
  134. package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
  135. package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
  136. package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
  137. package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
  138. package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
  139. package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
  140. package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
  141. package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
  142. package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
  143. package/dist/push-manager-C0ECQgva.js.map +1 -0
  144. package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
  145. package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
  146. package/dist/quote-generator-CTdR8eEI.js.map +1 -0
  147. package/dist/rbac-DzbyFhVH.js +2 -0
  148. package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
  149. package/dist/rbac-msmBc_tK.js.map +1 -0
  150. package/dist/regex-Jt5DatPi.js +13 -0
  151. package/dist/regex-Jt5DatPi.js.map +1 -0
  152. package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
  153. package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
  154. package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
  155. package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
  156. package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
  157. package/dist/safe-path-mpp0dKtO.js +18 -0
  158. package/dist/safe-path-mpp0dKtO.js.map +1 -0
  159. package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
  160. package/dist/segments-DI3LOQNe.js.map +1 -0
  161. package/dist/sequence-engine-C6nnewHX.js +2 -0
  162. package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
  163. package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
  164. package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
  165. package/dist/sequence-store-CmYb6s0g.js.map +1 -0
  166. package/dist/{server-Dyva03K8.js → server-DqSMYhSA.js} +278 -220
  167. package/dist/server-DqSMYhSA.js.map +1 -0
  168. package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
  169. package/dist/session-B6XaP83h.js.map +1 -0
  170. package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
  171. package/dist/session-BgGDyP2C.js.map +1 -0
  172. package/dist/session-Bp4zTh4l.js +2 -0
  173. package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
  174. package/dist/session-Mm7GQbSH.cjs.map +1 -0
  175. package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
  176. package/dist/session-store-DWxJ5Pof.js.map +1 -0
  177. package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
  178. package/dist/session-store-yfwnj0OC.cjs.map +1 -0
  179. package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
  180. package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
  181. package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
  182. package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
  183. package/dist/sop-BV7ICAFR.js.map +1 -0
  184. package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
  185. package/dist/survey-engine-DKctGcLQ.js +2 -0
  186. package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
  187. package/dist/survey-engine-DngXBv47.js.map +1 -0
  188. package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
  189. package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
  190. package/dist/sync-state-DMZgzpez.js.map +1 -0
  191. package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
  192. package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
  193. package/dist/ticket-writer-a9on36Wb.js.map +1 -0
  194. package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
  195. package/dist/tone-C7bqK69y.js.map +1 -0
  196. package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
  197. package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
  198. package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
  199. package/dist/tone-mXSftvTn.js.map +1 -0
  200. package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-0mh2ZhmH.js} +18 -11
  201. package/dist/transcript-watcher-0mh2ZhmH.js.map +1 -0
  202. package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
  203. package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
  204. package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
  205. package/dist/update-deal-CWy1eLJI.js +2 -0
  206. package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
  207. package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
  208. package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
  209. package/dist/usage-BVlFlKW_.js.map +1 -0
  210. package/dist/usage-CClTf5e6.cjs.map +1 -1
  211. package/dist/usage-D0u9a-lV.js.map +1 -1
  212. package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
  213. package/dist/vault-CfwZdNzC.js.map +1 -0
  214. package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
  215. package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
  216. package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
  217. package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
  218. package/dist/webhooks-DXr1IoKn.js.map +1 -0
  219. package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
  220. package/dist/webhooks-sWZ8CJtR.js.map +1 -0
  221. package/package.json +11 -2
  222. package/dist/approvals-DpjxGHFp.js.map +0 -1
  223. package/dist/auth-CyFuu9X_.js +0 -2
  224. package/dist/auth-DFWwWcYD.js.map +0 -1
  225. package/dist/backup-CeMk9z86.js.map +0 -1
  226. package/dist/backup-f_hC7rBV.js +0 -2
  227. package/dist/context-builder-BzWAp3Zs.js.map +0 -1
  228. package/dist/context-builder-DlrRcqmJ.js +0 -2
  229. package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
  230. package/dist/custom-objects-BHgn1GEX.js.map +0 -1
  231. package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
  232. package/dist/file-lock-B_zi7NQl.js.map +0 -1
  233. package/dist/gmail-sync-DIaxInDT.js.map +0 -1
  234. package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
  235. package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
  236. package/dist/goal-engine-KpBftn4V.js.map +0 -1
  237. package/dist/identity-gyfWdrcX.js +0 -2
  238. package/dist/import-hubspot-BaK71U_K.js.map +0 -1
  239. package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
  240. package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
  241. package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
  242. package/dist/interactions-writer-dSPy1XfO.js +0 -2
  243. package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
  244. package/dist/lancedb-rlvWoPwl.js.map +0 -1
  245. package/dist/lead-model-BCFzyktm.js.map +0 -1
  246. package/dist/memory-Bb6ky3kb.js.map +0 -1
  247. package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
  248. package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
  249. package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
  250. package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
  251. package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
  252. package/dist/push-manager-CdqIIkuh.js.map +0 -1
  253. package/dist/quote-generator-BfwENXzg.js.map +0 -1
  254. package/dist/rbac-C7c8tcES.js +0 -2
  255. package/dist/rbac-CTIktZaC.js.map +0 -1
  256. package/dist/relationship-health-odxEoQdJ.js.map +0 -1
  257. package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
  258. package/dist/segments-BqcD5HIl.js.map +0 -1
  259. package/dist/sequence-engine-CCTHEBgi.js +0 -2
  260. package/dist/sequence-engine-J1lTW_in.js.map +0 -1
  261. package/dist/sequence-store-DaaWr0Os.js.map +0 -1
  262. package/dist/server-Dyva03K8.js.map +0 -1
  263. package/dist/session-B9AilxOE.js.map +0 -1
  264. package/dist/session-D0qFkBla.cjs.map +0 -1
  265. package/dist/session-D9ub6Wl1.js.map +0 -1
  266. package/dist/session-mWHA71Lw.js +0 -2
  267. package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
  268. package/dist/session-store-C8tEvMPw.js.map +0 -1
  269. package/dist/sop-Vp0UPWFW.js.map +0 -1
  270. package/dist/survey-engine-C06hcQt3.js +0 -2
  271. package/dist/survey-engine-DBjCYqCv.js.map +0 -1
  272. package/dist/sync-state-ChaLbamC.js.map +0 -1
  273. package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
  274. package/dist/tone-Bdm5uaht.js.map +0 -1
  275. package/dist/tone-DRKlZgPr.cjs.map +0 -1
  276. package/dist/tone-vNb2DAAD.js.map +0 -1
  277. package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
  278. package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
  279. package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
  280. package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
  281. package/dist/update-deal-BNwPGaTV.js +0 -2
  282. package/dist/usage-D0-TYJkw.js.map +0 -1
  283. package/dist/vault-DXCg29W-.js.map +0 -1
  284. package/dist/webhooks-7EpA05Qr.js.map +0 -1
  285. package/dist/webhooks-BO2UAnmn.js.map +0 -1
  286. package/dist/webhooks-Xn6zO6kd.cjs.map +0 -1
package/dist/mcp.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.js","names":["b","DATA_DIR","ArrowFloat32","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","addDaysToDate","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR"],"sources":["../src/core/oauth-store.ts","../src/sync/push-manager.ts","../src/fs/sync-state.ts","../src/sync/gmail-webhook-handler.ts","../src/sync/microsoft-webhook-handler.ts","../src/sync/slack-webhook-handler.ts","../src/mcp/capabilities.ts","../src/mcp/tools/get-capabilities.ts","../src/mcp/tools/get-active-session.ts","../src/core/context-builder.ts","../src/mcp/tools/get-customer-context.ts","../src/core/embedder.ts","../src/core/lancedb.ts","../src/mcp/tools/search-customer-knowledge.ts","../src/mcp/tools/list-customers.ts","../src/core/file-lock.ts","../src/core/graph.ts","../src/core/email-normalizer.ts","../src/core/graph-extractor.ts","../src/core/relationship-health.ts","../src/mcp/tools/log-interaction.ts","../src/mcp/tools/update-deal.ts","../src/mcp/tools/export-customer.ts","../src/mcp/tools/update-customer-facts.ts","../src/core/deal-health.ts","../src/mcp/tools/get-deal-health.ts","../src/mcp/tools/get-pipeline-forecast.ts","../src/mcp/tools/summarize-meeting.ts","../src/core/pipeline-stages.ts","../src/mcp/tools/get-pipeline-stages.ts","../src/core/cross-customer.ts","../src/mcp/tools/get-market-intelligence.ts","../src/mcp/tools/get-relationship-graph.ts","../src/mcp/tools/get-relationship-health.ts","../src/core/playbooks.ts","../src/agents/deal-agent.ts","../src/mcp/tools/run-deal-agent.ts","../src/mcp/tools/approve-agent-action.ts","../src/core/revenue-simulation.ts","../src/mcp/tools/simulate-revenue.ts","../src/mcp/tools/get-playbook.ts","../src/mcp/tools/create-playbook.ts","../src/mcp/tools/list-playbooks.ts","../src/mcp/tools/distill-playbook.ts","../src/core/goal-engine.ts","../src/mcp/tools/pursue-goal.ts","../src/mcp/tools/get-goal-status.ts","../src/mcp/tools/register-push-subscription.ts","../src/mcp/tools/get-push-status.ts","../src/core/org-intelligence.ts","../src/mcp/tools/get-org-intelligence.ts","../src/agents/deal-room.ts","../src/mcp/tools/open-deal-room.ts","../src/core/proactive-agent.ts","../src/mcp/tools/get-proactive-briefing.ts","../src/schemas/email-template.ts","../src/fs/template-store.ts","../src/mcp/tools/list-email-templates.ts","../src/core/template-engine.ts","../src/mcp/tools/get-email-template.ts","../src/mcp/tools/draft-email.ts","../src/schemas/sequence.ts","../src/fs/sequence-store.ts","../src/mcp/tools/enroll-in-sequence.ts","../src/mcp/tools/list-sequence-enrollments.ts","../src/mcp/tools/unenroll-from-sequence.ts","../src/mcp/tools/list-sequences.ts","../src/core/quote-generator.ts","../src/mcp/tools/generate-quote.ts","../src/mcp/tools/get-quote-status.ts","../src/mcp/tools/get-booking-link.ts","../src/schemas/ticket.ts","../src/fs/ticket-writer.ts","../src/core/sla-engine.ts","../src/mcp/tools/create-ticket.ts","../src/mcp/tools/update-ticket.ts","../src/mcp/tools/list-tickets.ts","../src/mcp/tools/close-ticket.ts","../src/schemas/survey.ts","../src/core/survey-engine.ts","../src/mcp/tools/send-nps-survey.ts","../src/mcp/tools/get-survey-results.ts","../src/schemas/kb-article.ts","../src/fs/knowledge-base.ts","../src/mcp/tools/search-knowledge-base.ts","../src/mcp/tools/create-kb-article.ts","../src/mcp/tools/backup-now.ts","../src/mcp/tools/list-backups.ts","../src/mcp/tools/trigger-sync.ts","../src/mcp/tools/get-audit-log.ts","../src/mcp/prompts.ts","../src/mcp/resources.ts","../src/core/custom-fields.ts","../src/core/custom-objects.ts","../src/mcp/tools/custom-objects.ts","../src/mcp/auth.ts","../src/mcp/server.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { OAuth2Client } from \"google-auth-library\";\n\nlet _auth: OAuth2Client | null = null;\n\nexport async function initOAuthFromDisk(dataDir: string): Promise<boolean> {\n const credPath = path.join(dataDir, \".agentic\", \"gmail-credentials.json\");\n const tokenPath = path.join(dataDir, \".agentic\", \"gmail-token.json\");\n\n if (!fs.existsSync(credPath) || !fs.existsSync(tokenPath)) {\n return false;\n }\n\n try {\n const { getGmailAuth: loadAuth } = await import(\"../sync/gmail-auth.js\");\n _auth = await loadAuth(credPath, tokenPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getGmailAuth(): OAuth2Client | null {\n return _auth;\n}\n\nexport function resetOAuthStore(): void {\n _auth = null;\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport type PushProvider = \"gmail\" | \"microsoft-graph\" | \"slack\";\nexport type PushStatus = \"active\" | \"expired\" | \"revoked\" | \"error\" | \"permanently_failed\";\n\nexport interface PushSubscription {\n id: string;\n provider: PushProvider;\n slug: string;\n webhookUrl: string;\n expiresAt: string | null;\n renewedAt: string | null;\n createdAt: string;\n providerData: {\n gmailHistoryId?: string;\n gmailTopicName?: string;\n gmailLabelIds?: string[];\n gmailEmailAddress?: string;\n microsoftSubscriptionId?: string;\n microsoftResource?: string;\n microsoftClientState?: string;\n slackTeamId?: string;\n slackChannelId?: string;\n slackBotToken?: string;\n };\n status: PushStatus;\n lastEventAt: string | null;\n eventsProcessed: number;\n renewFailures?: number;\n}\n\ninterface PushSubscriptionsFile {\n subscriptions: PushSubscription[];\n updatedAt: string;\n}\n\nexport function makePushSubId(): string {\n return `psub_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;\n}\n\nexport function subscriptionsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"push-subscriptions.json\");\n}\n\nexport async function readSubscriptions(dataDir: string): Promise<PushSubscription[]> {\n const filePath = subscriptionsPath(dataDir);\n if (!fs.existsSync(filePath)) return [];\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\") as string;\n const parsed = JSON.parse(raw) as PushSubscriptionsFile;\n return parsed.subscriptions ?? [];\n } catch {\n return [];\n }\n}\n\nexport async function writeSubscriptions(dataDir: string, subs: PushSubscription[]): Promise<void> {\n const filePath = subscriptionsPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const file: PushSubscriptionsFile = { subscriptions: subs, updatedAt: new Date().toISOString() };\n fs.writeFileSync(filePath, JSON.stringify(file, null, 2), \"utf-8\");\n}\n\nfunction expiresAtForProvider(provider: PushProvider): string | null {\n if (provider === \"gmail\") {\n return new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();\n }\n if (provider === \"microsoft-graph\") {\n return new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString();\n }\n return null; // slack: no expiry\n}\n\nexport async function register(\n dataDir: string,\n provider: PushProvider,\n slug: string,\n opts: { webhookUrl: string; providerData?: Partial<PushSubscription[\"providerData\"]> }\n): Promise<PushSubscription> {\n const subs = await readSubscriptions(dataDir);\n const sub: PushSubscription = {\n id: makePushSubId(),\n provider,\n slug,\n webhookUrl: opts.webhookUrl,\n expiresAt: expiresAtForProvider(provider),\n renewedAt: null,\n createdAt: new Date().toISOString(),\n providerData: opts.providerData ?? {},\n status: \"active\",\n lastEventAt: null,\n eventsProcessed: 0,\n };\n await writeSubscriptions(dataDir, [...subs, sub]);\n return sub;\n}\n\nexport async function revoke(dataDir: string, id: string): Promise<void> {\n const subs = await readSubscriptions(dataDir);\n const idx = subs.findIndex((s) => s.id === id);\n if (idx === -1) throw new Error(`Subscription ${id} not found`);\n subs[idx] = { ...subs[idx]!, status: \"revoked\" };\n await writeSubscriptions(dataDir, subs);\n}\n\nexport type RenewFn = (\n sub: PushSubscription\n) => Promise<{ expiresAt: string; providerData?: Partial<PushSubscription[\"providerData\"]> }>;\n\nexport async function renewExpiringSubscriptions(\n dataDir: string,\n renewFn: RenewFn,\n thresholdHours = 24\n): Promise<{ renewed: string[]; errors: string[] }> {\n const subs = await readSubscriptions(dataDir);\n const thresholdMs = thresholdHours * 60 * 60 * 1000;\n const cutoff = Date.now() + thresholdMs;\n\n const renewed: string[] = [];\n const errors: string[] = [];\n\n const PERMANENT_FAILURE_THRESHOLD = 3;\n\n for (let i = 0; i < subs.length; i++) {\n const sub = subs[i]!;\n if (sub.status !== \"active\" && sub.status !== \"error\") continue;\n if (sub.expiresAt === null) continue; // slack: no expiry\n if (new Date(sub.expiresAt).getTime() > cutoff) continue;\n\n try {\n const result = await renewFn(sub);\n subs[i] = {\n ...sub,\n status: \"active\",\n expiresAt: result.expiresAt,\n renewedAt: new Date().toISOString(),\n renewFailures: 0,\n providerData: result.providerData\n ? { ...sub.providerData, ...result.providerData }\n : sub.providerData,\n };\n renewed.push(sub.id);\n } catch {\n const failures = (sub.renewFailures ?? 0) + 1;\n const newStatus: PushStatus =\n failures >= PERMANENT_FAILURE_THRESHOLD ? \"permanently_failed\" : \"error\";\n subs[i] = { ...sub, status: newStatus, renewFailures: failures };\n errors.push(sub.id);\n }\n }\n\n await writeSubscriptions(dataDir, subs);\n return { renewed, errors };\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport interface SlugSyncState {\n lastGmailSync?: string;\n lastCalendarSync?: string;\n lastGmailPushHistoryId?: string;\n lastMicrosoftPushAt?: string;\n}\n\nexport interface SyncState {\n [slug: string]: SlugSyncState;\n}\n\nfunction getSyncStatePath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sync-state.json\");\n}\n\nexport function readSyncState(dataDir: string): SyncState {\n const filePath = getSyncStatePath(dataDir);\n if (!fs.existsSync(filePath)) return {};\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as SyncState;\n } catch {\n return {};\n }\n}\n\nexport function writeSyncState(dataDir: string, state: SyncState): void {\n const filePath = getSyncStatePath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(state, null, 2), \"utf-8\");\n}\n\nexport function updateSlugSyncState(\n dataDir: string,\n slug: string,\n update: Partial<SlugSyncState>\n): void {\n const filePath = getSyncStatePath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const state = readSyncState(dataDir);\n state[slug] = { ...state[slug], ...update };\n fs.writeFileSync(filePath, JSON.stringify(state, null, 2), \"utf-8\");\n}\n\nexport function getLastGmailSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastGmailSync;\n return ts ? new Date(ts) : undefined;\n}\n\nexport function getLastCalendarSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastCalendarSync;\n return ts ? new Date(ts) : undefined;\n}\n","import {\n readSubscriptions,\n writeSubscriptions,\n type PushSubscription,\n type RenewFn,\n} from \"./push-manager.js\";\nimport { updateSlugSyncState, readSyncState } from \"../fs/sync-state.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\nimport type { HistoryMessage, WatchRegistration } from \"./gmail-push-watch.js\";\n\nexport interface GmailPubSubMessage {\n emailAddress: string;\n historyId: string;\n}\n\nexport function decodeGmailPubSubPayload(body: unknown): GmailPubSubMessage | null {\n try {\n const b = body as { message?: { data?: string } };\n const data = b?.message?.data;\n if (!data) return null;\n const decoded = Buffer.from(data, \"base64\").toString(\"utf-8\");\n const parsed = JSON.parse(decoded) as { emailAddress?: string; historyId?: string };\n if (!parsed.emailAddress || !parsed.historyId) return null;\n return { emailAddress: parsed.emailAddress, historyId: parsed.historyId };\n } catch {\n return null;\n }\n}\n\nexport function verifyGmailPubSubSignature(\n authHeader: string | undefined,\n expectedToken: string\n): boolean {\n if (!authHeader) return false;\n const token = authHeader.startsWith(\"Bearer \") ? authHeader.slice(7) : authHeader;\n return token === expectedToken;\n}\n\nfunction findSubscriptionByEmail(\n subs: PushSubscription[],\n emailAddress: string\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"gmail\" &&\n s.status === \"active\" &&\n (s.providerData.gmailEmailAddress === emailAddress || s.slug === emailAddress)\n ) ?? null\n );\n}\n\nexport type FetchHistoryFn = (\n accessToken: string,\n startHistoryId: string\n) => Promise<HistoryMessage[]>;\nexport type FetchMessageFn = (\n accessToken: string,\n messageId: string\n) => Promise<{\n id: string;\n threadId: string;\n subject: string;\n from: string;\n date: string;\n body: string;\n}>;\nexport type AppendInteractionFn = typeof appendInteraction;\n\nexport interface HandleGmailPushOptions {\n fetchHistoryFn?: FetchHistoryFn;\n fetchMessageFn?: FetchMessageFn;\n appendInteractionFn?: AppendInteractionFn;\n accessToken?: string;\n}\n\nexport { readSubscriptions };\n\nexport async function handleGmailPushEvent(\n dataDir: string,\n payload: GmailPubSubMessage,\n subscriptionId: string,\n options: HandleGmailPushOptions = {}\n): Promise<{ processed: number; slug: string | null }> {\n const subs = await readSubscriptions(dataDir);\n const sub = findSubscriptionByEmail(subs, payload.emailAddress);\n if (!sub) return { processed: 0, slug: null };\n\n const slug = sub.slug;\n const syncState = readSyncState(dataDir);\n const lastHistoryId =\n syncState[slug]?.lastGmailPushHistoryId ?? sub.providerData.gmailHistoryId ?? \"0\";\n\n // Skip if already processed\n if (BigInt(payload.historyId) <= BigInt(lastHistoryId)) {\n return { processed: 0, slug };\n }\n\n const startHistoryId = sub.providerData.gmailHistoryId ?? lastHistoryId;\n\n const {\n fetchHistoryFn,\n fetchMessageFn,\n appendInteractionFn = appendInteraction,\n accessToken = \"\",\n } = options;\n\n if (!fetchHistoryFn) return { processed: 0, slug };\n\n const messages = await fetchHistoryFn(accessToken, startHistoryId);\n let processed = 0;\n\n for (const msg of messages) {\n if (!fetchMessageFn) continue;\n try {\n const full = await fetchMessageFn(accessToken, msg.id);\n const sourceRef = `gmail://thread/${full.threadId}`;\n\n await appendInteractionFn(dataDir, slug, {\n date: new Date().toISOString().slice(0, 10),\n type: \"Email\",\n direction: \"inbound\",\n with: full.from,\n subject: full.subject,\n summary: full.body.slice(0, 300) || \"(no body)\",\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n processed++;\n } catch {\n // Skip individual message errors\n }\n }\n\n // Update sync state\n updateSlugSyncState(dataDir, slug, { lastGmailPushHistoryId: payload.historyId });\n\n // Update subscription counters\n const subIdx = subs.findIndex((s) => s.id === sub.id);\n if (subIdx !== -1) {\n subs[subIdx] = {\n ...subs[subIdx]!,\n eventsProcessed: subs[subIdx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed, slug };\n}\n\nexport type RegisterGmailWatchFn = (\n accessToken: string,\n topicName: string\n) => Promise<WatchRegistration>;\n\nexport function buildGmailRenewFn(\n accessToken: string,\n topicName: string,\n registerFn?: RegisterGmailWatchFn\n): RenewFn {\n return async (_sub: PushSubscription) => {\n const doRegister: RegisterGmailWatchFn =\n registerFn ??\n (async (token: string, topic: string) => {\n const { registerGmailWatch } = await import(\"./gmail-push-watch.js\");\n return registerGmailWatch(token, topic);\n });\n\n const registration = await doRegister(accessToken, topicName);\n return {\n expiresAt: new Date(Number(registration.expiration)).toISOString(),\n providerData: { gmailHistoryId: registration.historyId },\n };\n };\n}\n","import { readSubscriptions, writeSubscriptions, type PushSubscription } from \"./push-manager.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\n\nexport interface MicrosoftGraphNotification {\n subscriptionId: string;\n clientState?: string;\n resource: string;\n resourceData?: { id: string; \"@odata.type\": string };\n}\n\nexport function verifyMicrosoftGraphSignature(\n body: { value?: Array<Partial<MicrosoftGraphNotification>> },\n expectedClientState: string\n): boolean {\n const notifications = body.value ?? [];\n if (notifications.length === 0) return expectedClientState === \"\";\n return notifications.every((n) => n.clientState === expectedClientState);\n}\n\nexport interface ValidationResult {\n isValidation: boolean;\n token?: string;\n}\n\nexport function handleMicrosoftValidationRequest(\n queryParams: Record<string, string | undefined>\n): ValidationResult {\n const token = queryParams[\"validationToken\"];\n if (token) return { isValidation: true, token };\n return { isValidation: false };\n}\n\nexport interface GraphMessage {\n id: string;\n subject?: string;\n from?: { emailAddress?: { address?: string; name?: string } };\n receivedDateTime?: string;\n bodyPreview?: string;\n}\n\nexport type FetchGraphMessageFn = (\n accessToken: string,\n messageId: string\n) => Promise<GraphMessage | null>;\nexport type AppendInteractionFn = typeof appendInteraction;\n\nexport interface HandleMicrosoftPushOptions {\n fetchMessageFn?: FetchGraphMessageFn;\n appendInteractionFn?: AppendInteractionFn;\n}\n\nexport { readSubscriptions };\n\nfunction findSubscriptionByMsId(\n subs: PushSubscription[],\n subscriptionId: string\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"microsoft-graph\" &&\n s.status === \"active\" &&\n s.providerData.microsoftSubscriptionId === subscriptionId\n ) ?? null\n );\n}\n\nexport async function handleMicrosoftPushEvent(\n dataDir: string,\n notifications: MicrosoftGraphNotification[],\n accessToken: string,\n options: HandleMicrosoftPushOptions = {}\n): Promise<{ processed: number; skipped: number }> {\n const subs = await readSubscriptions(dataDir);\n const { fetchMessageFn, appendInteractionFn = appendInteraction } = options;\n\n let processed = 0;\n let skipped = 0;\n let anyProcessed = false;\n\n for (const notification of notifications) {\n const sub = findSubscriptionByMsId(subs, notification.subscriptionId);\n if (!sub) {\n skipped++;\n continue;\n }\n\n const messageId = notification.resourceData?.id;\n if (!messageId || !fetchMessageFn) {\n skipped++;\n continue;\n }\n\n try {\n const message = await fetchMessageFn(accessToken, messageId);\n if (!message) {\n skipped++;\n continue;\n }\n\n const from = message.from?.emailAddress?.address ?? \"unknown\";\n const sourceRef = `msgraph://message/${message.id}`;\n\n await appendInteractionFn(dataDir, sub.slug, {\n date: message.receivedDateTime\n ? new Date(message.receivedDateTime).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10),\n type: \"Email\",\n direction: \"inbound\",\n with: from,\n subject: message.subject ?? \"(no subject)\",\n summary: message.bodyPreview ?? \"(no preview)\",\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n\n processed++;\n anyProcessed = true;\n\n // Update sub counters\n const idx = subs.findIndex((s) => s.id === sub.id);\n if (idx !== -1) {\n subs[idx] = {\n ...subs[idx]!,\n eventsProcessed: subs[idx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n }\n } catch {\n skipped++;\n }\n }\n\n if (anyProcessed) {\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed, skipped };\n}\n","import crypto from \"crypto\";\nimport { readSubscriptions, writeSubscriptions, type PushSubscription } from \"./push-manager.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\n\nexport interface SlackEvent {\n type: string;\n user?: string;\n text?: string;\n channel?: string;\n ts?: string;\n bot_id?: string;\n}\n\nexport function verifySlackSignature(\n body: string,\n headers: { \"x-slack-signature\"?: string; \"x-slack-request-timestamp\"?: string },\n signingSecret: string\n): boolean {\n const sig = headers[\"x-slack-signature\"];\n const ts = headers[\"x-slack-request-timestamp\"];\n if (!sig || !ts) return false;\n\n // Replay protection: reject requests older than 5 minutes\n const tsNum = Number(ts);\n if (Math.abs(Date.now() / 1000 - tsNum) > 5 * 60) return false;\n\n const sigBase = `v0:${ts}:${body}`;\n const expected = \"v0=\" + crypto.createHmac(\"sha256\", signingSecret).update(sigBase).digest(\"hex\");\n\n try {\n const sigBuf = Buffer.from(sig);\n const expBuf = Buffer.from(expected);\n if (sigBuf.length !== expBuf.length) return false;\n return crypto.timingSafeEqual(sigBuf, expBuf);\n } catch {\n return false;\n }\n}\n\nexport type SlackUrlVerificationResult =\n | { isVerification: true; challenge: string }\n | { isVerification: false; challenge?: never };\n\nexport function handleSlackUrlVerification(body: {\n type?: string;\n challenge?: string;\n}): SlackUrlVerificationResult {\n if (body.type === \"url_verification\") {\n return { isVerification: true, challenge: body.challenge ?? \"\" };\n }\n return { isVerification: false };\n}\n\nexport type AppendInteractionFn = typeof appendInteraction;\nexport type FetchUserInfoFn = (\n botToken: string,\n userId: string\n) => Promise<{ email?: string; name?: string }>;\n\nexport interface HandleSlackPushOptions {\n appendInteractionFn?: AppendInteractionFn;\n fetchUserInfoFn?: FetchUserInfoFn;\n teamId?: string;\n}\n\nfunction findSubscriptionByTeam(\n subs: PushSubscription[],\n teamId: string | undefined\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"slack\" &&\n s.status === \"active\" &&\n (!teamId || s.providerData.slackTeamId === teamId)\n ) ?? null\n );\n}\n\nexport async function handleSlackPushEvent(\n dataDir: string,\n event: SlackEvent,\n botToken: string,\n options: HandleSlackPushOptions = {}\n): Promise<{ processed: number; skipped: number }> {\n // Only process message events\n if (event.type !== \"message\") return { processed: 0, skipped: 1 };\n\n // Skip bot messages\n if (event.bot_id) return { processed: 0, skipped: 1 };\n\n // Skip empty text\n if (!event.text?.trim()) return { processed: 0, skipped: 1 };\n\n const subs = await readSubscriptions(dataDir);\n const sub = findSubscriptionByTeam(subs, options.teamId);\n if (!sub) return { processed: 0, skipped: 1 };\n\n const { appendInteractionFn = appendInteraction, fetchUserInfoFn } = options;\n\n let senderName = event.user ?? \"unknown\";\n if (fetchUserInfoFn && event.user) {\n try {\n const info = await fetchUserInfoFn(botToken, event.user);\n senderName = info.name ?? info.email ?? event.user;\n } catch {\n // keep default\n }\n }\n\n const ts = event.ts\n ? new Date(Number(event.ts) * 1000).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10);\n const sourceRef = `slack://channel/${event.channel ?? \"dm\"}/ts/${event.ts ?? Date.now()}`;\n\n try {\n await appendInteractionFn(dataDir, sub.slug, {\n date: ts,\n type: \"Meeting\",\n direction: \"inbound\",\n with: senderName,\n subject: `Slack message in ${event.channel ?? \"DM\"}`,\n summary: event.text.slice(0, 300),\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n\n // Update sub counters\n const idx = subs.findIndex((s) => s.id === sub.id);\n if (idx !== -1) {\n subs[idx] = {\n ...subs[idx]!,\n eventsProcessed: subs[idx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed: 1, skipped: 0 };\n } catch {\n return { processed: 0, skipped: 1 };\n }\n}\n","// src/mcp/capabilities.ts\n// Single Source of Truth for capability documentation.\n// Used by get_capabilities() MCP tool AND dxcrm guide CLI command.\n\nexport const CAPABILITIES_TEXT = `\n# DatasynxOpenCRM — Agent Guide\n\n## Product\nDatasynxOpenCRM is a local-first, MCP-native CRM. All customer data lives in markdown\nfiles on your machine. No cloud, no HubSpot, no per-seat pricing.\n\n## Agent Wake (Telegram Notifications)\n\\`dxcrm agent spawn\\` enables a wake-triggered agent for a customer. When new emails arrive,\nthe agent sends a Telegram notification so you never miss an inbound message.\n\\`\\`\\`\ndxcrm agent spawn acme-corp --channel telegram\n\\`\\`\\`\nRequires: \\`TELEGRAM_BOT_TOKEN\\` + \\`TELEGRAM_CHAT_ID\\` env vars.\n\n## Golden Path — Agent Session Workflow\n\nThe recommended sequence for a productive agent session:\n\n1. \\`get_capabilities()\\` — understand available tools (this guide)\n2. \\`get_active_session()\\` — check for an active customer session\n3. \\`get_customer_context({ slug })\\` — load full briefing for the customer\n4. \\`search_customer_knowledge({ slug, query })\\` — find specific historical information\n5. \\`log_interaction()\\` / \\`update_deal()\\` — write back what happened\n\n## RBAC — Role-Based Access Control\n\nTools enforce the \\`DXCRM_ACTOR\\` environment variable for identity. Configure roles with \\`dxcrm rbac set\\`.\n\n| Role | Permissions |\n|---|---|\n| admin | All tools, all customers |\n| manager | log_interaction, update_deal, pursue_goal + all read tools |\n| rep | log_interaction, update_deal (own customers only) + all read tools |\n\nDefault role: rep (when DXCRM_ACTOR is not set or has no assigned role).\n\nConfig: \\`.agentic/rbac.json\\` | Actor: \\`DXCRM_ACTOR\\` env var\n\n## Available Tools\n\n| Tool | Purpose | RBAC |\n|---|---|---|\n| get_capabilities | Returns this guide — understand what the CRM can do | any |\n| get_active_session | Check which customer session is currently active | any |\n| get_customer_context | Full LLM-ready briefing for a customer (last 10 interactions, pipeline, contacts) | any (rep: own only) |\n| search_customer_knowledge | Hybrid vector + full-text search across emails and transcripts for a customer | any |\n| list_customers | List all customers with stage, last interaction date, and deal value | any (rep: own only) |\n| log_interaction | Write a new interaction entry (call, email, meeting, note) — immediately searchable | rep+ |\n| update_deal | Create or update a deal in pipeline.md — upserts by deal name | rep+ |\n| update_customer_facts | Update fields in customer profile (domain, contact, stage, tags) | admin |\n| export_customer | Export all customer data as JSON or Markdown | admin |\n| get_deal_health | Score deal health 0–100 (A–F grade) based on activity, velocity, close date, probability | any |\n| get_pipeline_forecast | Aggregate weighted pipeline revenue across all customers grouped by stage | any |\n| get_pipeline_stages | List all configured pipeline stages (defaults: lead, qualified, proposal, negotiation, won, lost) | any |\n| summarize_meeting | LLM-summarize a transcript and log it as a Meeting interaction | rep+ |\n| get_market_intelligence | Semantic search across all customers for patterns and common topics | any |\n| get_relationship_graph | Stakeholder map: champions, blockers, economic buyers, warm intro paths | any |\n| get_relationship_health | Health scores (0–100, A–F, trend) per contact with decay detection and risk flags | any |\n| run_deal_agent | Analyze deal and generate prioritized action plan (observe/suggest/act autonomy levels) | rep+ |\n| approve_agent_action | Approve or reject a pending action from the deal agent queue | rep+ |\n| simulate_revenue | Monte Carlo pipeline forecast — P10/P50/P90, sensitivity map, at-risk revenue | any |\n| get_playbook | Retrieve playbooks matching current deal situation (trigger-matched, sorted by success rate) | any |\n| create_playbook | Create or update a playbook with trigger DSL encoding proven tactics | rep+ |\n| list_playbooks | List all playbooks for a customer (metadata only, no body) | any |\n| distill_playbook | LLM-extract a reusable playbook from a won or lost deal's interaction history | rep+ |\n| pursue_goal | Set a revenue/pipeline goal and get an AI-decomposed action plan with sub-goals | manager+ |\n| get_goal_status | Get all active goals or a specific goal with progress, days remaining, sub-goals | any |\n| register_push_subscription | Register real-time push subscription (Gmail Pub/Sub, MS Graph, Slack Events) | admin |\n| get_push_status | Show all push subscriptions: expiry, events processed, renewal needs | any |\n| get_org_intelligence | Stakeholder map with champions, buyers, blockers, health scores, risk flags, recommendation | any |\n| open_deal_room | Multi-agent deal brief: graph + health + deal health + simulation + playbook in one call | any |\n| get_proactive_briefing | Daily briefing: urgent alerts, opportunities, P50/P90 forecast, top action | any |\n| list_email_templates | List all saved email templates with id, name, category, subject | any |\n| get_email_template | Retrieve a single email template with full body and detected variables | any |\n| draft_email | Draft a personalized email from a template with auto-filled customer variables | rep+ |\n| enroll_in_sequence | Enroll a contact in a multi-step email sequence | rep+ |\n| list_sequence_enrollments | List active sequence enrollments filtered by customer or status | any |\n| unenroll_from_sequence | Pause (soft-unenroll) a contact from an active sequence | rep+ |\n| list_sequences | List all defined email sequences with step count and enrollment count | any |\n| generate_quote | Generate a professional HTML quote with line items, VAT, subtotal, total | rep+ |\n| get_quote_status | Retrieve a generated quote by number or list all quotes for a customer | any |\n| get_booking_link | Get a Calendly booking link for a customer — optionally pre-fills name/email | rep+ |\n| create_ticket | Create a support ticket with auto-calculated SLA due date based on priority | rep+ |\n| update_ticket | Update ticket status or assignee (resolved auto-sets resolution date) | rep+ |\n| list_tickets | List tickets filtered by customer, status, priority, or assignee | any |\n| close_ticket | Close a ticket and optionally log resolution as an interaction | rep+ |\n| send_nps_survey | Generate NPS/CSAT survey token + HTML email draft (does not send automatically) | rep+ |\n| get_survey_results | NPS score, promoter/passive/detractor breakdown, all responses for a survey | any |\n| search_knowledge_base | Full-text search across KB articles (title, body, tags) with category and public filters | any |\n| create_kb_article | Create a new knowledge base article stored as Markdown in .agentic/knowledge-base/ | rep+ |\n| backup_now | Trigger immediate backup of customers/ + .agentic/ with SHA-256 integrity check | admin |\n| list_backups | List available backups with date, size, verification status, and customer count | any |\n| trigger_sync | Force immediate Gmail sync for one or all customers | rep+ |\n| get_audit_log | Read audit log — all write operations with actor, tool, customer | admin |\n| define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |\n| create_record | Create a record of a custom object (validated against its schema) | rep+ |\n| list_records | List records of a custom object | any |\n| list_custom_objects | List all defined custom objects and their schemas | any |\n\n## MCP Resources (read-only)\n\nBesides Tools, the server exposes read-only Resources you can fetch via resources/read:\n- crm://customers — list of all customer slugs (JSON)\n- crm://customer/{slug} — LLM-ready briefing (main facts, recent interactions, pipeline)\n- crm://pipeline/{slug} — deals for a customer (JSON)\n- crm://timeline/{slug} — newest-first interaction history (Markdown)\n\n## MCP Prompts (playbooks)\n\nReusable playbook prompts via prompts/get (argument: slug):\n- deal_risk_review — assess deal health and risk, recommend next steps\n- draft_follow_up — draft a personalized follow-up email\n- account_brief — concise executive account brief\n- pipeline_summary — pipeline + forecast summary\n\n## Tool Reference\n\n### get_capabilities()\nReturns all available MCP tools, their inputs, and the CRM workflow guide.\n- Input: none\n- Returns: This guide text\n\n### get_active_session()\nCheck which customer is currently active in the session store.\n- Input: none\n- Returns: { hasSession: boolean, customerSlug?, customerName?, startedAt?, owner? }\n\n### get_customer_context({ slug? })\nLoad complete briefing for a customer. Reads main_facts.md, last 10 interactions,\nand pipeline deals. Returns a structured markdown context block.\nAutomatically triggers a background Gmail sync if last sync was >30 minutes ago.\n- Input: { slug?: string } — Customer ID (e.g. \"acme-corp\"). Leave empty for active session.\n- Returns: Formatted markdown with Quick Reference, Contacts, Critical Context,\n Recent Activity, Pipeline, and Open Questions\n- Performance: <3 seconds. Token budget: <3000 tokens.\n\n### search_customer_knowledge({ slug, query, limit? })\nHybrid vector + full-text search across all emails and transcripts for a customer.\nSearches the LanceDB docs table for the given customer.\n- Input: { slug: string, query: string, limit?: number (default 5, max 50) }\n- Returns: { results: Array<{ content, score, source }> }\n\n### list_customers({ filter? })\nList all customers with their stage, last interaction date, and deal value.\nRBAC: rep role only sees owned customers.\n- Input: { filter?: string } — Optional substring filter on name or slug (case-insensitive)\n- Returns: Array of { slug, name, stage, lastInteraction?, dealValue? }\n\n### log_interaction({ slug, type, summary, with, nextSteps?, direction?, source?, date? })\nWrite a new interaction entry to interactions.md. Immediately searchable.\nAlso auto-updates the relationship graph and health scores (fire-and-forget).\nUse after every call, meeting, or email.\nRBAC: rep+\n- Input:\n slug: Customer ID\n type: \"Email\" | \"Call\" | \"Meeting\" | \"Note\" | \"Demo\" | \"Proposal\" | \"Contract\" | \"Other\"\n summary: 2-5 sentences describing what happened\n with: Who was involved (name or email)\n nextSteps?: Array of action items\n direction?: \"inbound\" | \"outbound\"\n source?: Source reference string (auto-generated if omitted)\n date?: Interaction date YYYY-MM-DD (defaults to today)\n- Returns: { success: boolean, path: string, entry: string }\n\n### update_deal({ slug, dealName, stage?, value?, probability?, closeDate?, notes? })\nUpdate or create a deal in pipeline.md. Upserts by deal name.\nRBAC: rep+\n- Input:\n slug: Customer ID\n dealName: Deal name (used as unique key)\n stage?: \"lead\" | \"qualified\" | \"proposal\" | \"negotiation\" | \"won\" | \"lost\"\n value?: Deal value in euros\n probability?: Win probability (0-100)\n closeDate?: Expected close date (YYYY-MM-DD)\n notes?: Free-text notes\n- Returns: { success: boolean, deal: object }\n\n### update_customer_facts({ slug, name?, domain?, email?, phone?, industry?, relationshipStage?, dealValue?, primaryContact?, timezone?, tags? })\nUpdate fields in a customer's main_facts.md profile. Merges patch into existing data. Sets updated = today.\nRBAC: admin\n- Input: slug (required) + any combination of the optional fields\n- Returns: { success: boolean, facts: object }\n\n### export_customer({ slug, format? })\nExport all customer data (main_facts + interactions count + pipeline + attachments list).\nRBAC: admin\n- Input: { slug: string, format?: \"json\" | \"markdown\" (default \"json\") }\n- Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments }\n- Returns (Markdown): Formatted document with all sections\n\n### get_deal_health({ slug })\nScore the health of all deals for a customer based on activity recency, stage velocity,\nclose date proximity, and probability.\n- Input: { slug: string }\n- Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }\n\n### get_pipeline_forecast({ filter? })\nAggregate weighted pipeline revenue across all customers. Groups open deals by stage,\ncomputes probability-weighted expected revenue. Excludes won/lost deals.\n- Input: { filter?: string } — Optional filter by customer slug substring\n- Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count, weightedValue } } }\n\n### get_pipeline_stages()\nReturns all configured pipeline stages. Falls back to default stages if no custom stages configured.\n- Input: none\n- Returns: { stages: [{ id, label, order, probability, color, final }] }\n\n### summarize_meeting({ slug, transcript, with?, date? })\nLLM-summarize a meeting transcript and log it as a Meeting interaction.\nFalls back to raw text slice if LLM unavailable.\nRBAC: rep+\n- Input:\n slug: Customer ID\n transcript: Full meeting transcript text\n with?: Participant names\n date?: Meeting date YYYY-MM-DD (defaults to today)\n- Returns: { success, summary, nextSteps, sourceRef }\n\n### get_market_intelligence({ query, excludeCurrentCustomer?, slug? })\nSearch across all customers to find patterns, common topics, or similar issues.\nUses semantic search (LanceDB) across all customer knowledge bases.\nResults use slug (not real names) for privacy.\n- Input: { query: string, excludeCurrentCustomer?: boolean, slug?: string }\n- Returns: { query, results: CrossCustomerResult[], totalCustomersSearched }\n\n### get_relationship_graph({ slug })\nReturns the knowledge graph for a customer: contacts, companies, and their relationships.\nAuto-populated from every log_interaction call. Shows stakeholder map with champions, blockers,\neconomic buyers, and warm intro paths.\n- Input: { slug: string }\n- Returns: { nodeCount, edgeCount, updatedAt, stakeholders: { champions[], blockers[], economicBuyers[], allContacts[], missingRoles[] }, warmIntroPaths[], nodes[], edges[] }\n\n### get_relationship_health({ slug })\nReturns health scores (0-100, A-F grade) for all contacts. Scores decay when cadence breaks.\nRisk flags: NO_CONTACT_14D, NO_CONTACT_30D, CHAMPION_SILENT.\nRecomputes automatically if stale (>1h) or missing.\n- Input: { slug: string }\n- Returns: { overallHealth, updatedAt, atRiskContacts[], coldContacts[], contacts: ContactHealth[] }\n\n### run_deal_agent({ slug, dealName, autonomyLevel?, instruction?, valueThreshold? })\nAnalyzes deal situation (health, relationships, stakeholder gaps) via LLM (rule-based fallback).\nReturns prioritized action plan with confidence scores and full reasoning trace.\nautonomyLevel: \"observe\" (read-only) | \"suggest\" (queue for review, default) | \"act\" (auto-execute if confidence ≥ 0.7 and value < valueThreshold)\nRBAC: rep+\n- Input: { slug, dealName, autonomyLevel?: \"observe\"|\"suggest\"|\"act\", instruction?, valueThreshold?: number (default 50000) }\n- Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], trace }\n\n### approve_agent_action({ slug, actionId, approved })\nExecute (approved=true) or reject (approved=false) a pending deal agent action.\nFind actionId in run_deal_agent response.actionsQueued[].actionId\nRBAC: rep+\n- Input: { slug, actionId, approved: boolean }\n- Returns: { success, actionId, status }\n\n### simulate_revenue({ horizon?, iterations? })\nMonte Carlo simulation over all active deals. Adjusts probabilities via health score (D12) and\nchampion presence (D11). Returns P10/P50/P90 confidence interval + sensitivity map.\nhorizon: \"quarter\" (default) | \"year\"\n- Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMonth, topRisks, sensitivityMap }, confidence, dealCount, horizon }\n\n### get_playbook({ slug, stage?, value?, healthScore?, daysSinceContact?, championPresent? })\nReturns playbooks matching the current deal situation. Without deal context, returns all playbooks.\nPlaybooks are sorted by success rate (highest first). run_deal_agent uses playbooks automatically.\n- Input: slug (required) + optional deal context fields for trigger matching\n- Returns: { matches: [{ name, score, matchedConditions, trigger, successRate, usedCount, content }], totalPlaybooks, slug }\n\n### create_playbook({ slug, name, trigger, content, successRate? })\nCreate or update a playbook encoding proven tactics for a specific deal situation.\nTrigger DSL uses AND-only conditions: deal_stage_<s> | value > N | value < N | days_stalled > N | health < N | health > N | no_champion | has_champion\nRBAC: rep+\n- Input: slug, name, trigger (DSL string), content (markdown), successRate? (0–1, default 0.5)\n- Returns: { success: true, playbook: { name, trigger, successRate, usedCount, lastUpdated, path } }\n\n### list_playbooks({ slug })\nList all playbooks for a customer (metadata only — no body content for performance).\n- Input: { slug: string }\n- Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }\n\n### distill_playbook({ slug, dealName, outcome })\nLLM analyzes a deal's interaction history and extracts a reusable playbook.\nRun after every won or lost deal to build procedural memory.\nRBAC: rep+\noutcome: \"won\" | \"lost\"\n- Returns: { success: true, playbook: { name, trigger, successRate, path }, reasoning }\n\n### pursue_goal({ goal, deadline, context? })\nSet a revenue or pipeline goal and get an AI-decomposed action plan.\nAnalyzes current pipeline (P50 forecast) and decomposes the gap into prioritized sub-goals per deal.\nPersists goal to .agentic/goals.json for tracking.\nRBAC: manager+\n- Input: { goal: string, deadline: \"YYYY-MM-DD\", context?: string }\n- Returns: { goalId, description, target, deadline, decomposition: { analysis, currentPipeline, gap, subGoals, probabilisticOutcome } }\n\n### get_goal_status({ goalId? })\nGet the status of active goals. Without goalId, returns all active goals.\n- Input: { goalId?: string } — omit for all active goals\n- Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }\n\n### register_push_subscription({ provider, slug, webhookUrl, ... })\nRegister a real-time push subscription so providers send events in real-time (no polling).\nRBAC: admin only\n- Input:\n provider: \"gmail\" | \"microsoft-graph\" | \"slack\"\n slug: Customer slug to receive events for\n webhookUrl: Public HTTPS URL for provider callbacks\n gmailTopicName?: (Gmail) Cloud Pub/Sub topic name\n microsoftClientState?: (MS Graph) Secret for HMAC verification\n microsoftResource?: (MS Graph) Resource path\n slackTeamId?: (Slack) Workspace team ID\n slackChannelId?: (Slack) Optional specific channel\n- Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning? }\n\n### get_push_status({ slug?, provider? })\nShow all push subscriptions with expiry and event counts.\n- Input: { slug?: string, provider?: \"gmail\" | \"microsoft-graph\" | \"slack\" }\n- Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHours, needsRenewal, lastEventAt, eventsProcessed }], summary: { total, active, expiringSoon, expired } }\n\n### get_org_intelligence({ slug, dealName? })\nBuild a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.\n- Input: { slug: string, dealName?: string }\n- Returns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }\n\n### open_deal_room({ slug, dealName })\nMulti-agent deal brief: orchestrates relationship graph, health scores, deal health, Monte Carlo simulation, and playbook matching into a unified brief with executive summary, top priorities, and risk score (0–100).\n- Input: { slug: string, dealName: string }\n- Returns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }\n\n### get_proactive_briefing({ date? })\nGenerate a proactive daily briefing: urgent alerts (relationship decay, deal risk, overdue close dates),\npipeline forecast (P50/P90), and a single top-action recommendation.\n- Input: { date?: \"YYYY-MM-DD\" } — defaults to today\n- Returns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }\n\n### list_email_templates({ category? })\nList available email templates. Optionally filter by category.\n- Input: { category?: string } — e.g. \"outreach\", \"followup\", \"support\"\n- Returns: Array of { id, name, category, subject } (body excluded for performance)\n\n### get_email_template({ id })\nGet a specific email template with full body and detected template variables.\n- Input: { id: string } — Template ID (e.g. \"enterprise-intro\")\n- Returns: { id, name, category, subject, body, detectedVariables: string[] }\n\n### draft_email({ slug, templateId, overrides?, tone? })\nDraft a personalized email for a customer using a stored template.\nVariables are auto-filled from the customer's main_facts.md.\nOptional tone (e.g. \"formal\", \"friendly\", \"concise\") LLM-polishes the body;\nfalls back to plain template-fill without an ANTHROPIC_API_KEY.\nDoes NOT send automatically — returns the draft for review.\nRBAC: rep+\n- Input: { slug, templateId, overrides?: Record<string, string> }\n- Returns: { subject, body, to, slug, templateId, resolvedVariables }\n\n### enroll_in_sequence({ slug, contactEmail, sequenceId })\nEnroll a contact in an email sequence. Validates that the sequence and first template exist.\nRBAC: rep+\n- Input: { slug: string, contactEmail: string, sequenceId: string }\n- Returns: { enrollmentId, sequenceName, totalSteps }\n\n### list_sequence_enrollments({ slug?, status? })\nList email sequence enrollments. Filter by customer slug or status.\n- Input: { slug?: string, status?: \"active\" | \"paused\" | \"completed\" }\n- Returns: { enrollments: SequenceEnrollment[] }\n\n### unenroll_from_sequence({ enrollmentId })\nPause (soft-unenroll) a contact from an email sequence. Sets status to \"paused\".\nRBAC: rep+\n- Input: { enrollmentId: string }\n- Returns: { success: boolean }\n\n### list_sequences()\nList all defined email sequences with step count and current enrollment count.\n- Input: none\n- Returns: { sequences: [{ id, name, stepCount, enrollmentCount }] }\n\n### generate_quote({ slug, dealName, lineItems, vatPercent?, validUntilDays?, currency? })\nGenerate a professional HTML quote for a customer deal.\nCalculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.\nRBAC: rep+\n- Input:\n slug: Customer slug\n dealName: Deal name this quote is for\n lineItems: Array<{ description, quantity, unitPrice }>\n vatPercent?: VAT percentage (default 19)\n validUntilDays?: Quote validity in days (default 30)\n currency?: Currency code (default EUR)\n- Returns: { quoteNumber, htmlPath, total, subtotal, vat, vatPercent, currency, validUntil, status }\n\n### get_quote_status({ quoteNumber?, slug? })\nGet quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for customer).\n- Input: { quoteNumber?: string, slug?: string }\n- Returns (single): Full quote object with status: draft | sent | viewed | accepted | declined\n- Returns (list): { quotes: [...] }\n\n### get_booking_link({ slug, eventType?, prefillName? })\nGet a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.\nRequires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.\nRBAC: rep+\n- Input: { slug, eventType?: string, prefillName?: boolean }\n- Returns: { bookingUrl, eventType, duration }\n\n### create_ticket({ slug, title, description?, priority?, assignee? })\nCreate a support ticket. Auto-calculates SLA due date based on priority.\nSLA defaults: urgent=4h, high=24h, normal=72h, low=168h.\nRBAC: rep+\n- Input:\n slug: Customer slug\n title: Ticket title\n description?: Detailed description\n priority?: \"urgent\" | \"high\" | \"normal\" | \"low\" (default: normal)\n assignee?: Assignee name or email\n- Returns: { ticket } with id T-NNN, status=open, slaDue\n\n### update_ticket({ slug, ticketId, status?, assignee? })\nUpdate a ticket's status or assignee. Setting status=resolved auto-sets resolved date.\nRBAC: rep+\n- Input: { slug, ticketId, status?: \"open\"|\"in-progress\"|\"waiting\"|\"resolved\"|\"closed\", assignee?: string }\n- Returns: { ticket }\n\n### list_tickets({ slug?, status?, priority?, assignee? })\nList support tickets sorted by priority then date. Filter by any combination of fields.\n- Input: { slug?, status?: \"open\"|\"in-progress\"|\"waiting\"|\"resolved\"|\"closed\", priority?: \"urgent\"|\"high\"|\"normal\"|\"low\", assignee? }\n- Returns: { tickets: Array<{ slug, ticket }> }\n\n### close_ticket({ slug, ticketId, resolution? })\nClose a ticket and optionally log the resolution as an interaction in interactions.md.\nRBAC: rep+\n- Input: { slug, ticketId, resolution?: string }\n- Returns: { ticket } with status=closed\n\n### send_nps_survey({ slug, contactEmail, surveyId, serverUrl? })\nGenerate an NPS/CSAT survey email draft. Returns subject, HTML body, and a token-based response URL.\nDoes NOT send automatically — returns draft for review.\nRequires survey definition in .agentic/surveys/.\nRBAC: rep+\n- Input: { slug, contactEmail, surveyId, serverUrl?: string }\n- Returns: { token, subject, body, surveyUrl, note }\n\n### get_survey_results({ surveyId, slug? })\nCalculate NPS score and breakdown for a survey. Optionally filter to a single customer.\n- Input: { surveyId: string, slug?: string }\n- Returns: { surveyId, totalResponses, npsScore (-100 to 100), promoters, passives, detractors, responses: [{ slug, email, score, comment?, respondedAt }] }\n\n### search_knowledge_base({ query, category?, publicOnly?, limit? })\nFull-text search across all KB articles (title, body, tags).\n- Input: { query, category?: string, publicOnly?: boolean, limit?: number (default 10) }\n- Returns: { query, count, articles: [{ id, title, category, excerpt, public, tags }] }\n\n### create_kb_article({ id, title, body, category?, tags?, public?, sourceTicketId? })\nCreate a new knowledge base article. Articles are stored as Markdown in .agentic/knowledge-base/.\nReturns an error if an article with the same ID already exists.\nRBAC: rep+\n- Input:\n id: Article slug (e.g. \"troubleshoot-api-timeout\")\n title: Article title\n body: Article body in Markdown\n category?: Category (default: \"general\")\n tags?: Array of tags for search\n public?: Make publicly accessible (default: false)\n sourceTicketId?: Ticket this article was created from\n- Returns: { id, title, category, path }\n\n### backup_now({ remote?, note? })\nTrigger an immediate backup of all CRM data (customers/ + .agentic/).\nCreates a timestamped ZIP with SHA-256 manifest. Optionally uploads to S3/rsync/local.\nRBAC: admin\n- Input: { remote?: string, note?: string }\n- Returns: { path, createdAt, customerCount, fileCount, sizeMb, directories, verified, uploadedTo? }\n\n### list_backups({ limit? })\nList available CRM backups with metadata. Shows log-tracked backups first, falls back to directory scan.\n- Input: { limit?: number (default 10, max 50) }\n- Returns: { count, totalAvailable, backups: [{ filename, createdAt, sizeMb, verified, encrypted, customerCount, fileCount }] }\n\n## Data Structure\n\nCustomer data lives in \\`customers/<slug>/\\`:\n- \\`main_facts.md\\` — YAML frontmatter + free-text sections\n- \\`interactions.md\\` — Chronological log (newest first)\n- \\`pipeline.md\\` — Deal table in Markdown\n- \\`sources.json\\` — Gmail/transcript sync config per customer\n\nAgentic data lives in \\`.agentic/\\`:\n- \\`goals.json\\` — Active goals and decompositions\n- \\`push-subscriptions.json\\` — Real-time push registrations\n- \\`backup-log.json\\` — Backup history\n- \\`rbac.json\\` — Role assignments\n- \\`audit.log\\` — Full audit trail\n- \\`knowledge-base/\\` — KB articles\n- \\`quotes/\\` — Generated quote files\n\n## Response Format\n\nAlways cite sources (gmail://thread/... or file://...) when available.\n\n## Framework Integration\n\n| Framework | Tier | Config |\n|---|---|---|\n| Claude Code | 1 | CLAUDE.md + ~/.claude.json + .claude/settings.json |\n| Codex CLI | 1 | AGENTS.md + ~/.codex/config.toml |\n| Grok Build (xAI) | 1 | AGENTS.md + ~/.grok/user-settings.json + .grok/settings.json |\n| OpenClaw | 1 | SOUL.md + AGENTS.md + TOOLS.md |\n| Hermes Agent | 1 | SOUL.md + Skill |\n| Antigravity CLI | 1 | GEMINI.md + AGENTS.md + SKILL.md |\n| Cursor | 2 | .cursor/rules/datasynx-crm.mdc |\n| Windsurf | 2 | MCP config only |\n| Cline | 2 | MCP config only |\n| Claude Desktop | 2 | MCP config only |\n\n### Manual Grok Build configuration\n\\`\\`\\`json\n// ~/.grok/user-settings.json (mcpServers is an ARRAY in Grok, not a map)\n{\n \"mcpServers\": [\n {\n \"name\": \"datasynx-opencrm\",\n \"transport\": {\n \"type\": \"stdio\",\n \"command\": \"node\",\n \"args\": [\"/path/to/node_modules/datasynx-opencrm/dist/mcp.js\"],\n \"env\": { \"DXCRM_DATA_DIR\": \"~/.dxcrm\" }\n }\n }\n ]\n}\n\\`\\`\\`\nRun \\`grok inspect\\` to verify the server is discovered.\n\n## CLI Reference (Phase 2)\n\n### dxcrm status\nShow daemon status, customer count, sync ages, and unmatched transcript queue.\n\\`\\`\\`\ndxcrm status\ndxcrm status --unmatched # list full unmatched transcript queue\n\\`\\`\\`\n\n### dxcrm agent spawn <slug>\nSpawn a wake-triggered agent for a customer. Sends Telegram notifications on new email.\n\\`\\`\\`\ndxcrm agent spawn acme-corp --channel telegram\ndxcrm agent spawn acme-corp --channel telegram --chat-id 12345\ndxcrm agent status\ndxcrm agent remove acme-corp\n\\`\\`\\`\nRequires: \\`TELEGRAM_BOT_TOKEN\\` + \\`TELEGRAM_CHAT_ID\\` env vars.\n\n### dxcrm import <path>\nImport customers and interactions from HubSpot or generic CSV export.\n\\`\\`\\`\ndxcrm import contacts.csv --from csv\ndxcrm import hubspot-export.csv --from hubspot\ndxcrm import hubspot-export.csv --from hubspot --dry-run\n\\`\\`\\`\n- Two-pass: creates customers first, then imports activities\n- Idempotent: re-running skips already-imported rows\n- sourceRef format: \\`hubspot://activity/<id>\\` or \\`csv://row/<hash>\\`\n\n## CLI Reference (Phase 3 — Team)\n\n### dxcrm server start\nStart a shared HTTP MCP server. Multiple team members connect via URL.\n\\`\\`\\`\ndxcrm server start --data /mnt/crm-data --port 3847\ndxcrm server status\n\\`\\`\\`\nSet actor identity: \\`export DXCRM_ACTOR=alice\\`\n\n### dxcrm audit\nShow who changed what and when. Audit trail at \\`.agentic/audit.log\\`.\n\\`\\`\\`\ndxcrm audit # Last 20 entries\ndxcrm audit --slug acme-corp # Filter by customer\ndxcrm audit --actor alice # Filter by actor\n\\`\\`\\`\nLog format: \\`2026-06-01T09:14:00Z | alice | log_interaction | acme-corp | summary\\`\n\n### session ownership\n\\`\\`\\`\ndxcrm session open acme-corp --owner alice\n# or: DXCRM_ACTOR=alice dxcrm session open acme-corp\n\\`\\`\\`\n\\`get_active_session()\\` returns \\`{ owner: \"alice\", ... }\\` when owner is set.\n\n## CLI Reference (Phase 5 — Migration)\n\n### dxcrm import — Pipedrive API\n\\`\\`\\`\ndxcrm import --from pipedrive --mode api --token <tok> --url https://myco.pipedrive.com\n\\`\\`\\`\nTwo-pass: persons → customers, activities → interactions.\nsourceRef: \\`pipedrive://activity/<id>\\`\n\n### CSV LLM Field Mapping\nGeneric CSV imports now use LLM-assisted column detection (fallback to heuristics when ANTHROPIC_API_KEY is unset).\n\n## CLI Reference (Enterprise — Sprints R1–R5)\n\n### dxcrm stages\nManage custom pipeline stages.\n\\`\\`\\`\ndxcrm stages list # List all stages\ndxcrm stages set <id> <label> [--order N] [--probability N] [--color #hex] [--final]\ndxcrm stages delete <id> # Remove a stage\ndxcrm stages reset # Reset to defaults\n\\`\\`\\`\nDefault stages: lead → qualified → proposal → negotiation → won → lost\n\n### dxcrm rbac\nRole-based access control. Roles: admin > manager > rep.\n\\`\\`\\`\ndxcrm rbac set alice admin # Assign role\ndxcrm rbac show # List all roles\ndxcrm rbac check alice update_deal # Permission check\n\\`\\`\\`\nConfig: \\`.agentic/rbac.json\\` | Actor: \\`DXCRM_ACTOR\\` env var\nEnforcement: per MCP tool call | Default role: rep\n\n### dxcrm gdpr\nGDPR erasure with dry-run safety and audit trail.\n\\`\\`\\`\ndxcrm gdpr erase acme-corp # Dry-run (shows plan)\ndxcrm gdpr erase acme-corp --confirm # Permanent deletion\ndxcrm gdpr list-erasures # Erasure history\n\\`\\`\\`\nOn confirm: deletes customers/<slug>/, writes audit.log, appends gdpr-erasures.json\n\n### dxcrm security-report\nGenerate Markdown security questionnaire for procurement/SOC2 review.\n\\`\\`\\`\ndxcrm security-report\ndxcrm security-report --output sec-report.md\n\\`\\`\\`\n\n### Microsoft Outlook Sync\n\\`\\`\\`\ndxcrm sync --provider microsoft\n\\`\\`\\`\nPrerequisites: write \\`.agentic/microsoft-token.json\\` with \\`{ \"accessToken\": \"...\" }\\`\nsourceRef: \\`microsoft://message/<id>\\` | API: Microsoft Graph v1.0\n\n### Salesforce Import\n\\`\\`\\`\ndxcrm import --from salesforce --mode api --token <tok> --url https://myco.salesforce.com\n\\`\\`\\`\nTwo-pass: contacts → customers, tasks → interactions (WhoId attribution)\nsourceRef: \\`salesforce://task/<id>\\` | API: Salesforce REST v58.0\n\n## CLI Reference (D16 — Goal-Based Orchestration)\n\n### dxcrm goal set\nSet a goal and get a decomposed action plan based on current pipeline state.\n\\`\\`\\`\ndxcrm goal set \"Close €500k ARR this quarter\" --deadline 2026-09-30\n\\`\\`\\`\n\n### dxcrm goal status\nShow all active goals with progress bars and days remaining.\n\\`\\`\\`\ndxcrm goal status\n\\`\\`\\`\n\n### dxcrm goal update\nManually update goal progress (0–100%).\n\\`\\`\\`\ndxcrm goal update goal_abc123 --progress 45\n\\`\\`\\`\n\n### dxcrm goal cancel\nCancel an active goal.\n\\`\\`\\`\ndxcrm goal cancel goal_abc123\n\\`\\`\\`\n\n## CLI Reference (D17 — Real-Time Push Ingestion)\n\n### dxcrm push register\nRegister a push subscription so providers send events in real-time (no polling).\n\\`\\`\\`\ndxcrm push register acme-corp --provider gmail --webhook-url https://myserver.com/webhooks/gmail --topic-name projects/x/topics/gmail-push\ndxcrm push register acme-corp --provider microsoft-graph --webhook-url https://myserver.com/webhooks/microsoft --client-state <secret>\ndxcrm push register acme-corp --provider slack --webhook-url https://myserver.com/webhooks/slack --team-id T12345\n\\`\\`\\`\n\n### dxcrm push status\nShow all push subscriptions, expiry and events processed.\n\\`\\`\\`\ndxcrm push status\ndxcrm push status --slug acme-corp\ndxcrm push status --provider gmail\n\\`\\`\\`\n\n### dxcrm push revoke\nRevoke a push subscription by ID.\n\\`\\`\\`\ndxcrm push revoke psub_1716892800_a1b2c3\n\\`\\`\\`\n\n### dxcrm push renew\nRenew expiring push subscriptions (also runs automatically daily at 06:00).\n\\`\\`\\`\ndxcrm push renew --all\n\\`\\`\\`\n\n### register_push_subscription (MCP)\nRegister a real-time push subscription. Admin only.\n\\`\\`\\`\nregister_push_subscription({ provider: \"gmail\", slug: \"acme-corp\", webhookUrl: \"https://myserver.com/webhooks/gmail\", gmailTopicName: \"projects/x/topics/y\" })\n\\`\\`\\`\nReturns: { subscriptionId, provider, slug, status, expiresAt, warning? }\n\n### get_push_status (MCP)\nShow all push subscriptions with expiry and event counts.\n\\`\\`\\`\nget_push_status() // all subscriptions\nget_push_status({ slug: \"acme-corp\" }) // filter by customer\nget_push_status({ provider: \"gmail\" }) // filter by provider\n\\`\\`\\`\nReturns: { subscriptions: [...], summary: { total, active, expiringSoon, expired } }\n\n### get_org_intelligence (MCP)\nBuild a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.\n\\`\\`\\`\nget_org_intelligence({ slug: \"acme-corp\" })\nget_org_intelligence({ slug: \"acme-corp\", dealName: \"Enterprise License\" })\n\\`\\`\\`\nReturns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }\n\n### open_deal_room (MCP)\nMulti-agent deal brief: orchestrates stakeholder map, relationship health, deal health, Monte Carlo simulation, and playbook matching into a single structured brief.\n\\`\\`\\`\nopen_deal_room({ slug: \"acme-corp\", dealName: \"Enterprise License 2026\" })\n\\`\\`\\`\nReturns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }\n\n### get_proactive_briefing (MCP)\nGenerate a proactive daily briefing: urgent alerts (relationship decay, imminent close dates), opportunities (high-health customers with active pipeline), P50/P90 forecast, and a single top-action recommendation.\n\\`\\`\\`\nget_proactive_briefing() // today\nget_proactive_briefing({ date: \"2026-05-28\" }) // specific date\n\\`\\`\\`\nReturns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }\n\n## H2 — Email Templates\n\n### list_email_templates (MCP)\nList all saved email templates. Returns id, name, category, subject, and body preview.\n\\`\\`\\`\nlist_email_templates()\nlist_email_templates({ category: \"follow-up\" })\n\\`\\`\\`\nReturns: { templates: [{ id, name, category, subject, bodyPreview }] }\n\n### get_email_template (MCP)\nRetrieve a single email template with full body and all variables.\n\\`\\`\\`\nget_email_template({ id: \"proposal-follow-up\" })\n\\`\\`\\`\nReturns: { id, name, category, subject, body, variables: string[] }\n\n### draft_email (MCP)\nDraft a personalized email from a template, substituting variables from customer context.\n\\`\\`\\`\ndraft_email({ slug: \"acme-corp\", templateId: \"proposal-follow-up\", overrides: { subject: \"Following up on your proposal\" } })\n\\`\\`\\`\nReturns: { subject, body, suggestedTo, suggestedCc?, variables }\n\n## H1 — Email Sequences\n\n### enroll_in_sequence (MCP)\nEnroll a customer contact in a multi-step email sequence. Steps are sent automatically.\n\\`\\`\\`\nenroll_in_sequence({ slug: \"acme-corp\", sequenceId: \"onboarding-7day\", contactEmail: \"alice@acme.com\" })\n\\`\\`\\`\nReturns: { enrollmentId, slug, sequenceId, contactEmail, enrolledAt, nextStepDue, totalSteps }\n\n### list_sequence_enrollments (MCP)\nList active (and optionally completed) sequence enrollments.\n\\`\\`\\`\nlist_sequence_enrollments()\nlist_sequence_enrollments({ slug: \"acme-corp\", status: \"active\" })\n\\`\\`\\`\nReturns: { enrollments: [{ enrollmentId, slug, sequenceId, contactEmail, currentStep, nextStepDue, status }] }\n\n### unenroll_from_sequence (MCP)\nRemove a contact from an active sequence (marks as cancelled).\n\\`\\`\\`\nunenroll_from_sequence({ enrollmentId: \"enr_abc123\" })\n\\`\\`\\`\nReturns: { success: boolean, enrollmentId }\n\n### list_sequences (MCP)\nList all defined email sequences with step count and description.\n\\`\\`\\`\nlist_sequences()\n\\`\\`\\`\nReturns: { sequences: [{ id, name, description, steps: number, triggerOn? }] }\n\n## H4 — Quotes\n\n### generate_quote (MCP)\nGenerate a structured quote document for a customer deal.\n\\`\\`\\`\ngenerate_quote({ slug: \"acme-corp\", dealName: \"Enterprise License\", lineItems: [{ description: \"Platform (12 mo)\", quantity: 1, unitPrice: 24000 }], validDays: 30 })\n\\`\\`\\`\nReturns: { quoteId, slug, dealName, total, currency, validUntil, markdownTable, fullText }\n\n### get_quote_status (MCP)\nRetrieve a generated quote with full line items and total.\n\\`\\`\\`\nget_quote_status({ quoteId: \"Q-2026-001\" })\n\\`\\`\\`\nReturns: { quoteId, slug, dealName, lineItems, subtotal, total, validUntil, status }\n\n## H3 — Meeting Scheduler\n\n### get_booking_link (MCP)\nGet a scheduling link for a meeting with a customer. Configure via DXCRM_CALENDLY_URL or per-customer sources.json.\n\\`\\`\\`\nget_booking_link({ slug: \"acme-corp\", meetingType: \"demo\" })\n\\`\\`\\`\nReturns: { url, meetingType, calendarProvider, prefillEmail?, note? }\n\n## H6 — Ticket Management\n\n### create_ticket (MCP)\nCreate a support ticket. Auto-sets SLA due date: critical=4h, high=24h, medium=72h, low=168h.\n\\`\\`\\`\ncreate_ticket({ slug: \"acme-corp\", title: \"Login broken\", priority: \"high\", description: \"Cannot login since yesterday\", assignee: \"alice\" })\n\\`\\`\\`\nReturns: { ticketId, slug, title, priority, status, slaDue, assignee?, createdAt }\n\n### update_ticket (MCP)\nUpdate ticket status or assignee.\n\\`\\`\\`\nupdate_ticket({ slug: \"acme-corp\", ticketId: \"T-001\", status: \"in-progress\", assignee: \"bob\" })\n\\`\\`\\`\nReturns: { ticketId, status, updatedAt }\n\n### list_tickets (MCP)\nList tickets sorted by priority. Filter by customer, status, priority, or assignee.\n\\`\\`\\`\nlist_tickets()\nlist_tickets({ slug: \"acme-corp\", status: \"open\" })\nlist_tickets({ priority: \"high\", assignee: \"alice\" })\n\\`\\`\\`\nReturns: { tickets: [{ ticketId, slug, title, priority, status, slaDue, assignee?, createdAt }] }\n\n### close_ticket (MCP)\nClose a ticket and optionally log a resolution note to interactions.md.\n\\`\\`\\`\nclose_ticket({ slug: \"acme-corp\", ticketId: \"T-001\", resolution: \"Fixed by updating oauth token\" })\n\\`\\`\\`\nReturns: { ticketId, status: \"closed\", closedAt, resolution? }\n\n## H7 — NPS/CSAT Survey Engine\n\n### send_nps_survey (MCP)\nGenerate a survey token and HTML email body. Customers click a score button (0–10) which\nposts to your server's /survey/respond endpoint. Set DXCRM_SERVER_URL or pass serverUrl.\n\\`\\`\\`\nsend_nps_survey({ slug: \"acme-corp\", contactEmail: \"alice@acme.com\", surveyId: \"q1-nps\" })\nsend_nps_survey({ slug: \"acme-corp\", contactEmail: \"alice@acme.com\", surveyId: \"q1-nps\", serverUrl: \"https://crm.myco.com\" })\n\\`\\`\\`\nReturns: { token, emailSubject, emailBody (HTML), surveyId, expiresAt }\n\n### get_survey_results (MCP)\nCalculate NPS score and breakdown by promoter/passive/detractor.\n\\`\\`\\`\nget_survey_results({ surveyId: \"q1-nps\" })\nget_survey_results({ surveyId: \"q1-nps\", slug: \"acme-corp\" })\n\\`\\`\\`\nReturns: { surveyId, npsScore (-100 to 100), responseCount, promoters, passives, detractors, responses: [{ slug, contactEmail, score, comment?, respondedAt }] }\n\n## H8 — Knowledge Base\n\n### search_knowledge_base (MCP)\nFull-text search across all KB articles (title, body, tags).\n\\`\\`\\`\nsearch_knowledge_base({ query: \"password reset\" })\nsearch_knowledge_base({ query: \"billing\", publicOnly: true })\n\\`\\`\\`\nReturns: { results: [{ id, title, category, excerpt, public, tags }] }\n\n### create_kb_article (MCP)\nCreate or update a knowledge base article (upserts by ID).\n\\`\\`\\`\ncreate_kb_article({ id: \"password-reset\", title: \"How to reset your password\", body: \"## Steps\\\\n1. Go to login...\", category: \"account\", tags: [\"password\", \"auth\"], public: true })\n\\`\\`\\`\nReturns: { id, title, createdAt, updatedAt, public }\n\n## Enterprise Backup\n\n### backup_now (MCP)\nTrigger an immediate backup of customers/ + .agentic/. Creates a timestamped ZIP with\nSHA-256 manifest. Optionally encrypts (AES-256-GCM) and uploads to S3/rsync/local.\n\\`\\`\\`\nbackup_now({})\nbackup_now({ remote: \"s3://my-bucket/crm-backups/\", note: \"Pre-migration backup\" })\n\\`\\`\\`\nReturns: { path, createdAt, customerCount, fileCount, sizeMb, directories, verified, uploadedTo? }\n\n### list_backups (MCP)\nList available CRM backups with metadata from .agentic/backup-log.json.\nFalls back to directory scan if log unavailable.\n\\`\\`\\`\nlist_backups({ limit: 10 })\n\\`\\`\\`\nReturns: { count, totalAvailable, backups: [{ filename, createdAt, sizeMb, verified, encrypted, customerCount, fileCount }] }\n\n### trigger_sync (MCP)\nForce an immediate Gmail sync without waiting for the 30-minute daemon cycle.\n\\`\\`\\`\ntrigger_sync({ slug: \"acme-corp\" }) // sync one customer\ntrigger_sync({}) // sync all customers\ntrigger_sync({ since: \"2026-06-01\" }) // sync from specific date\n\\`\\`\\`\nReturns: { success, synced, skipped, customers: [...], errors: [...] }\n\n### get_audit_log (MCP)\nRead the append-only CRM audit log of all write operations.\n\\`\\`\\`\nget_audit_log({}) // last 50 entries\nget_audit_log({ slug: \"acme-corp\" }) // filtered by customer\nget_audit_log({ actor: \"alice\", limit: 20 }) // filtered by actor\n\\`\\`\\`\nReturns: { total, returned, entries: [{ timestamp, actor, tool, slug, summary }] }\n`.trim();\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { CAPABILITIES_TEXT } from \"../capabilities.js\";\n\nexport async function handleGetCapabilities(): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n return {\n content: [{ type: \"text\", text: CAPABILITIES_TEXT }],\n };\n}\n\nexport function registerGetCapabilities(server: McpServer): void {\n server.registerTool(\n \"get_capabilities\",\n {\n title: \"Get Capabilities\",\n description:\n \"Returns all available MCP tools, their inputs, and the CRM workflow guide. \" +\n \"Call this first to understand what DatasynxOpenCRM can do.\",\n inputSchema: z.object({}),\n },\n async () => handleGetCapabilities()\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getSession } from \"../../core/session-store.js\";\n\nexport interface ActiveSessionResult {\n hasSession: boolean;\n customerSlug?: string;\n customerName?: string;\n startedAt?: string;\n owner?: string;\n}\n\nexport async function handleGetActiveSession(): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const session = getSession();\n\n const result: ActiveSessionResult = session\n ? {\n hasSession: true,\n customerSlug: session.customerSlug,\n customerName: session.customerName,\n startedAt: session.startedAt,\n ...(session.owner !== undefined ? { owner: session.owner } : {}),\n }\n : { hasSession: false };\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n };\n}\n\nexport function registerGetActiveSession(server: McpServer): void {\n server.registerTool(\n \"get_active_session\",\n {\n title: \"Get Active Session\",\n description:\n \"Check which customer is currently active in the session store. \" +\n \"Returns session info if a customer session is open, otherwise returns hasSession: false.\",\n inputSchema: z.object({}),\n },\n async () => handleGetActiveSession()\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\n\nconst MAX_INTERACTIONS = 10;\n\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nfunction parseRecentInteractions(filePath: string, limit: number): string {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n\n // Split on ## date headings\n const entries = content.split(/(?=^## \\d{4}-\\d{2}-\\d{2})/m).filter((e) => e.trim());\n const recent = entries.slice(0, limit);\n return recent.join(\"\\n\").trim();\n}\n\nfunction parsePipelineContent(filePath: string): string {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n return content.trim();\n}\n\nfunction extractSection(content: string, sectionName: string): string {\n const regex = new RegExp(`## ${sectionName}([\\\\s\\\\S]*?)(?=^## |$)`, \"m\");\n const match = regex.exec(content);\n return match ? (match[1] ?? \"\").trim() : \"\";\n}\n\nexport async function buildContext(dataDir: string, slug: string): Promise<string> {\n const customerDir = path.join(dataDir, \"customers\", slug);\n\n if (!fs.existsSync(customerDir)) {\n throw new Error(`Customer '${slug}' not found`);\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n const pipelinePath = path.join(customerDir, \"pipeline.md\");\n\n // Read main_facts.md\n let mainContent = \"\";\n let frontmatterStr = \"\";\n if (fs.existsSync(mainFactsPath)) {\n const fileContent = fs.readFileSync(mainFactsPath, \"utf-8\") as string;\n const raw = matter(fileContent);\n mainContent = raw.content ?? \"\";\n frontmatterStr = Object.entries(raw.data as Record<string, unknown>)\n .map(([k, v]) => `${k}: ${JSON.stringify(v)}`)\n .join(\"\\n\");\n }\n\n const quickRef = extractSection(mainContent, \"Quick Reference\");\n const contacts = extractSection(mainContent, \"Contacts\");\n const criticalContext = extractSection(mainContent, \"Critical Context\");\n const openQuestions = extractSection(mainContent, \"Open Questions\");\n\n const recentActivity = parseRecentInteractions(interactionsPath, MAX_INTERACTIONS);\n const pipelineContent = parsePipelineContent(pipelinePath);\n\n const sections: string[] = [\n `# Customer Context: ${slug}`,\n \"\",\n \"## Metadata\",\n frontmatterStr || \"(no metadata)\",\n \"\",\n \"## Quick Reference\",\n quickRef || \"(not set)\",\n \"\",\n \"## Contacts\",\n contacts || \"(not set)\",\n \"\",\n \"## Critical Context\",\n criticalContext || \"(not set)\",\n \"\",\n \"## Recent Activity (last 10 interactions)\",\n recentActivity || \"(no interactions yet)\",\n \"\",\n \"## Pipeline\",\n pipelineContent || \"(no deals)\",\n \"\",\n \"## Open Questions\",\n openQuestions || \"(none)\",\n ];\n\n const raw = sections.join(\"\\n\");\n const tokenEstimate = estimateTokens(raw);\n\n // If over 3000 tokens, trim interactions\n if (tokenEstimate > 3000) {\n const trimmedActivity = parseRecentInteractions(interactionsPath, 5);\n const trimmedSections: string[] = [\n `# Customer Context: ${slug}`,\n \"\",\n \"## Metadata\",\n frontmatterStr || \"(no metadata)\",\n \"\",\n \"## Quick Reference\",\n quickRef || \"(not set)\",\n \"\",\n \"## Contacts\",\n contacts || \"(not set)\",\n \"\",\n \"## Critical Context\",\n criticalContext || \"(not set)\",\n \"\",\n \"## Recent Activity (last 5 interactions — trimmed for token budget)\",\n trimmedActivity || \"(no interactions yet)\",\n \"\",\n \"## Pipeline\",\n pipelineContent || \"(no deals)\",\n \"\",\n \"## Open Questions\",\n openQuestions || \"(none)\",\n ];\n return trimmedSections.join(\"\\n\");\n }\n\n return raw;\n}\n\n/** Robust section-body extractor: from a `## Name` heading to the next `## ` heading. */\nfunction sectionBody(content: string, name: string): string {\n const lines = content.split(\"\\n\");\n const start = lines.findIndex((l) => l.trim() === `## ${name}`);\n if (start < 0) return \"\";\n const body: string[] = [];\n for (let i = start + 1; i < lines.length; i++) {\n if (lines[i]!.startsWith(\"## \")) break;\n body.push(lines[i]!);\n }\n return body.join(\"\\n\").trim();\n}\n\nexport interface ContextBlock {\n slug: string;\n metadata: Record<string, unknown>;\n quickReference: string;\n contacts: string;\n criticalContext: string;\n openQuestions: string;\n recentActivity: string;\n pipeline: string;\n}\n\n/**\n * Structured variant of buildContext (REF-2): returns a typed object instead of\n * a markdown string, for callers that need fields programmatically (e.g. MCP\n * responses, SDK consumers). buildContext remains the token-budgeted string form.\n */\nexport async function buildContextBlock(\n dataDir: string,\n slug: string,\n role?: \"admin\" | \"manager\" | \"rep\"\n): Promise<ContextBlock> {\n const customerDir = path.join(dataDir, \"customers\", slug);\n if (!fs.existsSync(customerDir)) {\n throw new Error(`Customer '${slug}' not found`);\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n const pipelinePath = path.join(customerDir, \"pipeline.md\");\n\n let mainContent = \"\";\n let metadata: Record<string, unknown> = {};\n if (fs.existsSync(mainFactsPath)) {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\") as string);\n mainContent = raw.content ?? \"\";\n metadata = raw.data as Record<string, unknown>;\n }\n\n // Field-level security: redact metadata fields the role may not see.\n if (role) {\n const { loadFieldAcl, redactFields } = await import(\"./rbac.js\");\n metadata = redactFields(metadata, role, loadFieldAcl(dataDir));\n }\n\n return {\n slug,\n metadata,\n quickReference: sectionBody(mainContent, \"Quick Reference\"),\n contacts: sectionBody(mainContent, \"Contacts\"),\n criticalContext: sectionBody(mainContent, \"Critical Context\"),\n openQuestions: sectionBody(mainContent, \"Open Questions\"),\n recentActivity: parseRecentInteractions(interactionsPath, MAX_INTERACTIONS),\n pipeline: parsePipelineContent(pipelinePath),\n };\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildContext } from \"../../core/context-builder.js\";\nimport { getSession } from \"../../core/session-store.js\";\nimport { getLastGmailSync, updateSlugSyncState } from \"../../fs/sync-state.js\";\nimport { getGmailAuth } from \"../../core/oauth-store.js\";\nimport { canSeeCustomer } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nfunction triggerOnQuerySync(dataDir: string, slug: string): void {\n const auth = getGmailAuth();\n if (!auth) return;\n\n const lastSync = getLastGmailSync(dataDir, slug);\n const thirtyMinAgo = new Date(Date.now() - 30 * 60 * 1000);\n if (lastSync && lastSync >= thirtyMinAgo) return;\n\n const sourcesPath = path.join(dataDir, \"customers\", slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) return;\n\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\") as string) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n if (!sources.gmail?.enabled || !sources.gmail.query) return;\n\n const query = sources.gmail.query;\n void import(\"../../sync/gmail-sync.js\")\n .then(({ syncGmail }) =>\n syncGmail({ slug, dataDir, auth, query })\n .then(() =>\n updateSlugSyncState(dataDir, slug, { lastGmailSync: new Date().toISOString() })\n )\n .catch(() => {})\n )\n .catch(() => {});\n } catch {\n // non-critical\n }\n}\n\nexport async function handleGetCustomerContext(\n input: { slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n}> {\n const targetSlug = input.slug ?? getSession()?.customerSlug;\n\n if (!targetSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No customer specified and no active session. Use: get_customer_context({ slug: 'acme-corp' })\",\n },\n ],\n isError: true,\n };\n }\n\n // RBAC data-visibility: rep role only sees owned customers\n const actor = process.env[\"DXCRM_ACTOR\"] ?? \"system\";\n if (!canSeeCustomer(dataDir, actor, targetSlug)) {\n return {\n content: [\n { type: \"text\", text: `Access denied: '${actor}' cannot view customer '${targetSlug}'.` },\n ],\n isError: true,\n };\n }\n\n // Fire-and-forget on-query sync — does not block context return\n triggerOnQuerySync(dataDir, targetSlug);\n\n try {\n const context = await buildContext(dataDir, targetSlug);\n return {\n content: [{ type: \"text\", text: context }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${(err as Error).message}`,\n },\n ],\n isError: true,\n };\n }\n}\n\nexport function registerGetCustomerContext(server: McpServer): void {\n server.registerTool(\n \"get_customer_context\",\n {\n title: \"Get Customer Context\",\n description: `Returns a complete, LLM-ready context block for a customer.\nUse this before any customer-related conversation or action.\nAutomatically triggers a background Gmail sync if last sync was >30 minutes ago.\n\nArgs:\n slug: Customer ID (e.g. \"acme-corp\"). Leave empty to use active session customer.\n\nReturns: Structured markdown with Quick Reference, Contacts, Critical Context,\nRecent Activity (last 10 interactions), Pipeline, and Open Questions.\n\nPerformance: <3 seconds. Token budget: <3000.`,\n inputSchema: z.object({\n slug: z\n .string()\n .optional()\n .describe(\"Customer slug (e.g. 'acme-corp'). Leave empty for active session customer.\"),\n }),\n },\n async ({ slug }) => handleGetCustomerContext({ ...(slug !== undefined ? { slug } : {}) })\n );\n}\n","import { pipeline, env, type FeatureExtractionPipeline } from \"@huggingface/transformers\";\nimport path from \"path\";\nimport os from \"os\";\n\nenv.cacheDir =\n process.env[\"HF_CACHE_DIR\"] ?? path.join(os.homedir(), \".cache\", \"datasynx-opencrm\", \"models\");\n\nclass EmbeddingPipeline {\n private static instance: Promise<FeatureExtractionPipeline> | null = null;\n\n static get(): Promise<FeatureExtractionPipeline> {\n if (!this.instance) {\n console.error(\"Loading embedding model (first time, ~25MB)...\");\n this.instance = pipeline(\n \"feature-extraction\",\n \"Xenova/all-MiniLM-L6-v2\"\n ) as Promise<FeatureExtractionPipeline>;\n }\n return this.instance;\n }\n\n static reset(): void {\n this.instance = null;\n }\n}\n\nexport async function embedText(text: string): Promise<Float32Array> {\n const extractor = await EmbeddingPipeline.get();\n const output = await extractor(text, { pooling: \"mean\", normalize: true });\n return (output as unknown as Array<{ data: Float32Array }>)[0]?.data ?? new Float32Array(384);\n}\n\nexport async function embedBatch(texts: string[]): Promise<Float32Array[]> {\n if (texts.length === 0) return [];\n const extractor = await EmbeddingPipeline.get();\n const output = await extractor(texts, { pooling: \"mean\", normalize: true });\n return (output as unknown as Array<{ data: Float32Array }>).map(\n (o) => o.data ?? new Float32Array(384)\n );\n}\n\nexport function resetEmbeddingPipeline(): void {\n EmbeddingPipeline.reset();\n}\n","import * as lancedb from \"@lancedb/lancedb\";\nimport { Index } from \"@lancedb/lancedb\";\nimport { makeArrowTable } from \"@lancedb/lancedb\";\nimport { Schema, Field, FixedSizeList, Float32 as ArrowFloat32, Utf8 } from \"apache-arrow\";\nimport path from \"path\";\nimport { embedText } from \"./embedder.js\";\n\nlet _db: lancedb.Connection | null = null;\n\nasync function getDb(dataDir: string): Promise<lancedb.Connection> {\n if (!_db) {\n const dbPath = path.join(dataDir, \".agentic\", \"lancedb\");\n _db = await lancedb.connect(dbPath);\n }\n return _db;\n}\n\n// Reset connection (useful for testing)\nexport function resetConnection(): void {\n _db = null;\n}\n\nconst CUSTOMER_TABLE_SCHEMA = new Schema([\n new Field(\"source_ref\", new Utf8(), false),\n new Field(\"text\", new Utf8(), false),\n new Field(\"date\", new Utf8(), false),\n new Field(\"type\", new Utf8(), false),\n new Field(\"vector\", new FixedSizeList(384, new Field(\"item\", new ArrowFloat32(), true)), false),\n]);\n\nasync function getOrCreateCustomerTable(\n db: lancedb.Connection,\n tableName: string\n): Promise<lancedb.Table> {\n const tableNames: string[] = await db.tableNames();\n if (!tableNames.includes(tableName)) {\n const table = await db.createEmptyTable(tableName, CUSTOMER_TABLE_SCHEMA);\n await table.createIndex(\"source_ref\", { config: Index.btree() });\n return table;\n }\n return db.openTable(tableName);\n}\n\nexport async function indexInLanceDB(\n dataDir: string,\n slug: string,\n text: string,\n sourceRef: string,\n meta?: { date?: string; type?: string }\n): Promise<void> {\n try {\n const vectorFloat32 = await embedText(text);\n const db = await getDb(dataDir);\n const tableName = `docs_${slug.replace(/[^a-z0-9]/gi, \"_\")}`;\n const table = await getOrCreateCustomerTable(db, tableName);\n\n const date = meta?.date ?? new Date().toISOString().slice(0, 10);\n const type = meta?.type ?? \"unknown\";\n\n const data = makeArrowTable([\n {\n source_ref: sourceRef,\n text: text.slice(0, 2000),\n date,\n type,\n vector: Array.from(vectorFloat32),\n },\n ]);\n\n await table\n .mergeInsert(\"source_ref\")\n .whenMatchedUpdateAll()\n .whenNotMatchedInsertAll()\n .execute(data);\n } catch (err) {\n process.stderr.write(`[lancedb] indexInLanceDB failed: ${(err as Error).message}\\n`);\n }\n}\n\nexport async function dropCustomerTable(dataDir: string, slug: string): Promise<void> {\n try {\n const db = await getDb(dataDir);\n const tableName = `docs_${slug.replace(/[^a-z0-9]/gi, \"_\")}`;\n const tableNames: string[] = await db.tableNames();\n if (tableNames.includes(tableName)) {\n await db.dropTable(tableName);\n }\n } catch (err) {\n process.stderr.write(`[lancedb] dropCustomerTable failed: ${(err as Error).message}\\n`);\n }\n}\n\nexport async function searchKnowledge(\n dataDir: string,\n slug: string,\n query: string,\n limit: number\n): Promise<Array<{ content: string; score: number; source: string }>> {\n try {\n const vectorFloat32 = await embedText(query);\n const db = await getDb(dataDir);\n const tableName = `docs_${slug.replace(/[^a-z0-9]/gi, \"_\")}`;\n\n // Check if table exists\n const tableNames: string[] = await db.tableNames();\n if (!tableNames.includes(tableName)) {\n return [];\n }\n\n const table = await db.openTable(tableName);\n\n const results = await table.search(Array.from(vectorFloat32)).limit(limit).toArray();\n\n return results.map((r: Record<string, unknown>) => ({\n content: String(r[\"text\"] ?? \"\"),\n score: typeof r[\"_distance\"] === \"number\" ? 1 - r[\"_distance\"] : 1,\n source: String(r[\"source_ref\"] ?? \"\"),\n }));\n } catch {\n // If LanceDB table doesn't exist or search fails, return empty array\n return [];\n }\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { searchKnowledge } from \"../../core/lancedb.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSearchCustomerKnowledge(\n input: { slug: string; query: string; limit?: number },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const limit = input.limit ?? 5;\n\n try {\n const results = await searchKnowledge(dataDir, input.slug, input.query, limit);\n\n const response =\n results.length === 0\n ? {\n results: [],\n message:\n `No results found for \"${input.query}\" in customer \"${input.slug}\". ` +\n \"The customer may not have been synced yet. Run dxcrm sync to index emails and transcripts.\",\n }\n : { results };\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(response, null, 2) }],\n };\n } catch {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n results: [],\n message: `Search unavailable for customer \"${input.slug}\". LanceDB may not be initialized.`,\n }),\n },\n ],\n };\n }\n}\n\nexport function registerSearchCustomerKnowledge(server: McpServer): void {\n server.registerTool(\n \"search_customer_knowledge\",\n {\n title: \"Search Customer Knowledge\",\n description: `Hybrid vector + full-text search across all emails and transcripts for a customer.\nUse when you need to find specific information from past communications.\n\nArgs:\n slug: Customer ID (e.g. \"acme-corp\")\n query: Natural language search query (e.g. \"pricing discussion\", \"GDPR concerns\")\n limit: Max results to return (default 5)\n\nReturns: { results: Array<{ content, score, source }> }\nIf no results: returns empty array with a helpful sync suggestion.`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n query: z.string().describe(\"Search query (natural language or keywords)\"),\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Max results to return (default 5)\"),\n }),\n },\n async ({ slug, query, limit }) =>\n handleSearchCustomerKnowledge({\n slug,\n query,\n ...(limit !== undefined ? { limit } : {}),\n })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { canSeeCustomer } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport interface CustomerSummary {\n slug: string;\n name: string;\n stage: string;\n lastInteraction?: string | undefined;\n dealValue?: number | undefined;\n}\n\nfunction extractLastInteractionDate(interactionsPath: string): string | undefined {\n if (!fs.existsSync(interactionsPath)) return undefined;\n\n const content = fs.readFileSync(interactionsPath, \"utf-8\") as string;\n // Match first ## YYYY-MM-DD heading\n const match = /^## (\\d{4}-\\d{2}-\\d{2})/m.exec(content);\n return match ? match[1] : undefined;\n}\n\nexport async function handleListCustomers(\n input: { filter?: string },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const customersDir = path.join(dataDir, \"customers\");\n const customers: CustomerSummary[] = [];\n\n if (!fs.existsSync(customersDir)) {\n return {\n content: [{ type: \"text\", text: JSON.stringify([], null, 2) }],\n };\n }\n\n const entries = fs.readdirSync(customersDir) as string[];\n\n for (const entry of entries) {\n const customerDir = path.join(customersDir, entry);\n\n // Skip if not a directory\n try {\n const stat = fs.statSync(customerDir);\n if (!stat.isDirectory()) continue;\n } catch {\n continue;\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n if (!fs.existsSync(mainFactsPath)) continue;\n\n try {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\") as string);\n const data = raw.data as Record<string, unknown>;\n\n const name = typeof data[\"name\"] === \"string\" ? data[\"name\"] : entry;\n const stage =\n typeof data[\"relationship_stage\"] === \"string\" ? data[\"relationship_stage\"] : \"unknown\";\n const dealValue = typeof data[\"deal_value\"] === \"number\" ? data[\"deal_value\"] : undefined;\n\n const lastInteraction = extractLastInteractionDate(path.join(customerDir, \"interactions.md\"));\n\n const summary: CustomerSummary = {\n slug: entry,\n name,\n stage,\n ...(lastInteraction !== undefined ? { lastInteraction } : {}),\n ...(dealValue !== undefined ? { dealValue } : {}),\n };\n\n // Apply filter if provided (name, slug, or stage)\n if (input.filter) {\n const filterLower = input.filter.toLowerCase();\n const matches =\n name.toLowerCase().includes(filterLower) ||\n entry.toLowerCase().includes(filterLower) ||\n stage.toLowerCase().includes(filterLower);\n if (!matches) continue;\n }\n\n // RBAC data-visibility: rep role only sees owned customers\n const actor = process.env[\"DXCRM_ACTOR\"] ?? \"system\";\n if (!canSeeCustomer(dataDir, actor, entry)) continue;\n\n customers.push(summary);\n } catch {\n // Skip customers with malformed data\n continue;\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(customers, null, 2) }],\n };\n}\n\nexport function registerListCustomers(server: McpServer): void {\n server.registerTool(\n \"list_customers\",\n {\n title: \"List Customers\",\n description: `List all customers with their pipeline stage, last interaction date, and deal value.\nUseful for morning briefings and pipeline overviews.\n\nArgs:\n filter: Optional substring to filter by name or slug (case-insensitive)\n\nReturns: Array of { slug, name, stage, lastInteraction?, dealValue? }`,\n inputSchema: z.object({\n filter: z\n .string()\n .optional()\n .describe(\"Substring filter on customer name or slug (case-insensitive)\"),\n }),\n },\n async ({ filter }) => handleListCustomers({ ...(filter !== undefined ? { filter } : {}) })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { withFileQueue } from \"../fs/write-queue.js\";\n\nexport async function withJsonFile<T>(\n filePath: string,\n updater: (current: T | null) => T | Promise<T>\n): Promise<T> {\n return withFileQueue(filePath, async () => {\n // Read current state\n let current: T | null = null;\n if (fs.existsSync(filePath)) {\n try {\n current = JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as T;\n } catch {\n current = null;\n }\n }\n\n // Apply updater — may throw, in which case we do NOT write\n const next = await updater(current);\n\n // Write atomically (within the queue lock)\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(next, null, 2), \"utf-8\");\n\n return next;\n });\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { withJsonFile } from \"./file-lock.js\";\n\nexport type NodeType = \"person\" | \"company\" | \"deal\" | \"product\" | \"event\";\n\nexport type EdgeType =\n | \"KNOWS\"\n | \"WORKS_AT\"\n | \"IS_CHAMPION\"\n | \"IS_BLOCKER\"\n | \"IS_ECONOMIC_BUYER\"\n | \"INTRODUCED_BY\"\n | \"OWNS_DEAL\"\n | \"COMPETES_WITH\";\n\nexport interface GraphNode {\n id: string;\n type: NodeType;\n label: string;\n status?: \"active\" | \"inactive\";\n properties: {\n email?: string;\n title?: string;\n company?: string;\n domain?: string;\n [key: string]: unknown;\n };\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface GraphEdge {\n id: string;\n from: string;\n to: string;\n type: EdgeType;\n weight: number;\n sentiment: number;\n lastContact: string;\n contactCount: number;\n properties: Record<string, unknown>;\n // Bi-temporal fields (Graphiti-style), optional for backward compatibility:\n // valid time = when the fact held in the world; transaction time = when the\n // system recorded/retracted it. Edges are invalidated, never deleted.\n validFrom?: string;\n validTo?: string;\n recordedAt?: string;\n invalidatedAt?: string;\n}\n\nexport interface CustomerGraph {\n schemaVersion: \"1\";\n slug: string;\n nodes: GraphNode[];\n edges: GraphEdge[];\n updatedAt: string;\n}\n\nexport type StakeholderRole = \"champion\" | \"blocker\" | \"economic_buyer\" | \"user\";\n\nexport interface MissingRole {\n role: \"champion\" | \"economic_buyer\";\n urgency: \"critical\" | \"important\";\n suggestion: string;\n}\n\nexport interface StakeholderSummary {\n champions: GraphNode[];\n blockers: GraphNode[];\n economicBuyers: GraphNode[];\n allContacts: GraphNode[];\n missingRoles: MissingRole[];\n}\n\n// ─── File path ────────────────────────────────────────────────────────────────\n\nexport function graphPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"graph.json\");\n}\n\nexport function emptyGraph(slug: string): CustomerGraph {\n return {\n schemaVersion: \"1\",\n slug,\n nodes: [],\n edges: [],\n updatedAt: new Date().toISOString(),\n };\n}\n\n// ─── Read / Write ─────────────────────────────────────────────────────────────\n\nexport function readGraph(dataDir: string, slug: string): CustomerGraph {\n const p = graphPath(dataDir, slug);\n if (!fs.existsSync(p)) return emptyGraph(slug);\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\")) as CustomerGraph;\n } catch {\n process.stderr.write(`[graph] failed to parse ${p} — returning empty graph\\n`);\n return emptyGraph(slug);\n }\n}\n\nexport async function writeGraph(\n dataDir: string,\n slug: string,\n updater: (current: CustomerGraph | null) => CustomerGraph\n): Promise<CustomerGraph> {\n return withJsonFile<CustomerGraph>(graphPath(dataDir, slug), (current) => {\n const g = updater(current);\n return { ...g, updatedAt: new Date().toISOString() };\n });\n}\n\n// ─── Node operations ──────────────────────────────────────────────────────────\n\nexport function upsertNode(\n graph: CustomerGraph,\n node: Omit<GraphNode, \"createdAt\" | \"updatedAt\">\n): CustomerGraph {\n const now = new Date().toISOString();\n const idx = graph.nodes.findIndex((n) => n.id === node.id);\n if (idx !== -1) {\n const existing = graph.nodes[idx]!;\n const updated: GraphNode = {\n ...existing,\n label: node.label || existing.label,\n properties: { ...existing.properties, ...node.properties },\n updatedAt: now,\n };\n const nodes = [...graph.nodes];\n nodes[idx] = updated;\n return { ...graph, nodes };\n }\n const newNode: GraphNode = { ...node, createdAt: now, updatedAt: now };\n return { ...graph, nodes: [...graph.nodes, newNode] };\n}\n\nexport function findNode(graph: CustomerGraph, id: string): GraphNode | undefined {\n return graph.nodes.find((n) => n.id === id);\n}\n\nexport function findNodesByType(graph: CustomerGraph, type: NodeType): GraphNode[] {\n return graph.nodes.filter((n) => n.type === type);\n}\n\n// ─── Edge operations ──────────────────────────────────────────────────────────\n\nexport function makeEdgeId(type: EdgeType, fromId: string, toId: string): string {\n return `${type}:${fromId}__${toId}`;\n}\n\nexport function upsertEdge(\n graph: CustomerGraph,\n edge: Omit<GraphEdge, \"id\"> & { id?: string }\n): CustomerGraph {\n const id = edge.id ?? makeEdgeId(edge.type, edge.from, edge.to);\n const idx = graph.edges.findIndex((e) => e.id === id);\n if (idx !== -1) {\n const existing = graph.edges[idx]!;\n const updated: GraphEdge = {\n ...existing,\n weight: Math.min(1.0, existing.weight + 0.05),\n contactCount: existing.contactCount + 1,\n lastContact: edge.lastContact,\n properties: { ...existing.properties, ...edge.properties },\n };\n const edges = [...graph.edges];\n edges[idx] = updated;\n return { ...graph, edges };\n }\n const now = new Date().toISOString();\n const newEdge: GraphEdge = {\n ...edge,\n id,\n recordedAt: edge.recordedAt ?? now,\n validFrom: edge.validFrom ?? edge.lastContact ?? now,\n };\n return { ...graph, edges: [...graph.edges, newEdge] };\n}\n\n/**\n * Invalidate an edge instead of deleting it: records when the fact stopped\n * being true (validTo) and when the system retracted it (invalidatedAt),\n * preserving full temporal auditability.\n */\nexport function invalidateEdge(graph: CustomerGraph, edgeId: string, at?: string): CustomerGraph {\n const stamp = at ?? new Date().toISOString();\n const edges = graph.edges.map((e) =>\n e.id === edgeId ? { ...e, validTo: stamp, invalidatedAt: new Date().toISOString() } : e\n );\n return { ...graph, edges };\n}\n\n/**\n * Edges considered active at a point in time (default: now): not invalidated,\n * already valid (validFrom <= at), and not yet expired (at < validTo).\n * Edges without temporal fields (legacy) are treated as active.\n */\nexport function activeEdges(graph: CustomerGraph, at?: string): GraphEdge[] {\n const t = at ?? new Date().toISOString();\n return graph.edges.filter((e) => {\n if (e.invalidatedAt) return false;\n if (e.validFrom && e.validFrom > t) return false;\n if (e.validTo && t >= e.validTo) return false;\n return true;\n });\n}\n\nexport function findEdges(graph: CustomerGraph, fromId: string, type?: EdgeType): GraphEdge[] {\n return graph.edges.filter((e) => e.from === fromId && (type === undefined || e.type === type));\n}\n\nexport function findEdgesTo(graph: CustomerGraph, toId: string, type?: EdgeType): GraphEdge[] {\n return graph.edges.filter((e) => e.to === toId && (type === undefined || e.type === type));\n}\n\n// ─── Role assignment ──────────────────────────────────────────────────────────\n\nconst ROLE_EDGE_MAP: Record<Exclude<StakeholderRole, \"user\">, EdgeType> = {\n champion: \"IS_CHAMPION\",\n blocker: \"IS_BLOCKER\",\n economic_buyer: \"IS_ECONOMIC_BUYER\",\n};\n\nexport function setNodeRole(\n graph: CustomerGraph,\n nodeId: string,\n targetId: string,\n role: StakeholderRole\n): CustomerGraph {\n if (role === \"user\") return graph;\n const edgeType = ROLE_EDGE_MAP[role];\n const today = new Date().toISOString().slice(0, 10);\n return upsertEdge(graph, {\n from: nodeId,\n to: targetId,\n type: edgeType,\n weight: 0.8,\n sentiment: 0,\n lastContact: today,\n contactCount: 1,\n properties: {},\n });\n}\n\n// ─── Stakeholder query ────────────────────────────────────────────────────────\n\nexport function getStakeholders(graph: CustomerGraph): StakeholderSummary {\n const dedup = (nodes: GraphNode[]): GraphNode[] => {\n const seen = new Set<string>();\n return nodes.filter((n) => (seen.has(n.id) ? false : (seen.add(n.id), true)));\n };\n\n const resolve = (edges: GraphEdge[]): GraphNode[] =>\n dedup(edges.map((e) => findNode(graph, e.from)).filter((n): n is GraphNode => n !== undefined));\n\n const champions = resolve(graph.edges.filter((e) => e.type === \"IS_CHAMPION\"));\n const blockers = resolve(graph.edges.filter((e) => e.type === \"IS_BLOCKER\"));\n const economicBuyers = resolve(graph.edges.filter((e) => e.type === \"IS_ECONOMIC_BUYER\"));\n const allContacts = findNodesByType(graph, \"person\");\n\n const missingRoles: MissingRole[] = [];\n if (allContacts.length > 0) {\n if (champions.length === 0) {\n missingRoles.push({\n role: \"champion\",\n urgency: \"important\",\n suggestion: \"Identify who is driving this deal internally.\",\n });\n }\n if (economicBuyers.length === 0) {\n missingRoles.push({\n role: \"economic_buyer\",\n urgency: \"critical\",\n suggestion: \"Find out who signs the contract. Ask your champion directly.\",\n });\n }\n }\n\n return { champions, blockers, economicBuyers, allContacts, missingRoles };\n}\n\n// ─── Pruning ──────────────────────────────────────────────────────────────────\n\nexport function pruneStaleNodes(\n graph: CustomerGraph,\n maxAgeDays = 365,\n today?: string\n): CustomerGraph {\n const todayMs = today ? new Date(`${today}T00:00:00Z`).getTime() : Date.now();\n const threshold = maxAgeDays * 86_400_000;\n return {\n ...graph,\n nodes: graph.nodes.map((node) => {\n const age = todayMs - new Date(node.updatedAt).getTime();\n if (age > threshold && node.status !== \"inactive\") {\n return { ...node, status: \"inactive\" as const };\n }\n return node;\n }),\n };\n}\n\n// ─── Path finding ─────────────────────────────────────────────────────────────\n\n/** BFS shortest path between two nodes. Returns [] when no path exists. */\nexport function findPath(graph: CustomerGraph, fromId: string, toId: string): string[] {\n if (fromId === toId) return [fromId];\n\n const visited = new Set<string>([fromId]);\n const queue: Array<{ nodeId: string; path: string[] }> = [{ nodeId: fromId, path: [fromId] }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n const neighbors = graph.edges\n .filter((e) => e.from === current.nodeId || e.to === current.nodeId)\n .map((e) => (e.from === current.nodeId ? e.to : e.from))\n .filter((id) => !visited.has(id));\n\n for (const neighborId of neighbors) {\n const newPath = [...current.path, neighborId];\n if (neighborId === toId) return newPath;\n visited.add(neighborId);\n queue.push({ nodeId: neighborId, path: newPath });\n }\n }\n\n return [];\n}\n","export function normalizeEmail(raw: string): string {\n if (!raw) return \"\";\n const trimmed = raw.trim();\n // Extract email from \"Display Name <email@example.com>\" format\n const angleMatch = trimmed.match(/<([^>]+)>/);\n const address = angleMatch ? angleMatch[1]! : trimmed;\n return address.toLowerCase().trim();\n}\n\nexport function isSameContact(a: string, b: string): boolean {\n return normalizeEmail(a) === normalizeEmail(b);\n}\n\nexport function normalizeContactId(raw: string): string {\n const email = normalizeEmail(raw);\n // Replace @ with _at_ so it can be used as an object key safely\n return email.replace(\"@\", \"_at_\");\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport type { GraphNode, GraphEdge, EdgeType, CustomerGraph } from \"./graph.js\";\nimport { writeGraph, upsertNode, upsertEdge } from \"./graph.js\";\nimport { normalizeEmail } from \"./email-normalizer.js\";\n\nexport interface ExtractionInput {\n slug: string;\n withStr: string;\n interactionDate: string;\n domain?: string;\n companyName?: string;\n}\n\nexport function extractEmail(withStr: string): string | undefined {\n const angleMatch = withStr.match(/<([^>]+@[^>]+)>/);\n if (angleMatch?.[1]) return angleMatch[1].toLowerCase();\n const bareMatch = withStr.match(/^([^\\s]+@[^\\s]+)$/);\n if (bareMatch?.[1]) return bareMatch[1].toLowerCase();\n return undefined;\n}\n\nexport function extractDisplayName(withStr: string): string {\n const match = withStr.match(/^(.+?)\\s*<[^>]+>$/);\n if (match?.[1]) return match[1].trim();\n return withStr.trim();\n}\n\nexport function makePersonId(withStr: string, slug: string): string {\n const email = normalizeEmail(withStr);\n if (email.includes(\"@\")) return `person:${email}`;\n const name = extractDisplayName(withStr);\n const nameSlug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return `person:${slug}:${nameSlug}`;\n}\n\nexport function makeCompanyId(domain?: string, slug?: string, companyName?: string): string {\n if (domain) return `company:${domain.toLowerCase()}`;\n if (slug) return `company:${slug}`;\n if (companyName) {\n const s = companyName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return `company:${s}`;\n }\n return `company:unknown`;\n}\n\nexport function extractNodes(input: ExtractionInput): Omit<GraphNode, \"createdAt\" | \"updatedAt\">[] {\n const email = extractEmail(input.withStr);\n const label = extractDisplayName(input.withStr);\n const personId = makePersonId(input.withStr, input.slug);\n\n const personProps: GraphNode[\"properties\"] = {};\n if (email !== undefined) personProps[\"email\"] = email;\n if (input.companyName !== undefined) personProps[\"company\"] = input.companyName;\n if (input.domain !== undefined) personProps[\"domain\"] = input.domain;\n\n const personNode: Omit<GraphNode, \"createdAt\" | \"updatedAt\"> = {\n id: personId,\n type: \"person\",\n label,\n properties: personProps,\n };\n\n const nodes: Omit<GraphNode, \"createdAt\" | \"updatedAt\">[] = [personNode];\n\n if (input.domain !== undefined || input.companyName !== undefined) {\n const companyId = makeCompanyId(input.domain, input.slug, input.companyName);\n const companyProps: GraphNode[\"properties\"] = {};\n if (input.domain !== undefined) companyProps[\"domain\"] = input.domain;\n\n const companyNode: Omit<GraphNode, \"createdAt\" | \"updatedAt\"> = {\n id: companyId,\n type: \"company\",\n label: input.companyName ?? input.domain ?? input.slug,\n properties: companyProps,\n };\n nodes.push(companyNode);\n }\n\n return nodes;\n}\n\nexport function extractEdges(\n personId: string,\n companyId: string | undefined,\n interactionDate: string\n): GraphEdge[] {\n if (!companyId) return [];\n return [\n {\n id: `WORKS_AT:${personId}__${companyId}`,\n from: personId,\n to: companyId,\n type: \"WORKS_AT\" as EdgeType,\n weight: 0.5,\n sentiment: 0,\n lastContact: interactionDate,\n contactCount: 1,\n properties: {},\n },\n ];\n}\n\nexport async function updateGraphFromInteraction(\n dataDir: string,\n slug: string,\n input: { withStr: string; interactionDate: string }\n): Promise<void> {\n if (!input.withStr.trim()) return;\n\n let domain: string | undefined;\n let companyName: string | undefined;\n const factsPath = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (fs.existsSync(factsPath)) {\n try {\n const parsed = matter(fs.readFileSync(factsPath, \"utf-8\"));\n domain = parsed.data[\"domain\"] as string | undefined;\n companyName = parsed.data[\"name\"] as string | undefined;\n } catch {\n // non-critical\n }\n }\n\n const extractionInput: ExtractionInput = {\n slug,\n withStr: input.withStr,\n interactionDate: input.interactionDate,\n };\n if (domain !== undefined) extractionInput.domain = domain;\n if (companyName !== undefined) extractionInput.companyName = companyName;\n const nodes = extractNodes(extractionInput);\n\n const personId = makePersonId(input.withStr, slug);\n const companyId =\n domain !== undefined || companyName !== undefined\n ? makeCompanyId(domain, slug, companyName)\n : undefined;\n const edges = extractEdges(personId, companyId, input.interactionDate);\n\n await writeGraph(dataDir, slug, (current) => {\n const empty: CustomerGraph = {\n schemaVersion: \"1\",\n slug,\n nodes: [],\n edges: [],\n updatedAt: new Date().toISOString(),\n };\n let graph = current ?? empty;\n for (const node of nodes) graph = upsertNode(graph, node);\n for (const edge of edges) graph = upsertEdge(graph, edge);\n return graph;\n });\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { readGraph } from \"./graph.js\";\nimport { extractEmail, extractDisplayName, makePersonId } from \"./graph-extractor.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type HealthGrade = \"A\" | \"B\" | \"C\" | \"D\" | \"F\";\n\nexport type HealthTrend = \"rising\" | \"stable\" | \"declining\" | \"cold\";\n\nexport type RiskFlag =\n | \"NO_CONTACT_14D\"\n | \"NO_CONTACT_30D\"\n | \"SENTIMENT_DECLINING\"\n | \"CHAMPION_SILENT\"\n | \"DEAL_STALLED\"\n | \"CLOSE_DATE_PASSED\"\n | \"CONTACT_LEFT_COMPANY\"\n | \"RESPONSE_LATENCY_INCREASING\";\n\nexport interface ContactHealth {\n contactId: string;\n name: string;\n email?: string;\n score: number;\n grade: HealthGrade;\n trend: HealthTrend;\n daysSinceContact: number;\n avgCadenceDays: number;\n sentimentTrend: number;\n riskFlags: RiskFlag[];\n lastContact: string;\n interactionCount30d: number;\n recommendation: string;\n updatedAt: string;\n}\n\nexport interface HealthSnapshot {\n schemaVersion: \"1\";\n slug: string;\n contacts: ContactHealth[];\n overallHealth: number;\n updatedAt: string;\n}\n\n// ─── Parsed interaction (from interactions.md) ────────────────────────────────\n\nexport interface ParsedInteraction {\n date: string;\n type: string;\n withStr: string;\n}\n\nexport interface ContactInteractionGroup {\n contactId: string;\n name: string;\n email?: string;\n interactions: ParsedInteraction[];\n}\n\n// ─── File path ────────────────────────────────────────────────────────────────\n\nexport function healthPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"health.json\");\n}\n\n// ─── Read / Write ─────────────────────────────────────────────────────────────\n\nexport function readHealth(dataDir: string, slug: string): HealthSnapshot | null {\n const p = healthPath(dataDir, slug);\n if (!fs.existsSync(p)) return null;\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\")) as HealthSnapshot;\n } catch {\n return null;\n }\n}\n\nexport function writeHealth(dataDir: string, slug: string, health: HealthSnapshot): void {\n const p = healthPath(dataDir, slug);\n const dir = path.dirname(p);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n const updated: HealthSnapshot = { ...health, updatedAt: new Date().toISOString() };\n fs.writeFileSync(p, JSON.stringify(updated, null, 2), \"utf-8\");\n}\n\n// ─── Parsing ──────────────────────────────────────────────────────────────────\n\nexport function parseContactInteractions(content: string): ParsedInteraction[] {\n const blocks = content.split(/(?=^## \\d{4}-\\d{2}-\\d{2})/m).filter((b) => b.trim().length > 0);\n\n const result: ParsedInteraction[] = [];\n for (const block of blocks) {\n const headingMatch = block.match(/^## (\\d{4}-\\d{2}-\\d{2}) · (\\w+)/m);\n if (!headingMatch) continue;\n const date = headingMatch[1]!;\n const type = headingMatch[2]!;\n\n const withMatch = block.match(/^\\*\\*(?:With|Subject):\\*\\*\\s*(.+)$/m);\n if (!withMatch) continue;\n const withStr = withMatch[1]!.trim();\n\n result.push({ date, type, withStr });\n }\n return result;\n}\n\n// ─── Score functions (pure) ───────────────────────────────────────────────────\n\nexport function calcRecencyScore(daysSince: number): number {\n if (daysSince <= 0) return 100;\n if (daysSince >= 30) return 0;\n return Math.round(100 * (1 - daysSince / 30));\n}\n\nexport function calcCadenceScore(daysSince: number, avgCadenceDays: number): number {\n if (avgCadenceDays <= 0) return 50;\n const ratio = daysSince / avgCadenceDays;\n if (ratio <= 1.0) return 100;\n if (ratio >= 3.0) return 0;\n return Math.round(100 * (1 - (ratio - 1.0) / 2.0));\n}\n\nexport function calcMomentumScore(last30d: number, prev30d: number): number {\n if (last30d === 0 && prev30d === 0) return 50;\n if (prev30d === 0) return 80;\n const ratio = last30d / prev30d;\n if (ratio >= 1.5) return 100;\n if (ratio >= 1.0) return 75;\n if (ratio >= 0.5) return 50;\n if (ratio >= 0.25) return 25;\n return 0;\n}\n\nexport function gradeFromScore(score: number): HealthGrade {\n if (score >= 80) return \"A\";\n if (score >= 60) return \"B\";\n if (score >= 40) return \"C\";\n if (score >= 20) return \"D\";\n return \"F\";\n}\n\nexport function trendFromState(\n score: number,\n daysSince: number,\n avgCadenceDays: number,\n momentumScore: number\n): HealthTrend {\n if (score < 20 || daysSince >= 30) return \"cold\";\n if (momentumScore > 70 && score > 60) return \"rising\";\n if (momentumScore < 30 || (daysSince > avgCadenceDays * 1.5 && score < 60)) return \"declining\";\n return \"stable\";\n}\n\nexport function calcRiskFlags(\n _contactId: string,\n daysSince: number,\n score: number,\n isChampion: boolean\n): RiskFlag[] {\n const flags: RiskFlag[] = [];\n if (daysSince >= 30) flags.push(\"NO_CONTACT_30D\");\n if (daysSince >= 14) flags.push(\"NO_CONTACT_14D\");\n if (isChampion && score < 50) flags.push(\"CHAMPION_SILENT\");\n return flags;\n}\n\nexport function generateRecommendation(\n name: string,\n grade: HealthGrade,\n trend: HealthTrend,\n riskFlags: RiskFlag[],\n daysSince: number,\n avgCadenceDays: number\n): string {\n if (riskFlags.includes(\"NO_CONTACT_30D\")) {\n return `Re-engage ${name} urgently — no contact in ${daysSince} days.`;\n }\n if (riskFlags.includes(\"CHAMPION_SILENT\")) {\n return `Champion ${name} has gone quiet — critical to re-engage before deal stalls.`;\n }\n if (riskFlags.includes(\"NO_CONTACT_14D\")) {\n return `Schedule contact with ${name} — ${daysSince} days since last touchpoint.`;\n }\n if (trend === \"declining\") {\n return `${name} relationship declining — increase touchpoint frequency.`;\n }\n if (grade === \"A\") {\n return `${name} — strong relationship. Keep current cadence.`;\n }\n const daysUntilDue = Math.max(0, avgCadenceDays - daysSince);\n return `${name} — grade ${grade}. Next contact due in ~${daysUntilDue} day${daysUntilDue === 1 ? \"\" : \"s\"}.`;\n}\n\n// ─── Average cadence ──────────────────────────────────────────────────────────\n\nfunction dateUtcMs(d: string): number {\n return new Date(`${d}T00:00:00Z`).getTime();\n}\n\nexport function calcAvgCadence(interactions: ParsedInteraction[]): number {\n if (interactions.length < 2) return 0;\n const sorted = [...interactions].sort((a, b) => b.date.localeCompare(a.date));\n let totalDays = 0;\n for (let i = 0; i < sorted.length - 1; i++) {\n const gap = Math.round(\n (dateUtcMs(sorted[i]!.date) - dateUtcMs(sorted[i + 1]!.date)) / 86_400_000\n );\n totalDays += gap;\n }\n return Math.round(totalDays / (sorted.length - 1));\n}\n\n// ─── Group interactions by contact ───────────────────────────────────────────\n\nexport function groupInteractionsByContact(\n interactions: ParsedInteraction[],\n slug: string\n): ContactInteractionGroup[] {\n const map = new Map<\n string,\n { contactId: string; name: string; email?: string; interactions: ParsedInteraction[] }\n >();\n\n for (const ix of interactions) {\n const email = extractEmail(ix.withStr);\n const name = extractDisplayName(ix.withStr);\n const contactId = makePersonId(ix.withStr, slug);\n\n if (!map.has(contactId)) {\n const entry: {\n contactId: string;\n name: string;\n email?: string;\n interactions: ParsedInteraction[];\n } = {\n contactId,\n name,\n interactions: [],\n };\n if (email !== undefined) entry.email = email;\n map.set(contactId, entry);\n }\n map.get(contactId)!.interactions.push(ix);\n }\n\n return Array.from(map.values());\n}\n\n// ─── Per-contact health ───────────────────────────────────────────────────────\n\nexport function computeContactHealth(\n group: ContactInteractionGroup,\n today: string,\n isChampion: boolean\n): ContactHealth {\n const sorted = [...group.interactions].sort((a, b) => b.date.localeCompare(a.date));\n const lastContact = sorted[0]?.date ?? \"\";\n\n const daysSince = lastContact\n ? Math.round((dateUtcMs(today) - dateUtcMs(lastContact)) / 86_400_000)\n : 999;\n\n const avgCadenceDays = calcAvgCadence(group.interactions);\n\n const todayMs = dateUtcMs(today);\n const d30 = todayMs - 30 * 86_400_000;\n const d60 = todayMs - 60 * 86_400_000;\n const last30d = group.interactions.filter((i) => dateUtcMs(i.date) >= d30).length;\n const prev30d = group.interactions.filter(\n (i) => dateUtcMs(i.date) >= d60 && dateUtcMs(i.date) < d30\n ).length;\n\n const recency = calcRecencyScore(daysSince);\n const cadence = calcCadenceScore(daysSince, avgCadenceDays);\n const sentiment = 50;\n const response = 50;\n const momentum = calcMomentumScore(last30d, prev30d);\n\n const score = Math.round(\n recency * 0.35 + cadence * 0.25 + sentiment * 0.2 + response * 0.1 + momentum * 0.1\n );\n\n const grade = gradeFromScore(score);\n const trend = trendFromState(score, daysSince, avgCadenceDays, momentum);\n const riskFlags = calcRiskFlags(group.contactId, daysSince, score, isChampion);\n const recommendation = generateRecommendation(\n group.name,\n grade,\n trend,\n riskFlags,\n daysSince,\n avgCadenceDays\n );\n\n const health: ContactHealth = {\n contactId: group.contactId,\n name: group.name,\n score,\n grade,\n trend,\n daysSinceContact: daysSince,\n avgCadenceDays,\n sentimentTrend: 0,\n riskFlags,\n lastContact,\n interactionCount30d: last30d,\n recommendation,\n updatedAt: new Date().toISOString(),\n };\n if (group.email !== undefined) health.email = group.email;\n return health;\n}\n\n// ─── Full customer health ─────────────────────────────────────────────────────\n\nexport function computeCustomerHealth(\n dataDir: string,\n slug: string,\n today: string = new Date().toISOString().slice(0, 10)\n): HealthSnapshot {\n const interactionsPath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n const content = fs.existsSync(interactionsPath)\n ? (fs.readFileSync(interactionsPath, \"utf-8\") as string)\n : \"\";\n\n const parsed = parseContactInteractions(content);\n const groups = groupInteractionsByContact(parsed, slug);\n\n const graph = readGraph(dataDir, slug);\n const championIds = new Set(\n graph.edges.filter((e) => e.type === \"IS_CHAMPION\").map((e) => e.from)\n );\n\n const contacts = groups.map((group) =>\n computeContactHealth(group, today, championIds.has(group.contactId))\n );\n\n const overallHealth =\n contacts.length === 0\n ? 100\n : Math.round(contacts.reduce((sum, c) => sum + c.score, 0) / contacts.length);\n\n return {\n schemaVersion: \"1\",\n slug,\n contacts,\n overallHealth,\n updatedAt: new Date().toISOString(),\n };\n}\n\n// ─── Fire-and-forget update ───────────────────────────────────────────────────\n\nexport async function updateHealthFromInteraction(dataDir: string, slug: string): Promise<void> {\n const health = computeCustomerHealth(dataDir, slug);\n writeHealth(dataDir, slug, health);\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport matter from \"gray-matter\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { appendInteraction, formatInteractionEntry } from \"../../fs/interactions-writer.js\";\nimport type { InteractionEntry } from \"../../schemas/interaction.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport { updateGraphFromInteraction } from \"../../core/graph-extractor.js\";\nimport { updateHealthFromInteraction } from \"../../core/relationship-health.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleLogInteraction(\n input: {\n slug: string;\n type: InteractionEntry[\"type\"];\n summary: string;\n with: string;\n nextSteps?: string[];\n direction?: \"inbound\" | \"outbound\";\n source?: string;\n date?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const today = new Date().toISOString().split(\"T\")[0] as string;\n const interactionDate = input.date ?? today;\n const sourceRef = input.source ?? `agent://log/${Date.now()}`;\n\n const entry: InteractionEntry = {\n date: interactionDate,\n type: input.type,\n with: input.with,\n summary: input.summary,\n nextSteps: input.nextSteps ?? [],\n sourceRef,\n synced: new Date().toISOString(),\n ...(input.direction !== undefined ? { direction: input.direction } : {}),\n };\n\n const interactionsPath = path.join(dataDir, \"customers\", input.slug, \"interactions.md\");\n const entryText = formatInteractionEntry(entry);\n\n try {\n enforceRbac(dataDir, \"log_interaction\");\n\n await appendInteraction(dataDir, input.slug, entry);\n\n // Graph auto-update: fire-and-forget\n updateGraphFromInteraction(dataDir, input.slug, {\n withStr: input.with,\n interactionDate: interactionDate,\n }).catch(() => {\n // non-critical — interaction already written\n });\n\n // Health auto-update: fire-and-forget (runs after graph update)\n updateHealthFromInteraction(dataDir, input.slug).catch(() => {\n // non-critical — interaction already written\n });\n\n // Update last_touchpoint in main_facts.md\n const mainFactsPath = path.join(dataDir, \"customers\", input.slug, \"main_facts.md\");\n if (fs.existsSync(mainFactsPath)) {\n try {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\"));\n raw.data.last_touchpoint = interactionDate;\n // js-yaml quotes YYYY-MM-DD strings; strip quotes to keep plain date format\n let serialized = matter.stringify(raw.content, raw.data);\n serialized = serialized.replace(\n /^(last_touchpoint:\\s*)['\"](\\d{4}-\\d{2}-\\d{2})['\"]/m,\n \"$1$2\"\n );\n fs.writeFileSync(mainFactsPath, serialized, \"utf-8\");\n } catch {\n // non-critical — interaction is already written\n }\n }\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"log_interaction\",\n slug: input.slug,\n summary: input.summary,\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n path: interactionsPath,\n entry: entryText,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: false,\n error: (err as Error).message,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n}\n\nexport function registerLogInteraction(server: McpServer): void {\n server.registerTool(\n \"log_interaction\",\n {\n title: \"Log Interaction\",\n description: `Write a new interaction entry to the CRM. Use after every call, meeting, or email.\nFormat matches auto-synced entries exactly — immediately searchable.\n\nArgs:\n slug: Customer ID\n type: Interaction type (\"Email\" | \"Call\" | \"Meeting\" | \"Note\" | \"Demo\" | \"Proposal\" | \"Contract\" | \"Other\")\n summary: 2-5 sentences describing what happened (min 1 char)\n with: Who was involved (name or email address)\n nextSteps: Array of action items (optional)\n direction: \"inbound\" or \"outbound\" (optional)\n source: Source reference string (optional, auto-generated if omitted)\n\nReturns: { success: boolean, path: string, entry: string }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n type: z\n .enum([\"Email\", \"Call\", \"Meeting\", \"Note\", \"Demo\", \"Proposal\", \"Contract\", \"Other\"])\n .describe(\"Type of interaction\"),\n summary: z.string().min(1).describe(\"2-5 sentence summary of what happened\"),\n with: z.string().describe(\"Who was involved (name or email)\"),\n nextSteps: z.array(z.string()).optional().describe(\"Action items for follow-up\"),\n direction: z\n .enum([\"inbound\", \"outbound\"])\n .optional()\n .describe(\"Direction of communication\"),\n source: z\n .string()\n .optional()\n .describe(\"Source reference (e.g. gmail://thread/123). Auto-generated if omitted.\"),\n date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional()\n .describe(\"Date of interaction (YYYY-MM-DD). Defaults to today.\"),\n }),\n },\n async ({ slug, type, summary, with: withStr, nextSteps, direction, source, date }) =>\n handleLogInteraction({\n slug,\n type,\n summary,\n with: withStr,\n ...(nextSteps !== undefined ? { nextSteps } : {}),\n ...(direction !== undefined ? { direction } : {}),\n ...(source !== undefined ? { source } : {}),\n ...(date !== undefined ? { date } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { upsertDeal } from \"../../fs/pipeline-writer.js\";\nimport type { PipelineDeal } from \"../../schemas/pipeline.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUpdateDeal(\n input: {\n slug: string;\n dealName: string;\n stage?: string;\n value?: number;\n probability?: number;\n closeDate?: string;\n notes?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const today = new Date().toISOString().split(\"T\")[0] as string;\n\n const deal: PipelineDeal = {\n name: input.dealName,\n stage: (input.stage as PipelineDeal[\"stage\"]) ?? \"lead\",\n currency: \"EUR\",\n updated: today,\n ...(input.value !== undefined ? { value: input.value } : {}),\n ...(input.probability !== undefined ? { probability: input.probability } : {}),\n ...(input.closeDate !== undefined ? { close_date: input.closeDate } : {}),\n ...(input.notes !== undefined ? { notes: input.notes } : {}),\n };\n\n try {\n enforceRbac(dataDir, \"update_deal\");\n\n await upsertDeal(dataDir, input.slug, deal);\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"update_deal\",\n slug: input.slug,\n summary: input.dealName,\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n deal,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: false,\n error: (err as Error).message,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n}\n\nexport function registerUpdateDeal(server: McpServer): void {\n server.registerTool(\n \"update_deal\",\n {\n title: \"Update Deal\",\n description: `Update or create a deal in the customer's pipeline. Upserts by deal name.\nUse after pipeline discussions or when deal status changes.\n\nArgs:\n slug: Customer ID\n dealName: Deal name (used as unique key for upsert)\n stage: Deal stage (\"lead\" | \"qualified\" | \"proposal\" | \"negotiation\" | \"won\" | \"lost\")\n value: Deal value in euros\n probability: Win probability percentage (0-100)\n closeDate: Expected close date (YYYY-MM-DD)\n notes: Free-text notes about the deal\n\nReturns: { success: boolean, deal: object }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n dealName: z.string().describe(\"Deal name (unique identifier for upsert)\"),\n stage: z\n .enum([\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"])\n .optional()\n .describe(\"Deal stage\"),\n value: z.number().optional().describe(\"Deal value in euros\"),\n probability: z.number().min(0).max(100).optional().describe(\"Win probability (0-100)\"),\n closeDate: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional()\n .describe(\"Expected close date (YYYY-MM-DD)\"),\n notes: z.string().optional().describe(\"Free-text notes about the deal\"),\n }),\n },\n async ({ slug, dealName, stage, value, probability, closeDate, notes }) =>\n handleUpdateDeal({\n slug,\n dealName,\n ...(stage !== undefined ? { stage } : {}),\n ...(value !== undefined ? { value } : {}),\n ...(probability !== undefined ? { probability } : {}),\n ...(closeDate !== undefined ? { closeDate } : {}),\n ...(notes !== undefined ? { notes } : {}),\n })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readPipeline } from \"../../fs/pipeline-writer.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nfunction countInteractions(content: string): number {\n // Count ## YYYY-MM-DD headings\n const matches = content.match(/^## \\d{4}-\\d{2}-\\d{2}/gm);\n return matches ? matches.length : 0;\n}\n\nexport async function handleExportCustomer(\n input: { slug: string; format?: \"json\" | \"markdown\" },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n}> {\n enforceRbac(dataDir, \"export_customer\");\n\n const customerDir = path.join(dataDir, \"customers\", input.slug);\n\n if (!fs.existsSync(customerDir)) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Customer '${input.slug}' not found. Check 'list_customers()' for available customers.`,\n },\n ],\n isError: true,\n };\n }\n\n const format = input.format ?? \"json\";\n\n // Read main_facts.md\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n let mainFacts: Record<string, unknown> = {};\n let mainFactsContent = \"\";\n if (fs.existsSync(mainFactsPath)) {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\") as string);\n mainFacts = raw.data as Record<string, unknown>;\n mainFactsContent = raw.content ?? \"\";\n }\n\n // Read interactions\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n let interactionsContent = \"\";\n let interactionsCount = 0;\n if (fs.existsSync(interactionsPath)) {\n interactionsContent = fs.readFileSync(interactionsPath, \"utf-8\") as string;\n interactionsCount = countInteractions(interactionsContent);\n }\n\n // Read pipeline\n const pipeline = await readPipeline(dataDir, input.slug);\n\n // Read attachments list\n const attachmentsDir = path.join(customerDir, \"attachments\");\n const attachments: string[] = [];\n if (fs.existsSync(attachmentsDir)) {\n try {\n const files = fs.readdirSync(attachmentsDir) as string[];\n for (const f of files) {\n try {\n if (fs.statSync(path.join(attachmentsDir, f)).isFile()) attachments.push(f);\n } catch {\n /* skip */\n }\n }\n } catch {\n /* skip */\n }\n }\n\n if (format === \"markdown\") {\n const markdown = [\n `# Export: ${input.slug}`,\n \"\",\n \"## Main Facts\",\n mainFactsContent.trim() || \"(no content)\",\n \"\",\n \"## Metadata\",\n Object.entries(mainFacts)\n .map(([k, v]) => `- **${k}**: ${JSON.stringify(v)}`)\n .join(\"\\n\") || \"(no metadata)\",\n \"\",\n `## Interactions (${interactionsCount} total)`,\n interactionsContent.trim() || \"(no interactions)\",\n \"\",\n \"## Pipeline\",\n pipeline.length > 0\n ? pipeline\n .map(\n (d) =>\n `- **${d.name}** · ${d.stage}${d.value !== undefined ? ` · €${d.value}` : \"\"}${d.close_date ? ` · close: ${d.close_date}` : \"\"}`\n )\n .join(\"\\n\")\n : \"(no deals)\",\n \"\",\n `## Attachments (${attachments.length})`,\n attachments.length > 0 ? attachments.map((f) => `- ${f}`).join(\"\\n\") : \"(none)\",\n ].join(\"\\n\");\n\n return {\n content: [{ type: \"text\", text: markdown }],\n };\n }\n\n // Default: JSON\n const exported = {\n slug: input.slug,\n exportedAt: new Date().toISOString(),\n mainFacts,\n interactionsCount,\n pipeline,\n attachments,\n };\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(exported, null, 2) }],\n };\n}\n\nexport function registerExportCustomer(server: McpServer): void {\n server.registerTool(\n \"export_customer\",\n {\n title: \"Export Customer\",\n description: `Export all customer data (main_facts + interactions count + pipeline deals).\nUseful for reporting, audits, or creating backups.\n\nArgs:\n slug: Customer ID (e.g. \"acme-corp\")\n format: Output format — \"json\" (default) or \"markdown\"\n\nReturns:\n JSON: { slug, exportedAt, mainFacts, interactionsCount, pipeline }\n Markdown: Formatted document with all sections`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n format: z\n .enum([\"json\", \"markdown\"])\n .optional()\n .describe(\"Output format: 'json' (default) or 'markdown'\"),\n }),\n },\n async ({ slug, format }) =>\n handleExportCustomer({ slug, ...(format !== undefined ? { format } : {}) })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n readMainFacts,\n writeMainFacts,\n ensureCustomerDir,\n customerExists,\n} from \"../../fs/customer-dir.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport type { MainFacts } from \"../../schemas/main-facts.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUpdateCustomerFacts(\n input: {\n slug: string;\n name?: string | undefined;\n domain?: string | undefined;\n email?: string | undefined;\n phone?: string | undefined;\n industry?: string | undefined;\n relationshipStage?: \"prospect\" | \"active\" | \"churned\" | \"paused\" | undefined;\n dealValue?: number | undefined;\n primaryContact?: string | undefined;\n timezone?: string | undefined;\n tags?: string[] | undefined;\n notes?: string | undefined;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const today = new Date().toISOString().slice(0, 10);\n\n try {\n enforceRbac(dataDir, \"update_customer_facts\");\n\n let existing: MainFacts;\n let created = false;\n if (!customerExists(dataDir, input.slug)) {\n await ensureCustomerDir(dataDir, input.slug);\n existing = {\n name: input.name ?? input.slug,\n relationship_stage: \"prospect\",\n currency: \"EUR\",\n tags: [],\n created: today,\n updated: today,\n };\n created = true;\n } else {\n existing = await readMainFacts(dataDir, input.slug);\n }\n\n const updated = {\n ...existing,\n ...(input.name !== undefined ? { name: input.name } : {}),\n ...(input.domain !== undefined ? { domain: input.domain } : {}),\n ...(input.email !== undefined ? { email: input.email } : {}),\n ...(input.phone !== undefined ? { phone: input.phone } : {}),\n ...(input.industry !== undefined ? { industry: input.industry } : {}),\n ...(input.relationshipStage !== undefined\n ? { relationship_stage: input.relationshipStage }\n : {}),\n ...(input.dealValue !== undefined ? { deal_value: input.dealValue } : {}),\n ...(input.primaryContact !== undefined ? { primary_contact: input.primaryContact } : {}),\n ...(input.timezone !== undefined ? { timezone: input.timezone } : {}),\n ...(input.tags !== undefined ? { tags: input.tags } : {}),\n updated: today,\n };\n\n await writeMainFacts(dataDir, input.slug, updated);\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"update_customer_facts\",\n slug: input.slug,\n summary: Object.keys(input)\n .filter((k) => k !== \"slug\")\n .join(\", \"),\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true, created, facts: updated }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerUpdateCustomerFacts(server: McpServer): void {\n server.registerTool(\n \"update_customer_facts\",\n {\n title: \"Update Customer Facts\",\n description: `Create or update a customer's main_facts.md profile.\nIf the customer slug does not exist yet, creates the customer directory and initial profile.\nIf it exists, merges the provided fields into existing data.\n\nUse to add a new customer (\"create acme-corp\") or update existing info.\n\nArgs:\n slug: Customer ID / slug — e.g. \"acme-corp\" (kebab-case, no spaces)\n name: Company name (used as display name)\n domain: Primary domain (e.g. \"acme.com\")\n email: Primary contact email\n phone: Phone number\n industry: Industry vertical\n relationshipStage: \"prospect\" | \"active\" | \"churned\" | \"paused\"\n dealValue: Expected deal value in EUR\n primaryContact: Primary contact person name\n timezone: Timezone (e.g. \"Europe/Berlin\")\n tags: Array of tags (replaces existing tags)\n\nReturns: { success: boolean, created: boolean, facts: object }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n name: z.string().optional().describe(\"Company name\"),\n domain: z.string().optional().describe(\"Primary domain\"),\n email: z.string().optional().describe(\"Primary contact email\"),\n phone: z.string().optional().describe(\"Phone number\"),\n industry: z.string().optional().describe(\"Industry vertical\"),\n relationshipStage: z\n .enum([\"prospect\", \"active\", \"churned\", \"paused\"])\n .optional()\n .describe(\"Relationship stage\"),\n dealValue: z.number().optional().describe(\"Expected deal value in EUR\"),\n primaryContact: z.string().optional().describe(\"Primary contact person name\"),\n timezone: z.string().optional().describe(\"Timezone (e.g. Europe/Berlin)\"),\n tags: z.array(z.string()).optional().describe(\"Tags (replaces existing)\"),\n }),\n },\n async (input) => handleUpdateCustomerFacts(input)\n );\n}\n","import type { PipelineDeal } from \"../schemas/pipeline.js\";\n\nexport interface DealHealthSignals {\n daysSinceLastActivity: number;\n daysInCurrentStage: number;\n daysToClose?: number | undefined;\n probability?: number | undefined;\n}\n\nexport interface DealHealthScore {\n score: number; // 0–100\n grade: \"A\" | \"B\" | \"C\" | \"D\" | \"F\";\n signals: DealHealthSignals;\n warnings: string[];\n}\n\nfunction grade(score: number): \"A\" | \"B\" | \"C\" | \"D\" | \"F\" {\n if (score >= 80) return \"A\";\n if (score >= 65) return \"B\";\n if (score >= 50) return \"C\";\n if (score >= 35) return \"D\";\n return \"F\";\n}\n\nexport function scoreDeal(deal: PipelineDeal, signals: DealHealthSignals): DealHealthScore {\n let score = 100;\n const warnings: string[] = [];\n\n // Activity recency (max penalty -40)\n if (signals.daysSinceLastActivity > 60) {\n score -= 40;\n warnings.push(`No activity in ${signals.daysSinceLastActivity} days`);\n } else if (signals.daysSinceLastActivity > 30) {\n score -= 25;\n warnings.push(`Low activity — last touch ${signals.daysSinceLastActivity} days ago`);\n } else if (signals.daysSinceLastActivity > 14) {\n score -= 10;\n }\n\n // Stage stagnation (max penalty -25)\n if (signals.daysInCurrentStage > 90) {\n score -= 25;\n warnings.push(`Stuck in \"${deal.stage}\" for ${signals.daysInCurrentStage} days`);\n } else if (signals.daysInCurrentStage > 45) {\n score -= 12;\n warnings.push(`Slow progress in \"${deal.stage}\"`);\n }\n\n // Close date risk (max penalty -20)\n if (signals.daysToClose !== undefined) {\n if (signals.daysToClose < 0) {\n score -= 20;\n warnings.push(\"Close date passed\");\n } else if (signals.daysToClose < 7) {\n score -= 10;\n warnings.push(\"Close date in less than 7 days\");\n }\n }\n\n // Probability sanity (max penalty -15)\n if (signals.probability !== undefined) {\n if (signals.probability < 20 && deal.stage !== \"lead\") {\n score -= 15;\n warnings.push(`Low probability (${signals.probability}%) for stage \"${deal.stage}\"`);\n }\n }\n\n score = Math.max(0, score);\n return { score, grade: grade(score), signals, warnings };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readPipeline } from \"../../fs/pipeline-writer.js\";\nimport { scoreDeal } from \"../../core/deal-health.js\";\nimport type { DealHealthScore } from \"../../core/deal-health.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetDealHealth(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const deals = await readPipeline(dataDir, input.slug);\n const today = new Date();\n\n const results: Array<{ deal: string; stage: string } & DealHealthScore> = [];\n\n for (const deal of deals) {\n // Approximate signals from deal metadata\n const updatedDate = deal.updated ? new Date(deal.updated) : today;\n const daysSinceLastActivity = Math.floor(\n (today.getTime() - updatedDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n const daysToClose = deal.close_date\n ? Math.floor(\n (new Date(deal.close_date).getTime() - today.getTime()) / (1000 * 60 * 60 * 24)\n )\n : undefined;\n\n const health = scoreDeal(deal, {\n daysSinceLastActivity,\n daysInCurrentStage: daysSinceLastActivity,\n ...(daysToClose !== undefined ? { daysToClose } : {}),\n ...(deal.probability !== undefined ? { probability: deal.probability } : {}),\n });\n\n results.push({ deal: deal.name, stage: deal.stage, ...health });\n }\n\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ slug: input.slug, deals: results }, null, 2) },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetDealHealth(server: McpServer): void {\n server.registerTool(\n \"get_deal_health\",\n {\n title: \"Get Deal Health\",\n description: `Score the health of all deals for a customer (0–100). Grade A–F based on activity recency, stage velocity, close date proximity, and probability.\n\nReturns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n }),\n },\n async ({ slug }) => handleGetDealHealth({ slug })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport fs from \"fs\";\nimport path from \"path\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\ninterface ForecastDeal {\n slug: string;\n dealName: string;\n stage: string;\n value?: number | undefined;\n probability?: number | undefined;\n weightedValue: number;\n closeDate?: string | undefined;\n}\n\nexport async function handleGetPipelineForecast(\n input: { filter?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ deals: [], totalWeightedValue: 0, byStage: {} }, null, 2),\n },\n ],\n };\n }\n\n const slugs = fs.readdirSync(customersDir).filter((d) => {\n if (input.filter && !d.includes(input.filter)) return false;\n return fs.statSync(path.join(customersDir, d)).isDirectory();\n });\n\n const allDeals: ForecastDeal[] = [];\n\n for (const slug of slugs) {\n const pipelinePath = path.join(customersDir, slug, \"pipeline.md\");\n if (!fs.existsSync(pipelinePath)) continue;\n\n const { readPipeline } = await import(\"../../fs/pipeline-writer.js\");\n const deals = await readPipeline(dataDir, slug).catch(() => []);\n\n for (const deal of deals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n const prob = deal.probability ?? 50;\n const value = deal.value ?? 0;\n const forecastDeal: ForecastDeal = {\n slug,\n dealName: deal.name,\n stage: deal.stage,\n value,\n probability: prob,\n weightedValue: Math.round((value * prob) / 100),\n };\n if (deal.close_date !== undefined) forecastDeal.closeDate = deal.close_date;\n allDeals.push(forecastDeal);\n }\n }\n\n const totalWeightedValue = allDeals.reduce((sum, d) => sum + d.weightedValue, 0);\n const byStage = allDeals.reduce<Record<string, { count: number; weightedValue: number }>>(\n (acc, d) => {\n if (!acc[d.stage]) acc[d.stage] = { count: 0, weightedValue: 0 };\n acc[d.stage]!.count++;\n acc[d.stage]!.weightedValue += d.weightedValue;\n return acc;\n },\n {}\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ deals: allDeals, totalWeightedValue, byStage }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetPipelineForecast(server: McpServer): void {\n server.registerTool(\n \"get_pipeline_forecast\",\n {\n title: \"Get Pipeline Forecast\",\n description: `Aggregate weighted pipeline revenue across all customers. Groups open deals by stage, computes probability-weighted expected revenue.\n\nReturns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count, weightedValue } } }`,\n inputSchema: z.object({\n filter: z.string().optional().describe(\"Filter by customer slug substring\"),\n }),\n },\n async ({ filter }) => handleGetPipelineForecast({ ...(filter !== undefined ? { filter } : {}) })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { appendInteraction } from \"../../fs/interactions-writer.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSummarizeMeeting(\n input: {\n slug: string;\n transcript: string;\n with?: string;\n date?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n let summary = input.transcript.slice(0, 400);\n let nextSteps: string[] = [];\n\n try {\n const { callLlm } = await import(\"../../core/llm.js\");\n const prompt = `Summarize this meeting transcript in 3-5 sentences and extract action items.\\n\\nTranscript:\\n${input.transcript.slice(0, 3000)}\\n\\nRespond as JSON: { \"summary\": \"...\", \"nextSteps\": [\"...\"] }`;\n const response = await callLlm(prompt);\n const parsed = JSON.parse(response) as { summary?: string; nextSteps?: string[] };\n summary = parsed.summary ?? summary;\n nextSteps = parsed.nextSteps ?? [];\n } catch {\n // LLM unavailable — use raw transcript slice\n }\n\n const date = input.date ?? new Date().toISOString().slice(0, 10);\n const sourceRef = `agent://meeting/${Date.now()}`;\n\n await appendInteraction(dataDir, input.slug, {\n date,\n type: \"Meeting\",\n with: input.with ?? \"Meeting Participant\",\n summary,\n nextSteps,\n sourceRef,\n synced: new Date().toISOString(),\n });\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"summarize_meeting\",\n slug: input.slug,\n summary: summary.slice(0, 100),\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true, summary, nextSteps, sourceRef }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerSummarizeMeeting(server: McpServer): void {\n server.registerTool(\n \"summarize_meeting\",\n {\n title: \"Summarize Meeting\",\n description: `Summarize a meeting transcript and log it as an interaction. Uses LLM to extract key points and action items (falls back to raw text slice if LLM unavailable).\n\nArgs:\n slug: Customer ID\n transcript: Full meeting transcript text\n with: Participant name(s) (optional)\n date: Meeting date YYYY-MM-DD (optional, defaults to today)\n\nReturns: { success, summary, nextSteps, sourceRef }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n transcript: z.string().min(1).describe(\"Full meeting transcript\"),\n with: z.string().optional().describe(\"Participant names\"),\n date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional()\n .describe(\"Meeting date\"),\n }),\n },\n async ({ slug, transcript, with: withStr, date }) =>\n handleSummarizeMeeting({\n slug,\n transcript,\n ...(withStr !== undefined ? { with: withStr } : {}),\n ...(date !== undefined ? { date } : {}),\n })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\nexport interface PipelineStage {\n id: string;\n label: string;\n color?: string;\n order: number;\n isFinal?: boolean;\n probability?: number;\n}\n\nexport const DEFAULT_STAGES: PipelineStage[] = [\n { id: \"lead\", label: \"Lead\", order: 1, probability: 10 },\n { id: \"qualified\", label: \"Qualified\", order: 2, probability: 30 },\n { id: \"proposal\", label: \"Proposal\", order: 3, probability: 50 },\n { id: \"negotiation\", label: \"Negotiation\", order: 4, probability: 75 },\n { id: \"won\", label: \"Won\", order: 5, isFinal: true, probability: 100 },\n { id: \"lost\", label: \"Lost\", order: 6, isFinal: true, probability: 0 },\n];\n\nfunction stagesPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"pipeline-stages.json\");\n}\n\nexport function getPipelineStages(dataDir: string): PipelineStage[] {\n const p = stagesPath(dataDir);\n if (!fs.existsSync(p)) return DEFAULT_STAGES;\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as PipelineStage[];\n } catch {\n return DEFAULT_STAGES;\n }\n}\n\nexport function setPipelineStage(dataDir: string, stage: PipelineStage): void {\n const stages = getPipelineStages(dataDir);\n const idx = stages.findIndex((s) => s.id === stage.id);\n if (idx >= 0) stages[idx] = stage;\n else stages.push(stage);\n stages.sort((a, b) => a.order - b.order);\n fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });\n fs.writeFileSync(stagesPath(dataDir), JSON.stringify(stages, null, 2));\n}\n\nexport function deletePipelineStage(dataDir: string, id: string): void {\n const stages = getPipelineStages(dataDir).filter((s) => s.id !== id);\n fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });\n fs.writeFileSync(stagesPath(dataDir), JSON.stringify(stages, null, 2));\n}\n\nexport function resetToDefaults(dataDir: string): void {\n fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });\n fs.writeFileSync(stagesPath(dataDir), JSON.stringify(DEFAULT_STAGES, null, 2));\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getPipelineStages } from \"../../core/pipeline-stages.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetPipelineStages(\n _input: Record<string, never>,\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const stages = getPipelineStages(dataDir);\n return {\n content: [{ type: \"text\", text: JSON.stringify({ stages }, null, 2) }],\n };\n}\n\nexport function registerGetPipelineStages(server: McpServer): void {\n server.registerTool(\n \"get_pipeline_stages\",\n {\n title: \"Get Pipeline Stages\",\n description:\n \"Returns all configured pipeline stages. Falls back to default stages (lead, qualified, proposal, negotiation, won, lost) if no custom stages are configured.\",\n inputSchema: z.object({}),\n },\n async () => handleGetPipelineStages({})\n );\n}\n","import { searchKnowledge } from \"./lancedb.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\n\nexport interface CrossCustomerResult {\n slug: string;\n relevantContent: string;\n score: number;\n}\n\nexport async function searchAcrossCustomers(\n dataDir: string,\n query: string,\n limit = 5,\n excludeSlug?: string\n): Promise<CrossCustomerResult[]> {\n const slugs = listCustomerSlugs(dataDir).filter((d) => d !== excludeSlug);\n\n const allResults: CrossCustomerResult[] = [];\n\n for (const slug of slugs) {\n const results = await searchKnowledge(dataDir, slug, query, 2);\n for (const r of results) {\n allResults.push({\n slug,\n relevantContent: r.content.slice(0, 200),\n score: r.score,\n });\n }\n }\n\n return allResults.sort((a, b) => b.score - a.score).slice(0, limit);\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { searchAcrossCustomers, type CrossCustomerResult } from \"../../core/cross-customer.js\";\nimport { listCustomerSlugs } from \"../../fs/customer-dir.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetMarketIntelligence(\n input: { query: string; excludeCurrentCustomer?: boolean; slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const excludeSlug = input.excludeCurrentCustomer ? input.slug : undefined;\n\n // Count total customers before excluding\n const all = listCustomerSlugs(dataDir);\n const totalCustomersSearched = excludeSlug\n ? all.filter((s) => s !== excludeSlug).length\n : all.length;\n\n const results: CrossCustomerResult[] = await searchAcrossCustomers(\n dataDir,\n input.query,\n 10,\n excludeSlug\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ query: input.query, results, totalCustomersSearched }, null, 2),\n },\n ],\n };\n}\n\nexport function registerGetMarketIntelligence(server: McpServer): void {\n server.registerTool(\n \"get_market_intelligence\",\n {\n title: \"Get Market Intelligence\",\n description:\n \"Search across all customers to find patterns, common topics, or similar issues. Uses semantic search (LanceDB) across all customer knowledge bases. Results use slug (not real names) for privacy.\",\n inputSchema: z.object({\n query: z.string().describe(\"What to search for across all customers\"),\n excludeCurrentCustomer: z\n .boolean()\n .optional()\n .describe(\"Exclude the current customer from results\"),\n slug: z\n .string()\n .optional()\n .describe(\"Current customer slug (used with excludeCurrentCustomer)\"),\n }),\n },\n async ({ query, excludeCurrentCustomer, slug }) =>\n handleGetMarketIntelligence({\n query,\n ...(excludeCurrentCustomer !== undefined ? { excludeCurrentCustomer } : {}),\n ...(slug !== undefined ? { slug } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readGraph, getStakeholders, findPath } from \"../../core/graph.js\";\nimport type { GraphNode } from \"../../core/graph.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nfunction summarizeNode(n: GraphNode) {\n return { id: n.id, name: n.label, email: n.properties[\"email\"] };\n}\n\nexport async function handleGetRelationshipGraph(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const graph = readGraph(dataDir, input.slug);\n const stakeholders = getStakeholders(graph);\n\n // Warm intro paths: BFS from owner contacts to each economic buyer\n const ownerContactIds = graph.nodes\n .filter((n) => n.type === \"person\" && n.properties[\"isOwnerContact\"] === true)\n .map((n) => n.id);\n const economicBuyerIds = stakeholders.economicBuyers.map((n) => n.id);\n\n const warmIntroPaths: Array<{ target: string; path: string[] }> = [];\n for (const ebId of economicBuyerIds) {\n for (const ownerId of ownerContactIds) {\n const p = findPath(graph, ownerId, ebId);\n if (p.length > 1) {\n warmIntroPaths.push({ target: ebId, path: p });\n break;\n }\n }\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n slug: input.slug,\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n updatedAt: graph.updatedAt,\n stakeholders: {\n champions: stakeholders.champions.map(summarizeNode),\n blockers: stakeholders.blockers.map(summarizeNode),\n economicBuyers: stakeholders.economicBuyers.map(summarizeNode),\n allContacts: stakeholders.allContacts.map(summarizeNode),\n missingRoles: stakeholders.missingRoles,\n },\n warmIntroPaths,\n nodes: graph.nodes,\n edges: graph.edges,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetRelationshipGraph(server: McpServer): void {\n server.registerTool(\n \"get_relationship_graph\",\n {\n title: \"Get Relationship Graph\",\n description: `Returns the knowledge graph for a customer: all known contacts, companies,\nand the relationships between them (KNOWS, WORKS_AT, IS_CHAMPION, IS_BLOCKER, IS_ECONOMIC_BUYER).\n\nThe graph auto-populates from every log_interaction call.\nUse this before a complex deal conversation to understand the stakeholder map.\n\nArgs:\n slug: Customer slug\n\nReturns: {\n stakeholders: { champions[], blockers[], economicBuyers[], allContacts[], missingRoles[] },\n nodes: GraphNode[],\n edges: GraphEdge[]\n}`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n }),\n },\n async ({ slug }) => handleGetRelationshipGraph({ slug })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readHealth, computeCustomerHealth, writeHealth } from \"../../core/relationship-health.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\nconst MAX_HEALTH_AGE_MS = 60 * 60 * 1000; // 1 hour\n\nexport async function handleGetRelationshipHealth(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n let health = readHealth(dataDir, input.slug);\n if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {\n health = computeCustomerHealth(dataDir, input.slug);\n writeHealth(dataDir, input.slug, health);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n slug: input.slug,\n overallHealth: health.overallHealth,\n updatedAt: health.updatedAt,\n atRiskContacts: health.contacts\n .filter((c) => c.riskFlags.length > 0)\n .map((c) => c.email ?? c.contactId),\n coldContacts: health.contacts\n .filter((c) => c.trend === \"cold\")\n .map((c) => c.email ?? c.contactId),\n contacts: health.contacts,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetRelationshipHealth(server: McpServer): void {\n server.registerTool(\n \"get_relationship_health\",\n {\n title: \"Get Relationship Health\",\n description: `Returns health scores for all contacts of a customer.\nScores decay automatically when communication cadence breaks — without any manual input.\n\nEach contact gets:\n- score (0–100), grade (A–F), trend (rising|stable|declining|cold)\n- riskFlags: NO_CONTACT_14D, NO_CONTACT_30D, CHAMPION_SILENT\n- recommendation: concrete next action\n\noverallHealth is the average across all contacts.\natRiskContacts + coldContacts are pre-filtered for quick triage.\nHealth auto-updates after every log_interaction call. Recomputes if stale (>1h).\n\nArgs:\n slug: Customer slug\n\nReturns: {\n overallHealth: number,\n atRiskContacts: string[],\n coldContacts: string[],\n contacts: ContactHealth[]\n}`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n }),\n },\n async ({ slug }) => handleGetRelationshipHealth({ slug })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { callLlm } from \"./llm.js\";\nimport { withFileQueue } from \"../fs/write-queue.js\";\nimport type { DealSnapshot } from \"./revenue-simulation.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface PlaybookFrontmatter {\n trigger: string;\n successRate: number;\n usedCount: number;\n lastUpdated: string;\n}\n\nexport interface Playbook {\n slug: string;\n name: string;\n frontmatter: PlaybookFrontmatter;\n content: string;\n path: string;\n}\n\nexport interface TriggerCondition {\n type:\n | \"stage\"\n | \"value_gt\"\n | \"value_lt\"\n | \"days_stalled_gt\"\n | \"days_stalled_lt\"\n | \"health_lt\"\n | \"health_gt\"\n | \"no_champion\"\n | \"has_champion\";\n value?: number;\n stage?: string;\n}\n\nexport interface PlaybookMatch {\n playbook: Playbook;\n score: number;\n matchedConditions: TriggerCondition[];\n totalConditions: number;\n}\n\nexport interface LlmDistillation {\n name: string;\n trigger: string;\n content: string;\n successRate: number;\n reasoning: string;\n}\n\n// ─── File paths ───────────────────────────────────────────────────────────────\n\nexport function playbooksDir(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"playbooks\");\n}\n\n// ─── File operations ──────────────────────────────────────────────────────────\n\nexport function listPlaybooks(dataDir: string, slug: string): Playbook[] {\n const dir = playbooksDir(dataDir, slug);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".md\"))\n .map((f): Playbook => {\n const filePath = path.join(dir, f);\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = matter(raw);\n return {\n slug,\n name: f.replace(/\\.md$/, \"\"),\n frontmatter: parsed.data as PlaybookFrontmatter,\n content: parsed.content.trim(),\n path: filePath,\n };\n });\n}\n\nexport function readPlaybook(dataDir: string, slug: string, name: string): Playbook | null {\n const filePath = path.join(playbooksDir(dataDir, slug), `${name}.md`);\n if (!fs.existsSync(filePath)) return null;\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = matter(raw);\n return {\n slug,\n name,\n frontmatter: parsed.data as PlaybookFrontmatter,\n content: parsed.content.trim(),\n path: filePath,\n };\n}\n\nexport async function writePlaybook(\n dataDir: string,\n slug: string,\n playbook: Playbook\n): Promise<void> {\n const dir = playbooksDir(dataDir, slug);\n const filePath = path.join(dir, `${playbook.name}.md`);\n await withFileQueue(filePath, async () => {\n fs.mkdirSync(dir, { recursive: true });\n const raw = matter.stringify(playbook.content, playbook.frontmatter);\n fs.writeFileSync(filePath, raw, \"utf-8\");\n });\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport function toKebabCase(name: string): string {\n return name\n .replace(/[^a-z0-9-]/gi, \"-\")\n .toLowerCase()\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n// ─── Trigger DSL ──────────────────────────────────────────────────────────────\n\nexport interface ParsedTrigger {\n conditions: TriggerCondition[];\n operator: \"AND\" | \"OR\";\n}\n\nfunction parseTokens(tokens: string[]): TriggerCondition[] {\n return tokens.flatMap((token): TriggerCondition[] => {\n if (token.startsWith(\"deal_stage_\")) {\n return [{ type: \"stage\", stage: token.slice(\"deal_stage_\".length) }];\n }\n const valueGt = token.match(/^value\\s*>\\s*(\\d+)$/);\n if (valueGt) return [{ type: \"value_gt\", value: Number(valueGt[1]) }];\n\n const valueLt = token.match(/^value\\s*<\\s*(\\d+)$/);\n if (valueLt) return [{ type: \"value_lt\", value: Number(valueLt[1]) }];\n\n const stalledGt = token.match(/^days_stalled\\s*>\\s*(\\d+)$/);\n if (stalledGt) return [{ type: \"days_stalled_gt\", value: Number(stalledGt[1]) }];\n\n const stalledLt = token.match(/^days_stalled\\s*<\\s*(\\d+)$/);\n if (stalledLt) return [{ type: \"days_stalled_lt\", value: Number(stalledLt[1]) }];\n\n const healthLt = token.match(/^health\\s*<\\s*(\\d+)$/);\n if (healthLt) return [{ type: \"health_lt\", value: Number(healthLt[1]) }];\n\n const healthGt = token.match(/^health\\s*>\\s*(\\d+)$/);\n if (healthGt) return [{ type: \"health_gt\", value: Number(healthGt[1]) }];\n\n if (token === \"no_champion\") return [{ type: \"no_champion\" }];\n if (token === \"has_champion\") return [{ type: \"has_champion\" }];\n\n return []; // unknown token — silently dropped\n });\n}\n\nexport function parseTrigger(triggerStr: string | null | undefined): TriggerCondition[] {\n if (!triggerStr?.trim()) return [];\n const tokens = triggerStr\n .split(/\\s+AND\\s+/)\n .map((t) => t.trim())\n .filter(Boolean);\n return parseTokens(tokens);\n}\n\nexport function parseTriggerFull(triggerStr: string | null | undefined): ParsedTrigger {\n if (!triggerStr?.trim()) return { conditions: [], operator: \"AND\" };\n const orTokens = triggerStr\n .split(/\\s+OR\\s+/)\n .map((t) => t.trim())\n .filter(Boolean);\n if (orTokens.length > 1) {\n return { conditions: parseTokens(orTokens), operator: \"OR\" };\n }\n const andTokens = triggerStr\n .split(/\\s+AND\\s+/)\n .map((t) => t.trim())\n .filter(Boolean);\n return { conditions: parseTokens(andTokens), operator: \"AND\" };\n}\n\nexport function evaluateCondition(\n cond: TriggerCondition,\n deal: DealSnapshot,\n daysSinceContact: number\n): boolean {\n switch (cond.type) {\n case \"stage\":\n return deal.stage === cond.stage;\n case \"value_gt\":\n return deal.value > (cond.value ?? 0);\n case \"value_lt\":\n return deal.value < (cond.value ?? Infinity);\n // v1: days_stalled uses daysSinceContact as proxy (stage-change timestamps not tracked)\n case \"days_stalled_gt\":\n return daysSinceContact > (cond.value ?? 0);\n case \"days_stalled_lt\":\n return daysSinceContact < (cond.value ?? Infinity);\n case \"health_lt\":\n return deal.healthScore < (cond.value ?? 100);\n case \"health_gt\":\n return deal.healthScore > (cond.value ?? 0);\n case \"no_champion\":\n return !deal.championPresent;\n case \"has_champion\":\n return deal.championPresent;\n default:\n return false;\n }\n}\n\nexport function evaluateTrigger(\n conditions: TriggerCondition[],\n deal: DealSnapshot,\n daysSinceContact: number = 0,\n operator: \"AND\" | \"OR\" = \"AND\"\n): boolean {\n if (operator === \"OR\") {\n return conditions.some((c) => evaluateCondition(c, deal, daysSinceContact));\n }\n return conditions.every((c) => evaluateCondition(c, deal, daysSinceContact));\n}\n\n// ─── Matching ─────────────────────────────────────────────────────────────────\n\nexport function matchPlaybooks(\n playbooks: Playbook[],\n deal: DealSnapshot,\n daysSinceContact: number = 0\n): PlaybookMatch[] {\n const results: PlaybookMatch[] = [];\n for (const pb of playbooks) {\n const { conditions, operator } = parseTriggerFull(pb.frontmatter.trigger);\n if (conditions.length === 0) continue;\n const matched = conditions.filter((c) => evaluateCondition(c, deal, daysSinceContact));\n const isMatch = operator === \"OR\" ? matched.length > 0 : matched.length === conditions.length;\n if (isMatch) {\n results.push({\n playbook: pb,\n score: 1.0,\n matchedConditions: matched,\n totalConditions: conditions.length,\n });\n }\n }\n return results.sort((a, b) => {\n const rateDiff =\n (b.playbook.frontmatter.successRate ?? 0) - (a.playbook.frontmatter.successRate ?? 0);\n return rateDiff !== 0\n ? rateDiff\n : (b.playbook.frontmatter.usedCount ?? 0) - (a.playbook.frontmatter.usedCount ?? 0);\n });\n}\n\nexport function getBestPlaybook(\n dataDir: string,\n slug: string,\n deal: DealSnapshot,\n daysSinceContact: number = 0\n): PlaybookMatch | null {\n return matchPlaybooks(listPlaybooks(dataDir, slug), deal, daysSinceContact)[0] ?? null;\n}\n\n// ─── Distillation ─────────────────────────────────────────────────────────────\n\nexport function buildDistillPrompt(\n slug: string,\n dealName: string,\n outcome: \"won\" | \"lost\",\n interactions: string\n): string {\n return `You are analyzing a sales deal to extract a reusable playbook.\n\nCustomer: ${slug}\nDeal: ${dealName}\nOutcome: ${outcome}\nInteractions (chronological):\n${interactions.slice(0, 4000)}\n\nExtract a reusable playbook from this deal's journey.\n\nAllowed trigger tokens (combine with \" AND \"):\n- deal_stage_<stage> (e.g. deal_stage_negotiation)\n- value > <n> (e.g. value > 50000)\n- value < <n>\n- days_stalled > <n> (e.g. days_stalled > 7)\n- days_stalled < <n>\n- health < <n> (e.g. health < 60)\n- health > <n>\n- no_champion\n- has_champion\n\nReturn JSON only (no markdown wrapper):\n{\n \"name\": \"<kebab-case-playbook-name>\",\n \"trigger\": \"<DSL string using allowed tokens>\",\n \"content\": \"<markdown with ## Situation, ## Steps, ## Warnings sections>\",\n \"successRate\": <0.0-1.0>,\n \"reasoning\": \"<why these trigger conditions>\"\n}`;\n}\n\nexport function parseLlmDistillation(\n response: string,\n outcomeFallback: number = 0.5\n): LlmDistillation | null {\n try {\n const match = response.match(/\\{[\\s\\S]*\\}/);\n if (!match) return null;\n const parsed = JSON.parse(match[0]) as Partial<LlmDistillation>;\n if (!parsed.name || !parsed.trigger || !parsed.content) return null;\n return {\n name: parsed.name,\n trigger: parsed.trigger,\n content: parsed.content,\n successRate: typeof parsed.successRate === \"number\" ? parsed.successRate : outcomeFallback,\n reasoning: parsed.reasoning ?? \"\",\n };\n } catch {\n return null;\n }\n}\n\nexport type DistillPlaybookResult =\n | { ok: true; playbook: Playbook; reasoning: string }\n | { ok: false; errorKind: \"no_interactions\" | \"parse_failed\" };\n\nexport async function distillPlaybook(\n dataDir: string,\n slug: string,\n dealName: string,\n outcome: \"won\" | \"lost\",\n llmFn: (prompt: string) => Promise<string> = callLlm\n): Promise<DistillPlaybookResult> {\n const interactionsPath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) return { ok: false, errorKind: \"no_interactions\" };\n\n const interactions = fs.readFileSync(interactionsPath, \"utf-8\");\n const prompt = buildDistillPrompt(slug, dealName, outcome, interactions);\n const response = await llmFn(prompt);\n\n const outcomeFallback = outcome === \"won\" ? 1.0 : 0.0;\n const distillation = parseLlmDistillation(response, outcomeFallback);\n if (!distillation) return { ok: false, errorKind: \"parse_failed\" };\n\n const today = new Date().toISOString().slice(0, 10);\n const name = toKebabCase(distillation.name);\n\n const playbook: Playbook = {\n slug,\n name,\n frontmatter: {\n trigger: distillation.trigger,\n successRate: distillation.successRate,\n usedCount: 1,\n lastUpdated: today,\n },\n content: distillation.content,\n path: path.join(playbooksDir(dataDir, slug), `${name}.md`),\n };\n\n await writePlaybook(dataDir, slug, playbook);\n return { ok: true, playbook, reasoning: distillation.reasoning };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { scoreDeal } from \"../core/deal-health.js\";\nimport { computeCustomerHealth } from \"../core/relationship-health.js\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { readGraph, getStakeholders } from \"../core/graph.js\";\nimport { callLlm } from \"../core/llm.js\";\nimport { listPlaybooks, matchPlaybooks, type PlaybookMatch } from \"../core/playbooks.js\";\nimport type { PipelineDeal } from \"../schemas/pipeline.js\";\nimport type { DealHealthScore } from \"../core/deal-health.js\";\nimport type { HealthSnapshot } from \"../core/relationship-health.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AutonomyLevel = \"observe\" | \"suggest\" | \"act\";\nexport type ActionType = \"log_interaction\" | \"update_deal\" | \"alert\" | \"schedule_meeting\";\nexport type ActionStatus = \"pending\" | \"approved\" | \"executed\" | \"rejected\" | \"skipped\";\nexport type RiskLevel = \"low\" | \"medium\" | \"high\" | \"critical\";\n\nexport interface DealAgentConfig {\n slug: string;\n dealName: string;\n autonomyLevel: AutonomyLevel;\n instruction?: string;\n valueThreshold: number;\n today: string;\n}\n\nexport interface DealAgentAction {\n actionId: string;\n type: ActionType;\n payload: Record<string, unknown>;\n confidence: number;\n reasoning: string;\n requiresHumanApproval: boolean;\n status: ActionStatus;\n createdAt: string;\n}\n\nexport interface DealPlanStep {\n step: number;\n action: string;\n priority: \"urgent\" | \"high\" | \"medium\" | \"low\";\n reason: string;\n}\n\nexport interface DealAgentTrace {\n timestamp: string;\n slug: string;\n dealName: string;\n autonomyLevel: AutonomyLevel;\n observation: string;\n plan: string[];\n actionsConsidered: DealAgentAction[];\n actionTaken: DealAgentAction | null;\n outcome: \"queued\" | \"executed\" | \"observed\" | \"error\";\n}\n\nexport interface DealAgentResult {\n slug: string;\n dealName: string;\n assessment: string;\n riskLevel: RiskLevel;\n plan: DealPlanStep[];\n actionsQueued: DealAgentAction[];\n actionsExecuted: DealAgentAction[];\n trace: DealAgentTrace;\n}\n\nexport interface LlmDealAnalysis {\n assessment: string;\n riskLevel: RiskLevel;\n plan: DealPlanStep[];\n actions: Array<{\n type: ActionType;\n payload: Record<string, unknown>;\n confidence: number;\n reasoning: string;\n }>;\n}\n\nexport interface DealObservation {\n deal: PipelineDeal;\n daysSinceLastActivity: number;\n daysInCurrentStage: number;\n daysToClose?: number;\n dealHealthScore: DealHealthScore;\n overallRelationshipHealth: number;\n atRiskContacts: string[];\n coldContacts: string[];\n missingRoles: Array<{ role: string; urgency: string }>;\n championCount: number;\n recentInteractionsSummary: string;\n contextSummary: string;\n matchingPlaybooks?: PlaybookMatch[]; // D15: procedural memory\n}\n\nexport interface AgentQueue {\n schemaVersion: \"1\";\n slug: string;\n pendingActions: DealAgentAction[];\n updatedAt: string;\n}\n\n// ─── File paths ───────────────────────────────────────────────────────────────\n\nexport function agentQueuePath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"agent-queue.json\");\n}\n\n// ─── Queue read / write ───────────────────────────────────────────────────────\n\nexport function readAgentQueue(dataDir: string, slug: string): AgentQueue {\n const p = agentQueuePath(dataDir, slug);\n if (!fs.existsSync(p)) {\n return { schemaVersion: \"1\", slug, pendingActions: [], updatedAt: new Date().toISOString() };\n }\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\")) as AgentQueue;\n } catch {\n return { schemaVersion: \"1\", slug, pendingActions: [], updatedAt: new Date().toISOString() };\n }\n}\n\nexport function writeAgentQueue(dataDir: string, slug: string, queue: AgentQueue): void {\n const p = agentQueuePath(dataDir, slug);\n const dir = path.dirname(p);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n const updated: AgentQueue = { ...queue, updatedAt: new Date().toISOString() };\n fs.writeFileSync(p, JSON.stringify(updated, null, 2), \"utf-8\");\n}\n\n// ─── Action ID ────────────────────────────────────────────────────────────────\n\nexport function makeActionId(): string {\n return `da_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n// ─── Observation helpers ──────────────────────────────────────────────────────\n\nfunction buildRecentInteractionsSummary(interactionsPath: string): string {\n if (!fs.existsSync(interactionsPath)) return \"(no interactions)\";\n const content = fs.readFileSync(interactionsPath, \"utf-8\") as string;\n const blocks = content\n .split(/(?=^## \\d{4}-\\d{2}-\\d{2})/m)\n .filter((b) => b.trim().length > 0)\n .slice(0, 3);\n return blocks\n .map((b) => {\n const dateMatch = b.match(/^## (\\d{4}-\\d{2}-\\d{2}) · (\\w+)/m);\n const summaryMatch = b.match(/^\\*\\*Summary:\\*\\*\\s*(.+)$/m);\n if (!dateMatch || !summaryMatch) return \"\";\n return `[${dateMatch[1]!}/${dateMatch[2]!}] ${summaryMatch[1]!.trim()}`;\n })\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nfunction buildContextSummary(data: {\n deal: PipelineDeal;\n daysSinceLastActivity: number;\n daysInCurrentStage: number;\n daysToClose: number | undefined;\n dealHealthScore: DealHealthScore;\n health: HealthSnapshot;\n atRiskContacts: string[];\n coldContacts: string[];\n missingRoles: Array<{ role: string; urgency: string }>;\n championCount: number;\n recentInteractionsSummary: string;\n}): string {\n const lines: string[] = [\n `Deal: ${data.deal.name} | Stage: ${data.deal.stage} | Value: €${data.deal.value ?? \"?\"} | Close: ${data.deal.close_date ?? \"not set\"}`,\n `Days since activity: ${data.daysSinceLastActivity} | Days to close: ${data.daysToClose ?? \"?\"}`,\n `Deal health: grade ${data.dealHealthScore.grade} (score ${data.dealHealthScore.score})`,\n `Warnings: ${data.dealHealthScore.warnings.join(\"; \") || \"none\"}`,\n ``,\n `Relationship health: ${data.health.overallHealth}/100`,\n `At-risk contacts: ${data.atRiskContacts.join(\", \") || \"none\"}`,\n `Cold contacts: ${data.coldContacts.join(\", \") || \"none\"}`,\n `Missing stakeholder roles: ${data.missingRoles.map((r) => r.role).join(\", \") || \"none\"}`,\n `Champions identified: ${data.championCount}`,\n ``,\n `Recent interactions:`,\n data.recentInteractionsSummary || \"(none)\",\n ];\n return lines.join(\"\\n\");\n}\n\n// ─── Observation Layer ────────────────────────────────────────────────────────\n\nexport async function observeDeal(\n dataDir: string,\n slug: string,\n dealName: string,\n today: string\n): Promise<DealObservation | null> {\n const deals = await readPipeline(dataDir, slug).catch(() => [] as PipelineDeal[]);\n const deal = deals.find((d) => d.name.toLowerCase() === dealName.toLowerCase());\n if (!deal) return null;\n\n const todayDate = new Date(today);\n const updatedDate = deal.updated ? new Date(deal.updated) : todayDate;\n const daysSinceLastActivity = Math.floor(\n (todayDate.getTime() - updatedDate.getTime()) / 86_400_000\n );\n const daysInCurrentStage = daysSinceLastActivity;\n\n const daysToClose =\n deal.close_date && deal.close_date.trim() !== \"\"\n ? Math.floor((new Date(deal.close_date).getTime() - todayDate.getTime()) / 86_400_000)\n : undefined;\n\n const dealHealthScore = scoreDeal(deal, {\n daysSinceLastActivity,\n daysInCurrentStage,\n ...(daysToClose !== undefined ? { daysToClose } : {}),\n ...(deal.probability !== undefined ? { probability: deal.probability } : {}),\n });\n\n const health = computeCustomerHealth(dataDir, slug, today);\n const atRiskContacts = health.contacts\n .filter((c) => c.riskFlags.length > 0)\n .map((c) => c.email ?? c.contactId);\n const coldContacts = health.contacts\n .filter((c) => c.trend === \"cold\")\n .map((c) => c.email ?? c.contactId);\n\n const graph = readGraph(dataDir, slug);\n const stakeholders = getStakeholders(graph);\n const missingRoles = stakeholders.missingRoles.map((r) => ({\n role: r.role,\n urgency: r.urgency,\n }));\n const championCount = stakeholders.champions.length;\n\n const interactionsPath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n const recentInteractionsSummary = buildRecentInteractionsSummary(interactionsPath);\n\n const contextSummary = buildContextSummary({\n deal,\n daysSinceLastActivity,\n daysInCurrentStage,\n daysToClose,\n dealHealthScore,\n health,\n atRiskContacts,\n coldContacts,\n missingRoles,\n championCount,\n recentInteractionsSummary,\n });\n\n const obs: DealObservation = {\n deal,\n daysSinceLastActivity,\n daysInCurrentStage,\n dealHealthScore,\n overallRelationshipHealth: health.overallHealth,\n atRiskContacts,\n coldContacts,\n missingRoles,\n championCount,\n recentInteractionsSummary,\n contextSummary,\n };\n if (daysToClose !== undefined) obs.daysToClose = daysToClose;\n\n // D15: load matching playbooks from procedural memory\n const dealSnap = {\n slug,\n name: deal.name,\n stage: deal.stage,\n value: deal.value ?? 0,\n probability: deal.probability ?? 50,\n healthScore: health.overallHealth,\n daysSinceContact: daysSinceLastActivity,\n championPresent: championCount > 0,\n };\n const allPlaybooks = listPlaybooks(dataDir, slug);\n const matchingPlaybooks = matchPlaybooks(allPlaybooks, dealSnap, daysSinceLastActivity);\n if (matchingPlaybooks.length > 0) obs.matchingPlaybooks = matchingPlaybooks;\n\n return obs;\n}\n\n// ─── LLM Prompt ──────────────────────────────────────────────────────────────\n\nexport function buildLlmPrompt(obs: DealObservation, config: DealAgentConfig): string {\n const instruction = config.instruction ?? \"Analyze this deal and recommend next actions.\";\n\n const playbookSection =\n obs.matchingPlaybooks && obs.matchingPlaybooks.length > 0\n ? `\\n## Matching Playbooks (${obs.matchingPlaybooks.length} found — apply these proven tactics)\\n` +\n obs.matchingPlaybooks\n .slice(0, 2)\n .map(\n (m) =>\n `### ${m.playbook.name} (${Math.round(m.playbook.frontmatter.successRate * 100)}% success rate, used ${m.playbook.frontmatter.usedCount}x)\\n${m.playbook.content.slice(0, 500)}`\n )\n .join(\"\\n\\n\")\n : \"\";\n\n return `You are a CRM deal agent. Analyze the deal situation and return an action plan.\nReturn ONLY valid JSON — no markdown, no explanation.\n\n${obs.contextSummary}${playbookSection}\n\nInstruction: ${instruction}\n\nRespond with JSON matching exactly:\n{\n \"assessment\": \"<2-3 sentence situation assessment>\",\n \"riskLevel\": \"low\" | \"medium\" | \"high\" | \"critical\",\n \"plan\": [\n { \"step\": 1, \"action\": \"<what to do>\", \"priority\": \"urgent\" | \"high\" | \"medium\" | \"low\", \"reason\": \"<why>\" }\n ],\n \"actions\": [\n {\n \"type\": \"log_interaction\" | \"update_deal\" | \"alert\" | \"schedule_meeting\",\n \"payload\": { /* tool-specific fields */ },\n \"confidence\": 0.0-1.0,\n \"reasoning\": \"<why this action>\"\n }\n ]\n}\n\nPayload schema per type:\n- log_interaction: { slug, type: \"Note\"|\"Call\"|\"Meeting\", summary, with }\n- update_deal: { slug, dealName, stage?, probability?, closeDate?, notes? }\n- alert: { slug, message, urgency: \"critical\"|\"high\"|\"medium\" }\n- schedule_meeting: { slug, with, notes }`;\n}\n\n// ─── LLM Response Parser ──────────────────────────────────────────────────────\n\nexport function parseLlmResponse(response: string): LlmDealAnalysis | null {\n try {\n const cleaned = response\n .replace(/^```(?:json)?\\n?/m, \"\")\n .replace(/\\n?```$/m, \"\")\n .trim();\n const parsed = JSON.parse(cleaned) as Partial<LlmDealAnalysis>;\n if (!parsed.assessment || !parsed.riskLevel || !Array.isArray(parsed.plan)) {\n return null;\n }\n return {\n assessment: String(parsed.assessment),\n riskLevel: parsed.riskLevel,\n plan: Array.isArray(parsed.plan) ? parsed.plan : [],\n actions: Array.isArray(parsed.actions) ? parsed.actions : [],\n };\n } catch {\n return null;\n }\n}\n\n// ─── Rule-Based Fallback ──────────────────────────────────────────────────────\n\nexport function buildRuleBasedAnalysis(\n obs: DealObservation,\n config: DealAgentConfig\n): LlmDealAnalysis {\n const plan: DealPlanStep[] = [];\n const actions: LlmDealAnalysis[\"actions\"] = [];\n let riskLevel: RiskLevel = \"low\";\n\n if (obs.dealHealthScore.grade === \"F\" || obs.coldContacts.length > 0) riskLevel = \"critical\";\n else if (obs.dealHealthScore.grade === \"D\" || obs.atRiskContacts.length > 0) riskLevel = \"high\";\n else if (obs.dealHealthScore.grade === \"C\") riskLevel = \"medium\";\n\n let step = 1;\n\n // D15: Playbook alerts as first plan items\n if (obs.matchingPlaybooks && obs.matchingPlaybooks.length > 0) {\n for (const match of obs.matchingPlaybooks.slice(0, 2)) {\n plan.push({\n step: step++,\n action: `Apply playbook: \"${match.playbook.name}\"`,\n priority: \"high\",\n reason: `Proven tactic (${Math.round(match.playbook.frontmatter.successRate * 100)}% success, used ${match.playbook.frontmatter.usedCount}x) — trigger: ${match.playbook.frontmatter.trigger}`,\n });\n actions.push({\n type: \"alert\",\n payload: {\n slug: config.slug,\n message: `Playbook available: \"${match.playbook.name}\" (${Math.round(match.playbook.frontmatter.successRate * 100)}% success rate)`,\n playbookContent: match.playbook.content.slice(0, 1000),\n urgency: \"high\",\n },\n confidence: match.playbook.frontmatter.successRate,\n reasoning: `Trigger matched: ${match.playbook.frontmatter.trigger}`,\n });\n }\n }\n\n if (obs.coldContacts.length > 0) {\n plan.push({\n step: step++,\n action: `Re-engage cold contacts: ${obs.coldContacts.join(\", \")}`,\n priority: \"urgent\",\n reason: \"No contact in 30+ days\",\n });\n actions.push({\n type: \"alert\",\n payload: {\n slug: config.slug,\n message: `Cold contacts: ${obs.coldContacts.join(\", \")}`,\n urgency: \"critical\",\n },\n confidence: 0.95,\n reasoning: \"No contact in 30+ days\",\n });\n }\n\n if (obs.atRiskContacts.length > 0) {\n plan.push({\n step: step++,\n action: `Schedule call with at-risk contacts`,\n priority: \"high\",\n reason: \"14+ days without contact\",\n });\n actions.push({\n type: \"schedule_meeting\",\n payload: {\n slug: config.slug,\n with: obs.atRiskContacts[0] ?? \"\",\n notes: \"Scheduled by deal agent — relationship at risk\",\n },\n confidence: 0.8,\n reasoning: \"At-risk contact identified\",\n });\n }\n\n if (obs.missingRoles.some((r) => r.role === \"economic_buyer\")) {\n plan.push({\n step: step++,\n action: \"Identify economic buyer\",\n priority: \"high\",\n reason: \"No budget owner identified\",\n });\n }\n\n if (obs.daysToClose !== undefined && obs.daysToClose < 14 && obs.dealHealthScore.grade !== \"A\") {\n plan.push({\n step: step++,\n action: \"Update deal close date or probability\",\n priority: \"urgent\",\n reason: `Close date in ${obs.daysToClose} days, deal at grade ${obs.dealHealthScore.grade}`,\n });\n actions.push({\n type: \"update_deal\",\n payload: {\n slug: config.slug,\n dealName: config.dealName,\n notes: `Reviewed by deal agent — ${obs.daysToClose}d to close`,\n },\n confidence: 0.75,\n reasoning: \"Close date imminent\",\n });\n }\n\n if (plan.length === 0) {\n plan.push({\n step: 1,\n action: \"Maintain current cadence\",\n priority: \"low\",\n reason: \"Deal healthy\",\n });\n }\n\n const assessment = `Deal \"${config.dealName}\" in stage \"${obs.deal.stage}\" — health grade ${obs.dealHealthScore.grade} (${obs.dealHealthScore.score}/100). Risk: ${riskLevel}.`;\n return { assessment, riskLevel, plan, actions };\n}\n\n// ─── Action Selection ─────────────────────────────────────────────────────────\n\nexport function selectActions(\n analysis: LlmDealAnalysis,\n obs: DealObservation,\n config: DealAgentConfig\n): DealAgentAction[] {\n return analysis.actions.map((a) => {\n const dealValue = obs.deal.value ?? 0;\n const autoExecutable =\n config.autonomyLevel === \"act\" && a.confidence >= 0.7 && dealValue < config.valueThreshold;\n\n return {\n actionId: makeActionId(),\n type: a.type,\n payload: a.payload,\n confidence: a.confidence,\n reasoning: a.reasoning,\n requiresHumanApproval: !autoExecutable,\n status: \"pending\" as ActionStatus,\n createdAt: new Date().toISOString(),\n };\n });\n}\n\n// ─── Action Execution ─────────────────────────────────────────────────────────\n\nconst VALID_STAGES = [\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"] as const;\n\nexport async function executeAction(\n action: DealAgentAction,\n dataDir: string\n): Promise<\"executed\" | \"skipped\"> {\n const slug = action.payload[\"slug\"] as string | undefined;\n if (!slug) return \"skipped\";\n\n switch (action.type) {\n case \"log_interaction\": {\n const { appendInteraction } = await import(\"../fs/interactions-writer.js\");\n const today = new Date().toISOString().slice(0, 10);\n await appendInteraction(dataDir, slug, {\n date: today,\n type: (action.payload[\"type\"] as InteractionEntry[\"type\"]) ?? \"Note\",\n with: String(action.payload[\"with\"] ?? \"agent\"),\n summary: String(action.payload[\"summary\"] ?? \"\"),\n nextSteps: [],\n sourceRef: `agent://deal-agent/${action.actionId}`,\n synced: new Date().toISOString(),\n });\n return \"executed\";\n }\n\n case \"schedule_meeting\": {\n const { appendInteraction } = await import(\"../fs/interactions-writer.js\");\n const today = new Date().toISOString().slice(0, 10);\n await appendInteraction(dataDir, slug, {\n date: today,\n type: \"Note\",\n with: String(action.payload[\"with\"] ?? \"\"),\n summary: `[Agent scheduled] ${String(action.payload[\"notes\"] ?? \"Meeting scheduled by deal agent\")}`,\n nextSteps: [`Schedule meeting with ${String(action.payload[\"with\"] ?? \"contact\")}`],\n sourceRef: `agent://deal-agent/${action.actionId}`,\n synced: new Date().toISOString(),\n });\n return \"executed\";\n }\n\n case \"update_deal\": {\n const { handleUpdateDeal } = await import(\"../mcp/tools/update-deal.js\");\n const payload = action.payload as {\n slug: string;\n dealName: string;\n stage?: string;\n value?: number;\n probability?: number;\n closeDate?: string;\n notes?: string;\n };\n const validStage = VALID_STAGES.find((s) => s === payload.stage);\n await handleUpdateDeal(\n {\n slug: payload.slug,\n dealName: payload.dealName,\n ...(validStage !== undefined ? { stage: validStage } : {}),\n ...(payload.value !== undefined ? { value: payload.value } : {}),\n ...(payload.probability !== undefined ? { probability: payload.probability } : {}),\n ...(payload.closeDate !== undefined ? { closeDate: payload.closeDate } : {}),\n ...(payload.notes !== undefined ? { notes: payload.notes } : {}),\n },\n dataDir\n );\n return \"executed\";\n }\n\n case \"alert\": {\n const queue = readAgentQueue(dataDir, slug);\n const alertAction: DealAgentAction = { ...action, status: \"pending\" };\n if (!queue.pendingActions.find((a) => a.actionId === action.actionId)) {\n queue.pendingActions.push(alertAction);\n writeAgentQueue(dataDir, slug, queue);\n }\n return \"executed\";\n }\n }\n}\n\n// ─── Main: runDealAgent ───────────────────────────────────────────────────────\n\nexport async function runDealAgent(\n config: DealAgentConfig,\n dataDir: string,\n llmFn: (prompt: string) => Promise<string> = callLlm\n): Promise<DealAgentResult> {\n const timestamp = new Date().toISOString();\n\n const obs = await observeDeal(dataDir, config.slug, config.dealName, config.today);\n if (!obs) {\n throw new Error(`Deal \"${config.dealName}\" not found for customer \"${config.slug}\"`);\n }\n\n let analysis: LlmDealAnalysis;\n try {\n const prompt = buildLlmPrompt(obs, config);\n const rawResponse = await llmFn(prompt);\n analysis = parseLlmResponse(rawResponse) ?? buildRuleBasedAnalysis(obs, config);\n } catch {\n analysis = buildRuleBasedAnalysis(obs, config);\n }\n\n const allActions = selectActions(analysis, obs, config);\n\n const actionsQueued: DealAgentAction[] = [];\n const actionsExecuted: DealAgentAction[] = [];\n\n if (config.autonomyLevel === \"observe\") {\n // No side effects\n } else if (config.autonomyLevel === \"suggest\") {\n if (allActions.length > 0) {\n const queue = readAgentQueue(dataDir, config.slug);\n for (const action of allActions) {\n queue.pendingActions.push({ ...action, requiresHumanApproval: true });\n actionsQueued.push(action);\n }\n writeAgentQueue(dataDir, config.slug, queue);\n }\n } else {\n // act mode\n const queue = readAgentQueue(dataDir, config.slug);\n let queueDirty = false;\n for (const action of allActions) {\n if (!action.requiresHumanApproval) {\n const outcome = await executeAction(action, dataDir).catch(() => \"skipped\" as const);\n actionsExecuted.push({\n ...action,\n status: outcome === \"executed\" ? \"executed\" : \"skipped\",\n });\n } else {\n queue.pendingActions.push(action);\n actionsQueued.push(action);\n queueDirty = true;\n }\n }\n if (queueDirty) writeAgentQueue(dataDir, config.slug, queue);\n }\n\n const trace: DealAgentTrace = {\n timestamp,\n slug: config.slug,\n dealName: config.dealName,\n autonomyLevel: config.autonomyLevel,\n observation: obs.contextSummary,\n plan: analysis.plan.map((s) => `${s.step}. ${s.action} [${s.priority}]`),\n actionsConsidered: allActions,\n actionTaken: actionsExecuted[0] ?? null,\n outcome:\n actionsExecuted.length > 0 ? \"executed\" : actionsQueued.length > 0 ? \"queued\" : \"observed\",\n };\n\n return {\n slug: config.slug,\n dealName: config.dealName,\n assessment: analysis.assessment,\n riskLevel: analysis.riskLevel,\n plan: analysis.plan,\n actionsQueued,\n actionsExecuted,\n trace,\n };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { runDealAgent } from \"../../agents/deal-agent.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleRunDealAgent(\n input: {\n slug: string;\n dealName: string;\n autonomyLevel?: \"observe\" | \"suggest\" | \"act\";\n instruction?: string;\n valueThreshold?: number;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const result = await runDealAgent(\n {\n slug: input.slug,\n dealName: input.dealName,\n autonomyLevel: input.autonomyLevel ?? \"suggest\",\n valueThreshold: input.valueThreshold ?? 50_000,\n today,\n ...(input.instruction !== undefined ? { instruction: input.instruction } : {}),\n },\n dataDir\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerRunDealAgent(server: McpServer): void {\n server.registerTool(\n \"run_deal_agent\",\n {\n title: \"Run Deal Agent\",\n description: `Analyzes a specific deal and generates a prioritized action plan.\n\nThree autonomy levels:\n- observe: analyze and return plan, no side effects\n- suggest (default): queue actions for human review in agent-queue.json\n- act: auto-execute actions with confidence ≥ 0.7 and value < valueThreshold\n\nEach action includes confidence score and reasoning (glass-box).\nReturns full trace for inspection.\n\nArgs:\n slug: Customer slug\n dealName: Exact deal name\n autonomyLevel: \"observe\" | \"suggest\" | \"act\" (default: \"suggest\")\n instruction: Optional context/question for the agent\n valueThreshold: EUR value above which no auto-execution (default: 50000)\n\nReturns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], trace }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().describe(\"Exact deal name\"),\n autonomyLevel: z\n .enum([\"observe\", \"suggest\", \"act\"])\n .optional()\n .describe(\"Autonomy level (default: suggest)\"),\n instruction: z.string().optional().describe(\"Optional instruction for the agent\"),\n valueThreshold: z\n .number()\n .optional()\n .describe(\"EUR value above which no auto-execution (default: 50000)\"),\n }),\n },\n async ({ slug, dealName, autonomyLevel, instruction, valueThreshold }) =>\n handleRunDealAgent({\n slug,\n dealName,\n ...(autonomyLevel !== undefined ? { autonomyLevel } : {}),\n ...(instruction !== undefined ? { instruction } : {}),\n ...(valueThreshold !== undefined ? { valueThreshold } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readAgentQueue, writeAgentQueue, executeAction } from \"../../agents/deal-agent.js\";\n\nexport { readAgentQueue } from \"../../agents/deal-agent.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleApproveAgentAction(\n input: { slug: string; actionId: string; approved: boolean },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const queue = readAgentQueue(dataDir, input.slug);\n const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);\n\n if (idx === -1) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: false, error: `Action ${input.actionId} not found in queue` },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const action = queue.pendingActions[idx]!;\n\n if (!input.approved) {\n queue.pendingActions[idx] = { ...action, status: \"rejected\" };\n writeAgentQueue(dataDir, input.slug, queue);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: true, actionId: input.actionId, status: \"rejected\" },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const outcome = await executeAction(action, dataDir);\n queue.pendingActions[idx] = {\n ...action,\n status: outcome === \"executed\" ? \"executed\" : \"skipped\",\n };\n writeAgentQueue(dataDir, input.slug, queue);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n actionId: input.actionId,\n status: queue.pendingActions[idx]!.status,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerApproveAgentAction(server: McpServer): void {\n server.registerTool(\n \"approve_agent_action\",\n {\n title: \"Approve Agent Action\",\n description: `Approve or reject a pending action from the deal agent queue.\n\nFind actionId in the actionsQueued array returned by run_deal_agent.\n\nArgs:\n slug: Customer slug\n actionId: Action ID from the agent queue\n approved: true to execute, false to reject\n\nReturns: { success, actionId, status }`,\n inputSchema: z.object({\n slug: z.string(),\n actionId: z.string(),\n approved: z.boolean(),\n }),\n },\n async ({ slug, actionId, approved }) => handleApproveAgentAction({ slug, actionId, approved })\n );\n}\n","import { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport { readHealth, computeCustomerHealth } from \"./relationship-health.js\";\nimport { readGraph, getStakeholders } from \"./graph.js\";\nimport { getPipelineStages } from \"./pipeline-stages.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ExternalSignal {\n slug: string;\n type: \"funding_round\" | \"leadership_change\" | \"news_positive\" | \"news_negative\";\n impact: \"positive\" | \"negative\" | \"neutral\";\n magnitude: number; // 0.0–1.0\n summary: string;\n}\n\nexport interface DealSnapshot {\n slug: string;\n name: string;\n stage: string;\n value: number;\n probability: number;\n closeDate?: string;\n healthScore: number;\n daysSinceContact: number;\n championPresent: boolean;\n}\n\nexport interface SimulationInput {\n deals: DealSnapshot[];\n externalSignals: ExternalSignal[];\n iterations: number;\n horizon: \"quarter\" | \"year\";\n today: string;\n}\n\nexport interface MonthForecast {\n p50: number;\n range: [number, number];\n}\n\nexport interface SimulationResult {\n p10: number;\n p50: number;\n p90: number;\n expected: number;\n stdDev: number;\n atRiskRevenue: number;\n byCloseMonth: Record<string, MonthForecast>;\n topRisks: string[];\n sensitivityMap: Record<string, number>;\n}\n\n// ─── Pure helpers ─────────────────────────────────────────────────────────────\n\nexport function percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0;\n const idx = Math.max(0, Math.ceil((p / 100) * sorted.length) - 1);\n return sorted[idx]!;\n}\n\nexport function mean(values: number[]): number {\n if (values.length === 0) return 0;\n return values.reduce((s, v) => s + v, 0) / values.length;\n}\n\nexport function stdDevFn(values: number[], m: number): number {\n if (values.length < 2) return 0;\n const variance = values.reduce((s, v) => s + (v - m) ** 2, 0) / values.length;\n return Math.sqrt(variance);\n}\n\nexport function adjustProbability(deal: DealSnapshot, signals: ExternalSignal[] = []): number {\n let prob = deal.probability / 100;\n\n // Health adjustment: health 60 = neutral, range -0.12 to +0.08\n const healthAdj = ((deal.healthScore - 60) / 100) * 0.2;\n prob += healthAdj;\n\n // Champion bonus\n if (deal.championPresent) prob += 0.05;\n\n // External signals (D18-ready)\n for (const signal of signals) {\n if (signal.slug === deal.slug) {\n if (signal.impact === \"positive\") prob += 0.05 * signal.magnitude;\n if (signal.impact === \"negative\") prob -= 0.1 * signal.magnitude;\n }\n }\n\n return Math.max(0.02, Math.min(0.98, prob));\n}\n\nexport function closeVarianceFn(\n deal: DealSnapshot,\n randFn: () => number,\n todayMs: number = Date.now()\n): number {\n const daysToClose = deal.closeDate\n ? Math.max(0, Math.floor((new Date(deal.closeDate).getTime() - todayMs) / 86_400_000))\n : 90;\n const variance = daysToClose < 30 ? 0.05 : 0.15;\n return 1 + (randFn() - 0.5) * 2 * variance;\n}\n\nexport function buildSensitivityMap(\n deals: DealSnapshot[],\n signals: ExternalSignal[]\n): Record<string, number> {\n const map: Record<string, number> = {};\n for (const deal of deals) {\n map[deal.name] = Math.round(deal.value * adjustProbability(deal, signals));\n }\n return map;\n}\n\nexport function buildTopRisks(\n deals: DealSnapshot[],\n signals: ExternalSignal[],\n sensitivityMap: Record<string, number>\n): string[] {\n const atRisk = deals.filter((d) => d.healthScore < 60 || d.daysSinceContact > 14);\n return atRisk\n .sort((a, b) => (sensitivityMap[b.name] ?? 0) - (sensitivityMap[a.name] ?? 0))\n .slice(0, 5)\n .map((d) => {\n const reasons: string[] = [];\n if (d.healthScore < 60) reasons.push(`health ${d.healthScore}`);\n if (d.daysSinceContact > 14) reasons.push(`${d.daysSinceContact}d no contact`);\n if (!d.championPresent) reasons.push(\"no champion\");\n return `${d.slug}/${d.name}: ${reasons.join(\", \")} — €${d.value} at risk`;\n });\n}\n\n// ─── Monte Carlo Core ─────────────────────────────────────────────────────────\n\nconst MAX_ITERATIONS = 50_000;\n\nexport function runSimulation(\n input: SimulationInput,\n randFn: () => number = Math.random\n): SimulationResult {\n const { deals, externalSignals } = input;\n const iterations = Math.min(input.iterations, MAX_ITERATIONS);\n\n if (deals.length === 0) {\n return {\n p10: 0,\n p50: 0,\n p90: 0,\n expected: 0,\n stdDev: 0,\n atRiskRevenue: 0,\n byCloseMonth: {},\n topRisks: [],\n sensitivityMap: {},\n };\n }\n\n const todayMs = new Date(input.today).getTime();\n const adjustedProbs = deals.map((d) => adjustProbability(d, externalSignals));\n const outcomes: number[] = [];\n const byMonthOutcomes: Record<string, number[]> = {};\n\n for (let i = 0; i < iterations; i++) {\n let total = 0;\n const monthTotals: Record<string, number> = {};\n\n for (let j = 0; j < deals.length; j++) {\n const deal = deals[j]!;\n const prob = adjustedProbs[j]!;\n if (randFn() < prob) {\n const closedValue = Math.round(deal.value * closeVarianceFn(deal, randFn, todayMs));\n total += closedValue;\n if (deal.closeDate) {\n const month = deal.closeDate.slice(0, 7);\n monthTotals[month] = (monthTotals[month] ?? 0) + closedValue;\n }\n }\n }\n\n outcomes.push(total);\n // Winning-only: only record months where at least one deal closed in this iteration\n for (const [month, val] of Object.entries(monthTotals)) {\n if (val > 0) {\n byMonthOutcomes[month] ??= [];\n byMonthOutcomes[month]!.push(val);\n }\n }\n }\n\n outcomes.sort((a, b) => a - b);\n const exp = Math.round(mean(outcomes));\n const sd = Math.round(stdDevFn(outcomes, exp));\n\n const byCloseMonth: Record<string, MonthForecast> = {};\n for (const [month, vals] of Object.entries(byMonthOutcomes)) {\n const sorted = [...vals].sort((a, b) => a - b);\n byCloseMonth[month] = {\n p50: Math.round(percentile(sorted, 50)),\n range: [Math.round(percentile(sorted, 10)), Math.round(percentile(sorted, 90))],\n };\n }\n\n const sensitivityMap = buildSensitivityMap(deals, externalSignals);\n const topRisks = buildTopRisks(deals, externalSignals, sensitivityMap);\n const atRiskRevenue = deals.filter((d) => d.healthScore < 60).reduce((s, d) => s + d.value, 0);\n\n return {\n p10: Math.round(percentile(outcomes, 10)),\n p50: Math.round(percentile(outcomes, 50)),\n p90: Math.round(percentile(outcomes, 90)),\n expected: exp,\n stdDev: sd,\n atRiskRevenue,\n byCloseMonth,\n topRisks,\n sensitivityMap,\n };\n}\n\n// ─── Confidence message ───────────────────────────────────────────────────────\n\nexport function buildConfidenceMessage(result: SimulationResult, dealCount: number): string {\n const range = result.p90 - result.p10;\n const atRiskPct =\n result.expected > 0 ? Math.round((result.atRiskRevenue / result.expected) * 100) : 0;\n return `P50 forecast: €${(result.p50 / 1000).toFixed(1)}k with ±€${(range / 2 / 1000).toFixed(1)}k uncertainty (P10–P90 range). ${atRiskPct}% of pipeline is at risk. ${dealCount} deals simulated.`;\n}\n\n// ─── Quarter helper ───────────────────────────────────────────────────────────\n\nfunction getQuarterEnd(date: Date): Date {\n const month = date.getMonth();\n const quarterEndMonth = Math.floor(month / 3) * 3 + 2;\n return new Date(date.getFullYear(), quarterEndMonth + 1, 0);\n}\n\n// ─── Data aggregation ─────────────────────────────────────────────────────────\n\nexport async function buildSimulationInput(\n dataDir: string,\n horizon: \"quarter\" | \"year\",\n today: string,\n externalSignals: ExternalSignal[] = []\n): Promise<SimulationInput> {\n const slugs = listCustomerSlugs(dataDir);\n if (slugs.length === 0) {\n return { deals: [], externalSignals, iterations: 10_000, horizon, today };\n }\n\n const stages = getPipelineStages(dataDir);\n const stageProb: Record<string, number> = {};\n for (const s of stages) {\n stageProb[s.id] = s.probability ?? 50;\n }\n\n const deals: DealSnapshot[] = [];\n const todayDate = new Date(today);\n const horizonEnd =\n horizon === \"quarter\" ? getQuarterEnd(todayDate) : new Date(todayDate.getFullYear(), 11, 31);\n\n for (const slug of slugs) {\n const pipelineDeals = await readPipeline(dataDir, slug).catch(() => []);\n if (pipelineDeals.length === 0) continue;\n\n const health = readHealth(dataDir, slug) ?? computeCustomerHealth(dataDir, slug, today);\n const healthScore = health.overallHealth;\n\n const graph = readGraph(dataDir, slug);\n const stakeholders = getStakeholders(graph);\n const championPresent = stakeholders.champions.length > 0;\n\n const lastContact = health.contacts\n .map((c) => c.lastContact)\n .filter((lc): lc is string => !!lc)\n .sort()\n .pop();\n const daysSinceContact = lastContact\n ? Math.floor((todayDate.getTime() - new Date(lastContact).getTime()) / 86_400_000)\n : 999;\n\n for (const deal of pipelineDeals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n const closeDate = new Date(deal.close_date);\n if (closeDate > horizonEnd) continue;\n }\n\n const probability = deal.probability ?? stageProb[deal.stage] ?? 50;\n const snapshot: DealSnapshot = {\n slug,\n name: deal.name,\n stage: deal.stage,\n value: deal.value ?? 0,\n probability,\n healthScore,\n daysSinceContact,\n championPresent,\n };\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n snapshot.closeDate = deal.close_date;\n }\n deals.push(snapshot);\n }\n }\n\n return { deals, externalSignals, iterations: 10_000, horizon, today };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n buildSimulationInput,\n runSimulation,\n buildConfidenceMessage,\n} from \"../../core/revenue-simulation.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSimulateRevenue(\n input: { horizon?: \"quarter\" | \"year\"; iterations?: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const horizon = input.horizon ?? \"quarter\";\n\n const simInput = await buildSimulationInput(dataDir, horizon, today);\n if (input.iterations !== undefined) simInput.iterations = input.iterations;\n\n const result = runSimulation(simInput);\n const confidence = buildConfidenceMessage(result, simInput.deals.length);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n forecast: result,\n confidence,\n dealCount: simInput.deals.length,\n horizon,\n simulatedAt: new Date().toISOString(),\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerSimulateRevenue(server: McpServer): void {\n server.registerTool(\n \"simulate_revenue\",\n {\n title: \"Simulate Revenue\",\n description: `Monte Carlo pipeline revenue simulation with P10/P50/P90 confidence intervals.\n\nAdjusts deal win probabilities using relationship health scores (D12) and\nchampion presence (D11). Returns the range of possible quarterly/annual outcomes.\n\nUse this instead of (or alongside) get_pipeline_forecast when you need:\n- Uncertainty quantification (not just expected value)\n- At-risk revenue identification\n- Deal sensitivity analysis (\"which deal matters most\")\n- Month-by-month close distribution\n\nArgs:\n horizon: \"quarter\" (default) | \"year\"\n iterations: simulation iterations (default: 10000)\n\nReturns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMonth, topRisks, sensitivityMap }, confidence, dealCount, horizon }`,\n inputSchema: z.object({\n horizon: z\n .enum([\"quarter\", \"year\"])\n .optional()\n .describe('Forecast horizon (default: \"quarter\")'),\n iterations: z.number().optional().describe(\"Monte Carlo iterations (default: 10000)\"),\n }),\n },\n async ({ horizon, iterations }) =>\n handleSimulateRevenue({\n ...(horizon !== undefined ? { horizon } : {}),\n ...(iterations !== undefined ? { iterations } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listPlaybooks, matchPlaybooks } from \"../../core/playbooks.js\";\nimport type { DealSnapshot } from \"../../core/revenue-simulation.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetPlaybook(\n input: {\n slug: string;\n stage?: string;\n value?: number;\n healthScore?: number;\n daysSinceContact?: number;\n championPresent?: boolean;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const playbooks = listPlaybooks(dataDir, input.slug);\n\n const hasDealContext =\n input.stage !== undefined || input.value !== undefined || input.healthScore !== undefined;\n\n if (!hasDealContext) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n matches: playbooks.map((pb) => ({\n name: pb.name,\n trigger: pb.frontmatter.trigger,\n successRate: pb.frontmatter.successRate,\n usedCount: pb.frontmatter.usedCount,\n lastUpdated: pb.frontmatter.lastUpdated,\n content: pb.content,\n })),\n totalPlaybooks: playbooks.length,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const mockDeal: DealSnapshot = {\n slug: input.slug,\n name: \"\",\n stage: input.stage ?? \"lead\",\n value: input.value ?? 0,\n probability: 50,\n healthScore: input.healthScore ?? 60,\n daysSinceContact: input.daysSinceContact ?? 0,\n championPresent: input.championPresent ?? false,\n };\n\n const matches = matchPlaybooks(playbooks, mockDeal, input.daysSinceContact ?? 0);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n matches: matches.map((m) => ({\n name: m.playbook.name,\n score: m.score,\n matchedConditions: m.matchedConditions,\n trigger: m.playbook.frontmatter.trigger,\n successRate: m.playbook.frontmatter.successRate,\n usedCount: m.playbook.frontmatter.usedCount,\n content: m.playbook.content,\n })),\n totalPlaybooks: playbooks.length,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetPlaybook(server: McpServer): void {\n server.registerTool(\n \"get_playbook\",\n {\n title: \"Get Playbook\",\n description: `Retrieve playbooks for a customer. With deal context, returns only matching playbooks sorted by success rate. Without deal context, returns all playbooks.\n\nUse after run_deal_agent or before a sales call to get proven guidance for the current situation.\n\nArgs:\n slug: Customer ID (required)\n stage: Deal stage (optional — enables trigger matching)\n value: Deal value in euros (optional)\n healthScore: Relationship health score 0–100 (optional)\n daysSinceContact: Days since last contact / days_stalled proxy (optional)\n championPresent: Whether a champion is identified (optional)\n\nReturns: { matches: [{ name, score, trigger, successRate, usedCount, content }], totalPlaybooks, slug }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n stage: z.string().optional().describe(\"Deal stage\"),\n value: z.number().optional().describe(\"Deal value in euros\"),\n healthScore: z.number().optional().describe(\"Health score 0–100\"),\n daysSinceContact: z.number().optional().describe(\"Days since last contact\"),\n championPresent: z.boolean().optional().describe(\"Champion identified\"),\n }),\n },\n async ({ slug, stage, value, healthScore, daysSinceContact, championPresent }) =>\n handleGetPlaybook(\n {\n slug,\n ...(stage !== undefined ? { stage } : {}),\n ...(value !== undefined ? { value } : {}),\n ...(healthScore !== undefined ? { healthScore } : {}),\n ...(daysSinceContact !== undefined ? { daysSinceContact } : {}),\n ...(championPresent !== undefined ? { championPresent } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { writePlaybook, playbooksDir, toKebabCase } from \"../../core/playbooks.js\";\nimport path from \"path\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCreatePlaybook(\n input: {\n slug: string;\n name: string;\n trigger: string;\n content: string;\n successRate?: number;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const name = toKebabCase(input.name);\n const today = new Date().toISOString().slice(0, 10);\n const filePath = path.join(playbooksDir(dataDir, input.slug), `${name}.md`);\n\n const playbook = {\n slug: input.slug,\n name,\n frontmatter: {\n trigger: input.trigger,\n successRate: input.successRate ?? 0.5,\n usedCount: 0,\n lastUpdated: today,\n },\n content: input.content,\n path: filePath,\n };\n\n await writePlaybook(dataDir, input.slug, playbook);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n playbook: {\n name,\n trigger: input.trigger,\n successRate: playbook.frontmatter.successRate,\n usedCount: 0,\n lastUpdated: today,\n path: filePath,\n },\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerCreatePlaybook(server: McpServer): void {\n server.registerTool(\n \"create_playbook\",\n {\n title: \"Create Playbook\",\n description: `Create or update a playbook for a customer. Playbooks encode proven tactics for specific deal situations.\n\nTrigger DSL uses AND-only conditions:\n deal_stage_<stage> | value > N | value < N | days_stalled > N | health < N | health > N | no_champion | has_champion\n\nExample: \"deal_stage_negotiation AND value > 50000 AND days_stalled > 7\"\n\nArgs:\n slug: Customer ID\n name: Playbook name (auto-converted to kebab-case)\n trigger: Trigger DSL string (conditions separated by AND)\n content: Playbook markdown body (## Situation, ## Steps, ## Warnings, ## Templates)\n successRate: Historical win rate 0.0–1.0 (default: 0.5)\n\nReturns: { success: true, playbook: { name, trigger, successRate, path } }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n name: z.string().describe(\"Playbook name\"),\n trigger: z.string().describe(\"Trigger DSL string\"),\n content: z.string().describe(\"Playbook markdown body\"),\n successRate: z.number().min(0).max(1).optional().describe(\"Historical win rate 0.0–1.0\"),\n }),\n },\n async ({ slug, name, trigger, content, successRate }) =>\n handleCreatePlaybook(\n {\n slug,\n name,\n trigger,\n content,\n ...(successRate !== undefined ? { successRate } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listPlaybooks } from \"../../core/playbooks.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListPlaybooks(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const playbooks = listPlaybooks(dataDir, input.slug);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n playbooks: playbooks.map((pb) => ({\n name: pb.name,\n trigger: pb.frontmatter.trigger,\n successRate: pb.frontmatter.successRate,\n usedCount: pb.frontmatter.usedCount,\n lastUpdated: pb.frontmatter.lastUpdated,\n })),\n count: playbooks.length,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerListPlaybooks(server: McpServer): void {\n server.registerTool(\n \"list_playbooks\",\n {\n title: \"List Playbooks\",\n description: `List all playbooks for a customer (metadata only, no body content).\n\nUse to discover available playbooks before calling get_playbook with deal context.\n\nArgs:\n slug: Customer ID\n\nReturns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n }),\n },\n async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { distillPlaybook } from \"../../core/playbooks.js\";\nimport { callLlm } from \"../../core/llm.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleDistillPlaybook(\n input: { slug: string; dealName: string; outcome: \"won\" | \"lost\" },\n dataDir: string = DATA_DIR,\n llmFn: (prompt: string) => Promise<string> = callLlm\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);\n\n if (!result.ok) {\n const error =\n result.errorKind === \"no_interactions\"\n ? `No interactions.md found for ${input.slug}`\n : \"LLM response could not be parsed as playbook\";\n return {\n content: [{ type: \"text\", text: JSON.stringify({ success: false, error }, null, 2) }],\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n playbook: {\n name: result.playbook.name,\n trigger: result.playbook.frontmatter.trigger,\n successRate: result.playbook.frontmatter.successRate,\n usedCount: result.playbook.frontmatter.usedCount,\n path: result.playbook.path,\n },\n reasoning: result.reasoning,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerDistillPlaybook(server: McpServer): void {\n server.registerTool(\n \"distill_playbook\",\n {\n title: \"Distill Playbook\",\n description: `Use LLM to extract a reusable playbook from a won or lost deal's interaction history. Analyzes the customer's interactions.md and identifies the winning/losing pattern as a structured playbook.\n\nRun after every won or lost deal to build your procedural memory library.\n\nArgs:\n slug: Customer ID\n dealName: Name of the deal to analyze\n outcome: \"won\" or \"lost\"\n\nReturns: { success: true, playbook: { name, trigger, successRate, path }, reasoning }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n dealName: z.string().describe(\"Deal name to analyze\"),\n outcome: z.enum([\"won\", \"lost\"]).describe(\"Deal outcome\"),\n }),\n },\n async ({ slug, dealName, outcome }) =>\n handleDistillPlaybook({ slug, dealName, outcome }, DATA_DIR)\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { runSimulation } from \"./revenue-simulation.js\";\nimport { callLlm } from \"./llm.js\";\nimport { getActor } from \"../fs/audit-log.js\";\nimport { withJsonFile } from \"./file-lock.js\";\nimport { guardIsoDate } from \"./input-guard.js\";\nimport type { DealSnapshot, SimulationInput } from \"./revenue-simulation.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type GoalMetric = \"revenue\" | \"deals_closed\" | \"meetings_booked\" | \"pipeline_created\";\nexport type GoalType = \"revenue\" | \"pipeline\" | \"relationship\" | \"churn_prevention\";\nexport type GoalStatus = \"active\" | \"completed\" | \"cancelled\" | \"blocked\";\n\nexport interface GoalSubGoal {\n priority: number;\n action: string;\n slug: string;\n dealName?: string;\n why: string;\n nextStep: string;\n targetDelta: number;\n playbookName?: string;\n}\n\nexport interface GoalDecomposition {\n analysis: string;\n currentPipeline: number;\n gap: number;\n subGoals: GoalSubGoal[];\n probabilisticOutcome: string;\n decomposedAt: string;\n}\n\nexport interface Goal {\n id: string;\n description: string;\n type: GoalType;\n target: number;\n metric: GoalMetric;\n deadline: string;\n decomposition: GoalDecomposition;\n progress: number;\n status: GoalStatus;\n createdAt: string;\n updatedAt: string;\n actor: string;\n}\n\nexport type BuildInputFn = (\n dataDir: string,\n horizon: \"quarter\" | \"year\",\n today: string\n) => Promise<SimulationInput>;\n\n// ─── Persistence ──────────────────────────────────────────────────────────────\n\nexport function goalsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"goals.json\");\n}\n\nexport function readGoals(dataDir: string): Goal[] {\n const p = goalsPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n if (Array.isArray(raw)) return raw as Goal[];\n return (raw as { goals?: Goal[] }).goals ?? [];\n } catch {\n return [];\n }\n}\n\nexport function writeGoals(dataDir: string, goals: Goal[]): void {\n const p = goalsPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(\n p,\n JSON.stringify({ goals, updatedAt: new Date().toISOString() }, null, 2),\n \"utf-8\"\n );\n}\n\nexport function makeGoalId(): string {\n return `goal_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;\n}\n\n// ─── Parsing ──────────────────────────────────────────────────────────────────\n\nexport function parseTargetFromDescription(desc: string): number {\n // Try millions first: \"1.5M\", \"1.5 million\", \"$1.5M\"\n const millionMatch = desc.match(/[\\$€£]?\\s*(\\d+(?:\\.\\d+)?)\\s*(?:M\\b|million)/i);\n if (millionMatch) return Math.round(parseFloat(millionMatch[1]!) * 1_000_000);\n\n // Then thousands: \"500k\", \"€500k\"\n const kMatch = desc.match(/[\\$€£]?\\s*(\\d+(?:\\.\\d+)?)\\s*k\\b/i);\n if (kMatch) return Math.round(parseFloat(kMatch[1]!) * 1_000);\n\n // Then raw numbers with optional currency: \"€75000\"\n const rawMatch = desc.match(/[\\$€£]\\s*(\\d{4,}(?:[,.\\d]*\\d)?)/);\n if (rawMatch) return parseInt(rawMatch[1]!.replace(/[,. ]/g, \"\"), 10);\n\n return 0;\n}\n\nexport function inferGoalType(desc: string): GoalType {\n const lower = desc.toLowerCase();\n if (/churn|retain|renewal|renew/.test(lower)) return \"churn_prevention\";\n if (/meeting|call|book|relationship|contact/.test(lower)) return \"relationship\";\n if (/pipeline|prospect|lead|qualify/.test(lower)) return \"pipeline\";\n return \"revenue\";\n}\n\nexport function inferMetric(type: GoalType): GoalMetric {\n switch (type) {\n case \"pipeline\":\n return \"pipeline_created\";\n case \"relationship\":\n return \"meetings_booked\";\n case \"revenue\":\n return \"revenue\";\n case \"churn_prevention\":\n return \"revenue\";\n }\n}\n\n// ─── Rule-based decomposition ─────────────────────────────────────────────────\n\nexport function rankDealsByLeverage(deals: DealSnapshot[]): DealSnapshot[] {\n return deals\n .filter((d) => d.stage !== \"won\" && d.stage !== \"lost\")\n .sort((a, b) => {\n const leverageA = a.value * (a.probability / 100) * (a.healthScore / 100);\n const leverageB = b.value * (b.probability / 100) * (b.healthScore / 100);\n return leverageB - leverageA;\n });\n}\n\nfunction generateNextStep(deal: DealSnapshot): string {\n if (deal.healthScore < 40 && !deal.championPresent)\n return \"Re-engage urgently and identify a champion\";\n if (deal.healthScore < 60) return \"Schedule an urgent check-in call\";\n if (deal.daysSinceContact > 14) return \"Reach out — contact is overdue\";\n if (!deal.championPresent) return \"Identify a champion or economic buyer\";\n return \"Push to next pipeline stage\";\n}\n\nexport function decomposeGoalRuleBased(\n deals: DealSnapshot[],\n target: number,\n currentP50: number,\n today: string,\n playbookLookup?: (slug: string, deal: DealSnapshot) => string | undefined\n): GoalDecomposition {\n const gap = Math.max(0, target - currentP50);\n const decomposedAt = new Date(today + \"T00:00:00Z\").toISOString();\n\n if (gap === 0) {\n return {\n analysis: `Current pipeline (P50: €${currentP50.toLocaleString()}) already meets or exceeds the target of €${target.toLocaleString()}.`,\n currentPipeline: currentP50,\n gap: 0,\n subGoals: [],\n probabilisticOutcome: `Pipeline P50 (€${currentP50.toLocaleString()}) ≥ target (€${target.toLocaleString()}).`,\n decomposedAt,\n };\n }\n\n const ranked = rankDealsByLeverage(deals);\n\n if (ranked.length === 0) {\n return {\n analysis: `No active deals found. Gap to close: €${gap.toLocaleString()}. Focus on building pipeline.`,\n currentPipeline: currentP50,\n gap,\n subGoals: [\n {\n priority: 1,\n action: \"Build pipeline from scratch\",\n slug: \"_all\",\n why: `No active deals. Need €${gap.toLocaleString()} to reach target.`,\n nextStep:\n \"Use list_customers() to find prospects and log_interaction to initiate outreach\",\n targetDelta: target,\n },\n ],\n probabilisticOutcome: `Insufficient pipeline. Need €${gap.toLocaleString()} in new deals.`,\n decomposedAt,\n };\n }\n\n const subGoals: GoalSubGoal[] = [];\n let cumulative = 0;\n\n for (const deal of ranked.slice(0, 5)) {\n if (subGoals.length >= 5) break;\n const playbookName = playbookLookup?.(deal.slug, deal);\n const subGoal: GoalSubGoal = {\n priority: subGoals.length + 1,\n action: `Accelerate ${deal.slug}/${deal.name}`,\n slug: deal.slug,\n ...(deal.name ? { dealName: deal.name } : {}),\n why: `€${deal.value.toLocaleString()} deal in ${deal.stage} — health ${deal.healthScore}/100`,\n nextStep: generateNextStep(deal),\n targetDelta: deal.value,\n ...(playbookName ? { playbookName } : {}),\n };\n subGoals.push(subGoal);\n cumulative += deal.value;\n if (cumulative >= gap) break;\n }\n\n const projectedTotal = currentP50 + cumulative;\n return {\n analysis: `Current pipeline P50: €${currentP50.toLocaleString()}. Gap to target: €${gap.toLocaleString()}. Top ${subGoals.length} deal(s) identified.`,\n currentPipeline: currentP50,\n gap,\n subGoals,\n probabilisticOutcome: `If all recommended deals close: ~€${projectedTotal.toLocaleString()} (target: €${target.toLocaleString()}).`,\n decomposedAt,\n };\n}\n\n// ─── LLM path ─────────────────────────────────────────────────────────────────\n\nexport function buildDecompositionPrompt(\n description: string,\n target: number,\n deadline: string,\n currentP50: number,\n deals: DealSnapshot[],\n today: string\n): string {\n const gap = Math.max(0, target - currentP50);\n const dealLines = deals\n .filter((d) => d.stage !== \"won\" && d.stage !== \"lost\")\n .slice(0, 8)\n .map(\n (d, i) =>\n `${i + 1}. ${d.slug}/${d.name} — €${d.value.toLocaleString()}, stage: ${d.stage}, health: ${d.healthScore}/100, probability: ${d.probability}%${d.championPresent ? \", champion ✓\" : \"\"}`\n )\n .join(\"\\n\");\n\n return `You are a sales strategy AI helping decompose a revenue goal into actionable sub-goals.\n\nGoal: ${description}\nTarget: €${target.toLocaleString()}\nDeadline: ${deadline}\nCurrent date: ${today}\nCurrent weighted pipeline (P50): €${currentP50.toLocaleString()}\nGap to close: €${gap.toLocaleString()}\n\nActive deals (sorted by weighted value):\n${dealLines || \"(no active deals)\"}\n\nReturn JSON only (no markdown wrapper):\n{\n \"analysis\": \"<1-2 sentence summary of the situation>\",\n \"subGoals\": [\n {\n \"priority\": 1,\n \"action\": \"<what to do>\",\n \"slug\": \"<customer-slug>\",\n \"dealName\": \"<deal name>\",\n \"why\": \"<why this deal matters for the goal>\",\n \"nextStep\": \"<concrete next action with deadline>\",\n \"targetDelta\": <expected revenue contribution in euros>\n }\n ],\n \"probabilisticOutcome\": \"<P50 forecast summary after actions>\"\n}`;\n}\n\nexport function parseLlmDecomposition(\n response: string,\n fallback: GoalDecomposition\n): GoalDecomposition {\n try {\n const match = response.match(/\\{[\\s\\S]*\\}/);\n if (!match) return fallback;\n const parsed = JSON.parse(match[0]) as Partial<{\n analysis: string;\n subGoals: unknown[];\n probabilisticOutcome: string;\n }>;\n if (!parsed.analysis || !Array.isArray(parsed.subGoals)) return fallback;\n return {\n analysis: parsed.analysis,\n currentPipeline: fallback.currentPipeline,\n gap: fallback.gap,\n subGoals: (parsed.subGoals as Partial<GoalSubGoal>[]).map((s, i) => ({\n priority: s.priority ?? i + 1,\n action: s.action ?? \"\",\n slug: s.slug ?? \"_all\",\n ...(s.dealName ? { dealName: s.dealName } : {}),\n why: s.why ?? \"\",\n nextStep: s.nextStep ?? \"\",\n targetDelta: s.targetDelta ?? 0,\n ...(s.playbookName ? { playbookName: s.playbookName } : {}),\n })),\n probabilisticOutcome: parsed.probabilisticOutcome ?? fallback.probabilisticOutcome,\n decomposedAt: fallback.decomposedAt,\n };\n } catch {\n return fallback;\n }\n}\n\n// ─── pursueGoal ───────────────────────────────────────────────────────────────\n\nexport async function pursueGoal(\n dataDir: string,\n input: { description: string; deadline: string; context?: string },\n options: {\n llmFn?: (prompt: string) => Promise<string>;\n buildInputFn?: BuildInputFn;\n today?: string;\n actor?: string;\n } = {}\n): Promise<Goal> {\n guardIsoDate(input.deadline, \"deadline\");\n const today = options.today ?? new Date().toISOString().slice(0, 10);\n const actor = options.actor ?? getActor();\n\n const buildFn =\n options.buildInputFn ??\n ((async (dir, horizon, t) => {\n const { buildSimulationInput } = await import(\"./revenue-simulation.js\");\n return buildSimulationInput(dir, horizon, t);\n }) as BuildInputFn);\n\n const simInput = await buildFn(dataDir, \"quarter\", today);\n const simResult = runSimulation(simInput);\n const currentP50 = simResult.p50;\n const deals = simInput.deals;\n\n const target = parseTargetFromDescription(input.description);\n const type = inferGoalType(input.description);\n const metric = inferMetric(type);\n\n const ruleBasedDecomp = decomposeGoalRuleBased(deals, target, currentP50, today);\n\n let decomposition = ruleBasedDecomp;\n const llmFn = options.llmFn ?? callLlm;\n if (options.llmFn !== undefined) {\n const prompt = buildDecompositionPrompt(\n input.description,\n target,\n input.deadline,\n currentP50,\n deals,\n today\n );\n const response = await llmFn(prompt);\n decomposition = parseLlmDecomposition(response, ruleBasedDecomp);\n }\n\n const now = new Date().toISOString();\n const goal: Goal = {\n id: makeGoalId(),\n description: input.description,\n type,\n target,\n metric,\n deadline: input.deadline,\n decomposition,\n progress: 0,\n status: \"active\",\n createdAt: now,\n updatedAt: now,\n actor,\n };\n\n await withJsonFile<{ goals: Goal[]; updatedAt: string }>(goalsPath(dataDir), (current) => {\n const existing: Goal[] = Array.isArray(current?.goals) ? current.goals : [];\n return { goals: [...existing, goal], updatedAt: new Date().toISOString() };\n });\n return goal;\n}\n\n// ─── Goal management ──────────────────────────────────────────────────────────\n\nexport function getActiveGoals(dataDir: string): Goal[] {\n return readGoals(dataDir).filter((g) => g.status === \"active\");\n}\n\nexport async function updateGoalProgress(\n dataDir: string,\n goalId: string,\n progress: number\n): Promise<Goal | null> {\n let updated: Goal | null = null;\n await withJsonFile<{ goals: Goal[]; updatedAt: string }>(goalsPath(dataDir), (current) => {\n const goals: Goal[] = Array.isArray(current?.goals) ? [...current.goals] : [];\n const idx = goals.findIndex((g) => g.id === goalId);\n if (idx >= 0) {\n updated = { ...goals[idx]!, progress, updatedAt: new Date().toISOString() };\n goals[idx] = updated;\n }\n return { goals, updatedAt: new Date().toISOString() };\n });\n return updated;\n}\n\nexport async function cancelGoal(dataDir: string, goalId: string): Promise<Goal | null> {\n let cancelled: Goal | null = null;\n await withJsonFile<{ goals: Goal[]; updatedAt: string }>(goalsPath(dataDir), (current) => {\n const goals: Goal[] = Array.isArray(current?.goals) ? [...current.goals] : [];\n const idx = goals.findIndex((g) => g.id === goalId);\n if (idx >= 0) {\n cancelled = {\n ...goals[idx]!,\n status: \"cancelled\" as const,\n updatedAt: new Date().toISOString(),\n };\n goals[idx] = cancelled;\n }\n return { goals, updatedAt: new Date().toISOString() };\n });\n return cancelled;\n}\n\n// ─── Pipeline-driven progress sync ────────────────────────────────────────────\n\nexport interface SyncResult {\n updated: string[];\n skipped: number;\n}\n\nexport async function syncGoalProgressFromPipeline(\n dataDir: string,\n _today?: string\n): Promise<SyncResult> {\n const activeGoals = getActiveGoals(dataDir);\n const revenueGoals = activeGoals.filter((g) => g.metric === \"revenue\" && g.target > 0);\n\n if (revenueGoals.length === 0) return { updated: [], skipped: activeGoals.length };\n\n let totalWon = 0;\n for (const slug of listCustomerSlugs(dataDir)) {\n const deals = await readPipeline(dataDir, slug).catch(() => []);\n for (const deal of deals) {\n if (deal.stage === \"won\") totalWon += deal.value ?? 0;\n }\n }\n\n const updated: string[] = [];\n for (const goal of revenueGoals) {\n const progress = Math.min(100, Math.round((totalWon / goal.target) * 100));\n const result = await updateGoalProgress(dataDir, goal.id, progress);\n if (result) updated.push(goal.id);\n }\n\n return { updated, skipped: activeGoals.length - revenueGoals.length };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { pursueGoal, type BuildInputFn } from \"../../core/goal-engine.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport { getActor } from \"../../fs/audit-log.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handlePursueGoal(\n input: { goal: string; deadline: string; context?: string },\n dataDir: string = DATA_DIR,\n options: { buildInputFn?: BuildInputFn; llmFn?: (p: string) => Promise<string> } = {}\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n enforceRbac(dataDir, \"pursue_goal\");\n\n const goal = await pursueGoal(\n dataDir,\n {\n description: input.goal,\n deadline: input.deadline,\n ...(input.context ? { context: input.context } : {}),\n },\n {\n actor: getActor(),\n ...(options.buildInputFn ? { buildInputFn: options.buildInputFn } : {}),\n ...(options.llmFn ? { llmFn: options.llmFn } : {}),\n }\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n goalId: goal.id,\n description: goal.description,\n target: goal.target,\n deadline: goal.deadline,\n type: goal.type,\n decomposition: {\n analysis: goal.decomposition.analysis,\n currentPipeline: goal.decomposition.currentPipeline,\n gap: goal.decomposition.gap,\n subGoals: goal.decomposition.subGoals,\n probabilisticOutcome: goal.decomposition.probabilisticOutcome,\n },\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerPursueGoal(server: McpServer): void {\n server.registerTool(\n \"pursue_goal\",\n {\n title: \"Pursue Goal\",\n description: `Set a revenue or pipeline goal and get an AI-decomposed action plan.\n\nAnalyzes current pipeline (P50 forecast) and decomposes the gap into prioritized sub-goals per deal. Persists the goal in .agentic/goals.json for tracking.\n\nRBAC: manager+\n\nArgs:\n goal: Natural language goal description (e.g. \"Close €500k ARR this quarter\")\n deadline: Target deadline (YYYY-MM-DD)\n context: Optional constraints (e.g. \"Focus on existing pipeline only\")\n\nReturns: { goalId, description, target, deadline, decomposition: { analysis, currentPipeline, gap, subGoals, probabilisticOutcome } }`,\n inputSchema: z.object({\n goal: z.string().describe(\"Natural language goal (e.g. 'Close €500k ARR this quarter')\"),\n deadline: z.string().describe(\"Target deadline YYYY-MM-DD\"),\n context: z.string().optional().describe(\"Optional constraints or focus areas\"),\n }),\n },\n async ({ goal, deadline, context }) =>\n handlePursueGoal({ goal, deadline, ...(context !== undefined ? { context } : {}) }, DATA_DIR)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readGoals, getActiveGoals } from \"../../core/goal-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetGoalStatus(\n input: { goalId?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const allGoals = input.goalId\n ? readGoals(dataDir).filter((g) => g.id === input.goalId)\n : getActiveGoals(dataDir);\n\n if (input.goalId && allGoals.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: false, error: `Goal '${input.goalId}' not found` },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const goals = allGoals.map((g) => {\n const deadlineMs = new Date(g.deadline).getTime();\n const todayMs = new Date(today).getTime();\n const daysRemaining = Math.max(0, Math.ceil((deadlineMs - todayMs) / 86_400_000));\n return {\n id: g.id,\n description: g.description,\n target: g.target,\n progress: g.progress,\n status: g.status,\n deadline: g.deadline,\n daysRemaining,\n subGoals: g.decomposition.subGoals.slice(0, 3),\n createdAt: g.createdAt,\n };\n });\n\n const active = allGoals.filter((g) => g.status === \"active\");\n const completed = allGoals.filter((g) => g.status === \"completed\");\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { goals, activeCount: active.length, completedCount: completed.length },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetGoalStatus(server: McpServer): void {\n server.registerTool(\n \"get_goal_status\",\n {\n title: \"Get Goal Status\",\n description: `Get the status of active goals. Without goalId, returns all active goals. With goalId, returns that specific goal.\n\nReturns progress, days remaining, and top sub-goals for each goal.\n\nArgs:\n goalId: (optional) Specific goal ID — if omitted, returns all active goals\n\nReturns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,\n inputSchema: z.object({\n goalId: z.string().optional().describe(\"Specific goal ID (omit for all active goals)\"),\n }),\n },\n async ({ goalId }) =>\n handleGetGoalStatus({ ...(goalId !== undefined ? { goalId } : {}) }, DATA_DIR)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { register, type PushProvider } from \"../../sync/push-manager.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst VALID_PROVIDERS: PushProvider[] = [\"gmail\", \"microsoft-graph\", \"slack\"];\n\nexport async function handleRegisterPushSubscription(\n input: {\n provider: PushProvider;\n slug: string;\n webhookUrl: string;\n gmailTopicName?: string;\n microsoftClientState?: string;\n microsoftResource?: string;\n slackTeamId?: string;\n slackChannelId?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n if (!VALID_PROVIDERS.includes(input.provider)) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: false, error: `Unknown provider: ${input.provider}` },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n enforceRbac(dataDir, \"register_push_subscription\");\n\n const providerData: Record<string, string> = {};\n if (input.gmailTopicName) providerData[\"gmailTopicName\"] = input.gmailTopicName;\n if (input.microsoftClientState)\n providerData[\"microsoftClientState\"] = input.microsoftClientState;\n if (input.microsoftResource) providerData[\"microsoftResource\"] = input.microsoftResource;\n if (input.slackTeamId) providerData[\"slackTeamId\"] = input.slackTeamId;\n if (input.slackChannelId) providerData[\"slackChannelId\"] = input.slackChannelId;\n\n const sub = await register(dataDir, input.provider, input.slug, {\n webhookUrl: input.webhookUrl,\n providerData,\n });\n\n const warning = input.webhookUrl.includes(\"localhost\")\n ? \"Warning: webhookUrl contains 'localhost' — providers cannot reach local endpoints. Use a tunnel (e.g. ngrok http 3847) for development.\"\n : undefined;\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n subscriptionId: sub.id,\n provider: sub.provider,\n slug: sub.slug,\n status: sub.status,\n expiresAt: sub.expiresAt,\n createdAt: sub.createdAt,\n ...(warning ? { warning } : {}),\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerRegisterPushSubscription(server: McpServer): void {\n server.registerTool(\n \"register_push_subscription\",\n {\n title: \"Register Push Subscription\",\n description: `Register a real-time push subscription for a customer (Gmail Pub/Sub, MS Graph webhook, or Slack Events).\n\nInstead of polling every 30 minutes, the provider will push new events to dxcrm within seconds.\n\nRBAC: admin only\n\nArgs:\n provider: \"gmail\" | \"microsoft-graph\" | \"slack\"\n slug: Customer slug to receive events for\n webhookUrl: Public HTTPS URL where the provider will POST events (e.g. https://yourserver.com/webhooks/gmail)\n gmailTopicName: (Gmail only) Cloud Pub/Sub topic name (e.g. projects/my-project/topics/gmail-push)\n microsoftClientState: (MS Graph only) Secret for HMAC verification\n microsoftResource: (MS Graph only) Resource path (e.g. /me/mailFolders/Inbox/messages)\n slackTeamId: (Slack only) Workspace/team ID (e.g. T12345)\n slackChannelId: (Slack only) Optional specific channel to monitor\n\nReturns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning? }`,\n inputSchema: z.object({\n provider: z.enum([\"gmail\", \"microsoft-graph\", \"slack\"]).describe(\"Push provider\"),\n slug: z.string().describe(\"Customer slug\"),\n webhookUrl: z.string().describe(\"Public HTTPS URL for provider callbacks\"),\n gmailTopicName: z.string().optional().describe(\"Gmail: Cloud Pub/Sub topic name\"),\n microsoftClientState: z.string().optional().describe(\"MS Graph: secret for verification\"),\n microsoftResource: z.string().optional().describe(\"MS Graph: resource path\"),\n slackTeamId: z.string().optional().describe(\"Slack: workspace team ID\"),\n slackChannelId: z.string().optional().describe(\"Slack: optional channel ID\"),\n }),\n },\n async ({\n provider,\n slug,\n webhookUrl,\n gmailTopicName,\n microsoftClientState,\n microsoftResource,\n slackTeamId,\n slackChannelId,\n }) =>\n handleRegisterPushSubscription(\n {\n provider,\n slug,\n webhookUrl,\n ...(gmailTopicName !== undefined ? { gmailTopicName } : {}),\n ...(microsoftClientState !== undefined ? { microsoftClientState } : {}),\n ...(microsoftResource !== undefined ? { microsoftResource } : {}),\n ...(slackTeamId !== undefined ? { slackTeamId } : {}),\n ...(slackChannelId !== undefined ? { slackChannelId } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readSubscriptions, type PushProvider, type PushStatus } from \"../../sync/push-manager.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetPushStatus(\n input: { slug?: string; provider?: PushProvider },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n let subs = await readSubscriptions(dataDir);\n\n if (input.slug) subs = subs.filter((s) => s.slug === input.slug);\n if (input.provider) subs = subs.filter((s) => s.provider === input.provider);\n\n const now = Date.now();\n const RENEWAL_THRESHOLD_MS = 24 * 60 * 60 * 1000;\n\n const subscriptions = subs.map((s) => {\n const expiresInHours = s.expiresAt\n ? Math.round((new Date(s.expiresAt).getTime() - now) / (60 * 60 * 1000))\n : null;\n const needsRenewal =\n s.expiresAt !== null && new Date(s.expiresAt).getTime() - now < RENEWAL_THRESHOLD_MS;\n\n return {\n id: s.id,\n provider: s.provider,\n slug: s.slug,\n status: s.status,\n expiresAt: s.expiresAt,\n expiresInHours,\n needsRenewal,\n lastEventAt: s.lastEventAt,\n eventsProcessed: s.eventsProcessed,\n webhookUrl: s.webhookUrl,\n };\n });\n\n const countByStatus = (status: PushStatus) => subs.filter((s) => s.status === status).length;\n\n const summary = {\n total: subs.length,\n active: countByStatus(\"active\"),\n expiringSoon: subscriptions.filter((s) => s.needsRenewal && s.status === \"active\").length,\n expired: countByStatus(\"expired\"),\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ subscriptions, summary }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetPushStatus(server: McpServer): void {\n server.registerTool(\n \"get_push_status\",\n {\n title: \"Get Push Status\",\n description: `Get the status of all active push subscriptions (Gmail Pub/Sub, MS Graph, Slack Events).\n\nShows which customers have real-time push enabled, when subscriptions expire, and how many events have been processed.\n\nRBAC: any\n\nArgs:\n slug: (optional) Filter by customer slug\n provider: (optional) Filter by provider — \"gmail\" | \"microsoft-graph\" | \"slack\"\n\nReturns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHours, needsRenewal, lastEventAt, eventsProcessed }], summary: { total, active, expiringSoon, expired } }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n provider: z\n .enum([\"gmail\", \"microsoft-graph\", \"slack\"])\n .optional()\n .describe(\"Filter by provider\"),\n }),\n },\n async ({ slug, provider }) =>\n handleGetPushStatus(\n {\n ...(slug !== undefined ? { slug } : {}),\n ...(provider !== undefined ? { provider } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { readGraph, getStakeholders, type MissingRole } from \"./graph.js\";\nimport { readHealth } from \"./relationship-health.js\";\nimport type { ExternalSignal } from \"../sync/external-signals.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type StakeholderRole =\n | \"champion\"\n | \"economic_buyer\"\n | \"blocker\"\n | \"influencer\"\n | \"user\"\n | \"unknown\";\n\nexport interface StakeholderProfile {\n name: string;\n email?: string;\n role: StakeholderRole;\n healthScore: number;\n daysSinceContact: number;\n contactStrength: number;\n riskFlags: string[];\n}\n\nexport interface StakeholderMap {\n slug: string;\n dealName?: string;\n updatedAt: string;\n people: StakeholderProfile[];\n missingRoles: MissingRole[];\n riskAssessment: string;\n recommendation: string;\n}\n\n// ─── Core functions ───────────────────────────────────────────────────────────\n\nexport function buildStakeholderMap(\n dataDir: string,\n slug: string,\n today: string,\n dealName?: string\n): StakeholderMap {\n const graph = readGraph(dataDir, slug);\n const stakeholders = getStakeholders(graph);\n const health = readHealth(dataDir, slug);\n\n const champIds = new Set(stakeholders.champions.map((n) => n.id));\n const buyerIds = new Set(stakeholders.economicBuyers.map((n) => n.id));\n const blockerIds = new Set(stakeholders.blockers.map((n) => n.id));\n\n const healthByContactId = new Map((health?.contacts ?? []).map((c) => [c.contactId, c]));\n\n const personNodes = graph.nodes.filter((n) => n.type === \"person\");\n\n const people: StakeholderProfile[] = personNodes.map((node) => {\n const contactHealth = healthByContactId.get(node.id);\n\n let role: StakeholderRole = \"unknown\";\n if (champIds.has(node.id)) role = \"champion\";\n else if (buyerIds.has(node.id)) role = \"economic_buyer\";\n else if (blockerIds.has(node.id)) role = \"blocker\";\n\n const edges = graph.edges.filter((e) => e.from === node.id);\n const contactStrength =\n edges.length > 0 ? Math.round(Math.max(...edges.map((e) => e.weight)) * 100) / 100 : 0.5;\n\n const profile: StakeholderProfile = {\n name: node.label,\n role,\n healthScore: contactHealth?.score ?? 50,\n daysSinceContact: contactHealth?.daysSinceContact ?? 999,\n contactStrength,\n riskFlags: contactHealth?.riskFlags ?? [],\n };\n if (node.properties.email) {\n profile.email = node.properties.email as string;\n }\n return profile;\n });\n\n const riskAssessment = buildRiskAssessment(people, stakeholders.missingRoles, []);\n\n const recommendation = deriveRecommendation(people, stakeholders.missingRoles);\n\n return {\n slug,\n ...(dealName ? { dealName } : {}),\n updatedAt: new Date(`${today}T00:00:00Z`).toISOString(),\n people,\n missingRoles: stakeholders.missingRoles,\n riskAssessment,\n recommendation,\n };\n}\n\nexport function buildRiskAssessment(\n people: StakeholderProfile[],\n missingRoles: MissingRole[],\n signals: ExternalSignal[] = []\n): string {\n const risks: string[] = [];\n\n if (missingRoles.some((r) => r.role === \"champion\")) {\n risks.push(\"No champion identified — deal lacks an internal advocate.\");\n }\n if (missingRoles.some((r) => r.role === \"economic_buyer\")) {\n risks.push(\"Economic buyer unknown — decision authority not confirmed.\");\n }\n\n const coldPeople = people.filter((p) => p.riskFlags.includes(\"NO_CONTACT_30D\"));\n if (coldPeople.length > 0) {\n risks.push(`Cold contacts (30d+ silence): ${coldPeople.map((p) => p.name).join(\", \")}.`);\n }\n\n const lowHealth = people.filter((p) => p.healthScore < 40);\n if (lowHealth.length > 0) {\n risks.push(`Low health score (<40): ${lowHealth.map((p) => p.name).join(\", \")}.`);\n }\n\n const negativeSignals = signals.filter((s) => s.impact === \"negative\");\n for (const sig of negativeSignals.slice(0, 2)) {\n risks.push(`External signal: ${sig.summary}`);\n }\n\n return risks.length > 0 ? risks.join(\" \") : \"No critical risks detected.\";\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction deriveRecommendation(people: StakeholderProfile[], missingRoles: MissingRole[]): string {\n const critical = missingRoles.find((r) => r.urgency === \"critical\");\n if (critical) return critical.suggestion;\n\n const coldPeople = people.filter((p) => p.riskFlags.includes(\"NO_CONTACT_30D\"));\n if (coldPeople.length > 0) {\n return `Re-engage ${coldPeople.map((p) => p.name).join(\", \")} — no contact in 30+ days.`;\n }\n\n const important = missingRoles.find((r) => r.urgency === \"important\");\n if (important) return important.suggestion;\n\n const avgHealth =\n people.length > 0\n ? Math.round(people.reduce((s, p) => s + p.healthScore, 0) / people.length)\n : 0;\n return `Relationship health avg ${avgHealth}/100. Maintain regular contact cadence.`;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildStakeholderMap } from \"../../core/org-intelligence.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetOrgIntelligence(\n input: { slug: string; dealName?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);\n return {\n content: [{ type: \"text\", text: JSON.stringify(map, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetOrgIntelligence(server: McpServer): void {\n server.registerTool(\n \"get_org_intelligence\",\n {\n title: \"Get Org Intelligence\",\n description: `Build a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.\n\nReturns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().optional().describe(\"Optional deal name to scope the analysis\"),\n }),\n },\n async ({ slug, dealName }) => handleGetOrgIntelligence(dealName ? { slug, dealName } : { slug })\n );\n}\n","import { buildStakeholderMap, type StakeholderMap } from \"../core/org-intelligence.js\";\nimport { readHealth } from \"../core/relationship-health.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { scoreDeal } from \"../core/deal-health.js\";\nimport {\n buildSimulationInput,\n runSimulation,\n type SimulationResult,\n} from \"../core/revenue-simulation.js\";\nimport { listPlaybooks, matchPlaybooks, type PlaybookMatch } from \"../core/playbooks.js\";\nimport type { DealHealthScore } from \"../core/deal-health.js\";\nimport type { ContactHealth } from \"../core/relationship-health.js\";\nimport type { MissingRole } from \"../core/graph.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface DealHealthEntry {\n deal: string;\n stage: string;\n score: number;\n grade: string;\n warnings: string[];\n}\n\nexport interface DealRoomBrief {\n slug: string;\n dealName: string;\n generatedAt: string;\n stakeholders: StakeholderMap;\n relationshipHealth: ContactHealth[];\n dealHealth: DealHealthEntry[];\n revenueSimulation: Pick<SimulationResult, \"p50\" | \"p10\" | \"p90\" | \"expected\" | \"atRiskRevenue\">;\n recommendedPlaybook: PlaybookMatch | null;\n executiveSummary: string;\n topPriorities: string[];\n riskScore: number;\n}\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n\nexport async function buildDealRoom(\n dataDir: string,\n slug: string,\n dealName: string,\n today: string\n): Promise<DealRoomBrief> {\n // Parallel data collection\n const [pipelineDeals, simInput] = await Promise.all([\n readPipeline(dataDir, slug).catch(() => []),\n buildSimulationInput(dataDir, \"quarter\", today).catch(() => ({\n deals: [],\n externalSignals: [],\n iterations: 1000,\n horizon: \"quarter\" as const,\n today,\n })),\n ]);\n\n // Sync reads (fast FS reads)\n const stakeholders = buildStakeholderMap(dataDir, slug, today, dealName);\n const health = readHealth(dataDir, slug);\n const simResult = runSimulation({ ...simInput, iterations: 1000 });\n\n // Deal health scores\n const todayDate = new Date(today);\n const dealHealth: DealHealthEntry[] = pipelineDeals\n .filter((d) => d.stage !== \"won\" && d.stage !== \"lost\")\n .map((deal) => {\n const updatedDate = deal.updated ? new Date(deal.updated) : todayDate;\n const daysSinceLastActivity = Math.floor(\n (todayDate.getTime() - updatedDate.getTime()) / 86_400_000\n );\n const daysToClose = deal.close_date\n ? Math.floor((new Date(deal.close_date).getTime() - todayDate.getTime()) / 86_400_000)\n : undefined;\n const scored: DealHealthScore = scoreDeal(deal, {\n daysSinceLastActivity,\n daysInCurrentStage: daysSinceLastActivity,\n ...(daysToClose !== undefined ? { daysToClose } : {}),\n ...(deal.probability !== undefined ? { probability: deal.probability } : {}),\n });\n return {\n deal: deal.name,\n stage: deal.stage,\n score: scored.score,\n grade: scored.grade,\n warnings: scored.warnings,\n };\n });\n\n // Playbook matching — use the first active deal as context\n const firstActiveDeal = pipelineDeals.find((d) => d.stage !== \"won\" && d.stage !== \"lost\");\n let recommendedPlaybook: PlaybookMatch | null = null;\n if (firstActiveDeal) {\n // Prefer champion's contact health for accurate daysSinceContact signal\n const championEmail = stakeholders.people.find((p) => p.role === \"champion\")?.email;\n const champContact = championEmail\n ? health?.contacts.find(\n (c) => c.email === championEmail || c.contactId === `person:${championEmail}`\n )\n : undefined;\n const contactHealth = champContact ?? health?.contacts?.[0];\n const daysSinceContact = contactHealth?.daysSinceContact ?? 999;\n const dealSnapshot = {\n slug,\n name: firstActiveDeal.name,\n stage: firstActiveDeal.stage,\n value: firstActiveDeal.value ?? 0,\n probability: firstActiveDeal.probability ?? 50,\n healthScore: health?.overallHealth ?? 50,\n daysSinceContact,\n championPresent: stakeholders.people.some((p) => p.role === \"champion\"),\n };\n const matches = matchPlaybooks(listPlaybooks(dataDir, slug), dealSnapshot, daysSinceContact);\n recommendedPlaybook = matches[0] ?? null;\n }\n\n const riskScore = computeRiskScore(\n stakeholders.missingRoles,\n dealHealth,\n health?.overallHealth ?? 100\n );\n const topPriorities = buildTopPriorities(stakeholders, dealHealth);\n const executiveSummary = buildExecutiveSummary(\n slug,\n dealName,\n stakeholders,\n health?.overallHealth ?? 100,\n simResult,\n riskScore\n );\n\n return {\n slug,\n dealName,\n generatedAt: new Date().toISOString(),\n stakeholders,\n relationshipHealth: health?.contacts ?? [],\n dealHealth,\n revenueSimulation: {\n p50: simResult.p50,\n p10: simResult.p10,\n p90: simResult.p90,\n expected: simResult.expected,\n atRiskRevenue: simResult.atRiskRevenue,\n },\n recommendedPlaybook,\n executiveSummary,\n topPriorities,\n riskScore,\n };\n}\n\n// ─── Synthesis helpers ────────────────────────────────────────────────────────\n\nfunction computeRiskScore(\n missingRoles: MissingRole[],\n dealHealth: DealHealthEntry[],\n overallHealth: number\n): number {\n let risk = 0;\n\n for (const mr of missingRoles) {\n risk += mr.urgency === \"critical\" ? 25 : 10;\n }\n\n const avgDealScore =\n dealHealth.length > 0 ? dealHealth.reduce((s, d) => s + d.score, 0) / dealHealth.length : 100;\n risk += Math.round((100 - avgDealScore) * 0.3);\n\n risk += Math.round((100 - overallHealth) * 0.2);\n\n return Math.min(100, Math.max(0, risk));\n}\n\nfunction buildTopPriorities(stakeholders: StakeholderMap, dealHealth: DealHealthEntry[]): string[] {\n const priorities: string[] = [];\n\n for (const mr of stakeholders.missingRoles) {\n if (mr.urgency === \"critical\") priorities.push(mr.suggestion);\n }\n\n const coldPeople = stakeholders.people.filter((p) => p.riskFlags.includes(\"NO_CONTACT_30D\"));\n if (coldPeople.length > 0) {\n priorities.push(`Re-engage ${coldPeople.map((p) => p.name).join(\", \")} — silent 30+ days.`);\n }\n\n const atRiskDeals = dealHealth.filter((d) => d.score < 50);\n const showCount = Math.min(3, atRiskDeals.length);\n for (const d of atRiskDeals.slice(0, showCount)) {\n priorities.push(\n `Rescue deal \"${d.deal}\" (health ${d.score}/100): ${d.warnings[0] ?? \"at risk\"}`\n );\n }\n if (atRiskDeals.length > showCount) {\n priorities.push(`+${atRiskDeals.length - showCount} more at-risk deal(s) need attention.`);\n }\n\n for (const mr of stakeholders.missingRoles) {\n if (mr.urgency === \"important\") priorities.push(mr.suggestion);\n }\n\n if (priorities.length === 0) {\n priorities.push(\"Maintain current momentum — schedule next check-in.\");\n }\n\n return priorities;\n}\n\nfunction buildExecutiveSummary(\n slug: string,\n dealName: string,\n stakeholders: StakeholderMap,\n overallHealth: number,\n sim: Pick<SimulationResult, \"p50\" | \"atRiskRevenue\">,\n riskScore: number\n): string {\n const champCount = stakeholders.people.filter((p) => p.role === \"champion\").length;\n const missingCritical = stakeholders.missingRoles.filter((r) => r.urgency === \"critical\").length;\n\n const parts: string[] = [];\n parts.push(\n `${slug}/${dealName}: relationship health ${overallHealth}/100, ${champCount} champion(s) identified.`\n );\n if (sim.p50 > 0) {\n parts.push(`Pipeline P50 forecast: €${(sim.p50 / 1000).toFixed(1)}k.`);\n }\n if (missingCritical > 0) {\n parts.push(\n `Critical gaps: ${missingCritical} key role(s) unidentified — risk score ${riskScore}/100.`\n );\n } else {\n parts.push(`Overall risk score: ${riskScore}/100.`);\n }\n return parts.join(\" \");\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildDealRoom } from \"../../agents/deal-room.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleOpenDealRoom(\n input: { slug: string; dealName: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);\n return {\n content: [{ type: \"text\", text: JSON.stringify(brief, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerOpenDealRoom(server: McpServer): void {\n server.registerTool(\n \"open_deal_room\",\n {\n title: \"Open Deal Room\",\n description: `Multi-agent deal brief: orchestrates relationship graph, health scores, deal health, revenue simulation, and playbook matching into a unified deal-room brief with executive summary, top priorities, and risk score (0–100).\n\nReturns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().describe(\"Name of the deal to analyse\"),\n }),\n },\n async ({ slug, dealName }) => handleOpenDealRoom({ slug, dealName })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { withJsonFile } from \"./file-lock.js\";\nimport { computeCustomerHealth, readHealth } from \"./relationship-health.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport { buildSimulationInput, runSimulation } from \"./revenue-simulation.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type TaskType =\n | \"daily_briefing\"\n | \"relationship_decay_alert\"\n | \"deal_risk_alert\"\n | \"external_signal_alert\"\n | \"follow_up_nudge\"\n | \"goal_progress_update\"\n | \"pipeline_forecast_weekly\"\n | \"playbook_suggestion\";\n\nexport type NotificationChannel = \"telegram\" | \"slack\" | \"email\" | \"mcp_tool_response\";\nexport type TaskPriority = \"urgent\" | \"high\" | \"normal\";\nexport type TaskStatus = \"pending\" | \"processing\" | \"done\" | \"failed\";\n\nexport interface AgentTask {\n id: string;\n type: TaskType;\n slug?: string;\n priority: TaskPriority;\n payload: unknown;\n createdAt: string;\n scheduledFor: string;\n status: TaskStatus;\n result?: string;\n channel: NotificationChannel;\n}\n\nexport interface DailyBriefing {\n date: string;\n generatedAt: string;\n urgent: string[];\n opportunities: string[];\n forecast: string;\n topAction: string;\n}\n\n// ─── Queue path ───────────────────────────────────────────────────────────────\n\nfunction queuePath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"agent-queue.json\");\n}\n\n// ─── Queue operations ─────────────────────────────────────────────────────────\n\nexport function readQueue(dataDir: string): AgentTask[] {\n const p = queuePath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n return Array.isArray(raw) ? (raw as AgentTask[]) : [];\n } catch {\n return [];\n }\n}\n\nexport async function enqueueTask(\n dataDir: string,\n task: Omit<AgentTask, \"id\" | \"createdAt\" | \"status\">\n): Promise<AgentTask> {\n const now = new Date().toISOString();\n const newTask: AgentTask = {\n ...task,\n id: `task_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,\n createdAt: now,\n status: \"pending\",\n };\n\n await withJsonFile<AgentTask[]>(queuePath(dataDir), (current) => {\n const existing = Array.isArray(current) ? current : [];\n return [...existing, newTask];\n });\n\n return newTask;\n}\n\nexport async function markTaskDone(\n dataDir: string,\n taskId: string,\n result?: string\n): Promise<void> {\n await withJsonFile<AgentTask[]>(queuePath(dataDir), (current) => {\n const tasks = Array.isArray(current) ? [...current] : [];\n const idx = tasks.findIndex((t) => t.id === taskId);\n if (idx >= 0) {\n tasks[idx] = { ...tasks[idx]!, status: \"done\", ...(result ? { result } : {}) };\n }\n return tasks;\n });\n}\n\n// ─── Daily briefing ───────────────────────────────────────────────────────────\n\nexport async function buildDailyBriefing(dataDir: string, today: string): Promise<DailyBriefing> {\n const slugs = listCustomerSlugs(dataDir);\n\n const urgent: string[] = [];\n const opportunities: string[] = [];\n const todayDate = new Date(`${today}T00:00:00Z`);\n\n // Parallel I/O across all customers\n const customerData = await Promise.all(\n slugs.map(async (slug) => {\n const cached = readHealth(dataDir, slug);\n const health = cached ?? computeCustomerHealth(dataDir, slug, today);\n const deals = await readPipeline(dataDir, slug).catch(() => []);\n return { slug, health, deals };\n })\n );\n\n for (const { slug, health, deals } of customerData) {\n // Relationship decay alerts\n for (const contact of health.contacts) {\n if (contact.riskFlags.includes(\"NO_CONTACT_30D\")) {\n urgent.push(\n `${slug}: ${contact.name} has been silent for ${contact.daysSinceContact} days — health ${contact.score}/100`\n );\n }\n }\n\n // Opportunities — B-grade+ relationship health with active deals\n const activeDeals = deals.filter((d) => d.stage !== \"won\" && d.stage !== \"lost\");\n if (health.overallHealth >= 65 && activeDeals.length > 0) {\n opportunities.push(\n `${slug}: relationship health ${health.overallHealth}/100 with ${activeDeals.length} active deal(s) — good time for expansion or upsell.`\n );\n }\n\n // Deal risk alerts\n for (const deal of deals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n const daysToClose = Math.floor(\n (new Date(deal.close_date).getTime() - todayDate.getTime()) / 86_400_000\n );\n if (daysToClose <= 7 && daysToClose >= 0) {\n urgent.push(\n `${slug}: Deal \"${deal.name}\" closes in ${daysToClose} day(s) — ${deal.stage}`\n );\n } else if (daysToClose < 0) {\n urgent.push(\n `${slug}: Deal \"${deal.name}\" close date passed (${Math.abs(daysToClose)} days overdue)`\n );\n }\n }\n }\n }\n\n // Revenue forecast\n let forecast = \"No active pipeline.\";\n try {\n const simInput = await buildSimulationInput(dataDir, \"quarter\", today);\n if (simInput.deals.length > 0) {\n const sim = runSimulation({ ...simInput, iterations: 1000 });\n forecast = `Q forecast: P50 €${(sim.p50 / 1000).toFixed(1)}k / P90 €${(sim.p90 / 1000).toFixed(1)}k — ${simInput.deals.length} deal(s) in pipeline.`;\n }\n } catch {\n // forecast stays as default\n }\n\n // Top action\n const topAction =\n urgent.length > 0\n ? (urgent[0]!\n .replace(/^[^:]+: /, \"\")\n .split(\"—\")[0]\n ?.trim() ?? urgent[0]!)\n : \"Review your pipeline and schedule next customer check-ins.\";\n\n return {\n date: today,\n generatedAt: new Date().toISOString(),\n urgent,\n opportunities,\n forecast,\n topAction,\n };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildDailyBriefing } from \"../../core/proactive-agent.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetProactiveBriefing(\n input: { date?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const date = input.date ?? new Date().toISOString().slice(0, 10);\n const briefing = await buildDailyBriefing(dataDir, date);\n return {\n content: [{ type: \"text\", text: JSON.stringify(briefing, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetProactiveBriefing(server: McpServer): void {\n server.registerTool(\n \"get_proactive_briefing\",\n {\n title: \"Get Proactive Briefing\",\n description: `Generate a proactive daily briefing: urgent alerts (relationship decay, deal risk, overdue close dates), pipeline forecast (P50/P90), and a single top-action recommendation.\n\nReturns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }`,\n inputSchema: z.object({\n date: z.string().optional().describe(\"ISO date (YYYY-MM-DD). Defaults to today.\"),\n }),\n },\n async ({ date }) => handleGetProactiveBriefing(date ? { date } : {})\n );\n}\n","import { z } from \"zod\";\n\nexport const EmailTemplateSchema = z.object({\n id: z.string().min(1),\n subject: z.string().min(1),\n category: z.string().default(\"general\"),\n variables: z.array(z.string()).default([]),\n language: z.string().default(\"de\"),\n createdAt: z.string(),\n updatedAt: z.string().optional(),\n});\n\nexport type EmailTemplateMeta = z.infer<typeof EmailTemplateSchema>;\n\nexport interface EmailTemplate extends EmailTemplateMeta {\n body: string;\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { EmailTemplateSchema, type EmailTemplate } from \"../schemas/email-template.js\";\n\nexport function templatesDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"templates\");\n}\n\nfunction parseTemplateFile(filePath: string, id: string): EmailTemplate | null {\n try {\n const raw = matter(fs.readFileSync(filePath, \"utf-8\"));\n const result = EmailTemplateSchema.safeParse({\n id,\n createdAt: new Date().toISOString(),\n ...raw.data,\n });\n if (!result.success) return null;\n return { ...result.data, body: raw.content.trim() };\n } catch {\n return null;\n }\n}\n\nexport function listTemplates(dataDir: string, opts?: { category?: string }): EmailTemplate[] {\n const base = templatesDir(dataDir);\n if (!fs.existsSync(base)) return [];\n const results: EmailTemplate[] = [];\n\n const categories = fs.readdirSync(base).filter((name) => {\n try {\n return fs.statSync(path.join(base, name)).isDirectory();\n } catch {\n return false;\n }\n });\n\n for (const cat of categories) {\n if (opts?.category && cat !== opts.category) continue;\n const catDir = path.join(base, cat);\n const files = fs.readdirSync(catDir).filter((f) => f.endsWith(\".md\"));\n for (const file of files) {\n const id = file.replace(/\\.md$/, \"\");\n const tmpl = parseTemplateFile(path.join(catDir, file), id);\n if (tmpl) results.push(tmpl);\n }\n }\n return results;\n}\n\nexport function getTemplate(dataDir: string, id: string): EmailTemplate | null {\n const base = templatesDir(dataDir);\n if (!fs.existsSync(base)) return null;\n\n // Search all categories\n const categories = fs.existsSync(base)\n ? fs.readdirSync(base).filter((n) => {\n try {\n return fs.statSync(path.join(base, n)).isDirectory();\n } catch {\n return false;\n }\n })\n : [];\n\n for (const cat of categories) {\n const p = path.join(base, cat, `${id}.md`);\n if (fs.existsSync(p)) return parseTemplateFile(p, id);\n }\n return null;\n}\n\nexport function writeTemplate(dataDir: string, tmpl: EmailTemplate): void {\n const { body, ...meta } = tmpl;\n const category = meta.category ?? \"general\";\n const dir = path.join(templatesDir(dataDir), category);\n fs.mkdirSync(dir, { recursive: true });\n const content = matter.stringify(body, { ...meta, updatedAt: new Date().toISOString() });\n fs.writeFileSync(path.join(dir, `${tmpl.id}.md`), content, \"utf-8\");\n}\n\nexport function deleteTemplate(dataDir: string, id: string): boolean {\n const tmpl = getTemplate(dataDir, id);\n if (!tmpl) return false;\n const p = path.join(templatesDir(dataDir), tmpl.category, `${id}.md`);\n if (fs.existsSync(p)) {\n fs.unlinkSync(p);\n return true;\n }\n return false;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listTemplates } from \"../../fs/template-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListEmailTemplates(\n input: { category?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const templates = listTemplates(dataDir, input.category ? { category: input.category } : {});\n const summary = templates.map(({ body: _body, ...meta }) => meta);\n return { content: [{ type: \"text\", text: JSON.stringify(summary, null, 2) }] };\n}\n\nexport function registerListEmailTemplates(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"list_email_templates\",\n {\n description:\n \"List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').\",\n inputSchema: z.object({\n category: z.string().optional().describe(\"Filter by category\"),\n }),\n },\n ({ category }) => handleListEmailTemplates(category ? { category } : {}, dataDir)\n );\n}\n","import { readMainFacts } from \"../fs/customer-dir.js\";\n\nexport type TemplateVariables = Record<string, string | number | undefined>;\n\nconst VARIABLE_REGEX = /\\{\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}\\}/g;\n\nexport function interpolate(template: string, vars: TemplateVariables): string {\n return template.replace(VARIABLE_REGEX, (match, key: string) => {\n const val = vars[key];\n return val !== undefined ? String(val) : match; // keep {{key}} if unresolved\n });\n}\n\nexport function extractVariables(template: string): string[] {\n return [...template.matchAll(new RegExp(VARIABLE_REGEX.source, \"g\"))].map((m) => m[1]!);\n}\n\nexport async function buildVariablesFromCustomer(\n dataDir: string,\n slug: string\n): Promise<TemplateVariables> {\n const facts = await readMainFacts(dataDir, slug).catch(() => null);\n const now = new Date();\n return {\n company: facts?.name ?? slug,\n domain: facts?.domain ?? \"\",\n email: facts?.email ?? \"\",\n stage: facts?.relationship_stage ?? \"\",\n slug,\n date: now.toLocaleDateString(\"de-DE\"),\n year: now.getFullYear(),\n month: now.toLocaleDateString(\"de-DE\", { month: \"long\" }),\n };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getTemplate } from \"../../fs/template-store.js\";\nimport { extractVariables } from \"../../core/template-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetEmailTemplate(\n input: { id: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tmpl = getTemplate(dataDir, input.id);\n if (!tmpl) {\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ error: `Template '${input.id}' not found` }) },\n ],\n };\n }\n const allVars = extractVariables(`${tmpl.subject}\\n${tmpl.body}`);\n const unique = [...new Set(allVars)];\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ ...tmpl, detectedVariables: unique }, null, 2),\n },\n ],\n };\n}\n\nexport function registerGetEmailTemplate(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_email_template\",\n {\n description:\n \"Get a specific email template by ID, including its body and detected variables.\",\n inputSchema: z.object({\n id: z.string().describe(\"Template ID (e.g. 'enterprise-intro')\"),\n }),\n },\n ({ id }) => handleGetEmailTemplate({ id }, dataDir)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getTemplate } from \"../../fs/template-store.js\";\nimport { interpolate, buildVariablesFromCustomer } from \"../../core/template-engine.js\";\nimport { readMainFacts } from \"../../fs/customer-dir.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleDraftEmail(\n input: {\n slug: string;\n templateId: string;\n overrides?: Record<string, string>;\n tone?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tmpl = getTemplate(dataDir, input.templateId);\n if (!tmpl) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Template '${input.templateId}' not found` }),\n },\n ],\n };\n }\n\n const autoVars = await buildVariablesFromCustomer(dataDir, input.slug);\n const vars = { ...autoVars, ...(input.overrides ?? {}) };\n\n const subject = interpolate(tmpl.subject, vars);\n const interpolatedBody = interpolate(tmpl.body, vars);\n\n // Tone: explicit override wins; otherwise fall back to the customer's tone\n // profile (D8), then the global default.\n let effectiveTone = input.tone;\n if (!effectiveTone) {\n const { resolveTone, toneInstruction } = await import(\"../../core/tone.js\");\n const instr = toneInstruction(resolveTone(dataDir, input.slug));\n if (instr) effectiveTone = instr;\n }\n\n // Optional LLM polish: rewrite the interpolated body in the requested tone.\n // Falls back to the plain interpolation when no ANTHROPIC_API_KEY is set or\n // the call fails — the template-fill behaviour is always preserved.\n let body = interpolatedBody;\n let polished = false;\n if (effectiveTone) {\n try {\n const { callLlm } = await import(\"../../core/llm.js\");\n const refined = await callLlm(\n `Rewrite the following email in a ${effectiveTone} tone. Keep the same language, ` +\n `preserve all names and facts, and do not invent details. ` +\n `Return ONLY the rewritten email body, no preamble.\\n\\n---\\n${interpolatedBody}`\n );\n if (refined && refined.trim()) {\n // EU-AI-Act Art. 50 (D17): label AI-generated content (opt-out).\n const { labelAiContent } = await import(\"../../core/compliance.js\");\n body = labelAiContent(refined.trim());\n polished = true;\n }\n } catch {\n // graceful fallback to the interpolated body\n }\n }\n\n // Try to get email from main_facts for 'to' field\n const facts = await readMainFacts(dataDir, input.slug).catch(() => null);\n const to = facts?.email ?? \"\";\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n subject,\n body,\n to,\n slug: input.slug,\n templateId: input.templateId,\n tone: effectiveTone ?? null,\n polished,\n resolvedVariables: vars,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerDraftEmail(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"draft_email\",\n {\n description: `Draft a personalized email for a customer using a stored template.\nVariables are auto-filled from the customer's main_facts.md. Override any variable manually.\nOptionally pass a tone (e.g. \"formal\", \"friendly\", \"concise\") to LLM-polish the body —\nfalls back to plain template-fill when no ANTHROPIC_API_KEY is configured.\nReturns: { subject, body, to, tone, polished, resolvedVariables } — does NOT send automatically.`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n templateId: z.string().describe(\"Template ID to use\"),\n overrides: z\n .record(z.string())\n .optional()\n .describe(\"Override any template variable (e.g. {firstName: 'Alice'})\"),\n tone: z\n .string()\n .optional()\n .describe(\"Optional tone to LLM-polish the body (e.g. 'formal', 'friendly', 'concise')\"),\n }),\n },\n ({ slug, templateId, overrides, tone }) =>\n handleDraftEmail(\n {\n slug,\n templateId,\n ...(overrides !== undefined ? { overrides } : {}),\n ...(tone !== undefined ? { tone } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const SequenceStepSchema = z.object({\n day: z.number().int().min(0),\n templateId: z.string().min(1),\n skipIfReplied: z.boolean().default(true),\n});\n\nexport const SequenceSchema = z.object({\n id: z.string().min(1),\n name: z.string().min(1),\n steps: z.array(SequenceStepSchema).min(1),\n createdAt: z.string(),\n});\n\nexport const SequenceEnrollmentSchema = z.object({\n id: z.string(),\n sequenceId: z.string(),\n slug: z.string(),\n contactEmail: z.string().email(),\n enrolledAt: z.string(),\n status: z.enum([\"active\", \"paused\", \"completed\", \"bounced\"]),\n currentStep: z.number().int().min(0),\n stepsCompleted: z.array(z.number()),\n lastSentAt: z.string().optional(),\n lastRepliedAt: z.string().optional(),\n});\n\nexport type SequenceStep = z.infer<typeof SequenceStepSchema>;\nexport type Sequence = z.infer<typeof SequenceSchema>;\nexport type SequenceEnrollment = z.infer<typeof SequenceEnrollmentSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport { withJsonFile } from \"../core/file-lock.js\";\nimport {\n SequenceSchema,\n SequenceEnrollmentSchema,\n type Sequence,\n type SequenceEnrollment,\n} from \"../schemas/sequence.js\";\n\nexport function sequencesDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sequences\");\n}\n\nexport function enrollmentsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sequence-enrollments.json\");\n}\n\nexport function listSequences(dataDir: string): Sequence[] {\n const dir = sequencesDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n\n const files = fs.readdirSync(dir).filter((f) => f.endsWith(\".yaml\"));\n const results: Sequence[] = [];\n\n for (const file of files) {\n try {\n const content = fs.readFileSync(path.join(dir, file), \"utf-8\") as string;\n const raw = yaml.load(content);\n const parsed = SequenceSchema.safeParse(raw);\n if (parsed.success) {\n results.push(parsed.data);\n }\n } catch {\n // skip invalid files\n }\n }\n\n return results;\n}\n\nexport function getSequence(dataDir: string, id: string): Sequence | null {\n const sequences = listSequences(dataDir);\n return sequences.find((s) => s.id === id) ?? null;\n}\n\nexport function writeSequence(dataDir: string, seq: Sequence): void {\n const dir = sequencesDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const content = yaml.dump(seq);\n fs.writeFileSync(path.join(dir, `${seq.id}.yaml`), content, \"utf-8\");\n}\n\nexport function readEnrollments(dataDir: string): SequenceEnrollment[] {\n const p = enrollmentsPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n return Array.isArray(raw) ? (raw as SequenceEnrollment[]) : [];\n } catch {\n return [];\n }\n}\n\nexport async function writeEnrollment(\n dataDir: string,\n enrollment: SequenceEnrollment\n): Promise<void> {\n await withJsonFile<SequenceEnrollment[]>(enrollmentsPath(dataDir), (current) => {\n const existing = Array.isArray(current) ? current : [];\n return [...existing, enrollment];\n });\n}\n\nexport async function updateEnrollment(\n dataDir: string,\n id: string,\n updates: Partial<SequenceEnrollment>\n): Promise<SequenceEnrollment | null> {\n let updated: SequenceEnrollment | null = null;\n\n await withJsonFile<SequenceEnrollment[]>(enrollmentsPath(dataDir), (current) => {\n const existing = Array.isArray(current) ? current : [];\n const idx = existing.findIndex((e) => e.id === id);\n if (idx < 0) return existing;\n\n const merged = SequenceEnrollmentSchema.parse({ ...existing[idx], ...updates });\n updated = merged;\n const next = [...existing];\n next[idx] = merged;\n return next;\n });\n\n return updated;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getSequence, writeEnrollment } from \"../../fs/sequence-store.js\";\nimport { getTemplate } from \"../../fs/template-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleEnrollInSequence(\n input: { slug: string; contactEmail: string; sequenceId: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const sequence = getSequence(dataDir, input.sequenceId);\n if (!sequence) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Sequence '${input.sequenceId}' not found` }),\n },\n ],\n };\n }\n\n // Validate that the first step's template exists\n const firstStep = sequence.steps[0]!;\n const template = getTemplate(dataDir, firstStep.templateId);\n if (!template) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error: `Template '${firstStep.templateId}' for step 0 not found`,\n }),\n },\n ],\n };\n }\n\n const enrollmentId = `enroll_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;\n const now = new Date().toISOString();\n\n const enrollment = {\n id: enrollmentId,\n sequenceId: input.sequenceId,\n slug: input.slug,\n contactEmail: input.contactEmail,\n enrolledAt: now,\n status: \"active\" as const,\n currentStep: 0,\n stepsCompleted: [] as number[],\n };\n\n await writeEnrollment(dataDir, enrollment);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n enrollmentId,\n sequenceName: sequence.name,\n totalSteps: sequence.steps.length,\n }),\n },\n ],\n };\n}\n\nexport function registerEnrollInSequence(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"enroll_in_sequence\",\n {\n description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.\nReturns: { enrollmentId, sequenceName, totalSteps }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n contactEmail: z.string().email().describe(\"Email address of the contact to enroll\"),\n sequenceId: z.string().describe(\"ID of the sequence to enroll in\"),\n }),\n },\n ({ slug, contactEmail, sequenceId }) =>\n handleEnrollInSequence({ slug, contactEmail, sequenceId }, dataDir)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readEnrollments } from \"../../fs/sequence-store.js\";\nimport type { SequenceEnrollment } from \"../../schemas/sequence.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListSequenceEnrollments(\n input: { slug?: string; status?: \"active\" | \"paused\" | \"completed\" },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n let enrollments = readEnrollments(dataDir);\n\n if (input.slug !== undefined) {\n enrollments = enrollments.filter((e: SequenceEnrollment) => e.slug === input.slug);\n }\n\n if (input.status !== undefined) {\n enrollments = enrollments.filter((e: SequenceEnrollment) => e.status === input.status);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ enrollments }, null, 2),\n },\n ],\n };\n}\n\nexport function registerListSequenceEnrollments(\n server: McpServer,\n dataDir: string = DATA_DIR\n): void {\n server.registerTool(\n \"list_sequence_enrollments\",\n {\n description: `List email sequence enrollments. Filter by customer slug or status.\nReturns: { enrollments: SequenceEnrollment[] }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n status: z\n .enum([\"active\", \"paused\", \"completed\"])\n .optional()\n .describe(\"Filter by enrollment status\"),\n }),\n },\n ({ slug, status }) =>\n handleListSequenceEnrollments(\n {\n ...(slug !== undefined ? { slug } : {}),\n ...(status !== undefined ? { status } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { updateEnrollment } from \"../../fs/sequence-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUnenrollFromSequence(\n input: { enrollmentId: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const updated = await updateEnrollment(dataDir, input.enrollmentId, { status: \"paused\" });\n\n if (!updated) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: `Enrollment '${input.enrollmentId}' not found`,\n }),\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true }),\n },\n ],\n };\n}\n\nexport function registerUnenrollFromSequence(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"unenroll_from_sequence\",\n {\n description: `Unenroll (pause) a contact from an email sequence. Sets status to \"paused\" (soft delete).\nReturns: { success: boolean }`,\n inputSchema: z.object({\n enrollmentId: z.string().describe(\"ID of the enrollment to pause\"),\n }),\n },\n ({ enrollmentId }) => handleUnenrollFromSequence({ enrollmentId }, dataDir)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listSequences, readEnrollments } from \"../../fs/sequence-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListSequences(\n _input: Record<string, never>,\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const sequences = listSequences(dataDir);\n const enrollments = readEnrollments(dataDir);\n\n const result = sequences.map((seq) => ({\n id: seq.id,\n name: seq.name,\n stepCount: seq.steps.length,\n enrollmentCount: enrollments.filter((e) => e.sequenceId === seq.id).length,\n }));\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ sequences: result }, null, 2),\n },\n ],\n };\n}\n\nexport function registerListSequences(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"list_sequences\",\n {\n description: `List all email sequences with step count and enrollment count.\nReturns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,\n inputSchema: z.object({}),\n },\n () => handleListSequences({}, dataDir)\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type { Quote, QuoteLineItem } from \"../schemas/quote.js\";\n\ninterface QuoteConfig {\n companyName?: string;\n companyAddress?: string;\n vatId?: string;\n currency?: string;\n paymentTerms?: string;\n footerText?: string;\n}\n\nexport interface GenerateQuoteInput {\n slug: string;\n dealName: string;\n lineItems: Array<{ description: string; quantity: number; unitPrice: number }>;\n vatPercent?: number;\n validUntilDays?: number;\n currency?: string;\n}\n\nfunction quotesDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"quotes\");\n}\n\nfunction loadQuoteConfig(dataDir: string): QuoteConfig {\n const p = path.join(dataDir, \".agentic\", \"quote-config.yaml\");\n if (!fs.existsSync(p)) return {};\n try {\n return (yaml.load(fs.readFileSync(p, \"utf-8\") as string) as QuoteConfig) ?? {};\n } catch {\n return {};\n }\n}\n\nfunction nextQuoteNumber(dataDir: string): string {\n const year = new Date().getFullYear();\n const dir = quotesDir(dataDir);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n return `Q-${year}-001`;\n }\n const existing = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\") && f.startsWith(`Q-${year}-`))\n .map((f) => parseInt(f.replace(`Q-${year}-`, \"\").replace(\".json\", \"\"), 10))\n .filter((n) => !isNaN(n));\n const max = existing.length > 0 ? Math.max(...existing) : 0;\n return `Q-${year}-${String(max + 1).padStart(3, \"0\")}`;\n}\n\nfunction addDaysToDate(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.slice(0, 10).split(\"-\").map(Number) as [\n number,\n number,\n number,\n ];\n const d = new Date(Date.UTC(year, month - 1, day));\n d.setUTCDate(d.getUTCDate() + days);\n return d.toISOString().slice(0, 10);\n}\n\nfunction buildHtml(quote: Quote, config: QuoteConfig, customerName: string): string {\n const lineRows = quote.lineItems\n .map(\n (item) =>\n `<tr><td>${item.description}</td><td style=\"text-align:right\">${item.quantity}</td><td style=\"text-align:right\">${item.unitPrice.toFixed(2)} ${quote.currency}</td><td style=\"text-align:right\">${item.total.toFixed(2)} ${quote.currency}</td></tr>`\n )\n .join(\"\\n\");\n\n return `<!DOCTYPE html>\n<html lang=\"de\">\n<head><meta charset=\"UTF-8\"><title>Angebot ${quote.quoteNumber}</title>\n<style>body{font-family:Arial,sans-serif;max-width:800px;margin:40px auto;color:#222}table{width:100%;border-collapse:collapse}th,td{padding:8px 12px;border:1px solid #ddd}th{background:#f5f5f5}h1{color:#1a1a2e}.total{font-weight:bold;font-size:1.1em}</style>\n</head>\n<body>\n<h1>Angebot ${quote.quoteNumber}</h1>\n<p><strong>${config.companyName ?? \"\"}</strong><br>${config.companyAddress ?? \"\"}<br>${config.vatId ? `USt-IdNr.: ${config.vatId}` : \"\"}</p>\n<hr>\n<p><strong>An:</strong> ${customerName}</p>\n<p><strong>Datum:</strong> ${quote.createdAt.slice(0, 10)} &nbsp;&nbsp; <strong>Gültig bis:</strong> ${quote.validUntil}</p>\n<h2>Leistungen</h2>\n<table>\n<thead><tr><th>Beschreibung</th><th style=\"text-align:right\">Menge</th><th style=\"text-align:right\">Einzelpreis</th><th style=\"text-align:right\">Gesamt</th></tr></thead>\n<tbody>${lineRows}</tbody>\n</table>\n<br>\n<table style=\"width:300px;margin-left:auto\">\n<tr><td>Nettobetrag</td><td style=\"text-align:right\">${quote.subtotal.toFixed(2)} ${quote.currency}</td></tr>\n<tr><td>MwSt. (${quote.vatPercent}%)</td><td style=\"text-align:right\">${quote.vat.toFixed(2)} ${quote.currency}</td></tr>\n<tr class=\"total\"><td><strong>Gesamtbetrag</strong></td><td style=\"text-align:right\"><strong>${quote.total.toFixed(2)} ${quote.currency}</strong></td></tr>\n</table>\n<br><p>${config.paymentTerms ?? \"\"}</p>\n<hr><small>${config.footerText ?? \"\"}</small>\n</body></html>`;\n}\n\nfunction readCustomerName(dataDir: string, slug: string): string {\n const p = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (!fs.existsSync(p)) return slug;\n const content = fs.readFileSync(p, \"utf-8\") as string;\n const match = /^name:\\s*(.+)$/m.exec(content);\n return match?.[1]?.trim() ?? slug;\n}\n\nexport function readQuote(dataDir: string, quoteNumber: string): Quote | null {\n const p = path.join(quotesDir(dataDir), `${quoteNumber}.json`);\n if (!fs.existsSync(p)) return null;\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as Quote;\n } catch {\n return null;\n }\n}\n\nexport function listQuotes(dataDir: string, slug?: string): Quote[] {\n const dir = quotesDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .flatMap((f) => {\n try {\n const q = JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\") as string) as Quote;\n return slug === undefined || q.slug === slug ? [q] : [];\n } catch {\n return [];\n }\n });\n}\n\nexport function updateQuoteStatus(\n dataDir: string,\n quoteNumber: string,\n status: Quote[\"status\"]\n): void {\n const q = readQuote(dataDir, quoteNumber);\n if (!q) return;\n const updated: Quote = { ...q, status };\n if (status === \"viewed\" && !q.viewedAt) updated.viewedAt = new Date().toISOString();\n if (status === \"accepted\" && !q.acceptedAt) updated.acceptedAt = new Date().toISOString();\n fs.writeFileSync(\n path.join(quotesDir(dataDir), `${quoteNumber}.json`),\n JSON.stringify(updated, null, 2),\n \"utf-8\"\n );\n}\n\nexport async function generateQuote(dataDir: string, input: GenerateQuoteInput): Promise<Quote> {\n const config = loadQuoteConfig(dataDir);\n const vatPercent = input.vatPercent ?? 19;\n const validUntilDays = input.validUntilDays ?? 30;\n const currency = input.currency ?? config.currency ?? \"EUR\";\n\n const items: QuoteLineItem[] = input.lineItems.map((item) => ({\n description: item.description,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n total: item.quantity * item.unitPrice,\n }));\n\n const subtotal = items.reduce((sum, i) => sum + i.total, 0);\n const vat = Math.round(subtotal * (vatPercent / 100) * 100) / 100;\n const total = Math.round((subtotal + vat) * 100) / 100;\n\n const quoteNumber = nextQuoteNumber(dataDir);\n const now = new Date().toISOString();\n const validUntil = addDaysToDate(now.slice(0, 10), validUntilDays);\n\n const dir = quotesDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n\n const htmlPath = path.join(dir, `${quoteNumber}.html`);\n\n const quote: Quote = {\n quoteNumber,\n slug: input.slug,\n dealName: input.dealName,\n lineItems: items,\n subtotal,\n vatPercent,\n vat,\n total,\n currency,\n createdAt: now,\n validUntilDays,\n validUntil,\n status: \"draft\",\n htmlPath,\n };\n\n fs.writeFileSync(path.join(dir, `${quoteNumber}.json`), JSON.stringify(quote, null, 2), \"utf-8\");\n\n const customerName = readCustomerName(dataDir, input.slug);\n const html = buildHtml(quote, config, customerName);\n fs.writeFileSync(htmlPath, html, \"utf-8\");\n\n return quote;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { generateQuote } from \"../../core/quote-generator.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGenerateQuote(\n input: {\n slug: string;\n dealName: string;\n lineItems: Array<{ description: string; quantity: number; unitPrice: number }>;\n vatPercent?: number;\n validUntilDays?: number;\n currency?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const quote = await generateQuote(dataDir, input);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n quoteNumber: quote.quoteNumber,\n htmlPath: quote.htmlPath,\n total: quote.total,\n subtotal: quote.subtotal,\n vat: quote.vat,\n vatPercent: quote.vatPercent,\n currency: quote.currency,\n validUntil: quote.validUntil,\n status: quote.status,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: JSON.stringify({ error: (err as Error).message }) }],\n };\n }\n}\n\nexport function registerGenerateQuote(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"generate_quote\",\n {\n description: `Generate a professional HTML quote/offer for a customer deal.\nCalculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.\nReturns: { quoteNumber, htmlPath, total, currency, validUntil }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().describe(\"Name of the deal this quote is for\"),\n lineItems: z\n .array(\n z.object({\n description: z.string(),\n quantity: z.number().positive(),\n unitPrice: z.number().min(0),\n })\n )\n .min(1)\n .describe(\"Line items for the quote\"),\n vatPercent: z.number().min(0).max(100).optional().describe(\"VAT percentage (default 19)\"),\n validUntilDays: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Quote validity in days (default 30)\"),\n currency: z.string().optional().describe(\"Currency code (default EUR)\"),\n }),\n },\n ({ slug, dealName, lineItems, vatPercent, validUntilDays, currency }) =>\n handleGenerateQuote(\n {\n slug,\n dealName,\n lineItems,\n ...(vatPercent !== undefined ? { vatPercent } : {}),\n ...(validUntilDays !== undefined ? { validUntilDays } : {}),\n ...(currency !== undefined ? { currency } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readQuote, listQuotes } from \"../../core/quote-generator.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetQuoteStatus(\n input: { quoteNumber?: string; slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n if (input.quoteNumber) {\n const quote = readQuote(dataDir, input.quoteNumber);\n if (!quote) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Quote '${input.quoteNumber}' not found` }),\n },\n ],\n };\n }\n return { content: [{ type: \"text\", text: JSON.stringify(quote, null, 2) }] };\n }\n\n const quotes = listQuotes(dataDir, input.slug);\n return { content: [{ type: \"text\", text: JSON.stringify({ quotes }, null, 2) }] };\n}\n\nexport function registerGetQuoteStatus(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_quote_status\",\n {\n description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).\nReturns quote with status: draft | sent | viewed | accepted | declined`,\n inputSchema: z.object({\n quoteNumber: z.string().optional().describe(\"Specific quote number (e.g. Q-2026-001)\"),\n slug: z.string().optional().describe(\"Customer slug to list all quotes for\"),\n }),\n },\n ({ quoteNumber, slug }) =>\n handleGetQuoteStatus(\n {\n ...(quoteNumber !== undefined ? { quoteNumber } : {}),\n ...(slug !== undefined ? { slug } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\ninterface CalendlyConfig {\n apiKey?: string;\n defaultEventType?: string;\n autoLogMeetings?: boolean;\n}\n\nfunction loadCalendlyConfig(dataDir: string): CalendlyConfig {\n const p = path.join(dataDir, \".agentic\", \"integrations\", \"calendly.yaml\");\n if (!fs.existsSync(p)) return {};\n try {\n return (yaml.load(fs.readFileSync(p, \"utf-8\") as string) as CalendlyConfig) ?? {};\n } catch {\n return {};\n }\n}\n\nfunction readCustomerFacts(dataDir: string, slug: string): { name?: string; email?: string } {\n const p = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (!fs.existsSync(p)) return {};\n const content = fs.readFileSync(p, \"utf-8\") as string;\n const nameMatch = /^name:\\s*(.+)$/m.exec(content);\n const emailMatch = /^email:\\s*(.+)$/m.exec(content);\n const name = nameMatch?.[1]?.trim();\n const email = emailMatch?.[1]?.trim();\n return {\n ...(name ? { name } : {}),\n ...(email ? { email } : {}),\n };\n}\n\nexport async function handleGetBookingLink(\n input: { slug: string; eventType?: string; prefillName?: boolean },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const config = loadCalendlyConfig(dataDir);\n const apiKey = config.apiKey ?? process.env[\"CALENDLY_API_KEY\"] ?? \"\";\n\n if (!apiKey) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error:\n \"Calendly API key not configured. Set CALENDLY_API_KEY env var or configure .agentic/integrations/calendly.yaml\",\n }),\n },\n ],\n };\n }\n\n const eventTypeSlug = input.eventType ?? config.defaultEventType ?? \"30min\";\n\n try {\n const { getSchedulingLink, listEventTypes } = await import(\"../../sync/calendly.js\");\n const prefill = input.prefillName ? readCustomerFacts(dataDir, input.slug) : undefined;\n const bookingUrl = await getSchedulingLink(apiKey, eventTypeSlug, prefill);\n\n const eventTypes = await listEventTypes(apiKey);\n const eventType = eventTypes.find(\n (et) =>\n et.slug === eventTypeSlug || et.name.toLowerCase().includes(eventTypeSlug.toLowerCase())\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n bookingUrl,\n eventType: eventType?.name ?? eventTypeSlug,\n duration: eventType?.duration ?? 30,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: JSON.stringify({ error: (err as Error).message }) }],\n };\n }\n}\n\nexport function registerGetBookingLink(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_booking_link\",\n {\n description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.\nRequires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.\nReturns: { bookingUrl, eventType, duration }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n eventType: z\n .string()\n .optional()\n .describe(\n \"Calendly event type slug (e.g. '30min', '60min'). Uses default if not specified.\"\n ),\n prefillName: z\n .boolean()\n .optional()\n .describe(\"Pre-fill customer name and email in the booking link\"),\n }),\n },\n ({ slug, eventType, prefillName }) =>\n handleGetBookingLink(\n {\n slug,\n ...(eventType !== undefined ? { eventType } : {}),\n ...(prefillName !== undefined ? { prefillName } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const TicketStatusSchema = z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]);\nexport const TicketPrioritySchema = z.enum([\"urgent\", \"high\", \"normal\", \"low\"]);\n\nexport const TicketSchema = z.object({\n id: z.string().regex(/^T-\\d{3,}$/),\n title: z.string().min(1),\n status: TicketStatusSchema,\n priority: TicketPrioritySchema.default(\"normal\"),\n assignee: z.string().optional(),\n created: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n slaDue: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n resolved: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n description: z.string().optional(),\n});\n\nexport type Ticket = z.infer<typeof TicketSchema>;\nexport type TicketStatus = z.infer<typeof TicketStatusSchema>;\nexport type TicketPriority = z.infer<typeof TicketPrioritySchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { TicketSchema, type Ticket } from \"../schemas/ticket.js\";\n\nconst TICKET_HEADER = \"# Tickets\\n\\n\";\nconst TABLE_HEADER = `| ID | Title | Status | Priority | Assignee | Created | SLA Due | Resolved |\n|----|-------|--------|----------|----------|---------|---------|---------|`;\n\nfunction ticketsPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tickets.md\");\n}\n\nfunction escapeMd(s: string | undefined): string {\n return (s ?? \"\").replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n}\n\nfunction parseTicketsFromMarkdown(content: string): Ticket[] {\n const tickets: Ticket[] = [];\n const lines = content.split(\"\\n\");\n let inTable = false;\n\n for (const line of lines) {\n if (line.startsWith(\"| ID |\") || line.startsWith(\"|----\")) {\n inTable = true;\n continue;\n }\n if (!inTable) continue;\n if (!line.startsWith(\"|\")) {\n inTable = false;\n continue;\n }\n\n const cols = line\n .split(\"|\")\n .slice(1, -1)\n .map((c) => c.trim());\n if (cols.length < 8) continue;\n const [id, title, status, priority, assignee, created, slaDue, resolved] = cols;\n if (!id || !title || id === \"ID\") continue;\n\n const raw = {\n id,\n title,\n status: status || \"open\",\n priority: priority || \"normal\",\n ...(assignee ? { assignee } : {}),\n created: created || new Date().toISOString().slice(0, 10),\n ...(slaDue ? { slaDue } : {}),\n ...(resolved ? { resolved } : {}),\n };\n\n const parsed = TicketSchema.safeParse(raw);\n if (parsed.success) tickets.push(parsed.data);\n }\n return tickets;\n}\n\nfunction serializeTickets(tickets: Ticket[]): string {\n const rows = tickets.map(\n (t) =>\n `| ${t.id} | ${escapeMd(t.title)} | ${t.status} | ${t.priority} | ${t.assignee ?? \"\"} | ${t.created} | ${t.slaDue ?? \"\"} | ${t.resolved ?? \"\"} |`\n );\n return `${TICKET_HEADER}${TABLE_HEADER}\\n${rows.join(\"\\n\")}\\n`;\n}\n\nexport async function readTickets(dataDir: string, slug: string): Promise<Ticket[]> {\n const p = ticketsPath(dataDir, slug);\n if (!fs.existsSync(p)) return [];\n return parseTicketsFromMarkdown(fs.readFileSync(p, \"utf-8\") as string);\n}\n\nexport async function upsertTicket(dataDir: string, slug: string, ticket: Ticket): Promise<void> {\n const p = ticketsPath(dataDir, slug);\n const existing = await readTickets(dataDir, slug);\n const idx = existing.findIndex((t) => t.id === ticket.id);\n if (idx >= 0) {\n existing[idx] = ticket;\n } else {\n existing.push(ticket);\n }\n fs.writeFileSync(p, serializeTickets(existing), \"utf-8\");\n}\n\nexport function nextTicketId(tickets: Ticket[]): string {\n const nums = tickets.map((t) => parseInt(t.id.replace(\"T-\", \"\"), 10)).filter((n) => !isNaN(n));\n const max = nums.length > 0 ? Math.max(...nums) : 0;\n return `T-${String(max + 1).padStart(3, \"0\")}`;\n}\n\nexport async function listAllTickets(\n dataDir: string,\n filter?: { slug?: string; status?: string; priority?: string; assignee?: string }\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) return [];\n\n const slugs = filter?.slug\n ? [filter.slug]\n : fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const results: Array<{ slug: string; ticket: Ticket }> = [];\n for (const slug of slugs) {\n const tickets = await readTickets(dataDir, slug);\n for (const ticket of tickets) {\n if (filter?.status && ticket.status !== filter.status) continue;\n if (filter?.priority && ticket.priority !== filter.priority) continue;\n if (filter?.assignee && ticket.assignee !== filter.assignee) continue;\n results.push({ slug, ticket });\n }\n }\n\n // Sort: urgent first, then by created date\n const priorityOrder: Record<string, number> = { urgent: 0, high: 1, normal: 2, low: 3 };\n results.sort((a, b) => {\n const pa = priorityOrder[a.ticket.priority] ?? 2;\n const pb = priorityOrder[b.ticket.priority] ?? 2;\n if (pa !== pb) return pa - pb;\n return a.ticket.created.localeCompare(b.ticket.created);\n });\n\n return results;\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type { Ticket } from \"../schemas/ticket.js\";\n\nexport interface SlaRule {\n priority: Ticket[\"priority\"];\n resolveDays: number;\n}\n\nconst DEFAULT_RULES: SlaRule[] = [\n { priority: \"urgent\", resolveDays: 1 },\n { priority: \"high\", resolveDays: 2 },\n { priority: \"normal\", resolveDays: 5 },\n { priority: \"low\", resolveDays: 10 },\n];\n\nexport function loadSlaRules(dataDir: string): SlaRule[] {\n const p = path.join(dataDir, \".agentic\", \"sla-rules.yaml\");\n if (!fs.existsSync(p)) return DEFAULT_RULES;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string) as { rules?: SlaRule[] };\n return raw?.rules ?? DEFAULT_RULES;\n } catch {\n return DEFAULT_RULES;\n }\n}\n\nfunction addDaysToDate(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.split(\"-\").map(Number) as [number, number, number];\n const d = new Date(Date.UTC(year, month - 1, day));\n d.setUTCDate(d.getUTCDate() + days);\n return d.toISOString().slice(0, 10);\n}\n\nexport function calcSlaDue(\n createdDate: string,\n priority: Ticket[\"priority\"],\n rules: SlaRule[]\n): string {\n const rule = rules.find((r) => r.priority === priority) ?? { resolveDays: 5 };\n return addDaysToDate(createdDate, rule.resolveDays);\n}\n\nexport function isSlaBreach(ticket: Ticket, today: string): boolean {\n if (ticket.status === \"resolved\" || ticket.status === \"closed\") return false;\n if (!ticket.slaDue) return false;\n return ticket.slaDue < today;\n}\n\nexport async function checkSlaBreaches(\n dataDir: string,\n today: string\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const { listAllTickets } = await import(\"../fs/ticket-writer.js\");\n const all = await listAllTickets(dataDir);\n return all.filter(({ ticket }) => isSlaBreach(ticket, today));\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readTickets, upsertTicket, nextTicketId } from \"../../fs/ticket-writer.js\";\nimport { calcSlaDue, loadSlaRules } from \"../../core/sla-engine.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCreateTicket(\n input: {\n slug: string;\n title: string;\n description?: string;\n priority?: Ticket[\"priority\"];\n assignee?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const today = new Date().toISOString().slice(0, 10);\n const rules = loadSlaRules(dataDir);\n const priority = input.priority ?? \"normal\";\n const existing = await readTickets(dataDir, input.slug);\n const id = nextTicketId(existing);\n const slaDue = calcSlaDue(today, priority, rules);\n\n const ticket: Ticket = {\n id,\n title: input.title,\n status: \"open\",\n priority,\n ...(input.assignee ? { assignee: input.assignee } : {}),\n created: today,\n slaDue,\n ...(input.description ? { description: input.description } : {}),\n };\n\n await upsertTicket(dataDir, input.slug, ticket);\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ ticket }, null, 2) }],\n };\n}\n\nexport function registerCreateTicket(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"create_ticket\",\n {\n description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.\nReturns: { ticket } with id T-NNN, status=open, slaDue`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n title: z.string().min(1).describe(\"Ticket title\"),\n description: z.string().optional().describe(\"Detailed description\"),\n priority: z\n .enum([\"urgent\", \"high\", \"normal\", \"low\"])\n .optional()\n .describe(\"Priority (default: normal)\"),\n assignee: z.string().optional().describe(\"Assignee name or email\"),\n }),\n },\n ({ slug, title, description, priority, assignee }) =>\n handleCreateTicket(\n {\n slug,\n title,\n ...(description !== undefined ? { description } : {}),\n ...(priority !== undefined ? { priority } : {}),\n ...(assignee !== undefined ? { assignee } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readTickets, upsertTicket } from \"../../fs/ticket-writer.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUpdateTicket(\n input: { slug: string; ticketId: string; status?: Ticket[\"status\"]; assignee?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tickets = await readTickets(dataDir, input.slug);\n const ticket = tickets.find((t) => t.id === input.ticketId);\n\n if (!ticket) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error: `Ticket '${input.ticketId}' not found for customer '${input.slug}'`,\n }),\n },\n ],\n };\n }\n\n const today = new Date().toISOString().slice(0, 10);\n const updated: Ticket = {\n ...ticket,\n ...(input.status ? { status: input.status } : {}),\n ...(input.assignee !== undefined ? { assignee: input.assignee } : {}),\n ...(input.status === \"resolved\" && !ticket.resolved ? { resolved: today } : {}),\n };\n\n await upsertTicket(dataDir, input.slug, updated);\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ ticket: updated }, null, 2) }],\n };\n}\n\nexport function registerUpdateTicket(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"update_ticket\",\n {\n description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.\nReturns: { ticket }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n ticketId: z.string().describe(\"Ticket ID (e.g. T-001)\"),\n status: z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]).optional(),\n assignee: z.string().optional().describe(\"New assignee\"),\n }),\n },\n ({ slug, ticketId, status, assignee }) =>\n handleUpdateTicket(\n {\n slug,\n ticketId,\n ...(status !== undefined ? { status } : {}),\n ...(assignee !== undefined ? { assignee } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listAllTickets } from \"../../fs/ticket-writer.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListTickets(\n input: {\n slug?: string;\n status?: Ticket[\"status\"];\n priority?: Ticket[\"priority\"];\n assignee?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const results = await listAllTickets(dataDir, {\n ...(input.slug !== undefined ? { slug: input.slug } : {}),\n ...(input.status !== undefined ? { status: input.status } : {}),\n ...(input.priority !== undefined ? { priority: input.priority } : {}),\n ...(input.assignee !== undefined ? { assignee: input.assignee } : {}),\n });\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ tickets: results }, null, 2) }],\n };\n}\n\nexport function registerListTickets(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"list_tickets\",\n {\n description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.\nReturns: { tickets: Array<{ slug, ticket }> }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n status: z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]).optional(),\n priority: z.enum([\"urgent\", \"high\", \"normal\", \"low\"]).optional(),\n assignee: z.string().optional().describe(\"Filter by assignee\"),\n }),\n },\n ({ slug, status, priority, assignee }) =>\n handleListTickets(\n {\n ...(slug !== undefined ? { slug } : {}),\n ...(status !== undefined ? { status } : {}),\n ...(priority !== undefined ? { priority } : {}),\n ...(assignee !== undefined ? { assignee } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readTickets, upsertTicket } from \"../../fs/ticket-writer.js\";\nimport { appendInteraction } from \"../../fs/interactions-writer.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCloseTicket(\n input: { slug: string; ticketId: string; resolution?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tickets = await readTickets(dataDir, input.slug);\n const ticket = tickets.find((t) => t.id === input.ticketId);\n\n if (!ticket) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error: `Ticket '${input.ticketId}' not found for '${input.slug}'`,\n }),\n },\n ],\n };\n }\n\n const today = new Date().toISOString().slice(0, 10);\n const updated: Ticket = {\n ...ticket,\n status: \"closed\",\n resolved: ticket.resolved ?? today,\n };\n\n await upsertTicket(dataDir, input.slug, updated);\n\n if (input.resolution) {\n await appendInteraction(dataDir, input.slug, {\n date: today,\n type: \"Note\",\n with: input.slug,\n summary: `Ticket ${input.ticketId} closed: ${input.resolution}`,\n nextSteps: [],\n sourceRef: `ticket://${input.ticketId}/close`,\n synced: new Date().toISOString(),\n });\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ ticket: updated }, null, 2) }],\n };\n}\n\nexport function registerCloseTicket(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"close_ticket\",\n {\n description: `Close a support ticket. Optionally logs the resolution as an interaction.\nReturns: { ticket } with status=closed`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n ticketId: z.string().describe(\"Ticket ID (e.g. T-001)\"),\n resolution: z.string().optional().describe(\"Resolution notes (logged as interaction)\"),\n }),\n },\n ({ slug, ticketId, resolution }) =>\n handleCloseTicket(\n {\n slug,\n ticketId,\n ...(resolution !== undefined ? { resolution } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const SurveyDefinitionSchema = z.object({\n id: z.string().min(1),\n type: z.enum([\"nps\", \"csat\", \"ces\"]).default(\"nps\"),\n question: z.string().min(1),\n scale: z\n .object({ min: z.number().default(0), max: z.number().default(10) })\n .default({ min: 0, max: 10 }),\n includeComment: z.boolean().default(true),\n commentPrompt: z.string().optional(),\n createdAt: z.string(),\n});\n\nexport const SurveyResponseSchema = z.object({\n surveyId: z.string(),\n slug: z.string(),\n contactEmail: z.string().email(),\n score: z.number().int(),\n comment: z.string().optional(),\n respondedAt: z.string(),\n token: z.string(),\n sentAt: z.string(),\n});\n\nexport type SurveyDefinition = z.infer<typeof SurveyDefinitionSchema>;\nexport type SurveyResponse = z.infer<typeof SurveyResponseSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { createHmac } from \"crypto\";\nimport yaml from \"js-yaml\";\nimport {\n SurveyDefinitionSchema,\n SurveyResponseSchema,\n type SurveyDefinition,\n type SurveyResponse,\n} from \"../schemas/survey.js\";\n\nconst SURVEY_SECRET = process.env[\"DXCRM_SURVEY_SECRET\"] ?? \"dxcrm-survey-default-secret\";\n\nexport function surveysDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"surveys\");\n}\n\nexport function responsesDir(dataDir: string, surveyId: string): string {\n return path.join(dataDir, \".agentic\", \"survey-responses\", surveyId);\n}\n\nexport function getSurvey(dataDir: string, surveyId: string): SurveyDefinition | null {\n const p = path.join(surveysDir(dataDir), `${surveyId}.yaml`);\n if (!fs.existsSync(p)) return null;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string);\n const result = SurveyDefinitionSchema.safeParse(raw);\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\nexport function writeSurvey(dataDir: string, survey: SurveyDefinition): void {\n const dir = surveysDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey), \"utf-8\");\n}\n\nexport function listSurveys(dataDir: string): SurveyDefinition[] {\n const dir = surveysDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".yaml\"))\n .flatMap((f) => {\n const s = getSurvey(dataDir, f.replace(/\\.yaml$/, \"\"));\n return s ? [s] : [];\n });\n}\n\nexport function generateSurveyToken(slug: string, contactEmail: string, surveyId: string): string {\n return createHmac(\"sha256\", SURVEY_SECRET)\n .update(`${slug}:${contactEmail}:${surveyId}`)\n .digest(\"hex\")\n .slice(0, 16);\n}\n\nexport function buildSurveyEmail(\n survey: SurveyDefinition,\n serverUrl: string,\n token: string\n): { subject: string; body: string } {\n const scores = Array.from(\n { length: survey.scale.max - survey.scale.min + 1 },\n (_, i) => i + survey.scale.min\n );\n const buttons = scores\n .map(\n (s) =>\n `<a href=\"${serverUrl}/survey/respond?token=${token}&score=${s}\" style=\"display:inline-block;margin:4px;padding:10px 16px;background:#1a1a2e;color:white;text-decoration:none;border-radius:4px;\">${s}</a>`\n )\n .join(\"\");\n\n const body = `<p>${survey.question}</p>\n<p>${buttons}</p>\n${survey.includeComment ? `<p>Or <a href=\"${serverUrl}/survey/respond?token=${token}&comment=true\">Click here to add a comment</a></p>` : \"\"}`;\n\n return {\n subject: survey.type === \"nps\" ? \"How likely are you to recommend us?\" : \"Rate your experience\",\n body,\n };\n}\n\nexport async function recordSurveyResponse(\n dataDir: string,\n token: string,\n score: number,\n comment?: string\n): Promise<SurveyResponse | null> {\n // Find pending response by token\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n if (!fs.existsSync(pendingDir)) return null;\n\n const files = fs.readdirSync(pendingDir).filter((f) => f.endsWith(\".json\"));\n for (const file of files) {\n try {\n const pending = JSON.parse(\n fs.readFileSync(path.join(pendingDir, file), \"utf-8\") as string\n ) as {\n token: string;\n surveyId: string;\n slug: string;\n contactEmail: string;\n sentAt: string;\n };\n if (pending.token !== token) continue;\n\n const response: SurveyResponse = {\n surveyId: pending.surveyId,\n slug: pending.slug,\n contactEmail: pending.contactEmail,\n score,\n ...(comment ? { comment } : {}),\n respondedAt: new Date().toISOString(),\n token,\n sentAt: pending.sentAt,\n };\n\n const dir = responsesDir(dataDir, pending.surveyId);\n fs.mkdirSync(dir, { recursive: true });\n const filename = `${pending.slug}_${pending.contactEmail.replace(\"@\", \"_at_\")}_${Date.now()}.json`;\n fs.writeFileSync(path.join(dir, filename), JSON.stringify(response, null, 2), \"utf-8\");\n\n // Delete pending entry\n fs.unlinkSync(path.join(pendingDir, file));\n return response;\n } catch {\n continue;\n }\n }\n return null;\n}\n\nexport function loadSurveyResponses(\n dataDir: string,\n surveyId: string,\n slug?: string\n): SurveyResponse[] {\n const dir = responsesDir(dataDir, surveyId);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .flatMap((f) => {\n try {\n const raw = JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\") as string) as unknown;\n const parsed = SurveyResponseSchema.safeParse(raw);\n if (!parsed.success) return [];\n if (slug && parsed.data.slug !== slug) return [];\n return [parsed.data];\n } catch {\n return [];\n }\n });\n}\n\nexport function calcNpsScore(responses: SurveyResponse[]): number {\n if (responses.length === 0) return 0;\n const promoters = responses.filter((r) => r.score >= 9).length;\n const detractors = responses.filter((r) => r.score <= 6).length;\n return Math.round(((promoters - detractors) / responses.length) * 100);\n}\n\nexport async function savePendingSurvey(\n dataDir: string,\n surveyId: string,\n slug: string,\n contactEmail: string,\n token: string\n): Promise<void> {\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n fs.mkdirSync(pendingDir, { recursive: true });\n const filename = `${token}.json`;\n const pending = { token, surveyId, slug, contactEmail, sentAt: new Date().toISOString() };\n fs.writeFileSync(path.join(pendingDir, filename), JSON.stringify(pending, null, 2), \"utf-8\");\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n getSurvey,\n generateSurveyToken,\n buildSurveyEmail,\n savePendingSurvey,\n} from \"../../core/survey-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSendNpsSurvey(\n input: { slug: string; contactEmail: string; surveyId: string; serverUrl?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const survey = getSurvey(dataDir, input.surveyId);\n if (!survey) {\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ error: `Survey '${input.surveyId}' not found` }) },\n ],\n };\n }\n\n const serverUrl = input.serverUrl ?? process.env[\"DXCRM_SERVER_URL\"] ?? \"http://localhost:3456\";\n const token = generateSurveyToken(input.slug, input.contactEmail, input.surveyId);\n const email = buildSurveyEmail(survey, serverUrl, token);\n\n await savePendingSurvey(dataDir, input.surveyId, input.slug, input.contactEmail, token);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n token,\n subject: email.subject,\n body: email.body,\n surveyUrl: `${serverUrl}/survey/respond?token=${token}`,\n note: \"Email draft ready. Use draft_email or Gmail to send.\",\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerSendNpsSurvey(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"send_nps_survey\",\n {\n description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.\nDoes NOT send automatically — returns draft for review.\nReturns: { token, subject, body, surveyUrl }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n contactEmail: z.string().email().describe(\"Contact email to send survey to\"),\n surveyId: z.string().describe(\"Survey definition ID from .agentic/surveys/\"),\n serverUrl: z\n .string()\n .optional()\n .describe(\n \"Server URL for response links (default: DXCRM_SERVER_URL env var or localhost:3456)\"\n ),\n }),\n },\n ({ slug, contactEmail, surveyId, serverUrl }) =>\n handleSendNpsSurvey(\n {\n slug,\n contactEmail,\n surveyId,\n ...(serverUrl !== undefined ? { serverUrl } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { loadSurveyResponses, calcNpsScore } from \"../../core/survey-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetSurveyResults(\n input: { surveyId: string; slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);\n const nps = calcNpsScore(responses);\n const promoters = responses.filter((r) => r.score >= 9).length;\n const passives = responses.filter((r) => r.score >= 7 && r.score <= 8).length;\n const detractors = responses.filter((r) => r.score <= 6).length;\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n surveyId: input.surveyId,\n ...(input.slug ? { slug: input.slug } : {}),\n totalResponses: responses.length,\n npsScore: nps,\n promoters,\n passives,\n detractors,\n responses: responses.map((r) => ({\n slug: r.slug,\n email: r.contactEmail,\n score: r.score,\n ...(r.comment ? { comment: r.comment } : {}),\n respondedAt: r.respondedAt,\n })),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerGetSurveyResults(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_survey_results\",\n {\n description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.\nReturns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,\n inputSchema: z.object({\n surveyId: z.string().describe(\"Survey ID\"),\n slug: z.string().optional().describe(\"Filter results to a specific customer\"),\n }),\n },\n ({ surveyId, slug }) =>\n handleGetSurveyResults(\n {\n surveyId,\n ...(slug !== undefined ? { slug } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const KbArticleSchema = z.object({\n id: z.string().min(1),\n title: z.string().min(1),\n category: z.string().default(\"general\"),\n tags: z.array(z.string()).default([]),\n public: z.boolean().default(false),\n createdAt: z.string(),\n updatedAt: z.string(),\n sourceTicketId: z.string().optional(),\n});\n\nexport type KbArticleMeta = z.infer<typeof KbArticleSchema>;\nexport type KbArticle = KbArticleMeta & { body: string };\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { KbArticleSchema, type KbArticle, type KbArticleMeta } from \"../schemas/kb-article.js\";\n\nexport function kbDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"knowledge-base\");\n}\n\nexport function listKbArticles(\n dataDir: string,\n opts?: { category?: string; publicOnly?: boolean }\n): KbArticle[] {\n const dir = kbDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n\n const results: KbArticle[] = [];\n const categories = fs.readdirSync(dir).filter((f) => {\n try {\n return fs.statSync(path.join(dir, f)).isDirectory();\n } catch {\n return false;\n }\n });\n\n for (const cat of categories) {\n const catDir = path.join(dir, cat);\n const files = fs.readdirSync(catDir).filter((f) => f.endsWith(\".md\"));\n for (const file of files) {\n try {\n const content = fs.readFileSync(path.join(catDir, file), \"utf-8\") as string;\n const parsed = matter(content);\n const meta = KbArticleSchema.safeParse(parsed.data);\n if (!meta.success) continue;\n if (opts?.category && meta.data.category !== opts.category) continue;\n if (opts?.publicOnly && !meta.data.public) continue;\n results.push({ ...meta.data, body: parsed.content.trim() });\n } catch {\n continue;\n }\n }\n }\n\n return results;\n}\n\nexport function getKbArticle(dataDir: string, id: string): KbArticle | null {\n const all = listKbArticles(dataDir);\n return all.find((a) => a.id === id) ?? null;\n}\n\nexport function writeKbArticle(dataDir: string, article: KbArticle): void {\n const dir = path.join(kbDir(dataDir), article.category);\n fs.mkdirSync(dir, { recursive: true });\n const { body, ...meta } = article;\n const content = matter.stringify(body, meta as Record<string, unknown>);\n fs.writeFileSync(path.join(dir, `${article.id}.md`), content, \"utf-8\");\n}\n\nexport function deleteKbArticle(dataDir: string, id: string): boolean {\n const all = listKbArticles(dataDir);\n const article = all.find((a) => a.id === id);\n if (!article) return false;\n const p = path.join(kbDir(dataDir), article.category, `${id}.md`);\n if (!fs.existsSync(p)) return false;\n fs.unlinkSync(p);\n return true;\n}\n\nexport function searchKbSimple(\n dataDir: string,\n query: string,\n opts?: { publicOnly?: boolean }\n): KbArticle[] {\n const all = listKbArticles(dataDir, opts?.publicOnly ? { publicOnly: true } : {});\n const lower = query.toLowerCase();\n return all.filter(\n (a) =>\n a.title.toLowerCase().includes(lower) ||\n a.body.toLowerCase().includes(lower) ||\n a.tags.some((t) => t.toLowerCase().includes(lower))\n );\n}\n\nexport function getKbMetaForExport(article: KbArticle): KbArticleMeta {\n const { body: _body, ...meta } = article;\n return meta;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { searchKbSimple, getKbMetaForExport } from \"../../fs/knowledge-base.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSearchKnowledgeBase(\n input: { query: string; category?: string; publicOnly?: boolean; limit?: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const results = searchKbSimple(dataDir, input.query, {\n ...(input.publicOnly ? { publicOnly: true } : {}),\n });\n\n const filtered = input.category ? results.filter((a) => a.category === input.category) : results;\n const limited = filtered.slice(0, input.limit ?? 10);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n query: input.query,\n count: limited.length,\n articles: limited.map((a) => ({\n ...getKbMetaForExport(a),\n excerpt: a.body.slice(0, 300).trim(),\n })),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerSearchKnowledgeBase(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"search_knowledge_base\",\n {\n description: `Search the knowledge base for articles. Text search on title, body, and tags.\nReturns: { count, articles[] } with excerpts`,\n inputSchema: z.object({\n query: z.string().describe(\"Search query\"),\n category: z\n .string()\n .optional()\n .describe(\"Filter by category (e.g. 'troubleshooting', 'howto')\"),\n publicOnly: z.boolean().optional().describe(\"Only return public articles\"),\n limit: z.number().int().positive().optional().describe(\"Max results (default 10)\"),\n }),\n },\n ({ query, category, publicOnly, limit }) =>\n handleSearchKnowledgeBase(\n {\n query,\n ...(category !== undefined ? { category } : {}),\n ...(publicOnly !== undefined ? { publicOnly } : {}),\n ...(limit !== undefined ? { limit } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { writeKbArticle, getKbArticle } from \"../../fs/knowledge-base.js\";\nimport type { KbArticle } from \"../../schemas/kb-article.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCreateKbArticle(\n input: {\n id: string;\n title: string;\n body: string;\n category?: string;\n tags?: string[];\n public?: boolean;\n sourceTicketId?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const existing = getKbArticle(dataDir, input.id);\n if (existing) {\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ error: `Article '${input.id}' already exists` }) },\n ],\n };\n }\n\n const now = new Date().toISOString();\n const article: KbArticle = {\n id: input.id,\n title: input.title,\n body: input.body,\n category: input.category ?? \"general\",\n tags: input.tags ?? [],\n public: input.public ?? false,\n createdAt: now,\n updatedAt: now,\n ...(input.sourceTicketId ? { sourceTicketId: input.sourceTicketId } : {}),\n };\n\n writeKbArticle(dataDir, article);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n id: article.id,\n title: article.title,\n category: article.category,\n path: `.agentic/knowledge-base/${article.category}/${article.id}.md`,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerCreateKbArticle(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"create_kb_article\",\n {\n description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.\nReturns: { id, title, category, path }`,\n inputSchema: z.object({\n id: z.string().min(1).describe(\"Article ID (slug, e.g. 'troubleshoot-api-timeout')\"),\n title: z.string().min(1).describe(\"Article title\"),\n body: z.string().min(1).describe(\"Article body in Markdown\"),\n category: z.string().optional().describe(\"Category (default: 'general')\"),\n tags: z.array(z.string()).optional().describe(\"Tags for search\"),\n public: z\n .boolean()\n .optional()\n .describe(\"Make article publicly accessible (default: false)\"),\n sourceTicketId: z.string().optional().describe(\"Ticket ID this article was created from\"),\n }),\n },\n ({ id, title, body, category, tags, public: pub, sourceTicketId }) =>\n handleCreateKbArticle(\n {\n id,\n title,\n body,\n ...(category !== undefined ? { category } : {}),\n ...(tags !== undefined ? { tags } : {}),\n ...(pub !== undefined ? { public: pub } : {}),\n ...(sourceTicketId !== undefined ? { sourceTicketId } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { runBackup } from \"../../commands/backup.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleBackupNow(\n input: { remote?: string; note?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const zipPath = path.join(\n dataDir,\n `dxcrm-backup-${new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19)}.zip`\n );\n\n const manifest = await runBackup(zipPath, dataDir, {\n ...(input.remote ? { remote: input.remote } : {}),\n }).catch(() => null);\n\n if (!manifest) {\n return {\n content: [{ type: \"text\", text: \"Backup failed. Check disk space and permissions.\" }],\n };\n }\n\n const sizeMb = fs.existsSync(zipPath)\n ? (fs.statSync(zipPath).size / 1024 / 1024).toFixed(1)\n : \"?\";\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n path: zipPath,\n createdAt: manifest.createdAt,\n customerCount: manifest.customerCount,\n fileCount: manifest.fileCount,\n sizeMb: `${sizeMb} MB`,\n directories: manifest.directories,\n verified: true,\n ...(input.remote ? { uploadedTo: input.remote } : {}),\n ...(input.note ? { note: input.note } : {}),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerBackupNow(server: McpServer): void {\n server.registerTool(\n \"backup_now\",\n {\n description:\n \"Trigger an immediate backup of all CRM data (customers/ + .agentic/). Returns backup path, size, and integrity status. Use before risky operations or on user request.\",\n inputSchema: z.object({\n remote: z\n .string()\n .optional()\n .describe(\n \"Upload destination: s3://bucket/path/, rsync://user@host:/path/, or local directory\"\n ),\n note: z.string().optional().describe(\"Optional note to tag this backup\"),\n }),\n },\n ({ remote, note }) =>\n handleBackupNow({\n ...(remote !== undefined ? { remote } : {}),\n ...(note !== undefined ? { note } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readBackupLog, listBackupsInDir } from \"../../commands/backup.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListBackups(\n input: { limit: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const logEntries = readBackupLog(dataDir);\n const fileEntries = listBackupsInDir(dataDir);\n\n // Prefer log entries (have richer metadata), fallback to file scan\n const entries = logEntries.length > 0 ? logEntries : fileEntries;\n const limited = entries.slice(0, input.limit);\n\n if (limited.length === 0) {\n return {\n content: [{ type: \"text\", text: \"No backups found. Run backup_now to create one.\" }],\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n count: limited.length,\n totalAvailable: entries.length,\n backups: limited.map((e) => ({\n filename: e.filename,\n createdAt: e.createdAt,\n sizeMb: e.sizeBytes > 0 ? `${(e.sizeBytes / 1024 / 1024).toFixed(1)} MB` : \"unknown\",\n verified: e.verified,\n encrypted: e.encrypted,\n customerCount: e.customerCount,\n fileCount: e.fileCount,\n })),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerListBackups(server: McpServer): void {\n server.registerTool(\n \"list_backups\",\n {\n description:\n \"List available CRM backups with metadata (date, size, verification status, customer count). Shows log-tracked backups first, falls back to directory scan.\",\n inputSchema: z.object({\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .default(10)\n .describe(\"Maximum number of backups to return\"),\n }),\n },\n (input) => handleListBackups(input)\n );\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getGmailAuth } from \"../../core/oauth-store.js\";\nimport { updateSlugSyncState } from \"../../fs/sync-state.js\";\n\nconst DATA_DIR = process.cwd();\n\nexport async function handleTriggerSync(\n input: { slug?: string; since?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const auth = getGmailAuth();\n if (!auth) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: \"Gmail auth not configured. Run `dxcrm sync gmail --init` first.\",\n }),\n },\n ],\n };\n }\n\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true, synced: 0, skipped: 0, customers: [] }),\n },\n ],\n };\n }\n\n const slugs = input.slug\n ? [input.slug]\n : fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const sinceDate = input.since\n ? new Date(input.since)\n : new Date(Date.now() - 24 * 60 * 60 * 1000);\n const results: Array<{ slug: string; synced: number; skipped: number }> = [];\n const errors: string[] = [];\n\n for (const slug of slugs) {\n const sourcesPath = path.join(customersDir, slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) continue;\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\") as string) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n if (!sources.gmail?.enabled || !sources.gmail.query) continue;\n\n const { syncGmail } = await import(\"../../sync/gmail-sync.js\");\n const result = await syncGmail({\n slug,\n dataDir,\n auth,\n query: sources.gmail.query,\n since: sinceDate,\n });\n updateSlugSyncState(dataDir, slug, { lastGmailSync: new Date().toISOString() });\n results.push({ slug, ...result });\n } catch (err) {\n errors.push(`${slug}: ${(err as Error).message}`);\n }\n }\n\n const total = results.reduce(\n (acc, r) => ({ synced: acc.synced + r.synced, skipped: acc.skipped + r.skipped }),\n { synced: 0, skipped: 0 }\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n ...total,\n customers: results,\n errors,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerTriggerSync(server: McpServer): void {\n server.registerTool(\n \"trigger_sync\",\n {\n title: \"Trigger Sync\",\n description: `Immediately trigger a Gmail sync for one or all customers.\nUse when you need fresh email data before answering a question.\nThe background daemon syncs every 30 minutes automatically — this forces an immediate sync.\n\nArgs:\n slug: Customer slug to sync (leave empty to sync all customers)\n since: ISO date string — only fetch emails since this date (default: last 24 hours)\n\nReturns: { success: boolean, synced: number, skipped: number, customers: [...], errors: [...] }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Customer slug to sync (empty = all customers)\"),\n since: z\n .string()\n .optional()\n .describe(\"Sync emails since this ISO date (default: last 24h)\"),\n }),\n },\n async ({ slug, since }) => {\n const input: { slug?: string; since?: string } = {};\n if (slug !== undefined) input.slug = slug;\n if (since !== undefined) input.since = since;\n return handleTriggerSync(input);\n }\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readAuditLog, filterAuditLog } from \"../../fs/audit-log.js\";\n\nconst DATA_DIR = process.cwd();\n\nexport async function handleGetAuditLog(\n input: { slug?: string; actor?: string; limit?: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const entries = readAuditLog(dataDir);\n const filterOpts: { slug?: string; actor?: string; limit?: number } = {\n limit: input.limit ?? 50,\n };\n if (input.slug !== undefined) filterOpts.slug = input.slug;\n if (input.actor !== undefined) filterOpts.actor = input.actor;\n const filtered = filterAuditLog(entries, filterOpts);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n total: entries.length,\n returned: filtered.length,\n entries: filtered,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerGetAuditLog(server: McpServer): void {\n server.registerTool(\n \"get_audit_log\",\n {\n title: \"Get Audit Log\",\n description: `Read the CRM audit log — all write operations with timestamp, actor, tool, and customer.\nUse to answer \"what changed recently?\", \"what did alice do?\", or \"show me all actions for acme-corp\".\n\nArgs:\n slug: Filter by customer slug (optional)\n actor: Filter by actor name (optional)\n limit: Max entries to return (default: 50, most recent)\n\nReturns: { total: number, returned: number, entries: [{timestamp, actor, tool, slug, summary}] }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n actor: z.string().optional().describe(\"Filter by actor (user or system)\"),\n limit: z.number().int().min(1).max(500).optional().describe(\"Max entries (default 50)\"),\n }),\n },\n async ({ slug, actor, limit }) => {\n const input: { slug?: string; actor?: string; limit?: number } = {};\n if (slug !== undefined) input.slug = slug;\n if (actor !== undefined) input.actor = actor;\n if (limit !== undefined) input.limit = limit;\n return handleGetAuditLog(input);\n }\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nexport interface CrmPrompt {\n name: string;\n title: string;\n description: string;\n build: (args: { slug: string }) => string;\n}\n\n/**\n * CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.\n * Each renders an actionable, tool-referencing instruction for the host LLM —\n * the agent-native equivalent of a Salesforce \"playbook\".\n */\nexport const CRM_PROMPTS: CrmPrompt[] = [\n {\n name: \"deal_risk_review\",\n title: \"Assess deal risk\",\n description:\n \"Evaluate the health and risk of a customer's open deals and recommend next steps.\",\n build: ({ slug }) =>\n `Assess the deal risk for customer \"${slug}\".\\n` +\n `1. Call open_deal_room({ slug: \"${slug}\" }) for a consolidated brief, or get_customer_context + get_deal_health.\\n` +\n `2. Identify stalled deals, approaching close dates, and silent champions (get_relationship_health).\\n` +\n `3. Summarise the top risks and recommend concrete next actions. Do not invent data — cite what you read.`,\n },\n {\n name: \"draft_follow_up\",\n title: \"Draft a follow-up email\",\n description:\n \"Draft a personalized follow-up email for a customer based on recent interactions.\",\n build: ({ slug }) =>\n `Draft a follow-up email for customer \"${slug}\".\\n` +\n `1. Read recent context with get_customer_context({ slug: \"${slug}\" }).\\n` +\n `2. Use draft_email({ slug: \"${slug}\", templateId, tone: \"friendly\" }) with an appropriate template.\\n` +\n `3. Reference the latest interaction concretely; keep it concise. Return the draft for review — do not send.`,\n },\n {\n name: \"account_brief\",\n title: \"Create an account brief\",\n description: \"Produce a concise executive brief for a customer account.\",\n build: ({ slug }) =>\n `Create an executive account brief for \"${slug}\".\\n` +\n `1. get_customer_context({ slug: \"${slug}\" }) and get_org_intelligence({ slug: \"${slug}\" }).\\n` +\n `2. Summarise: who the stakeholders are (champions/buyers/blockers), open pipeline, health, and risks.\\n` +\n `3. End with the single most important next action.`,\n },\n {\n name: \"pipeline_summary\",\n title: \"Summarize the pipeline\",\n description: \"Summarize pipeline and forecast, optionally focused on one customer.\",\n build: ({ slug }) =>\n `Summarize the sales pipeline (focus customer: \"${slug}\").\\n` +\n `1. get_pipeline_forecast() for the weighted total and per-stage breakdown.\\n` +\n `2. simulate_revenue() for P10/P50/P90 if a probabilistic view helps.\\n` +\n `3. Highlight at-risk revenue and the deals that most move the forecast.`,\n },\n];\n\nexport function registerPrompts(server: McpServer): void {\n for (const prompt of CRM_PROMPTS) {\n server.registerPrompt(\n prompt.name,\n {\n title: prompt.title,\n description: prompt.description,\n argsSchema: { slug: z.string().describe(\"Customer slug\") },\n },\n ({ slug }) => ({\n messages: [{ role: \"user\", content: { type: \"text\", text: prompt.build({ slug }) } }],\n })\n );\n }\n}\n","import { type McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\n/**\n * Read-only MCP Resources for CRM entities. Complements the action Tools:\n * agents can `resources/read` a customer briefing, pipeline or timeline by URI\n * instead of calling a tool.\n */\nexport function registerResources(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerResource(\n \"customers\",\n \"crm://customers\",\n {\n title: \"Customers\",\n description: \"List of all customer slugs\",\n mimeType: \"application/json\",\n },\n (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(listCustomerSlugs(dataDir), null, 2),\n },\n ],\n })\n );\n\n server.registerResource(\n \"customer\",\n new ResourceTemplate(\"crm://customer/{slug}\", { list: undefined }),\n {\n title: \"Customer context\",\n description: \"LLM-ready briefing (main facts, recent interactions, pipeline) for a customer\",\n mimeType: \"text/markdown\",\n },\n async (uri, variables) => {\n const { buildContext } = await import(\"../core/context-builder.js\");\n const text = await buildContext(dataDir, String(variables[\"slug\"]));\n return { contents: [{ uri: uri.href, mimeType: \"text/markdown\", text }] };\n }\n );\n\n server.registerResource(\n \"pipeline\",\n new ResourceTemplate(\"crm://pipeline/{slug}\", { list: undefined }),\n {\n title: \"Pipeline\",\n description: \"Open and closed deals for a customer\",\n mimeType: \"application/json\",\n },\n async (uri, variables) => {\n const { readPipeline } = await import(\"../fs/pipeline-writer.js\");\n const deals = await readPipeline(dataDir, String(variables[\"slug\"]));\n return {\n contents: [\n { uri: uri.href, mimeType: \"application/json\", text: JSON.stringify(deals, null, 2) },\n ],\n };\n }\n );\n\n server.registerResource(\n \"timeline\",\n new ResourceTemplate(\"crm://timeline/{slug}\", { list: undefined }),\n {\n title: \"Interaction timeline\",\n description: \"Newest-first interaction history for a customer\",\n mimeType: \"text/markdown\",\n },\n async (uri, variables) => {\n const { readInteractions } = await import(\"../fs/interactions-writer.js\");\n const text = await readInteractions(dataDir, String(variables[\"slug\"]));\n return { contents: [{ uri: uri.href, mimeType: \"text/markdown\", text }] };\n }\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Metadata-driven custom fields — the first increment of the metadata model\n * (next-plan N1-7). Definitions live in .agentic/schema/custom-fields.json and\n * extend customers without code changes. Core schemas stay strict; custom\n * fields are validated separately against this registry.\n */\nexport type CustomFieldType = \"text\" | \"number\" | \"boolean\" | \"date\" | \"select\";\n\nexport interface FieldDefinition {\n name: string;\n type: CustomFieldType;\n label?: string;\n options?: string[];\n}\n\nfunction schemaPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"schema\", \"custom-fields.json\");\n}\n\nexport function loadFieldDefinitions(dataDir: string): FieldDefinition[] {\n const p = schemaPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as {\n fields?: FieldDefinition[];\n };\n return Array.isArray(data.fields) ? data.fields : [];\n } catch {\n return [];\n }\n}\n\n/** Add or update (by name) a custom field definition. */\nexport function defineCustomField(dataDir: string, def: FieldDefinition): FieldDefinition[] {\n const defs = loadFieldDefinitions(dataDir);\n const idx = defs.findIndex((d) => d.name === def.name);\n if (idx >= 0) defs[idx] = def;\n else defs.push(def);\n const p = schemaPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ fields: defs }, null, 2), \"utf-8\");\n return defs;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n values: Record<string, string | number | boolean>;\n errors: string[];\n}\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Validate + coerce a record of raw values against custom field definitions. */\nexport function validateCustomFields(\n input: Record<string, unknown>,\n defs: FieldDefinition[]\n): ValidationResult {\n const byName = new Map(defs.map((d) => [d.name, d]));\n const values: Record<string, string | number | boolean> = {};\n const errors: string[] = [];\n\n for (const [key, raw] of Object.entries(input)) {\n const def = byName.get(key);\n if (!def) {\n errors.push(`Unknown custom field: ${key}`);\n continue;\n }\n const str = String(raw).trim();\n switch (def.type) {\n case \"number\": {\n const n = Number(str);\n if (!Number.isFinite(n)) errors.push(`${key}: not a number`);\n else values[key] = n;\n break;\n }\n case \"boolean\": {\n if (/^(true|yes|1)$/i.test(str)) values[key] = true;\n else if (/^(false|no|0)$/i.test(str)) values[key] = false;\n else errors.push(`${key}: not a boolean`);\n break;\n }\n case \"date\": {\n if (DATE_RE.test(str)) values[key] = str;\n else errors.push(`${key}: expected YYYY-MM-DD`);\n break;\n }\n case \"select\": {\n if (def.options && def.options.includes(str)) values[key] = str;\n else errors.push(`${key}: must be one of ${(def.options ?? []).join(\", \")}`);\n break;\n }\n default:\n values[key] = str;\n }\n }\n\n return { valid: errors.length === 0, values, errors };\n}\n","import { randomBytes } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { validateCustomFields, type FieldDefinition } from \"./custom-fields.js\";\n\n/**\n * Custom objects — runtime-defined entity types with their own fields, stored\n * as JSON without code migrations (Twenty-style \"no-migration\" model, the\n * N5-1 increment of the metadata layer). Definitions live in\n * .agentic/schema/custom-objects.json; records in .agentic/objects/<name>.json.\n */\nexport interface ObjectDefinition {\n name: string;\n label?: string;\n fields: FieldDefinition[];\n}\n\nexport interface ObjectRecord {\n id: string;\n createdAt: string;\n updatedAt: string;\n values: Record<string, string | number | boolean>;\n}\n\nexport interface RecordResult {\n ok: boolean;\n record?: ObjectRecord;\n errors?: string[];\n}\n\nfunction objectsSchemaPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"schema\", \"custom-objects.json\");\n}\nfunction recordsPath(dataDir: string, name: string): string {\n return path.join(dataDir, \".agentic\", \"objects\", `${name}.json`);\n}\n\nexport function loadCustomObjects(dataDir: string): ObjectDefinition[] {\n const p = objectsSchemaPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as {\n objects?: ObjectDefinition[];\n };\n return Array.isArray(data.objects) ? data.objects : [];\n } catch {\n return [];\n }\n}\n\nexport function getObjectDefinition(dataDir: string, name: string): ObjectDefinition | undefined {\n return loadCustomObjects(dataDir).find((o) => o.name === name);\n}\n\n/** Add or update (by name) a custom object definition. */\nexport function defineCustomObject(dataDir: string, def: ObjectDefinition): ObjectDefinition[] {\n const objs = loadCustomObjects(dataDir);\n const idx = objs.findIndex((o) => o.name === def.name);\n if (idx >= 0) objs[idx] = def;\n else objs.push(def);\n const p = objectsSchemaPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ objects: objs }, null, 2), \"utf-8\");\n return objs;\n}\n\nexport function listRecords(dataDir: string, name: string): ObjectRecord[] {\n const p = recordsPath(dataDir, name);\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as { records?: ObjectRecord[] };\n return Array.isArray(data.records) ? data.records : [];\n } catch {\n return [];\n }\n}\n\nfunction writeRecords(dataDir: string, name: string, records: ObjectRecord[]): void {\n const p = recordsPath(dataDir, name);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ records }, null, 2), \"utf-8\");\n}\n\nexport function getRecord(dataDir: string, name: string, id: string): ObjectRecord | undefined {\n return listRecords(dataDir, name).find((r) => r.id === id);\n}\n\nexport function createRecord(\n dataDir: string,\n name: string,\n values: Record<string, unknown>\n): RecordResult {\n const def = getObjectDefinition(dataDir, name);\n if (!def) return { ok: false, errors: [`Unknown object: ${name}`] };\n\n const validation = validateCustomFields(values, def.fields);\n if (!validation.valid) return { ok: false, errors: validation.errors };\n\n const now = new Date().toISOString();\n const record: ObjectRecord = {\n id: `${name}_${randomBytes(6).toString(\"hex\")}`,\n createdAt: now,\n updatedAt: now,\n values: validation.values,\n };\n writeRecords(dataDir, name, [...listRecords(dataDir, name), record]);\n return { ok: true, record };\n}\n\nexport function updateRecord(\n dataDir: string,\n name: string,\n id: string,\n values: Record<string, unknown>\n): RecordResult {\n const def = getObjectDefinition(dataDir, name);\n if (!def) return { ok: false, errors: [`Unknown object: ${name}`] };\n\n const records = listRecords(dataDir, name);\n const idx = records.findIndex((r) => r.id === id);\n if (idx < 0) return { ok: false, errors: [`Record not found: ${id}`] };\n\n const validation = validateCustomFields(values, def.fields);\n if (!validation.valid) return { ok: false, errors: validation.errors };\n\n const updated: ObjectRecord = {\n ...records[idx]!,\n updatedAt: new Date().toISOString(),\n values: { ...records[idx]!.values, ...validation.values },\n };\n records[idx] = updated;\n writeRecords(dataDir, name, records);\n return { ok: true, record: updated };\n}\n\nexport function deleteRecord(dataDir: string, name: string, id: string): boolean {\n const records = listRecords(dataDir, name);\n const next = records.filter((r) => r.id !== id);\n if (next.length === records.length) return false;\n writeRecords(dataDir, name, next);\n return true;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport {\n defineCustomObject,\n loadCustomObjects,\n createRecord,\n listRecords,\n} from \"../../core/custom-objects.js\";\nimport type { CustomFieldType } from \"../../core/custom-fields.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst FIELD_TYPES = [\"text\", \"number\", \"boolean\", \"date\", \"select\"] as const;\n\nfunction json(data: unknown): { content: Array<{ type: \"text\"; text: string }> } {\n return { content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }] };\n}\n\nexport function handleDefineCustomObject(\n input: {\n name: string;\n label?: string;\n fields: Array<{ name: string; type: CustomFieldType; label?: string; options?: string[] }>;\n },\n dataDir: string = DATA_DIR\n): { content: Array<{ type: \"text\"; text: string }> } {\n enforceRbac(dataDir, \"define_custom_object\");\n const objects = defineCustomObject(dataDir, {\n name: input.name,\n ...(input.label ? { label: input.label } : {}),\n fields: input.fields,\n });\n return json({ defined: input.name, objectCount: objects.length });\n}\n\nexport function handleCreateRecord(\n input: { object: string; values: Record<string, string> },\n dataDir: string = DATA_DIR\n): { content: Array<{ type: \"text\"; text: string }> } {\n enforceRbac(dataDir, \"create_record\");\n const res = createRecord(dataDir, input.object, input.values);\n if (!res.ok) return json({ error: (res.errors ?? []).join(\"; \") });\n // Fire-and-forget outbound webhook (event-driven; failures queue for replay).\n void import(\"../../core/webhooks.js\").then(({ emitEvent }) =>\n emitEvent(dataDir, \"record.created\", { object: input.object, record: res.record })\n );\n return json({ record: res.record });\n}\n\nexport function handleListRecords(\n input: { object: string },\n dataDir: string = DATA_DIR\n): { content: Array<{ type: \"text\"; text: string }> } {\n return json({ object: input.object, records: listRecords(dataDir, input.object) });\n}\n\nexport function handleListCustomObjects(dataDir: string = DATA_DIR): {\n content: Array<{ type: \"text\"; text: string }>;\n} {\n return json({ objects: loadCustomObjects(dataDir) });\n}\n\nexport function registerCustomObjectTools(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"define_custom_object\",\n {\n description:\n \"Define a custom object (runtime entity type) with typed fields — no code migration. admin only.\",\n inputSchema: z.object({\n name: z.string().describe(\"Object name (e.g. contract)\"),\n label: z.string().optional(),\n fields: z\n .array(\n z.object({\n name: z.string(),\n type: z.enum(FIELD_TYPES),\n label: z.string().optional(),\n options: z.array(z.string()).optional(),\n })\n )\n .describe(\"Field definitions\"),\n }),\n },\n ({ name, label, fields }) =>\n handleDefineCustomObject(\n { name, ...(label ? { label } : {}), fields: fields as never },\n dataDir\n )\n );\n\n server.registerTool(\n \"create_record\",\n {\n description:\n \"Create a record of a custom object. Values are validated against the schema. rep+.\",\n inputSchema: z.object({\n object: z.string().describe(\"Custom object name\"),\n values: z.record(z.string()).describe(\"Field values (key=value)\"),\n }),\n },\n ({ object, values }) => handleCreateRecord({ object, values }, dataDir)\n );\n\n server.registerTool(\n \"list_records\",\n {\n description: \"List records of a custom object.\",\n inputSchema: z.object({ object: z.string().describe(\"Custom object name\") }),\n },\n ({ object }) => handleListRecords({ object }, dataDir)\n );\n\n server.registerTool(\n \"list_custom_objects\",\n {\n description: \"List all defined custom objects and their field schemas.\",\n inputSchema: z.object({}),\n },\n () => handleListCustomObjects(dataDir)\n );\n}\n","import { createHash, randomBytes, timingSafeEqual } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\n\nexport type McpRole = \"admin\" | \"manager\" | \"rep\";\n\nexport interface McpTokenRecord {\n hash: string;\n actor: string;\n role: McpRole;\n label?: string;\n createdAt?: string;\n}\n\nexport interface AuthResult {\n ok: boolean;\n actor?: string;\n role?: McpRole;\n}\n\nfunction tokensPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"mcp-tokens.json\");\n}\n\n/** SHA-256 hex of a token. Only hashes are ever persisted. */\nexport function hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\nexport function loadMcpTokens(dataDir: string): McpTokenRecord[] {\n const p = tokensPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as { tokens?: McpTokenRecord[] };\n return Array.isArray(data.tokens) ? data.tokens : [];\n } catch {\n return [];\n }\n}\n\n/**\n * Whether the HTTP MCP endpoint must require a bearer token.\n * - `DXCRM_MCP_AUTH=required` forces auth on (even with no tokens yet).\n * - `DXCRM_MCP_AUTH=off` forces it off.\n * - Otherwise: on as soon as at least one token is configured (opt-in by\n * provisioning a token; stays open for local/firewalled dev by default).\n */\nexport function isAuthRequired(dataDir: string): boolean {\n const mode = process.env[\"DXCRM_MCP_AUTH\"];\n if (mode === \"required\") return true;\n if (mode === \"off\") return false;\n return loadMcpTokens(dataDir).length > 0;\n}\n\n/** Validate an `Authorization: Bearer <token>` header against stored hashes. */\nexport function verifyBearer(authHeader: string | undefined, dataDir: string): AuthResult {\n if (!authHeader || !authHeader.startsWith(\"Bearer \")) return { ok: false };\n const token = authHeader.slice(\"Bearer \".length).trim();\n if (!token) return { ok: false };\n\n const candidate = hashToken(token);\n const candidateBuf = Buffer.from(candidate, \"hex\");\n for (const rec of loadMcpTokens(dataDir)) {\n if (rec.hash.length !== candidate.length) continue;\n let recBuf: Buffer;\n try {\n recBuf = Buffer.from(rec.hash, \"hex\");\n } catch {\n continue;\n }\n if (recBuf.length === candidateBuf.length && timingSafeEqual(recBuf, candidateBuf)) {\n return { ok: true, actor: rec.actor, role: rec.role };\n }\n }\n return { ok: false };\n}\n\n/**\n * Mint a new token: generates a random secret, persists only its hash mapped\n * to an actor/role, and returns the plaintext ONCE (never stored).\n */\nexport function createMcpToken(\n dataDir: string,\n actor: string,\n role: McpRole,\n label?: string\n): string {\n const token = randomBytes(24).toString(\"base64url\");\n const records = loadMcpTokens(dataDir);\n records.push({\n hash: hashToken(token),\n actor,\n role,\n ...(label ? { label } : {}),\n createdAt: new Date().toISOString(),\n });\n const p = tokensPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ tokens: records }, null, 2), \"utf-8\");\n return token;\n}\n\n/** RFC 9728 OAuth 2.0 Protected Resource Metadata document. */\nexport function protectedResourceMetadata(resourceUrl: string): Record<string, unknown> {\n return {\n resource: resourceUrl,\n // Self-hosted default: tokens are provisioned out-of-band (createMcpToken).\n // Populate with an external Authorization Server to enable full OAuth flows.\n authorization_servers: [] as string[],\n bearer_methods_supported: [\"header\"],\n scopes_supported: [\"crm:read\", \"crm:write\"],\n };\n}\n\n/** Value for the `WWW-Authenticate` header on a 401 (RFC 9728 §5.1). */\nexport function wwwAuthenticateHeader(metadataUrl: string): string {\n return `Bearer resource_metadata=\"${metadataUrl}\"`;\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport { initOAuthFromDisk } from \"../core/oauth-store.js\";\nimport {\n decodeGmailPubSubPayload,\n verifyGmailPubSubSignature,\n handleGmailPushEvent,\n} from \"../sync/gmail-webhook-handler.js\";\nimport {\n handleMicrosoftValidationRequest,\n verifyMicrosoftGraphSignature,\n handleMicrosoftPushEvent,\n type MicrosoftGraphNotification,\n} from \"../sync/microsoft-webhook-handler.js\";\nimport {\n verifySlackSignature,\n handleSlackUrlVerification,\n handleSlackPushEvent,\n type SlackEvent,\n} from \"../sync/slack-webhook-handler.js\";\nimport { registerGetCapabilities } from \"./tools/get-capabilities.js\";\nimport { registerGetActiveSession } from \"./tools/get-active-session.js\";\nimport { registerGetCustomerContext } from \"./tools/get-customer-context.js\";\nimport { registerSearchCustomerKnowledge } from \"./tools/search-customer-knowledge.js\";\nimport { registerListCustomers } from \"./tools/list-customers.js\";\nimport { registerLogInteraction } from \"./tools/log-interaction.js\";\nimport { registerUpdateDeal } from \"./tools/update-deal.js\";\nimport { registerExportCustomer } from \"./tools/export-customer.js\";\nimport { registerUpdateCustomerFacts } from \"./tools/update-customer-facts.js\";\nimport { registerGetDealHealth } from \"./tools/get-deal-health.js\";\nimport { registerGetPipelineForecast } from \"./tools/get-pipeline-forecast.js\";\nimport { registerSummarizeMeeting } from \"./tools/summarize-meeting.js\";\nimport { registerGetPipelineStages } from \"./tools/get-pipeline-stages.js\";\nimport { registerGetMarketIntelligence } from \"./tools/get-market-intelligence.js\";\nimport { registerGetRelationshipGraph } from \"./tools/get-relationship-graph.js\";\nimport { registerGetRelationshipHealth } from \"./tools/get-relationship-health.js\";\nimport { registerRunDealAgent } from \"./tools/run-deal-agent.js\";\nimport { registerApproveAgentAction } from \"./tools/approve-agent-action.js\";\nimport { registerSimulateRevenue } from \"./tools/simulate-revenue.js\";\nimport { registerGetPlaybook } from \"./tools/get-playbook.js\";\nimport { registerCreatePlaybook } from \"./tools/create-playbook.js\";\nimport { registerListPlaybooks } from \"./tools/list-playbooks.js\";\nimport { registerDistillPlaybook } from \"./tools/distill-playbook.js\";\nimport { registerPursueGoal } from \"./tools/pursue-goal.js\";\nimport { registerGetGoalStatus } from \"./tools/get-goal-status.js\";\nimport { registerRegisterPushSubscription } from \"./tools/register-push-subscription.js\";\nimport { registerGetPushStatus } from \"./tools/get-push-status.js\";\nimport { registerGetOrgIntelligence } from \"./tools/get-org-intelligence.js\";\nimport { registerOpenDealRoom } from \"./tools/open-deal-room.js\";\nimport { registerGetProactiveBriefing } from \"./tools/get-proactive-briefing.js\";\nimport { registerListEmailTemplates } from \"./tools/list-email-templates.js\";\nimport { registerGetEmailTemplate } from \"./tools/get-email-template.js\";\nimport { registerDraftEmail } from \"./tools/draft-email.js\";\nimport { registerEnrollInSequence } from \"./tools/enroll-in-sequence.js\";\nimport { registerListSequenceEnrollments } from \"./tools/list-sequence-enrollments.js\";\nimport { registerUnenrollFromSequence } from \"./tools/unenroll-from-sequence.js\";\nimport { registerListSequences } from \"./tools/list-sequences.js\";\nimport { registerGenerateQuote } from \"./tools/generate-quote.js\";\nimport { registerGetQuoteStatus } from \"./tools/get-quote-status.js\";\nimport { registerGetBookingLink } from \"./tools/get-booking-link.js\";\nimport { registerCreateTicket } from \"./tools/create-ticket.js\";\nimport { registerUpdateTicket } from \"./tools/update-ticket.js\";\nimport { registerListTickets } from \"./tools/list-tickets.js\";\nimport { registerCloseTicket } from \"./tools/close-ticket.js\";\nimport { registerSendNpsSurvey } from \"./tools/send-nps-survey.js\";\nimport { registerGetSurveyResults } from \"./tools/get-survey-results.js\";\nimport { registerSearchKnowledgeBase } from \"./tools/search-knowledge-base.js\";\nimport { registerCreateKbArticle } from \"./tools/create-kb-article.js\";\nimport { registerBackupNow } from \"./tools/backup-now.js\";\nimport { registerListBackups } from \"./tools/list-backups.js\";\nimport { registerTriggerSync } from \"./tools/trigger-sync.js\";\nimport { registerGetAuditLog } from \"./tools/get-audit-log.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { registerResources } from \"./resources.js\";\nimport { registerCustomObjectTools } from \"./tools/custom-objects.js\";\nimport {\n isAuthRequired,\n verifyBearer,\n protectedResourceMetadata,\n wwwAuthenticateHeader,\n} from \"./auth.js\";\n\nexport function surveyThankYouPage(score: number, comment?: string): string {\n const emoji = score >= 9 ? \"🎉\" : score >= 7 ? \"🙂\" : \"🙏\";\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Thank you</title>\n<style>body{font-family:sans-serif;max-width:480px;margin:80px auto;text-align:center;padding:0 20px}\nh1{font-size:2.5em;margin-bottom:.3em}p{color:#555;font-size:1.1em}</style></head>\n<body><h1>${emoji}</h1><h2>Thank you for your feedback!</h2>\n<p>You rated us <strong>${score}/10</strong>.${comment ? `<br>Your comment: <em>\"${String(comment).slice(0, 200).replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")}\"</em>` : \"\"}</p>\n<p style=\"margin-top:40px;color:#aaa;font-size:.85em\">Powered by DatasynxOpenCRM</p>\n</body></html>`;\n}\n\nexport function createMcpServer(): McpServer {\n const server = new McpServer({\n name: \"datasynx-opencrm\",\n version: \"0.1.0\",\n });\n\n // Register all 56 tools\n // IMPORTANT: Use server.registerTool() — server.tool() is deprecated in v2\n registerGetCapabilities(server);\n registerGetActiveSession(server);\n registerGetCustomerContext(server);\n registerSearchCustomerKnowledge(server);\n registerListCustomers(server);\n registerLogInteraction(server);\n registerUpdateDeal(server);\n registerExportCustomer(server);\n registerUpdateCustomerFacts(server);\n registerGetDealHealth(server);\n registerGetPipelineForecast(server);\n registerSummarizeMeeting(server);\n registerGetPipelineStages(server);\n registerGetMarketIntelligence(server);\n registerGetRelationshipGraph(server);\n registerGetRelationshipHealth(server);\n registerRunDealAgent(server);\n registerApproveAgentAction(server);\n registerSimulateRevenue(server);\n registerGetPlaybook(server);\n registerCreatePlaybook(server);\n registerListPlaybooks(server);\n registerDistillPlaybook(server);\n registerPursueGoal(server);\n registerGetGoalStatus(server);\n registerRegisterPushSubscription(server);\n registerGetPushStatus(server);\n registerGetOrgIntelligence(server);\n registerOpenDealRoom(server);\n registerGetProactiveBriefing(server);\n registerListEmailTemplates(server);\n registerGetEmailTemplate(server);\n registerDraftEmail(server);\n registerEnrollInSequence(server);\n registerListSequenceEnrollments(server);\n registerUnenrollFromSequence(server);\n registerListSequences(server);\n registerGenerateQuote(server);\n registerGetQuoteStatus(server);\n registerGetBookingLink(server);\n registerCreateTicket(server);\n registerUpdateTicket(server);\n registerListTickets(server);\n registerCloseTicket(server);\n registerSendNpsSurvey(server);\n registerGetSurveyResults(server);\n registerSearchKnowledgeBase(server);\n registerCreateKbArticle(server);\n registerBackupNow(server);\n registerListBackups(server);\n registerTriggerSync(server);\n registerGetAuditLog(server);\n registerCustomObjectTools(server);\n\n // MCP Prompts (playbooks) + Resources (read-only entities) — agent-native primitives\n registerPrompts(server);\n registerResources(server);\n\n return server;\n}\n\nexport async function startStdio(): Promise<void> {\n await initOAuthFromDisk(process.cwd());\n const server = createMcpServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n // IMPORTANT: console.log would corrupt the MCP stdio protocol — always use console.error\n console.error(\"DatasynxOpenCRM MCP Server running via stdio\");\n}\n\nexport async function startHttp(port = 3847): Promise<void> {\n await initOAuthFromDisk(process.cwd());\n const { default: express } = await import(\"express\");\n const app = express();\n app.use(express.json());\n\n const server = createMcpServer();\n const dataDir = process.cwd();\n\n // RFC 9728 — OAuth 2.0 Protected Resource Metadata\n app.get(\"/.well-known/oauth-protected-resource\", (req, res) => {\n const base = `${req.protocol}://${req.get(\"host\") ?? \"localhost\"}`;\n res.json(protectedResourceMetadata(`${base}/mcp`));\n });\n\n app.post(\"/mcp\", async (req, res) => {\n // OAuth 2.1 resource-server gate: require a valid bearer token when auth is\n // enabled (a token is provisioned or DXCRM_MCP_AUTH=required).\n if (isAuthRequired(dataDir)) {\n const auth = verifyBearer(req.headers[\"authorization\"], dataDir);\n if (!auth.ok) {\n const base = `${req.protocol}://${req.get(\"host\") ?? \"localhost\"}`;\n res\n .status(401)\n .set(\n \"WWW-Authenticate\",\n wwwAuthenticateHeader(`${base}/.well-known/oauth-protected-resource`)\n )\n .json({ error: \"unauthorized\" });\n return;\n }\n // Attach the token's actor for RBAC enforcement on this request.\n if (auth.actor) process.env[\"DXCRM_ACTOR\"] = auth.actor;\n }\n\n const transport = new StreamableHTTPServerTransport({ enableJsonResponse: true });\n // Ensure onclose is always a function (required by Transport interface with exactOptionalPropertyTypes)\n transport.onclose = () => {\n /* no-op */\n };\n res.on(\"close\", () => {\n void transport.close();\n });\n await server.connect(transport as unknown as Transport);\n await transport.handleRequest(req, res, req.body as Record<string, unknown>);\n });\n\n app.get(\"/health\", (_req, res) => {\n res.json({ status: \"ok\", server: \"datasynx-opencrm\", version: \"0.1.0\" });\n });\n\n app.get(\"/sessions\", async (_req, res) => {\n try {\n const { readAllSessions } = await import(\"../commands/session.js\");\n const sessions = readAllSessions(dataDir);\n res.json({ sessions });\n } catch {\n res.json({ sessions: [] });\n }\n });\n\n // Gmail Pub/Sub webhook\n app.post(\"/webhooks/gmail\", async (req, res) => {\n const token = process.env[\"GMAIL_PUBSUB_TOKEN\"] ?? \"\";\n if (!verifyGmailPubSubSignature(req.headers[\"authorization\"] as string | undefined, token)) {\n res.status(401).json({ error: \"unauthorized\" });\n return;\n }\n const payload = decodeGmailPubSubPayload(req.body);\n if (!payload) {\n res.status(400).json({ error: \"invalid_payload\" });\n return;\n }\n const result = await handleGmailPushEvent(dataDir, payload, \"\").catch(() => ({\n processed: 0,\n slug: null,\n }));\n res.json({ ok: true, processed: result.processed });\n });\n\n // Microsoft Graph webhook\n app.all(\"/webhooks/microsoft\", async (req, res) => {\n const validation = handleMicrosoftValidationRequest(req.query as Record<string, string>);\n if (validation.isValidation) {\n res.setHeader(\"content-type\", \"text/plain\");\n res.status(200).send(validation.token);\n return;\n }\n const clientState = process.env[\"MS_GRAPH_CLIENT_STATE\"] ?? \"\";\n const body = req.body as { value?: MicrosoftGraphNotification[] };\n if (!verifyMicrosoftGraphSignature(body, clientState)) {\n res.status(401).json({ error: \"unauthorized\" });\n return;\n }\n const result = await handleMicrosoftPushEvent(dataDir, body.value ?? [], \"\").catch(() => ({\n processed: 0,\n skipped: 0,\n }));\n res.json({ ok: true, ...result });\n });\n\n // Slack Events API webhook\n app.post(\"/webhooks/slack\", express.text({ type: \"*/*\" }), async (req, res) => {\n const rawBody = req.body as string;\n const signingSecret = process.env[\"SLACK_SIGNING_SECRET\"] ?? \"\";\n if (\n !verifySlackSignature(\n rawBody,\n req.headers as { \"x-slack-signature\"?: string; \"x-slack-request-timestamp\"?: string },\n signingSecret\n )\n ) {\n res.status(401).json({ error: \"unauthorized\" });\n return;\n }\n let parsed: { type?: string; challenge?: string; event?: SlackEvent; team_id?: string };\n try {\n parsed = JSON.parse(rawBody) as typeof parsed;\n } catch {\n res.status(400).json({ error: \"invalid_json\" });\n return;\n }\n const verification = handleSlackUrlVerification(parsed);\n if (verification.isVerification) {\n res.json({ challenge: verification.challenge });\n return;\n }\n if (!parsed.event) {\n res.json({ ok: true, processed: 0 });\n return;\n }\n const botToken = process.env[\"SLACK_BOT_TOKEN\"] ?? \"\";\n const result = await handleSlackPushEvent(dataDir, parsed.event, botToken, {\n ...(parsed.team_id !== undefined ? { teamId: parsed.team_id } : {}),\n }).catch(() => ({ processed: 0, skipped: 1 }));\n res.json({ ok: true, ...result });\n });\n\n // NPS/CSAT survey response endpoint — linked from survey emails\n // GET /survey/respond?token=<t>&score=<0-10> → record score + thank-you page\n // GET /survey/respond?token=<t>&comment=true → show comment form\n // POST /survey/respond → record comment + thank-you page\n app.get(\"/survey/respond\", async (req, res) => {\n const { token, score, comment } = req.query as Record<string, string | undefined>;\n if (!token) {\n res.status(400).send(\"<h2>Invalid survey link.</h2>\");\n return;\n }\n\n if (comment === \"true\") {\n res.setHeader(\"content-type\", \"text/html\");\n res.send(`<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Survey Comment</title>\n<style>body{font-family:sans-serif;max-width:520px;margin:60px auto;padding:0 20px}\ntextarea{width:100%;padding:10px;font-size:1em;border:1px solid #ccc;border-radius:4px}\ninput[type=number]{width:80px;padding:8px;font-size:1em}\nbutton{margin-top:12px;padding:12px 28px;background:#1a1a2e;color:#fff;border:none;border-radius:4px;font-size:1em;cursor:pointer}</style></head>\n<body><h2>Leave a comment</h2>\n<form method=\"POST\" action=\"/survey/respond\">\n<input type=\"hidden\" name=\"token\" value=\"${String(token).replace(/&/g, \"&amp;\").replace(/\"/g, \"&quot;\").replace(/</g, \"&lt;\")}\">\n<label>Your score (0–10):<br><input type=\"number\" name=\"score\" min=\"0\" max=\"10\" required></label><br><br>\n<label>Comment (optional):<br><textarea name=\"comment\" rows=\"5\" placeholder=\"What can we improve?\"></textarea></label><br>\n<button type=\"submit\">Submit</button>\n</form></body></html>`);\n return;\n }\n\n const numScore = score !== undefined ? parseInt(score, 10) : NaN;\n if (isNaN(numScore) || numScore < 0 || numScore > 10) {\n res.status(400).send(\"<h2>Invalid score. Please use the link from your email.</h2>\");\n return;\n }\n\n const { recordSurveyResponse } = await import(\"../core/survey-engine.js\");\n await recordSurveyResponse(dataDir, token, numScore).catch(() => null);\n res.setHeader(\"content-type\", \"text/html\");\n res.send(surveyThankYouPage(numScore));\n });\n\n app.post(\"/survey/respond\", express.urlencoded({ extended: false }), async (req, res) => {\n const { token, score, comment: commentText } = req.body as Record<string, string | undefined>;\n if (!token) {\n res.status(400).send(\"<h2>Invalid survey link.</h2>\");\n return;\n }\n const numScore = score !== undefined ? parseInt(score, 10) : NaN;\n if (isNaN(numScore) || numScore < 0 || numScore > 10) {\n res\n .status(400)\n .send(\"<h2>Invalid score. Please go back and enter a number between 0 and 10.</h2>\");\n return;\n }\n const { recordSurveyResponse } = await import(\"../core/survey-engine.js\");\n await recordSurveyResponse(dataDir, token, numScore, commentText || undefined).catch(\n () => null\n );\n res.setHeader(\"content-type\", \"text/html\");\n res.send(surveyThankYouPage(numScore, commentText));\n });\n\n app.listen(port, () => {\n console.error(`DatasynxOpenCRM MCP Server running on http://0.0.0.0:${port}/mcp`);\n });\n}\n\n// Entry point when run directly (e.g. node dist/mcp.js)\nconst mode = process.env[\"DXCRM_MCP_MODE\"] ?? \"stdio\";\nif (mode === \"http\") {\n const port = parseInt(process.env[\"DXCRM_MCP_PORT\"] ?? \"3847\", 10);\n startHttp(port).catch((err: unknown) => {\n console.error(\"MCP Server fatal error:\", (err as Error).message);\n process.exit(1);\n });\n} else {\n startStdio().catch((err: unknown) => {\n console.error(\"MCP Server fatal error:\", (err as Error).message);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp.js","names":["b","DATA_DIR","ArrowFloat32","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","addDaysToDate","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR","DATA_DIR"],"sources":["../src/core/oauth-store.ts","../src/sync/push-manager.ts","../src/fs/sync-state.ts","../src/sync/gmail-webhook-handler.ts","../src/sync/microsoft-webhook-handler.ts","../src/sync/slack-webhook-handler.ts","../src/mcp/capabilities.ts","../src/mcp/tools/get-capabilities.ts","../src/mcp/tools/get-active-session.ts","../src/core/regex.ts","../src/core/context-builder.ts","../src/mcp/tools/get-customer-context.ts","../src/core/embedder.ts","../src/core/lancedb.ts","../src/mcp/tools/search-customer-knowledge.ts","../src/mcp/tools/list-customers.ts","../src/core/file-lock.ts","../src/core/graph.ts","../src/core/email-normalizer.ts","../src/core/graph-extractor.ts","../src/core/relationship-health.ts","../src/mcp/tools/log-interaction.ts","../src/mcp/tools/update-deal.ts","../src/mcp/tools/export-customer.ts","../src/mcp/tools/update-customer-facts.ts","../src/core/deal-health.ts","../src/mcp/tools/get-deal-health.ts","../src/mcp/tools/get-pipeline-forecast.ts","../src/mcp/tools/summarize-meeting.ts","../src/core/pipeline-stages.ts","../src/mcp/tools/get-pipeline-stages.ts","../src/core/cross-customer.ts","../src/mcp/tools/get-market-intelligence.ts","../src/mcp/tools/get-relationship-graph.ts","../src/mcp/tools/get-relationship-health.ts","../src/core/playbooks.ts","../src/agents/deal-agent.ts","../src/mcp/tools/run-deal-agent.ts","../src/mcp/tools/approve-agent-action.ts","../src/core/revenue-simulation.ts","../src/mcp/tools/simulate-revenue.ts","../src/mcp/tools/get-playbook.ts","../src/mcp/tools/create-playbook.ts","../src/mcp/tools/list-playbooks.ts","../src/mcp/tools/distill-playbook.ts","../src/core/goal-engine.ts","../src/mcp/tools/pursue-goal.ts","../src/mcp/tools/get-goal-status.ts","../src/mcp/tools/register-push-subscription.ts","../src/mcp/tools/get-push-status.ts","../src/core/org-intelligence.ts","../src/mcp/tools/get-org-intelligence.ts","../src/agents/deal-room.ts","../src/mcp/tools/open-deal-room.ts","../src/core/proactive-agent.ts","../src/mcp/tools/get-proactive-briefing.ts","../src/schemas/email-template.ts","../src/fs/template-store.ts","../src/mcp/tools/list-email-templates.ts","../src/core/template-engine.ts","../src/mcp/tools/get-email-template.ts","../src/mcp/tools/draft-email.ts","../src/schemas/sequence.ts","../src/fs/sequence-store.ts","../src/mcp/tools/enroll-in-sequence.ts","../src/mcp/tools/list-sequence-enrollments.ts","../src/mcp/tools/unenroll-from-sequence.ts","../src/mcp/tools/list-sequences.ts","../src/core/quote-generator.ts","../src/mcp/tools/generate-quote.ts","../src/mcp/tools/get-quote-status.ts","../src/mcp/tools/get-booking-link.ts","../src/schemas/ticket.ts","../src/fs/ticket-writer.ts","../src/core/sla-engine.ts","../src/mcp/tools/create-ticket.ts","../src/mcp/tools/update-ticket.ts","../src/mcp/tools/list-tickets.ts","../src/mcp/tools/close-ticket.ts","../src/schemas/survey.ts","../src/core/survey-engine.ts","../src/mcp/tools/send-nps-survey.ts","../src/mcp/tools/get-survey-results.ts","../src/schemas/kb-article.ts","../src/fs/knowledge-base.ts","../src/mcp/tools/search-knowledge-base.ts","../src/mcp/tools/create-kb-article.ts","../src/mcp/tools/backup-now.ts","../src/mcp/tools/list-backups.ts","../src/mcp/tools/trigger-sync.ts","../src/mcp/tools/get-audit-log.ts","../src/mcp/tools/get-logs.ts","../src/mcp/prompts.ts","../src/mcp/resources.ts","../src/core/custom-fields.ts","../src/core/custom-objects.ts","../src/mcp/tools/custom-objects.ts","../src/mcp/auth.ts","../src/mcp/server.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport type { OAuth2Client } from \"google-auth-library\";\n\nlet _auth: OAuth2Client | null = null;\n\nexport async function initOAuthFromDisk(dataDir: string): Promise<boolean> {\n const credPath = path.join(dataDir, \".agentic\", \"gmail-credentials.json\");\n const tokenPath = path.join(dataDir, \".agentic\", \"gmail-token.json\");\n\n if (!fs.existsSync(credPath) || !fs.existsSync(tokenPath)) {\n return false;\n }\n\n try {\n const { getGmailAuth: loadAuth } = await import(\"../sync/gmail-auth.js\");\n _auth = await loadAuth(credPath, tokenPath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getGmailAuth(): OAuth2Client | null {\n return _auth;\n}\n\nexport function resetOAuthStore(): void {\n _auth = null;\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\n\nexport type PushProvider = \"gmail\" | \"microsoft-graph\" | \"slack\";\nexport type PushStatus = \"active\" | \"expired\" | \"revoked\" | \"error\" | \"permanently_failed\";\n\nexport interface PushSubscription {\n id: string;\n provider: PushProvider;\n slug: string;\n webhookUrl: string;\n expiresAt: string | null;\n renewedAt: string | null;\n createdAt: string;\n providerData: {\n gmailHistoryId?: string;\n gmailTopicName?: string;\n gmailLabelIds?: string[];\n gmailEmailAddress?: string;\n microsoftSubscriptionId?: string;\n microsoftResource?: string;\n microsoftClientState?: string;\n slackTeamId?: string;\n slackChannelId?: string;\n slackBotToken?: string;\n };\n status: PushStatus;\n lastEventAt: string | null;\n eventsProcessed: number;\n renewFailures?: number;\n}\n\ninterface PushSubscriptionsFile {\n subscriptions: PushSubscription[];\n updatedAt: string;\n}\n\nexport function makePushSubId(): string {\n return `psub_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;\n}\n\nexport function subscriptionsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"push-subscriptions.json\");\n}\n\nexport async function readSubscriptions(dataDir: string): Promise<PushSubscription[]> {\n const filePath = subscriptionsPath(dataDir);\n if (!fs.existsSync(filePath)) return [];\n try {\n const raw = fs.readFileSync(filePath, \"utf-8\") as string;\n const parsed = JSON.parse(raw) as PushSubscriptionsFile;\n return parsed.subscriptions ?? [];\n } catch {\n return [];\n }\n}\n\nexport async function writeSubscriptions(dataDir: string, subs: PushSubscription[]): Promise<void> {\n const filePath = subscriptionsPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const file: PushSubscriptionsFile = { subscriptions: subs, updatedAt: new Date().toISOString() };\n writeJsonFile(filePath, file);\n}\n\nfunction expiresAtForProvider(provider: PushProvider): string | null {\n if (provider === \"gmail\") {\n return new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();\n }\n if (provider === \"microsoft-graph\") {\n return new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString();\n }\n return null; // slack: no expiry\n}\n\nexport async function register(\n dataDir: string,\n provider: PushProvider,\n slug: string,\n opts: { webhookUrl: string; providerData?: Partial<PushSubscription[\"providerData\"]> }\n): Promise<PushSubscription> {\n const subs = await readSubscriptions(dataDir);\n const sub: PushSubscription = {\n id: makePushSubId(),\n provider,\n slug,\n webhookUrl: opts.webhookUrl,\n expiresAt: expiresAtForProvider(provider),\n renewedAt: null,\n createdAt: new Date().toISOString(),\n providerData: opts.providerData ?? {},\n status: \"active\",\n lastEventAt: null,\n eventsProcessed: 0,\n };\n await writeSubscriptions(dataDir, [...subs, sub]);\n return sub;\n}\n\nexport async function revoke(dataDir: string, id: string): Promise<void> {\n const subs = await readSubscriptions(dataDir);\n const idx = subs.findIndex((s) => s.id === id);\n if (idx === -1) throw new Error(`Subscription ${id} not found`);\n subs[idx] = { ...subs[idx]!, status: \"revoked\" };\n await writeSubscriptions(dataDir, subs);\n}\n\nexport type RenewFn = (\n sub: PushSubscription\n) => Promise<{ expiresAt: string; providerData?: Partial<PushSubscription[\"providerData\"]> }>;\n\nexport async function renewExpiringSubscriptions(\n dataDir: string,\n renewFn: RenewFn,\n thresholdHours = 24\n): Promise<{ renewed: string[]; errors: string[] }> {\n const subs = await readSubscriptions(dataDir);\n const thresholdMs = thresholdHours * 60 * 60 * 1000;\n const cutoff = Date.now() + thresholdMs;\n\n const renewed: string[] = [];\n const errors: string[] = [];\n\n const PERMANENT_FAILURE_THRESHOLD = 3;\n\n for (let i = 0; i < subs.length; i++) {\n const sub = subs[i]!;\n if (sub.status !== \"active\" && sub.status !== \"error\") continue;\n if (sub.expiresAt === null) continue; // slack: no expiry\n if (new Date(sub.expiresAt).getTime() > cutoff) continue;\n\n try {\n const result = await renewFn(sub);\n subs[i] = {\n ...sub,\n status: \"active\",\n expiresAt: result.expiresAt,\n renewedAt: new Date().toISOString(),\n renewFailures: 0,\n providerData: result.providerData\n ? { ...sub.providerData, ...result.providerData }\n : sub.providerData,\n };\n renewed.push(sub.id);\n } catch {\n const failures = (sub.renewFailures ?? 0) + 1;\n const newStatus: PushStatus =\n failures >= PERMANENT_FAILURE_THRESHOLD ? \"permanently_failed\" : \"error\";\n subs[i] = { ...sub, status: newStatus, renewFailures: failures };\n errors.push(sub.id);\n }\n }\n\n await writeSubscriptions(dataDir, subs);\n return { renewed, errors };\n}\n","import path from \"path\";\nimport { readJsonFile, writeJsonFile } from \"./json-store.js\";\n\nexport interface SlugSyncState {\n lastGmailSync?: string;\n lastCalendarSync?: string;\n lastGmailPushHistoryId?: string;\n lastMicrosoftPushAt?: string;\n}\n\nexport interface SyncState {\n [slug: string]: SlugSyncState;\n}\n\nfunction getSyncStatePath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sync-state.json\");\n}\n\nexport function readSyncState(dataDir: string): SyncState {\n return readJsonFile<SyncState>(getSyncStatePath(dataDir), {});\n}\n\nexport function writeSyncState(dataDir: string, state: SyncState): void {\n writeJsonFile(getSyncStatePath(dataDir), state);\n}\n\nexport function updateSlugSyncState(\n dataDir: string,\n slug: string,\n update: Partial<SlugSyncState>\n): void {\n const state = readSyncState(dataDir);\n state[slug] = { ...state[slug], ...update };\n writeJsonFile(getSyncStatePath(dataDir), state);\n}\n\nexport function getLastGmailSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastGmailSync;\n return ts ? new Date(ts) : undefined;\n}\n\nexport function getLastCalendarSync(dataDir: string, slug: string): Date | undefined {\n const ts = readSyncState(dataDir)[slug]?.lastCalendarSync;\n return ts ? new Date(ts) : undefined;\n}\n","import {\n readSubscriptions,\n writeSubscriptions,\n type PushSubscription,\n type RenewFn,\n} from \"./push-manager.js\";\nimport { updateSlugSyncState, readSyncState } from \"../fs/sync-state.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\nimport type { HistoryMessage, WatchRegistration } from \"./gmail-push-watch.js\";\n\nexport interface GmailPubSubMessage {\n emailAddress: string;\n historyId: string;\n}\n\nexport function decodeGmailPubSubPayload(body: unknown): GmailPubSubMessage | null {\n try {\n const b = body as { message?: { data?: string } };\n const data = b?.message?.data;\n if (!data) return null;\n const decoded = Buffer.from(data, \"base64\").toString(\"utf-8\");\n const parsed = JSON.parse(decoded) as { emailAddress?: string; historyId?: string };\n if (!parsed.emailAddress || !parsed.historyId) return null;\n return { emailAddress: parsed.emailAddress, historyId: parsed.historyId };\n } catch {\n return null;\n }\n}\n\nexport function verifyGmailPubSubSignature(\n authHeader: string | undefined,\n expectedToken: string\n): boolean {\n if (!authHeader) return false;\n const token = authHeader.startsWith(\"Bearer \") ? authHeader.slice(7) : authHeader;\n return token === expectedToken;\n}\n\nfunction findSubscriptionByEmail(\n subs: PushSubscription[],\n emailAddress: string\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"gmail\" &&\n s.status === \"active\" &&\n (s.providerData.gmailEmailAddress === emailAddress || s.slug === emailAddress)\n ) ?? null\n );\n}\n\nexport type FetchHistoryFn = (\n accessToken: string,\n startHistoryId: string\n) => Promise<HistoryMessage[]>;\nexport type FetchMessageFn = (\n accessToken: string,\n messageId: string\n) => Promise<{\n id: string;\n threadId: string;\n subject: string;\n from: string;\n date: string;\n body: string;\n}>;\nexport type AppendInteractionFn = typeof appendInteraction;\n\nexport interface HandleGmailPushOptions {\n fetchHistoryFn?: FetchHistoryFn;\n fetchMessageFn?: FetchMessageFn;\n appendInteractionFn?: AppendInteractionFn;\n accessToken?: string;\n}\n\nexport { readSubscriptions };\n\nexport async function handleGmailPushEvent(\n dataDir: string,\n payload: GmailPubSubMessage,\n subscriptionId: string,\n options: HandleGmailPushOptions = {}\n): Promise<{ processed: number; slug: string | null }> {\n const subs = await readSubscriptions(dataDir);\n const sub = findSubscriptionByEmail(subs, payload.emailAddress);\n if (!sub) return { processed: 0, slug: null };\n\n const slug = sub.slug;\n const syncState = readSyncState(dataDir);\n const lastHistoryId =\n syncState[slug]?.lastGmailPushHistoryId ?? sub.providerData.gmailHistoryId ?? \"0\";\n\n // Skip if already processed\n if (BigInt(payload.historyId) <= BigInt(lastHistoryId)) {\n return { processed: 0, slug };\n }\n\n const startHistoryId = sub.providerData.gmailHistoryId ?? lastHistoryId;\n\n const {\n fetchHistoryFn,\n fetchMessageFn,\n appendInteractionFn = appendInteraction,\n accessToken = \"\",\n } = options;\n\n if (!fetchHistoryFn) return { processed: 0, slug };\n\n const messages = await fetchHistoryFn(accessToken, startHistoryId);\n let processed = 0;\n\n for (const msg of messages) {\n if (!fetchMessageFn) continue;\n try {\n const full = await fetchMessageFn(accessToken, msg.id);\n const sourceRef = `gmail://thread/${full.threadId}`;\n\n await appendInteractionFn(dataDir, slug, {\n date: new Date().toISOString().slice(0, 10),\n type: \"Email\",\n direction: \"inbound\",\n with: full.from,\n subject: full.subject,\n summary: full.body.slice(0, 300) || \"(no body)\",\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n processed++;\n } catch {\n // Skip individual message errors\n }\n }\n\n // Update sync state\n updateSlugSyncState(dataDir, slug, { lastGmailPushHistoryId: payload.historyId });\n\n // Update subscription counters\n const subIdx = subs.findIndex((s) => s.id === sub.id);\n if (subIdx !== -1) {\n subs[subIdx] = {\n ...subs[subIdx]!,\n eventsProcessed: subs[subIdx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed, slug };\n}\n\nexport type RegisterGmailWatchFn = (\n accessToken: string,\n topicName: string\n) => Promise<WatchRegistration>;\n\nexport function buildGmailRenewFn(\n accessToken: string,\n topicName: string,\n registerFn?: RegisterGmailWatchFn\n): RenewFn {\n return async (_sub: PushSubscription) => {\n const doRegister: RegisterGmailWatchFn =\n registerFn ??\n (async (token: string, topic: string) => {\n const { registerGmailWatch } = await import(\"./gmail-push-watch.js\");\n return registerGmailWatch(token, topic);\n });\n\n const registration = await doRegister(accessToken, topicName);\n return {\n expiresAt: new Date(Number(registration.expiration)).toISOString(),\n providerData: { gmailHistoryId: registration.historyId },\n };\n };\n}\n","import { readSubscriptions, writeSubscriptions, type PushSubscription } from \"./push-manager.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\n\nexport interface MicrosoftGraphNotification {\n subscriptionId: string;\n clientState?: string;\n resource: string;\n resourceData?: { id: string; \"@odata.type\": string };\n}\n\nexport function verifyMicrosoftGraphSignature(\n body: { value?: Array<Partial<MicrosoftGraphNotification>> },\n expectedClientState: string\n): boolean {\n const notifications = body.value ?? [];\n if (notifications.length === 0) return expectedClientState === \"\";\n return notifications.every((n) => n.clientState === expectedClientState);\n}\n\nexport interface ValidationResult {\n isValidation: boolean;\n token?: string;\n}\n\nexport function handleMicrosoftValidationRequest(\n queryParams: Record<string, string | undefined>\n): ValidationResult {\n const token = queryParams[\"validationToken\"];\n if (token) return { isValidation: true, token };\n return { isValidation: false };\n}\n\nexport interface GraphMessage {\n id: string;\n subject?: string;\n from?: { emailAddress?: { address?: string; name?: string } };\n receivedDateTime?: string;\n bodyPreview?: string;\n}\n\nexport type FetchGraphMessageFn = (\n accessToken: string,\n messageId: string\n) => Promise<GraphMessage | null>;\nexport type AppendInteractionFn = typeof appendInteraction;\n\nexport interface HandleMicrosoftPushOptions {\n fetchMessageFn?: FetchGraphMessageFn;\n appendInteractionFn?: AppendInteractionFn;\n}\n\nexport { readSubscriptions };\n\nfunction findSubscriptionByMsId(\n subs: PushSubscription[],\n subscriptionId: string\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"microsoft-graph\" &&\n s.status === \"active\" &&\n s.providerData.microsoftSubscriptionId === subscriptionId\n ) ?? null\n );\n}\n\nexport async function handleMicrosoftPushEvent(\n dataDir: string,\n notifications: MicrosoftGraphNotification[],\n accessToken: string,\n options: HandleMicrosoftPushOptions = {}\n): Promise<{ processed: number; skipped: number }> {\n const subs = await readSubscriptions(dataDir);\n const { fetchMessageFn, appendInteractionFn = appendInteraction } = options;\n\n let processed = 0;\n let skipped = 0;\n let anyProcessed = false;\n\n for (const notification of notifications) {\n const sub = findSubscriptionByMsId(subs, notification.subscriptionId);\n if (!sub) {\n skipped++;\n continue;\n }\n\n const messageId = notification.resourceData?.id;\n if (!messageId || !fetchMessageFn) {\n skipped++;\n continue;\n }\n\n try {\n const message = await fetchMessageFn(accessToken, messageId);\n if (!message) {\n skipped++;\n continue;\n }\n\n const from = message.from?.emailAddress?.address ?? \"unknown\";\n const sourceRef = `msgraph://message/${message.id}`;\n\n await appendInteractionFn(dataDir, sub.slug, {\n date: message.receivedDateTime\n ? new Date(message.receivedDateTime).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10),\n type: \"Email\",\n direction: \"inbound\",\n with: from,\n subject: message.subject ?? \"(no subject)\",\n summary: message.bodyPreview ?? \"(no preview)\",\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n\n processed++;\n anyProcessed = true;\n\n // Update sub counters\n const idx = subs.findIndex((s) => s.id === sub.id);\n if (idx !== -1) {\n subs[idx] = {\n ...subs[idx]!,\n eventsProcessed: subs[idx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n }\n } catch {\n skipped++;\n }\n }\n\n if (anyProcessed) {\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed, skipped };\n}\n","import crypto from \"crypto\";\nimport { readSubscriptions, writeSubscriptions, type PushSubscription } from \"./push-manager.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\n\nexport interface SlackEvent {\n type: string;\n user?: string;\n text?: string;\n channel?: string;\n ts?: string;\n bot_id?: string;\n}\n\nexport function verifySlackSignature(\n body: string,\n headers: { \"x-slack-signature\"?: string; \"x-slack-request-timestamp\"?: string },\n signingSecret: string\n): boolean {\n const sig = headers[\"x-slack-signature\"];\n const ts = headers[\"x-slack-request-timestamp\"];\n if (!sig || !ts) return false;\n\n // Replay protection: reject requests older than 5 minutes\n const tsNum = Number(ts);\n if (Math.abs(Date.now() / 1000 - tsNum) > 5 * 60) return false;\n\n const sigBase = `v0:${ts}:${body}`;\n const expected = \"v0=\" + crypto.createHmac(\"sha256\", signingSecret).update(sigBase).digest(\"hex\");\n\n try {\n const sigBuf = Buffer.from(sig);\n const expBuf = Buffer.from(expected);\n if (sigBuf.length !== expBuf.length) return false;\n return crypto.timingSafeEqual(sigBuf, expBuf);\n } catch {\n return false;\n }\n}\n\nexport type SlackUrlVerificationResult =\n | { isVerification: true; challenge: string }\n | { isVerification: false; challenge?: never };\n\nexport function handleSlackUrlVerification(body: {\n type?: string;\n challenge?: string;\n}): SlackUrlVerificationResult {\n if (body.type === \"url_verification\") {\n return { isVerification: true, challenge: body.challenge ?? \"\" };\n }\n return { isVerification: false };\n}\n\nexport type AppendInteractionFn = typeof appendInteraction;\nexport type FetchUserInfoFn = (\n botToken: string,\n userId: string\n) => Promise<{ email?: string; name?: string }>;\n\nexport interface HandleSlackPushOptions {\n appendInteractionFn?: AppendInteractionFn;\n fetchUserInfoFn?: FetchUserInfoFn;\n teamId?: string;\n}\n\nfunction findSubscriptionByTeam(\n subs: PushSubscription[],\n teamId: string | undefined\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"slack\" &&\n s.status === \"active\" &&\n (!teamId || s.providerData.slackTeamId === teamId)\n ) ?? null\n );\n}\n\nexport async function handleSlackPushEvent(\n dataDir: string,\n event: SlackEvent,\n botToken: string,\n options: HandleSlackPushOptions = {}\n): Promise<{ processed: number; skipped: number }> {\n // Only process message events\n if (event.type !== \"message\") return { processed: 0, skipped: 1 };\n\n // Skip bot messages\n if (event.bot_id) return { processed: 0, skipped: 1 };\n\n // Skip empty text\n if (!event.text?.trim()) return { processed: 0, skipped: 1 };\n\n const subs = await readSubscriptions(dataDir);\n const sub = findSubscriptionByTeam(subs, options.teamId);\n if (!sub) return { processed: 0, skipped: 1 };\n\n const { appendInteractionFn = appendInteraction, fetchUserInfoFn } = options;\n\n let senderName = event.user ?? \"unknown\";\n if (fetchUserInfoFn && event.user) {\n try {\n const info = await fetchUserInfoFn(botToken, event.user);\n senderName = info.name ?? info.email ?? event.user;\n } catch {\n // keep default\n }\n }\n\n const ts = event.ts\n ? new Date(Number(event.ts) * 1000).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10);\n const sourceRef = `slack://channel/${event.channel ?? \"dm\"}/ts/${event.ts ?? Date.now()}`;\n\n try {\n await appendInteractionFn(dataDir, sub.slug, {\n date: ts,\n type: \"Meeting\",\n direction: \"inbound\",\n with: senderName,\n subject: `Slack message in ${event.channel ?? \"DM\"}`,\n summary: event.text.slice(0, 300),\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n\n // Update sub counters\n const idx = subs.findIndex((s) => s.id === sub.id);\n if (idx !== -1) {\n subs[idx] = {\n ...subs[idx]!,\n eventsProcessed: subs[idx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed: 1, skipped: 0 };\n } catch {\n return { processed: 0, skipped: 1 };\n }\n}\n","// src/mcp/capabilities.ts\n// Single Source of Truth for capability documentation.\n// Used by get_capabilities() MCP tool AND dxcrm guide CLI command.\n\nexport const CAPABILITIES_TEXT = `\n# DatasynxOpenCRM — Agent Guide\n\n## Product\nDatasynxOpenCRM is a local-first, MCP-native CRM. All customer data lives in markdown\nfiles on your machine. No cloud, no HubSpot, no per-seat pricing.\n\n## Agent Wake (Telegram Notifications)\n\\`dxcrm agent spawn\\` enables a wake-triggered agent for a customer. When new emails arrive,\nthe agent sends a Telegram notification so you never miss an inbound message.\n\\`\\`\\`\ndxcrm agent spawn acme-corp --channel telegram\n\\`\\`\\`\nRequires: \\`TELEGRAM_BOT_TOKEN\\` + \\`TELEGRAM_CHAT_ID\\` env vars.\n\n## Golden Path — Agent Session Workflow\n\nThe recommended sequence for a productive agent session:\n\n1. \\`get_capabilities()\\` — understand available tools (this guide)\n2. \\`get_active_session()\\` — check for an active customer session\n3. \\`get_customer_context({ slug })\\` — load full briefing for the customer\n4. \\`search_customer_knowledge({ slug, query })\\` — find specific historical information\n5. \\`log_interaction()\\` / \\`update_deal()\\` — write back what happened\n\n## RBAC — Role-Based Access Control\n\nTools enforce the \\`DXCRM_ACTOR\\` environment variable for identity. Configure roles with \\`dxcrm rbac set\\`.\n\n| Role | Permissions |\n|---|---|\n| admin | All tools, all customers |\n| manager | log_interaction, update_deal, pursue_goal + all read tools |\n| rep | log_interaction, update_deal (own customers only) + all read tools |\n\nDefault role: rep (when DXCRM_ACTOR is not set or has no assigned role).\n\nConfig: \\`.agentic/rbac.json\\` | Actor: \\`DXCRM_ACTOR\\` env var\n\n## Available Tools\n\n| Tool | Purpose | RBAC |\n|---|---|---|\n| get_capabilities | Returns this guide — understand what the CRM can do | any |\n| get_active_session | Check which customer session is currently active | any |\n| get_customer_context | Full LLM-ready briefing for a customer (last 10 interactions, pipeline, contacts) | any (rep: own only) |\n| search_customer_knowledge | Hybrid vector + full-text search across emails and transcripts for a customer | any |\n| list_customers | List all customers with stage, last interaction date, and deal value | any (rep: own only) |\n| log_interaction | Write a new interaction entry (call, email, meeting, note) — immediately searchable | rep+ |\n| update_deal | Create or update a deal in pipeline.md — upserts by deal name | rep+ |\n| update_customer_facts | Update fields in customer profile (domain, contact, stage, tags) | admin |\n| export_customer | Export all customer data as JSON or Markdown | admin |\n| get_deal_health | Score deal health 0–100 (A–F grade) based on activity, velocity, close date, probability | any |\n| get_pipeline_forecast | Aggregate weighted pipeline revenue across all customers grouped by stage | any |\n| get_pipeline_stages | List all configured pipeline stages (defaults: lead, qualified, proposal, negotiation, won, lost) | any |\n| summarize_meeting | LLM-summarize a transcript and log it as a Meeting interaction | rep+ |\n| get_market_intelligence | Semantic search across all customers for patterns and common topics | any |\n| get_relationship_graph | Stakeholder map: champions, blockers, economic buyers, warm intro paths | any |\n| get_relationship_health | Health scores (0–100, A–F, trend) per contact with decay detection and risk flags | any |\n| run_deal_agent | Analyze deal and generate prioritized action plan (observe/suggest/act autonomy levels) | rep+ |\n| approve_agent_action | Approve or reject a pending action from the deal agent queue | rep+ |\n| simulate_revenue | Monte Carlo pipeline forecast — P10/P50/P90, sensitivity map, at-risk revenue | any |\n| get_playbook | Retrieve playbooks matching current deal situation (trigger-matched, sorted by success rate) | any |\n| create_playbook | Create or update a playbook with trigger DSL encoding proven tactics | rep+ |\n| list_playbooks | List all playbooks for a customer (metadata only, no body) | any |\n| distill_playbook | LLM-extract a reusable playbook from a won or lost deal's interaction history | rep+ |\n| pursue_goal | Set a revenue/pipeline goal and get an AI-decomposed action plan with sub-goals | manager+ |\n| get_goal_status | Get all active goals or a specific goal with progress, days remaining, sub-goals | any |\n| register_push_subscription | Register real-time push subscription (Gmail Pub/Sub, MS Graph, Slack Events) | admin |\n| get_push_status | Show all push subscriptions: expiry, events processed, renewal needs | any |\n| get_org_intelligence | Stakeholder map with champions, buyers, blockers, health scores, risk flags, recommendation | any |\n| open_deal_room | Multi-agent deal brief: graph + health + deal health + simulation + playbook in one call | any |\n| get_proactive_briefing | Daily briefing: urgent alerts, opportunities, P50/P90 forecast, top action | any |\n| list_email_templates | List all saved email templates with id, name, category, subject | any |\n| get_email_template | Retrieve a single email template with full body and detected variables | any |\n| draft_email | Draft a personalized email from a template with auto-filled customer variables | rep+ |\n| enroll_in_sequence | Enroll a contact in a multi-step email sequence | rep+ |\n| list_sequence_enrollments | List active sequence enrollments filtered by customer or status | any |\n| unenroll_from_sequence | Pause (soft-unenroll) a contact from an active sequence | rep+ |\n| list_sequences | List all defined email sequences with step count and enrollment count | any |\n| generate_quote | Generate a professional HTML quote with line items, VAT, subtotal, total | rep+ |\n| get_quote_status | Retrieve a generated quote by number or list all quotes for a customer | any |\n| get_booking_link | Get a Calendly booking link for a customer — optionally pre-fills name/email | rep+ |\n| create_ticket | Create a support ticket with auto-calculated SLA due date based on priority | rep+ |\n| update_ticket | Update ticket status or assignee (resolved auto-sets resolution date) | rep+ |\n| list_tickets | List tickets filtered by customer, status, priority, or assignee | any |\n| close_ticket | Close a ticket and optionally log resolution as an interaction | rep+ |\n| send_nps_survey | Generate NPS/CSAT survey token + HTML email draft (does not send automatically) | rep+ |\n| get_survey_results | NPS score, promoter/passive/detractor breakdown, all responses for a survey | any |\n| search_knowledge_base | Full-text search across KB articles (title, body, tags) with category and public filters | any |\n| create_kb_article | Create a new knowledge base article stored as Markdown in .agentic/knowledge-base/ | rep+ |\n| backup_now | Trigger immediate backup of customers/ + .agentic/ with SHA-256 integrity check | admin |\n| list_backups | List available backups with date, size, verification status, and customer count | any |\n| trigger_sync | Force immediate Gmail sync for one or all customers | rep+ |\n| get_audit_log | Read audit log — all write operations with actor, tool, customer | admin |\n| get_logs | Query/aggregate the structured application log (level, component, errors) | admin |\n| define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |\n| create_record | Create a record of a custom object (validated against its schema) | rep+ |\n| list_records | List records of a custom object | any |\n| list_custom_objects | List all defined custom objects and their schemas | any |\n\n## MCP Resources (read-only)\n\nBesides Tools, the server exposes read-only Resources you can fetch via resources/read:\n- crm://customers — list of all customer slugs (JSON)\n- crm://customer/{slug} — LLM-ready briefing (main facts, recent interactions, pipeline)\n- crm://pipeline/{slug} — deals for a customer (JSON)\n- crm://timeline/{slug} — newest-first interaction history (Markdown)\n\n## MCP Prompts (playbooks)\n\nReusable playbook prompts via prompts/get (argument: slug):\n- deal_risk_review — assess deal health and risk, recommend next steps\n- draft_follow_up — draft a personalized follow-up email\n- account_brief — concise executive account brief\n- pipeline_summary — pipeline + forecast summary\n\n## Tool Reference\n\n### get_capabilities()\nReturns all available MCP tools, their inputs, and the CRM workflow guide.\n- Input: none\n- Returns: This guide text\n\n### get_active_session()\nCheck which customer is currently active in the session store.\n- Input: none\n- Returns: { hasSession: boolean, customerSlug?, customerName?, startedAt?, owner? }\n\n### get_customer_context({ slug? })\nLoad complete briefing for a customer. Reads main_facts.md, last 10 interactions,\nand pipeline deals. Returns a structured markdown context block.\nAutomatically triggers a background Gmail sync if last sync was >30 minutes ago.\n- Input: { slug?: string } — Customer ID (e.g. \"acme-corp\"). Leave empty for active session.\n- Returns: Formatted markdown with Quick Reference, Contacts, Critical Context,\n Recent Activity, Pipeline, and Open Questions\n- Performance: <3 seconds. Token budget: <3000 tokens.\n\n### search_customer_knowledge({ slug, query, limit? })\nHybrid vector + full-text search across all emails and transcripts for a customer.\nSearches the LanceDB docs table for the given customer.\n- Input: { slug: string, query: string, limit?: number (default 5, max 50) }\n- Returns: { results: Array<{ content, score, source }> }\n\n### list_customers({ filter? })\nList all customers with their stage, last interaction date, and deal value.\nRBAC: rep role only sees owned customers.\n- Input: { filter?: string } — Optional substring filter on name or slug (case-insensitive)\n- Returns: Array of { slug, name, stage, lastInteraction?, dealValue? }\n\n### log_interaction({ slug, type, summary, with, nextSteps?, direction?, source?, date? })\nWrite a new interaction entry to interactions.md. Immediately searchable.\nAlso auto-updates the relationship graph and health scores (fire-and-forget).\nUse after every call, meeting, or email.\nRBAC: rep+\n- Input:\n slug: Customer ID\n type: \"Email\" | \"Call\" | \"Meeting\" | \"Note\" | \"Demo\" | \"Proposal\" | \"Contract\" | \"Other\"\n summary: 2-5 sentences describing what happened\n with: Who was involved (name or email)\n nextSteps?: Array of action items\n direction?: \"inbound\" | \"outbound\"\n source?: Source reference string (auto-generated if omitted)\n date?: Interaction date YYYY-MM-DD (defaults to today)\n- Returns: { success: boolean, path: string, entry: string }\n\n### update_deal({ slug, dealName, stage?, value?, probability?, closeDate?, notes? })\nUpdate or create a deal in pipeline.md. Upserts by deal name.\nRBAC: rep+\n- Input:\n slug: Customer ID\n dealName: Deal name (used as unique key)\n stage?: \"lead\" | \"qualified\" | \"proposal\" | \"negotiation\" | \"won\" | \"lost\"\n value?: Deal value in euros\n probability?: Win probability (0-100)\n closeDate?: Expected close date (YYYY-MM-DD)\n notes?: Free-text notes\n- Returns: { success: boolean, deal: object }\n\n### update_customer_facts({ slug, name?, domain?, email?, phone?, industry?, relationshipStage?, dealValue?, primaryContact?, timezone?, tags? })\nUpdate fields in a customer's main_facts.md profile. Merges patch into existing data. Sets updated = today.\nRBAC: admin\n- Input: slug (required) + any combination of the optional fields\n- Returns: { success: boolean, facts: object }\n\n### export_customer({ slug, format? })\nExport all customer data (main_facts + interactions count + pipeline + attachments list).\nRBAC: admin\n- Input: { slug: string, format?: \"json\" | \"markdown\" (default \"json\") }\n- Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments }\n- Returns (Markdown): Formatted document with all sections\n\n### get_deal_health({ slug })\nScore the health of all deals for a customer based on activity recency, stage velocity,\nclose date proximity, and probability.\n- Input: { slug: string }\n- Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }\n\n### get_pipeline_forecast({ filter? })\nAggregate weighted pipeline revenue across all customers. Groups open deals by stage,\ncomputes probability-weighted expected revenue. Excludes won/lost deals.\n- Input: { filter?: string } — Optional filter by customer slug substring\n- Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count, weightedValue } } }\n\n### get_pipeline_stages()\nReturns all configured pipeline stages. Falls back to default stages if no custom stages configured.\n- Input: none\n- Returns: { stages: [{ id, label, order, probability, color, final }] }\n\n### summarize_meeting({ slug, transcript, with?, date? })\nLLM-summarize a meeting transcript and log it as a Meeting interaction.\nFalls back to raw text slice if LLM unavailable.\nRBAC: rep+\n- Input:\n slug: Customer ID\n transcript: Full meeting transcript text\n with?: Participant names\n date?: Meeting date YYYY-MM-DD (defaults to today)\n- Returns: { success, summary, nextSteps, sourceRef }\n\n### get_market_intelligence({ query, excludeCurrentCustomer?, slug? })\nSearch across all customers to find patterns, common topics, or similar issues.\nUses semantic search (LanceDB) across all customer knowledge bases.\nResults use slug (not real names) for privacy.\n- Input: { query: string, excludeCurrentCustomer?: boolean, slug?: string }\n- Returns: { query, results: CrossCustomerResult[], totalCustomersSearched }\n\n### get_relationship_graph({ slug })\nReturns the knowledge graph for a customer: contacts, companies, and their relationships.\nAuto-populated from every log_interaction call. Shows stakeholder map with champions, blockers,\neconomic buyers, and warm intro paths.\n- Input: { slug: string }\n- Returns: { nodeCount, edgeCount, updatedAt, stakeholders: { champions[], blockers[], economicBuyers[], allContacts[], missingRoles[] }, warmIntroPaths[], nodes[], edges[] }\n\n### get_relationship_health({ slug })\nReturns health scores (0-100, A-F grade) for all contacts. Scores decay when cadence breaks.\nRisk flags: NO_CONTACT_14D, NO_CONTACT_30D, CHAMPION_SILENT.\nRecomputes automatically if stale (>1h) or missing.\n- Input: { slug: string }\n- Returns: { overallHealth, updatedAt, atRiskContacts[], coldContacts[], contacts: ContactHealth[] }\n\n### run_deal_agent({ slug, dealName, autonomyLevel?, instruction?, valueThreshold? })\nAnalyzes deal situation (health, relationships, stakeholder gaps) via LLM (rule-based fallback).\nReturns prioritized action plan with confidence scores and full reasoning trace.\nautonomyLevel: \"observe\" (read-only) | \"suggest\" (queue for review, default) | \"act\" (auto-execute if confidence ≥ 0.7 and value < valueThreshold)\nRBAC: rep+\n- Input: { slug, dealName, autonomyLevel?: \"observe\"|\"suggest\"|\"act\", instruction?, valueThreshold?: number (default 50000) }\n- Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], trace }\n\n### approve_agent_action({ slug, actionId, approved })\nExecute (approved=true) or reject (approved=false) a pending deal agent action.\nFind actionId in run_deal_agent response.actionsQueued[].actionId\nRBAC: rep+\n- Input: { slug, actionId, approved: boolean }\n- Returns: { success, actionId, status }\n\n### simulate_revenue({ horizon?, iterations? })\nMonte Carlo simulation over all active deals. Adjusts probabilities via health score (D12) and\nchampion presence (D11). Returns P10/P50/P90 confidence interval + sensitivity map.\nhorizon: \"quarter\" (default) | \"year\"\n- Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMonth, topRisks, sensitivityMap }, confidence, dealCount, horizon }\n\n### get_playbook({ slug, stage?, value?, healthScore?, daysSinceContact?, championPresent? })\nReturns playbooks matching the current deal situation. Without deal context, returns all playbooks.\nPlaybooks are sorted by success rate (highest first). run_deal_agent uses playbooks automatically.\n- Input: slug (required) + optional deal context fields for trigger matching\n- Returns: { matches: [{ name, score, matchedConditions, trigger, successRate, usedCount, content }], totalPlaybooks, slug }\n\n### create_playbook({ slug, name, trigger, content, successRate? })\nCreate or update a playbook encoding proven tactics for a specific deal situation.\nTrigger DSL uses AND-only conditions: deal_stage_<s> | value > N | value < N | days_stalled > N | health < N | health > N | no_champion | has_champion\nRBAC: rep+\n- Input: slug, name, trigger (DSL string), content (markdown), successRate? (0–1, default 0.5)\n- Returns: { success: true, playbook: { name, trigger, successRate, usedCount, lastUpdated, path } }\n\n### list_playbooks({ slug })\nList all playbooks for a customer (metadata only — no body content for performance).\n- Input: { slug: string }\n- Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }\n\n### distill_playbook({ slug, dealName, outcome })\nLLM analyzes a deal's interaction history and extracts a reusable playbook.\nRun after every won or lost deal to build procedural memory.\nRBAC: rep+\noutcome: \"won\" | \"lost\"\n- Returns: { success: true, playbook: { name, trigger, successRate, path }, reasoning }\n\n### pursue_goal({ goal, deadline, context? })\nSet a revenue or pipeline goal and get an AI-decomposed action plan.\nAnalyzes current pipeline (P50 forecast) and decomposes the gap into prioritized sub-goals per deal.\nPersists goal to .agentic/goals.json for tracking.\nRBAC: manager+\n- Input: { goal: string, deadline: \"YYYY-MM-DD\", context?: string }\n- Returns: { goalId, description, target, deadline, decomposition: { analysis, currentPipeline, gap, subGoals, probabilisticOutcome } }\n\n### get_goal_status({ goalId? })\nGet the status of active goals. Without goalId, returns all active goals.\n- Input: { goalId?: string } — omit for all active goals\n- Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }\n\n### register_push_subscription({ provider, slug, webhookUrl, ... })\nRegister a real-time push subscription so providers send events in real-time (no polling).\nRBAC: admin only\n- Input:\n provider: \"gmail\" | \"microsoft-graph\" | \"slack\"\n slug: Customer slug to receive events for\n webhookUrl: Public HTTPS URL for provider callbacks\n gmailTopicName?: (Gmail) Cloud Pub/Sub topic name\n microsoftClientState?: (MS Graph) Secret for HMAC verification\n microsoftResource?: (MS Graph) Resource path\n slackTeamId?: (Slack) Workspace team ID\n slackChannelId?: (Slack) Optional specific channel\n- Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning? }\n\n### get_push_status({ slug?, provider? })\nShow all push subscriptions with expiry and event counts.\n- Input: { slug?: string, provider?: \"gmail\" | \"microsoft-graph\" | \"slack\" }\n- Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHours, needsRenewal, lastEventAt, eventsProcessed }], summary: { total, active, expiringSoon, expired } }\n\n### get_org_intelligence({ slug, dealName? })\nBuild a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.\n- Input: { slug: string, dealName?: string }\n- Returns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }\n\n### open_deal_room({ slug, dealName })\nMulti-agent deal brief: orchestrates relationship graph, health scores, deal health, Monte Carlo simulation, and playbook matching into a unified brief with executive summary, top priorities, and risk score (0–100).\n- Input: { slug: string, dealName: string }\n- Returns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }\n\n### get_proactive_briefing({ date? })\nGenerate a proactive daily briefing: urgent alerts (relationship decay, deal risk, overdue close dates),\npipeline forecast (P50/P90), and a single top-action recommendation.\n- Input: { date?: \"YYYY-MM-DD\" } — defaults to today\n- Returns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }\n\n### list_email_templates({ category? })\nList available email templates. Optionally filter by category.\n- Input: { category?: string } — e.g. \"outreach\", \"followup\", \"support\"\n- Returns: Array of { id, name, category, subject } (body excluded for performance)\n\n### get_email_template({ id })\nGet a specific email template with full body and detected template variables.\n- Input: { id: string } — Template ID (e.g. \"enterprise-intro\")\n- Returns: { id, name, category, subject, body, detectedVariables: string[] }\n\n### draft_email({ slug, templateId, overrides?, tone? })\nDraft a personalized email for a customer using a stored template.\nVariables are auto-filled from the customer's main_facts.md.\nOptional tone (e.g. \"formal\", \"friendly\", \"concise\") LLM-polishes the body;\nfalls back to plain template-fill without an ANTHROPIC_API_KEY.\nDoes NOT send automatically — returns the draft for review.\nRBAC: rep+\n- Input: { slug, templateId, overrides?: Record<string, string> }\n- Returns: { subject, body, to, slug, templateId, resolvedVariables }\n\n### enroll_in_sequence({ slug, contactEmail, sequenceId })\nEnroll a contact in an email sequence. Validates that the sequence and first template exist.\nRBAC: rep+\n- Input: { slug: string, contactEmail: string, sequenceId: string }\n- Returns: { enrollmentId, sequenceName, totalSteps }\n\n### list_sequence_enrollments({ slug?, status? })\nList email sequence enrollments. Filter by customer slug or status.\n- Input: { slug?: string, status?: \"active\" | \"paused\" | \"completed\" }\n- Returns: { enrollments: SequenceEnrollment[] }\n\n### unenroll_from_sequence({ enrollmentId })\nPause (soft-unenroll) a contact from an email sequence. Sets status to \"paused\".\nRBAC: rep+\n- Input: { enrollmentId: string }\n- Returns: { success: boolean }\n\n### list_sequences()\nList all defined email sequences with step count and current enrollment count.\n- Input: none\n- Returns: { sequences: [{ id, name, stepCount, enrollmentCount }] }\n\n### generate_quote({ slug, dealName, lineItems, vatPercent?, validUntilDays?, currency? })\nGenerate a professional HTML quote for a customer deal.\nCalculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.\nRBAC: rep+\n- Input:\n slug: Customer slug\n dealName: Deal name this quote is for\n lineItems: Array<{ description, quantity, unitPrice }>\n vatPercent?: VAT percentage (default 19)\n validUntilDays?: Quote validity in days (default 30)\n currency?: Currency code (default EUR)\n- Returns: { quoteNumber, htmlPath, total, subtotal, vat, vatPercent, currency, validUntil, status }\n\n### get_quote_status({ quoteNumber?, slug? })\nGet quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for customer).\n- Input: { quoteNumber?: string, slug?: string }\n- Returns (single): Full quote object with status: draft | sent | viewed | accepted | declined\n- Returns (list): { quotes: [...] }\n\n### get_booking_link({ slug, eventType?, prefillName? })\nGet a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.\nRequires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.\nRBAC: rep+\n- Input: { slug, eventType?: string, prefillName?: boolean }\n- Returns: { bookingUrl, eventType, duration }\n\n### create_ticket({ slug, title, description?, priority?, assignee? })\nCreate a support ticket. Auto-calculates SLA due date based on priority.\nSLA defaults: urgent=4h, high=24h, normal=72h, low=168h.\nRBAC: rep+\n- Input:\n slug: Customer slug\n title: Ticket title\n description?: Detailed description\n priority?: \"urgent\" | \"high\" | \"normal\" | \"low\" (default: normal)\n assignee?: Assignee name or email\n- Returns: { ticket } with id T-NNN, status=open, slaDue\n\n### update_ticket({ slug, ticketId, status?, assignee? })\nUpdate a ticket's status or assignee. Setting status=resolved auto-sets resolved date.\nRBAC: rep+\n- Input: { slug, ticketId, status?: \"open\"|\"in-progress\"|\"waiting\"|\"resolved\"|\"closed\", assignee?: string }\n- Returns: { ticket }\n\n### list_tickets({ slug?, status?, priority?, assignee? })\nList support tickets sorted by priority then date. Filter by any combination of fields.\n- Input: { slug?, status?: \"open\"|\"in-progress\"|\"waiting\"|\"resolved\"|\"closed\", priority?: \"urgent\"|\"high\"|\"normal\"|\"low\", assignee? }\n- Returns: { tickets: Array<{ slug, ticket }> }\n\n### close_ticket({ slug, ticketId, resolution? })\nClose a ticket and optionally log the resolution as an interaction in interactions.md.\nRBAC: rep+\n- Input: { slug, ticketId, resolution?: string }\n- Returns: { ticket } with status=closed\n\n### send_nps_survey({ slug, contactEmail, surveyId, serverUrl? })\nGenerate an NPS/CSAT survey email draft. Returns subject, HTML body, and a token-based response URL.\nDoes NOT send automatically — returns draft for review.\nRequires survey definition in .agentic/surveys/.\nRBAC: rep+\n- Input: { slug, contactEmail, surveyId, serverUrl?: string }\n- Returns: { token, subject, body, surveyUrl, note }\n\n### get_survey_results({ surveyId, slug? })\nCalculate NPS score and breakdown for a survey. Optionally filter to a single customer.\n- Input: { surveyId: string, slug?: string }\n- Returns: { surveyId, totalResponses, npsScore (-100 to 100), promoters, passives, detractors, responses: [{ slug, email, score, comment?, respondedAt }] }\n\n### search_knowledge_base({ query, category?, publicOnly?, limit? })\nFull-text search across all KB articles (title, body, tags).\n- Input: { query, category?: string, publicOnly?: boolean, limit?: number (default 10) }\n- Returns: { query, count, articles: [{ id, title, category, excerpt, public, tags }] }\n\n### create_kb_article({ id, title, body, category?, tags?, public?, sourceTicketId? })\nCreate a new knowledge base article. Articles are stored as Markdown in .agentic/knowledge-base/.\nReturns an error if an article with the same ID already exists.\nRBAC: rep+\n- Input:\n id: Article slug (e.g. \"troubleshoot-api-timeout\")\n title: Article title\n body: Article body in Markdown\n category?: Category (default: \"general\")\n tags?: Array of tags for search\n public?: Make publicly accessible (default: false)\n sourceTicketId?: Ticket this article was created from\n- Returns: { id, title, category, path }\n\n### backup_now({ remote?, note? })\nTrigger an immediate backup of all CRM data (customers/ + .agentic/).\nCreates a timestamped ZIP with SHA-256 manifest. Optionally uploads to S3/rsync/local.\nRBAC: admin\n- Input: { remote?: string, note?: string }\n- Returns: { path, createdAt, customerCount, fileCount, sizeMb, directories, verified, uploadedTo? }\n\n### list_backups({ limit? })\nList available CRM backups with metadata. Shows log-tracked backups first, falls back to directory scan.\n- Input: { limit?: number (default 10, max 50) }\n- Returns: { count, totalAvailable, backups: [{ filename, createdAt, sizeMb, verified, encrypted, customerCount, fileCount }] }\n\n## Data Structure\n\nCustomer data lives in \\`customers/<slug>/\\`:\n- \\`main_facts.md\\` — YAML frontmatter + free-text sections\n- \\`interactions.md\\` — Chronological log (newest first)\n- \\`pipeline.md\\` — Deal table in Markdown\n- \\`sources.json\\` — Gmail/transcript sync config per customer\n\nAgentic data lives in \\`.agentic/\\`:\n- \\`goals.json\\` — Active goals and decompositions\n- \\`push-subscriptions.json\\` — Real-time push registrations\n- \\`backup-log.json\\` — Backup history\n- \\`rbac.json\\` — Role assignments\n- \\`audit.log\\` — Full audit trail\n- \\`knowledge-base/\\` — KB articles\n- \\`quotes/\\` — Generated quote files\n\n## Response Format\n\nAlways cite sources (gmail://thread/... or file://...) when available.\n\n## Framework Integration\n\n| Framework | Tier | Config |\n|---|---|---|\n| Claude Code | 1 | CLAUDE.md + ~/.claude.json + .claude/settings.json |\n| Codex CLI | 1 | AGENTS.md + ~/.codex/config.toml |\n| Grok Build (xAI) | 1 | AGENTS.md + ~/.grok/user-settings.json + .grok/settings.json |\n| OpenClaw | 1 | SOUL.md + AGENTS.md + TOOLS.md |\n| Hermes Agent | 1 | SOUL.md + Skill |\n| Antigravity CLI | 1 | GEMINI.md + AGENTS.md + SKILL.md |\n| Cursor | 2 | .cursor/rules/datasynx-crm.mdc |\n| Windsurf | 2 | MCP config only |\n| Cline | 2 | MCP config only |\n| Claude Desktop | 2 | MCP config only |\n\n### Manual Grok Build configuration\n\\`\\`\\`json\n// ~/.grok/user-settings.json (mcpServers is an ARRAY in Grok, not a map)\n{\n \"mcpServers\": [\n {\n \"name\": \"datasynx-opencrm\",\n \"transport\": {\n \"type\": \"stdio\",\n \"command\": \"node\",\n \"args\": [\"/path/to/node_modules/datasynx-opencrm/dist/mcp.js\"],\n \"env\": { \"DXCRM_DATA_DIR\": \"~/.dxcrm\" }\n }\n }\n ]\n}\n\\`\\`\\`\nRun \\`grok inspect\\` to verify the server is discovered.\n\n## CLI Reference (Phase 2)\n\n### dxcrm status\nShow daemon status, customer count, sync ages, and unmatched transcript queue.\n\\`\\`\\`\ndxcrm status\ndxcrm status --unmatched # list full unmatched transcript queue\n\\`\\`\\`\n\n### dxcrm agent spawn <slug>\nSpawn a wake-triggered agent for a customer. Sends Telegram notifications on new email.\n\\`\\`\\`\ndxcrm agent spawn acme-corp --channel telegram\ndxcrm agent spawn acme-corp --channel telegram --chat-id 12345\ndxcrm agent status\ndxcrm agent remove acme-corp\n\\`\\`\\`\nRequires: \\`TELEGRAM_BOT_TOKEN\\` + \\`TELEGRAM_CHAT_ID\\` env vars.\n\n### dxcrm import <path>\nImport customers and interactions from HubSpot or generic CSV export.\n\\`\\`\\`\ndxcrm import contacts.csv --from csv\ndxcrm import hubspot-export.csv --from hubspot\ndxcrm import hubspot-export.csv --from hubspot --dry-run\n\\`\\`\\`\n- Two-pass: creates customers first, then imports activities\n- Idempotent: re-running skips already-imported rows\n- sourceRef format: \\`hubspot://activity/<id>\\` or \\`csv://row/<hash>\\`\n\n## CLI Reference (Phase 3 — Team)\n\n### dxcrm server start\nStart a shared HTTP MCP server. Multiple team members connect via URL.\n\\`\\`\\`\ndxcrm server start --data /mnt/crm-data --port 3847\ndxcrm server status\n\\`\\`\\`\nSet actor identity: \\`export DXCRM_ACTOR=alice\\`\n\n### dxcrm audit\nShow who changed what and when. Audit trail at \\`.agentic/audit.log\\`.\n\\`\\`\\`\ndxcrm audit # Last 20 entries\ndxcrm audit --slug acme-corp # Filter by customer\ndxcrm audit --actor alice # Filter by actor\n\\`\\`\\`\nLog format: \\`2026-06-01T09:14:00Z | alice | log_interaction | acme-corp | summary\\`\n\n### session ownership\n\\`\\`\\`\ndxcrm session open acme-corp --owner alice\n# or: DXCRM_ACTOR=alice dxcrm session open acme-corp\n\\`\\`\\`\n\\`get_active_session()\\` returns \\`{ owner: \"alice\", ... }\\` when owner is set.\n\n## CLI Reference (Phase 5 — Migration)\n\n### dxcrm import — Pipedrive API\n\\`\\`\\`\ndxcrm import --from pipedrive --mode api --token <tok> --url https://myco.pipedrive.com\n\\`\\`\\`\nTwo-pass: persons → customers, activities → interactions.\nsourceRef: \\`pipedrive://activity/<id>\\`\n\n### CSV LLM Field Mapping\nGeneric CSV imports now use LLM-assisted column detection (fallback to heuristics when ANTHROPIC_API_KEY is unset).\n\n## CLI Reference (Enterprise — Sprints R1–R5)\n\n### dxcrm stages\nManage custom pipeline stages.\n\\`\\`\\`\ndxcrm stages list # List all stages\ndxcrm stages set <id> <label> [--order N] [--probability N] [--color #hex] [--final]\ndxcrm stages delete <id> # Remove a stage\ndxcrm stages reset # Reset to defaults\n\\`\\`\\`\nDefault stages: lead → qualified → proposal → negotiation → won → lost\n\n### dxcrm rbac\nRole-based access control. Roles: admin > manager > rep.\n\\`\\`\\`\ndxcrm rbac set alice admin # Assign role\ndxcrm rbac show # List all roles\ndxcrm rbac check alice update_deal # Permission check\n\\`\\`\\`\nConfig: \\`.agentic/rbac.json\\` | Actor: \\`DXCRM_ACTOR\\` env var\nEnforcement: per MCP tool call | Default role: rep\n\n### dxcrm gdpr\nGDPR erasure with dry-run safety and audit trail.\n\\`\\`\\`\ndxcrm gdpr erase acme-corp # Dry-run (shows plan)\ndxcrm gdpr erase acme-corp --confirm # Permanent deletion\ndxcrm gdpr list-erasures # Erasure history\n\\`\\`\\`\nOn confirm: deletes customers/<slug>/, writes audit.log, appends gdpr-erasures.json\n\n### dxcrm security-report\nGenerate Markdown security questionnaire for procurement/SOC2 review.\n\\`\\`\\`\ndxcrm security-report\ndxcrm security-report --output sec-report.md\n\\`\\`\\`\n\n### Microsoft Outlook Sync\n\\`\\`\\`\ndxcrm sync --provider microsoft\n\\`\\`\\`\nPrerequisites: write \\`.agentic/microsoft-token.json\\` with \\`{ \"accessToken\": \"...\" }\\`\nsourceRef: \\`microsoft://message/<id>\\` | API: Microsoft Graph v1.0\n\n### Salesforce Import\n\\`\\`\\`\ndxcrm import --from salesforce --mode api --token <tok> --url https://myco.salesforce.com\n\\`\\`\\`\nTwo-pass: contacts → customers, tasks → interactions (WhoId attribution)\nsourceRef: \\`salesforce://task/<id>\\` | API: Salesforce REST v58.0\n\n## CLI Reference (D16 — Goal-Based Orchestration)\n\n### dxcrm goal set\nSet a goal and get a decomposed action plan based on current pipeline state.\n\\`\\`\\`\ndxcrm goal set \"Close €500k ARR this quarter\" --deadline 2026-09-30\n\\`\\`\\`\n\n### dxcrm goal status\nShow all active goals with progress bars and days remaining.\n\\`\\`\\`\ndxcrm goal status\n\\`\\`\\`\n\n### dxcrm goal update\nManually update goal progress (0–100%).\n\\`\\`\\`\ndxcrm goal update goal_abc123 --progress 45\n\\`\\`\\`\n\n### dxcrm goal cancel\nCancel an active goal.\n\\`\\`\\`\ndxcrm goal cancel goal_abc123\n\\`\\`\\`\n\n## CLI Reference (D17 — Real-Time Push Ingestion)\n\n### dxcrm push register\nRegister a push subscription so providers send events in real-time (no polling).\n\\`\\`\\`\ndxcrm push register acme-corp --provider gmail --webhook-url https://myserver.com/webhooks/gmail --topic-name projects/x/topics/gmail-push\ndxcrm push register acme-corp --provider microsoft-graph --webhook-url https://myserver.com/webhooks/microsoft --client-state <secret>\ndxcrm push register acme-corp --provider slack --webhook-url https://myserver.com/webhooks/slack --team-id T12345\n\\`\\`\\`\n\n### dxcrm push status\nShow all push subscriptions, expiry and events processed.\n\\`\\`\\`\ndxcrm push status\ndxcrm push status --slug acme-corp\ndxcrm push status --provider gmail\n\\`\\`\\`\n\n### dxcrm push revoke\nRevoke a push subscription by ID.\n\\`\\`\\`\ndxcrm push revoke psub_1716892800_a1b2c3\n\\`\\`\\`\n\n### dxcrm push renew\nRenew expiring push subscriptions (also runs automatically daily at 06:00).\n\\`\\`\\`\ndxcrm push renew --all\n\\`\\`\\`\n\n### register_push_subscription (MCP)\nRegister a real-time push subscription. Admin only.\n\\`\\`\\`\nregister_push_subscription({ provider: \"gmail\", slug: \"acme-corp\", webhookUrl: \"https://myserver.com/webhooks/gmail\", gmailTopicName: \"projects/x/topics/y\" })\n\\`\\`\\`\nReturns: { subscriptionId, provider, slug, status, expiresAt, warning? }\n\n### get_push_status (MCP)\nShow all push subscriptions with expiry and event counts.\n\\`\\`\\`\nget_push_status() // all subscriptions\nget_push_status({ slug: \"acme-corp\" }) // filter by customer\nget_push_status({ provider: \"gmail\" }) // filter by provider\n\\`\\`\\`\nReturns: { subscriptions: [...], summary: { total, active, expiringSoon, expired } }\n\n### get_org_intelligence (MCP)\nBuild a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.\n\\`\\`\\`\nget_org_intelligence({ slug: \"acme-corp\" })\nget_org_intelligence({ slug: \"acme-corp\", dealName: \"Enterprise License\" })\n\\`\\`\\`\nReturns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }\n\n### open_deal_room (MCP)\nMulti-agent deal brief: orchestrates stakeholder map, relationship health, deal health, Monte Carlo simulation, and playbook matching into a single structured brief.\n\\`\\`\\`\nopen_deal_room({ slug: \"acme-corp\", dealName: \"Enterprise License 2026\" })\n\\`\\`\\`\nReturns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }\n\n### get_proactive_briefing (MCP)\nGenerate a proactive daily briefing: urgent alerts (relationship decay, imminent close dates), opportunities (high-health customers with active pipeline), P50/P90 forecast, and a single top-action recommendation.\n\\`\\`\\`\nget_proactive_briefing() // today\nget_proactive_briefing({ date: \"2026-05-28\" }) // specific date\n\\`\\`\\`\nReturns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }\n\n## H2 — Email Templates\n\n### list_email_templates (MCP)\nList all saved email templates. Returns id, name, category, subject, and body preview.\n\\`\\`\\`\nlist_email_templates()\nlist_email_templates({ category: \"follow-up\" })\n\\`\\`\\`\nReturns: { templates: [{ id, name, category, subject, bodyPreview }] }\n\n### get_email_template (MCP)\nRetrieve a single email template with full body and all variables.\n\\`\\`\\`\nget_email_template({ id: \"proposal-follow-up\" })\n\\`\\`\\`\nReturns: { id, name, category, subject, body, variables: string[] }\n\n### draft_email (MCP)\nDraft a personalized email from a template, substituting variables from customer context.\n\\`\\`\\`\ndraft_email({ slug: \"acme-corp\", templateId: \"proposal-follow-up\", overrides: { subject: \"Following up on your proposal\" } })\n\\`\\`\\`\nReturns: { subject, body, suggestedTo, suggestedCc?, variables }\n\n## H1 — Email Sequences\n\n### enroll_in_sequence (MCP)\nEnroll a customer contact in a multi-step email sequence. Steps are sent automatically.\n\\`\\`\\`\nenroll_in_sequence({ slug: \"acme-corp\", sequenceId: \"onboarding-7day\", contactEmail: \"alice@acme.com\" })\n\\`\\`\\`\nReturns: { enrollmentId, slug, sequenceId, contactEmail, enrolledAt, nextStepDue, totalSteps }\n\n### list_sequence_enrollments (MCP)\nList active (and optionally completed) sequence enrollments.\n\\`\\`\\`\nlist_sequence_enrollments()\nlist_sequence_enrollments({ slug: \"acme-corp\", status: \"active\" })\n\\`\\`\\`\nReturns: { enrollments: [{ enrollmentId, slug, sequenceId, contactEmail, currentStep, nextStepDue, status }] }\n\n### unenroll_from_sequence (MCP)\nRemove a contact from an active sequence (marks as cancelled).\n\\`\\`\\`\nunenroll_from_sequence({ enrollmentId: \"enr_abc123\" })\n\\`\\`\\`\nReturns: { success: boolean, enrollmentId }\n\n### list_sequences (MCP)\nList all defined email sequences with step count and description.\n\\`\\`\\`\nlist_sequences()\n\\`\\`\\`\nReturns: { sequences: [{ id, name, description, steps: number, triggerOn? }] }\n\n## H4 — Quotes\n\n### generate_quote (MCP)\nGenerate a structured quote document for a customer deal.\n\\`\\`\\`\ngenerate_quote({ slug: \"acme-corp\", dealName: \"Enterprise License\", lineItems: [{ description: \"Platform (12 mo)\", quantity: 1, unitPrice: 24000 }], validDays: 30 })\n\\`\\`\\`\nReturns: { quoteId, slug, dealName, total, currency, validUntil, markdownTable, fullText }\n\n### get_quote_status (MCP)\nRetrieve a generated quote with full line items and total.\n\\`\\`\\`\nget_quote_status({ quoteId: \"Q-2026-001\" })\n\\`\\`\\`\nReturns: { quoteId, slug, dealName, lineItems, subtotal, total, validUntil, status }\n\n## H3 — Meeting Scheduler\n\n### get_booking_link (MCP)\nGet a scheduling link for a meeting with a customer. Configure via DXCRM_CALENDLY_URL or per-customer sources.json.\n\\`\\`\\`\nget_booking_link({ slug: \"acme-corp\", meetingType: \"demo\" })\n\\`\\`\\`\nReturns: { url, meetingType, calendarProvider, prefillEmail?, note? }\n\n## H6 — Ticket Management\n\n### create_ticket (MCP)\nCreate a support ticket. Auto-sets SLA due date: critical=4h, high=24h, medium=72h, low=168h.\n\\`\\`\\`\ncreate_ticket({ slug: \"acme-corp\", title: \"Login broken\", priority: \"high\", description: \"Cannot login since yesterday\", assignee: \"alice\" })\n\\`\\`\\`\nReturns: { ticketId, slug, title, priority, status, slaDue, assignee?, createdAt }\n\n### update_ticket (MCP)\nUpdate ticket status or assignee.\n\\`\\`\\`\nupdate_ticket({ slug: \"acme-corp\", ticketId: \"T-001\", status: \"in-progress\", assignee: \"bob\" })\n\\`\\`\\`\nReturns: { ticketId, status, updatedAt }\n\n### list_tickets (MCP)\nList tickets sorted by priority. Filter by customer, status, priority, or assignee.\n\\`\\`\\`\nlist_tickets()\nlist_tickets({ slug: \"acme-corp\", status: \"open\" })\nlist_tickets({ priority: \"high\", assignee: \"alice\" })\n\\`\\`\\`\nReturns: { tickets: [{ ticketId, slug, title, priority, status, slaDue, assignee?, createdAt }] }\n\n### close_ticket (MCP)\nClose a ticket and optionally log a resolution note to interactions.md.\n\\`\\`\\`\nclose_ticket({ slug: \"acme-corp\", ticketId: \"T-001\", resolution: \"Fixed by updating oauth token\" })\n\\`\\`\\`\nReturns: { ticketId, status: \"closed\", closedAt, resolution? }\n\n## H7 — NPS/CSAT Survey Engine\n\n### send_nps_survey (MCP)\nGenerate a survey token and HTML email body. Customers click a score button (0–10) which\nposts to your server's /survey/respond endpoint. Set DXCRM_SERVER_URL or pass serverUrl.\n\\`\\`\\`\nsend_nps_survey({ slug: \"acme-corp\", contactEmail: \"alice@acme.com\", surveyId: \"q1-nps\" })\nsend_nps_survey({ slug: \"acme-corp\", contactEmail: \"alice@acme.com\", surveyId: \"q1-nps\", serverUrl: \"https://crm.myco.com\" })\n\\`\\`\\`\nReturns: { token, emailSubject, emailBody (HTML), surveyId, expiresAt }\n\n### get_survey_results (MCP)\nCalculate NPS score and breakdown by promoter/passive/detractor.\n\\`\\`\\`\nget_survey_results({ surveyId: \"q1-nps\" })\nget_survey_results({ surveyId: \"q1-nps\", slug: \"acme-corp\" })\n\\`\\`\\`\nReturns: { surveyId, npsScore (-100 to 100), responseCount, promoters, passives, detractors, responses: [{ slug, contactEmail, score, comment?, respondedAt }] }\n\n## H8 — Knowledge Base\n\n### search_knowledge_base (MCP)\nFull-text search across all KB articles (title, body, tags).\n\\`\\`\\`\nsearch_knowledge_base({ query: \"password reset\" })\nsearch_knowledge_base({ query: \"billing\", publicOnly: true })\n\\`\\`\\`\nReturns: { results: [{ id, title, category, excerpt, public, tags }] }\n\n### create_kb_article (MCP)\nCreate or update a knowledge base article (upserts by ID).\n\\`\\`\\`\ncreate_kb_article({ id: \"password-reset\", title: \"How to reset your password\", body: \"## Steps\\\\n1. Go to login...\", category: \"account\", tags: [\"password\", \"auth\"], public: true })\n\\`\\`\\`\nReturns: { id, title, createdAt, updatedAt, public }\n\n## Enterprise Backup\n\n### backup_now (MCP)\nTrigger an immediate backup of customers/ + .agentic/. Creates a timestamped ZIP with\nSHA-256 manifest. Optionally encrypts (AES-256-GCM) and uploads to S3/rsync/local.\n\\`\\`\\`\nbackup_now({})\nbackup_now({ remote: \"s3://my-bucket/crm-backups/\", note: \"Pre-migration backup\" })\n\\`\\`\\`\nReturns: { path, createdAt, customerCount, fileCount, sizeMb, directories, verified, uploadedTo? }\n\n### list_backups (MCP)\nList available CRM backups with metadata from .agentic/backup-log.json.\nFalls back to directory scan if log unavailable.\n\\`\\`\\`\nlist_backups({ limit: 10 })\n\\`\\`\\`\nReturns: { count, totalAvailable, backups: [{ filename, createdAt, sizeMb, verified, encrypted, customerCount, fileCount }] }\n\n### trigger_sync (MCP)\nForce an immediate Gmail sync without waiting for the 30-minute daemon cycle.\n\\`\\`\\`\ntrigger_sync({ slug: \"acme-corp\" }) // sync one customer\ntrigger_sync({}) // sync all customers\ntrigger_sync({ since: \"2026-06-01\" }) // sync from specific date\n\\`\\`\\`\nReturns: { success, synced, skipped, customers: [...], errors: [...] }\n\n### get_audit_log (MCP)\nRead the append-only CRM audit log of all write operations.\n\\`\\`\\`\nget_audit_log({}) // last 50 entries\nget_audit_log({ slug: \"acme-corp\" }) // filtered by customer\nget_audit_log({ actor: \"alice\", limit: 20 }) // filtered by actor\n\\`\\`\\`\nReturns: { total, returned, entries: [{ timestamp, actor, tool, slug, summary }] }\n`.trim();\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { CAPABILITIES_TEXT } from \"../capabilities.js\";\n\nexport async function handleGetCapabilities(): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n return {\n content: [{ type: \"text\", text: CAPABILITIES_TEXT }],\n };\n}\n\nexport function registerGetCapabilities(server: McpServer): void {\n server.registerTool(\n \"get_capabilities\",\n {\n title: \"Get Capabilities\",\n description:\n \"Returns all available MCP tools, their inputs, and the CRM workflow guide. \" +\n \"Call this first to understand what DatasynxOpenCRM can do.\",\n inputSchema: z.object({}),\n },\n async () => handleGetCapabilities()\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getSession } from \"../../core/session-store.js\";\n\nexport interface ActiveSessionResult {\n hasSession: boolean;\n customerSlug?: string;\n customerName?: string;\n startedAt?: string;\n owner?: string;\n}\n\nexport async function handleGetActiveSession(): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const session = getSession();\n\n const result: ActiveSessionResult = session\n ? {\n hasSession: true,\n customerSlug: session.customerSlug,\n customerName: session.customerName,\n startedAt: session.startedAt,\n ...(session.owner !== undefined ? { owner: session.owner } : {}),\n }\n : { hasSession: false };\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n };\n}\n\nexport function registerGetActiveSession(server: McpServer): void {\n server.registerTool(\n \"get_active_session\",\n {\n title: \"Get Active Session\",\n description:\n \"Check which customer is currently active in the session store. \" +\n \"Returns session info if a customer session is open, otherwise returns hasSession: false.\",\n inputSchema: z.object({}),\n },\n async () => handleGetActiveSession()\n );\n}\n","/**\n * Escape a string so it can be embedded safely as a literal inside a `RegExp`.\n * Prevents both broken patterns and ReDoS/injection when interpolating\n * field names, section headers, or other dynamic values into a regex.\n */\nexport function escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { escapeRegExp } from \"./regex.js\";\n\nconst MAX_INTERACTIONS = 10;\n\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nfunction parseRecentInteractions(filePath: string, limit: number): string {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n\n // Split on ## date headings\n const entries = content.split(/(?=^## \\d{4}-\\d{2}-\\d{2})/m).filter((e) => e.trim());\n const recent = entries.slice(0, limit);\n return recent.join(\"\\n\").trim();\n}\n\nfunction parsePipelineContent(filePath: string): string {\n if (!fs.existsSync(filePath)) return \"\";\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n return content.trim();\n}\n\nfunction extractSection(content: string, sectionName: string): string {\n const regex = new RegExp(`## ${escapeRegExp(sectionName)}([\\\\s\\\\S]*?)(?=^## |$)`, \"m\");\n const match = regex.exec(content);\n return match ? (match[1] ?? \"\").trim() : \"\";\n}\n\nexport async function buildContext(dataDir: string, slug: string): Promise<string> {\n const customerDir = path.join(dataDir, \"customers\", slug);\n\n if (!fs.existsSync(customerDir)) {\n throw new Error(`Customer '${slug}' not found`);\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n const pipelinePath = path.join(customerDir, \"pipeline.md\");\n\n // Read main_facts.md\n let mainContent = \"\";\n let frontmatterStr = \"\";\n if (fs.existsSync(mainFactsPath)) {\n const fileContent = fs.readFileSync(mainFactsPath, \"utf-8\") as string;\n const raw = matter(fileContent);\n mainContent = raw.content ?? \"\";\n frontmatterStr = Object.entries(raw.data as Record<string, unknown>)\n .map(([k, v]) => `${k}: ${JSON.stringify(v)}`)\n .join(\"\\n\");\n }\n\n const quickRef = extractSection(mainContent, \"Quick Reference\");\n const contacts = extractSection(mainContent, \"Contacts\");\n const criticalContext = extractSection(mainContent, \"Critical Context\");\n const openQuestions = extractSection(mainContent, \"Open Questions\");\n\n const recentActivity = parseRecentInteractions(interactionsPath, MAX_INTERACTIONS);\n const pipelineContent = parsePipelineContent(pipelinePath);\n\n const sections: string[] = [\n `# Customer Context: ${slug}`,\n \"\",\n \"## Metadata\",\n frontmatterStr || \"(no metadata)\",\n \"\",\n \"## Quick Reference\",\n quickRef || \"(not set)\",\n \"\",\n \"## Contacts\",\n contacts || \"(not set)\",\n \"\",\n \"## Critical Context\",\n criticalContext || \"(not set)\",\n \"\",\n \"## Recent Activity (last 10 interactions)\",\n recentActivity || \"(no interactions yet)\",\n \"\",\n \"## Pipeline\",\n pipelineContent || \"(no deals)\",\n \"\",\n \"## Open Questions\",\n openQuestions || \"(none)\",\n ];\n\n const raw = sections.join(\"\\n\");\n const tokenEstimate = estimateTokens(raw);\n\n // If over 3000 tokens, trim interactions\n if (tokenEstimate > 3000) {\n const trimmedActivity = parseRecentInteractions(interactionsPath, 5);\n const trimmedSections: string[] = [\n `# Customer Context: ${slug}`,\n \"\",\n \"## Metadata\",\n frontmatterStr || \"(no metadata)\",\n \"\",\n \"## Quick Reference\",\n quickRef || \"(not set)\",\n \"\",\n \"## Contacts\",\n contacts || \"(not set)\",\n \"\",\n \"## Critical Context\",\n criticalContext || \"(not set)\",\n \"\",\n \"## Recent Activity (last 5 interactions — trimmed for token budget)\",\n trimmedActivity || \"(no interactions yet)\",\n \"\",\n \"## Pipeline\",\n pipelineContent || \"(no deals)\",\n \"\",\n \"## Open Questions\",\n openQuestions || \"(none)\",\n ];\n return trimmedSections.join(\"\\n\");\n }\n\n return raw;\n}\n\n/** Robust section-body extractor: from a `## Name` heading to the next `## ` heading. */\nfunction sectionBody(content: string, name: string): string {\n const lines = content.split(\"\\n\");\n const start = lines.findIndex((l) => l.trim() === `## ${name}`);\n if (start < 0) return \"\";\n const body: string[] = [];\n for (let i = start + 1; i < lines.length; i++) {\n if (lines[i]!.startsWith(\"## \")) break;\n body.push(lines[i]!);\n }\n return body.join(\"\\n\").trim();\n}\n\nexport interface ContextBlock {\n slug: string;\n metadata: Record<string, unknown>;\n quickReference: string;\n contacts: string;\n criticalContext: string;\n openQuestions: string;\n recentActivity: string;\n pipeline: string;\n}\n\n/**\n * Structured variant of buildContext (REF-2): returns a typed object instead of\n * a markdown string, for callers that need fields programmatically (e.g. MCP\n * responses, SDK consumers). buildContext remains the token-budgeted string form.\n */\nexport async function buildContextBlock(\n dataDir: string,\n slug: string,\n role?: \"admin\" | \"manager\" | \"rep\"\n): Promise<ContextBlock> {\n const customerDir = path.join(dataDir, \"customers\", slug);\n if (!fs.existsSync(customerDir)) {\n throw new Error(`Customer '${slug}' not found`);\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n const pipelinePath = path.join(customerDir, \"pipeline.md\");\n\n let mainContent = \"\";\n let metadata: Record<string, unknown> = {};\n if (fs.existsSync(mainFactsPath)) {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\") as string);\n mainContent = raw.content ?? \"\";\n metadata = raw.data as Record<string, unknown>;\n }\n\n // Field-level security: redact metadata fields the role may not see.\n if (role) {\n const { loadFieldAcl, redactFields } = await import(\"./rbac.js\");\n metadata = redactFields(metadata, role, loadFieldAcl(dataDir));\n }\n\n return {\n slug,\n metadata,\n quickReference: sectionBody(mainContent, \"Quick Reference\"),\n contacts: sectionBody(mainContent, \"Contacts\"),\n criticalContext: sectionBody(mainContent, \"Critical Context\"),\n openQuestions: sectionBody(mainContent, \"Open Questions\"),\n recentActivity: parseRecentInteractions(interactionsPath, MAX_INTERACTIONS),\n pipeline: parsePipelineContent(pipelinePath),\n };\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildContext } from \"../../core/context-builder.js\";\nimport { getSession } from \"../../core/session-store.js\";\nimport { getLastGmailSync, updateSlugSyncState } from \"../../fs/sync-state.js\";\nimport { getGmailAuth } from \"../../core/oauth-store.js\";\nimport { canSeeCustomer } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nfunction triggerOnQuerySync(dataDir: string, slug: string): void {\n const auth = getGmailAuth();\n if (!auth) return;\n\n const lastSync = getLastGmailSync(dataDir, slug);\n const thirtyMinAgo = new Date(Date.now() - 30 * 60 * 1000);\n if (lastSync && lastSync >= thirtyMinAgo) return;\n\n const sourcesPath = path.join(dataDir, \"customers\", slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) return;\n\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\") as string) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n if (!sources.gmail?.enabled || !sources.gmail.query) return;\n\n const query = sources.gmail.query;\n void import(\"../../sync/gmail-sync.js\")\n .then(({ syncGmail }) =>\n syncGmail({ slug, dataDir, auth, query })\n .then(() =>\n updateSlugSyncState(dataDir, slug, { lastGmailSync: new Date().toISOString() })\n )\n .catch(() => {})\n )\n .catch(() => {});\n } catch {\n // non-critical\n }\n}\n\nexport async function handleGetCustomerContext(\n input: { slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n}> {\n const targetSlug = input.slug ?? getSession()?.customerSlug;\n\n if (!targetSlug) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No customer specified and no active session. Use: get_customer_context({ slug: 'acme-corp' })\",\n },\n ],\n isError: true,\n };\n }\n\n // RBAC data-visibility: rep role only sees owned customers\n const actor = process.env[\"DXCRM_ACTOR\"] ?? \"system\";\n if (!canSeeCustomer(dataDir, actor, targetSlug)) {\n return {\n content: [\n { type: \"text\", text: `Access denied: '${actor}' cannot view customer '${targetSlug}'.` },\n ],\n isError: true,\n };\n }\n\n // Fire-and-forget on-query sync — does not block context return\n triggerOnQuerySync(dataDir, targetSlug);\n\n try {\n const context = await buildContext(dataDir, targetSlug);\n return {\n content: [{ type: \"text\", text: context }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${(err as Error).message}`,\n },\n ],\n isError: true,\n };\n }\n}\n\nexport function registerGetCustomerContext(server: McpServer): void {\n server.registerTool(\n \"get_customer_context\",\n {\n title: \"Get Customer Context\",\n description: `Returns a complete, LLM-ready context block for a customer.\nUse this before any customer-related conversation or action.\nAutomatically triggers a background Gmail sync if last sync was >30 minutes ago.\n\nArgs:\n slug: Customer ID (e.g. \"acme-corp\"). Leave empty to use active session customer.\n\nReturns: Structured markdown with Quick Reference, Contacts, Critical Context,\nRecent Activity (last 10 interactions), Pipeline, and Open Questions.\n\nPerformance: <3 seconds. Token budget: <3000.`,\n inputSchema: z.object({\n slug: z\n .string()\n .optional()\n .describe(\"Customer slug (e.g. 'acme-corp'). Leave empty for active session customer.\"),\n }),\n },\n async ({ slug }) => handleGetCustomerContext({ ...(slug !== undefined ? { slug } : {}) })\n );\n}\n","import { pipeline, env, type FeatureExtractionPipeline } from \"@huggingface/transformers\";\nimport path from \"path\";\nimport os from \"os\";\n\nenv.cacheDir =\n process.env[\"HF_CACHE_DIR\"] ?? path.join(os.homedir(), \".cache\", \"datasynx-opencrm\", \"models\");\n\nclass EmbeddingPipeline {\n private static instance: Promise<FeatureExtractionPipeline> | null = null;\n\n static get(): Promise<FeatureExtractionPipeline> {\n if (!this.instance) {\n console.error(\"Loading embedding model (first time, ~25MB)...\");\n this.instance = pipeline(\n \"feature-extraction\",\n \"Xenova/all-MiniLM-L6-v2\"\n ) as Promise<FeatureExtractionPipeline>;\n }\n return this.instance;\n }\n\n static reset(): void {\n this.instance = null;\n }\n}\n\nexport async function embedText(text: string): Promise<Float32Array> {\n const extractor = await EmbeddingPipeline.get();\n const output = await extractor(text, { pooling: \"mean\", normalize: true });\n return (output as unknown as Array<{ data: Float32Array }>)[0]?.data ?? new Float32Array(384);\n}\n\nexport async function embedBatch(texts: string[]): Promise<Float32Array[]> {\n if (texts.length === 0) return [];\n const extractor = await EmbeddingPipeline.get();\n const output = await extractor(texts, { pooling: \"mean\", normalize: true });\n return (output as unknown as Array<{ data: Float32Array }>).map(\n (o) => o.data ?? new Float32Array(384)\n );\n}\n\nexport function resetEmbeddingPipeline(): void {\n EmbeddingPipeline.reset();\n}\n","import * as lancedb from \"@lancedb/lancedb\";\nimport { Index } from \"@lancedb/lancedb\";\nimport { makeArrowTable } from \"@lancedb/lancedb\";\nimport { Schema, Field, FixedSizeList, Float32 as ArrowFloat32, Utf8 } from \"apache-arrow\";\nimport path from \"path\";\nimport { embedText } from \"./embedder.js\";\nimport { logger } from \"./logger.js\";\n\nlet _db: lancedb.Connection | null = null;\n\nasync function getDb(dataDir: string): Promise<lancedb.Connection> {\n if (!_db) {\n const dbPath = path.join(dataDir, \".agentic\", \"lancedb\");\n _db = await lancedb.connect(dbPath);\n }\n return _db;\n}\n\n// Reset connection (useful for testing)\nexport function resetConnection(): void {\n _db = null;\n}\n\nconst CUSTOMER_TABLE_SCHEMA = new Schema([\n new Field(\"source_ref\", new Utf8(), false),\n new Field(\"text\", new Utf8(), false),\n new Field(\"date\", new Utf8(), false),\n new Field(\"type\", new Utf8(), false),\n new Field(\"vector\", new FixedSizeList(384, new Field(\"item\", new ArrowFloat32(), true)), false),\n]);\n\nasync function getOrCreateCustomerTable(\n db: lancedb.Connection,\n tableName: string\n): Promise<lancedb.Table> {\n const tableNames: string[] = await db.tableNames();\n if (!tableNames.includes(tableName)) {\n const table = await db.createEmptyTable(tableName, CUSTOMER_TABLE_SCHEMA);\n await table.createIndex(\"source_ref\", { config: Index.btree() });\n return table;\n }\n return db.openTable(tableName);\n}\n\nexport async function indexInLanceDB(\n dataDir: string,\n slug: string,\n text: string,\n sourceRef: string,\n meta?: { date?: string; type?: string }\n): Promise<void> {\n try {\n const vectorFloat32 = await embedText(text);\n const db = await getDb(dataDir);\n const tableName = `docs_${slug.replace(/[^a-z0-9]/gi, \"_\")}`;\n const table = await getOrCreateCustomerTable(db, tableName);\n\n const date = meta?.date ?? new Date().toISOString().slice(0, 10);\n const type = meta?.type ?? \"unknown\";\n\n const data = makeArrowTable([\n {\n source_ref: sourceRef,\n text: text.slice(0, 2000),\n date,\n type,\n vector: Array.from(vectorFloat32),\n },\n ]);\n\n await table\n .mergeInsert(\"source_ref\")\n .whenMatchedUpdateAll()\n .whenNotMatchedInsertAll()\n .execute(data);\n } catch (err) {\n logger.error(\"lancedb\", \"indexInLanceDB failed\", { error: (err as Error).message });\n }\n}\n\nexport async function dropCustomerTable(dataDir: string, slug: string): Promise<void> {\n try {\n const db = await getDb(dataDir);\n const tableName = `docs_${slug.replace(/[^a-z0-9]/gi, \"_\")}`;\n const tableNames: string[] = await db.tableNames();\n if (tableNames.includes(tableName)) {\n await db.dropTable(tableName);\n }\n } catch (err) {\n logger.error(\"lancedb\", \"dropCustomerTable failed\", { error: (err as Error).message });\n }\n}\n\nexport async function searchKnowledge(\n dataDir: string,\n slug: string,\n query: string,\n limit: number\n): Promise<Array<{ content: string; score: number; source: string }>> {\n try {\n const vectorFloat32 = await embedText(query);\n const db = await getDb(dataDir);\n const tableName = `docs_${slug.replace(/[^a-z0-9]/gi, \"_\")}`;\n\n // Check if table exists\n const tableNames: string[] = await db.tableNames();\n if (!tableNames.includes(tableName)) {\n return [];\n }\n\n const table = await db.openTable(tableName);\n\n const results = await table.search(Array.from(vectorFloat32)).limit(limit).toArray();\n\n return results.map((r: Record<string, unknown>) => ({\n content: String(r[\"text\"] ?? \"\"),\n score: typeof r[\"_distance\"] === \"number\" ? 1 - r[\"_distance\"] : 1,\n source: String(r[\"source_ref\"] ?? \"\"),\n }));\n } catch {\n // If LanceDB table doesn't exist or search fails, return empty array\n return [];\n }\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { searchKnowledge } from \"../../core/lancedb.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSearchCustomerKnowledge(\n input: { slug: string; query: string; limit?: number },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const limit = input.limit ?? 5;\n\n try {\n const results = await searchKnowledge(dataDir, input.slug, input.query, limit);\n\n const response =\n results.length === 0\n ? {\n results: [],\n message:\n `No results found for \"${input.query}\" in customer \"${input.slug}\". ` +\n \"The customer may not have been synced yet. Run dxcrm sync to index emails and transcripts.\",\n }\n : { results };\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(response, null, 2) }],\n };\n } catch {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n results: [],\n message: `Search unavailable for customer \"${input.slug}\". LanceDB may not be initialized.`,\n }),\n },\n ],\n };\n }\n}\n\nexport function registerSearchCustomerKnowledge(server: McpServer): void {\n server.registerTool(\n \"search_customer_knowledge\",\n {\n title: \"Search Customer Knowledge\",\n description: `Hybrid vector + full-text search across all emails and transcripts for a customer.\nUse when you need to find specific information from past communications.\n\nArgs:\n slug: Customer ID (e.g. \"acme-corp\")\n query: Natural language search query (e.g. \"pricing discussion\", \"GDPR concerns\")\n limit: Max results to return (default 5)\n\nReturns: { results: Array<{ content, score, source }> }\nIf no results: returns empty array with a helpful sync suggestion.`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n query: z.string().describe(\"Search query (natural language or keywords)\"),\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Max results to return (default 5)\"),\n }),\n },\n async ({ slug, query, limit }) =>\n handleSearchCustomerKnowledge({\n slug,\n query,\n ...(limit !== undefined ? { limit } : {}),\n })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { customerVisibility } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport interface CustomerSummary {\n slug: string;\n name: string;\n stage: string;\n lastInteraction?: string | undefined;\n dealValue?: number | undefined;\n}\n\nfunction extractLastInteractionDate(interactionsPath: string): string | undefined {\n if (!fs.existsSync(interactionsPath)) return undefined;\n\n const content = fs.readFileSync(interactionsPath, \"utf-8\") as string;\n // Match first ## YYYY-MM-DD heading\n const match = /^## (\\d{4}-\\d{2}-\\d{2})/m.exec(content);\n return match ? match[1] : undefined;\n}\n\nexport async function handleListCustomers(\n input: { filter?: string },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const customersDir = path.join(dataDir, \"customers\");\n const customers: CustomerSummary[] = [];\n\n if (!fs.existsSync(customersDir)) {\n return {\n content: [{ type: \"text\", text: JSON.stringify([], null, 2) }],\n };\n }\n\n const entries = fs.readdirSync(customersDir) as string[];\n\n // Resolve RBAC visibility once (reads rbac.json a single time, not per customer).\n const actor = process.env[\"DXCRM_ACTOR\"] ?? \"system\";\n const canSee = customerVisibility(dataDir, actor);\n\n for (const entry of entries) {\n const customerDir = path.join(customersDir, entry);\n\n // Skip if not a directory\n try {\n const stat = fs.statSync(customerDir);\n if (!stat.isDirectory()) continue;\n } catch {\n continue;\n }\n\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n if (!fs.existsSync(mainFactsPath)) continue;\n\n try {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\") as string);\n const data = raw.data as Record<string, unknown>;\n\n const name = typeof data[\"name\"] === \"string\" ? data[\"name\"] : entry;\n const stage =\n typeof data[\"relationship_stage\"] === \"string\" ? data[\"relationship_stage\"] : \"unknown\";\n const dealValue = typeof data[\"deal_value\"] === \"number\" ? data[\"deal_value\"] : undefined;\n\n const lastInteraction = extractLastInteractionDate(path.join(customerDir, \"interactions.md\"));\n\n const summary: CustomerSummary = {\n slug: entry,\n name,\n stage,\n ...(lastInteraction !== undefined ? { lastInteraction } : {}),\n ...(dealValue !== undefined ? { dealValue } : {}),\n };\n\n // Apply filter if provided (name, slug, or stage)\n if (input.filter) {\n const filterLower = input.filter.toLowerCase();\n const matches =\n name.toLowerCase().includes(filterLower) ||\n entry.toLowerCase().includes(filterLower) ||\n stage.toLowerCase().includes(filterLower);\n if (!matches) continue;\n }\n\n // RBAC data-visibility: rep role only sees owned customers\n if (!canSee(entry)) continue;\n\n customers.push(summary);\n } catch {\n // Skip customers with malformed data\n continue;\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(customers, null, 2) }],\n };\n}\n\nexport function registerListCustomers(server: McpServer): void {\n server.registerTool(\n \"list_customers\",\n {\n title: \"List Customers\",\n description: `List all customers with their pipeline stage, last interaction date, and deal value.\nUseful for morning briefings and pipeline overviews.\n\nArgs:\n filter: Optional substring to filter by name or slug (case-insensitive)\n\nReturns: Array of { slug, name, stage, lastInteraction?, dealValue? }`,\n inputSchema: z.object({\n filter: z\n .string()\n .optional()\n .describe(\"Substring filter on customer name or slug (case-insensitive)\"),\n }),\n },\n async ({ filter }) => handleListCustomers({ ...(filter !== undefined ? { filter } : {}) })\n );\n}\n","import fs from \"fs\";\nimport { withFileQueue } from \"../fs/write-queue.js\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\n\nexport async function withJsonFile<T>(\n filePath: string,\n updater: (current: T | null) => T | Promise<T>\n): Promise<T> {\n return withFileQueue(filePath, async () => {\n // Read current state\n let current: T | null = null;\n if (fs.existsSync(filePath)) {\n try {\n current = JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as T;\n } catch {\n current = null;\n }\n }\n\n // Apply updater — may throw, in which case we do NOT write\n const next = await updater(current);\n\n // Serialized by the queue lock AND crash-safe via temp-file + rename.\n writeFileAtomic(filePath, JSON.stringify(next, null, 2));\n\n return next;\n });\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { withJsonFile } from \"./file-lock.js\";\nimport { logger } from \"./logger.js\";\n\nexport type NodeType = \"person\" | \"company\" | \"deal\" | \"product\" | \"event\";\n\nexport type EdgeType =\n | \"KNOWS\"\n | \"WORKS_AT\"\n | \"IS_CHAMPION\"\n | \"IS_BLOCKER\"\n | \"IS_ECONOMIC_BUYER\"\n | \"INTRODUCED_BY\"\n | \"OWNS_DEAL\"\n | \"COMPETES_WITH\";\n\nexport interface GraphNode {\n id: string;\n type: NodeType;\n label: string;\n status?: \"active\" | \"inactive\";\n properties: {\n email?: string;\n title?: string;\n company?: string;\n domain?: string;\n [key: string]: unknown;\n };\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface GraphEdge {\n id: string;\n from: string;\n to: string;\n type: EdgeType;\n weight: number;\n sentiment: number;\n lastContact: string;\n contactCount: number;\n properties: Record<string, unknown>;\n // Bi-temporal fields (Graphiti-style), optional for backward compatibility:\n // valid time = when the fact held in the world; transaction time = when the\n // system recorded/retracted it. Edges are invalidated, never deleted.\n validFrom?: string;\n validTo?: string;\n recordedAt?: string;\n invalidatedAt?: string;\n}\n\nexport interface CustomerGraph {\n schemaVersion: \"1\";\n slug: string;\n nodes: GraphNode[];\n edges: GraphEdge[];\n updatedAt: string;\n}\n\nexport type StakeholderRole = \"champion\" | \"blocker\" | \"economic_buyer\" | \"user\";\n\nexport interface MissingRole {\n role: \"champion\" | \"economic_buyer\";\n urgency: \"critical\" | \"important\";\n suggestion: string;\n}\n\nexport interface StakeholderSummary {\n champions: GraphNode[];\n blockers: GraphNode[];\n economicBuyers: GraphNode[];\n allContacts: GraphNode[];\n missingRoles: MissingRole[];\n}\n\n// ─── File path ────────────────────────────────────────────────────────────────\n\nexport function graphPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"graph.json\");\n}\n\nexport function emptyGraph(slug: string): CustomerGraph {\n return {\n schemaVersion: \"1\",\n slug,\n nodes: [],\n edges: [],\n updatedAt: new Date().toISOString(),\n };\n}\n\n// ─── Read / Write ─────────────────────────────────────────────────────────────\n\nexport function readGraph(dataDir: string, slug: string): CustomerGraph {\n const p = graphPath(dataDir, slug);\n if (!fs.existsSync(p)) return emptyGraph(slug);\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\")) as CustomerGraph;\n } catch {\n logger.warn(\"graph\", \"failed to parse graph file — returning empty graph\", { path: p });\n return emptyGraph(slug);\n }\n}\n\nexport async function writeGraph(\n dataDir: string,\n slug: string,\n updater: (current: CustomerGraph | null) => CustomerGraph\n): Promise<CustomerGraph> {\n return withJsonFile<CustomerGraph>(graphPath(dataDir, slug), (current) => {\n const g = updater(current);\n return { ...g, updatedAt: new Date().toISOString() };\n });\n}\n\n// ─── Node operations ──────────────────────────────────────────────────────────\n\nexport function upsertNode(\n graph: CustomerGraph,\n node: Omit<GraphNode, \"createdAt\" | \"updatedAt\">\n): CustomerGraph {\n const now = new Date().toISOString();\n const idx = graph.nodes.findIndex((n) => n.id === node.id);\n if (idx !== -1) {\n const existing = graph.nodes[idx]!;\n const updated: GraphNode = {\n ...existing,\n label: node.label || existing.label,\n properties: { ...existing.properties, ...node.properties },\n updatedAt: now,\n };\n const nodes = [...graph.nodes];\n nodes[idx] = updated;\n return { ...graph, nodes };\n }\n const newNode: GraphNode = { ...node, createdAt: now, updatedAt: now };\n return { ...graph, nodes: [...graph.nodes, newNode] };\n}\n\nexport function findNode(graph: CustomerGraph, id: string): GraphNode | undefined {\n return graph.nodes.find((n) => n.id === id);\n}\n\nexport function findNodesByType(graph: CustomerGraph, type: NodeType): GraphNode[] {\n return graph.nodes.filter((n) => n.type === type);\n}\n\n// ─── Edge operations ──────────────────────────────────────────────────────────\n\nexport function makeEdgeId(type: EdgeType, fromId: string, toId: string): string {\n return `${type}:${fromId}__${toId}`;\n}\n\nexport function upsertEdge(\n graph: CustomerGraph,\n edge: Omit<GraphEdge, \"id\"> & { id?: string }\n): CustomerGraph {\n const id = edge.id ?? makeEdgeId(edge.type, edge.from, edge.to);\n const idx = graph.edges.findIndex((e) => e.id === id);\n if (idx !== -1) {\n const existing = graph.edges[idx]!;\n const updated: GraphEdge = {\n ...existing,\n weight: Math.min(1.0, existing.weight + 0.05),\n contactCount: existing.contactCount + 1,\n lastContact: edge.lastContact,\n properties: { ...existing.properties, ...edge.properties },\n };\n const edges = [...graph.edges];\n edges[idx] = updated;\n return { ...graph, edges };\n }\n const now = new Date().toISOString();\n const newEdge: GraphEdge = {\n ...edge,\n id,\n recordedAt: edge.recordedAt ?? now,\n validFrom: edge.validFrom ?? edge.lastContact ?? now,\n };\n return { ...graph, edges: [...graph.edges, newEdge] };\n}\n\n/**\n * Invalidate an edge instead of deleting it: records when the fact stopped\n * being true (validTo) and when the system retracted it (invalidatedAt),\n * preserving full temporal auditability.\n */\nexport function invalidateEdge(graph: CustomerGraph, edgeId: string, at?: string): CustomerGraph {\n const stamp = at ?? new Date().toISOString();\n const edges = graph.edges.map((e) =>\n e.id === edgeId ? { ...e, validTo: stamp, invalidatedAt: new Date().toISOString() } : e\n );\n return { ...graph, edges };\n}\n\n/**\n * Edges considered active at a point in time (default: now): not invalidated,\n * already valid (validFrom <= at), and not yet expired (at < validTo).\n * Edges without temporal fields (legacy) are treated as active.\n */\nexport function activeEdges(graph: CustomerGraph, at?: string): GraphEdge[] {\n const t = at ?? new Date().toISOString();\n return graph.edges.filter((e) => {\n if (e.invalidatedAt) return false;\n if (e.validFrom && e.validFrom > t) return false;\n if (e.validTo && t >= e.validTo) return false;\n return true;\n });\n}\n\nexport function findEdges(graph: CustomerGraph, fromId: string, type?: EdgeType): GraphEdge[] {\n return graph.edges.filter((e) => e.from === fromId && (type === undefined || e.type === type));\n}\n\nexport function findEdgesTo(graph: CustomerGraph, toId: string, type?: EdgeType): GraphEdge[] {\n return graph.edges.filter((e) => e.to === toId && (type === undefined || e.type === type));\n}\n\n// ─── Role assignment ──────────────────────────────────────────────────────────\n\nconst ROLE_EDGE_MAP: Record<Exclude<StakeholderRole, \"user\">, EdgeType> = {\n champion: \"IS_CHAMPION\",\n blocker: \"IS_BLOCKER\",\n economic_buyer: \"IS_ECONOMIC_BUYER\",\n};\n\nexport function setNodeRole(\n graph: CustomerGraph,\n nodeId: string,\n targetId: string,\n role: StakeholderRole\n): CustomerGraph {\n if (role === \"user\") return graph;\n const edgeType = ROLE_EDGE_MAP[role];\n const today = new Date().toISOString().slice(0, 10);\n return upsertEdge(graph, {\n from: nodeId,\n to: targetId,\n type: edgeType,\n weight: 0.8,\n sentiment: 0,\n lastContact: today,\n contactCount: 1,\n properties: {},\n });\n}\n\n// ─── Stakeholder query ────────────────────────────────────────────────────────\n\nexport function getStakeholders(graph: CustomerGraph): StakeholderSummary {\n const dedup = (nodes: GraphNode[]): GraphNode[] => {\n const seen = new Set<string>();\n return nodes.filter((n) => (seen.has(n.id) ? false : (seen.add(n.id), true)));\n };\n\n const resolve = (edges: GraphEdge[]): GraphNode[] =>\n dedup(edges.map((e) => findNode(graph, e.from)).filter((n): n is GraphNode => n !== undefined));\n\n const champions = resolve(graph.edges.filter((e) => e.type === \"IS_CHAMPION\"));\n const blockers = resolve(graph.edges.filter((e) => e.type === \"IS_BLOCKER\"));\n const economicBuyers = resolve(graph.edges.filter((e) => e.type === \"IS_ECONOMIC_BUYER\"));\n const allContacts = findNodesByType(graph, \"person\");\n\n const missingRoles: MissingRole[] = [];\n if (allContacts.length > 0) {\n if (champions.length === 0) {\n missingRoles.push({\n role: \"champion\",\n urgency: \"important\",\n suggestion: \"Identify who is driving this deal internally.\",\n });\n }\n if (economicBuyers.length === 0) {\n missingRoles.push({\n role: \"economic_buyer\",\n urgency: \"critical\",\n suggestion: \"Find out who signs the contract. Ask your champion directly.\",\n });\n }\n }\n\n return { champions, blockers, economicBuyers, allContacts, missingRoles };\n}\n\n// ─── Pruning ──────────────────────────────────────────────────────────────────\n\nexport function pruneStaleNodes(\n graph: CustomerGraph,\n maxAgeDays = 365,\n today?: string\n): CustomerGraph {\n const todayMs = today ? new Date(`${today}T00:00:00Z`).getTime() : Date.now();\n const threshold = maxAgeDays * 86_400_000;\n return {\n ...graph,\n nodes: graph.nodes.map((node) => {\n const age = todayMs - new Date(node.updatedAt).getTime();\n if (age > threshold && node.status !== \"inactive\") {\n return { ...node, status: \"inactive\" as const };\n }\n return node;\n }),\n };\n}\n\n// ─── Path finding ─────────────────────────────────────────────────────────────\n\n/** BFS shortest path between two nodes. Returns [] when no path exists. */\nexport function findPath(graph: CustomerGraph, fromId: string, toId: string): string[] {\n if (fromId === toId) return [fromId];\n\n const visited = new Set<string>([fromId]);\n const queue: Array<{ nodeId: string; path: string[] }> = [{ nodeId: fromId, path: [fromId] }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n const neighbors = graph.edges\n .filter((e) => e.from === current.nodeId || e.to === current.nodeId)\n .map((e) => (e.from === current.nodeId ? e.to : e.from))\n .filter((id) => !visited.has(id));\n\n for (const neighborId of neighbors) {\n const newPath = [...current.path, neighborId];\n if (neighborId === toId) return newPath;\n visited.add(neighborId);\n queue.push({ nodeId: neighborId, path: newPath });\n }\n }\n\n return [];\n}\n","export function normalizeEmail(raw: string): string {\n if (!raw) return \"\";\n const trimmed = raw.trim();\n // Extract email from \"Display Name <email@example.com>\" format\n const angleMatch = trimmed.match(/<([^>]+)>/);\n const address = angleMatch ? angleMatch[1]! : trimmed;\n return address.toLowerCase().trim();\n}\n\nexport function isSameContact(a: string, b: string): boolean {\n return normalizeEmail(a) === normalizeEmail(b);\n}\n\nexport function normalizeContactId(raw: string): string {\n const email = normalizeEmail(raw);\n // Replace @ with _at_ so it can be used as an object key safely\n return email.replace(\"@\", \"_at_\");\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport type { GraphNode, GraphEdge, EdgeType, CustomerGraph } from \"./graph.js\";\nimport { writeGraph, upsertNode, upsertEdge } from \"./graph.js\";\nimport { normalizeEmail } from \"./email-normalizer.js\";\n\nexport interface ExtractionInput {\n slug: string;\n withStr: string;\n interactionDate: string;\n domain?: string;\n companyName?: string;\n}\n\nexport function extractEmail(withStr: string): string | undefined {\n const angleMatch = withStr.match(/<([^>]+@[^>]+)>/);\n if (angleMatch?.[1]) return angleMatch[1].toLowerCase();\n const bareMatch = withStr.match(/^([^\\s]+@[^\\s]+)$/);\n if (bareMatch?.[1]) return bareMatch[1].toLowerCase();\n return undefined;\n}\n\nexport function extractDisplayName(withStr: string): string {\n const match = withStr.match(/^(.+?)\\s*<[^>]+>$/);\n if (match?.[1]) return match[1].trim();\n return withStr.trim();\n}\n\nexport function makePersonId(withStr: string, slug: string): string {\n const email = normalizeEmail(withStr);\n if (email.includes(\"@\")) return `person:${email}`;\n const name = extractDisplayName(withStr);\n const nameSlug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return `person:${slug}:${nameSlug}`;\n}\n\nexport function makeCompanyId(domain?: string, slug?: string, companyName?: string): string {\n if (domain) return `company:${domain.toLowerCase()}`;\n if (slug) return `company:${slug}`;\n if (companyName) {\n const s = companyName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n return `company:${s}`;\n }\n return `company:unknown`;\n}\n\nexport function extractNodes(input: ExtractionInput): Omit<GraphNode, \"createdAt\" | \"updatedAt\">[] {\n const email = extractEmail(input.withStr);\n const label = extractDisplayName(input.withStr);\n const personId = makePersonId(input.withStr, input.slug);\n\n const personProps: GraphNode[\"properties\"] = {};\n if (email !== undefined) personProps[\"email\"] = email;\n if (input.companyName !== undefined) personProps[\"company\"] = input.companyName;\n if (input.domain !== undefined) personProps[\"domain\"] = input.domain;\n\n const personNode: Omit<GraphNode, \"createdAt\" | \"updatedAt\"> = {\n id: personId,\n type: \"person\",\n label,\n properties: personProps,\n };\n\n const nodes: Omit<GraphNode, \"createdAt\" | \"updatedAt\">[] = [personNode];\n\n if (input.domain !== undefined || input.companyName !== undefined) {\n const companyId = makeCompanyId(input.domain, input.slug, input.companyName);\n const companyProps: GraphNode[\"properties\"] = {};\n if (input.domain !== undefined) companyProps[\"domain\"] = input.domain;\n\n const companyNode: Omit<GraphNode, \"createdAt\" | \"updatedAt\"> = {\n id: companyId,\n type: \"company\",\n label: input.companyName ?? input.domain ?? input.slug,\n properties: companyProps,\n };\n nodes.push(companyNode);\n }\n\n return nodes;\n}\n\nexport function extractEdges(\n personId: string,\n companyId: string | undefined,\n interactionDate: string\n): GraphEdge[] {\n if (!companyId) return [];\n return [\n {\n id: `WORKS_AT:${personId}__${companyId}`,\n from: personId,\n to: companyId,\n type: \"WORKS_AT\" as EdgeType,\n weight: 0.5,\n sentiment: 0,\n lastContact: interactionDate,\n contactCount: 1,\n properties: {},\n },\n ];\n}\n\nexport async function updateGraphFromInteraction(\n dataDir: string,\n slug: string,\n input: { withStr: string; interactionDate: string }\n): Promise<void> {\n if (!input.withStr.trim()) return;\n\n let domain: string | undefined;\n let companyName: string | undefined;\n const factsPath = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (fs.existsSync(factsPath)) {\n try {\n const parsed = matter(fs.readFileSync(factsPath, \"utf-8\"));\n domain = parsed.data[\"domain\"] as string | undefined;\n companyName = parsed.data[\"name\"] as string | undefined;\n } catch {\n // non-critical\n }\n }\n\n const extractionInput: ExtractionInput = {\n slug,\n withStr: input.withStr,\n interactionDate: input.interactionDate,\n };\n if (domain !== undefined) extractionInput.domain = domain;\n if (companyName !== undefined) extractionInput.companyName = companyName;\n const nodes = extractNodes(extractionInput);\n\n const personId = makePersonId(input.withStr, slug);\n const companyId =\n domain !== undefined || companyName !== undefined\n ? makeCompanyId(domain, slug, companyName)\n : undefined;\n const edges = extractEdges(personId, companyId, input.interactionDate);\n\n await writeGraph(dataDir, slug, (current) => {\n const empty: CustomerGraph = {\n schemaVersion: \"1\",\n slug,\n nodes: [],\n edges: [],\n updatedAt: new Date().toISOString(),\n };\n let graph = current ?? empty;\n for (const node of nodes) graph = upsertNode(graph, node);\n for (const edge of edges) graph = upsertEdge(graph, edge);\n return graph;\n });\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { readGraph } from \"./graph.js\";\nimport { extractEmail, extractDisplayName, makePersonId } from \"./graph-extractor.js\";\nimport { readJsonFile, writeJsonFile } from \"../fs/json-store.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type HealthGrade = \"A\" | \"B\" | \"C\" | \"D\" | \"F\";\n\nexport type HealthTrend = \"rising\" | \"stable\" | \"declining\" | \"cold\";\n\nexport type RiskFlag =\n | \"NO_CONTACT_14D\"\n | \"NO_CONTACT_30D\"\n | \"SENTIMENT_DECLINING\"\n | \"CHAMPION_SILENT\"\n | \"DEAL_STALLED\"\n | \"CLOSE_DATE_PASSED\"\n | \"CONTACT_LEFT_COMPANY\"\n | \"RESPONSE_LATENCY_INCREASING\";\n\nexport interface ContactHealth {\n contactId: string;\n name: string;\n email?: string;\n score: number;\n grade: HealthGrade;\n trend: HealthTrend;\n daysSinceContact: number;\n avgCadenceDays: number;\n sentimentTrend: number;\n riskFlags: RiskFlag[];\n lastContact: string;\n interactionCount30d: number;\n recommendation: string;\n updatedAt: string;\n}\n\nexport interface HealthSnapshot {\n schemaVersion: \"1\";\n slug: string;\n contacts: ContactHealth[];\n overallHealth: number;\n updatedAt: string;\n}\n\n// ─── Parsed interaction (from interactions.md) ────────────────────────────────\n\nexport interface ParsedInteraction {\n date: string;\n type: string;\n withStr: string;\n}\n\nexport interface ContactInteractionGroup {\n contactId: string;\n name: string;\n email?: string;\n interactions: ParsedInteraction[];\n}\n\n// ─── File path ────────────────────────────────────────────────────────────────\n\nexport function healthPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"health.json\");\n}\n\n// ─── Read / Write ─────────────────────────────────────────────────────────────\n\nexport function readHealth(dataDir: string, slug: string): HealthSnapshot | null {\n return readJsonFile<HealthSnapshot | null>(healthPath(dataDir, slug), null);\n}\n\nexport function writeHealth(dataDir: string, slug: string, health: HealthSnapshot): void {\n writeJsonFile(healthPath(dataDir, slug), { ...health, updatedAt: new Date().toISOString() });\n}\n\n// ─── Parsing ──────────────────────────────────────────────────────────────────\n\nexport function parseContactInteractions(content: string): ParsedInteraction[] {\n const blocks = content.split(/(?=^## \\d{4}-\\d{2}-\\d{2})/m).filter((b) => b.trim().length > 0);\n\n const result: ParsedInteraction[] = [];\n for (const block of blocks) {\n const headingMatch = block.match(/^## (\\d{4}-\\d{2}-\\d{2}) · (\\w+)/m);\n if (!headingMatch) continue;\n const date = headingMatch[1]!;\n const type = headingMatch[2]!;\n\n const withMatch = block.match(/^\\*\\*(?:With|Subject):\\*\\*\\s*(.+)$/m);\n if (!withMatch) continue;\n const withStr = withMatch[1]!.trim();\n\n result.push({ date, type, withStr });\n }\n return result;\n}\n\n// ─── Score functions (pure) ───────────────────────────────────────────────────\n\nexport function calcRecencyScore(daysSince: number): number {\n if (daysSince <= 0) return 100;\n if (daysSince >= 30) return 0;\n return Math.round(100 * (1 - daysSince / 30));\n}\n\nexport function calcCadenceScore(daysSince: number, avgCadenceDays: number): number {\n if (avgCadenceDays <= 0) return 50;\n const ratio = daysSince / avgCadenceDays;\n if (ratio <= 1.0) return 100;\n if (ratio >= 3.0) return 0;\n return Math.round(100 * (1 - (ratio - 1.0) / 2.0));\n}\n\nexport function calcMomentumScore(last30d: number, prev30d: number): number {\n if (last30d === 0 && prev30d === 0) return 50;\n if (prev30d === 0) return 80;\n const ratio = last30d / prev30d;\n if (ratio >= 1.5) return 100;\n if (ratio >= 1.0) return 75;\n if (ratio >= 0.5) return 50;\n if (ratio >= 0.25) return 25;\n return 0;\n}\n\nexport function gradeFromScore(score: number): HealthGrade {\n if (score >= 80) return \"A\";\n if (score >= 60) return \"B\";\n if (score >= 40) return \"C\";\n if (score >= 20) return \"D\";\n return \"F\";\n}\n\nexport function trendFromState(\n score: number,\n daysSince: number,\n avgCadenceDays: number,\n momentumScore: number\n): HealthTrend {\n if (score < 20 || daysSince >= 30) return \"cold\";\n if (momentumScore > 70 && score > 60) return \"rising\";\n if (momentumScore < 30 || (daysSince > avgCadenceDays * 1.5 && score < 60)) return \"declining\";\n return \"stable\";\n}\n\nexport function calcRiskFlags(\n _contactId: string,\n daysSince: number,\n score: number,\n isChampion: boolean\n): RiskFlag[] {\n const flags: RiskFlag[] = [];\n if (daysSince >= 30) flags.push(\"NO_CONTACT_30D\");\n if (daysSince >= 14) flags.push(\"NO_CONTACT_14D\");\n if (isChampion && score < 50) flags.push(\"CHAMPION_SILENT\");\n return flags;\n}\n\nexport function generateRecommendation(\n name: string,\n grade: HealthGrade,\n trend: HealthTrend,\n riskFlags: RiskFlag[],\n daysSince: number,\n avgCadenceDays: number\n): string {\n if (riskFlags.includes(\"NO_CONTACT_30D\")) {\n return `Re-engage ${name} urgently — no contact in ${daysSince} days.`;\n }\n if (riskFlags.includes(\"CHAMPION_SILENT\")) {\n return `Champion ${name} has gone quiet — critical to re-engage before deal stalls.`;\n }\n if (riskFlags.includes(\"NO_CONTACT_14D\")) {\n return `Schedule contact with ${name} — ${daysSince} days since last touchpoint.`;\n }\n if (trend === \"declining\") {\n return `${name} relationship declining — increase touchpoint frequency.`;\n }\n if (grade === \"A\") {\n return `${name} — strong relationship. Keep current cadence.`;\n }\n const daysUntilDue = Math.max(0, avgCadenceDays - daysSince);\n return `${name} — grade ${grade}. Next contact due in ~${daysUntilDue} day${daysUntilDue === 1 ? \"\" : \"s\"}.`;\n}\n\n// ─── Average cadence ──────────────────────────────────────────────────────────\n\nfunction dateUtcMs(d: string): number {\n return new Date(`${d}T00:00:00Z`).getTime();\n}\n\nexport function calcAvgCadence(interactions: ParsedInteraction[]): number {\n if (interactions.length < 2) return 0;\n const sorted = [...interactions].sort((a, b) => b.date.localeCompare(a.date));\n let totalDays = 0;\n for (let i = 0; i < sorted.length - 1; i++) {\n const gap = Math.round(\n (dateUtcMs(sorted[i]!.date) - dateUtcMs(sorted[i + 1]!.date)) / 86_400_000\n );\n totalDays += gap;\n }\n return Math.round(totalDays / (sorted.length - 1));\n}\n\n// ─── Group interactions by contact ───────────────────────────────────────────\n\nexport function groupInteractionsByContact(\n interactions: ParsedInteraction[],\n slug: string\n): ContactInteractionGroup[] {\n const map = new Map<\n string,\n { contactId: string; name: string; email?: string; interactions: ParsedInteraction[] }\n >();\n\n for (const ix of interactions) {\n const email = extractEmail(ix.withStr);\n const name = extractDisplayName(ix.withStr);\n const contactId = makePersonId(ix.withStr, slug);\n\n if (!map.has(contactId)) {\n const entry: {\n contactId: string;\n name: string;\n email?: string;\n interactions: ParsedInteraction[];\n } = {\n contactId,\n name,\n interactions: [],\n };\n if (email !== undefined) entry.email = email;\n map.set(contactId, entry);\n }\n map.get(contactId)!.interactions.push(ix);\n }\n\n return Array.from(map.values());\n}\n\n// ─── Per-contact health ───────────────────────────────────────────────────────\n\nexport function computeContactHealth(\n group: ContactInteractionGroup,\n today: string,\n isChampion: boolean\n): ContactHealth {\n const sorted = [...group.interactions].sort((a, b) => b.date.localeCompare(a.date));\n const lastContact = sorted[0]?.date ?? \"\";\n\n const daysSince = lastContact\n ? Math.round((dateUtcMs(today) - dateUtcMs(lastContact)) / 86_400_000)\n : 999;\n\n const avgCadenceDays = calcAvgCadence(group.interactions);\n\n const todayMs = dateUtcMs(today);\n const d30 = todayMs - 30 * 86_400_000;\n const d60 = todayMs - 60 * 86_400_000;\n const last30d = group.interactions.filter((i) => dateUtcMs(i.date) >= d30).length;\n const prev30d = group.interactions.filter(\n (i) => dateUtcMs(i.date) >= d60 && dateUtcMs(i.date) < d30\n ).length;\n\n const recency = calcRecencyScore(daysSince);\n const cadence = calcCadenceScore(daysSince, avgCadenceDays);\n const sentiment = 50;\n const response = 50;\n const momentum = calcMomentumScore(last30d, prev30d);\n\n const score = Math.round(\n recency * 0.35 + cadence * 0.25 + sentiment * 0.2 + response * 0.1 + momentum * 0.1\n );\n\n const grade = gradeFromScore(score);\n const trend = trendFromState(score, daysSince, avgCadenceDays, momentum);\n const riskFlags = calcRiskFlags(group.contactId, daysSince, score, isChampion);\n const recommendation = generateRecommendation(\n group.name,\n grade,\n trend,\n riskFlags,\n daysSince,\n avgCadenceDays\n );\n\n const health: ContactHealth = {\n contactId: group.contactId,\n name: group.name,\n score,\n grade,\n trend,\n daysSinceContact: daysSince,\n avgCadenceDays,\n sentimentTrend: 0,\n riskFlags,\n lastContact,\n interactionCount30d: last30d,\n recommendation,\n updatedAt: new Date().toISOString(),\n };\n if (group.email !== undefined) health.email = group.email;\n return health;\n}\n\n// ─── Full customer health ─────────────────────────────────────────────────────\n\nexport function computeCustomerHealth(\n dataDir: string,\n slug: string,\n today: string = new Date().toISOString().slice(0, 10)\n): HealthSnapshot {\n const interactionsPath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n const content = fs.existsSync(interactionsPath)\n ? (fs.readFileSync(interactionsPath, \"utf-8\") as string)\n : \"\";\n\n const parsed = parseContactInteractions(content);\n const groups = groupInteractionsByContact(parsed, slug);\n\n const graph = readGraph(dataDir, slug);\n const championIds = new Set(\n graph.edges.filter((e) => e.type === \"IS_CHAMPION\").map((e) => e.from)\n );\n\n const contacts = groups.map((group) =>\n computeContactHealth(group, today, championIds.has(group.contactId))\n );\n\n const overallHealth =\n contacts.length === 0\n ? 100\n : Math.round(contacts.reduce((sum, c) => sum + c.score, 0) / contacts.length);\n\n return {\n schemaVersion: \"1\",\n slug,\n contacts,\n overallHealth,\n updatedAt: new Date().toISOString(),\n };\n}\n\n// ─── Fire-and-forget update ───────────────────────────────────────────────────\n\nexport async function updateHealthFromInteraction(dataDir: string, slug: string): Promise<void> {\n const health = computeCustomerHealth(dataDir, slug);\n writeHealth(dataDir, slug, health);\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport matter from \"gray-matter\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { appendInteraction, formatInteractionEntry } from \"../../fs/interactions-writer.js\";\nimport { writeFileAtomic } from \"../../fs/atomic-write.js\";\nimport type { InteractionEntry } from \"../../schemas/interaction.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport { updateGraphFromInteraction } from \"../../core/graph-extractor.js\";\nimport { updateHealthFromInteraction } from \"../../core/relationship-health.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleLogInteraction(\n input: {\n slug: string;\n type: InteractionEntry[\"type\"];\n summary: string;\n with: string;\n nextSteps?: string[];\n direction?: \"inbound\" | \"outbound\";\n source?: string;\n date?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const today = new Date().toISOString().split(\"T\")[0] as string;\n const interactionDate = input.date ?? today;\n const sourceRef = input.source ?? `agent://log/${Date.now()}`;\n\n const entry: InteractionEntry = {\n date: interactionDate,\n type: input.type,\n with: input.with,\n summary: input.summary,\n nextSteps: input.nextSteps ?? [],\n sourceRef,\n synced: new Date().toISOString(),\n ...(input.direction !== undefined ? { direction: input.direction } : {}),\n };\n\n const interactionsPath = path.join(dataDir, \"customers\", input.slug, \"interactions.md\");\n const entryText = formatInteractionEntry(entry);\n\n try {\n enforceRbac(dataDir, \"log_interaction\");\n\n await appendInteraction(dataDir, input.slug, entry);\n\n // Graph auto-update: fire-and-forget\n updateGraphFromInteraction(dataDir, input.slug, {\n withStr: input.with,\n interactionDate: interactionDate,\n }).catch(() => {\n // non-critical — interaction already written\n });\n\n // Health auto-update: fire-and-forget (runs after graph update)\n updateHealthFromInteraction(dataDir, input.slug).catch(() => {\n // non-critical — interaction already written\n });\n\n // Update last_touchpoint in main_facts.md\n const mainFactsPath = path.join(dataDir, \"customers\", input.slug, \"main_facts.md\");\n if (fs.existsSync(mainFactsPath)) {\n try {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\"));\n raw.data.last_touchpoint = interactionDate;\n // js-yaml quotes YYYY-MM-DD strings; strip quotes to keep plain date format\n let serialized = matter.stringify(raw.content, raw.data);\n serialized = serialized.replace(\n /^(last_touchpoint:\\s*)['\"](\\d{4}-\\d{2}-\\d{2})['\"]/m,\n \"$1$2\"\n );\n writeFileAtomic(mainFactsPath, serialized);\n } catch {\n // non-critical — interaction is already written\n }\n }\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"log_interaction\",\n slug: input.slug,\n summary: input.summary,\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n path: interactionsPath,\n entry: entryText,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: false,\n error: (err as Error).message,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n}\n\nexport function registerLogInteraction(server: McpServer): void {\n server.registerTool(\n \"log_interaction\",\n {\n title: \"Log Interaction\",\n description: `Write a new interaction entry to the CRM. Use after every call, meeting, or email.\nFormat matches auto-synced entries exactly — immediately searchable.\n\nArgs:\n slug: Customer ID\n type: Interaction type (\"Email\" | \"Call\" | \"Meeting\" | \"Note\" | \"Demo\" | \"Proposal\" | \"Contract\" | \"Other\")\n summary: 2-5 sentences describing what happened (min 1 char)\n with: Who was involved (name or email address)\n nextSteps: Array of action items (optional)\n direction: \"inbound\" or \"outbound\" (optional)\n source: Source reference string (optional, auto-generated if omitted)\n\nReturns: { success: boolean, path: string, entry: string }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n type: z\n .enum([\"Email\", \"Call\", \"Meeting\", \"Note\", \"Demo\", \"Proposal\", \"Contract\", \"Other\"])\n .describe(\"Type of interaction\"),\n summary: z.string().min(1).describe(\"2-5 sentence summary of what happened\"),\n with: z.string().describe(\"Who was involved (name or email)\"),\n nextSteps: z.array(z.string()).optional().describe(\"Action items for follow-up\"),\n direction: z\n .enum([\"inbound\", \"outbound\"])\n .optional()\n .describe(\"Direction of communication\"),\n source: z\n .string()\n .optional()\n .describe(\"Source reference (e.g. gmail://thread/123). Auto-generated if omitted.\"),\n date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional()\n .describe(\"Date of interaction (YYYY-MM-DD). Defaults to today.\"),\n }),\n },\n async ({ slug, type, summary, with: withStr, nextSteps, direction, source, date }) =>\n handleLogInteraction({\n slug,\n type,\n summary,\n with: withStr,\n ...(nextSteps !== undefined ? { nextSteps } : {}),\n ...(direction !== undefined ? { direction } : {}),\n ...(source !== undefined ? { source } : {}),\n ...(date !== undefined ? { date } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { upsertDeal } from \"../../fs/pipeline-writer.js\";\nimport type { PipelineDeal } from \"../../schemas/pipeline.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUpdateDeal(\n input: {\n slug: string;\n dealName: string;\n stage?: string;\n value?: number;\n probability?: number;\n closeDate?: string;\n notes?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const today = new Date().toISOString().split(\"T\")[0] as string;\n\n const deal: PipelineDeal = {\n name: input.dealName,\n stage: (input.stage as PipelineDeal[\"stage\"]) ?? \"lead\",\n currency: \"EUR\",\n updated: today,\n ...(input.value !== undefined ? { value: input.value } : {}),\n ...(input.probability !== undefined ? { probability: input.probability } : {}),\n ...(input.closeDate !== undefined ? { close_date: input.closeDate } : {}),\n ...(input.notes !== undefined ? { notes: input.notes } : {}),\n };\n\n try {\n enforceRbac(dataDir, \"update_deal\");\n\n await upsertDeal(dataDir, input.slug, deal);\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"update_deal\",\n slug: input.slug,\n summary: input.dealName,\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n deal,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: false,\n error: (err as Error).message,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n}\n\nexport function registerUpdateDeal(server: McpServer): void {\n server.registerTool(\n \"update_deal\",\n {\n title: \"Update Deal\",\n description: `Update or create a deal in the customer's pipeline. Upserts by deal name.\nUse after pipeline discussions or when deal status changes.\n\nArgs:\n slug: Customer ID\n dealName: Deal name (used as unique key for upsert)\n stage: Deal stage (\"lead\" | \"qualified\" | \"proposal\" | \"negotiation\" | \"won\" | \"lost\")\n value: Deal value in euros\n probability: Win probability percentage (0-100)\n closeDate: Expected close date (YYYY-MM-DD)\n notes: Free-text notes about the deal\n\nReturns: { success: boolean, deal: object }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n dealName: z.string().describe(\"Deal name (unique identifier for upsert)\"),\n stage: z\n .enum([\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"])\n .optional()\n .describe(\"Deal stage\"),\n value: z.number().optional().describe(\"Deal value in euros\"),\n probability: z.number().min(0).max(100).optional().describe(\"Win probability (0-100)\"),\n closeDate: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional()\n .describe(\"Expected close date (YYYY-MM-DD)\"),\n notes: z.string().optional().describe(\"Free-text notes about the deal\"),\n }),\n },\n async ({ slug, dealName, stage, value, probability, closeDate, notes }) =>\n handleUpdateDeal({\n slug,\n dealName,\n ...(stage !== undefined ? { stage } : {}),\n ...(value !== undefined ? { value } : {}),\n ...(probability !== undefined ? { probability } : {}),\n ...(closeDate !== undefined ? { closeDate } : {}),\n ...(notes !== undefined ? { notes } : {}),\n })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readPipeline } from \"../../fs/pipeline-writer.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nfunction countInteractions(content: string): number {\n // Count ## YYYY-MM-DD headings\n const matches = content.match(/^## \\d{4}-\\d{2}-\\d{2}/gm);\n return matches ? matches.length : 0;\n}\n\nexport async function handleExportCustomer(\n input: { slug: string; format?: \"json\" | \"markdown\" },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n isError?: boolean;\n}> {\n enforceRbac(dataDir, \"export_customer\");\n\n const customerDir = path.join(dataDir, \"customers\", input.slug);\n\n if (!fs.existsSync(customerDir)) {\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Customer '${input.slug}' not found. Check 'list_customers()' for available customers.`,\n },\n ],\n isError: true,\n };\n }\n\n const format = input.format ?? \"json\";\n\n // Read main_facts.md\n const mainFactsPath = path.join(customerDir, \"main_facts.md\");\n let mainFacts: Record<string, unknown> = {};\n let mainFactsContent = \"\";\n if (fs.existsSync(mainFactsPath)) {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\") as string);\n mainFacts = raw.data as Record<string, unknown>;\n mainFactsContent = raw.content ?? \"\";\n }\n\n // Read interactions\n const interactionsPath = path.join(customerDir, \"interactions.md\");\n let interactionsContent = \"\";\n let interactionsCount = 0;\n if (fs.existsSync(interactionsPath)) {\n interactionsContent = fs.readFileSync(interactionsPath, \"utf-8\") as string;\n interactionsCount = countInteractions(interactionsContent);\n }\n\n // Read pipeline\n const pipeline = await readPipeline(dataDir, input.slug);\n\n // Read attachments list\n const attachmentsDir = path.join(customerDir, \"attachments\");\n const attachments: string[] = [];\n if (fs.existsSync(attachmentsDir)) {\n try {\n const files = fs.readdirSync(attachmentsDir) as string[];\n for (const f of files) {\n try {\n if (fs.statSync(path.join(attachmentsDir, f)).isFile()) attachments.push(f);\n } catch {\n /* skip */\n }\n }\n } catch {\n /* skip */\n }\n }\n\n if (format === \"markdown\") {\n const markdown = [\n `# Export: ${input.slug}`,\n \"\",\n \"## Main Facts\",\n mainFactsContent.trim() || \"(no content)\",\n \"\",\n \"## Metadata\",\n Object.entries(mainFacts)\n .map(([k, v]) => `- **${k}**: ${JSON.stringify(v)}`)\n .join(\"\\n\") || \"(no metadata)\",\n \"\",\n `## Interactions (${interactionsCount} total)`,\n interactionsContent.trim() || \"(no interactions)\",\n \"\",\n \"## Pipeline\",\n pipeline.length > 0\n ? pipeline\n .map(\n (d) =>\n `- **${d.name}** · ${d.stage}${d.value !== undefined ? ` · €${d.value}` : \"\"}${d.close_date ? ` · close: ${d.close_date}` : \"\"}`\n )\n .join(\"\\n\")\n : \"(no deals)\",\n \"\",\n `## Attachments (${attachments.length})`,\n attachments.length > 0 ? attachments.map((f) => `- ${f}`).join(\"\\n\") : \"(none)\",\n ].join(\"\\n\");\n\n return {\n content: [{ type: \"text\", text: markdown }],\n };\n }\n\n // Default: JSON\n const exported = {\n slug: input.slug,\n exportedAt: new Date().toISOString(),\n mainFacts,\n interactionsCount,\n pipeline,\n attachments,\n };\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(exported, null, 2) }],\n };\n}\n\nexport function registerExportCustomer(server: McpServer): void {\n server.registerTool(\n \"export_customer\",\n {\n title: \"Export Customer\",\n description: `Export all customer data (main_facts + interactions count + pipeline deals).\nUseful for reporting, audits, or creating backups.\n\nArgs:\n slug: Customer ID (e.g. \"acme-corp\")\n format: Output format — \"json\" (default) or \"markdown\"\n\nReturns:\n JSON: { slug, exportedAt, mainFacts, interactionsCount, pipeline }\n Markdown: Formatted document with all sections`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n format: z\n .enum([\"json\", \"markdown\"])\n .optional()\n .describe(\"Output format: 'json' (default) or 'markdown'\"),\n }),\n },\n async ({ slug, format }) =>\n handleExportCustomer({ slug, ...(format !== undefined ? { format } : {}) })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n readMainFacts,\n writeMainFacts,\n ensureCustomerDir,\n customerExists,\n} from \"../../fs/customer-dir.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport type { MainFacts } from \"../../schemas/main-facts.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUpdateCustomerFacts(\n input: {\n slug: string;\n name?: string | undefined;\n domain?: string | undefined;\n email?: string | undefined;\n phone?: string | undefined;\n industry?: string | undefined;\n relationshipStage?: \"prospect\" | \"active\" | \"churned\" | \"paused\" | undefined;\n dealValue?: number | undefined;\n primaryContact?: string | undefined;\n timezone?: string | undefined;\n tags?: string[] | undefined;\n notes?: string | undefined;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const today = new Date().toISOString().slice(0, 10);\n\n try {\n enforceRbac(dataDir, \"update_customer_facts\");\n\n let existing: MainFacts;\n let created = false;\n if (!customerExists(dataDir, input.slug)) {\n await ensureCustomerDir(dataDir, input.slug);\n existing = {\n name: input.name ?? input.slug,\n relationship_stage: \"prospect\",\n currency: \"EUR\",\n tags: [],\n created: today,\n updated: today,\n };\n created = true;\n } else {\n existing = await readMainFacts(dataDir, input.slug);\n }\n\n const updated = {\n ...existing,\n ...(input.name !== undefined ? { name: input.name } : {}),\n ...(input.domain !== undefined ? { domain: input.domain } : {}),\n ...(input.email !== undefined ? { email: input.email } : {}),\n ...(input.phone !== undefined ? { phone: input.phone } : {}),\n ...(input.industry !== undefined ? { industry: input.industry } : {}),\n ...(input.relationshipStage !== undefined\n ? { relationship_stage: input.relationshipStage }\n : {}),\n ...(input.dealValue !== undefined ? { deal_value: input.dealValue } : {}),\n ...(input.primaryContact !== undefined ? { primary_contact: input.primaryContact } : {}),\n ...(input.timezone !== undefined ? { timezone: input.timezone } : {}),\n ...(input.tags !== undefined ? { tags: input.tags } : {}),\n updated: today,\n };\n\n await writeMainFacts(dataDir, input.slug, updated);\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"update_customer_facts\",\n slug: input.slug,\n summary: Object.keys(input)\n .filter((k) => k !== \"slug\")\n .join(\", \"),\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true, created, facts: updated }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerUpdateCustomerFacts(server: McpServer): void {\n server.registerTool(\n \"update_customer_facts\",\n {\n title: \"Update Customer Facts\",\n description: `Create or update a customer's main_facts.md profile.\nIf the customer slug does not exist yet, creates the customer directory and initial profile.\nIf it exists, merges the provided fields into existing data.\n\nUse to add a new customer (\"create acme-corp\") or update existing info.\n\nArgs:\n slug: Customer ID / slug — e.g. \"acme-corp\" (kebab-case, no spaces)\n name: Company name (used as display name)\n domain: Primary domain (e.g. \"acme.com\")\n email: Primary contact email\n phone: Phone number\n industry: Industry vertical\n relationshipStage: \"prospect\" | \"active\" | \"churned\" | \"paused\"\n dealValue: Expected deal value in EUR\n primaryContact: Primary contact person name\n timezone: Timezone (e.g. \"Europe/Berlin\")\n tags: Array of tags (replaces existing tags)\n\nReturns: { success: boolean, created: boolean, facts: object }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n name: z.string().optional().describe(\"Company name\"),\n domain: z.string().optional().describe(\"Primary domain\"),\n email: z.string().optional().describe(\"Primary contact email\"),\n phone: z.string().optional().describe(\"Phone number\"),\n industry: z.string().optional().describe(\"Industry vertical\"),\n relationshipStage: z\n .enum([\"prospect\", \"active\", \"churned\", \"paused\"])\n .optional()\n .describe(\"Relationship stage\"),\n dealValue: z.number().optional().describe(\"Expected deal value in EUR\"),\n primaryContact: z.string().optional().describe(\"Primary contact person name\"),\n timezone: z.string().optional().describe(\"Timezone (e.g. Europe/Berlin)\"),\n tags: z.array(z.string()).optional().describe(\"Tags (replaces existing)\"),\n }),\n },\n async (input) => handleUpdateCustomerFacts(input)\n );\n}\n","import type { PipelineDeal } from \"../schemas/pipeline.js\";\n\nexport interface DealHealthSignals {\n daysSinceLastActivity: number;\n daysInCurrentStage: number;\n daysToClose?: number | undefined;\n probability?: number | undefined;\n}\n\nexport interface DealHealthScore {\n score: number; // 0–100\n grade: \"A\" | \"B\" | \"C\" | \"D\" | \"F\";\n signals: DealHealthSignals;\n warnings: string[];\n}\n\nfunction grade(score: number): \"A\" | \"B\" | \"C\" | \"D\" | \"F\" {\n if (score >= 80) return \"A\";\n if (score >= 65) return \"B\";\n if (score >= 50) return \"C\";\n if (score >= 35) return \"D\";\n return \"F\";\n}\n\nexport function scoreDeal(deal: PipelineDeal, signals: DealHealthSignals): DealHealthScore {\n let score = 100;\n const warnings: string[] = [];\n\n // Activity recency (max penalty -40)\n if (signals.daysSinceLastActivity > 60) {\n score -= 40;\n warnings.push(`No activity in ${signals.daysSinceLastActivity} days`);\n } else if (signals.daysSinceLastActivity > 30) {\n score -= 25;\n warnings.push(`Low activity — last touch ${signals.daysSinceLastActivity} days ago`);\n } else if (signals.daysSinceLastActivity > 14) {\n score -= 10;\n }\n\n // Stage stagnation (max penalty -25)\n if (signals.daysInCurrentStage > 90) {\n score -= 25;\n warnings.push(`Stuck in \"${deal.stage}\" for ${signals.daysInCurrentStage} days`);\n } else if (signals.daysInCurrentStage > 45) {\n score -= 12;\n warnings.push(`Slow progress in \"${deal.stage}\"`);\n }\n\n // Close date risk (max penalty -20)\n if (signals.daysToClose !== undefined) {\n if (signals.daysToClose < 0) {\n score -= 20;\n warnings.push(\"Close date passed\");\n } else if (signals.daysToClose < 7) {\n score -= 10;\n warnings.push(\"Close date in less than 7 days\");\n }\n }\n\n // Probability sanity (max penalty -15)\n if (signals.probability !== undefined) {\n if (signals.probability < 20 && deal.stage !== \"lead\") {\n score -= 15;\n warnings.push(`Low probability (${signals.probability}%) for stage \"${deal.stage}\"`);\n }\n }\n\n score = Math.max(0, score);\n return { score, grade: grade(score), signals, warnings };\n}\n\nexport interface DealTiming {\n daysSinceLastActivity: number;\n daysInCurrentStage: number;\n daysToClose?: number;\n}\n\nconst MS_PER_DAY = 86_400_000;\n\n/**\n * Derive activity/close timing for a deal relative to `todayDate`. Centralizes\n * the day-diff math that deal-room and deal-agent each computed identically.\n * A blank/whitespace close_date yields `undefined` (not a NaN day count).\n */\nexport function deriveDealTiming(deal: PipelineDeal, todayDate: Date): DealTiming {\n const updatedDate = deal.updated ? new Date(deal.updated) : todayDate;\n const daysSinceLastActivity = Math.floor(\n (todayDate.getTime() - updatedDate.getTime()) / MS_PER_DAY\n );\n const timing: DealTiming = {\n daysSinceLastActivity,\n daysInCurrentStage: daysSinceLastActivity,\n };\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n timing.daysToClose = Math.floor(\n (new Date(deal.close_date).getTime() - todayDate.getTime()) / MS_PER_DAY\n );\n }\n return timing;\n}\n\n/** Score a deal using timing derived from `todayDate` plus the deal's probability. */\nexport function scoreDealForToday(deal: PipelineDeal, todayDate: Date): DealHealthScore {\n const timing = deriveDealTiming(deal, todayDate);\n return scoreDeal(deal, {\n daysSinceLastActivity: timing.daysSinceLastActivity,\n daysInCurrentStage: timing.daysInCurrentStage,\n ...(timing.daysToClose !== undefined ? { daysToClose: timing.daysToClose } : {}),\n ...(deal.probability !== undefined ? { probability: deal.probability } : {}),\n });\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readPipeline } from \"../../fs/pipeline-writer.js\";\nimport { scoreDeal } from \"../../core/deal-health.js\";\nimport type { DealHealthScore } from \"../../core/deal-health.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetDealHealth(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const deals = await readPipeline(dataDir, input.slug);\n const today = new Date();\n\n const results: Array<{ deal: string; stage: string } & DealHealthScore> = [];\n\n for (const deal of deals) {\n // Approximate signals from deal metadata\n const updatedDate = deal.updated ? new Date(deal.updated) : today;\n const daysSinceLastActivity = Math.floor(\n (today.getTime() - updatedDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n const daysToClose = deal.close_date\n ? Math.floor(\n (new Date(deal.close_date).getTime() - today.getTime()) / (1000 * 60 * 60 * 24)\n )\n : undefined;\n\n const health = scoreDeal(deal, {\n daysSinceLastActivity,\n daysInCurrentStage: daysSinceLastActivity,\n ...(daysToClose !== undefined ? { daysToClose } : {}),\n ...(deal.probability !== undefined ? { probability: deal.probability } : {}),\n });\n\n results.push({ deal: deal.name, stage: deal.stage, ...health });\n }\n\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ slug: input.slug, deals: results }, null, 2) },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetDealHealth(server: McpServer): void {\n server.registerTool(\n \"get_deal_health\",\n {\n title: \"Get Deal Health\",\n description: `Score the health of all deals for a customer (0–100). Grade A–F based on activity recency, stage velocity, close date proximity, and probability.\n\nReturns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n }),\n },\n async ({ slug }) => handleGetDealHealth({ slug })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readPipelineSync } from \"../../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../../fs/customer-dir.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\ninterface ForecastDeal {\n slug: string;\n dealName: string;\n stage: string;\n value?: number | undefined;\n probability?: number | undefined;\n weightedValue: number;\n closeDate?: string | undefined;\n}\n\nexport async function handleGetPipelineForecast(\n input: { filter?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const slugs = listCustomerSlugs(dataDir).filter(\n (d) => !input.filter || d.includes(input.filter)\n );\n\n const allDeals: ForecastDeal[] = [];\n\n for (const slug of slugs) {\n // readPipelineSync already guards a missing file (returns []); no need for\n // a per-iteration existsSync + dynamic import.\n const deals = readPipelineSync(dataDir, slug);\n\n for (const deal of deals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n const prob = deal.probability ?? 50;\n const value = deal.value ?? 0;\n const forecastDeal: ForecastDeal = {\n slug,\n dealName: deal.name,\n stage: deal.stage,\n value,\n probability: prob,\n weightedValue: Math.round((value * prob) / 100),\n };\n if (deal.close_date !== undefined) forecastDeal.closeDate = deal.close_date;\n allDeals.push(forecastDeal);\n }\n }\n\n const totalWeightedValue = allDeals.reduce((sum, d) => sum + d.weightedValue, 0);\n const byStage = allDeals.reduce<Record<string, { count: number; weightedValue: number }>>(\n (acc, d) => {\n if (!acc[d.stage]) acc[d.stage] = { count: 0, weightedValue: 0 };\n acc[d.stage]!.count++;\n acc[d.stage]!.weightedValue += d.weightedValue;\n return acc;\n },\n {}\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ deals: allDeals, totalWeightedValue, byStage }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetPipelineForecast(server: McpServer): void {\n server.registerTool(\n \"get_pipeline_forecast\",\n {\n title: \"Get Pipeline Forecast\",\n description: `Aggregate weighted pipeline revenue across all customers. Groups open deals by stage, computes probability-weighted expected revenue.\n\nReturns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count, weightedValue } } }`,\n inputSchema: z.object({\n filter: z.string().optional().describe(\"Filter by customer slug substring\"),\n }),\n },\n async ({ filter }) => handleGetPipelineForecast({ ...(filter !== undefined ? { filter } : {}) })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { appendInteraction } from \"../../fs/interactions-writer.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSummarizeMeeting(\n input: {\n slug: string;\n transcript: string;\n with?: string;\n date?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n let summary = input.transcript.slice(0, 400);\n let nextSteps: string[] = [];\n\n try {\n const { callLlm } = await import(\"../../core/llm.js\");\n const prompt = `Summarize this meeting transcript in 3-5 sentences and extract action items.\\n\\nTranscript:\\n${input.transcript.slice(0, 3000)}\\n\\nRespond as JSON: { \"summary\": \"...\", \"nextSteps\": [\"...\"] }`;\n const response = await callLlm(prompt);\n const parsed = JSON.parse(response) as { summary?: string; nextSteps?: string[] };\n summary = parsed.summary ?? summary;\n nextSteps = parsed.nextSteps ?? [];\n } catch {\n // LLM unavailable — use raw transcript slice\n }\n\n const date = input.date ?? new Date().toISOString().slice(0, 10);\n const sourceRef = `agent://meeting/${Date.now()}`;\n\n await appendInteraction(dataDir, input.slug, {\n date,\n type: \"Meeting\",\n with: input.with ?? \"Meeting Participant\",\n summary,\n nextSteps,\n sourceRef,\n synced: new Date().toISOString(),\n });\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"summarize_meeting\",\n slug: input.slug,\n summary: summary.slice(0, 100),\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true, summary, nextSteps, sourceRef }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerSummarizeMeeting(server: McpServer): void {\n server.registerTool(\n \"summarize_meeting\",\n {\n title: \"Summarize Meeting\",\n description: `Summarize a meeting transcript and log it as an interaction. Uses LLM to extract key points and action items (falls back to raw text slice if LLM unavailable).\n\nArgs:\n slug: Customer ID\n transcript: Full meeting transcript text\n with: Participant name(s) (optional)\n date: Meeting date YYYY-MM-DD (optional, defaults to today)\n\nReturns: { success, summary, nextSteps, sourceRef }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n transcript: z.string().min(1).describe(\"Full meeting transcript\"),\n with: z.string().optional().describe(\"Participant names\"),\n date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional()\n .describe(\"Meeting date\"),\n }),\n },\n async ({ slug, transcript, with: withStr, date }) =>\n handleSummarizeMeeting({\n slug,\n transcript,\n ...(withStr !== undefined ? { with: withStr } : {}),\n ...(date !== undefined ? { date } : {}),\n })\n );\n}\n","import path from \"path\";\nimport { readJsonFile, writeJsonFile } from \"../fs/json-store.js\";\n\nexport interface PipelineStage {\n id: string;\n label: string;\n color?: string;\n order: number;\n isFinal?: boolean;\n probability?: number;\n}\n\nexport const DEFAULT_STAGES: PipelineStage[] = [\n { id: \"lead\", label: \"Lead\", order: 1, probability: 10 },\n { id: \"qualified\", label: \"Qualified\", order: 2, probability: 30 },\n { id: \"proposal\", label: \"Proposal\", order: 3, probability: 50 },\n { id: \"negotiation\", label: \"Negotiation\", order: 4, probability: 75 },\n { id: \"won\", label: \"Won\", order: 5, isFinal: true, probability: 100 },\n { id: \"lost\", label: \"Lost\", order: 6, isFinal: true, probability: 0 },\n];\n\nfunction stagesPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"pipeline-stages.json\");\n}\n\nexport function getPipelineStages(dataDir: string): PipelineStage[] {\n return readJsonFile<PipelineStage[]>(stagesPath(dataDir), DEFAULT_STAGES);\n}\n\nexport function setPipelineStage(dataDir: string, stage: PipelineStage): void {\n const stages = getPipelineStages(dataDir);\n const idx = stages.findIndex((s) => s.id === stage.id);\n if (idx >= 0) stages[idx] = stage;\n else stages.push(stage);\n stages.sort((a, b) => a.order - b.order);\n writeJsonFile(stagesPath(dataDir), stages);\n}\n\nexport function deletePipelineStage(dataDir: string, id: string): void {\n writeJsonFile(\n stagesPath(dataDir),\n getPipelineStages(dataDir).filter((s) => s.id !== id)\n );\n}\n\nexport function resetToDefaults(dataDir: string): void {\n writeJsonFile(stagesPath(dataDir), DEFAULT_STAGES);\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getPipelineStages } from \"../../core/pipeline-stages.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetPipelineStages(\n _input: Record<string, never>,\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const stages = getPipelineStages(dataDir);\n return {\n content: [{ type: \"text\", text: JSON.stringify({ stages }, null, 2) }],\n };\n}\n\nexport function registerGetPipelineStages(server: McpServer): void {\n server.registerTool(\n \"get_pipeline_stages\",\n {\n title: \"Get Pipeline Stages\",\n description:\n \"Returns all configured pipeline stages. Falls back to default stages (lead, qualified, proposal, negotiation, won, lost) if no custom stages are configured.\",\n inputSchema: z.object({}),\n },\n async () => handleGetPipelineStages({})\n );\n}\n","import { searchKnowledge } from \"./lancedb.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\n\nexport interface CrossCustomerResult {\n slug: string;\n relevantContent: string;\n score: number;\n}\n\nexport async function searchAcrossCustomers(\n dataDir: string,\n query: string,\n limit = 5,\n excludeSlug?: string\n): Promise<CrossCustomerResult[]> {\n const slugs = listCustomerSlugs(dataDir).filter((d) => d !== excludeSlug);\n\n // Each customer's vector search is independent — fan out in parallel rather\n // than awaiting one LanceDB query at a time (latency was linear in #customers).\n const perCustomer = await Promise.all(\n slugs.map(async (slug) => {\n const results = await searchKnowledge(dataDir, slug, query, 2);\n return results.map((r) => ({\n slug,\n relevantContent: r.content.slice(0, 200),\n score: r.score,\n }));\n })\n );\n\n return perCustomer\n .flat()\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { searchAcrossCustomers, type CrossCustomerResult } from \"../../core/cross-customer.js\";\nimport { listCustomerSlugs } from \"../../fs/customer-dir.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetMarketIntelligence(\n input: { query: string; excludeCurrentCustomer?: boolean; slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const excludeSlug = input.excludeCurrentCustomer ? input.slug : undefined;\n\n // Count total customers before excluding\n const all = listCustomerSlugs(dataDir);\n const totalCustomersSearched = excludeSlug\n ? all.filter((s) => s !== excludeSlug).length\n : all.length;\n\n const results: CrossCustomerResult[] = await searchAcrossCustomers(\n dataDir,\n input.query,\n 10,\n excludeSlug\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ query: input.query, results, totalCustomersSearched }, null, 2),\n },\n ],\n };\n}\n\nexport function registerGetMarketIntelligence(server: McpServer): void {\n server.registerTool(\n \"get_market_intelligence\",\n {\n title: \"Get Market Intelligence\",\n description:\n \"Search across all customers to find patterns, common topics, or similar issues. Uses semantic search (LanceDB) across all customer knowledge bases. Results use slug (not real names) for privacy.\",\n inputSchema: z.object({\n query: z.string().describe(\"What to search for across all customers\"),\n excludeCurrentCustomer: z\n .boolean()\n .optional()\n .describe(\"Exclude the current customer from results\"),\n slug: z\n .string()\n .optional()\n .describe(\"Current customer slug (used with excludeCurrentCustomer)\"),\n }),\n },\n async ({ query, excludeCurrentCustomer, slug }) =>\n handleGetMarketIntelligence({\n query,\n ...(excludeCurrentCustomer !== undefined ? { excludeCurrentCustomer } : {}),\n ...(slug !== undefined ? { slug } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readGraph, getStakeholders, findPath } from \"../../core/graph.js\";\nimport type { GraphNode } from \"../../core/graph.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nfunction summarizeNode(n: GraphNode) {\n return { id: n.id, name: n.label, email: n.properties[\"email\"] };\n}\n\nexport async function handleGetRelationshipGraph(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const graph = readGraph(dataDir, input.slug);\n const stakeholders = getStakeholders(graph);\n\n // Warm intro paths: BFS from owner contacts to each economic buyer\n const ownerContactIds = graph.nodes\n .filter((n) => n.type === \"person\" && n.properties[\"isOwnerContact\"] === true)\n .map((n) => n.id);\n const economicBuyerIds = stakeholders.economicBuyers.map((n) => n.id);\n\n const warmIntroPaths: Array<{ target: string; path: string[] }> = [];\n for (const ebId of economicBuyerIds) {\n for (const ownerId of ownerContactIds) {\n const p = findPath(graph, ownerId, ebId);\n if (p.length > 1) {\n warmIntroPaths.push({ target: ebId, path: p });\n break;\n }\n }\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n slug: input.slug,\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n updatedAt: graph.updatedAt,\n stakeholders: {\n champions: stakeholders.champions.map(summarizeNode),\n blockers: stakeholders.blockers.map(summarizeNode),\n economicBuyers: stakeholders.economicBuyers.map(summarizeNode),\n allContacts: stakeholders.allContacts.map(summarizeNode),\n missingRoles: stakeholders.missingRoles,\n },\n warmIntroPaths,\n nodes: graph.nodes,\n edges: graph.edges,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetRelationshipGraph(server: McpServer): void {\n server.registerTool(\n \"get_relationship_graph\",\n {\n title: \"Get Relationship Graph\",\n description: `Returns the knowledge graph for a customer: all known contacts, companies,\nand the relationships between them (KNOWS, WORKS_AT, IS_CHAMPION, IS_BLOCKER, IS_ECONOMIC_BUYER).\n\nThe graph auto-populates from every log_interaction call.\nUse this before a complex deal conversation to understand the stakeholder map.\n\nArgs:\n slug: Customer slug\n\nReturns: {\n stakeholders: { champions[], blockers[], economicBuyers[], allContacts[], missingRoles[] },\n nodes: GraphNode[],\n edges: GraphEdge[]\n}`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n }),\n },\n async ({ slug }) => handleGetRelationshipGraph({ slug })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readHealth, computeCustomerHealth, writeHealth } from \"../../core/relationship-health.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\nconst MAX_HEALTH_AGE_MS = 60 * 60 * 1000; // 1 hour\n\nexport async function handleGetRelationshipHealth(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n let health = readHealth(dataDir, input.slug);\n if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {\n health = computeCustomerHealth(dataDir, input.slug);\n writeHealth(dataDir, input.slug, health);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n slug: input.slug,\n overallHealth: health.overallHealth,\n updatedAt: health.updatedAt,\n atRiskContacts: health.contacts\n .filter((c) => c.riskFlags.length > 0)\n .map((c) => c.email ?? c.contactId),\n coldContacts: health.contacts\n .filter((c) => c.trend === \"cold\")\n .map((c) => c.email ?? c.contactId),\n contacts: health.contacts,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetRelationshipHealth(server: McpServer): void {\n server.registerTool(\n \"get_relationship_health\",\n {\n title: \"Get Relationship Health\",\n description: `Returns health scores for all contacts of a customer.\nScores decay automatically when communication cadence breaks — without any manual input.\n\nEach contact gets:\n- score (0–100), grade (A–F), trend (rising|stable|declining|cold)\n- riskFlags: NO_CONTACT_14D, NO_CONTACT_30D, CHAMPION_SILENT\n- recommendation: concrete next action\n\noverallHealth is the average across all contacts.\natRiskContacts + coldContacts are pre-filtered for quick triage.\nHealth auto-updates after every log_interaction call. Recomputes if stale (>1h).\n\nArgs:\n slug: Customer slug\n\nReturns: {\n overallHealth: number,\n atRiskContacts: string[],\n coldContacts: string[],\n contacts: ContactHealth[]\n}`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n }),\n },\n async ({ slug }) => handleGetRelationshipHealth({ slug })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\nimport matter from \"gray-matter\";\nimport { callLlm } from \"./llm.js\";\nimport { withFileQueue } from \"../fs/write-queue.js\";\nimport type { DealSnapshot } from \"./revenue-simulation.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface PlaybookFrontmatter {\n trigger: string;\n successRate: number;\n usedCount: number;\n lastUpdated: string;\n}\n\nexport interface Playbook {\n slug: string;\n name: string;\n frontmatter: PlaybookFrontmatter;\n content: string;\n path: string;\n}\n\nexport interface TriggerCondition {\n type:\n | \"stage\"\n | \"value_gt\"\n | \"value_lt\"\n | \"days_stalled_gt\"\n | \"days_stalled_lt\"\n | \"health_lt\"\n | \"health_gt\"\n | \"no_champion\"\n | \"has_champion\";\n value?: number;\n stage?: string;\n}\n\nexport interface PlaybookMatch {\n playbook: Playbook;\n score: number;\n matchedConditions: TriggerCondition[];\n totalConditions: number;\n}\n\nexport interface LlmDistillation {\n name: string;\n trigger: string;\n content: string;\n successRate: number;\n reasoning: string;\n}\n\n// ─── File paths ───────────────────────────────────────────────────────────────\n\nexport function playbooksDir(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"playbooks\");\n}\n\n// ─── File operations ──────────────────────────────────────────────────────────\n\nexport function listPlaybooks(dataDir: string, slug: string): Playbook[] {\n const dir = playbooksDir(dataDir, slug);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".md\"))\n .map((f): Playbook => {\n const filePath = path.join(dir, f);\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = matter(raw);\n return {\n slug,\n name: f.replace(/\\.md$/, \"\"),\n frontmatter: parsed.data as PlaybookFrontmatter,\n content: parsed.content.trim(),\n path: filePath,\n };\n });\n}\n\nexport function readPlaybook(dataDir: string, slug: string, name: string): Playbook | null {\n const filePath = path.join(playbooksDir(dataDir, slug), `${name}.md`);\n if (!fs.existsSync(filePath)) return null;\n const raw = fs.readFileSync(filePath, \"utf-8\");\n const parsed = matter(raw);\n return {\n slug,\n name,\n frontmatter: parsed.data as PlaybookFrontmatter,\n content: parsed.content.trim(),\n path: filePath,\n };\n}\n\nexport async function writePlaybook(\n dataDir: string,\n slug: string,\n playbook: Playbook\n): Promise<void> {\n const dir = playbooksDir(dataDir, slug);\n const filePath = path.join(dir, `${playbook.name}.md`);\n await withFileQueue(filePath, async () => {\n fs.mkdirSync(dir, { recursive: true });\n const raw = matter.stringify(playbook.content, playbook.frontmatter);\n writeFileAtomic(filePath, raw);\n });\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nexport function toKebabCase(name: string): string {\n return name\n .replace(/[^a-z0-9-]/gi, \"-\")\n .toLowerCase()\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n// ─── Trigger DSL ──────────────────────────────────────────────────────────────\n\nexport interface ParsedTrigger {\n conditions: TriggerCondition[];\n operator: \"AND\" | \"OR\";\n}\n\nfunction parseTokens(tokens: string[]): TriggerCondition[] {\n return tokens.flatMap((token): TriggerCondition[] => {\n if (token.startsWith(\"deal_stage_\")) {\n return [{ type: \"stage\", stage: token.slice(\"deal_stage_\".length) }];\n }\n const valueGt = token.match(/^value\\s*>\\s*(\\d+)$/);\n if (valueGt) return [{ type: \"value_gt\", value: Number(valueGt[1]) }];\n\n const valueLt = token.match(/^value\\s*<\\s*(\\d+)$/);\n if (valueLt) return [{ type: \"value_lt\", value: Number(valueLt[1]) }];\n\n const stalledGt = token.match(/^days_stalled\\s*>\\s*(\\d+)$/);\n if (stalledGt) return [{ type: \"days_stalled_gt\", value: Number(stalledGt[1]) }];\n\n const stalledLt = token.match(/^days_stalled\\s*<\\s*(\\d+)$/);\n if (stalledLt) return [{ type: \"days_stalled_lt\", value: Number(stalledLt[1]) }];\n\n const healthLt = token.match(/^health\\s*<\\s*(\\d+)$/);\n if (healthLt) return [{ type: \"health_lt\", value: Number(healthLt[1]) }];\n\n const healthGt = token.match(/^health\\s*>\\s*(\\d+)$/);\n if (healthGt) return [{ type: \"health_gt\", value: Number(healthGt[1]) }];\n\n if (token === \"no_champion\") return [{ type: \"no_champion\" }];\n if (token === \"has_champion\") return [{ type: \"has_champion\" }];\n\n return []; // unknown token — silently dropped\n });\n}\n\nexport function parseTrigger(triggerStr: string | null | undefined): TriggerCondition[] {\n if (!triggerStr?.trim()) return [];\n const tokens = triggerStr\n .split(/\\s+AND\\s+/)\n .map((t) => t.trim())\n .filter(Boolean);\n return parseTokens(tokens);\n}\n\nexport function parseTriggerFull(triggerStr: string | null | undefined): ParsedTrigger {\n if (!triggerStr?.trim()) return { conditions: [], operator: \"AND\" };\n const orTokens = triggerStr\n .split(/\\s+OR\\s+/)\n .map((t) => t.trim())\n .filter(Boolean);\n if (orTokens.length > 1) {\n return { conditions: parseTokens(orTokens), operator: \"OR\" };\n }\n const andTokens = triggerStr\n .split(/\\s+AND\\s+/)\n .map((t) => t.trim())\n .filter(Boolean);\n return { conditions: parseTokens(andTokens), operator: \"AND\" };\n}\n\nexport function evaluateCondition(\n cond: TriggerCondition,\n deal: DealSnapshot,\n daysSinceContact: number\n): boolean {\n switch (cond.type) {\n case \"stage\":\n return deal.stage === cond.stage;\n case \"value_gt\":\n return deal.value > (cond.value ?? 0);\n case \"value_lt\":\n return deal.value < (cond.value ?? Infinity);\n // v1: days_stalled uses daysSinceContact as proxy (stage-change timestamps not tracked)\n case \"days_stalled_gt\":\n return daysSinceContact > (cond.value ?? 0);\n case \"days_stalled_lt\":\n return daysSinceContact < (cond.value ?? Infinity);\n case \"health_lt\":\n return deal.healthScore < (cond.value ?? 100);\n case \"health_gt\":\n return deal.healthScore > (cond.value ?? 0);\n case \"no_champion\":\n return !deal.championPresent;\n case \"has_champion\":\n return deal.championPresent;\n default:\n return false;\n }\n}\n\nexport function evaluateTrigger(\n conditions: TriggerCondition[],\n deal: DealSnapshot,\n daysSinceContact: number = 0,\n operator: \"AND\" | \"OR\" = \"AND\"\n): boolean {\n if (operator === \"OR\") {\n return conditions.some((c) => evaluateCondition(c, deal, daysSinceContact));\n }\n return conditions.every((c) => evaluateCondition(c, deal, daysSinceContact));\n}\n\n// ─── Matching ─────────────────────────────────────────────────────────────────\n\nexport function matchPlaybooks(\n playbooks: Playbook[],\n deal: DealSnapshot,\n daysSinceContact: number = 0\n): PlaybookMatch[] {\n const results: PlaybookMatch[] = [];\n for (const pb of playbooks) {\n const { conditions, operator } = parseTriggerFull(pb.frontmatter.trigger);\n if (conditions.length === 0) continue;\n const matched = conditions.filter((c) => evaluateCondition(c, deal, daysSinceContact));\n const isMatch = operator === \"OR\" ? matched.length > 0 : matched.length === conditions.length;\n if (isMatch) {\n results.push({\n playbook: pb,\n score: 1.0,\n matchedConditions: matched,\n totalConditions: conditions.length,\n });\n }\n }\n return results.sort((a, b) => {\n const rateDiff =\n (b.playbook.frontmatter.successRate ?? 0) - (a.playbook.frontmatter.successRate ?? 0);\n return rateDiff !== 0\n ? rateDiff\n : (b.playbook.frontmatter.usedCount ?? 0) - (a.playbook.frontmatter.usedCount ?? 0);\n });\n}\n\nexport function getBestPlaybook(\n dataDir: string,\n slug: string,\n deal: DealSnapshot,\n daysSinceContact: number = 0\n): PlaybookMatch | null {\n return matchPlaybooks(listPlaybooks(dataDir, slug), deal, daysSinceContact)[0] ?? null;\n}\n\n// ─── Distillation ─────────────────────────────────────────────────────────────\n\nexport function buildDistillPrompt(\n slug: string,\n dealName: string,\n outcome: \"won\" | \"lost\",\n interactions: string\n): string {\n return `You are analyzing a sales deal to extract a reusable playbook.\n\nCustomer: ${slug}\nDeal: ${dealName}\nOutcome: ${outcome}\nInteractions (chronological):\n${interactions.slice(0, 4000)}\n\nExtract a reusable playbook from this deal's journey.\n\nAllowed trigger tokens (combine with \" AND \"):\n- deal_stage_<stage> (e.g. deal_stage_negotiation)\n- value > <n> (e.g. value > 50000)\n- value < <n>\n- days_stalled > <n> (e.g. days_stalled > 7)\n- days_stalled < <n>\n- health < <n> (e.g. health < 60)\n- health > <n>\n- no_champion\n- has_champion\n\nReturn JSON only (no markdown wrapper):\n{\n \"name\": \"<kebab-case-playbook-name>\",\n \"trigger\": \"<DSL string using allowed tokens>\",\n \"content\": \"<markdown with ## Situation, ## Steps, ## Warnings sections>\",\n \"successRate\": <0.0-1.0>,\n \"reasoning\": \"<why these trigger conditions>\"\n}`;\n}\n\nexport function parseLlmDistillation(\n response: string,\n outcomeFallback: number = 0.5\n): LlmDistillation | null {\n try {\n const match = response.match(/\\{[\\s\\S]*\\}/);\n if (!match) return null;\n const parsed = JSON.parse(match[0]) as Partial<LlmDistillation>;\n if (!parsed.name || !parsed.trigger || !parsed.content) return null;\n return {\n name: parsed.name,\n trigger: parsed.trigger,\n content: parsed.content,\n successRate: typeof parsed.successRate === \"number\" ? parsed.successRate : outcomeFallback,\n reasoning: parsed.reasoning ?? \"\",\n };\n } catch {\n return null;\n }\n}\n\nexport type DistillPlaybookResult =\n | { ok: true; playbook: Playbook; reasoning: string }\n | { ok: false; errorKind: \"no_interactions\" | \"parse_failed\" };\n\nexport async function distillPlaybook(\n dataDir: string,\n slug: string,\n dealName: string,\n outcome: \"won\" | \"lost\",\n llmFn: (prompt: string) => Promise<string> = callLlm\n): Promise<DistillPlaybookResult> {\n const interactionsPath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n if (!fs.existsSync(interactionsPath)) return { ok: false, errorKind: \"no_interactions\" };\n\n const interactions = fs.readFileSync(interactionsPath, \"utf-8\");\n const prompt = buildDistillPrompt(slug, dealName, outcome, interactions);\n const response = await llmFn(prompt);\n\n const outcomeFallback = outcome === \"won\" ? 1.0 : 0.0;\n const distillation = parseLlmDistillation(response, outcomeFallback);\n if (!distillation) return { ok: false, errorKind: \"parse_failed\" };\n\n const today = new Date().toISOString().slice(0, 10);\n const name = toKebabCase(distillation.name);\n\n const playbook: Playbook = {\n slug,\n name,\n frontmatter: {\n trigger: distillation.trigger,\n successRate: distillation.successRate,\n usedCount: 1,\n lastUpdated: today,\n },\n content: distillation.content,\n path: path.join(playbooksDir(dataDir, slug), `${name}.md`),\n };\n\n await writePlaybook(dataDir, slug, playbook);\n return { ok: true, playbook, reasoning: distillation.reasoning };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { deriveDealTiming, scoreDealForToday } from \"../core/deal-health.js\";\nimport { computeCustomerHealth, readHealth } from \"../core/relationship-health.js\";\nimport type { InteractionEntry } from \"../schemas/interaction.js\";\nimport { readGraph, getStakeholders } from \"../core/graph.js\";\nimport { callLlm } from \"../core/llm.js\";\nimport { listPlaybooks, matchPlaybooks, type PlaybookMatch } from \"../core/playbooks.js\";\nimport type { PipelineDeal } from \"../schemas/pipeline.js\";\nimport type { DealHealthScore } from \"../core/deal-health.js\";\nimport type { HealthSnapshot } from \"../core/relationship-health.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type AutonomyLevel = \"observe\" | \"suggest\" | \"act\";\nexport type ActionType = \"log_interaction\" | \"update_deal\" | \"alert\" | \"schedule_meeting\";\nexport type ActionStatus = \"pending\" | \"approved\" | \"executed\" | \"rejected\" | \"skipped\";\nexport type RiskLevel = \"low\" | \"medium\" | \"high\" | \"critical\";\n\nexport interface DealAgentConfig {\n slug: string;\n dealName: string;\n autonomyLevel: AutonomyLevel;\n instruction?: string;\n valueThreshold: number;\n today: string;\n}\n\nexport interface DealAgentAction {\n actionId: string;\n type: ActionType;\n payload: Record<string, unknown>;\n confidence: number;\n reasoning: string;\n requiresHumanApproval: boolean;\n status: ActionStatus;\n createdAt: string;\n}\n\nexport interface DealPlanStep {\n step: number;\n action: string;\n priority: \"urgent\" | \"high\" | \"medium\" | \"low\";\n reason: string;\n}\n\nexport interface DealAgentTrace {\n timestamp: string;\n slug: string;\n dealName: string;\n autonomyLevel: AutonomyLevel;\n observation: string;\n plan: string[];\n actionsConsidered: DealAgentAction[];\n actionTaken: DealAgentAction | null;\n outcome: \"queued\" | \"executed\" | \"observed\" | \"error\";\n}\n\nexport interface DealAgentResult {\n slug: string;\n dealName: string;\n assessment: string;\n riskLevel: RiskLevel;\n plan: DealPlanStep[];\n actionsQueued: DealAgentAction[];\n actionsExecuted: DealAgentAction[];\n trace: DealAgentTrace;\n}\n\nexport interface LlmDealAnalysis {\n assessment: string;\n riskLevel: RiskLevel;\n plan: DealPlanStep[];\n actions: Array<{\n type: ActionType;\n payload: Record<string, unknown>;\n confidence: number;\n reasoning: string;\n }>;\n}\n\nexport interface DealObservation {\n deal: PipelineDeal;\n daysSinceLastActivity: number;\n daysInCurrentStage: number;\n daysToClose?: number;\n dealHealthScore: DealHealthScore;\n overallRelationshipHealth: number;\n atRiskContacts: string[];\n coldContacts: string[];\n missingRoles: Array<{ role: string; urgency: string }>;\n championCount: number;\n recentInteractionsSummary: string;\n contextSummary: string;\n matchingPlaybooks?: PlaybookMatch[]; // D15: procedural memory\n}\n\nexport interface AgentQueue {\n schemaVersion: \"1\";\n slug: string;\n pendingActions: DealAgentAction[];\n updatedAt: string;\n}\n\n// ─── File paths ───────────────────────────────────────────────────────────────\n\nexport function agentQueuePath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"agent-queue.json\");\n}\n\n// ─── Queue read / write ───────────────────────────────────────────────────────\n\nexport function readAgentQueue(dataDir: string, slug: string): AgentQueue {\n const p = agentQueuePath(dataDir, slug);\n if (!fs.existsSync(p)) {\n return { schemaVersion: \"1\", slug, pendingActions: [], updatedAt: new Date().toISOString() };\n }\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\")) as AgentQueue;\n } catch {\n return { schemaVersion: \"1\", slug, pendingActions: [], updatedAt: new Date().toISOString() };\n }\n}\n\nexport function writeAgentQueue(dataDir: string, slug: string, queue: AgentQueue): void {\n const p = agentQueuePath(dataDir, slug);\n const dir = path.dirname(p);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n const updated: AgentQueue = { ...queue, updatedAt: new Date().toISOString() };\n writeJsonFile(p, updated);\n}\n\n// ─── Action ID ────────────────────────────────────────────────────────────────\n\nexport function makeActionId(): string {\n return `da_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n// ─── Observation helpers ──────────────────────────────────────────────────────\n\nfunction buildRecentInteractionsSummary(interactionsPath: string): string {\n if (!fs.existsSync(interactionsPath)) return \"(no interactions)\";\n const content = fs.readFileSync(interactionsPath, \"utf-8\") as string;\n const blocks = content\n .split(/(?=^## \\d{4}-\\d{2}-\\d{2})/m)\n .filter((b) => b.trim().length > 0)\n .slice(0, 3);\n return blocks\n .map((b) => {\n const dateMatch = b.match(/^## (\\d{4}-\\d{2}-\\d{2}) · (\\w+)/m);\n const summaryMatch = b.match(/^\\*\\*Summary:\\*\\*\\s*(.+)$/m);\n if (!dateMatch || !summaryMatch) return \"\";\n return `[${dateMatch[1]!}/${dateMatch[2]!}] ${summaryMatch[1]!.trim()}`;\n })\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nfunction buildContextSummary(data: {\n deal: PipelineDeal;\n daysSinceLastActivity: number;\n daysInCurrentStage: number;\n daysToClose: number | undefined;\n dealHealthScore: DealHealthScore;\n health: HealthSnapshot;\n atRiskContacts: string[];\n coldContacts: string[];\n missingRoles: Array<{ role: string; urgency: string }>;\n championCount: number;\n recentInteractionsSummary: string;\n}): string {\n const lines: string[] = [\n `Deal: ${data.deal.name} | Stage: ${data.deal.stage} | Value: €${data.deal.value ?? \"?\"} | Close: ${data.deal.close_date ?? \"not set\"}`,\n `Days since activity: ${data.daysSinceLastActivity} | Days to close: ${data.daysToClose ?? \"?\"}`,\n `Deal health: grade ${data.dealHealthScore.grade} (score ${data.dealHealthScore.score})`,\n `Warnings: ${data.dealHealthScore.warnings.join(\"; \") || \"none\"}`,\n ``,\n `Relationship health: ${data.health.overallHealth}/100`,\n `At-risk contacts: ${data.atRiskContacts.join(\", \") || \"none\"}`,\n `Cold contacts: ${data.coldContacts.join(\", \") || \"none\"}`,\n `Missing stakeholder roles: ${data.missingRoles.map((r) => r.role).join(\", \") || \"none\"}`,\n `Champions identified: ${data.championCount}`,\n ``,\n `Recent interactions:`,\n data.recentInteractionsSummary || \"(none)\",\n ];\n return lines.join(\"\\n\");\n}\n\n// ─── Observation Layer ────────────────────────────────────────────────────────\n\nexport async function observeDeal(\n dataDir: string,\n slug: string,\n dealName: string,\n today: string\n): Promise<DealObservation | null> {\n const deals = await readPipeline(dataDir, slug).catch(() => [] as PipelineDeal[]);\n const deal = deals.find((d) => d.name.toLowerCase() === dealName.toLowerCase());\n if (!deal) return null;\n\n const todayDate = new Date(today);\n const { daysSinceLastActivity, daysInCurrentStage, daysToClose } = deriveDealTiming(\n deal,\n todayDate\n );\n const dealHealthScore = scoreDealForToday(deal, todayDate);\n\n // Prefer the cached health snapshot (written on each interaction); only\n // recompute — re-reading and parsing the full interactions file — when none\n // exists yet. Mirrors proactive-worker's read-then-compute pattern.\n const health = readHealth(dataDir, slug) ?? computeCustomerHealth(dataDir, slug, today);\n const atRiskContacts = health.contacts\n .filter((c) => c.riskFlags.length > 0)\n .map((c) => c.email ?? c.contactId);\n const coldContacts = health.contacts\n .filter((c) => c.trend === \"cold\")\n .map((c) => c.email ?? c.contactId);\n\n const graph = readGraph(dataDir, slug);\n const stakeholders = getStakeholders(graph);\n const missingRoles = stakeholders.missingRoles.map((r) => ({\n role: r.role,\n urgency: r.urgency,\n }));\n const championCount = stakeholders.champions.length;\n\n const interactionsPath = path.join(dataDir, \"customers\", slug, \"interactions.md\");\n const recentInteractionsSummary = buildRecentInteractionsSummary(interactionsPath);\n\n const contextSummary = buildContextSummary({\n deal,\n daysSinceLastActivity,\n daysInCurrentStage,\n daysToClose,\n dealHealthScore,\n health,\n atRiskContacts,\n coldContacts,\n missingRoles,\n championCount,\n recentInteractionsSummary,\n });\n\n const obs: DealObservation = {\n deal,\n daysSinceLastActivity,\n daysInCurrentStage,\n dealHealthScore,\n overallRelationshipHealth: health.overallHealth,\n atRiskContacts,\n coldContacts,\n missingRoles,\n championCount,\n recentInteractionsSummary,\n contextSummary,\n };\n if (daysToClose !== undefined) obs.daysToClose = daysToClose;\n\n // D15: load matching playbooks from procedural memory\n const dealSnap = {\n slug,\n name: deal.name,\n stage: deal.stage,\n value: deal.value ?? 0,\n probability: deal.probability ?? 50,\n healthScore: health.overallHealth,\n daysSinceContact: daysSinceLastActivity,\n championPresent: championCount > 0,\n };\n const allPlaybooks = listPlaybooks(dataDir, slug);\n const matchingPlaybooks = matchPlaybooks(allPlaybooks, dealSnap, daysSinceLastActivity);\n if (matchingPlaybooks.length > 0) obs.matchingPlaybooks = matchingPlaybooks;\n\n return obs;\n}\n\n// ─── LLM Prompt ──────────────────────────────────────────────────────────────\n\nexport function buildLlmPrompt(obs: DealObservation, config: DealAgentConfig): string {\n const instruction = config.instruction ?? \"Analyze this deal and recommend next actions.\";\n\n const playbookSection =\n obs.matchingPlaybooks && obs.matchingPlaybooks.length > 0\n ? `\\n## Matching Playbooks (${obs.matchingPlaybooks.length} found — apply these proven tactics)\\n` +\n obs.matchingPlaybooks\n .slice(0, 2)\n .map(\n (m) =>\n `### ${m.playbook.name} (${Math.round(m.playbook.frontmatter.successRate * 100)}% success rate, used ${m.playbook.frontmatter.usedCount}x)\\n${m.playbook.content.slice(0, 500)}`\n )\n .join(\"\\n\\n\")\n : \"\";\n\n return `You are a CRM deal agent. Analyze the deal situation and return an action plan.\nReturn ONLY valid JSON — no markdown, no explanation.\n\n${obs.contextSummary}${playbookSection}\n\nInstruction: ${instruction}\n\nRespond with JSON matching exactly:\n{\n \"assessment\": \"<2-3 sentence situation assessment>\",\n \"riskLevel\": \"low\" | \"medium\" | \"high\" | \"critical\",\n \"plan\": [\n { \"step\": 1, \"action\": \"<what to do>\", \"priority\": \"urgent\" | \"high\" | \"medium\" | \"low\", \"reason\": \"<why>\" }\n ],\n \"actions\": [\n {\n \"type\": \"log_interaction\" | \"update_deal\" | \"alert\" | \"schedule_meeting\",\n \"payload\": { /* tool-specific fields */ },\n \"confidence\": 0.0-1.0,\n \"reasoning\": \"<why this action>\"\n }\n ]\n}\n\nPayload schema per type:\n- log_interaction: { slug, type: \"Note\"|\"Call\"|\"Meeting\", summary, with }\n- update_deal: { slug, dealName, stage?, probability?, closeDate?, notes? }\n- alert: { slug, message, urgency: \"critical\"|\"high\"|\"medium\" }\n- schedule_meeting: { slug, with, notes }`;\n}\n\n// ─── LLM Response Parser ──────────────────────────────────────────────────────\n\nexport function parseLlmResponse(response: string): LlmDealAnalysis | null {\n try {\n const cleaned = response\n .replace(/^```(?:json)?\\n?/m, \"\")\n .replace(/\\n?```$/m, \"\")\n .trim();\n const parsed = JSON.parse(cleaned) as Partial<LlmDealAnalysis>;\n if (!parsed.assessment || !parsed.riskLevel || !Array.isArray(parsed.plan)) {\n return null;\n }\n return {\n assessment: String(parsed.assessment),\n riskLevel: parsed.riskLevel,\n plan: Array.isArray(parsed.plan) ? parsed.plan : [],\n actions: Array.isArray(parsed.actions) ? parsed.actions : [],\n };\n } catch {\n return null;\n }\n}\n\n// ─── Rule-Based Fallback ──────────────────────────────────────────────────────\n\nexport function buildRuleBasedAnalysis(\n obs: DealObservation,\n config: DealAgentConfig\n): LlmDealAnalysis {\n const plan: DealPlanStep[] = [];\n const actions: LlmDealAnalysis[\"actions\"] = [];\n let riskLevel: RiskLevel = \"low\";\n\n if (obs.dealHealthScore.grade === \"F\" || obs.coldContacts.length > 0) riskLevel = \"critical\";\n else if (obs.dealHealthScore.grade === \"D\" || obs.atRiskContacts.length > 0) riskLevel = \"high\";\n else if (obs.dealHealthScore.grade === \"C\") riskLevel = \"medium\";\n\n let step = 1;\n\n // D15: Playbook alerts as first plan items\n if (obs.matchingPlaybooks && obs.matchingPlaybooks.length > 0) {\n for (const match of obs.matchingPlaybooks.slice(0, 2)) {\n plan.push({\n step: step++,\n action: `Apply playbook: \"${match.playbook.name}\"`,\n priority: \"high\",\n reason: `Proven tactic (${Math.round(match.playbook.frontmatter.successRate * 100)}% success, used ${match.playbook.frontmatter.usedCount}x) — trigger: ${match.playbook.frontmatter.trigger}`,\n });\n actions.push({\n type: \"alert\",\n payload: {\n slug: config.slug,\n message: `Playbook available: \"${match.playbook.name}\" (${Math.round(match.playbook.frontmatter.successRate * 100)}% success rate)`,\n playbookContent: match.playbook.content.slice(0, 1000),\n urgency: \"high\",\n },\n confidence: match.playbook.frontmatter.successRate,\n reasoning: `Trigger matched: ${match.playbook.frontmatter.trigger}`,\n });\n }\n }\n\n if (obs.coldContacts.length > 0) {\n plan.push({\n step: step++,\n action: `Re-engage cold contacts: ${obs.coldContacts.join(\", \")}`,\n priority: \"urgent\",\n reason: \"No contact in 30+ days\",\n });\n actions.push({\n type: \"alert\",\n payload: {\n slug: config.slug,\n message: `Cold contacts: ${obs.coldContacts.join(\", \")}`,\n urgency: \"critical\",\n },\n confidence: 0.95,\n reasoning: \"No contact in 30+ days\",\n });\n }\n\n if (obs.atRiskContacts.length > 0) {\n plan.push({\n step: step++,\n action: `Schedule call with at-risk contacts`,\n priority: \"high\",\n reason: \"14+ days without contact\",\n });\n actions.push({\n type: \"schedule_meeting\",\n payload: {\n slug: config.slug,\n with: obs.atRiskContacts[0] ?? \"\",\n notes: \"Scheduled by deal agent — relationship at risk\",\n },\n confidence: 0.8,\n reasoning: \"At-risk contact identified\",\n });\n }\n\n if (obs.missingRoles.some((r) => r.role === \"economic_buyer\")) {\n plan.push({\n step: step++,\n action: \"Identify economic buyer\",\n priority: \"high\",\n reason: \"No budget owner identified\",\n });\n }\n\n if (obs.daysToClose !== undefined && obs.daysToClose < 14 && obs.dealHealthScore.grade !== \"A\") {\n plan.push({\n step: step++,\n action: \"Update deal close date or probability\",\n priority: \"urgent\",\n reason: `Close date in ${obs.daysToClose} days, deal at grade ${obs.dealHealthScore.grade}`,\n });\n actions.push({\n type: \"update_deal\",\n payload: {\n slug: config.slug,\n dealName: config.dealName,\n notes: `Reviewed by deal agent — ${obs.daysToClose}d to close`,\n },\n confidence: 0.75,\n reasoning: \"Close date imminent\",\n });\n }\n\n if (plan.length === 0) {\n plan.push({\n step: 1,\n action: \"Maintain current cadence\",\n priority: \"low\",\n reason: \"Deal healthy\",\n });\n }\n\n const assessment = `Deal \"${config.dealName}\" in stage \"${obs.deal.stage}\" — health grade ${obs.dealHealthScore.grade} (${obs.dealHealthScore.score}/100). Risk: ${riskLevel}.`;\n return { assessment, riskLevel, plan, actions };\n}\n\n// ─── Action Selection ─────────────────────────────────────────────────────────\n\nexport function selectActions(\n analysis: LlmDealAnalysis,\n obs: DealObservation,\n config: DealAgentConfig\n): DealAgentAction[] {\n return analysis.actions.map((a) => {\n const dealValue = obs.deal.value ?? 0;\n const autoExecutable =\n config.autonomyLevel === \"act\" && a.confidence >= 0.7 && dealValue < config.valueThreshold;\n\n return {\n actionId: makeActionId(),\n type: a.type,\n payload: a.payload,\n confidence: a.confidence,\n reasoning: a.reasoning,\n requiresHumanApproval: !autoExecutable,\n status: \"pending\" as ActionStatus,\n createdAt: new Date().toISOString(),\n };\n });\n}\n\n// ─── Action Execution ─────────────────────────────────────────────────────────\n\nconst VALID_STAGES = [\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"] as const;\n\nexport async function executeAction(\n action: DealAgentAction,\n dataDir: string\n): Promise<\"executed\" | \"skipped\"> {\n const slug = action.payload[\"slug\"] as string | undefined;\n if (!slug) return \"skipped\";\n\n switch (action.type) {\n case \"log_interaction\": {\n const { appendInteraction } = await import(\"../fs/interactions-writer.js\");\n const today = new Date().toISOString().slice(0, 10);\n await appendInteraction(dataDir, slug, {\n date: today,\n type: (action.payload[\"type\"] as InteractionEntry[\"type\"]) ?? \"Note\",\n with: String(action.payload[\"with\"] ?? \"agent\"),\n summary: String(action.payload[\"summary\"] ?? \"\"),\n nextSteps: [],\n sourceRef: `agent://deal-agent/${action.actionId}`,\n synced: new Date().toISOString(),\n });\n return \"executed\";\n }\n\n case \"schedule_meeting\": {\n const { appendInteraction } = await import(\"../fs/interactions-writer.js\");\n const today = new Date().toISOString().slice(0, 10);\n await appendInteraction(dataDir, slug, {\n date: today,\n type: \"Note\",\n with: String(action.payload[\"with\"] ?? \"\"),\n summary: `[Agent scheduled] ${String(action.payload[\"notes\"] ?? \"Meeting scheduled by deal agent\")}`,\n nextSteps: [`Schedule meeting with ${String(action.payload[\"with\"] ?? \"contact\")}`],\n sourceRef: `agent://deal-agent/${action.actionId}`,\n synced: new Date().toISOString(),\n });\n return \"executed\";\n }\n\n case \"update_deal\": {\n const { handleUpdateDeal } = await import(\"../mcp/tools/update-deal.js\");\n const payload = action.payload as {\n slug: string;\n dealName: string;\n stage?: string;\n value?: number;\n probability?: number;\n closeDate?: string;\n notes?: string;\n };\n const validStage = VALID_STAGES.find((s) => s === payload.stage);\n await handleUpdateDeal(\n {\n slug: payload.slug,\n dealName: payload.dealName,\n ...(validStage !== undefined ? { stage: validStage } : {}),\n ...(payload.value !== undefined ? { value: payload.value } : {}),\n ...(payload.probability !== undefined ? { probability: payload.probability } : {}),\n ...(payload.closeDate !== undefined ? { closeDate: payload.closeDate } : {}),\n ...(payload.notes !== undefined ? { notes: payload.notes } : {}),\n },\n dataDir\n );\n return \"executed\";\n }\n\n case \"alert\": {\n const queue = readAgentQueue(dataDir, slug);\n const alertAction: DealAgentAction = { ...action, status: \"pending\" };\n if (!queue.pendingActions.find((a) => a.actionId === action.actionId)) {\n queue.pendingActions.push(alertAction);\n writeAgentQueue(dataDir, slug, queue);\n }\n return \"executed\";\n }\n }\n}\n\n// ─── Main: runDealAgent ───────────────────────────────────────────────────────\n\nexport async function runDealAgent(\n config: DealAgentConfig,\n dataDir: string,\n llmFn: (prompt: string) => Promise<string> = callLlm\n): Promise<DealAgentResult> {\n const timestamp = new Date().toISOString();\n\n const obs = await observeDeal(dataDir, config.slug, config.dealName, config.today);\n if (!obs) {\n throw new Error(`Deal \"${config.dealName}\" not found for customer \"${config.slug}\"`);\n }\n\n let analysis: LlmDealAnalysis;\n try {\n const prompt = buildLlmPrompt(obs, config);\n const rawResponse = await llmFn(prompt);\n analysis = parseLlmResponse(rawResponse) ?? buildRuleBasedAnalysis(obs, config);\n } catch {\n analysis = buildRuleBasedAnalysis(obs, config);\n }\n\n const allActions = selectActions(analysis, obs, config);\n\n const actionsQueued: DealAgentAction[] = [];\n const actionsExecuted: DealAgentAction[] = [];\n\n if (config.autonomyLevel === \"observe\") {\n // No side effects\n } else if (config.autonomyLevel === \"suggest\") {\n if (allActions.length > 0) {\n const queue = readAgentQueue(dataDir, config.slug);\n for (const action of allActions) {\n queue.pendingActions.push({ ...action, requiresHumanApproval: true });\n actionsQueued.push(action);\n }\n writeAgentQueue(dataDir, config.slug, queue);\n }\n } else {\n // act mode\n const queue = readAgentQueue(dataDir, config.slug);\n let queueDirty = false;\n for (const action of allActions) {\n if (!action.requiresHumanApproval) {\n const outcome = await executeAction(action, dataDir).catch(() => \"skipped\" as const);\n actionsExecuted.push({\n ...action,\n status: outcome === \"executed\" ? \"executed\" : \"skipped\",\n });\n } else {\n queue.pendingActions.push(action);\n actionsQueued.push(action);\n queueDirty = true;\n }\n }\n if (queueDirty) writeAgentQueue(dataDir, config.slug, queue);\n }\n\n const trace: DealAgentTrace = {\n timestamp,\n slug: config.slug,\n dealName: config.dealName,\n autonomyLevel: config.autonomyLevel,\n observation: obs.contextSummary,\n plan: analysis.plan.map((s) => `${s.step}. ${s.action} [${s.priority}]`),\n actionsConsidered: allActions,\n actionTaken: actionsExecuted[0] ?? null,\n outcome:\n actionsExecuted.length > 0 ? \"executed\" : actionsQueued.length > 0 ? \"queued\" : \"observed\",\n };\n\n return {\n slug: config.slug,\n dealName: config.dealName,\n assessment: analysis.assessment,\n riskLevel: analysis.riskLevel,\n plan: analysis.plan,\n actionsQueued,\n actionsExecuted,\n trace,\n };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { runDealAgent } from \"../../agents/deal-agent.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleRunDealAgent(\n input: {\n slug: string;\n dealName: string;\n autonomyLevel?: \"observe\" | \"suggest\" | \"act\";\n instruction?: string;\n valueThreshold?: number;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const result = await runDealAgent(\n {\n slug: input.slug,\n dealName: input.dealName,\n autonomyLevel: input.autonomyLevel ?? \"suggest\",\n valueThreshold: input.valueThreshold ?? 50_000,\n today,\n ...(input.instruction !== undefined ? { instruction: input.instruction } : {}),\n },\n dataDir\n );\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerRunDealAgent(server: McpServer): void {\n server.registerTool(\n \"run_deal_agent\",\n {\n title: \"Run Deal Agent\",\n description: `Analyzes a specific deal and generates a prioritized action plan.\n\nThree autonomy levels:\n- observe: analyze and return plan, no side effects\n- suggest (default): queue actions for human review in agent-queue.json\n- act: auto-execute actions with confidence ≥ 0.7 and value < valueThreshold\n\nEach action includes confidence score and reasoning (glass-box).\nReturns full trace for inspection.\n\nArgs:\n slug: Customer slug\n dealName: Exact deal name\n autonomyLevel: \"observe\" | \"suggest\" | \"act\" (default: \"suggest\")\n instruction: Optional context/question for the agent\n valueThreshold: EUR value above which no auto-execution (default: 50000)\n\nReturns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], trace }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().describe(\"Exact deal name\"),\n autonomyLevel: z\n .enum([\"observe\", \"suggest\", \"act\"])\n .optional()\n .describe(\"Autonomy level (default: suggest)\"),\n instruction: z.string().optional().describe(\"Optional instruction for the agent\"),\n valueThreshold: z\n .number()\n .optional()\n .describe(\"EUR value above which no auto-execution (default: 50000)\"),\n }),\n },\n async ({ slug, dealName, autonomyLevel, instruction, valueThreshold }) =>\n handleRunDealAgent({\n slug,\n dealName,\n ...(autonomyLevel !== undefined ? { autonomyLevel } : {}),\n ...(instruction !== undefined ? { instruction } : {}),\n ...(valueThreshold !== undefined ? { valueThreshold } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readAgentQueue, writeAgentQueue, executeAction } from \"../../agents/deal-agent.js\";\n\nexport { readAgentQueue } from \"../../agents/deal-agent.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleApproveAgentAction(\n input: { slug: string; actionId: string; approved: boolean },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const queue = readAgentQueue(dataDir, input.slug);\n const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);\n\n if (idx === -1) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: false, error: `Action ${input.actionId} not found in queue` },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const action = queue.pendingActions[idx]!;\n\n if (!input.approved) {\n queue.pendingActions[idx] = { ...action, status: \"rejected\" };\n writeAgentQueue(dataDir, input.slug, queue);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: true, actionId: input.actionId, status: \"rejected\" },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const outcome = await executeAction(action, dataDir);\n queue.pendingActions[idx] = {\n ...action,\n status: outcome === \"executed\" ? \"executed\" : \"skipped\",\n };\n writeAgentQueue(dataDir, input.slug, queue);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n actionId: input.actionId,\n status: queue.pendingActions[idx]!.status,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerApproveAgentAction(server: McpServer): void {\n server.registerTool(\n \"approve_agent_action\",\n {\n title: \"Approve Agent Action\",\n description: `Approve or reject a pending action from the deal agent queue.\n\nFind actionId in the actionsQueued array returned by run_deal_agent.\n\nArgs:\n slug: Customer slug\n actionId: Action ID from the agent queue\n approved: true to execute, false to reject\n\nReturns: { success, actionId, status }`,\n inputSchema: z.object({\n slug: z.string(),\n actionId: z.string(),\n approved: z.boolean(),\n }),\n },\n async ({ slug, actionId, approved }) => handleApproveAgentAction({ slug, actionId, approved })\n );\n}\n","import { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport { readHealth, computeCustomerHealth } from \"./relationship-health.js\";\nimport { readGraph, getStakeholders } from \"./graph.js\";\nimport { getPipelineStages } from \"./pipeline-stages.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ExternalSignal {\n slug: string;\n type: \"funding_round\" | \"leadership_change\" | \"news_positive\" | \"news_negative\";\n impact: \"positive\" | \"negative\" | \"neutral\";\n magnitude: number; // 0.0–1.0\n summary: string;\n}\n\nexport interface DealSnapshot {\n slug: string;\n name: string;\n stage: string;\n value: number;\n probability: number;\n closeDate?: string;\n healthScore: number;\n daysSinceContact: number;\n championPresent: boolean;\n}\n\nexport interface SimulationInput {\n deals: DealSnapshot[];\n externalSignals: ExternalSignal[];\n iterations: number;\n horizon: \"quarter\" | \"year\";\n today: string;\n}\n\nexport interface MonthForecast {\n p50: number;\n range: [number, number];\n}\n\nexport interface SimulationResult {\n p10: number;\n p50: number;\n p90: number;\n expected: number;\n stdDev: number;\n atRiskRevenue: number;\n byCloseMonth: Record<string, MonthForecast>;\n topRisks: string[];\n sensitivityMap: Record<string, number>;\n}\n\n// ─── Pure helpers ─────────────────────────────────────────────────────────────\n\nexport function percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0;\n const idx = Math.max(0, Math.ceil((p / 100) * sorted.length) - 1);\n return sorted[idx]!;\n}\n\nexport function mean(values: number[]): number {\n if (values.length === 0) return 0;\n return values.reduce((s, v) => s + v, 0) / values.length;\n}\n\nexport function stdDevFn(values: number[], m: number): number {\n if (values.length < 2) return 0;\n const variance = values.reduce((s, v) => s + (v - m) ** 2, 0) / values.length;\n return Math.sqrt(variance);\n}\n\nexport function adjustProbability(deal: DealSnapshot, signals: ExternalSignal[] = []): number {\n let prob = deal.probability / 100;\n\n // Health adjustment: health 60 = neutral, range -0.12 to +0.08\n const healthAdj = ((deal.healthScore - 60) / 100) * 0.2;\n prob += healthAdj;\n\n // Champion bonus\n if (deal.championPresent) prob += 0.05;\n\n // External signals (D18-ready)\n for (const signal of signals) {\n if (signal.slug === deal.slug) {\n if (signal.impact === \"positive\") prob += 0.05 * signal.magnitude;\n if (signal.impact === \"negative\") prob -= 0.1 * signal.magnitude;\n }\n }\n\n return Math.max(0.02, Math.min(0.98, prob));\n}\n\nexport function closeVarianceFn(\n deal: DealSnapshot,\n randFn: () => number,\n todayMs: number = Date.now()\n): number {\n const daysToClose = deal.closeDate\n ? Math.max(0, Math.floor((new Date(deal.closeDate).getTime() - todayMs) / 86_400_000))\n : 90;\n const variance = daysToClose < 30 ? 0.05 : 0.15;\n return 1 + (randFn() - 0.5) * 2 * variance;\n}\n\nexport function buildSensitivityMap(\n deals: DealSnapshot[],\n signals: ExternalSignal[]\n): Record<string, number> {\n const map: Record<string, number> = {};\n for (const deal of deals) {\n map[deal.name] = Math.round(deal.value * adjustProbability(deal, signals));\n }\n return map;\n}\n\nexport function buildTopRisks(\n deals: DealSnapshot[],\n signals: ExternalSignal[],\n sensitivityMap: Record<string, number>\n): string[] {\n const atRisk = deals.filter((d) => d.healthScore < 60 || d.daysSinceContact > 14);\n return atRisk\n .sort((a, b) => (sensitivityMap[b.name] ?? 0) - (sensitivityMap[a.name] ?? 0))\n .slice(0, 5)\n .map((d) => {\n const reasons: string[] = [];\n if (d.healthScore < 60) reasons.push(`health ${d.healthScore}`);\n if (d.daysSinceContact > 14) reasons.push(`${d.daysSinceContact}d no contact`);\n if (!d.championPresent) reasons.push(\"no champion\");\n return `${d.slug}/${d.name}: ${reasons.join(\", \")} — €${d.value} at risk`;\n });\n}\n\n// ─── Monte Carlo Core ─────────────────────────────────────────────────────────\n\nconst MAX_ITERATIONS = 50_000;\n\nexport function runSimulation(\n input: SimulationInput,\n randFn: () => number = Math.random\n): SimulationResult {\n const { deals, externalSignals } = input;\n const iterations = Math.min(input.iterations, MAX_ITERATIONS);\n\n if (deals.length === 0) {\n return {\n p10: 0,\n p50: 0,\n p90: 0,\n expected: 0,\n stdDev: 0,\n atRiskRevenue: 0,\n byCloseMonth: {},\n topRisks: [],\n sensitivityMap: {},\n };\n }\n\n const todayMs = new Date(input.today).getTime();\n const adjustedProbs = deals.map((d) => adjustProbability(d, externalSignals));\n const outcomes: number[] = [];\n const byMonthOutcomes: Record<string, number[]> = {};\n\n for (let i = 0; i < iterations; i++) {\n let total = 0;\n const monthTotals: Record<string, number> = {};\n\n for (let j = 0; j < deals.length; j++) {\n const deal = deals[j]!;\n const prob = adjustedProbs[j]!;\n if (randFn() < prob) {\n const closedValue = Math.round(deal.value * closeVarianceFn(deal, randFn, todayMs));\n total += closedValue;\n if (deal.closeDate) {\n const month = deal.closeDate.slice(0, 7);\n monthTotals[month] = (monthTotals[month] ?? 0) + closedValue;\n }\n }\n }\n\n outcomes.push(total);\n // Winning-only: only record months where at least one deal closed in this iteration\n for (const [month, val] of Object.entries(monthTotals)) {\n if (val > 0) {\n byMonthOutcomes[month] ??= [];\n byMonthOutcomes[month]!.push(val);\n }\n }\n }\n\n outcomes.sort((a, b) => a - b);\n const exp = Math.round(mean(outcomes));\n const sd = Math.round(stdDevFn(outcomes, exp));\n\n const byCloseMonth: Record<string, MonthForecast> = {};\n for (const [month, vals] of Object.entries(byMonthOutcomes)) {\n const sorted = [...vals].sort((a, b) => a - b);\n byCloseMonth[month] = {\n p50: Math.round(percentile(sorted, 50)),\n range: [Math.round(percentile(sorted, 10)), Math.round(percentile(sorted, 90))],\n };\n }\n\n const sensitivityMap = buildSensitivityMap(deals, externalSignals);\n const topRisks = buildTopRisks(deals, externalSignals, sensitivityMap);\n const atRiskRevenue = deals.filter((d) => d.healthScore < 60).reduce((s, d) => s + d.value, 0);\n\n return {\n p10: Math.round(percentile(outcomes, 10)),\n p50: Math.round(percentile(outcomes, 50)),\n p90: Math.round(percentile(outcomes, 90)),\n expected: exp,\n stdDev: sd,\n atRiskRevenue,\n byCloseMonth,\n topRisks,\n sensitivityMap,\n };\n}\n\n// ─── Confidence message ───────────────────────────────────────────────────────\n\nexport function buildConfidenceMessage(result: SimulationResult, dealCount: number): string {\n const range = result.p90 - result.p10;\n const atRiskPct =\n result.expected > 0 ? Math.round((result.atRiskRevenue / result.expected) * 100) : 0;\n return `P50 forecast: €${(result.p50 / 1000).toFixed(1)}k with ±€${(range / 2 / 1000).toFixed(1)}k uncertainty (P10–P90 range). ${atRiskPct}% of pipeline is at risk. ${dealCount} deals simulated.`;\n}\n\n// ─── Quarter helper ───────────────────────────────────────────────────────────\n\nfunction getQuarterEnd(date: Date): Date {\n const month = date.getMonth();\n const quarterEndMonth = Math.floor(month / 3) * 3 + 2;\n return new Date(date.getFullYear(), quarterEndMonth + 1, 0);\n}\n\n// ─── Data aggregation ─────────────────────────────────────────────────────────\n\nexport async function buildSimulationInput(\n dataDir: string,\n horizon: \"quarter\" | \"year\",\n today: string,\n externalSignals: ExternalSignal[] = []\n): Promise<SimulationInput> {\n const slugs = listCustomerSlugs(dataDir);\n if (slugs.length === 0) {\n return { deals: [], externalSignals, iterations: 10_000, horizon, today };\n }\n\n const stages = getPipelineStages(dataDir);\n const stageProb: Record<string, number> = {};\n for (const s of stages) {\n stageProb[s.id] = s.probability ?? 50;\n }\n\n const deals: DealSnapshot[] = [];\n const todayDate = new Date(today);\n const horizonEnd =\n horizon === \"quarter\" ? getQuarterEnd(todayDate) : new Date(todayDate.getFullYear(), 11, 31);\n\n for (const slug of slugs) {\n const pipelineDeals = await readPipeline(dataDir, slug).catch(() => []);\n if (pipelineDeals.length === 0) continue;\n\n const health = readHealth(dataDir, slug) ?? computeCustomerHealth(dataDir, slug, today);\n const healthScore = health.overallHealth;\n\n const graph = readGraph(dataDir, slug);\n const stakeholders = getStakeholders(graph);\n const championPresent = stakeholders.champions.length > 0;\n\n const lastContact = health.contacts\n .map((c) => c.lastContact)\n .filter((lc): lc is string => !!lc)\n .sort()\n .pop();\n const daysSinceContact = lastContact\n ? Math.floor((todayDate.getTime() - new Date(lastContact).getTime()) / 86_400_000)\n : 999;\n\n for (const deal of pipelineDeals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n const closeDate = new Date(deal.close_date);\n if (closeDate > horizonEnd) continue;\n }\n\n const probability = deal.probability ?? stageProb[deal.stage] ?? 50;\n const snapshot: DealSnapshot = {\n slug,\n name: deal.name,\n stage: deal.stage,\n value: deal.value ?? 0,\n probability,\n healthScore,\n daysSinceContact,\n championPresent,\n };\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n snapshot.closeDate = deal.close_date;\n }\n deals.push(snapshot);\n }\n }\n\n return { deals, externalSignals, iterations: 10_000, horizon, today };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n buildSimulationInput,\n runSimulation,\n buildConfidenceMessage,\n} from \"../../core/revenue-simulation.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSimulateRevenue(\n input: { horizon?: \"quarter\" | \"year\"; iterations?: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const horizon = input.horizon ?? \"quarter\";\n\n const simInput = await buildSimulationInput(dataDir, horizon, today);\n if (input.iterations !== undefined) simInput.iterations = input.iterations;\n\n const result = runSimulation(simInput);\n const confidence = buildConfidenceMessage(result, simInput.deals.length);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n forecast: result,\n confidence,\n dealCount: simInput.deals.length,\n horizon,\n simulatedAt: new Date().toISOString(),\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerSimulateRevenue(server: McpServer): void {\n server.registerTool(\n \"simulate_revenue\",\n {\n title: \"Simulate Revenue\",\n description: `Monte Carlo pipeline revenue simulation with P10/P50/P90 confidence intervals.\n\nAdjusts deal win probabilities using relationship health scores (D12) and\nchampion presence (D11). Returns the range of possible quarterly/annual outcomes.\n\nUse this instead of (or alongside) get_pipeline_forecast when you need:\n- Uncertainty quantification (not just expected value)\n- At-risk revenue identification\n- Deal sensitivity analysis (\"which deal matters most\")\n- Month-by-month close distribution\n\nArgs:\n horizon: \"quarter\" (default) | \"year\"\n iterations: simulation iterations (default: 10000)\n\nReturns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMonth, topRisks, sensitivityMap }, confidence, dealCount, horizon }`,\n inputSchema: z.object({\n horizon: z\n .enum([\"quarter\", \"year\"])\n .optional()\n .describe('Forecast horizon (default: \"quarter\")'),\n iterations: z.number().optional().describe(\"Monte Carlo iterations (default: 10000)\"),\n }),\n },\n async ({ horizon, iterations }) =>\n handleSimulateRevenue({\n ...(horizon !== undefined ? { horizon } : {}),\n ...(iterations !== undefined ? { iterations } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listPlaybooks, matchPlaybooks } from \"../../core/playbooks.js\";\nimport type { DealSnapshot } from \"../../core/revenue-simulation.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetPlaybook(\n input: {\n slug: string;\n stage?: string;\n value?: number;\n healthScore?: number;\n daysSinceContact?: number;\n championPresent?: boolean;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const playbooks = listPlaybooks(dataDir, input.slug);\n\n const hasDealContext =\n input.stage !== undefined || input.value !== undefined || input.healthScore !== undefined;\n\n if (!hasDealContext) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n matches: playbooks.map((pb) => ({\n name: pb.name,\n trigger: pb.frontmatter.trigger,\n successRate: pb.frontmatter.successRate,\n usedCount: pb.frontmatter.usedCount,\n lastUpdated: pb.frontmatter.lastUpdated,\n content: pb.content,\n })),\n totalPlaybooks: playbooks.length,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const mockDeal: DealSnapshot = {\n slug: input.slug,\n name: \"\",\n stage: input.stage ?? \"lead\",\n value: input.value ?? 0,\n probability: 50,\n healthScore: input.healthScore ?? 60,\n daysSinceContact: input.daysSinceContact ?? 0,\n championPresent: input.championPresent ?? false,\n };\n\n const matches = matchPlaybooks(playbooks, mockDeal, input.daysSinceContact ?? 0);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n matches: matches.map((m) => ({\n name: m.playbook.name,\n score: m.score,\n matchedConditions: m.matchedConditions,\n trigger: m.playbook.frontmatter.trigger,\n successRate: m.playbook.frontmatter.successRate,\n usedCount: m.playbook.frontmatter.usedCount,\n content: m.playbook.content,\n })),\n totalPlaybooks: playbooks.length,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetPlaybook(server: McpServer): void {\n server.registerTool(\n \"get_playbook\",\n {\n title: \"Get Playbook\",\n description: `Retrieve playbooks for a customer. With deal context, returns only matching playbooks sorted by success rate. Without deal context, returns all playbooks.\n\nUse after run_deal_agent or before a sales call to get proven guidance for the current situation.\n\nArgs:\n slug: Customer ID (required)\n stage: Deal stage (optional — enables trigger matching)\n value: Deal value in euros (optional)\n healthScore: Relationship health score 0–100 (optional)\n daysSinceContact: Days since last contact / days_stalled proxy (optional)\n championPresent: Whether a champion is identified (optional)\n\nReturns: { matches: [{ name, score, trigger, successRate, usedCount, content }], totalPlaybooks, slug }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n stage: z.string().optional().describe(\"Deal stage\"),\n value: z.number().optional().describe(\"Deal value in euros\"),\n healthScore: z.number().optional().describe(\"Health score 0–100\"),\n daysSinceContact: z.number().optional().describe(\"Days since last contact\"),\n championPresent: z.boolean().optional().describe(\"Champion identified\"),\n }),\n },\n async ({ slug, stage, value, healthScore, daysSinceContact, championPresent }) =>\n handleGetPlaybook(\n {\n slug,\n ...(stage !== undefined ? { stage } : {}),\n ...(value !== undefined ? { value } : {}),\n ...(healthScore !== undefined ? { healthScore } : {}),\n ...(daysSinceContact !== undefined ? { daysSinceContact } : {}),\n ...(championPresent !== undefined ? { championPresent } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { writePlaybook, playbooksDir, toKebabCase } from \"../../core/playbooks.js\";\nimport path from \"path\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCreatePlaybook(\n input: {\n slug: string;\n name: string;\n trigger: string;\n content: string;\n successRate?: number;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const name = toKebabCase(input.name);\n const today = new Date().toISOString().slice(0, 10);\n const filePath = path.join(playbooksDir(dataDir, input.slug), `${name}.md`);\n\n const playbook = {\n slug: input.slug,\n name,\n frontmatter: {\n trigger: input.trigger,\n successRate: input.successRate ?? 0.5,\n usedCount: 0,\n lastUpdated: today,\n },\n content: input.content,\n path: filePath,\n };\n\n await writePlaybook(dataDir, input.slug, playbook);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n playbook: {\n name,\n trigger: input.trigger,\n successRate: playbook.frontmatter.successRate,\n usedCount: 0,\n lastUpdated: today,\n path: filePath,\n },\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerCreatePlaybook(server: McpServer): void {\n server.registerTool(\n \"create_playbook\",\n {\n title: \"Create Playbook\",\n description: `Create or update a playbook for a customer. Playbooks encode proven tactics for specific deal situations.\n\nTrigger DSL uses AND-only conditions:\n deal_stage_<stage> | value > N | value < N | days_stalled > N | health < N | health > N | no_champion | has_champion\n\nExample: \"deal_stage_negotiation AND value > 50000 AND days_stalled > 7\"\n\nArgs:\n slug: Customer ID\n name: Playbook name (auto-converted to kebab-case)\n trigger: Trigger DSL string (conditions separated by AND)\n content: Playbook markdown body (## Situation, ## Steps, ## Warnings, ## Templates)\n successRate: Historical win rate 0.0–1.0 (default: 0.5)\n\nReturns: { success: true, playbook: { name, trigger, successRate, path } }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n name: z.string().describe(\"Playbook name\"),\n trigger: z.string().describe(\"Trigger DSL string\"),\n content: z.string().describe(\"Playbook markdown body\"),\n successRate: z.number().min(0).max(1).optional().describe(\"Historical win rate 0.0–1.0\"),\n }),\n },\n async ({ slug, name, trigger, content, successRate }) =>\n handleCreatePlaybook(\n {\n slug,\n name,\n trigger,\n content,\n ...(successRate !== undefined ? { successRate } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listPlaybooks } from \"../../core/playbooks.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListPlaybooks(\n input: { slug: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const playbooks = listPlaybooks(dataDir, input.slug);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n playbooks: playbooks.map((pb) => ({\n name: pb.name,\n trigger: pb.frontmatter.trigger,\n successRate: pb.frontmatter.successRate,\n usedCount: pb.frontmatter.usedCount,\n lastUpdated: pb.frontmatter.lastUpdated,\n })),\n count: playbooks.length,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerListPlaybooks(server: McpServer): void {\n server.registerTool(\n \"list_playbooks\",\n {\n title: \"List Playbooks\",\n description: `List all playbooks for a customer (metadata only, no body content).\n\nUse to discover available playbooks before calling get_playbook with deal context.\n\nArgs:\n slug: Customer ID\n\nReturns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n }),\n },\n async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { distillPlaybook } from \"../../core/playbooks.js\";\nimport { callLlm } from \"../../core/llm.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleDistillPlaybook(\n input: { slug: string; dealName: string; outcome: \"won\" | \"lost\" },\n dataDir: string = DATA_DIR,\n llmFn: (prompt: string) => Promise<string> = callLlm\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);\n\n if (!result.ok) {\n const error =\n result.errorKind === \"no_interactions\"\n ? `No interactions.md found for ${input.slug}`\n : \"LLM response could not be parsed as playbook\";\n return {\n content: [{ type: \"text\", text: JSON.stringify({ success: false, error }, null, 2) }],\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n playbook: {\n name: result.playbook.name,\n trigger: result.playbook.frontmatter.trigger,\n successRate: result.playbook.frontmatter.successRate,\n usedCount: result.playbook.frontmatter.usedCount,\n path: result.playbook.path,\n },\n reasoning: result.reasoning,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerDistillPlaybook(server: McpServer): void {\n server.registerTool(\n \"distill_playbook\",\n {\n title: \"Distill Playbook\",\n description: `Use LLM to extract a reusable playbook from a won or lost deal's interaction history. Analyzes the customer's interactions.md and identifies the winning/losing pattern as a structured playbook.\n\nRun after every won or lost deal to build your procedural memory library.\n\nArgs:\n slug: Customer ID\n dealName: Name of the deal to analyze\n outcome: \"won\" or \"lost\"\n\nReturns: { success: true, playbook: { name, trigger, successRate, path }, reasoning }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer ID\"),\n dealName: z.string().describe(\"Deal name to analyze\"),\n outcome: z.enum([\"won\", \"lost\"]).describe(\"Deal outcome\"),\n }),\n },\n async ({ slug, dealName, outcome }) =>\n handleDistillPlaybook({ slug, dealName, outcome }, DATA_DIR)\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { runSimulation } from \"./revenue-simulation.js\";\nimport { callLlm } from \"./llm.js\";\nimport { getActor } from \"../fs/audit-log.js\";\nimport { withJsonFile } from \"./file-lock.js\";\nimport { guardIsoDate } from \"./input-guard.js\";\nimport type { DealSnapshot, SimulationInput } from \"./revenue-simulation.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type GoalMetric = \"revenue\" | \"deals_closed\" | \"meetings_booked\" | \"pipeline_created\";\nexport type GoalType = \"revenue\" | \"pipeline\" | \"relationship\" | \"churn_prevention\";\nexport type GoalStatus = \"active\" | \"completed\" | \"cancelled\" | \"blocked\";\n\nexport interface GoalSubGoal {\n priority: number;\n action: string;\n slug: string;\n dealName?: string;\n why: string;\n nextStep: string;\n targetDelta: number;\n playbookName?: string;\n}\n\nexport interface GoalDecomposition {\n analysis: string;\n currentPipeline: number;\n gap: number;\n subGoals: GoalSubGoal[];\n probabilisticOutcome: string;\n decomposedAt: string;\n}\n\nexport interface Goal {\n id: string;\n description: string;\n type: GoalType;\n target: number;\n metric: GoalMetric;\n deadline: string;\n decomposition: GoalDecomposition;\n progress: number;\n status: GoalStatus;\n createdAt: string;\n updatedAt: string;\n actor: string;\n}\n\nexport type BuildInputFn = (\n dataDir: string,\n horizon: \"quarter\" | \"year\",\n today: string\n) => Promise<SimulationInput>;\n\n// ─── Persistence ──────────────────────────────────────────────────────────────\n\nexport function goalsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"goals.json\");\n}\n\nexport function readGoals(dataDir: string): Goal[] {\n const p = goalsPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n if (Array.isArray(raw)) return raw as Goal[];\n return (raw as { goals?: Goal[] }).goals ?? [];\n } catch {\n return [];\n }\n}\n\nexport function writeGoals(dataDir: string, goals: Goal[]): void {\n writeJsonFile(goalsPath(dataDir), { goals, updatedAt: new Date().toISOString() });\n}\n\nexport function makeGoalId(): string {\n return `goal_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;\n}\n\n// ─── Parsing ──────────────────────────────────────────────────────────────────\n\nexport function parseTargetFromDescription(desc: string): number {\n // Try millions first: \"1.5M\", \"1.5 million\", \"$1.5M\"\n const millionMatch = desc.match(/[\\$€£]?\\s*(\\d+(?:\\.\\d+)?)\\s*(?:M\\b|million)/i);\n if (millionMatch) return Math.round(parseFloat(millionMatch[1]!) * 1_000_000);\n\n // Then thousands: \"500k\", \"€500k\"\n const kMatch = desc.match(/[\\$€£]?\\s*(\\d+(?:\\.\\d+)?)\\s*k\\b/i);\n if (kMatch) return Math.round(parseFloat(kMatch[1]!) * 1_000);\n\n // Then raw numbers with optional currency: \"€75000\"\n const rawMatch = desc.match(/[\\$€£]\\s*(\\d{4,}(?:[,.\\d]*\\d)?)/);\n if (rawMatch) return parseInt(rawMatch[1]!.replace(/[,. ]/g, \"\"), 10);\n\n return 0;\n}\n\nexport function inferGoalType(desc: string): GoalType {\n const lower = desc.toLowerCase();\n if (/churn|retain|renewal|renew/.test(lower)) return \"churn_prevention\";\n if (/meeting|call|book|relationship|contact/.test(lower)) return \"relationship\";\n if (/pipeline|prospect|lead|qualify/.test(lower)) return \"pipeline\";\n return \"revenue\";\n}\n\nexport function inferMetric(type: GoalType): GoalMetric {\n switch (type) {\n case \"pipeline\":\n return \"pipeline_created\";\n case \"relationship\":\n return \"meetings_booked\";\n case \"revenue\":\n return \"revenue\";\n case \"churn_prevention\":\n return \"revenue\";\n }\n}\n\n// ─── Rule-based decomposition ─────────────────────────────────────────────────\n\nexport function rankDealsByLeverage(deals: DealSnapshot[]): DealSnapshot[] {\n return deals\n .filter((d) => d.stage !== \"won\" && d.stage !== \"lost\")\n .sort((a, b) => {\n const leverageA = a.value * (a.probability / 100) * (a.healthScore / 100);\n const leverageB = b.value * (b.probability / 100) * (b.healthScore / 100);\n return leverageB - leverageA;\n });\n}\n\nfunction generateNextStep(deal: DealSnapshot): string {\n if (deal.healthScore < 40 && !deal.championPresent)\n return \"Re-engage urgently and identify a champion\";\n if (deal.healthScore < 60) return \"Schedule an urgent check-in call\";\n if (deal.daysSinceContact > 14) return \"Reach out — contact is overdue\";\n if (!deal.championPresent) return \"Identify a champion or economic buyer\";\n return \"Push to next pipeline stage\";\n}\n\nexport function decomposeGoalRuleBased(\n deals: DealSnapshot[],\n target: number,\n currentP50: number,\n today: string,\n playbookLookup?: (slug: string, deal: DealSnapshot) => string | undefined\n): GoalDecomposition {\n const gap = Math.max(0, target - currentP50);\n const decomposedAt = new Date(today + \"T00:00:00Z\").toISOString();\n\n if (gap === 0) {\n return {\n analysis: `Current pipeline (P50: €${currentP50.toLocaleString()}) already meets or exceeds the target of €${target.toLocaleString()}.`,\n currentPipeline: currentP50,\n gap: 0,\n subGoals: [],\n probabilisticOutcome: `Pipeline P50 (€${currentP50.toLocaleString()}) ≥ target (€${target.toLocaleString()}).`,\n decomposedAt,\n };\n }\n\n const ranked = rankDealsByLeverage(deals);\n\n if (ranked.length === 0) {\n return {\n analysis: `No active deals found. Gap to close: €${gap.toLocaleString()}. Focus on building pipeline.`,\n currentPipeline: currentP50,\n gap,\n subGoals: [\n {\n priority: 1,\n action: \"Build pipeline from scratch\",\n slug: \"_all\",\n why: `No active deals. Need €${gap.toLocaleString()} to reach target.`,\n nextStep:\n \"Use list_customers() to find prospects and log_interaction to initiate outreach\",\n targetDelta: target,\n },\n ],\n probabilisticOutcome: `Insufficient pipeline. Need €${gap.toLocaleString()} in new deals.`,\n decomposedAt,\n };\n }\n\n const subGoals: GoalSubGoal[] = [];\n let cumulative = 0;\n\n for (const deal of ranked.slice(0, 5)) {\n if (subGoals.length >= 5) break;\n const playbookName = playbookLookup?.(deal.slug, deal);\n const subGoal: GoalSubGoal = {\n priority: subGoals.length + 1,\n action: `Accelerate ${deal.slug}/${deal.name}`,\n slug: deal.slug,\n ...(deal.name ? { dealName: deal.name } : {}),\n why: `€${deal.value.toLocaleString()} deal in ${deal.stage} — health ${deal.healthScore}/100`,\n nextStep: generateNextStep(deal),\n targetDelta: deal.value,\n ...(playbookName ? { playbookName } : {}),\n };\n subGoals.push(subGoal);\n cumulative += deal.value;\n if (cumulative >= gap) break;\n }\n\n const projectedTotal = currentP50 + cumulative;\n return {\n analysis: `Current pipeline P50: €${currentP50.toLocaleString()}. Gap to target: €${gap.toLocaleString()}. Top ${subGoals.length} deal(s) identified.`,\n currentPipeline: currentP50,\n gap,\n subGoals,\n probabilisticOutcome: `If all recommended deals close: ~€${projectedTotal.toLocaleString()} (target: €${target.toLocaleString()}).`,\n decomposedAt,\n };\n}\n\n// ─── LLM path ─────────────────────────────────────────────────────────────────\n\nexport function buildDecompositionPrompt(\n description: string,\n target: number,\n deadline: string,\n currentP50: number,\n deals: DealSnapshot[],\n today: string\n): string {\n const gap = Math.max(0, target - currentP50);\n const dealLines = deals\n .filter((d) => d.stage !== \"won\" && d.stage !== \"lost\")\n .slice(0, 8)\n .map(\n (d, i) =>\n `${i + 1}. ${d.slug}/${d.name} — €${d.value.toLocaleString()}, stage: ${d.stage}, health: ${d.healthScore}/100, probability: ${d.probability}%${d.championPresent ? \", champion ✓\" : \"\"}`\n )\n .join(\"\\n\");\n\n return `You are a sales strategy AI helping decompose a revenue goal into actionable sub-goals.\n\nGoal: ${description}\nTarget: €${target.toLocaleString()}\nDeadline: ${deadline}\nCurrent date: ${today}\nCurrent weighted pipeline (P50): €${currentP50.toLocaleString()}\nGap to close: €${gap.toLocaleString()}\n\nActive deals (sorted by weighted value):\n${dealLines || \"(no active deals)\"}\n\nReturn JSON only (no markdown wrapper):\n{\n \"analysis\": \"<1-2 sentence summary of the situation>\",\n \"subGoals\": [\n {\n \"priority\": 1,\n \"action\": \"<what to do>\",\n \"slug\": \"<customer-slug>\",\n \"dealName\": \"<deal name>\",\n \"why\": \"<why this deal matters for the goal>\",\n \"nextStep\": \"<concrete next action with deadline>\",\n \"targetDelta\": <expected revenue contribution in euros>\n }\n ],\n \"probabilisticOutcome\": \"<P50 forecast summary after actions>\"\n}`;\n}\n\nexport function parseLlmDecomposition(\n response: string,\n fallback: GoalDecomposition\n): GoalDecomposition {\n try {\n const match = response.match(/\\{[\\s\\S]*\\}/);\n if (!match) return fallback;\n const parsed = JSON.parse(match[0]) as Partial<{\n analysis: string;\n subGoals: unknown[];\n probabilisticOutcome: string;\n }>;\n if (!parsed.analysis || !Array.isArray(parsed.subGoals)) return fallback;\n return {\n analysis: parsed.analysis,\n currentPipeline: fallback.currentPipeline,\n gap: fallback.gap,\n subGoals: (parsed.subGoals as Partial<GoalSubGoal>[]).map((s, i) => ({\n priority: s.priority ?? i + 1,\n action: s.action ?? \"\",\n slug: s.slug ?? \"_all\",\n ...(s.dealName ? { dealName: s.dealName } : {}),\n why: s.why ?? \"\",\n nextStep: s.nextStep ?? \"\",\n targetDelta: s.targetDelta ?? 0,\n ...(s.playbookName ? { playbookName: s.playbookName } : {}),\n })),\n probabilisticOutcome: parsed.probabilisticOutcome ?? fallback.probabilisticOutcome,\n decomposedAt: fallback.decomposedAt,\n };\n } catch {\n return fallback;\n }\n}\n\n// ─── pursueGoal ───────────────────────────────────────────────────────────────\n\nexport async function pursueGoal(\n dataDir: string,\n input: { description: string; deadline: string; context?: string },\n options: {\n llmFn?: (prompt: string) => Promise<string>;\n buildInputFn?: BuildInputFn;\n today?: string;\n actor?: string;\n } = {}\n): Promise<Goal> {\n guardIsoDate(input.deadline, \"deadline\");\n const today = options.today ?? new Date().toISOString().slice(0, 10);\n const actor = options.actor ?? getActor();\n\n const buildFn =\n options.buildInputFn ??\n ((async (dir, horizon, t) => {\n const { buildSimulationInput } = await import(\"./revenue-simulation.js\");\n return buildSimulationInput(dir, horizon, t);\n }) as BuildInputFn);\n\n const simInput = await buildFn(dataDir, \"quarter\", today);\n const simResult = runSimulation(simInput);\n const currentP50 = simResult.p50;\n const deals = simInput.deals;\n\n const target = parseTargetFromDescription(input.description);\n const type = inferGoalType(input.description);\n const metric = inferMetric(type);\n\n const ruleBasedDecomp = decomposeGoalRuleBased(deals, target, currentP50, today);\n\n let decomposition = ruleBasedDecomp;\n const llmFn = options.llmFn ?? callLlm;\n if (options.llmFn !== undefined) {\n const prompt = buildDecompositionPrompt(\n input.description,\n target,\n input.deadline,\n currentP50,\n deals,\n today\n );\n const response = await llmFn(prompt);\n decomposition = parseLlmDecomposition(response, ruleBasedDecomp);\n }\n\n const now = new Date().toISOString();\n const goal: Goal = {\n id: makeGoalId(),\n description: input.description,\n type,\n target,\n metric,\n deadline: input.deadline,\n decomposition,\n progress: 0,\n status: \"active\",\n createdAt: now,\n updatedAt: now,\n actor,\n };\n\n await withJsonFile<{ goals: Goal[]; updatedAt: string }>(goalsPath(dataDir), (current) => {\n const existing: Goal[] = Array.isArray(current?.goals) ? current.goals : [];\n return { goals: [...existing, goal], updatedAt: new Date().toISOString() };\n });\n return goal;\n}\n\n// ─── Goal management ──────────────────────────────────────────────────────────\n\nexport function getActiveGoals(dataDir: string): Goal[] {\n return readGoals(dataDir).filter((g) => g.status === \"active\");\n}\n\nexport async function updateGoalProgress(\n dataDir: string,\n goalId: string,\n progress: number\n): Promise<Goal | null> {\n let updated: Goal | null = null;\n await withJsonFile<{ goals: Goal[]; updatedAt: string }>(goalsPath(dataDir), (current) => {\n const goals: Goal[] = Array.isArray(current?.goals) ? [...current.goals] : [];\n const idx = goals.findIndex((g) => g.id === goalId);\n if (idx >= 0) {\n updated = { ...goals[idx]!, progress, updatedAt: new Date().toISOString() };\n goals[idx] = updated;\n }\n return { goals, updatedAt: new Date().toISOString() };\n });\n return updated;\n}\n\nexport async function cancelGoal(dataDir: string, goalId: string): Promise<Goal | null> {\n let cancelled: Goal | null = null;\n await withJsonFile<{ goals: Goal[]; updatedAt: string }>(goalsPath(dataDir), (current) => {\n const goals: Goal[] = Array.isArray(current?.goals) ? [...current.goals] : [];\n const idx = goals.findIndex((g) => g.id === goalId);\n if (idx >= 0) {\n cancelled = {\n ...goals[idx]!,\n status: \"cancelled\" as const,\n updatedAt: new Date().toISOString(),\n };\n goals[idx] = cancelled;\n }\n return { goals, updatedAt: new Date().toISOString() };\n });\n return cancelled;\n}\n\n// ─── Pipeline-driven progress sync ────────────────────────────────────────────\n\nexport interface SyncResult {\n updated: string[];\n skipped: number;\n}\n\nexport async function syncGoalProgressFromPipeline(\n dataDir: string,\n _today?: string\n): Promise<SyncResult> {\n const activeGoals = getActiveGoals(dataDir);\n const revenueGoals = activeGoals.filter((g) => g.metric === \"revenue\" && g.target > 0);\n\n if (revenueGoals.length === 0) return { updated: [], skipped: activeGoals.length };\n\n let totalWon = 0;\n for (const slug of listCustomerSlugs(dataDir)) {\n const deals = await readPipeline(dataDir, slug).catch(() => []);\n for (const deal of deals) {\n if (deal.stage === \"won\") totalWon += deal.value ?? 0;\n }\n }\n\n const updated: string[] = [];\n for (const goal of revenueGoals) {\n const progress = Math.min(100, Math.round((totalWon / goal.target) * 100));\n const result = await updateGoalProgress(dataDir, goal.id, progress);\n if (result) updated.push(goal.id);\n }\n\n return { updated, skipped: activeGoals.length - revenueGoals.length };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { pursueGoal, type BuildInputFn } from \"../../core/goal-engine.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport { getActor } from \"../../fs/audit-log.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handlePursueGoal(\n input: { goal: string; deadline: string; context?: string },\n dataDir: string = DATA_DIR,\n options: { buildInputFn?: BuildInputFn; llmFn?: (p: string) => Promise<string> } = {}\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n enforceRbac(dataDir, \"pursue_goal\");\n\n const goal = await pursueGoal(\n dataDir,\n {\n description: input.goal,\n deadline: input.deadline,\n ...(input.context ? { context: input.context } : {}),\n },\n {\n actor: getActor(),\n ...(options.buildInputFn ? { buildInputFn: options.buildInputFn } : {}),\n ...(options.llmFn ? { llmFn: options.llmFn } : {}),\n }\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n goalId: goal.id,\n description: goal.description,\n target: goal.target,\n deadline: goal.deadline,\n type: goal.type,\n decomposition: {\n analysis: goal.decomposition.analysis,\n currentPipeline: goal.decomposition.currentPipeline,\n gap: goal.decomposition.gap,\n subGoals: goal.decomposition.subGoals,\n probabilisticOutcome: goal.decomposition.probabilisticOutcome,\n },\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerPursueGoal(server: McpServer): void {\n server.registerTool(\n \"pursue_goal\",\n {\n title: \"Pursue Goal\",\n description: `Set a revenue or pipeline goal and get an AI-decomposed action plan.\n\nAnalyzes current pipeline (P50 forecast) and decomposes the gap into prioritized sub-goals per deal. Persists the goal in .agentic/goals.json for tracking.\n\nRBAC: manager+\n\nArgs:\n goal: Natural language goal description (e.g. \"Close €500k ARR this quarter\")\n deadline: Target deadline (YYYY-MM-DD)\n context: Optional constraints (e.g. \"Focus on existing pipeline only\")\n\nReturns: { goalId, description, target, deadline, decomposition: { analysis, currentPipeline, gap, subGoals, probabilisticOutcome } }`,\n inputSchema: z.object({\n goal: z.string().describe(\"Natural language goal (e.g. 'Close €500k ARR this quarter')\"),\n deadline: z.string().describe(\"Target deadline YYYY-MM-DD\"),\n context: z.string().optional().describe(\"Optional constraints or focus areas\"),\n }),\n },\n async ({ goal, deadline, context }) =>\n handlePursueGoal({ goal, deadline, ...(context !== undefined ? { context } : {}) }, DATA_DIR)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readGoals, getActiveGoals } from \"../../core/goal-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetGoalStatus(\n input: { goalId?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const allGoals = input.goalId\n ? readGoals(dataDir).filter((g) => g.id === input.goalId)\n : getActiveGoals(dataDir);\n\n if (input.goalId && allGoals.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: false, error: `Goal '${input.goalId}' not found` },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n const goals = allGoals.map((g) => {\n const deadlineMs = new Date(g.deadline).getTime();\n const todayMs = new Date(today).getTime();\n const daysRemaining = Math.max(0, Math.ceil((deadlineMs - todayMs) / 86_400_000));\n return {\n id: g.id,\n description: g.description,\n target: g.target,\n progress: g.progress,\n status: g.status,\n deadline: g.deadline,\n daysRemaining,\n subGoals: g.decomposition.subGoals.slice(0, 3),\n createdAt: g.createdAt,\n };\n });\n\n const active = allGoals.filter((g) => g.status === \"active\");\n const completed = allGoals.filter((g) => g.status === \"completed\");\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { goals, activeCount: active.length, completedCount: completed.length },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetGoalStatus(server: McpServer): void {\n server.registerTool(\n \"get_goal_status\",\n {\n title: \"Get Goal Status\",\n description: `Get the status of active goals. Without goalId, returns all active goals. With goalId, returns that specific goal.\n\nReturns progress, days remaining, and top sub-goals for each goal.\n\nArgs:\n goalId: (optional) Specific goal ID — if omitted, returns all active goals\n\nReturns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,\n inputSchema: z.object({\n goalId: z.string().optional().describe(\"Specific goal ID (omit for all active goals)\"),\n }),\n },\n async ({ goalId }) =>\n handleGetGoalStatus({ ...(goalId !== undefined ? { goalId } : {}) }, DATA_DIR)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { register, type PushProvider } from \"../../sync/push-manager.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst VALID_PROVIDERS: PushProvider[] = [\"gmail\", \"microsoft-graph\", \"slack\"];\n\nexport async function handleRegisterPushSubscription(\n input: {\n provider: PushProvider;\n slug: string;\n webhookUrl: string;\n gmailTopicName?: string;\n microsoftClientState?: string;\n microsoftResource?: string;\n slackTeamId?: string;\n slackChannelId?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n if (!VALID_PROVIDERS.includes(input.provider)) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n { success: false, error: `Unknown provider: ${input.provider}` },\n null,\n 2\n ),\n },\n ],\n };\n }\n\n enforceRbac(dataDir, \"register_push_subscription\");\n\n const providerData: Record<string, string> = {};\n if (input.gmailTopicName) providerData[\"gmailTopicName\"] = input.gmailTopicName;\n if (input.microsoftClientState)\n providerData[\"microsoftClientState\"] = input.microsoftClientState;\n if (input.microsoftResource) providerData[\"microsoftResource\"] = input.microsoftResource;\n if (input.slackTeamId) providerData[\"slackTeamId\"] = input.slackTeamId;\n if (input.slackChannelId) providerData[\"slackChannelId\"] = input.slackChannelId;\n\n const sub = await register(dataDir, input.provider, input.slug, {\n webhookUrl: input.webhookUrl,\n providerData,\n });\n\n const warning = input.webhookUrl.includes(\"localhost\")\n ? \"Warning: webhookUrl contains 'localhost' — providers cannot reach local endpoints. Use a tunnel (e.g. ngrok http 3847) for development.\"\n : undefined;\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n subscriptionId: sub.id,\n provider: sub.provider,\n slug: sub.slug,\n status: sub.status,\n expiresAt: sub.expiresAt,\n createdAt: sub.createdAt,\n ...(warning ? { warning } : {}),\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerRegisterPushSubscription(server: McpServer): void {\n server.registerTool(\n \"register_push_subscription\",\n {\n title: \"Register Push Subscription\",\n description: `Register a real-time push subscription for a customer (Gmail Pub/Sub, MS Graph webhook, or Slack Events).\n\nInstead of polling every 30 minutes, the provider will push new events to dxcrm within seconds.\n\nRBAC: admin only\n\nArgs:\n provider: \"gmail\" | \"microsoft-graph\" | \"slack\"\n slug: Customer slug to receive events for\n webhookUrl: Public HTTPS URL where the provider will POST events (e.g. https://yourserver.com/webhooks/gmail)\n gmailTopicName: (Gmail only) Cloud Pub/Sub topic name (e.g. projects/my-project/topics/gmail-push)\n microsoftClientState: (MS Graph only) Secret for HMAC verification\n microsoftResource: (MS Graph only) Resource path (e.g. /me/mailFolders/Inbox/messages)\n slackTeamId: (Slack only) Workspace/team ID (e.g. T12345)\n slackChannelId: (Slack only) Optional specific channel to monitor\n\nReturns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning? }`,\n inputSchema: z.object({\n provider: z.enum([\"gmail\", \"microsoft-graph\", \"slack\"]).describe(\"Push provider\"),\n slug: z.string().describe(\"Customer slug\"),\n webhookUrl: z.string().describe(\"Public HTTPS URL for provider callbacks\"),\n gmailTopicName: z.string().optional().describe(\"Gmail: Cloud Pub/Sub topic name\"),\n microsoftClientState: z.string().optional().describe(\"MS Graph: secret for verification\"),\n microsoftResource: z.string().optional().describe(\"MS Graph: resource path\"),\n slackTeamId: z.string().optional().describe(\"Slack: workspace team ID\"),\n slackChannelId: z.string().optional().describe(\"Slack: optional channel ID\"),\n }),\n },\n async ({\n provider,\n slug,\n webhookUrl,\n gmailTopicName,\n microsoftClientState,\n microsoftResource,\n slackTeamId,\n slackChannelId,\n }) =>\n handleRegisterPushSubscription(\n {\n provider,\n slug,\n webhookUrl,\n ...(gmailTopicName !== undefined ? { gmailTopicName } : {}),\n ...(microsoftClientState !== undefined ? { microsoftClientState } : {}),\n ...(microsoftResource !== undefined ? { microsoftResource } : {}),\n ...(slackTeamId !== undefined ? { slackTeamId } : {}),\n ...(slackChannelId !== undefined ? { slackChannelId } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readSubscriptions, type PushProvider, type PushStatus } from \"../../sync/push-manager.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetPushStatus(\n input: { slug?: string; provider?: PushProvider },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n let subs = await readSubscriptions(dataDir);\n\n if (input.slug) subs = subs.filter((s) => s.slug === input.slug);\n if (input.provider) subs = subs.filter((s) => s.provider === input.provider);\n\n const now = Date.now();\n const RENEWAL_THRESHOLD_MS = 24 * 60 * 60 * 1000;\n\n const subscriptions = subs.map((s) => {\n const expiresInHours = s.expiresAt\n ? Math.round((new Date(s.expiresAt).getTime() - now) / (60 * 60 * 1000))\n : null;\n const needsRenewal =\n s.expiresAt !== null && new Date(s.expiresAt).getTime() - now < RENEWAL_THRESHOLD_MS;\n\n return {\n id: s.id,\n provider: s.provider,\n slug: s.slug,\n status: s.status,\n expiresAt: s.expiresAt,\n expiresInHours,\n needsRenewal,\n lastEventAt: s.lastEventAt,\n eventsProcessed: s.eventsProcessed,\n webhookUrl: s.webhookUrl,\n };\n });\n\n const countByStatus = (status: PushStatus) => subs.filter((s) => s.status === status).length;\n\n const summary = {\n total: subs.length,\n active: countByStatus(\"active\"),\n expiringSoon: subscriptions.filter((s) => s.needsRenewal && s.status === \"active\").length,\n expired: countByStatus(\"expired\"),\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ subscriptions, summary }, null, 2),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetPushStatus(server: McpServer): void {\n server.registerTool(\n \"get_push_status\",\n {\n title: \"Get Push Status\",\n description: `Get the status of all active push subscriptions (Gmail Pub/Sub, MS Graph, Slack Events).\n\nShows which customers have real-time push enabled, when subscriptions expire, and how many events have been processed.\n\nRBAC: any\n\nArgs:\n slug: (optional) Filter by customer slug\n provider: (optional) Filter by provider — \"gmail\" | \"microsoft-graph\" | \"slack\"\n\nReturns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHours, needsRenewal, lastEventAt, eventsProcessed }], summary: { total, active, expiringSoon, expired } }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n provider: z\n .enum([\"gmail\", \"microsoft-graph\", \"slack\"])\n .optional()\n .describe(\"Filter by provider\"),\n }),\n },\n async ({ slug, provider }) =>\n handleGetPushStatus(\n {\n ...(slug !== undefined ? { slug } : {}),\n ...(provider !== undefined ? { provider } : {}),\n },\n DATA_DIR\n )\n );\n}\n","import { readGraph, getStakeholders, type MissingRole } from \"./graph.js\";\nimport { readHealth } from \"./relationship-health.js\";\nimport type { ExternalSignal } from \"../sync/external-signals.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type StakeholderRole =\n | \"champion\"\n | \"economic_buyer\"\n | \"blocker\"\n | \"influencer\"\n | \"user\"\n | \"unknown\";\n\nexport interface StakeholderProfile {\n name: string;\n email?: string;\n role: StakeholderRole;\n healthScore: number;\n daysSinceContact: number;\n contactStrength: number;\n riskFlags: string[];\n}\n\nexport interface StakeholderMap {\n slug: string;\n dealName?: string;\n updatedAt: string;\n people: StakeholderProfile[];\n missingRoles: MissingRole[];\n riskAssessment: string;\n recommendation: string;\n}\n\n// ─── Core functions ───────────────────────────────────────────────────────────\n\nexport function buildStakeholderMap(\n dataDir: string,\n slug: string,\n today: string,\n dealName?: string\n): StakeholderMap {\n const graph = readGraph(dataDir, slug);\n const stakeholders = getStakeholders(graph);\n const health = readHealth(dataDir, slug);\n\n const champIds = new Set(stakeholders.champions.map((n) => n.id));\n const buyerIds = new Set(stakeholders.economicBuyers.map((n) => n.id));\n const blockerIds = new Set(stakeholders.blockers.map((n) => n.id));\n\n const healthByContactId = new Map((health?.contacts ?? []).map((c) => [c.contactId, c]));\n\n const personNodes = graph.nodes.filter((n) => n.type === \"person\");\n\n const people: StakeholderProfile[] = personNodes.map((node) => {\n const contactHealth = healthByContactId.get(node.id);\n\n let role: StakeholderRole = \"unknown\";\n if (champIds.has(node.id)) role = \"champion\";\n else if (buyerIds.has(node.id)) role = \"economic_buyer\";\n else if (blockerIds.has(node.id)) role = \"blocker\";\n\n const edges = graph.edges.filter((e) => e.from === node.id);\n const contactStrength =\n edges.length > 0 ? Math.round(Math.max(...edges.map((e) => e.weight)) * 100) / 100 : 0.5;\n\n const profile: StakeholderProfile = {\n name: node.label,\n role,\n healthScore: contactHealth?.score ?? 50,\n daysSinceContact: contactHealth?.daysSinceContact ?? 999,\n contactStrength,\n riskFlags: contactHealth?.riskFlags ?? [],\n };\n if (node.properties.email) {\n profile.email = node.properties.email as string;\n }\n return profile;\n });\n\n const riskAssessment = buildRiskAssessment(people, stakeholders.missingRoles, []);\n\n const recommendation = deriveRecommendation(people, stakeholders.missingRoles);\n\n return {\n slug,\n ...(dealName ? { dealName } : {}),\n updatedAt: new Date(`${today}T00:00:00Z`).toISOString(),\n people,\n missingRoles: stakeholders.missingRoles,\n riskAssessment,\n recommendation,\n };\n}\n\nexport function buildRiskAssessment(\n people: StakeholderProfile[],\n missingRoles: MissingRole[],\n signals: ExternalSignal[] = []\n): string {\n const risks: string[] = [];\n\n if (missingRoles.some((r) => r.role === \"champion\")) {\n risks.push(\"No champion identified — deal lacks an internal advocate.\");\n }\n if (missingRoles.some((r) => r.role === \"economic_buyer\")) {\n risks.push(\"Economic buyer unknown — decision authority not confirmed.\");\n }\n\n const coldPeople = people.filter((p) => p.riskFlags.includes(\"NO_CONTACT_30D\"));\n if (coldPeople.length > 0) {\n risks.push(`Cold contacts (30d+ silence): ${coldPeople.map((p) => p.name).join(\", \")}.`);\n }\n\n const lowHealth = people.filter((p) => p.healthScore < 40);\n if (lowHealth.length > 0) {\n risks.push(`Low health score (<40): ${lowHealth.map((p) => p.name).join(\", \")}.`);\n }\n\n const negativeSignals = signals.filter((s) => s.impact === \"negative\");\n for (const sig of negativeSignals.slice(0, 2)) {\n risks.push(`External signal: ${sig.summary}`);\n }\n\n return risks.length > 0 ? risks.join(\" \") : \"No critical risks detected.\";\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction deriveRecommendation(people: StakeholderProfile[], missingRoles: MissingRole[]): string {\n const critical = missingRoles.find((r) => r.urgency === \"critical\");\n if (critical) return critical.suggestion;\n\n const coldPeople = people.filter((p) => p.riskFlags.includes(\"NO_CONTACT_30D\"));\n if (coldPeople.length > 0) {\n return `Re-engage ${coldPeople.map((p) => p.name).join(\", \")} — no contact in 30+ days.`;\n }\n\n const important = missingRoles.find((r) => r.urgency === \"important\");\n if (important) return important.suggestion;\n\n const avgHealth =\n people.length > 0\n ? Math.round(people.reduce((s, p) => s + p.healthScore, 0) / people.length)\n : 0;\n return `Relationship health avg ${avgHealth}/100. Maintain regular contact cadence.`;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildStakeholderMap } from \"../../core/org-intelligence.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetOrgIntelligence(\n input: { slug: string; dealName?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);\n return {\n content: [{ type: \"text\", text: JSON.stringify(map, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetOrgIntelligence(server: McpServer): void {\n server.registerTool(\n \"get_org_intelligence\",\n {\n title: \"Get Org Intelligence\",\n description: `Build a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.\n\nReturns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().optional().describe(\"Optional deal name to scope the analysis\"),\n }),\n },\n async ({ slug, dealName }) => handleGetOrgIntelligence(dealName ? { slug, dealName } : { slug })\n );\n}\n","import { buildStakeholderMap, type StakeholderMap } from \"../core/org-intelligence.js\";\nimport { readHealth } from \"../core/relationship-health.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { scoreDealForToday } from \"../core/deal-health.js\";\nimport {\n buildSimulationInput,\n runSimulation,\n type SimulationResult,\n} from \"../core/revenue-simulation.js\";\nimport { listPlaybooks, matchPlaybooks, type PlaybookMatch } from \"../core/playbooks.js\";\nimport type { DealHealthScore } from \"../core/deal-health.js\";\nimport type { ContactHealth } from \"../core/relationship-health.js\";\nimport type { MissingRole } from \"../core/graph.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface DealHealthEntry {\n deal: string;\n stage: string;\n score: number;\n grade: string;\n warnings: string[];\n}\n\nexport interface DealRoomBrief {\n slug: string;\n dealName: string;\n generatedAt: string;\n stakeholders: StakeholderMap;\n relationshipHealth: ContactHealth[];\n dealHealth: DealHealthEntry[];\n revenueSimulation: Pick<SimulationResult, \"p50\" | \"p10\" | \"p90\" | \"expected\" | \"atRiskRevenue\">;\n recommendedPlaybook: PlaybookMatch | null;\n executiveSummary: string;\n topPriorities: string[];\n riskScore: number;\n}\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n\nexport async function buildDealRoom(\n dataDir: string,\n slug: string,\n dealName: string,\n today: string\n): Promise<DealRoomBrief> {\n // Parallel data collection\n const [pipelineDeals, simInput] = await Promise.all([\n readPipeline(dataDir, slug).catch(() => []),\n buildSimulationInput(dataDir, \"quarter\", today).catch(() => ({\n deals: [],\n externalSignals: [],\n iterations: 1000,\n horizon: \"quarter\" as const,\n today,\n })),\n ]);\n\n // Sync reads (fast FS reads)\n const stakeholders = buildStakeholderMap(dataDir, slug, today, dealName);\n const health = readHealth(dataDir, slug);\n const simResult = runSimulation({ ...simInput, iterations: 1000 });\n\n // Deal health scores\n const todayDate = new Date(today);\n const dealHealth: DealHealthEntry[] = pipelineDeals\n .filter((d) => d.stage !== \"won\" && d.stage !== \"lost\")\n .map((deal) => {\n const scored: DealHealthScore = scoreDealForToday(deal, todayDate);\n return {\n deal: deal.name,\n stage: deal.stage,\n score: scored.score,\n grade: scored.grade,\n warnings: scored.warnings,\n };\n });\n\n // Playbook matching — use the first active deal as context\n const firstActiveDeal = pipelineDeals.find((d) => d.stage !== \"won\" && d.stage !== \"lost\");\n let recommendedPlaybook: PlaybookMatch | null = null;\n if (firstActiveDeal) {\n // Prefer champion's contact health for accurate daysSinceContact signal\n const championEmail = stakeholders.people.find((p) => p.role === \"champion\")?.email;\n const champContact = championEmail\n ? health?.contacts.find(\n (c) => c.email === championEmail || c.contactId === `person:${championEmail}`\n )\n : undefined;\n const contactHealth = champContact ?? health?.contacts?.[0];\n const daysSinceContact = contactHealth?.daysSinceContact ?? 999;\n const dealSnapshot = {\n slug,\n name: firstActiveDeal.name,\n stage: firstActiveDeal.stage,\n value: firstActiveDeal.value ?? 0,\n probability: firstActiveDeal.probability ?? 50,\n healthScore: health?.overallHealth ?? 50,\n daysSinceContact,\n championPresent: stakeholders.people.some((p) => p.role === \"champion\"),\n };\n const matches = matchPlaybooks(listPlaybooks(dataDir, slug), dealSnapshot, daysSinceContact);\n recommendedPlaybook = matches[0] ?? null;\n }\n\n const riskScore = computeRiskScore(\n stakeholders.missingRoles,\n dealHealth,\n health?.overallHealth ?? 100\n );\n const topPriorities = buildTopPriorities(stakeholders, dealHealth);\n const executiveSummary = buildExecutiveSummary(\n slug,\n dealName,\n stakeholders,\n health?.overallHealth ?? 100,\n simResult,\n riskScore\n );\n\n return {\n slug,\n dealName,\n generatedAt: new Date().toISOString(),\n stakeholders,\n relationshipHealth: health?.contacts ?? [],\n dealHealth,\n revenueSimulation: {\n p50: simResult.p50,\n p10: simResult.p10,\n p90: simResult.p90,\n expected: simResult.expected,\n atRiskRevenue: simResult.atRiskRevenue,\n },\n recommendedPlaybook,\n executiveSummary,\n topPriorities,\n riskScore,\n };\n}\n\n// ─── Synthesis helpers ────────────────────────────────────────────────────────\n\nfunction computeRiskScore(\n missingRoles: MissingRole[],\n dealHealth: DealHealthEntry[],\n overallHealth: number\n): number {\n let risk = 0;\n\n for (const mr of missingRoles) {\n risk += mr.urgency === \"critical\" ? 25 : 10;\n }\n\n const avgDealScore =\n dealHealth.length > 0 ? dealHealth.reduce((s, d) => s + d.score, 0) / dealHealth.length : 100;\n risk += Math.round((100 - avgDealScore) * 0.3);\n\n risk += Math.round((100 - overallHealth) * 0.2);\n\n return Math.min(100, Math.max(0, risk));\n}\n\nfunction buildTopPriorities(stakeholders: StakeholderMap, dealHealth: DealHealthEntry[]): string[] {\n const priorities: string[] = [];\n\n for (const mr of stakeholders.missingRoles) {\n if (mr.urgency === \"critical\") priorities.push(mr.suggestion);\n }\n\n const coldPeople = stakeholders.people.filter((p) => p.riskFlags.includes(\"NO_CONTACT_30D\"));\n if (coldPeople.length > 0) {\n priorities.push(`Re-engage ${coldPeople.map((p) => p.name).join(\", \")} — silent 30+ days.`);\n }\n\n const atRiskDeals = dealHealth.filter((d) => d.score < 50);\n const showCount = Math.min(3, atRiskDeals.length);\n for (const d of atRiskDeals.slice(0, showCount)) {\n priorities.push(\n `Rescue deal \"${d.deal}\" (health ${d.score}/100): ${d.warnings[0] ?? \"at risk\"}`\n );\n }\n if (atRiskDeals.length > showCount) {\n priorities.push(`+${atRiskDeals.length - showCount} more at-risk deal(s) need attention.`);\n }\n\n for (const mr of stakeholders.missingRoles) {\n if (mr.urgency === \"important\") priorities.push(mr.suggestion);\n }\n\n if (priorities.length === 0) {\n priorities.push(\"Maintain current momentum — schedule next check-in.\");\n }\n\n return priorities;\n}\n\nfunction buildExecutiveSummary(\n slug: string,\n dealName: string,\n stakeholders: StakeholderMap,\n overallHealth: number,\n sim: Pick<SimulationResult, \"p50\" | \"atRiskRevenue\">,\n riskScore: number\n): string {\n const champCount = stakeholders.people.filter((p) => p.role === \"champion\").length;\n const missingCritical = stakeholders.missingRoles.filter((r) => r.urgency === \"critical\").length;\n\n const parts: string[] = [];\n parts.push(\n `${slug}/${dealName}: relationship health ${overallHealth}/100, ${champCount} champion(s) identified.`\n );\n if (sim.p50 > 0) {\n parts.push(`Pipeline P50 forecast: €${(sim.p50 / 1000).toFixed(1)}k.`);\n }\n if (missingCritical > 0) {\n parts.push(\n `Critical gaps: ${missingCritical} key role(s) unidentified — risk score ${riskScore}/100.`\n );\n } else {\n parts.push(`Overall risk score: ${riskScore}/100.`);\n }\n return parts.join(\" \");\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildDealRoom } from \"../../agents/deal-room.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleOpenDealRoom(\n input: { slug: string; dealName: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const today = new Date().toISOString().slice(0, 10);\n const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);\n return {\n content: [{ type: \"text\", text: JSON.stringify(brief, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerOpenDealRoom(server: McpServer): void {\n server.registerTool(\n \"open_deal_room\",\n {\n title: \"Open Deal Room\",\n description: `Multi-agent deal brief: orchestrates relationship graph, health scores, deal health, revenue simulation, and playbook matching into a unified deal-room brief with executive summary, top priorities, and risk score (0–100).\n\nReturns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().describe(\"Name of the deal to analyse\"),\n }),\n },\n async ({ slug, dealName }) => handleOpenDealRoom({ slug, dealName })\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { withJsonFile } from \"./file-lock.js\";\nimport { computeCustomerHealth, readHealth } from \"./relationship-health.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport { buildSimulationInput, runSimulation } from \"./revenue-simulation.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type TaskType =\n | \"daily_briefing\"\n | \"relationship_decay_alert\"\n | \"deal_risk_alert\"\n | \"external_signal_alert\"\n | \"follow_up_nudge\"\n | \"goal_progress_update\"\n | \"pipeline_forecast_weekly\"\n | \"playbook_suggestion\";\n\nexport type NotificationChannel = \"telegram\" | \"slack\" | \"email\" | \"mcp_tool_response\";\nexport type TaskPriority = \"urgent\" | \"high\" | \"normal\";\nexport type TaskStatus = \"pending\" | \"processing\" | \"done\" | \"failed\";\n\nexport interface AgentTask {\n id: string;\n type: TaskType;\n slug?: string;\n priority: TaskPriority;\n payload: unknown;\n createdAt: string;\n scheduledFor: string;\n status: TaskStatus;\n result?: string;\n channel: NotificationChannel;\n}\n\nexport interface DailyBriefing {\n date: string;\n generatedAt: string;\n urgent: string[];\n opportunities: string[];\n forecast: string;\n topAction: string;\n}\n\n// ─── Queue path ───────────────────────────────────────────────────────────────\n\nfunction queuePath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"agent-queue.json\");\n}\n\n// ─── Queue operations ─────────────────────────────────────────────────────────\n\nexport function readQueue(dataDir: string): AgentTask[] {\n const p = queuePath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n return Array.isArray(raw) ? (raw as AgentTask[]) : [];\n } catch {\n return [];\n }\n}\n\nexport async function enqueueTask(\n dataDir: string,\n task: Omit<AgentTask, \"id\" | \"createdAt\" | \"status\">\n): Promise<AgentTask> {\n const now = new Date().toISOString();\n const newTask: AgentTask = {\n ...task,\n id: `task_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,\n createdAt: now,\n status: \"pending\",\n };\n\n await withJsonFile<AgentTask[]>(queuePath(dataDir), (current) => {\n const existing = Array.isArray(current) ? current : [];\n return [...existing, newTask];\n });\n\n return newTask;\n}\n\nexport async function markTaskDone(\n dataDir: string,\n taskId: string,\n result?: string\n): Promise<void> {\n await withJsonFile<AgentTask[]>(queuePath(dataDir), (current) => {\n const tasks = Array.isArray(current) ? [...current] : [];\n const idx = tasks.findIndex((t) => t.id === taskId);\n if (idx >= 0) {\n tasks[idx] = { ...tasks[idx]!, status: \"done\", ...(result ? { result } : {}) };\n }\n return tasks;\n });\n}\n\n// ─── Daily briefing ───────────────────────────────────────────────────────────\n\nexport async function buildDailyBriefing(dataDir: string, today: string): Promise<DailyBriefing> {\n const slugs = listCustomerSlugs(dataDir);\n\n const urgent: string[] = [];\n const opportunities: string[] = [];\n const todayDate = new Date(`${today}T00:00:00Z`);\n\n // Parallel I/O across all customers\n const customerData = await Promise.all(\n slugs.map(async (slug) => {\n const cached = readHealth(dataDir, slug);\n const health = cached ?? computeCustomerHealth(dataDir, slug, today);\n const deals = await readPipeline(dataDir, slug).catch(() => []);\n return { slug, health, deals };\n })\n );\n\n for (const { slug, health, deals } of customerData) {\n // Relationship decay alerts\n for (const contact of health.contacts) {\n if (contact.riskFlags.includes(\"NO_CONTACT_30D\")) {\n urgent.push(\n `${slug}: ${contact.name} has been silent for ${contact.daysSinceContact} days — health ${contact.score}/100`\n );\n }\n }\n\n // Opportunities — B-grade+ relationship health with active deals\n const activeDeals = deals.filter((d) => d.stage !== \"won\" && d.stage !== \"lost\");\n if (health.overallHealth >= 65 && activeDeals.length > 0) {\n opportunities.push(\n `${slug}: relationship health ${health.overallHealth}/100 with ${activeDeals.length} active deal(s) — good time for expansion or upsell.`\n );\n }\n\n // Deal risk alerts\n for (const deal of deals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n const daysToClose = Math.floor(\n (new Date(deal.close_date).getTime() - todayDate.getTime()) / 86_400_000\n );\n if (daysToClose <= 7 && daysToClose >= 0) {\n urgent.push(\n `${slug}: Deal \"${deal.name}\" closes in ${daysToClose} day(s) — ${deal.stage}`\n );\n } else if (daysToClose < 0) {\n urgent.push(\n `${slug}: Deal \"${deal.name}\" close date passed (${Math.abs(daysToClose)} days overdue)`\n );\n }\n }\n }\n }\n\n // Revenue forecast\n let forecast = \"No active pipeline.\";\n try {\n const simInput = await buildSimulationInput(dataDir, \"quarter\", today);\n if (simInput.deals.length > 0) {\n const sim = runSimulation({ ...simInput, iterations: 1000 });\n forecast = `Q forecast: P50 €${(sim.p50 / 1000).toFixed(1)}k / P90 €${(sim.p90 / 1000).toFixed(1)}k — ${simInput.deals.length} deal(s) in pipeline.`;\n }\n } catch {\n // forecast stays as default\n }\n\n // Top action\n const topAction =\n urgent.length > 0\n ? (urgent[0]!\n .replace(/^[^:]+: /, \"\")\n .split(\"—\")[0]\n ?.trim() ?? urgent[0]!)\n : \"Review your pipeline and schedule next customer check-ins.\";\n\n return {\n date: today,\n generatedAt: new Date().toISOString(),\n urgent,\n opportunities,\n forecast,\n topAction,\n };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { buildDailyBriefing } from \"../../core/proactive-agent.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetProactiveBriefing(\n input: { date?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const date = input.date ?? new Date().toISOString().slice(0, 10);\n const briefing = await buildDailyBriefing(dataDir, date);\n return {\n content: [{ type: \"text\", text: JSON.stringify(briefing, null, 2) }],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: false, error: (err as Error).message }, null, 2),\n },\n ],\n };\n }\n}\n\nexport function registerGetProactiveBriefing(server: McpServer): void {\n server.registerTool(\n \"get_proactive_briefing\",\n {\n title: \"Get Proactive Briefing\",\n description: `Generate a proactive daily briefing: urgent alerts (relationship decay, deal risk, overdue close dates), pipeline forecast (P50/P90), and a single top-action recommendation.\n\nReturns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }`,\n inputSchema: z.object({\n date: z.string().optional().describe(\"ISO date (YYYY-MM-DD). Defaults to today.\"),\n }),\n },\n async ({ date }) => handleGetProactiveBriefing(date ? { date } : {})\n );\n}\n","import { z } from \"zod\";\n\nexport const EmailTemplateSchema = z.object({\n id: z.string().min(1),\n subject: z.string().min(1),\n category: z.string().default(\"general\"),\n variables: z.array(z.string()).default([]),\n language: z.string().default(\"de\"),\n createdAt: z.string(),\n updatedAt: z.string().optional(),\n});\n\nexport type EmailTemplateMeta = z.infer<typeof EmailTemplateSchema>;\n\nexport interface EmailTemplate extends EmailTemplateMeta {\n body: string;\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport matter from \"gray-matter\";\nimport { EmailTemplateSchema, type EmailTemplate } from \"../schemas/email-template.js\";\n\nexport function templatesDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"templates\");\n}\n\nfunction parseTemplateFile(filePath: string, id: string): EmailTemplate | null {\n try {\n const raw = matter(fs.readFileSync(filePath, \"utf-8\"));\n const result = EmailTemplateSchema.safeParse({\n id,\n createdAt: new Date().toISOString(),\n ...raw.data,\n });\n if (!result.success) return null;\n return { ...result.data, body: raw.content.trim() };\n } catch {\n return null;\n }\n}\n\nexport function listTemplates(dataDir: string, opts?: { category?: string }): EmailTemplate[] {\n const base = templatesDir(dataDir);\n if (!fs.existsSync(base)) return [];\n const results: EmailTemplate[] = [];\n\n const categories = fs.readdirSync(base).filter((name) => {\n try {\n return fs.statSync(path.join(base, name)).isDirectory();\n } catch {\n return false;\n }\n });\n\n for (const cat of categories) {\n if (opts?.category && cat !== opts.category) continue;\n const catDir = path.join(base, cat);\n const files = fs.readdirSync(catDir).filter((f) => f.endsWith(\".md\"));\n for (const file of files) {\n const id = file.replace(/\\.md$/, \"\");\n const tmpl = parseTemplateFile(path.join(catDir, file), id);\n if (tmpl) results.push(tmpl);\n }\n }\n return results;\n}\n\nexport function getTemplate(dataDir: string, id: string): EmailTemplate | null {\n const base = templatesDir(dataDir);\n if (!fs.existsSync(base)) return null;\n\n // Search all categories\n const categories = fs.existsSync(base)\n ? fs.readdirSync(base).filter((n) => {\n try {\n return fs.statSync(path.join(base, n)).isDirectory();\n } catch {\n return false;\n }\n })\n : [];\n\n for (const cat of categories) {\n const p = path.join(base, cat, `${id}.md`);\n if (fs.existsSync(p)) return parseTemplateFile(p, id);\n }\n return null;\n}\n\nexport function writeTemplate(dataDir: string, tmpl: EmailTemplate): void {\n const { body, ...meta } = tmpl;\n const category = meta.category ?? \"general\";\n const dir = path.join(templatesDir(dataDir), category);\n fs.mkdirSync(dir, { recursive: true });\n const content = matter.stringify(body, { ...meta, updatedAt: new Date().toISOString() });\n writeFileAtomic(path.join(dir, `${tmpl.id}.md`), content);\n}\n\nexport function deleteTemplate(dataDir: string, id: string): boolean {\n const tmpl = getTemplate(dataDir, id);\n if (!tmpl) return false;\n const p = path.join(templatesDir(dataDir), tmpl.category, `${id}.md`);\n if (fs.existsSync(p)) {\n fs.unlinkSync(p);\n return true;\n }\n return false;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listTemplates } from \"../../fs/template-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListEmailTemplates(\n input: { category?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const templates = listTemplates(dataDir, input.category ? { category: input.category } : {});\n const summary = templates.map(({ body: _body, ...meta }) => meta);\n return { content: [{ type: \"text\", text: JSON.stringify(summary, null, 2) }] };\n}\n\nexport function registerListEmailTemplates(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"list_email_templates\",\n {\n description:\n \"List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').\",\n inputSchema: z.object({\n category: z.string().optional().describe(\"Filter by category\"),\n }),\n },\n ({ category }) => handleListEmailTemplates(category ? { category } : {}, dataDir)\n );\n}\n","import { readMainFacts } from \"../fs/customer-dir.js\";\n\nexport type TemplateVariables = Record<string, string | number | undefined>;\n\nconst VARIABLE_REGEX = /\\{\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}\\}/g;\n\nexport function interpolate(template: string, vars: TemplateVariables): string {\n return template.replace(VARIABLE_REGEX, (match, key: string) => {\n const val = vars[key];\n return val !== undefined ? String(val) : match; // keep {{key}} if unresolved\n });\n}\n\nexport function extractVariables(template: string): string[] {\n return [...template.matchAll(new RegExp(VARIABLE_REGEX.source, \"g\"))].map((m) => m[1]!);\n}\n\nexport async function buildVariablesFromCustomer(\n dataDir: string,\n slug: string\n): Promise<TemplateVariables> {\n const facts = await readMainFacts(dataDir, slug).catch(() => null);\n const now = new Date();\n return {\n company: facts?.name ?? slug,\n domain: facts?.domain ?? \"\",\n email: facts?.email ?? \"\",\n stage: facts?.relationship_stage ?? \"\",\n slug,\n date: now.toLocaleDateString(\"de-DE\"),\n year: now.getFullYear(),\n month: now.toLocaleDateString(\"de-DE\", { month: \"long\" }),\n };\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getTemplate } from \"../../fs/template-store.js\";\nimport { extractVariables } from \"../../core/template-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetEmailTemplate(\n input: { id: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tmpl = getTemplate(dataDir, input.id);\n if (!tmpl) {\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ error: `Template '${input.id}' not found` }) },\n ],\n };\n }\n const allVars = extractVariables(`${tmpl.subject}\\n${tmpl.body}`);\n const unique = [...new Set(allVars)];\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ ...tmpl, detectedVariables: unique }, null, 2),\n },\n ],\n };\n}\n\nexport function registerGetEmailTemplate(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_email_template\",\n {\n description:\n \"Get a specific email template by ID, including its body and detected variables.\",\n inputSchema: z.object({\n id: z.string().describe(\"Template ID (e.g. 'enterprise-intro')\"),\n }),\n },\n ({ id }) => handleGetEmailTemplate({ id }, dataDir)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getTemplate } from \"../../fs/template-store.js\";\nimport { interpolate, buildVariablesFromCustomer } from \"../../core/template-engine.js\";\nimport { readMainFacts } from \"../../fs/customer-dir.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleDraftEmail(\n input: {\n slug: string;\n templateId: string;\n overrides?: Record<string, string>;\n tone?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tmpl = getTemplate(dataDir, input.templateId);\n if (!tmpl) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Template '${input.templateId}' not found` }),\n },\n ],\n };\n }\n\n const autoVars = await buildVariablesFromCustomer(dataDir, input.slug);\n const vars = { ...autoVars, ...(input.overrides ?? {}) };\n\n const subject = interpolate(tmpl.subject, vars);\n const interpolatedBody = interpolate(tmpl.body, vars);\n\n // Tone: explicit override wins; otherwise fall back to the customer's tone\n // profile (D8), then the global default.\n let effectiveTone = input.tone;\n if (!effectiveTone) {\n const { resolveTone, toneInstruction } = await import(\"../../core/tone.js\");\n const instr = toneInstruction(resolveTone(dataDir, input.slug));\n if (instr) effectiveTone = instr;\n }\n\n // Optional LLM polish: rewrite the interpolated body in the requested tone.\n // Falls back to the plain interpolation when no ANTHROPIC_API_KEY is set or\n // the call fails — the template-fill behaviour is always preserved.\n let body = interpolatedBody;\n let polished = false;\n if (effectiveTone) {\n try {\n const { callLlm } = await import(\"../../core/llm.js\");\n const refined = await callLlm(\n `Rewrite the following email in a ${effectiveTone} tone. Keep the same language, ` +\n `preserve all names and facts, and do not invent details. ` +\n `Return ONLY the rewritten email body, no preamble.\\n\\n---\\n${interpolatedBody}`\n );\n if (refined && refined.trim()) {\n // EU-AI-Act Art. 50 (D17): label AI-generated content (opt-out).\n const { labelAiContent } = await import(\"../../core/compliance.js\");\n body = labelAiContent(refined.trim());\n polished = true;\n }\n } catch {\n // graceful fallback to the interpolated body\n }\n }\n\n // Try to get email from main_facts for 'to' field\n const facts = await readMainFacts(dataDir, input.slug).catch(() => null);\n const to = facts?.email ?? \"\";\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n subject,\n body,\n to,\n slug: input.slug,\n templateId: input.templateId,\n tone: effectiveTone ?? null,\n polished,\n resolvedVariables: vars,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerDraftEmail(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"draft_email\",\n {\n description: `Draft a personalized email for a customer using a stored template.\nVariables are auto-filled from the customer's main_facts.md. Override any variable manually.\nOptionally pass a tone (e.g. \"formal\", \"friendly\", \"concise\") to LLM-polish the body —\nfalls back to plain template-fill when no ANTHROPIC_API_KEY is configured.\nReturns: { subject, body, to, tone, polished, resolvedVariables } — does NOT send automatically.`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n templateId: z.string().describe(\"Template ID to use\"),\n overrides: z\n .record(z.string())\n .optional()\n .describe(\"Override any template variable (e.g. {firstName: 'Alice'})\"),\n tone: z\n .string()\n .optional()\n .describe(\"Optional tone to LLM-polish the body (e.g. 'formal', 'friendly', 'concise')\"),\n }),\n },\n ({ slug, templateId, overrides, tone }) =>\n handleDraftEmail(\n {\n slug,\n templateId,\n ...(overrides !== undefined ? { overrides } : {}),\n ...(tone !== undefined ? { tone } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const SequenceStepSchema = z.object({\n day: z.number().int().min(0),\n templateId: z.string().min(1),\n skipIfReplied: z.boolean().default(true),\n});\n\nexport const SequenceSchema = z.object({\n id: z.string().min(1),\n name: z.string().min(1),\n steps: z.array(SequenceStepSchema).min(1),\n createdAt: z.string(),\n});\n\nexport const SequenceEnrollmentSchema = z.object({\n id: z.string(),\n sequenceId: z.string(),\n slug: z.string(),\n contactEmail: z.string().email(),\n enrolledAt: z.string(),\n status: z.enum([\"active\", \"paused\", \"completed\", \"bounced\"]),\n currentStep: z.number().int().min(0),\n stepsCompleted: z.array(z.number()),\n lastSentAt: z.string().optional(),\n lastRepliedAt: z.string().optional(),\n});\n\nexport type SequenceStep = z.infer<typeof SequenceStepSchema>;\nexport type Sequence = z.infer<typeof SequenceSchema>;\nexport type SequenceEnrollment = z.infer<typeof SequenceEnrollmentSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport yaml from \"js-yaml\";\nimport { withJsonFile } from \"../core/file-lock.js\";\nimport {\n SequenceSchema,\n SequenceEnrollmentSchema,\n type Sequence,\n type SequenceEnrollment,\n} from \"../schemas/sequence.js\";\n\nexport function sequencesDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sequences\");\n}\n\nexport function enrollmentsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"sequence-enrollments.json\");\n}\n\nexport function listSequences(dataDir: string): Sequence[] {\n const dir = sequencesDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n\n const files = fs.readdirSync(dir).filter((f) => f.endsWith(\".yaml\"));\n const results: Sequence[] = [];\n\n for (const file of files) {\n try {\n const content = fs.readFileSync(path.join(dir, file), \"utf-8\") as string;\n const raw = yaml.load(content);\n const parsed = SequenceSchema.safeParse(raw);\n if (parsed.success) {\n results.push(parsed.data);\n }\n } catch {\n // skip invalid files\n }\n }\n\n return results;\n}\n\nexport function getSequence(dataDir: string, id: string): Sequence | null {\n const sequences = listSequences(dataDir);\n return sequences.find((s) => s.id === id) ?? null;\n}\n\nexport function writeSequence(dataDir: string, seq: Sequence): void {\n const dir = sequencesDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n const content = yaml.dump(seq);\n writeFileAtomic(path.join(dir, `${seq.id}.yaml`), content);\n}\n\nexport function readEnrollments(dataDir: string): SequenceEnrollment[] {\n const p = enrollmentsPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n return Array.isArray(raw) ? (raw as SequenceEnrollment[]) : [];\n } catch {\n return [];\n }\n}\n\nexport async function writeEnrollment(\n dataDir: string,\n enrollment: SequenceEnrollment\n): Promise<void> {\n await withJsonFile<SequenceEnrollment[]>(enrollmentsPath(dataDir), (current) => {\n const existing = Array.isArray(current) ? current : [];\n return [...existing, enrollment];\n });\n}\n\nexport async function updateEnrollment(\n dataDir: string,\n id: string,\n updates: Partial<SequenceEnrollment>\n): Promise<SequenceEnrollment | null> {\n let updated: SequenceEnrollment | null = null;\n\n await withJsonFile<SequenceEnrollment[]>(enrollmentsPath(dataDir), (current) => {\n const existing = Array.isArray(current) ? current : [];\n const idx = existing.findIndex((e) => e.id === id);\n if (idx < 0) return existing;\n\n const merged = SequenceEnrollmentSchema.parse({ ...existing[idx], ...updates });\n updated = merged;\n const next = [...existing];\n next[idx] = merged;\n return next;\n });\n\n return updated;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getSequence, writeEnrollment } from \"../../fs/sequence-store.js\";\nimport { getTemplate } from \"../../fs/template-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleEnrollInSequence(\n input: { slug: string; contactEmail: string; sequenceId: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const sequence = getSequence(dataDir, input.sequenceId);\n if (!sequence) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Sequence '${input.sequenceId}' not found` }),\n },\n ],\n };\n }\n\n // Validate that the first step's template exists\n const firstStep = sequence.steps[0]!;\n const template = getTemplate(dataDir, firstStep.templateId);\n if (!template) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error: `Template '${firstStep.templateId}' for step 0 not found`,\n }),\n },\n ],\n };\n }\n\n const enrollmentId = `enroll_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`;\n const now = new Date().toISOString();\n\n const enrollment = {\n id: enrollmentId,\n sequenceId: input.sequenceId,\n slug: input.slug,\n contactEmail: input.contactEmail,\n enrolledAt: now,\n status: \"active\" as const,\n currentStep: 0,\n stepsCompleted: [] as number[],\n };\n\n await writeEnrollment(dataDir, enrollment);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n enrollmentId,\n sequenceName: sequence.name,\n totalSteps: sequence.steps.length,\n }),\n },\n ],\n };\n}\n\nexport function registerEnrollInSequence(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"enroll_in_sequence\",\n {\n description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.\nReturns: { enrollmentId, sequenceName, totalSteps }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n contactEmail: z.string().email().describe(\"Email address of the contact to enroll\"),\n sequenceId: z.string().describe(\"ID of the sequence to enroll in\"),\n }),\n },\n ({ slug, contactEmail, sequenceId }) =>\n handleEnrollInSequence({ slug, contactEmail, sequenceId }, dataDir)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readEnrollments } from \"../../fs/sequence-store.js\";\nimport type { SequenceEnrollment } from \"../../schemas/sequence.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListSequenceEnrollments(\n input: { slug?: string; status?: \"active\" | \"paused\" | \"completed\" },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n let enrollments = readEnrollments(dataDir);\n\n if (input.slug !== undefined) {\n enrollments = enrollments.filter((e: SequenceEnrollment) => e.slug === input.slug);\n }\n\n if (input.status !== undefined) {\n enrollments = enrollments.filter((e: SequenceEnrollment) => e.status === input.status);\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ enrollments }, null, 2),\n },\n ],\n };\n}\n\nexport function registerListSequenceEnrollments(\n server: McpServer,\n dataDir: string = DATA_DIR\n): void {\n server.registerTool(\n \"list_sequence_enrollments\",\n {\n description: `List email sequence enrollments. Filter by customer slug or status.\nReturns: { enrollments: SequenceEnrollment[] }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n status: z\n .enum([\"active\", \"paused\", \"completed\"])\n .optional()\n .describe(\"Filter by enrollment status\"),\n }),\n },\n ({ slug, status }) =>\n handleListSequenceEnrollments(\n {\n ...(slug !== undefined ? { slug } : {}),\n ...(status !== undefined ? { status } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { updateEnrollment } from \"../../fs/sequence-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUnenrollFromSequence(\n input: { enrollmentId: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const updated = await updateEnrollment(dataDir, input.enrollmentId, { status: \"paused\" });\n\n if (!updated) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: `Enrollment '${input.enrollmentId}' not found`,\n }),\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true }),\n },\n ],\n };\n}\n\nexport function registerUnenrollFromSequence(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"unenroll_from_sequence\",\n {\n description: `Unenroll (pause) a contact from an email sequence. Sets status to \"paused\" (soft delete).\nReturns: { success: boolean }`,\n inputSchema: z.object({\n enrollmentId: z.string().describe(\"ID of the enrollment to pause\"),\n }),\n },\n ({ enrollmentId }) => handleUnenrollFromSequence({ enrollmentId }, dataDir)\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listSequences, readEnrollments } from \"../../fs/sequence-store.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListSequences(\n _input: Record<string, never>,\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const sequences = listSequences(dataDir);\n const enrollments = readEnrollments(dataDir);\n\n const result = sequences.map((seq) => ({\n id: seq.id,\n name: seq.name,\n stepCount: seq.steps.length,\n enrollmentCount: enrollments.filter((e) => e.sequenceId === seq.id).length,\n }));\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ sequences: result }, null, 2),\n },\n ],\n };\n}\n\nexport function registerListSequences(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"list_sequences\",\n {\n description: `List all email sequences with step count and enrollment count.\nReturns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,\n inputSchema: z.object({}),\n },\n () => handleListSequences({}, dataDir)\n );\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\nimport yaml from \"js-yaml\";\nimport type { Quote, QuoteLineItem } from \"../schemas/quote.js\";\n\ninterface QuoteConfig {\n companyName?: string;\n companyAddress?: string;\n vatId?: string;\n currency?: string;\n paymentTerms?: string;\n footerText?: string;\n}\n\nexport interface GenerateQuoteInput {\n slug: string;\n dealName: string;\n lineItems: Array<{ description: string; quantity: number; unitPrice: number }>;\n vatPercent?: number;\n validUntilDays?: number;\n currency?: string;\n}\n\nfunction quotesDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"quotes\");\n}\n\nfunction loadQuoteConfig(dataDir: string): QuoteConfig {\n const p = path.join(dataDir, \".agentic\", \"quote-config.yaml\");\n if (!fs.existsSync(p)) return {};\n try {\n return (yaml.load(fs.readFileSync(p, \"utf-8\") as string) as QuoteConfig) ?? {};\n } catch {\n return {};\n }\n}\n\nfunction nextQuoteNumber(dataDir: string): string {\n const year = new Date().getFullYear();\n const dir = quotesDir(dataDir);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n return `Q-${year}-001`;\n }\n const existing = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\") && f.startsWith(`Q-${year}-`))\n .map((f) => parseInt(f.replace(`Q-${year}-`, \"\").replace(\".json\", \"\"), 10))\n .filter((n) => !isNaN(n));\n const max = existing.length > 0 ? Math.max(...existing) : 0;\n return `Q-${year}-${String(max + 1).padStart(3, \"0\")}`;\n}\n\nfunction addDaysToDate(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.slice(0, 10).split(\"-\").map(Number) as [\n number,\n number,\n number,\n ];\n const d = new Date(Date.UTC(year, month - 1, day));\n d.setUTCDate(d.getUTCDate() + days);\n return d.toISOString().slice(0, 10);\n}\n\nfunction buildHtml(quote: Quote, config: QuoteConfig, customerName: string): string {\n const lineRows = quote.lineItems\n .map(\n (item) =>\n `<tr><td>${item.description}</td><td style=\"text-align:right\">${item.quantity}</td><td style=\"text-align:right\">${item.unitPrice.toFixed(2)} ${quote.currency}</td><td style=\"text-align:right\">${item.total.toFixed(2)} ${quote.currency}</td></tr>`\n )\n .join(\"\\n\");\n\n return `<!DOCTYPE html>\n<html lang=\"de\">\n<head><meta charset=\"UTF-8\"><title>Angebot ${quote.quoteNumber}</title>\n<style>body{font-family:Arial,sans-serif;max-width:800px;margin:40px auto;color:#222}table{width:100%;border-collapse:collapse}th,td{padding:8px 12px;border:1px solid #ddd}th{background:#f5f5f5}h1{color:#1a1a2e}.total{font-weight:bold;font-size:1.1em}</style>\n</head>\n<body>\n<h1>Angebot ${quote.quoteNumber}</h1>\n<p><strong>${config.companyName ?? \"\"}</strong><br>${config.companyAddress ?? \"\"}<br>${config.vatId ? `USt-IdNr.: ${config.vatId}` : \"\"}</p>\n<hr>\n<p><strong>An:</strong> ${customerName}</p>\n<p><strong>Date:</strong> ${quote.createdAt.slice(0, 10)} &nbsp;&nbsp; <strong>Valid until:</strong> ${quote.validUntil}</p>\n<h2>Leistungen</h2>\n<table>\n<thead><tr><th>Beschreibung</th><th style=\"text-align:right\">Menge</th><th style=\"text-align:right\">Einzelpreis</th><th style=\"text-align:right\">Gesamt</th></tr></thead>\n<tbody>${lineRows}</tbody>\n</table>\n<br>\n<table style=\"width:300px;margin-left:auto\">\n<tr><td>Nettobetrag</td><td style=\"text-align:right\">${quote.subtotal.toFixed(2)} ${quote.currency}</td></tr>\n<tr><td>MwSt. (${quote.vatPercent}%)</td><td style=\"text-align:right\">${quote.vat.toFixed(2)} ${quote.currency}</td></tr>\n<tr class=\"total\"><td><strong>Gesamtbetrag</strong></td><td style=\"text-align:right\"><strong>${quote.total.toFixed(2)} ${quote.currency}</strong></td></tr>\n</table>\n<br><p>${config.paymentTerms ?? \"\"}</p>\n<hr><small>${config.footerText ?? \"\"}</small>\n</body></html>`;\n}\n\nfunction readCustomerName(dataDir: string, slug: string): string {\n const p = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (!fs.existsSync(p)) return slug;\n const content = fs.readFileSync(p, \"utf-8\") as string;\n const match = /^name:\\s*(.+)$/m.exec(content);\n return match?.[1]?.trim() ?? slug;\n}\n\nexport function readQuote(dataDir: string, quoteNumber: string): Quote | null {\n const p = path.join(quotesDir(dataDir), `${quoteNumber}.json`);\n if (!fs.existsSync(p)) return null;\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as Quote;\n } catch {\n return null;\n }\n}\n\nexport function listQuotes(dataDir: string, slug?: string): Quote[] {\n const dir = quotesDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .flatMap((f) => {\n try {\n const q = JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\") as string) as Quote;\n return slug === undefined || q.slug === slug ? [q] : [];\n } catch {\n return [];\n }\n });\n}\n\nexport function updateQuoteStatus(\n dataDir: string,\n quoteNumber: string,\n status: Quote[\"status\"]\n): void {\n const q = readQuote(dataDir, quoteNumber);\n if (!q) return;\n const updated: Quote = { ...q, status };\n if (status === \"viewed\" && !q.viewedAt) updated.viewedAt = new Date().toISOString();\n if (status === \"accepted\" && !q.acceptedAt) updated.acceptedAt = new Date().toISOString();\n writeFileAtomic(\n path.join(quotesDir(dataDir), `${quoteNumber}.json`),\n JSON.stringify(updated, null, 2)\n );\n}\n\nexport async function generateQuote(dataDir: string, input: GenerateQuoteInput): Promise<Quote> {\n const config = loadQuoteConfig(dataDir);\n const vatPercent = input.vatPercent ?? 19;\n const validUntilDays = input.validUntilDays ?? 30;\n const currency = input.currency ?? config.currency ?? \"EUR\";\n\n const items: QuoteLineItem[] = input.lineItems.map((item) => ({\n description: item.description,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n total: item.quantity * item.unitPrice,\n }));\n\n const subtotal = items.reduce((sum, i) => sum + i.total, 0);\n const vat = Math.round(subtotal * (vatPercent / 100) * 100) / 100;\n const total = Math.round((subtotal + vat) * 100) / 100;\n\n const quoteNumber = nextQuoteNumber(dataDir);\n const now = new Date().toISOString();\n const validUntil = addDaysToDate(now.slice(0, 10), validUntilDays);\n\n const dir = quotesDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n\n const htmlPath = path.join(dir, `${quoteNumber}.html`);\n\n const quote: Quote = {\n quoteNumber,\n slug: input.slug,\n dealName: input.dealName,\n lineItems: items,\n subtotal,\n vatPercent,\n vat,\n total,\n currency,\n createdAt: now,\n validUntilDays,\n validUntil,\n status: \"draft\",\n htmlPath,\n };\n\n writeFileAtomic(path.join(dir, `${quoteNumber}.json`), JSON.stringify(quote, null, 2));\n\n const customerName = readCustomerName(dataDir, input.slug);\n const html = buildHtml(quote, config, customerName);\n writeFileAtomic(htmlPath, html);\n\n return quote;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { generateQuote } from \"../../core/quote-generator.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGenerateQuote(\n input: {\n slug: string;\n dealName: string;\n lineItems: Array<{ description: string; quantity: number; unitPrice: number }>;\n vatPercent?: number;\n validUntilDays?: number;\n currency?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n try {\n const quote = await generateQuote(dataDir, input);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n quoteNumber: quote.quoteNumber,\n htmlPath: quote.htmlPath,\n total: quote.total,\n subtotal: quote.subtotal,\n vat: quote.vat,\n vatPercent: quote.vatPercent,\n currency: quote.currency,\n validUntil: quote.validUntil,\n status: quote.status,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: JSON.stringify({ error: (err as Error).message }) }],\n };\n }\n}\n\nexport function registerGenerateQuote(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"generate_quote\",\n {\n description: `Generate a professional HTML quote/offer for a customer deal.\nCalculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.\nReturns: { quoteNumber, htmlPath, total, currency, validUntil }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n dealName: z.string().describe(\"Name of the deal this quote is for\"),\n lineItems: z\n .array(\n z.object({\n description: z.string(),\n quantity: z.number().positive(),\n unitPrice: z.number().min(0),\n })\n )\n .min(1)\n .describe(\"Line items for the quote\"),\n vatPercent: z.number().min(0).max(100).optional().describe(\"VAT percentage (default 19)\"),\n validUntilDays: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Quote validity in days (default 30)\"),\n currency: z.string().optional().describe(\"Currency code (default EUR)\"),\n }),\n },\n ({ slug, dealName, lineItems, vatPercent, validUntilDays, currency }) =>\n handleGenerateQuote(\n {\n slug,\n dealName,\n lineItems,\n ...(vatPercent !== undefined ? { vatPercent } : {}),\n ...(validUntilDays !== undefined ? { validUntilDays } : {}),\n ...(currency !== undefined ? { currency } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readQuote, listQuotes } from \"../../core/quote-generator.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetQuoteStatus(\n input: { quoteNumber?: string; slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n if (input.quoteNumber) {\n const quote = readQuote(dataDir, input.quoteNumber);\n if (!quote) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Quote '${input.quoteNumber}' not found` }),\n },\n ],\n };\n }\n return { content: [{ type: \"text\", text: JSON.stringify(quote, null, 2) }] };\n }\n\n const quotes = listQuotes(dataDir, input.slug);\n return { content: [{ type: \"text\", text: JSON.stringify({ quotes }, null, 2) }] };\n}\n\nexport function registerGetQuoteStatus(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_quote_status\",\n {\n description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).\nReturns quote with status: draft | sent | viewed | accepted | declined`,\n inputSchema: z.object({\n quoteNumber: z.string().optional().describe(\"Specific quote number (e.g. Q-2026-001)\"),\n slug: z.string().optional().describe(\"Customer slug to list all quotes for\"),\n }),\n },\n ({ quoteNumber, slug }) =>\n handleGetQuoteStatus(\n {\n ...(quoteNumber !== undefined ? { quoteNumber } : {}),\n ...(slug !== undefined ? { slug } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\ninterface CalendlyConfig {\n apiKey?: string;\n defaultEventType?: string;\n autoLogMeetings?: boolean;\n}\n\nfunction loadCalendlyConfig(dataDir: string): CalendlyConfig {\n const p = path.join(dataDir, \".agentic\", \"integrations\", \"calendly.yaml\");\n if (!fs.existsSync(p)) return {};\n try {\n return (yaml.load(fs.readFileSync(p, \"utf-8\") as string) as CalendlyConfig) ?? {};\n } catch {\n return {};\n }\n}\n\nfunction readCustomerFacts(dataDir: string, slug: string): { name?: string; email?: string } {\n const p = path.join(dataDir, \"customers\", slug, \"main_facts.md\");\n if (!fs.existsSync(p)) return {};\n const content = fs.readFileSync(p, \"utf-8\") as string;\n const nameMatch = /^name:\\s*(.+)$/m.exec(content);\n const emailMatch = /^email:\\s*(.+)$/m.exec(content);\n const name = nameMatch?.[1]?.trim();\n const email = emailMatch?.[1]?.trim();\n return {\n ...(name ? { name } : {}),\n ...(email ? { email } : {}),\n };\n}\n\nexport async function handleGetBookingLink(\n input: { slug: string; eventType?: string; prefillName?: boolean },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const config = loadCalendlyConfig(dataDir);\n const apiKey = config.apiKey ?? process.env[\"CALENDLY_API_KEY\"] ?? \"\";\n\n if (!apiKey) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error:\n \"Calendly API key not configured. Set CALENDLY_API_KEY env var or configure .agentic/integrations/calendly.yaml\",\n }),\n },\n ],\n };\n }\n\n const eventTypeSlug = input.eventType ?? config.defaultEventType ?? \"30min\";\n\n try {\n const { getSchedulingLink, listEventTypes } = await import(\"../../sync/calendly.js\");\n const prefill = input.prefillName ? readCustomerFacts(dataDir, input.slug) : undefined;\n const bookingUrl = await getSchedulingLink(apiKey, eventTypeSlug, prefill);\n\n const eventTypes = await listEventTypes(apiKey);\n const eventType = eventTypes.find(\n (et) =>\n et.slug === eventTypeSlug || et.name.toLowerCase().includes(eventTypeSlug.toLowerCase())\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n bookingUrl,\n eventType: eventType?.name ?? eventTypeSlug,\n duration: eventType?.duration ?? 30,\n slug: input.slug,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: JSON.stringify({ error: (err as Error).message }) }],\n };\n }\n}\n\nexport function registerGetBookingLink(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_booking_link\",\n {\n description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.\nRequires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.\nReturns: { bookingUrl, eventType, duration }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n eventType: z\n .string()\n .optional()\n .describe(\n \"Calendly event type slug (e.g. '30min', '60min'). Uses default if not specified.\"\n ),\n prefillName: z\n .boolean()\n .optional()\n .describe(\"Pre-fill customer name and email in the booking link\"),\n }),\n },\n ({ slug, eventType, prefillName }) =>\n handleGetBookingLink(\n {\n slug,\n ...(eventType !== undefined ? { eventType } : {}),\n ...(prefillName !== undefined ? { prefillName } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const TicketStatusSchema = z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]);\nexport const TicketPrioritySchema = z.enum([\"urgent\", \"high\", \"normal\", \"low\"]);\n\nexport const TicketSchema = z.object({\n id: z.string().regex(/^T-\\d{3,}$/),\n title: z.string().min(1),\n status: TicketStatusSchema,\n priority: TicketPrioritySchema.default(\"normal\"),\n assignee: z.string().optional(),\n created: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n slaDue: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n resolved: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n description: z.string().optional(),\n});\n\nexport type Ticket = z.infer<typeof TicketSchema>;\nexport type TicketStatus = z.infer<typeof TicketStatusSchema>;\nexport type TicketPriority = z.infer<typeof TicketPrioritySchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { TicketSchema, type Ticket } from \"../schemas/ticket.js\";\nimport { listCustomerSlugs, assertSafeSlug } from \"./customer-dir.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\n\nconst TICKET_HEADER = \"# Tickets\\n\\n\";\nconst TABLE_HEADER = `| ID | Title | Status | Priority | Assignee | Created | SLA Due | Resolved |\n|----|-------|--------|----------|----------|---------|---------|---------|`;\n\nfunction ticketsPath(dataDir: string, slug: string): string {\n assertSafeSlug(slug);\n return path.join(dataDir, \"customers\", slug, \"tickets.md\");\n}\n\nfunction escapeMd(s: string | undefined): string {\n return (s ?? \"\").replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n}\n\nfunction parseTicketsFromMarkdown(content: string): Ticket[] {\n const tickets: Ticket[] = [];\n const lines = content.split(\"\\n\");\n let inTable = false;\n\n for (const line of lines) {\n if (line.startsWith(\"| ID |\") || line.startsWith(\"|----\")) {\n inTable = true;\n continue;\n }\n if (!inTable) continue;\n if (!line.startsWith(\"|\")) {\n inTable = false;\n continue;\n }\n\n const cols = line\n .split(\"|\")\n .slice(1, -1)\n .map((c) => c.trim());\n if (cols.length < 8) continue;\n const [id, title, status, priority, assignee, created, slaDue, resolved] = cols;\n if (!id || !title || id === \"ID\") continue;\n\n const raw = {\n id,\n title,\n status: status || \"open\",\n priority: priority || \"normal\",\n ...(assignee ? { assignee } : {}),\n created: created || new Date().toISOString().slice(0, 10),\n ...(slaDue ? { slaDue } : {}),\n ...(resolved ? { resolved } : {}),\n };\n\n const parsed = TicketSchema.safeParse(raw);\n if (parsed.success) tickets.push(parsed.data);\n }\n return tickets;\n}\n\nfunction serializeTickets(tickets: Ticket[]): string {\n const rows = tickets.map(\n (t) =>\n `| ${t.id} | ${escapeMd(t.title)} | ${t.status} | ${t.priority} | ${t.assignee ?? \"\"} | ${t.created} | ${t.slaDue ?? \"\"} | ${t.resolved ?? \"\"} |`\n );\n return `${TICKET_HEADER}${TABLE_HEADER}\\n${rows.join(\"\\n\")}\\n`;\n}\n\nexport async function readTickets(dataDir: string, slug: string): Promise<Ticket[]> {\n const p = ticketsPath(dataDir, slug);\n if (!fs.existsSync(p)) return [];\n return parseTicketsFromMarkdown(fs.readFileSync(p, \"utf-8\") as string);\n}\n\nexport async function upsertTicket(dataDir: string, slug: string, ticket: Ticket): Promise<void> {\n const p = ticketsPath(dataDir, slug);\n const existing = await readTickets(dataDir, slug);\n const idx = existing.findIndex((t) => t.id === ticket.id);\n if (idx >= 0) {\n existing[idx] = ticket;\n } else {\n existing.push(ticket);\n }\n writeFileAtomic(p, serializeTickets(existing));\n}\n\nexport function nextTicketId(tickets: Ticket[]): string {\n const nums = tickets.map((t) => parseInt(t.id.replace(\"T-\", \"\"), 10)).filter((n) => !isNaN(n));\n const max = nums.length > 0 ? Math.max(...nums) : 0;\n return `T-${String(max + 1).padStart(3, \"0\")}`;\n}\n\nexport async function listAllTickets(\n dataDir: string,\n filter?: { slug?: string; status?: string; priority?: string; assignee?: string }\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const slugs = filter?.slug ? [filter.slug] : listCustomerSlugs(dataDir);\n\n // Each customer's tickets file is independent — read them in parallel.\n const perCustomer = await Promise.all(\n slugs.map(async (slug) => {\n const tickets = await readTickets(dataDir, slug);\n return tickets\n .filter(\n (ticket) =>\n (!filter?.status || ticket.status === filter.status) &&\n (!filter?.priority || ticket.priority === filter.priority) &&\n (!filter?.assignee || ticket.assignee === filter.assignee)\n )\n .map((ticket) => ({ slug, ticket }));\n })\n );\n const results = perCustomer.flat();\n\n // Sort: urgent first, then by created date\n const priorityOrder: Record<string, number> = { urgent: 0, high: 1, normal: 2, low: 3 };\n results.sort((a, b) => {\n const pa = priorityOrder[a.ticket.priority] ?? 2;\n const pb = priorityOrder[b.ticket.priority] ?? 2;\n if (pa !== pb) return pa - pb;\n return a.ticket.created.localeCompare(b.ticket.created);\n });\n\n return results;\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport yaml from \"js-yaml\";\nimport type { Ticket } from \"../schemas/ticket.js\";\n\nexport interface SlaRule {\n priority: Ticket[\"priority\"];\n resolveDays: number;\n}\n\nconst DEFAULT_RULES: SlaRule[] = [\n { priority: \"urgent\", resolveDays: 1 },\n { priority: \"high\", resolveDays: 2 },\n { priority: \"normal\", resolveDays: 5 },\n { priority: \"low\", resolveDays: 10 },\n];\n\nexport function loadSlaRules(dataDir: string): SlaRule[] {\n const p = path.join(dataDir, \".agentic\", \"sla-rules.yaml\");\n if (!fs.existsSync(p)) return DEFAULT_RULES;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string) as { rules?: SlaRule[] };\n return raw?.rules ?? DEFAULT_RULES;\n } catch {\n return DEFAULT_RULES;\n }\n}\n\nfunction addDaysToDate(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.split(\"-\").map(Number) as [number, number, number];\n const d = new Date(Date.UTC(year, month - 1, day));\n d.setUTCDate(d.getUTCDate() + days);\n return d.toISOString().slice(0, 10);\n}\n\nexport function calcSlaDue(\n createdDate: string,\n priority: Ticket[\"priority\"],\n rules: SlaRule[]\n): string {\n const rule = rules.find((r) => r.priority === priority) ?? { resolveDays: 5 };\n return addDaysToDate(createdDate, rule.resolveDays);\n}\n\nexport function isSlaBreach(ticket: Ticket, today: string): boolean {\n if (ticket.status === \"resolved\" || ticket.status === \"closed\") return false;\n if (!ticket.slaDue) return false;\n return ticket.slaDue < today;\n}\n\nexport async function checkSlaBreaches(\n dataDir: string,\n today: string\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const { listAllTickets } = await import(\"../fs/ticket-writer.js\");\n const all = await listAllTickets(dataDir);\n return all.filter(({ ticket }) => isSlaBreach(ticket, today));\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readTickets, upsertTicket, nextTicketId } from \"../../fs/ticket-writer.js\";\nimport { calcSlaDue, loadSlaRules } from \"../../core/sla-engine.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCreateTicket(\n input: {\n slug: string;\n title: string;\n description?: string;\n priority?: Ticket[\"priority\"];\n assignee?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const today = new Date().toISOString().slice(0, 10);\n const rules = loadSlaRules(dataDir);\n const priority = input.priority ?? \"normal\";\n const existing = await readTickets(dataDir, input.slug);\n const id = nextTicketId(existing);\n const slaDue = calcSlaDue(today, priority, rules);\n\n const ticket: Ticket = {\n id,\n title: input.title,\n status: \"open\",\n priority,\n ...(input.assignee ? { assignee: input.assignee } : {}),\n created: today,\n slaDue,\n ...(input.description ? { description: input.description } : {}),\n };\n\n await upsertTicket(dataDir, input.slug, ticket);\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ ticket }, null, 2) }],\n };\n}\n\nexport function registerCreateTicket(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"create_ticket\",\n {\n description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.\nReturns: { ticket } with id T-NNN, status=open, slaDue`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n title: z.string().min(1).describe(\"Ticket title\"),\n description: z.string().optional().describe(\"Detailed description\"),\n priority: z\n .enum([\"urgent\", \"high\", \"normal\", \"low\"])\n .optional()\n .describe(\"Priority (default: normal)\"),\n assignee: z.string().optional().describe(\"Assignee name or email\"),\n }),\n },\n ({ slug, title, description, priority, assignee }) =>\n handleCreateTicket(\n {\n slug,\n title,\n ...(description !== undefined ? { description } : {}),\n ...(priority !== undefined ? { priority } : {}),\n ...(assignee !== undefined ? { assignee } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readTickets, upsertTicket } from \"../../fs/ticket-writer.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUpdateTicket(\n input: { slug: string; ticketId: string; status?: Ticket[\"status\"]; assignee?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tickets = await readTickets(dataDir, input.slug);\n const ticket = tickets.find((t) => t.id === input.ticketId);\n\n if (!ticket) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error: `Ticket '${input.ticketId}' not found for customer '${input.slug}'`,\n }),\n },\n ],\n };\n }\n\n const today = new Date().toISOString().slice(0, 10);\n const updated: Ticket = {\n ...ticket,\n ...(input.status ? { status: input.status } : {}),\n ...(input.assignee !== undefined ? { assignee: input.assignee } : {}),\n ...(input.status === \"resolved\" && !ticket.resolved ? { resolved: today } : {}),\n };\n\n await upsertTicket(dataDir, input.slug, updated);\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ ticket: updated }, null, 2) }],\n };\n}\n\nexport function registerUpdateTicket(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"update_ticket\",\n {\n description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.\nReturns: { ticket }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n ticketId: z.string().describe(\"Ticket ID (e.g. T-001)\"),\n status: z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]).optional(),\n assignee: z.string().optional().describe(\"New assignee\"),\n }),\n },\n ({ slug, ticketId, status, assignee }) =>\n handleUpdateTicket(\n {\n slug,\n ticketId,\n ...(status !== undefined ? { status } : {}),\n ...(assignee !== undefined ? { assignee } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { listAllTickets } from \"../../fs/ticket-writer.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListTickets(\n input: {\n slug?: string;\n status?: Ticket[\"status\"];\n priority?: Ticket[\"priority\"];\n assignee?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const results = await listAllTickets(dataDir, {\n ...(input.slug !== undefined ? { slug: input.slug } : {}),\n ...(input.status !== undefined ? { status: input.status } : {}),\n ...(input.priority !== undefined ? { priority: input.priority } : {}),\n ...(input.assignee !== undefined ? { assignee: input.assignee } : {}),\n });\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ tickets: results }, null, 2) }],\n };\n}\n\nexport function registerListTickets(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"list_tickets\",\n {\n description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.\nReturns: { tickets: Array<{ slug, ticket }> }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n status: z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]).optional(),\n priority: z.enum([\"urgent\", \"high\", \"normal\", \"low\"]).optional(),\n assignee: z.string().optional().describe(\"Filter by assignee\"),\n }),\n },\n ({ slug, status, priority, assignee }) =>\n handleListTickets(\n {\n ...(slug !== undefined ? { slug } : {}),\n ...(status !== undefined ? { status } : {}),\n ...(priority !== undefined ? { priority } : {}),\n ...(assignee !== undefined ? { assignee } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readTickets, upsertTicket } from \"../../fs/ticket-writer.js\";\nimport { appendInteraction } from \"../../fs/interactions-writer.js\";\nimport type { Ticket } from \"../../schemas/ticket.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCloseTicket(\n input: { slug: string; ticketId: string; resolution?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const tickets = await readTickets(dataDir, input.slug);\n const ticket = tickets.find((t) => t.id === input.ticketId);\n\n if (!ticket) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n error: `Ticket '${input.ticketId}' not found for '${input.slug}'`,\n }),\n },\n ],\n };\n }\n\n const today = new Date().toISOString().slice(0, 10);\n const updated: Ticket = {\n ...ticket,\n status: \"closed\",\n resolved: ticket.resolved ?? today,\n };\n\n await upsertTicket(dataDir, input.slug, updated);\n\n if (input.resolution) {\n await appendInteraction(dataDir, input.slug, {\n date: today,\n type: \"Note\",\n with: input.slug,\n summary: `Ticket ${input.ticketId} closed: ${input.resolution}`,\n nextSteps: [],\n sourceRef: `ticket://${input.ticketId}/close`,\n synced: new Date().toISOString(),\n });\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ ticket: updated }, null, 2) }],\n };\n}\n\nexport function registerCloseTicket(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"close_ticket\",\n {\n description: `Close a support ticket. Optionally logs the resolution as an interaction.\nReturns: { ticket } with status=closed`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n ticketId: z.string().describe(\"Ticket ID (e.g. T-001)\"),\n resolution: z.string().optional().describe(\"Resolution notes (logged as interaction)\"),\n }),\n },\n ({ slug, ticketId, resolution }) =>\n handleCloseTicket(\n {\n slug,\n ticketId,\n ...(resolution !== undefined ? { resolution } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const SurveyDefinitionSchema = z.object({\n id: z.string().min(1),\n type: z.enum([\"nps\", \"csat\", \"ces\"]).default(\"nps\"),\n question: z.string().min(1),\n scale: z\n .object({ min: z.number().default(0), max: z.number().default(10) })\n .default({ min: 0, max: 10 }),\n includeComment: z.boolean().default(true),\n commentPrompt: z.string().optional(),\n createdAt: z.string(),\n});\n\nexport const SurveyResponseSchema = z.object({\n surveyId: z.string(),\n slug: z.string(),\n contactEmail: z.string().email(),\n score: z.number().int(),\n comment: z.string().optional(),\n respondedAt: z.string(),\n token: z.string(),\n sentAt: z.string(),\n});\n\nexport type SurveyDefinition = z.infer<typeof SurveyDefinitionSchema>;\nexport type SurveyResponse = z.infer<typeof SurveyResponseSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\nimport { createHmac } from \"crypto\";\nimport yaml from \"js-yaml\";\nimport {\n SurveyDefinitionSchema,\n SurveyResponseSchema,\n type SurveyDefinition,\n type SurveyResponse,\n} from \"../schemas/survey.js\";\n\nconst SURVEY_SECRET = process.env[\"DXCRM_SURVEY_SECRET\"] ?? \"dxcrm-survey-default-secret\";\n\nexport function surveysDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"surveys\");\n}\n\nexport function responsesDir(dataDir: string, surveyId: string): string {\n return path.join(dataDir, \".agentic\", \"survey-responses\", surveyId);\n}\n\nexport function getSurvey(dataDir: string, surveyId: string): SurveyDefinition | null {\n const p = path.join(surveysDir(dataDir), `${surveyId}.yaml`);\n if (!fs.existsSync(p)) return null;\n try {\n const raw = yaml.load(fs.readFileSync(p, \"utf-8\") as string);\n const result = SurveyDefinitionSchema.safeParse(raw);\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\nexport function writeSurvey(dataDir: string, survey: SurveyDefinition): void {\n const dir = surveysDir(dataDir);\n fs.mkdirSync(dir, { recursive: true });\n writeFileAtomic(path.join(dir, `${survey.id}.yaml`), yaml.dump(survey));\n}\n\nexport function listSurveys(dataDir: string): SurveyDefinition[] {\n const dir = surveysDir(dataDir);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".yaml\"))\n .flatMap((f) => {\n const s = getSurvey(dataDir, f.replace(/\\.yaml$/, \"\"));\n return s ? [s] : [];\n });\n}\n\nexport function generateSurveyToken(slug: string, contactEmail: string, surveyId: string): string {\n return createHmac(\"sha256\", SURVEY_SECRET)\n .update(`${slug}:${contactEmail}:${surveyId}`)\n .digest(\"hex\")\n .slice(0, 16);\n}\n\nexport function buildSurveyEmail(\n survey: SurveyDefinition,\n serverUrl: string,\n token: string\n): { subject: string; body: string } {\n const scores = Array.from(\n { length: survey.scale.max - survey.scale.min + 1 },\n (_, i) => i + survey.scale.min\n );\n const buttons = scores\n .map(\n (s) =>\n `<a href=\"${serverUrl}/survey/respond?token=${token}&score=${s}\" style=\"display:inline-block;margin:4px;padding:10px 16px;background:#1a1a2e;color:white;text-decoration:none;border-radius:4px;\">${s}</a>`\n )\n .join(\"\");\n\n const body = `<p>${survey.question}</p>\n<p>${buttons}</p>\n${survey.includeComment ? `<p>Or <a href=\"${serverUrl}/survey/respond?token=${token}&comment=true\">Click here to add a comment</a></p>` : \"\"}`;\n\n return {\n subject: survey.type === \"nps\" ? \"How likely are you to recommend us?\" : \"Rate your experience\",\n body,\n };\n}\n\nexport async function recordSurveyResponse(\n dataDir: string,\n token: string,\n score: number,\n comment?: string\n): Promise<SurveyResponse | null> {\n // Find pending response by token\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n if (!fs.existsSync(pendingDir)) return null;\n\n const files = fs.readdirSync(pendingDir).filter((f) => f.endsWith(\".json\"));\n for (const file of files) {\n try {\n const pending = JSON.parse(\n fs.readFileSync(path.join(pendingDir, file), \"utf-8\") as string\n ) as {\n token: string;\n surveyId: string;\n slug: string;\n contactEmail: string;\n sentAt: string;\n };\n if (pending.token !== token) continue;\n\n const response: SurveyResponse = {\n surveyId: pending.surveyId,\n slug: pending.slug,\n contactEmail: pending.contactEmail,\n score,\n ...(comment ? { comment } : {}),\n respondedAt: new Date().toISOString(),\n token,\n sentAt: pending.sentAt,\n };\n\n const dir = responsesDir(dataDir, pending.surveyId);\n fs.mkdirSync(dir, { recursive: true });\n const filename = `${pending.slug}_${pending.contactEmail.replace(\"@\", \"_at_\")}_${Date.now()}.json`;\n writeFileAtomic(path.join(dir, filename), JSON.stringify(response, null, 2));\n\n // Delete pending entry\n fs.unlinkSync(path.join(pendingDir, file));\n return response;\n } catch {\n continue;\n }\n }\n return null;\n}\n\nexport function loadSurveyResponses(\n dataDir: string,\n surveyId: string,\n slug?: string\n): SurveyResponse[] {\n const dir = responsesDir(dataDir, surveyId);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".json\"))\n .flatMap((f) => {\n try {\n const raw = JSON.parse(fs.readFileSync(path.join(dir, f), \"utf-8\") as string) as unknown;\n const parsed = SurveyResponseSchema.safeParse(raw);\n if (!parsed.success) return [];\n if (slug && parsed.data.slug !== slug) return [];\n return [parsed.data];\n } catch {\n return [];\n }\n });\n}\n\nexport function calcNpsScore(responses: SurveyResponse[]): number {\n if (responses.length === 0) return 0;\n const promoters = responses.filter((r) => r.score >= 9).length;\n const detractors = responses.filter((r) => r.score <= 6).length;\n return Math.round(((promoters - detractors) / responses.length) * 100);\n}\n\nexport async function savePendingSurvey(\n dataDir: string,\n surveyId: string,\n slug: string,\n contactEmail: string,\n token: string\n): Promise<void> {\n const pendingDir = path.join(dataDir, \".agentic\", \"survey-pending\");\n fs.mkdirSync(pendingDir, { recursive: true });\n const filename = `${token}.json`;\n const pending = { token, surveyId, slug, contactEmail, sentAt: new Date().toISOString() };\n writeFileAtomic(path.join(pendingDir, filename), JSON.stringify(pending, null, 2));\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n getSurvey,\n generateSurveyToken,\n buildSurveyEmail,\n savePendingSurvey,\n} from \"../../core/survey-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSendNpsSurvey(\n input: { slug: string; contactEmail: string; surveyId: string; serverUrl?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const survey = getSurvey(dataDir, input.surveyId);\n if (!survey) {\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ error: `Survey '${input.surveyId}' not found` }) },\n ],\n };\n }\n\n const serverUrl = input.serverUrl ?? process.env[\"DXCRM_SERVER_URL\"] ?? \"http://localhost:3456\";\n const token = generateSurveyToken(input.slug, input.contactEmail, input.surveyId);\n const email = buildSurveyEmail(survey, serverUrl, token);\n\n await savePendingSurvey(dataDir, input.surveyId, input.slug, input.contactEmail, token);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n token,\n subject: email.subject,\n body: email.body,\n surveyUrl: `${serverUrl}/survey/respond?token=${token}`,\n note: \"Email draft ready. Use draft_email or Gmail to send.\",\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerSendNpsSurvey(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"send_nps_survey\",\n {\n description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.\nDoes NOT send automatically — returns draft for review.\nReturns: { token, subject, body, surveyUrl }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug\"),\n contactEmail: z.string().email().describe(\"Contact email to send survey to\"),\n surveyId: z.string().describe(\"Survey definition ID from .agentic/surveys/\"),\n serverUrl: z\n .string()\n .optional()\n .describe(\n \"Server URL for response links (default: DXCRM_SERVER_URL env var or localhost:3456)\"\n ),\n }),\n },\n ({ slug, contactEmail, surveyId, serverUrl }) =>\n handleSendNpsSurvey(\n {\n slug,\n contactEmail,\n surveyId,\n ...(serverUrl !== undefined ? { serverUrl } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { loadSurveyResponses, calcNpsScore } from \"../../core/survey-engine.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetSurveyResults(\n input: { surveyId: string; slug?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);\n const nps = calcNpsScore(responses);\n const promoters = responses.filter((r) => r.score >= 9).length;\n const passives = responses.filter((r) => r.score >= 7 && r.score <= 8).length;\n const detractors = responses.filter((r) => r.score <= 6).length;\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n surveyId: input.surveyId,\n ...(input.slug ? { slug: input.slug } : {}),\n totalResponses: responses.length,\n npsScore: nps,\n promoters,\n passives,\n detractors,\n responses: responses.map((r) => ({\n slug: r.slug,\n email: r.contactEmail,\n score: r.score,\n ...(r.comment ? { comment: r.comment } : {}),\n respondedAt: r.respondedAt,\n })),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerGetSurveyResults(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"get_survey_results\",\n {\n description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.\nReturns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,\n inputSchema: z.object({\n surveyId: z.string().describe(\"Survey ID\"),\n slug: z.string().optional().describe(\"Filter results to a specific customer\"),\n }),\n },\n ({ surveyId, slug }) =>\n handleGetSurveyResults(\n {\n surveyId,\n ...(slug !== undefined ? { slug } : {}),\n },\n dataDir\n )\n );\n}\n","import { z } from \"zod\";\n\nexport const KbArticleSchema = z.object({\n id: z.string().min(1),\n title: z.string().min(1),\n category: z.string().default(\"general\"),\n tags: z.array(z.string()).default([]),\n public: z.boolean().default(false),\n createdAt: z.string(),\n updatedAt: z.string(),\n sourceTicketId: z.string().optional(),\n});\n\nexport type KbArticleMeta = z.infer<typeof KbArticleSchema>;\nexport type KbArticle = KbArticleMeta & { body: string };\n","import fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\nimport { KbArticleSchema, type KbArticle, type KbArticleMeta } from \"../schemas/kb-article.js\";\nimport { writeFileAtomic } from \"./atomic-write.js\";\nimport { assertSafePathSegment, isSafePathSegment } from \"./safe-path.js\";\n\nexport function kbDir(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"knowledge-base\");\n}\n\n/** Category subdirectories of the knowledge base. */\nfunction kbCategories(dir: string): string[] {\n if (!fs.existsSync(dir)) return [];\n return fs.readdirSync(dir).filter((f) => {\n try {\n return fs.statSync(path.join(dir, f)).isDirectory();\n } catch {\n return false;\n }\n });\n}\n\nexport function listKbArticles(\n dataDir: string,\n opts?: { category?: string; publicOnly?: boolean }\n): KbArticle[] {\n const dir = kbDir(dataDir);\n const results: KbArticle[] = [];\n\n for (const cat of kbCategories(dir)) {\n const catDir = path.join(dir, cat);\n const files = fs.readdirSync(catDir).filter((f) => f.endsWith(\".md\"));\n for (const file of files) {\n try {\n const content = fs.readFileSync(path.join(catDir, file), \"utf-8\") as string;\n const parsed = matter(content);\n const meta = KbArticleSchema.safeParse(parsed.data);\n if (!meta.success) continue;\n if (opts?.category && meta.data.category !== opts.category) continue;\n if (opts?.publicOnly && !meta.data.public) continue;\n results.push({ ...meta.data, body: parsed.content.trim() });\n } catch {\n continue;\n }\n }\n }\n\n return results;\n}\n\nexport function getKbArticle(dataDir: string, id: string): KbArticle | null {\n if (!isSafePathSegment(id)) return null;\n // Articles are stored as <category>/<id>.md, so locate the file directly\n // instead of parsing the whole knowledge base to find one by id.\n const dir = kbDir(dataDir);\n for (const cat of kbCategories(dir)) {\n const filePath = path.join(dir, cat, `${id}.md`);\n if (!fs.existsSync(filePath)) continue;\n try {\n const parsed = matter(fs.readFileSync(filePath, \"utf-8\") as string);\n const meta = KbArticleSchema.safeParse(parsed.data);\n if (!meta.success) return null;\n return { ...meta.data, body: parsed.content.trim() };\n } catch {\n return null;\n }\n }\n return null;\n}\n\nexport function writeKbArticle(dataDir: string, article: KbArticle): void {\n assertSafePathSegment(article.category, \"knowledge-base category\");\n assertSafePathSegment(article.id, \"knowledge-base article id\");\n const { body, ...meta } = article;\n const content = matter.stringify(body, meta as Record<string, unknown>);\n writeFileAtomic(path.join(kbDir(dataDir), article.category, `${article.id}.md`), content);\n}\n\nexport function deleteKbArticle(dataDir: string, id: string): boolean {\n if (!isSafePathSegment(id)) return false;\n // Locate <category>/<id>.md directly rather than parsing every article.\n const dir = kbDir(dataDir);\n for (const cat of kbCategories(dir)) {\n const p = path.join(dir, cat, `${id}.md`);\n if (fs.existsSync(p)) {\n fs.unlinkSync(p);\n return true;\n }\n }\n return false;\n}\n\nexport function searchKbSimple(\n dataDir: string,\n query: string,\n opts?: { publicOnly?: boolean }\n): KbArticle[] {\n const all = listKbArticles(dataDir, opts?.publicOnly ? { publicOnly: true } : {});\n const lower = query.toLowerCase();\n return all.filter(\n (a) =>\n a.title.toLowerCase().includes(lower) ||\n a.body.toLowerCase().includes(lower) ||\n a.tags.some((t) => t.toLowerCase().includes(lower))\n );\n}\n\nexport function getKbMetaForExport(article: KbArticle): KbArticleMeta {\n const { body: _body, ...meta } = article;\n return meta;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { searchKbSimple, getKbMetaForExport } from \"../../fs/knowledge-base.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleSearchKnowledgeBase(\n input: { query: string; category?: string; publicOnly?: boolean; limit?: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const results = searchKbSimple(dataDir, input.query, {\n ...(input.publicOnly ? { publicOnly: true } : {}),\n });\n\n const filtered = input.category ? results.filter((a) => a.category === input.category) : results;\n const limited = filtered.slice(0, input.limit ?? 10);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n query: input.query,\n count: limited.length,\n articles: limited.map((a) => ({\n ...getKbMetaForExport(a),\n excerpt: a.body.slice(0, 300).trim(),\n })),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerSearchKnowledgeBase(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"search_knowledge_base\",\n {\n description: `Search the knowledge base for articles. Text search on title, body, and tags.\nReturns: { count, articles[] } with excerpts`,\n inputSchema: z.object({\n query: z.string().describe(\"Search query\"),\n category: z\n .string()\n .optional()\n .describe(\"Filter by category (e.g. 'troubleshooting', 'howto')\"),\n publicOnly: z.boolean().optional().describe(\"Only return public articles\"),\n limit: z.number().int().positive().optional().describe(\"Max results (default 10)\"),\n }),\n },\n ({ query, category, publicOnly, limit }) =>\n handleSearchKnowledgeBase(\n {\n query,\n ...(category !== undefined ? { category } : {}),\n ...(publicOnly !== undefined ? { publicOnly } : {}),\n ...(limit !== undefined ? { limit } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { writeKbArticle, getKbArticle } from \"../../fs/knowledge-base.js\";\nimport type { KbArticle } from \"../../schemas/kb-article.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleCreateKbArticle(\n input: {\n id: string;\n title: string;\n body: string;\n category?: string;\n tags?: string[];\n public?: boolean;\n sourceTicketId?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const existing = getKbArticle(dataDir, input.id);\n if (existing) {\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ error: `Article '${input.id}' already exists` }) },\n ],\n };\n }\n\n const now = new Date().toISOString();\n const article: KbArticle = {\n id: input.id,\n title: input.title,\n body: input.body,\n category: input.category ?? \"general\",\n tags: input.tags ?? [],\n public: input.public ?? false,\n createdAt: now,\n updatedAt: now,\n ...(input.sourceTicketId ? { sourceTicketId: input.sourceTicketId } : {}),\n };\n\n writeKbArticle(dataDir, article);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n id: article.id,\n title: article.title,\n category: article.category,\n path: `.agentic/knowledge-base/${article.category}/${article.id}.md`,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerCreateKbArticle(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"create_kb_article\",\n {\n description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.\nReturns: { id, title, category, path }`,\n inputSchema: z.object({\n id: z.string().min(1).describe(\"Article ID (slug, e.g. 'troubleshoot-api-timeout')\"),\n title: z.string().min(1).describe(\"Article title\"),\n body: z.string().min(1).describe(\"Article body in Markdown\"),\n category: z.string().optional().describe(\"Category (default: 'general')\"),\n tags: z.array(z.string()).optional().describe(\"Tags for search\"),\n public: z\n .boolean()\n .optional()\n .describe(\"Make article publicly accessible (default: false)\"),\n sourceTicketId: z.string().optional().describe(\"Ticket ID this article was created from\"),\n }),\n },\n ({ id, title, body, category, tags, public: pub, sourceTicketId }) =>\n handleCreateKbArticle(\n {\n id,\n title,\n body,\n ...(category !== undefined ? { category } : {}),\n ...(tags !== undefined ? { tags } : {}),\n ...(pub !== undefined ? { public: pub } : {}),\n ...(sourceTicketId !== undefined ? { sourceTicketId } : {}),\n },\n dataDir\n )\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { runBackup } from \"../../commands/backup.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleBackupNow(\n input: { remote?: string; note?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const zipPath = path.join(\n dataDir,\n `dxcrm-backup-${new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19)}.zip`\n );\n\n const manifest = await runBackup(zipPath, dataDir, {\n ...(input.remote ? { remote: input.remote } : {}),\n }).catch(() => null);\n\n if (!manifest) {\n return {\n content: [{ type: \"text\", text: \"Backup failed. Check disk space and permissions.\" }],\n };\n }\n\n const sizeMb = fs.existsSync(zipPath)\n ? (fs.statSync(zipPath).size / 1024 / 1024).toFixed(1)\n : \"?\";\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n path: zipPath,\n createdAt: manifest.createdAt,\n customerCount: manifest.customerCount,\n fileCount: manifest.fileCount,\n sizeMb: `${sizeMb} MB`,\n directories: manifest.directories,\n verified: true,\n ...(input.remote ? { uploadedTo: input.remote } : {}),\n ...(input.note ? { note: input.note } : {}),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerBackupNow(server: McpServer): void {\n server.registerTool(\n \"backup_now\",\n {\n description:\n \"Trigger an immediate backup of all CRM data (customers/ + .agentic/). Returns backup path, size, and integrity status. Use before risky operations or on user request.\",\n inputSchema: z.object({\n remote: z\n .string()\n .optional()\n .describe(\n \"Upload destination: s3://bucket/path/, rsync://user@host:/path/, or local directory\"\n ),\n note: z.string().optional().describe(\"Optional note to tag this backup\"),\n }),\n },\n ({ remote, note }) =>\n handleBackupNow({\n ...(remote !== undefined ? { remote } : {}),\n ...(note !== undefined ? { note } : {}),\n })\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readBackupLog, listBackupsInDir } from \"../../commands/backup.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleListBackups(\n input: { limit: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const logEntries = readBackupLog(dataDir);\n const fileEntries = listBackupsInDir(dataDir);\n\n // Prefer log entries (have richer metadata), fallback to file scan\n const entries = logEntries.length > 0 ? logEntries : fileEntries;\n const limited = entries.slice(0, input.limit);\n\n if (limited.length === 0) {\n return {\n content: [{ type: \"text\", text: \"No backups found. Run backup_now to create one.\" }],\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n count: limited.length,\n totalAvailable: entries.length,\n backups: limited.map((e) => ({\n filename: e.filename,\n createdAt: e.createdAt,\n sizeMb: e.sizeBytes > 0 ? `${(e.sizeBytes / 1024 / 1024).toFixed(1)} MB` : \"unknown\",\n verified: e.verified,\n encrypted: e.encrypted,\n customerCount: e.customerCount,\n fileCount: e.fileCount,\n })),\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerListBackups(server: McpServer): void {\n server.registerTool(\n \"list_backups\",\n {\n description:\n \"List available CRM backups with metadata (date, size, verification status, customer count). Shows log-tracked backups first, falls back to directory scan.\",\n inputSchema: z.object({\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .default(10)\n .describe(\"Maximum number of backups to return\"),\n }),\n },\n (input) => handleListBackups(input)\n );\n}\n","import path from \"path\";\nimport fs from \"fs\";\nimport { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { getGmailAuth } from \"../../core/oauth-store.js\";\nimport { updateSlugSyncState } from \"../../fs/sync-state.js\";\n\nconst DATA_DIR = process.cwd();\n\nexport async function handleTriggerSync(\n input: { slug?: string; since?: string },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const auth = getGmailAuth();\n if (!auth) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({\n success: false,\n error: \"Gmail auth not configured. Run `dxcrm sync gmail --init` first.\",\n }),\n },\n ],\n };\n }\n\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ success: true, synced: 0, skipped: 0, customers: [] }),\n },\n ],\n };\n }\n\n const slugs = input.slug\n ? [input.slug]\n : fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const sinceDate = input.since\n ? new Date(input.since)\n : new Date(Date.now() - 24 * 60 * 60 * 1000);\n const results: Array<{ slug: string; synced: number; skipped: number }> = [];\n const errors: string[] = [];\n\n for (const slug of slugs) {\n const sourcesPath = path.join(customersDir, slug, \"sources.json\");\n if (!fs.existsSync(sourcesPath)) continue;\n try {\n const sources = JSON.parse(fs.readFileSync(sourcesPath, \"utf-8\") as string) as {\n gmail?: { query?: string; enabled?: boolean };\n };\n if (!sources.gmail?.enabled || !sources.gmail.query) continue;\n\n const { syncGmail } = await import(\"../../sync/gmail-sync.js\");\n const result = await syncGmail({\n slug,\n dataDir,\n auth,\n query: sources.gmail.query,\n since: sinceDate,\n });\n updateSlugSyncState(dataDir, slug, { lastGmailSync: new Date().toISOString() });\n results.push({ slug, ...result });\n } catch (err) {\n errors.push(`${slug}: ${(err as Error).message}`);\n }\n }\n\n const total = results.reduce(\n (acc, r) => ({ synced: acc.synced + r.synced, skipped: acc.skipped + r.skipped }),\n { synced: 0, skipped: 0 }\n );\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n ...total,\n customers: results,\n errors,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerTriggerSync(server: McpServer): void {\n server.registerTool(\n \"trigger_sync\",\n {\n title: \"Trigger Sync\",\n description: `Immediately trigger a Gmail sync for one or all customers.\nUse when you need fresh email data before answering a question.\nThe background daemon syncs every 30 minutes automatically — this forces an immediate sync.\n\nArgs:\n slug: Customer slug to sync (leave empty to sync all customers)\n since: ISO date string — only fetch emails since this date (default: last 24 hours)\n\nReturns: { success: boolean, synced: number, skipped: number, customers: [...], errors: [...] }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Customer slug to sync (empty = all customers)\"),\n since: z\n .string()\n .optional()\n .describe(\"Sync emails since this ISO date (default: last 24h)\"),\n }),\n },\n async ({ slug, since }) => {\n const input: { slug?: string; since?: string } = {};\n if (slug !== undefined) input.slug = slug;\n if (since !== undefined) input.since = since;\n return handleTriggerSync(input);\n }\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { readAuditLog, filterAuditLog } from \"../../fs/audit-log.js\";\n\nconst DATA_DIR = process.cwd();\n\nexport async function handleGetAuditLog(\n input: { slug?: string; actor?: string; limit?: number },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const entries = readAuditLog(dataDir);\n const filterOpts: { slug?: string; actor?: string; limit?: number } = {\n limit: input.limit ?? 50,\n };\n if (input.slug !== undefined) filterOpts.slug = input.slug;\n if (input.actor !== undefined) filterOpts.actor = input.actor;\n const filtered = filterAuditLog(entries, filterOpts);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n total: entries.length,\n returned: filtered.length,\n entries: filtered,\n },\n null,\n 2\n ),\n },\n ],\n };\n}\n\nexport function registerGetAuditLog(server: McpServer): void {\n server.registerTool(\n \"get_audit_log\",\n {\n title: \"Get Audit Log\",\n description: `Read the CRM audit log — all write operations with timestamp, actor, tool, and customer.\nUse to answer \"what changed recently?\", \"what did alice do?\", or \"show me all actions for acme-corp\".\n\nArgs:\n slug: Filter by customer slug (optional)\n actor: Filter by actor name (optional)\n limit: Max entries to return (default: 50, most recent)\n\nReturns: { total: number, returned: number, entries: [{timestamp, actor, tool, slug, summary}] }`,\n inputSchema: z.object({\n slug: z.string().optional().describe(\"Filter by customer slug\"),\n actor: z.string().optional().describe(\"Filter by actor (user or system)\"),\n limit: z.number().int().min(1).max(500).optional().describe(\"Max entries (default 50)\"),\n }),\n },\n async ({ slug, actor, limit }) => {\n const input: { slug?: string; actor?: string; limit?: number } = {};\n if (slug !== undefined) input.slug = slug;\n if (actor !== undefined) input.actor = actor;\n if (limit !== undefined) input.limit = limit;\n return handleGetAuditLog(input);\n }\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { queryLogs, summarizeLogs, type LogLevel } from \"../../core/logger.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleGetLogs(\n input: {\n level?: LogLevel;\n component?: string;\n since?: string;\n contains?: string;\n limit?: number;\n summary?: boolean;\n },\n dataDir: string = DATA_DIR\n): Promise<{ content: Array<{ type: \"text\"; text: string }> }> {\n const query = {\n ...(input.level !== undefined ? { level: input.level } : {}),\n ...(input.component !== undefined ? { component: input.component } : {}),\n ...(input.since !== undefined ? { since: input.since } : {}),\n ...(input.contains !== undefined ? { contains: input.contains } : {}),\n limit: input.limit ?? 100,\n };\n\n const payload = input.summary\n ? summarizeLogs(dataDir, query)\n : (() => {\n const entries = queryLogs(dataDir, query);\n return { returned: entries.length, entries };\n })();\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(payload, null, 2) }],\n };\n}\n\nexport function registerGetLogs(server: McpServer): void {\n server.registerTool(\n \"get_logs\",\n {\n title: \"Get Logs\",\n description: `Read and analyze the structured application log (.agentic/logs.ndjson).\nUse to answer \"what went wrong recently?\", \"show errors from gmail sync\", or \"summarize today's activity\".\n\nArgs:\n level: Minimum level to include — debug | info | warn | error (optional)\n component: Filter by component, e.g. \"gmail-sync\", \"lancedb\" (optional)\n since: ISO timestamp; only entries at or after it (optional)\n contains: Case-insensitive substring of the message (optional)\n limit: Max entries to return (default 100, most recent)\n summary: When true, return aggregated counts (by level + component) and recent errors instead of raw entries\n\nReturns (entries): { returned: number, entries: [{ts, level, component, message, context?}] }\nReturns (summary): { total, byLevel, byComponent, firstTs, lastTs, recentErrors }`,\n inputSchema: z.object({\n level: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).optional().describe(\"Minimum level\"),\n component: z.string().optional().describe(\"Filter by component\"),\n since: z.string().optional().describe(\"ISO timestamp lower bound\"),\n contains: z.string().optional().describe(\"Message substring filter\"),\n limit: z.number().int().min(1).max(1000).optional().describe(\"Max entries (default 100)\"),\n summary: z.boolean().optional().describe(\"Return aggregated summary instead of entries\"),\n }),\n },\n async ({ level, component, since, contains, limit, summary }) => {\n const input: Parameters<typeof handleGetLogs>[0] = {};\n if (level !== undefined) input.level = level;\n if (component !== undefined) input.component = component;\n if (since !== undefined) input.since = since;\n if (contains !== undefined) input.contains = contains;\n if (limit !== undefined) input.limit = limit;\n if (summary !== undefined) input.summary = summary;\n return handleGetLogs(input);\n }\n );\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\n\nexport interface CrmPrompt {\n name: string;\n title: string;\n description: string;\n build: (args: { slug: string }) => string;\n}\n\n/**\n * CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.\n * Each renders an actionable, tool-referencing instruction for the host LLM —\n * the agent-native equivalent of a Salesforce \"playbook\".\n */\nexport const CRM_PROMPTS: CrmPrompt[] = [\n {\n name: \"deal_risk_review\",\n title: \"Assess deal risk\",\n description:\n \"Evaluate the health and risk of a customer's open deals and recommend next steps.\",\n build: ({ slug }) =>\n `Assess the deal risk for customer \"${slug}\".\\n` +\n `1. Call open_deal_room({ slug: \"${slug}\" }) for a consolidated brief, or get_customer_context + get_deal_health.\\n` +\n `2. Identify stalled deals, approaching close dates, and silent champions (get_relationship_health).\\n` +\n `3. Summarise the top risks and recommend concrete next actions. Do not invent data — cite what you read.`,\n },\n {\n name: \"draft_follow_up\",\n title: \"Draft a follow-up email\",\n description:\n \"Draft a personalized follow-up email for a customer based on recent interactions.\",\n build: ({ slug }) =>\n `Draft a follow-up email for customer \"${slug}\".\\n` +\n `1. Read recent context with get_customer_context({ slug: \"${slug}\" }).\\n` +\n `2. Use draft_email({ slug: \"${slug}\", templateId, tone: \"friendly\" }) with an appropriate template.\\n` +\n `3. Reference the latest interaction concretely; keep it concise. Return the draft for review — do not send.`,\n },\n {\n name: \"account_brief\",\n title: \"Create an account brief\",\n description: \"Produce a concise executive brief for a customer account.\",\n build: ({ slug }) =>\n `Create an executive account brief for \"${slug}\".\\n` +\n `1. get_customer_context({ slug: \"${slug}\" }) and get_org_intelligence({ slug: \"${slug}\" }).\\n` +\n `2. Summarise: who the stakeholders are (champions/buyers/blockers), open pipeline, health, and risks.\\n` +\n `3. End with the single most important next action.`,\n },\n {\n name: \"pipeline_summary\",\n title: \"Summarize the pipeline\",\n description: \"Summarize pipeline and forecast, optionally focused on one customer.\",\n build: ({ slug }) =>\n `Summarize the sales pipeline (focus customer: \"${slug}\").\\n` +\n `1. get_pipeline_forecast() for the weighted total and per-stage breakdown.\\n` +\n `2. simulate_revenue() for P10/P50/P90 if a probabilistic view helps.\\n` +\n `3. Highlight at-risk revenue and the deals that most move the forecast.`,\n },\n];\n\nexport function registerPrompts(server: McpServer): void {\n for (const prompt of CRM_PROMPTS) {\n server.registerPrompt(\n prompt.name,\n {\n title: prompt.title,\n description: prompt.description,\n argsSchema: { slug: z.string().describe(\"Customer slug\") },\n },\n ({ slug }) => ({\n messages: [{ role: \"user\", content: { type: \"text\", text: prompt.build({ slug }) } }],\n })\n );\n }\n}\n","import { type McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\n/**\n * Read-only MCP Resources for CRM entities. Complements the action Tools:\n * agents can `resources/read` a customer briefing, pipeline or timeline by URI\n * instead of calling a tool.\n */\nexport function registerResources(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerResource(\n \"customers\",\n \"crm://customers\",\n {\n title: \"Customers\",\n description: \"List of all customer slugs\",\n mimeType: \"application/json\",\n },\n (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(listCustomerSlugs(dataDir), null, 2),\n },\n ],\n })\n );\n\n server.registerResource(\n \"customer\",\n new ResourceTemplate(\"crm://customer/{slug}\", { list: undefined }),\n {\n title: \"Customer context\",\n description: \"LLM-ready briefing (main facts, recent interactions, pipeline) for a customer\",\n mimeType: \"text/markdown\",\n },\n async (uri, variables) => {\n const { buildContext } = await import(\"../core/context-builder.js\");\n const text = await buildContext(dataDir, String(variables[\"slug\"]));\n return { contents: [{ uri: uri.href, mimeType: \"text/markdown\", text }] };\n }\n );\n\n server.registerResource(\n \"pipeline\",\n new ResourceTemplate(\"crm://pipeline/{slug}\", { list: undefined }),\n {\n title: \"Pipeline\",\n description: \"Open and closed deals for a customer\",\n mimeType: \"application/json\",\n },\n async (uri, variables) => {\n const { readPipeline } = await import(\"../fs/pipeline-writer.js\");\n const deals = await readPipeline(dataDir, String(variables[\"slug\"]));\n return {\n contents: [\n { uri: uri.href, mimeType: \"application/json\", text: JSON.stringify(deals, null, 2) },\n ],\n };\n }\n );\n\n server.registerResource(\n \"timeline\",\n new ResourceTemplate(\"crm://timeline/{slug}\", { list: undefined }),\n {\n title: \"Interaction timeline\",\n description: \"Newest-first interaction history for a customer\",\n mimeType: \"text/markdown\",\n },\n async (uri, variables) => {\n const { readInteractions } = await import(\"../fs/interactions-writer.js\");\n const text = await readInteractions(dataDir, String(variables[\"slug\"]));\n return { contents: [{ uri: uri.href, mimeType: \"text/markdown\", text }] };\n }\n );\n}\n","import path from \"path\";\nimport { readJsonArray, writeJsonArray } from \"../fs/json-store.js\";\n\n/**\n * Metadata-driven custom fields — the first increment of the metadata model\n * (next-plan N1-7). Definitions live in .agentic/schema/custom-fields.json and\n * extend customers without code changes. Core schemas stay strict; custom\n * fields are validated separately against this registry.\n */\nexport type CustomFieldType = \"text\" | \"number\" | \"boolean\" | \"date\" | \"select\";\n\nexport interface FieldDefinition {\n name: string;\n type: CustomFieldType;\n label?: string;\n options?: string[];\n}\n\nfunction schemaPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"schema\", \"custom-fields.json\");\n}\n\nexport function loadFieldDefinitions(dataDir: string): FieldDefinition[] {\n return readJsonArray<FieldDefinition>(schemaPath(dataDir), \"fields\");\n}\n\n/** Add or update (by name) a custom field definition. */\nexport function defineCustomField(dataDir: string, def: FieldDefinition): FieldDefinition[] {\n const defs = loadFieldDefinitions(dataDir);\n const idx = defs.findIndex((d) => d.name === def.name);\n if (idx >= 0) defs[idx] = def;\n else defs.push(def);\n writeJsonArray(schemaPath(dataDir), \"fields\", defs);\n return defs;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n values: Record<string, string | number | boolean>;\n errors: string[];\n}\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/** Validate + coerce a record of raw values against custom field definitions. */\nexport function validateCustomFields(\n input: Record<string, unknown>,\n defs: FieldDefinition[]\n): ValidationResult {\n const byName = new Map(defs.map((d) => [d.name, d]));\n const values: Record<string, string | number | boolean> = {};\n const errors: string[] = [];\n\n for (const [key, raw] of Object.entries(input)) {\n const def = byName.get(key);\n if (!def) {\n errors.push(`Unknown custom field: ${key}`);\n continue;\n }\n const str = String(raw).trim();\n switch (def.type) {\n case \"number\": {\n const n = Number(str);\n if (!Number.isFinite(n)) errors.push(`${key}: not a number`);\n else values[key] = n;\n break;\n }\n case \"boolean\": {\n if (/^(true|yes|1)$/i.test(str)) values[key] = true;\n else if (/^(false|no|0)$/i.test(str)) values[key] = false;\n else errors.push(`${key}: not a boolean`);\n break;\n }\n case \"date\": {\n if (DATE_RE.test(str)) values[key] = str;\n else errors.push(`${key}: expected YYYY-MM-DD`);\n break;\n }\n case \"select\": {\n if (def.options && def.options.includes(str)) values[key] = str;\n else errors.push(`${key}: must be one of ${(def.options ?? []).join(\", \")}`);\n break;\n }\n default:\n values[key] = str;\n }\n }\n\n return { valid: errors.length === 0, values, errors };\n}\n","import { randomBytes } from \"crypto\";\nimport path from \"path\";\nimport { validateCustomFields, type FieldDefinition } from \"./custom-fields.js\";\nimport { readJsonArray, writeJsonArray } from \"../fs/json-store.js\";\nimport { assertSafePathSegment } from \"../fs/safe-path.js\";\n\n/**\n * Custom objects — runtime-defined entity types with their own fields, stored\n * as JSON without code migrations (Twenty-style \"no-migration\" model, the\n * N5-1 increment of the metadata layer). Definitions live in\n * .agentic/schema/custom-objects.json; records in .agentic/objects/<name>.json.\n */\nexport interface ObjectDefinition {\n name: string;\n label?: string;\n fields: FieldDefinition[];\n}\n\nexport interface ObjectRecord {\n id: string;\n createdAt: string;\n updatedAt: string;\n values: Record<string, string | number | boolean>;\n}\n\nexport interface RecordResult {\n ok: boolean;\n record?: ObjectRecord;\n errors?: string[];\n}\n\nfunction objectsSchemaPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"schema\", \"custom-objects.json\");\n}\nfunction recordsPath(dataDir: string, name: string): string {\n assertSafePathSegment(name, \"custom object name\");\n return path.join(dataDir, \".agentic\", \"objects\", `${name}.json`);\n}\n\nexport function loadCustomObjects(dataDir: string): ObjectDefinition[] {\n return readJsonArray<ObjectDefinition>(objectsSchemaPath(dataDir), \"objects\");\n}\n\nexport function getObjectDefinition(dataDir: string, name: string): ObjectDefinition | undefined {\n return loadCustomObjects(dataDir).find((o) => o.name === name);\n}\n\n/** Add or update (by name) a custom object definition. */\nexport function defineCustomObject(dataDir: string, def: ObjectDefinition): ObjectDefinition[] {\n assertSafePathSegment(def.name, \"custom object name\");\n const objs = loadCustomObjects(dataDir);\n const idx = objs.findIndex((o) => o.name === def.name);\n if (idx >= 0) objs[idx] = def;\n else objs.push(def);\n writeJsonArray(objectsSchemaPath(dataDir), \"objects\", objs);\n return objs;\n}\n\nexport function listRecords(dataDir: string, name: string): ObjectRecord[] {\n return readJsonArray<ObjectRecord>(recordsPath(dataDir, name), \"records\");\n}\n\nfunction writeRecords(dataDir: string, name: string, records: ObjectRecord[]): void {\n writeJsonArray(recordsPath(dataDir, name), \"records\", records);\n}\n\nexport function getRecord(dataDir: string, name: string, id: string): ObjectRecord | undefined {\n return listRecords(dataDir, name).find((r) => r.id === id);\n}\n\nexport function createRecord(\n dataDir: string,\n name: string,\n values: Record<string, unknown>\n): RecordResult {\n const def = getObjectDefinition(dataDir, name);\n if (!def) return { ok: false, errors: [`Unknown object: ${name}`] };\n\n const validation = validateCustomFields(values, def.fields);\n if (!validation.valid) return { ok: false, errors: validation.errors };\n\n const now = new Date().toISOString();\n const record: ObjectRecord = {\n id: `${name}_${randomBytes(6).toString(\"hex\")}`,\n createdAt: now,\n updatedAt: now,\n values: validation.values,\n };\n writeRecords(dataDir, name, [...listRecords(dataDir, name), record]);\n return { ok: true, record };\n}\n\nexport function updateRecord(\n dataDir: string,\n name: string,\n id: string,\n values: Record<string, unknown>\n): RecordResult {\n const def = getObjectDefinition(dataDir, name);\n if (!def) return { ok: false, errors: [`Unknown object: ${name}`] };\n\n const records = listRecords(dataDir, name);\n const idx = records.findIndex((r) => r.id === id);\n if (idx < 0) return { ok: false, errors: [`Record not found: ${id}`] };\n\n const validation = validateCustomFields(values, def.fields);\n if (!validation.valid) return { ok: false, errors: validation.errors };\n\n const updated: ObjectRecord = {\n ...records[idx]!,\n updatedAt: new Date().toISOString(),\n values: { ...records[idx]!.values, ...validation.values },\n };\n records[idx] = updated;\n writeRecords(dataDir, name, records);\n return { ok: true, record: updated };\n}\n\nexport function deleteRecord(dataDir: string, name: string, id: string): boolean {\n const records = listRecords(dataDir, name);\n const next = records.filter((r) => r.id !== id);\n if (next.length === records.length) return false;\n writeRecords(dataDir, name, next);\n return true;\n}\n","import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { enforceRbac } from \"../../core/rbac.js\";\nimport {\n defineCustomObject,\n loadCustomObjects,\n createRecord,\n listRecords,\n} from \"../../core/custom-objects.js\";\nimport type { CustomFieldType } from \"../../core/custom-fields.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nconst FIELD_TYPES = [\"text\", \"number\", \"boolean\", \"date\", \"select\"] as const;\n\nfunction json(data: unknown): { content: Array<{ type: \"text\"; text: string }> } {\n return { content: [{ type: \"text\", text: JSON.stringify(data, null, 2) }] };\n}\n\nexport function handleDefineCustomObject(\n input: {\n name: string;\n label?: string;\n fields: Array<{ name: string; type: CustomFieldType; label?: string; options?: string[] }>;\n },\n dataDir: string = DATA_DIR\n): { content: Array<{ type: \"text\"; text: string }> } {\n enforceRbac(dataDir, \"define_custom_object\");\n const objects = defineCustomObject(dataDir, {\n name: input.name,\n ...(input.label ? { label: input.label } : {}),\n fields: input.fields,\n });\n return json({ defined: input.name, objectCount: objects.length });\n}\n\nexport function handleCreateRecord(\n input: { object: string; values: Record<string, string> },\n dataDir: string = DATA_DIR\n): { content: Array<{ type: \"text\"; text: string }> } {\n enforceRbac(dataDir, \"create_record\");\n const res = createRecord(dataDir, input.object, input.values);\n if (!res.ok) return json({ error: (res.errors ?? []).join(\"; \") });\n // Fire-and-forget outbound webhook (event-driven; failures queue for replay).\n void import(\"../../core/webhooks.js\").then(({ emitEvent }) =>\n emitEvent(dataDir, \"record.created\", { object: input.object, record: res.record })\n );\n return json({ record: res.record });\n}\n\nexport function handleListRecords(\n input: { object: string },\n dataDir: string = DATA_DIR\n): { content: Array<{ type: \"text\"; text: string }> } {\n return json({ object: input.object, records: listRecords(dataDir, input.object) });\n}\n\nexport function handleListCustomObjects(dataDir: string = DATA_DIR): {\n content: Array<{ type: \"text\"; text: string }>;\n} {\n return json({ objects: loadCustomObjects(dataDir) });\n}\n\nexport function registerCustomObjectTools(server: McpServer, dataDir: string = DATA_DIR): void {\n server.registerTool(\n \"define_custom_object\",\n {\n description:\n \"Define a custom object (runtime entity type) with typed fields — no code migration. admin only.\",\n inputSchema: z.object({\n name: z.string().describe(\"Object name (e.g. contract)\"),\n label: z.string().optional(),\n fields: z\n .array(\n z.object({\n name: z.string(),\n type: z.enum(FIELD_TYPES),\n label: z.string().optional(),\n options: z.array(z.string()).optional(),\n })\n )\n .describe(\"Field definitions\"),\n }),\n },\n ({ name, label, fields }) =>\n handleDefineCustomObject(\n { name, ...(label ? { label } : {}), fields: fields as never },\n dataDir\n )\n );\n\n server.registerTool(\n \"create_record\",\n {\n description:\n \"Create a record of a custom object. Values are validated against the schema. rep+.\",\n inputSchema: z.object({\n object: z.string().describe(\"Custom object name\"),\n values: z.record(z.string()).describe(\"Field values (key=value)\"),\n }),\n },\n ({ object, values }) => handleCreateRecord({ object, values }, dataDir)\n );\n\n server.registerTool(\n \"list_records\",\n {\n description: \"List records of a custom object.\",\n inputSchema: z.object({ object: z.string().describe(\"Custom object name\") }),\n },\n ({ object }) => handleListRecords({ object }, dataDir)\n );\n\n server.registerTool(\n \"list_custom_objects\",\n {\n description: \"List all defined custom objects and their field schemas.\",\n inputSchema: z.object({}),\n },\n () => handleListCustomObjects(dataDir)\n );\n}\n","import { createHash, randomBytes, timingSafeEqual } from \"crypto\";\nimport path from \"path\";\nimport { readJsonArray, writeJsonArray } from \"../fs/json-store.js\";\n\nexport type McpRole = \"admin\" | \"manager\" | \"rep\";\n\nexport interface McpTokenRecord {\n hash: string;\n actor: string;\n role: McpRole;\n label?: string;\n createdAt?: string;\n}\n\nexport interface AuthResult {\n ok: boolean;\n actor?: string;\n role?: McpRole;\n}\n\nfunction tokensPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"mcp-tokens.json\");\n}\n\n/** SHA-256 hex of a token. Only hashes are ever persisted. */\nexport function hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\nexport function loadMcpTokens(dataDir: string): McpTokenRecord[] {\n return readJsonArray<McpTokenRecord>(tokensPath(dataDir), \"tokens\");\n}\n\n/**\n * Whether the HTTP MCP endpoint must require a bearer token.\n * - `DXCRM_MCP_AUTH=required` forces auth on (even with no tokens yet).\n * - `DXCRM_MCP_AUTH=off` forces it off.\n * - Otherwise: on as soon as at least one token is configured (opt-in by\n * provisioning a token; stays open for local/firewalled dev by default).\n */\nexport function isAuthRequired(dataDir: string): boolean {\n const mode = process.env[\"DXCRM_MCP_AUTH\"];\n if (mode === \"required\") return true;\n if (mode === \"off\") return false;\n return loadMcpTokens(dataDir).length > 0;\n}\n\n/** Validate an `Authorization: Bearer <token>` header against stored hashes. */\nexport function verifyBearer(authHeader: string | undefined, dataDir: string): AuthResult {\n if (!authHeader || !authHeader.startsWith(\"Bearer \")) return { ok: false };\n const token = authHeader.slice(\"Bearer \".length).trim();\n if (!token) return { ok: false };\n\n const candidate = hashToken(token);\n const candidateBuf = Buffer.from(candidate, \"hex\");\n for (const rec of loadMcpTokens(dataDir)) {\n if (rec.hash.length !== candidate.length) continue;\n let recBuf: Buffer;\n try {\n recBuf = Buffer.from(rec.hash, \"hex\");\n } catch {\n continue;\n }\n if (recBuf.length === candidateBuf.length && timingSafeEqual(recBuf, candidateBuf)) {\n return { ok: true, actor: rec.actor, role: rec.role };\n }\n }\n return { ok: false };\n}\n\n/**\n * Mint a new token: generates a random secret, persists only its hash mapped\n * to an actor/role, and returns the plaintext ONCE (never stored).\n */\nexport function createMcpToken(\n dataDir: string,\n actor: string,\n role: McpRole,\n label?: string\n): string {\n const token = randomBytes(24).toString(\"base64url\");\n const records = loadMcpTokens(dataDir);\n records.push({\n hash: hashToken(token),\n actor,\n role,\n ...(label ? { label } : {}),\n createdAt: new Date().toISOString(),\n });\n writeJsonArray(tokensPath(dataDir), \"tokens\", records);\n return token;\n}\n\n/** RFC 9728 OAuth 2.0 Protected Resource Metadata document. */\nexport function protectedResourceMetadata(resourceUrl: string): Record<string, unknown> {\n return {\n resource: resourceUrl,\n // Self-hosted default: tokens are provisioned out-of-band (createMcpToken).\n // Populate with an external Authorization Server to enable full OAuth flows.\n authorization_servers: [] as string[],\n bearer_methods_supported: [\"header\"],\n scopes_supported: [\"crm:read\", \"crm:write\"],\n };\n}\n\n/** Value for the `WWW-Authenticate` header on a 401 (RFC 9728 §5.1). */\nexport function wwwAuthenticateHeader(metadataUrl: string): string {\n return `Bearer resource_metadata=\"${metadataUrl}\"`;\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport { initOAuthFromDisk } from \"../core/oauth-store.js\";\nimport {\n decodeGmailPubSubPayload,\n verifyGmailPubSubSignature,\n handleGmailPushEvent,\n} from \"../sync/gmail-webhook-handler.js\";\nimport {\n handleMicrosoftValidationRequest,\n verifyMicrosoftGraphSignature,\n handleMicrosoftPushEvent,\n type MicrosoftGraphNotification,\n} from \"../sync/microsoft-webhook-handler.js\";\nimport {\n verifySlackSignature,\n handleSlackUrlVerification,\n handleSlackPushEvent,\n type SlackEvent,\n} from \"../sync/slack-webhook-handler.js\";\nimport { registerGetCapabilities } from \"./tools/get-capabilities.js\";\nimport { registerGetActiveSession } from \"./tools/get-active-session.js\";\nimport { registerGetCustomerContext } from \"./tools/get-customer-context.js\";\nimport { registerSearchCustomerKnowledge } from \"./tools/search-customer-knowledge.js\";\nimport { registerListCustomers } from \"./tools/list-customers.js\";\nimport { registerLogInteraction } from \"./tools/log-interaction.js\";\nimport { registerUpdateDeal } from \"./tools/update-deal.js\";\nimport { registerExportCustomer } from \"./tools/export-customer.js\";\nimport { registerUpdateCustomerFacts } from \"./tools/update-customer-facts.js\";\nimport { registerGetDealHealth } from \"./tools/get-deal-health.js\";\nimport { registerGetPipelineForecast } from \"./tools/get-pipeline-forecast.js\";\nimport { registerSummarizeMeeting } from \"./tools/summarize-meeting.js\";\nimport { registerGetPipelineStages } from \"./tools/get-pipeline-stages.js\";\nimport { registerGetMarketIntelligence } from \"./tools/get-market-intelligence.js\";\nimport { registerGetRelationshipGraph } from \"./tools/get-relationship-graph.js\";\nimport { registerGetRelationshipHealth } from \"./tools/get-relationship-health.js\";\nimport { registerRunDealAgent } from \"./tools/run-deal-agent.js\";\nimport { registerApproveAgentAction } from \"./tools/approve-agent-action.js\";\nimport { registerSimulateRevenue } from \"./tools/simulate-revenue.js\";\nimport { registerGetPlaybook } from \"./tools/get-playbook.js\";\nimport { registerCreatePlaybook } from \"./tools/create-playbook.js\";\nimport { registerListPlaybooks } from \"./tools/list-playbooks.js\";\nimport { registerDistillPlaybook } from \"./tools/distill-playbook.js\";\nimport { registerPursueGoal } from \"./tools/pursue-goal.js\";\nimport { registerGetGoalStatus } from \"./tools/get-goal-status.js\";\nimport { registerRegisterPushSubscription } from \"./tools/register-push-subscription.js\";\nimport { registerGetPushStatus } from \"./tools/get-push-status.js\";\nimport { registerGetOrgIntelligence } from \"./tools/get-org-intelligence.js\";\nimport { registerOpenDealRoom } from \"./tools/open-deal-room.js\";\nimport { registerGetProactiveBriefing } from \"./tools/get-proactive-briefing.js\";\nimport { registerListEmailTemplates } from \"./tools/list-email-templates.js\";\nimport { registerGetEmailTemplate } from \"./tools/get-email-template.js\";\nimport { registerDraftEmail } from \"./tools/draft-email.js\";\nimport { registerEnrollInSequence } from \"./tools/enroll-in-sequence.js\";\nimport { registerListSequenceEnrollments } from \"./tools/list-sequence-enrollments.js\";\nimport { registerUnenrollFromSequence } from \"./tools/unenroll-from-sequence.js\";\nimport { registerListSequences } from \"./tools/list-sequences.js\";\nimport { registerGenerateQuote } from \"./tools/generate-quote.js\";\nimport { registerGetQuoteStatus } from \"./tools/get-quote-status.js\";\nimport { registerGetBookingLink } from \"./tools/get-booking-link.js\";\nimport { registerCreateTicket } from \"./tools/create-ticket.js\";\nimport { registerUpdateTicket } from \"./tools/update-ticket.js\";\nimport { registerListTickets } from \"./tools/list-tickets.js\";\nimport { registerCloseTicket } from \"./tools/close-ticket.js\";\nimport { registerSendNpsSurvey } from \"./tools/send-nps-survey.js\";\nimport { registerGetSurveyResults } from \"./tools/get-survey-results.js\";\nimport { registerSearchKnowledgeBase } from \"./tools/search-knowledge-base.js\";\nimport { registerCreateKbArticle } from \"./tools/create-kb-article.js\";\nimport { registerBackupNow } from \"./tools/backup-now.js\";\nimport { registerListBackups } from \"./tools/list-backups.js\";\nimport { registerTriggerSync } from \"./tools/trigger-sync.js\";\nimport { registerGetAuditLog } from \"./tools/get-audit-log.js\";\nimport { registerGetLogs } from \"./tools/get-logs.js\";\nimport { logger } from \"../core/logger.js\";\nimport { registerPrompts } from \"./prompts.js\";\nimport { registerResources } from \"./resources.js\";\nimport { registerCustomObjectTools } from \"./tools/custom-objects.js\";\nimport {\n isAuthRequired,\n verifyBearer,\n protectedResourceMetadata,\n wwwAuthenticateHeader,\n} from \"./auth.js\";\n\nexport function surveyThankYouPage(score: number, comment?: string): string {\n const emoji = score >= 9 ? \"🎉\" : score >= 7 ? \"🙂\" : \"🙏\";\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Thank you</title>\n<style>body{font-family:sans-serif;max-width:480px;margin:80px auto;text-align:center;padding:0 20px}\nh1{font-size:2.5em;margin-bottom:.3em}p{color:#555;font-size:1.1em}</style></head>\n<body><h1>${emoji}</h1><h2>Thank you for your feedback!</h2>\n<p>You rated us <strong>${score}/10</strong>.${comment ? `<br>Your comment: <em>\"${String(comment).slice(0, 200).replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")}\"</em>` : \"\"}</p>\n<p style=\"margin-top:40px;color:#aaa;font-size:.85em\">Powered by DatasynxOpenCRM</p>\n</body></html>`;\n}\n\nexport function createMcpServer(): McpServer {\n const server = new McpServer({\n name: \"datasynx-opencrm\",\n version: \"0.1.0\",\n });\n\n // Register all 57 tools\n // IMPORTANT: Use server.registerTool() — server.tool() is deprecated in v2\n registerGetCapabilities(server);\n registerGetActiveSession(server);\n registerGetCustomerContext(server);\n registerSearchCustomerKnowledge(server);\n registerListCustomers(server);\n registerLogInteraction(server);\n registerUpdateDeal(server);\n registerExportCustomer(server);\n registerUpdateCustomerFacts(server);\n registerGetDealHealth(server);\n registerGetPipelineForecast(server);\n registerSummarizeMeeting(server);\n registerGetPipelineStages(server);\n registerGetMarketIntelligence(server);\n registerGetRelationshipGraph(server);\n registerGetRelationshipHealth(server);\n registerRunDealAgent(server);\n registerApproveAgentAction(server);\n registerSimulateRevenue(server);\n registerGetPlaybook(server);\n registerCreatePlaybook(server);\n registerListPlaybooks(server);\n registerDistillPlaybook(server);\n registerPursueGoal(server);\n registerGetGoalStatus(server);\n registerRegisterPushSubscription(server);\n registerGetPushStatus(server);\n registerGetOrgIntelligence(server);\n registerOpenDealRoom(server);\n registerGetProactiveBriefing(server);\n registerListEmailTemplates(server);\n registerGetEmailTemplate(server);\n registerDraftEmail(server);\n registerEnrollInSequence(server);\n registerListSequenceEnrollments(server);\n registerUnenrollFromSequence(server);\n registerListSequences(server);\n registerGenerateQuote(server);\n registerGetQuoteStatus(server);\n registerGetBookingLink(server);\n registerCreateTicket(server);\n registerUpdateTicket(server);\n registerListTickets(server);\n registerCloseTicket(server);\n registerSendNpsSurvey(server);\n registerGetSurveyResults(server);\n registerSearchKnowledgeBase(server);\n registerCreateKbArticle(server);\n registerBackupNow(server);\n registerListBackups(server);\n registerTriggerSync(server);\n registerGetAuditLog(server);\n registerGetLogs(server);\n registerCustomObjectTools(server);\n\n // MCP Prompts (playbooks) + Resources (read-only entities) — agent-native primitives\n registerPrompts(server);\n registerResources(server);\n\n return server;\n}\n\nexport async function startStdio(): Promise<void> {\n await initOAuthFromDisk(process.cwd());\n const server = createMcpServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n // IMPORTANT: stdout would corrupt the MCP stdio protocol — the logger writes\n // only to stderr (and the persistent ledger), never stdout.\n logger.info(\"mcp-server\", \"running via stdio\");\n}\n\nexport async function startHttp(port = 3847): Promise<void> {\n await initOAuthFromDisk(process.cwd());\n const { default: express } = await import(\"express\");\n const app = express();\n app.use(express.json());\n\n const server = createMcpServer();\n const dataDir = process.cwd();\n\n // RFC 9728 — OAuth 2.0 Protected Resource Metadata\n app.get(\"/.well-known/oauth-protected-resource\", (req, res) => {\n const base = `${req.protocol}://${req.get(\"host\") ?? \"localhost\"}`;\n res.json(protectedResourceMetadata(`${base}/mcp`));\n });\n\n app.post(\"/mcp\", async (req, res) => {\n // OAuth 2.1 resource-server gate: require a valid bearer token when auth is\n // enabled (a token is provisioned or DXCRM_MCP_AUTH=required).\n if (isAuthRequired(dataDir)) {\n const auth = verifyBearer(req.headers[\"authorization\"], dataDir);\n if (!auth.ok) {\n const base = `${req.protocol}://${req.get(\"host\") ?? \"localhost\"}`;\n res\n .status(401)\n .set(\n \"WWW-Authenticate\",\n wwwAuthenticateHeader(`${base}/.well-known/oauth-protected-resource`)\n )\n .json({ error: \"unauthorized\" });\n return;\n }\n // Attach the token's actor for RBAC enforcement on this request.\n if (auth.actor) process.env[\"DXCRM_ACTOR\"] = auth.actor;\n }\n\n const transport = new StreamableHTTPServerTransport({ enableJsonResponse: true });\n // Ensure onclose is always a function (required by Transport interface with exactOptionalPropertyTypes)\n transport.onclose = () => {\n /* no-op */\n };\n res.on(\"close\", () => {\n void transport.close();\n });\n await server.connect(transport as unknown as Transport);\n await transport.handleRequest(req, res, req.body as Record<string, unknown>);\n });\n\n app.get(\"/health\", (_req, res) => {\n res.json({ status: \"ok\", server: \"datasynx-opencrm\", version: \"0.1.0\" });\n });\n\n app.get(\"/sessions\", async (_req, res) => {\n try {\n const { readAllSessions } = await import(\"../commands/session.js\");\n const sessions = readAllSessions(dataDir);\n res.json({ sessions });\n } catch {\n res.json({ sessions: [] });\n }\n });\n\n // Gmail Pub/Sub webhook\n app.post(\"/webhooks/gmail\", async (req, res) => {\n const token = process.env[\"GMAIL_PUBSUB_TOKEN\"] ?? \"\";\n if (!verifyGmailPubSubSignature(req.headers[\"authorization\"] as string | undefined, token)) {\n res.status(401).json({ error: \"unauthorized\" });\n return;\n }\n const payload = decodeGmailPubSubPayload(req.body);\n if (!payload) {\n res.status(400).json({ error: \"invalid_payload\" });\n return;\n }\n const result = await handleGmailPushEvent(dataDir, payload, \"\").catch(() => ({\n processed: 0,\n slug: null,\n }));\n res.json({ ok: true, processed: result.processed });\n });\n\n // Microsoft Graph webhook\n app.all(\"/webhooks/microsoft\", async (req, res) => {\n const validation = handleMicrosoftValidationRequest(req.query as Record<string, string>);\n if (validation.isValidation) {\n res.setHeader(\"content-type\", \"text/plain\");\n res.status(200).send(validation.token);\n return;\n }\n const clientState = process.env[\"MS_GRAPH_CLIENT_STATE\"] ?? \"\";\n const body = req.body as { value?: MicrosoftGraphNotification[] };\n if (!verifyMicrosoftGraphSignature(body, clientState)) {\n res.status(401).json({ error: \"unauthorized\" });\n return;\n }\n const result = await handleMicrosoftPushEvent(dataDir, body.value ?? [], \"\").catch(() => ({\n processed: 0,\n skipped: 0,\n }));\n res.json({ ok: true, ...result });\n });\n\n // Slack Events API webhook\n app.post(\"/webhooks/slack\", express.text({ type: \"*/*\" }), async (req, res) => {\n const rawBody = req.body as string;\n const signingSecret = process.env[\"SLACK_SIGNING_SECRET\"] ?? \"\";\n if (\n !verifySlackSignature(\n rawBody,\n req.headers as { \"x-slack-signature\"?: string; \"x-slack-request-timestamp\"?: string },\n signingSecret\n )\n ) {\n res.status(401).json({ error: \"unauthorized\" });\n return;\n }\n let parsed: { type?: string; challenge?: string; event?: SlackEvent; team_id?: string };\n try {\n parsed = JSON.parse(rawBody) as typeof parsed;\n } catch {\n res.status(400).json({ error: \"invalid_json\" });\n return;\n }\n const verification = handleSlackUrlVerification(parsed);\n if (verification.isVerification) {\n res.json({ challenge: verification.challenge });\n return;\n }\n if (!parsed.event) {\n res.json({ ok: true, processed: 0 });\n return;\n }\n const botToken = process.env[\"SLACK_BOT_TOKEN\"] ?? \"\";\n const result = await handleSlackPushEvent(dataDir, parsed.event, botToken, {\n ...(parsed.team_id !== undefined ? { teamId: parsed.team_id } : {}),\n }).catch(() => ({ processed: 0, skipped: 1 }));\n res.json({ ok: true, ...result });\n });\n\n // NPS/CSAT survey response endpoint — linked from survey emails\n // GET /survey/respond?token=<t>&score=<0-10> → record score + thank-you page\n // GET /survey/respond?token=<t>&comment=true → show comment form\n // POST /survey/respond → record comment + thank-you page\n app.get(\"/survey/respond\", async (req, res) => {\n const { token, score, comment } = req.query as Record<string, string | undefined>;\n if (!token) {\n res.status(400).send(\"<h2>Invalid survey link.</h2>\");\n return;\n }\n\n if (comment === \"true\") {\n res.setHeader(\"content-type\", \"text/html\");\n res.send(`<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>Survey Comment</title>\n<style>body{font-family:sans-serif;max-width:520px;margin:60px auto;padding:0 20px}\ntextarea{width:100%;padding:10px;font-size:1em;border:1px solid #ccc;border-radius:4px}\ninput[type=number]{width:80px;padding:8px;font-size:1em}\nbutton{margin-top:12px;padding:12px 28px;background:#1a1a2e;color:#fff;border:none;border-radius:4px;font-size:1em;cursor:pointer}</style></head>\n<body><h2>Leave a comment</h2>\n<form method=\"POST\" action=\"/survey/respond\">\n<input type=\"hidden\" name=\"token\" value=\"${String(token).replace(/&/g, \"&amp;\").replace(/\"/g, \"&quot;\").replace(/</g, \"&lt;\")}\">\n<label>Your score (0–10):<br><input type=\"number\" name=\"score\" min=\"0\" max=\"10\" required></label><br><br>\n<label>Comment (optional):<br><textarea name=\"comment\" rows=\"5\" placeholder=\"What can we improve?\"></textarea></label><br>\n<button type=\"submit\">Submit</button>\n</form></body></html>`);\n return;\n }\n\n const numScore = score !== undefined ? parseInt(score, 10) : NaN;\n if (isNaN(numScore) || numScore < 0 || numScore > 10) {\n res.status(400).send(\"<h2>Invalid score. Please use the link from your email.</h2>\");\n return;\n }\n\n const { recordSurveyResponse } = await import(\"../core/survey-engine.js\");\n await recordSurveyResponse(dataDir, token, numScore).catch(() => null);\n res.setHeader(\"content-type\", \"text/html\");\n res.send(surveyThankYouPage(numScore));\n });\n\n app.post(\"/survey/respond\", express.urlencoded({ extended: false }), async (req, res) => {\n const { token, score, comment: commentText } = req.body as Record<string, string | undefined>;\n if (!token) {\n res.status(400).send(\"<h2>Invalid survey link.</h2>\");\n return;\n }\n const numScore = score !== undefined ? parseInt(score, 10) : NaN;\n if (isNaN(numScore) || numScore < 0 || numScore > 10) {\n res\n .status(400)\n .send(\"<h2>Invalid score. Please go back and enter a number between 0 and 10.</h2>\");\n return;\n }\n const { recordSurveyResponse } = await import(\"../core/survey-engine.js\");\n await recordSurveyResponse(dataDir, token, numScore, commentText || undefined).catch(\n () => null\n );\n res.setHeader(\"content-type\", \"text/html\");\n res.send(surveyThankYouPage(numScore, commentText));\n });\n\n app.listen(port, () => {\n logger.info(\"mcp-server\", \"running over http\", { url: `http://0.0.0.0:${port}/mcp` });\n });\n}\n\n// Entry point when run directly (e.g. node dist/mcp.js)\nconst mode = process.env[\"DXCRM_MCP_MODE\"] ?? \"stdio\";\nif (mode === \"http\") {\n const port = parseInt(process.env[\"DXCRM_MCP_PORT\"] ?? \"3847\", 10);\n startHttp(port).catch((err: unknown) => {\n logger.error(\"mcp-server\", \"fatal error\", { error: (err as Error).message });\n process.exit(1);\n });\n} else {\n startStdio().catch((err: unknown) => {\n logger.error(\"mcp-server\", \"fatal error\", { error: (err as Error).message });\n process.exit(1);\n });\n}\n"],"mappings":"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"}