@datasynx/agentic-crm 0.1.0 → 1.1.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 (309) hide show
  1. package/README.md +270 -669
  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-CDysGnRg.js} +6 -6
  5. package/dist/{ask-CID3jnuL.js.map → ask-CDysGnRg.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/attachments-CX2GAtsw.cjs +517 -0
  11. package/dist/attachments-CX2GAtsw.cjs.map +1 -0
  12. package/dist/attachments-D207gXfN.js +514 -0
  13. package/dist/attachments-D207gXfN.js.map +1 -0
  14. package/dist/attachments-rLa96rOK.js +514 -0
  15. package/dist/attachments-rLa96rOK.js.map +1 -0
  16. package/dist/auth-B5DcjJ_6.js +2 -0
  17. package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
  18. package/dist/auth-DDXZTwS0.js.map +1 -0
  19. package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
  20. package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
  21. package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
  22. package/dist/backup-CTlIxUdO.js.map +1 -0
  23. package/dist/backup-LFnC09oV.js +2 -0
  24. package/dist/chunk-BfDYWZQ8.cjs +32 -0
  25. package/dist/chunk-BfDYWZQ8.cjs.map +1 -0
  26. package/dist/chunk-BhUZmQg5.js +32 -0
  27. package/dist/chunk-BhUZmQg5.js.map +1 -0
  28. package/dist/chunk-ChC83jai.js +2 -0
  29. package/dist/chunk-e_w8qqtP.js +32 -0
  30. package/dist/chunk-e_w8qqtP.js.map +1 -0
  31. package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
  32. package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
  33. package/dist/cli.js +285 -186
  34. package/dist/cli.js.map +1 -1
  35. package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
  36. package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
  37. package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
  38. package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
  39. package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
  40. package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
  41. package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
  42. package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
  43. package/dist/context-builder-7Uab5-G4.js.map +1 -0
  44. package/dist/context-builder-hmOPvgso.js +2 -0
  45. package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
  46. package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
  47. package/dist/custom-fields-GzpOHW_2.js.map +1 -0
  48. package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
  49. package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
  50. package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
  51. package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
  52. package/dist/customer-dir-CkMMXhb0.js.map +1 -0
  53. package/dist/daemon/worker.js +66 -40
  54. package/dist/daemon/worker.js.map +1 -1
  55. package/dist/doctor-C14-vnJ1.js +103 -0
  56. package/dist/doctor-C14-vnJ1.js.map +1 -0
  57. package/dist/email-body-BFSRa0AW.cjs +42 -0
  58. package/dist/email-body-BFSRa0AW.cjs.map +1 -0
  59. package/dist/email-body-BOd7U-D2.js +42 -0
  60. package/dist/email-body-BOd7U-D2.js.map +1 -0
  61. package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
  62. package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
  63. package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
  64. package/dist/file-lock-CcHotQkZ.js.map +1 -0
  65. package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-B4Iu3AQb.js} +56 -22
  66. package/dist/gmail-sync-B4Iu3AQb.js.map +1 -0
  67. package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-BpSVESSe.cjs} +55 -21
  68. package/dist/gmail-sync-BpSVESSe.cjs.map +1 -0
  69. package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-DIbrPnTK.js} +55 -21
  70. package/dist/gmail-sync-DIbrPnTK.js.map +1 -0
  71. package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-BzOFbvgh.js} +4 -4
  72. package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-BzOFbvgh.js.map} +1 -1
  73. package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-CvSDW_Js.js} +2 -2
  74. package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
  75. package/dist/goal-engine-BbroPhqm.js.map +1 -0
  76. package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
  77. package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-B_I1d54Y.js} +3 -3
  78. package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-B_I1d54Y.js.map} +1 -1
  79. package/dist/html-BaeOCZKE.js +36 -0
  80. package/dist/html-BaeOCZKE.js.map +1 -0
  81. package/dist/html-CmOku6jS.cjs +47 -0
  82. package/dist/html-CmOku6jS.cjs.map +1 -0
  83. package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
  84. package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
  85. package/dist/identity-CB7j-Zr1.js +2 -0
  86. package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
  87. package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
  88. package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-CTId9IGV.js} +51 -45
  89. package/dist/import-hubspot-CTId9IGV.js.map +1 -0
  90. package/dist/{index-YqwMd6aQ.d.cts → index-BAutNcAT.d.cts} +20 -12
  91. package/dist/index-BAutNcAT.d.cts.map +1 -0
  92. package/dist/{index-V8BFaH-b.d.ts → index-FzDsNSSb.d.ts} +12 -4
  93. package/dist/index-FzDsNSSb.d.ts.map +1 -0
  94. package/dist/index.cjs +19 -21
  95. package/dist/index.cjs.map +1 -1
  96. package/dist/index.d.cts +20 -12
  97. package/dist/index.d.cts.map +1 -1
  98. package/dist/index.d.ts +12 -4
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +19 -21
  101. package/dist/index.js.map +1 -1
  102. package/dist/interactions-writer-B2y-73lh.js +2 -0
  103. package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-B8XAzdqR.js} +34 -4
  104. package/dist/interactions-writer-B8XAzdqR.js.map +1 -0
  105. package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-BRJNrefF.cjs} +7 -3
  106. package/dist/interactions-writer-BRJNrefF.cjs.map +1 -0
  107. package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-ZQcpFOh9.js} +7 -3
  108. package/dist/interactions-writer-ZQcpFOh9.js.map +1 -0
  109. package/dist/json-store-WWsFzXub.js +43 -0
  110. package/dist/json-store-WWsFzXub.js.map +1 -0
  111. package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base--063Kpa3.js} +51 -22
  112. package/dist/knowledge-base--063Kpa3.js.map +1 -0
  113. package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
  114. package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
  115. package/dist/lancedb-CuHKNsNZ.js.map +1 -0
  116. package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
  117. package/dist/lead-model-CEmx7te7.js.map +1 -0
  118. package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
  119. package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
  120. package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
  121. package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
  122. package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
  123. package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
  124. package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
  125. package/dist/logger-BkInaGoV.cjs +167 -0
  126. package/dist/logger-BkInaGoV.cjs.map +1 -0
  127. package/dist/logger-Dyl4VcLO.js +147 -0
  128. package/dist/logger-Dyl4VcLO.js.map +1 -0
  129. package/dist/logger-UaF5p9d1.js +147 -0
  130. package/dist/logger-UaF5p9d1.js.map +1 -0
  131. package/dist/logger-vKQS34w9.js +2 -0
  132. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  133. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  134. package/dist/mcp.cjs +365 -319
  135. package/dist/mcp.cjs.map +1 -1
  136. package/dist/mcp.d.cts.map +1 -1
  137. package/dist/mcp.d.ts.map +1 -1
  138. package/dist/mcp.js +365 -319
  139. package/dist/mcp.js.map +1 -1
  140. package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
  141. package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
  142. package/dist/memory-Dzr9dXSM.js.map +1 -0
  143. package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-BgVR8GDv.js} +4 -4
  144. package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-BgVR8GDv.js.map} +1 -1
  145. package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-D30_XksI.js} +5 -5
  146. package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-D30_XksI.js.map} +1 -1
  147. package/dist/{nba-3wanmJ0U.js → nba-DwdfM93s.js} +3 -3
  148. package/dist/{nba-3wanmJ0U.js.map → nba-DwdfM93s.js.map} +1 -1
  149. package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
  150. package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
  151. package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
  152. package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
  153. package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
  154. package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
  155. package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
  156. package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
  157. package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
  158. package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
  159. package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
  160. package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
  161. package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
  162. package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
  163. package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
  164. package/dist/push-manager-C0ECQgva.js.map +1 -0
  165. package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
  166. package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
  167. package/dist/quote-generator-CTdR8eEI.js.map +1 -0
  168. package/dist/rbac-DzbyFhVH.js +2 -0
  169. package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
  170. package/dist/rbac-msmBc_tK.js.map +1 -0
  171. package/dist/regex-Jt5DatPi.js +13 -0
  172. package/dist/regex-Jt5DatPi.js.map +1 -0
  173. package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
  174. package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
  175. package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
  176. package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
  177. package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
  178. package/dist/safe-path-mpp0dKtO.js +18 -0
  179. package/dist/safe-path-mpp0dKtO.js.map +1 -0
  180. package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
  181. package/dist/segments-DI3LOQNe.js.map +1 -0
  182. package/dist/sequence-engine-C6nnewHX.js +2 -0
  183. package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
  184. package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
  185. package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
  186. package/dist/sequence-store-CmYb6s0g.js.map +1 -0
  187. package/dist/{server-Dyva03K8.js → server-DoRPPOeR.js} +308 -230
  188. package/dist/server-DoRPPOeR.js.map +1 -0
  189. package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
  190. package/dist/session-B6XaP83h.js.map +1 -0
  191. package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
  192. package/dist/session-BgGDyP2C.js.map +1 -0
  193. package/dist/session-Bp4zTh4l.js +2 -0
  194. package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
  195. package/dist/session-Mm7GQbSH.cjs.map +1 -0
  196. package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
  197. package/dist/session-store-DWxJ5Pof.js.map +1 -0
  198. package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
  199. package/dist/session-store-yfwnj0OC.cjs.map +1 -0
  200. package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
  201. package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
  202. package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
  203. package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
  204. package/dist/sop-BV7ICAFR.js.map +1 -0
  205. package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
  206. package/dist/survey-engine-DKctGcLQ.js +2 -0
  207. package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
  208. package/dist/survey-engine-DngXBv47.js.map +1 -0
  209. package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
  210. package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
  211. package/dist/sync-state-DMZgzpez.js.map +1 -0
  212. package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
  213. package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
  214. package/dist/ticket-writer-a9on36Wb.js.map +1 -0
  215. package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
  216. package/dist/tone-C7bqK69y.js.map +1 -0
  217. package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
  218. package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
  219. package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
  220. package/dist/tone-mXSftvTn.js.map +1 -0
  221. package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-BoClrJAz.js} +18 -11
  222. package/dist/transcript-watcher-BoClrJAz.js.map +1 -0
  223. package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
  224. package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
  225. package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
  226. package/dist/update-deal-CWy1eLJI.js +2 -0
  227. package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
  228. package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
  229. package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
  230. package/dist/usage-BVlFlKW_.js.map +1 -0
  231. package/dist/usage-CClTf5e6.cjs.map +1 -1
  232. package/dist/usage-D0u9a-lV.js.map +1 -1
  233. package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
  234. package/dist/vault-CfwZdNzC.js.map +1 -0
  235. package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
  236. package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
  237. package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
  238. package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
  239. package/dist/webhooks-DXr1IoKn.js.map +1 -0
  240. package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
  241. package/dist/webhooks-sWZ8CJtR.js.map +1 -0
  242. package/package.json +22 -2
  243. package/dist/approvals-DpjxGHFp.js.map +0 -1
  244. package/dist/auth-CyFuu9X_.js +0 -2
  245. package/dist/auth-DFWwWcYD.js.map +0 -1
  246. package/dist/backup-CeMk9z86.js.map +0 -1
  247. package/dist/backup-f_hC7rBV.js +0 -2
  248. package/dist/context-builder-BzWAp3Zs.js.map +0 -1
  249. package/dist/context-builder-DlrRcqmJ.js +0 -2
  250. package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
  251. package/dist/custom-objects-BHgn1GEX.js.map +0 -1
  252. package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
  253. package/dist/file-lock-B_zi7NQl.js.map +0 -1
  254. package/dist/gmail-sync-DIaxInDT.js.map +0 -1
  255. package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
  256. package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
  257. package/dist/goal-engine-KpBftn4V.js.map +0 -1
  258. package/dist/identity-gyfWdrcX.js +0 -2
  259. package/dist/import-hubspot-BaK71U_K.js.map +0 -1
  260. package/dist/index-V8BFaH-b.d.ts.map +0 -1
  261. package/dist/index-YqwMd6aQ.d.cts.map +0 -1
  262. package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
  263. package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
  264. package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
  265. package/dist/interactions-writer-dSPy1XfO.js +0 -2
  266. package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
  267. package/dist/lancedb-rlvWoPwl.js.map +0 -1
  268. package/dist/lead-model-BCFzyktm.js.map +0 -1
  269. package/dist/memory-Bb6ky3kb.js.map +0 -1
  270. package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
  271. package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
  272. package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
  273. package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
  274. package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
  275. package/dist/push-manager-CdqIIkuh.js.map +0 -1
  276. package/dist/quote-generator-BfwENXzg.js.map +0 -1
  277. package/dist/rbac-C7c8tcES.js +0 -2
  278. package/dist/rbac-CTIktZaC.js.map +0 -1
  279. package/dist/relationship-health-odxEoQdJ.js.map +0 -1
  280. package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
  281. package/dist/segments-BqcD5HIl.js.map +0 -1
  282. package/dist/sequence-engine-CCTHEBgi.js +0 -2
  283. package/dist/sequence-engine-J1lTW_in.js.map +0 -1
  284. package/dist/sequence-store-DaaWr0Os.js.map +0 -1
  285. package/dist/server-Dyva03K8.js.map +0 -1
  286. package/dist/session-B9AilxOE.js.map +0 -1
  287. package/dist/session-D0qFkBla.cjs.map +0 -1
  288. package/dist/session-D9ub6Wl1.js.map +0 -1
  289. package/dist/session-mWHA71Lw.js +0 -2
  290. package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
  291. package/dist/session-store-C8tEvMPw.js.map +0 -1
  292. package/dist/sop-Vp0UPWFW.js.map +0 -1
  293. package/dist/survey-engine-C06hcQt3.js +0 -2
  294. package/dist/survey-engine-DBjCYqCv.js.map +0 -1
  295. package/dist/sync-state-ChaLbamC.js.map +0 -1
  296. package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
  297. package/dist/tone-Bdm5uaht.js.map +0 -1
  298. package/dist/tone-DRKlZgPr.cjs.map +0 -1
  299. package/dist/tone-vNb2DAAD.js.map +0 -1
  300. package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
  301. package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
  302. package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
  303. package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
  304. package/dist/update-deal-BNwPGaTV.js +0 -2
  305. package/dist/usage-D0-TYJkw.js.map +0 -1
  306. package/dist/vault-DXCg29W-.js.map +0 -1
  307. package/dist/webhooks-7EpA05Qr.js.map +0 -1
  308. package/dist/webhooks-BO2UAnmn.js.map +0 -1
  309. package/dist/webhooks-Xn6zO6kd.cjs.map +0 -1
package/dist/mcp.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { C as readMainFacts, S as listCustomerSlugs, a as enforceRbac, b as customerExists, c as filterAuditLog, d as writeAuditEntry, f as listBackupsInDir, i as canSeeCustomer, l as getActor, m as runBackup, n as getSession, p as readBackupLog, u as readAuditLog, w as writeMainFacts, x as ensureCustomerDir } from "./session-store-C8tEvMPw.js";
2
+ import { A as writeMainFacts, C as writeJsonArray, D as ensureCustomerDir, E as customerExists, M as isSafePathSegment, O as listCustomerSlugs, S as readJsonFile, T as assertSafeSlug, a as customerVisibility, d as readAuditLog, f as writeAuditEntry, h as runBackup, i as canSeeCustomer, j as assertSafePathSegment, k as readMainFacts, l as filterAuditLog, m as readBackupLog, n as getSession, o as enforceRbac, p as listBackupsInDir, u as getActor, w as writeJsonFile, x as readJsonArray } from "./session-store-DWxJ5Pof.js";
3
+ import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
3
4
  import { t as withFileQueue } from "./write-queue-IbsAjUnh.js";
4
- import { n as formatInteractionEntry, t as appendInteraction } from "./interactions-writer-DO3KcSR3.js";
5
- import { n as readPipeline, r as upsertDeal } from "./pipeline-writer-eufx_0o1.js";
6
- import { i as guardIsoDate, t as callLlm } from "./llm-Z8RIYkpF.js";
5
+ import { n as formatInteractionEntry, t as appendInteraction } from "./interactions-writer-ZQcpFOh9.js";
6
+ import { n as queryLogs, r as summarizeLogs, t as logger } from "./logger-UaF5p9d1.js";
7
+ import { i as upsertDeal, n as readPipeline, r as readPipelineSync } from "./pipeline-writer-rDj-ni6q.js";
8
+ import { i as guardIsoDate, t as callLlm } from "./llm-BnSUBisu.js";
7
9
  import path from "path";
8
10
  import fs from "fs";
9
11
  import matter from "gray-matter";
@@ -56,11 +58,10 @@ async function readSubscriptions(dataDir) {
56
58
  async function writeSubscriptions(dataDir, subs) {
57
59
  const filePath = subscriptionsPath(dataDir);
58
60
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
59
- const file = {
61
+ writeJsonFile(filePath, {
60
62
  subscriptions: subs,
61
63
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
62
- };
63
- fs.writeFileSync(filePath, JSON.stringify(file, null, 2), "utf-8");
64
+ });
64
65
  }
65
66
  function expiresAtForProvider(provider) {
66
67
  if (provider === "gmail") return new Date(Date.now() + 10080 * 60 * 1e3).toISOString();
@@ -91,23 +92,15 @@ function getSyncStatePath(dataDir) {
91
92
  return path.join(dataDir, ".agentic", "sync-state.json");
92
93
  }
93
94
  function readSyncState(dataDir) {
94
- const filePath = getSyncStatePath(dataDir);
95
- if (!fs.existsSync(filePath)) return {};
96
- try {
97
- return JSON.parse(fs.readFileSync(filePath, "utf-8"));
98
- } catch {
99
- return {};
100
- }
95
+ return readJsonFile(getSyncStatePath(dataDir), {});
101
96
  }
102
97
  function updateSlugSyncState(dataDir, slug, update) {
103
- const filePath = getSyncStatePath(dataDir);
104
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
105
98
  const state = readSyncState(dataDir);
106
99
  state[slug] = {
107
100
  ...state[slug],
108
101
  ...update
109
102
  };
110
- fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
103
+ writeJsonFile(getSyncStatePath(dataDir), state);
111
104
  }
112
105
  function getLastGmailSync(dataDir, slug) {
113
106
  const ts = readSyncState(dataDir)[slug]?.lastGmailSync;
@@ -405,7 +398,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
405
398
  | log_interaction | Write a new interaction entry (call, email, meeting, note) — immediately searchable | rep+ |
406
399
  | update_deal | Create or update a deal in pipeline.md — upserts by deal name | rep+ |
407
400
  | update_customer_facts | Update fields in customer profile (domain, contact, stage, tags) | admin |
408
- | export_customer | Export all customer data as JSON or Markdown | admin |
401
+ | export_customer | Export all customer data (incl. attachment contents) as JSON or Markdown | admin |
409
402
  | get_deal_health | Score deal health 0–100 (A–F grade) based on activity, velocity, close date, probability | any |
410
403
  | get_pipeline_forecast | Aggregate weighted pipeline revenue across all customers grouped by stage | any |
411
404
  | get_pipeline_stages | List all configured pipeline stages (defaults: lead, qualified, proposal, negotiation, won, lost) | any |
@@ -449,6 +442,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
449
442
  | list_backups | List available backups with date, size, verification status, and customer count | any |
450
443
  | trigger_sync | Force immediate Gmail sync for one or all customers | rep+ |
451
444
  | get_audit_log | Read audit log — all write operations with actor, tool, customer | admin |
445
+ | get_logs | Query/aggregate the structured application log (level, component, errors) | admin |
452
446
  | define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
453
447
  | create_record | Create a record of a custom object (validated against its schema) | rep+ |
454
448
  | list_records | List records of a custom object | any |
@@ -538,12 +532,14 @@ RBAC: admin
538
532
  - Input: slug (required) + any combination of the optional fields
539
533
  - Returns: { success: boolean, facts: object }
540
534
 
541
- ### export_customer({ slug, format? })
542
- Export all customer data (main_facts + interactions count + pipeline + attachments list).
535
+ ### export_customer({ slug, format?, includeAttachmentContent? })
536
+ Export all customer data (main_facts + interactions + pipeline + attachments).
537
+ Set includeAttachmentContent to inline every attachment's converted Markdown —
538
+ a single sendable bundle of all conversations and documents for the customer.
543
539
  RBAC: admin
544
- - Input: { slug: string, format?: "json" | "markdown" (default "json") }
545
- - Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments }
546
- - Returns (Markdown): Formatted document with all sections
540
+ - Input: { slug: string, format?: "json" | "markdown" (default "json"), includeAttachmentContent?: boolean (default false) }
541
+ - Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments[, attachmentContents] }
542
+ - Returns (Markdown): Formatted document with all sections (and attachment contents when requested)
547
543
 
548
544
  ### get_deal_health({ slug })
549
545
  Score the health of all deals for a customer based on activity recency, stage velocity,
@@ -1323,6 +1319,16 @@ function registerGetActiveSession(server) {
1323
1319
  }, async () => handleGetActiveSession());
1324
1320
  }
1325
1321
  //#endregion
1322
+ //#region src/core/regex.ts
1323
+ /**
1324
+ * Escape a string so it can be embedded safely as a literal inside a `RegExp`.
1325
+ * Prevents both broken patterns and ReDoS/injection when interpolating
1326
+ * field names, section headers, or other dynamic values into a regex.
1327
+ */
1328
+ function escapeRegExp(value) {
1329
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1330
+ }
1331
+ //#endregion
1326
1332
  //#region src/core/context-builder.ts
1327
1333
  var context_builder_exports = /* @__PURE__ */ __exportAll({ buildContext: () => buildContext });
1328
1334
  const MAX_INTERACTIONS = 10;
@@ -1338,7 +1344,7 @@ function parsePipelineContent(filePath) {
1338
1344
  return fs.readFileSync(filePath, "utf-8").trim();
1339
1345
  }
1340
1346
  function extractSection(content, sectionName) {
1341
- const match = new RegExp(`## ${sectionName}([\\s\\S]*?)(?=^## |$)`, "m").exec(content);
1347
+ const match = new RegExp(`## ${escapeRegExp(sectionName)}([\\s\\S]*?)(?=^## |$)`, "m").exec(content);
1342
1348
  return match ? (match[1] ?? "").trim() : "";
1343
1349
  }
1344
1350
  async function buildContext(dataDir, slug) {
@@ -1415,7 +1421,7 @@ async function buildContext(dataDir, slug) {
1415
1421
  }
1416
1422
  //#endregion
1417
1423
  //#region src/mcp/tools/get-customer-context.ts
1418
- const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1424
+ const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1419
1425
  function triggerOnQuerySync(dataDir, slug) {
1420
1426
  const auth = getGmailAuth();
1421
1427
  if (!auth) return;
@@ -1428,7 +1434,7 @@ function triggerOnQuerySync(dataDir, slug) {
1428
1434
  const sources = JSON.parse(fs.readFileSync(sourcesPath, "utf-8"));
1429
1435
  if (!sources.gmail?.enabled || !sources.gmail.query) return;
1430
1436
  const query = sources.gmail.query;
1431
- import("./gmail-sync-rQaVqKWd.js").then(({ syncGmail }) => syncGmail({
1437
+ import("./gmail-sync-DIbrPnTK.js").then(({ syncGmail }) => syncGmail({
1432
1438
  slug,
1433
1439
  dataDir,
1434
1440
  auth,
@@ -1436,7 +1442,7 @@ function triggerOnQuerySync(dataDir, slug) {
1436
1442
  }).then(() => updateSlugSyncState(dataDir, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() })).catch(() => {})).catch(() => {});
1437
1443
  } catch {}
1438
1444
  }
1439
- async function handleGetCustomerContext(input, dataDir = DATA_DIR$51) {
1445
+ async function handleGetCustomerContext(input, dataDir = DATA_DIR$52) {
1440
1446
  const targetSlug = input.slug ?? getSession()?.customerSlug;
1441
1447
  if (!targetSlug) return {
1442
1448
  content: [{
@@ -1552,7 +1558,7 @@ async function indexInLanceDB(dataDir, slug, text, sourceRef, meta) {
1552
1558
  }]);
1553
1559
  await table.mergeInsert("source_ref").whenMatchedUpdateAll().whenNotMatchedInsertAll().execute(data);
1554
1560
  } catch (err) {
1555
- process.stderr.write(`[lancedb] indexInLanceDB failed: ${err.message}\n`);
1561
+ logger.error("lancedb", "indexInLanceDB failed", { error: err.message });
1556
1562
  }
1557
1563
  }
1558
1564
  async function searchKnowledge(dataDir, slug, query, limit) {
@@ -1572,8 +1578,8 @@ async function searchKnowledge(dataDir, slug, query, limit) {
1572
1578
  }
1573
1579
  //#endregion
1574
1580
  //#region src/mcp/tools/search-customer-knowledge.ts
1575
- const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1576
- async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$50) {
1581
+ const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1582
+ async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$51) {
1577
1583
  const limit = input.limit ?? 5;
1578
1584
  try {
1579
1585
  const results = await searchKnowledge(dataDir, input.slug, input.query, limit);
@@ -1621,14 +1627,14 @@ If no results: returns empty array with a helpful sync suggestion.`,
1621
1627
  }
1622
1628
  //#endregion
1623
1629
  //#region src/mcp/tools/list-customers.ts
1624
- const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1630
+ const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1625
1631
  function extractLastInteractionDate(interactionsPath) {
1626
1632
  if (!fs.existsSync(interactionsPath)) return void 0;
1627
1633
  const content = fs.readFileSync(interactionsPath, "utf-8");
1628
1634
  const match = /^## (\d{4}-\d{2}-\d{2})/m.exec(content);
1629
1635
  return match ? match[1] : void 0;
1630
1636
  }
1631
- async function handleListCustomers(input, dataDir = DATA_DIR$49) {
1637
+ async function handleListCustomers(input, dataDir = DATA_DIR$50) {
1632
1638
  const customersDir = path.join(dataDir, "customers");
1633
1639
  const customers = [];
1634
1640
  if (!fs.existsSync(customersDir)) return { content: [{
@@ -1636,6 +1642,7 @@ async function handleListCustomers(input, dataDir = DATA_DIR$49) {
1636
1642
  text: JSON.stringify([], null, 2)
1637
1643
  }] };
1638
1644
  const entries = fs.readdirSync(customersDir);
1645
+ const canSee = customerVisibility(dataDir, process.env["DXCRM_ACTOR"] ?? "system");
1639
1646
  for (const entry of entries) {
1640
1647
  const customerDir = path.join(customersDir, entry);
1641
1648
  try {
@@ -1662,7 +1669,7 @@ async function handleListCustomers(input, dataDir = DATA_DIR$49) {
1662
1669
  const filterLower = input.filter.toLowerCase();
1663
1670
  if (!(name.toLowerCase().includes(filterLower) || entry.toLowerCase().includes(filterLower) || stage.toLowerCase().includes(filterLower))) continue;
1664
1671
  }
1665
- if (!canSeeCustomer(dataDir, process.env["DXCRM_ACTOR"] ?? "system", entry)) continue;
1672
+ if (!canSee(entry)) continue;
1666
1673
  customers.push(summary);
1667
1674
  } catch {
1668
1675
  continue;
@@ -1697,8 +1704,7 @@ async function withJsonFile(filePath, updater) {
1697
1704
  current = null;
1698
1705
  }
1699
1706
  const next = await updater(current);
1700
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
1701
- fs.writeFileSync(filePath, JSON.stringify(next, null, 2), "utf-8");
1707
+ writeFileAtomic(filePath, JSON.stringify(next, null, 2));
1702
1708
  return next;
1703
1709
  });
1704
1710
  }
@@ -1722,7 +1728,7 @@ function readGraph(dataDir, slug) {
1722
1728
  try {
1723
1729
  return JSON.parse(fs.readFileSync(p, "utf-8"));
1724
1730
  } catch {
1725
- process.stderr.write(`[graph] failed to parse ${p} — returning empty graph\n`);
1731
+ logger.warn("graph", "failed to parse graph file — returning empty graph", { path: p });
1726
1732
  return emptyGraph(slug);
1727
1733
  }
1728
1734
  }
@@ -1975,23 +1981,13 @@ function healthPath(dataDir, slug) {
1975
1981
  return path.join(dataDir, "customers", slug, "health.json");
1976
1982
  }
1977
1983
  function readHealth(dataDir, slug) {
1978
- const p = healthPath(dataDir, slug);
1979
- if (!fs.existsSync(p)) return null;
1980
- try {
1981
- return JSON.parse(fs.readFileSync(p, "utf-8"));
1982
- } catch {
1983
- return null;
1984
- }
1984
+ return readJsonFile(healthPath(dataDir, slug), null);
1985
1985
  }
1986
1986
  function writeHealth(dataDir, slug, health) {
1987
- const p = healthPath(dataDir, slug);
1988
- const dir = path.dirname(p);
1989
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1990
- const updated = {
1987
+ writeJsonFile(healthPath(dataDir, slug), {
1991
1988
  ...health,
1992
1989
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1993
- };
1994
- fs.writeFileSync(p, JSON.stringify(updated, null, 2), "utf-8");
1990
+ });
1995
1991
  }
1996
1992
  function parseContactInteractions(content) {
1997
1993
  const blocks = content.split(/(?=^## \d{4}-\d{2}-\d{2})/m).filter((b) => b.trim().length > 0);
@@ -2151,8 +2147,8 @@ async function updateHealthFromInteraction(dataDir, slug) {
2151
2147
  }
2152
2148
  //#endregion
2153
2149
  //#region src/mcp/tools/log-interaction.ts
2154
- const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2155
- async function handleLogInteraction(input, dataDir = DATA_DIR$48) {
2150
+ const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2151
+ async function handleLogInteraction(input, dataDir = DATA_DIR$49) {
2156
2152
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2157
2153
  const interactionDate = input.date ?? today;
2158
2154
  const sourceRef = input.source ?? `agent://log/${Date.now()}`;
@@ -2182,7 +2178,7 @@ async function handleLogInteraction(input, dataDir = DATA_DIR$48) {
2182
2178
  raw.data.last_touchpoint = interactionDate;
2183
2179
  let serialized = matter.stringify(raw.content, raw.data);
2184
2180
  serialized = serialized.replace(/^(last_touchpoint:\s*)['"](\d{4}-\d{2}-\d{2})['"]/m, "$1$2");
2185
- fs.writeFileSync(mainFactsPath, serialized, "utf-8");
2181
+ writeFileAtomic(mainFactsPath, serialized);
2186
2182
  } catch {}
2187
2183
  writeAuditEntry(dataDir, {
2188
2184
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2261,8 +2257,8 @@ var update_deal_exports = /* @__PURE__ */ __exportAll({
2261
2257
  handleUpdateDeal: () => handleUpdateDeal,
2262
2258
  registerUpdateDeal: () => registerUpdateDeal
2263
2259
  });
2264
- const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2265
- async function handleUpdateDeal(input, dataDir = DATA_DIR$47) {
2260
+ const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2261
+ async function handleUpdateDeal(input, dataDir = DATA_DIR$48) {
2266
2262
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2267
2263
  const deal = {
2268
2264
  name: input.dealName,
@@ -2345,12 +2341,12 @@ Returns: { success: boolean, deal: object }`,
2345
2341
  }
2346
2342
  //#endregion
2347
2343
  //#region src/mcp/tools/export-customer.ts
2348
- const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2344
+ const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2349
2345
  function countInteractions(content) {
2350
2346
  const matches = content.match(/^## \d{4}-\d{2}-\d{2}/gm);
2351
2347
  return matches ? matches.length : 0;
2352
2348
  }
2353
- async function handleExportCustomer(input, dataDir = DATA_DIR$46) {
2349
+ async function handleExportCustomer(input, dataDir = DATA_DIR$47) {
2354
2350
  enforceRbac(dataDir, "export_customer");
2355
2351
  const customerDir = path.join(dataDir, "customers", input.slug);
2356
2352
  if (!fs.existsSync(customerDir)) return {
@@ -2377,14 +2373,27 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$46) {
2377
2373
  interactionsCount = countInteractions(interactionsContent);
2378
2374
  }
2379
2375
  const pipeline = await readPipeline(dataDir, input.slug);
2376
+ const includeAttachmentContent = input.includeAttachmentContent ?? false;
2380
2377
  const attachmentsDir = path.join(customerDir, "attachments");
2381
2378
  const attachments = [];
2379
+ const attachmentContents = {};
2382
2380
  if (fs.existsSync(attachmentsDir)) try {
2383
2381
  const files = fs.readdirSync(attachmentsDir);
2384
2382
  for (const f of files) try {
2385
- if (fs.statSync(path.join(attachmentsDir, f)).isFile()) attachments.push(f);
2383
+ if (!fs.statSync(path.join(attachmentsDir, f)).isFile()) continue;
2384
+ attachments.push(f);
2385
+ if (includeAttachmentContent && f.endsWith(".md")) attachmentContents[f] = fs.readFileSync(path.join(attachmentsDir, f), "utf-8");
2386
2386
  } catch {}
2387
2387
  } catch {}
2388
+ const attachmentContentSection = () => {
2389
+ const entries = Object.entries(attachmentContents);
2390
+ if (!includeAttachmentContent || entries.length === 0) return [];
2391
+ return [
2392
+ "",
2393
+ `## Attachment Contents (${entries.length})`,
2394
+ ...entries.map(([name, content]) => `\n### ${name}\n\n${content.trim()}`)
2395
+ ];
2396
+ };
2388
2397
  if (format === "markdown") return { content: [{
2389
2398
  type: "text",
2390
2399
  text: [
@@ -2403,7 +2412,8 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$46) {
2403
2412
  pipeline.length > 0 ? pipeline.map((d) => `- **${d.name}** · ${d.stage}${d.value !== void 0 ? ` · €${d.value}` : ""}${d.close_date ? ` · close: ${d.close_date}` : ""}`).join("\n") : "(no deals)",
2404
2413
  "",
2405
2414
  `## Attachments (${attachments.length})`,
2406
- attachments.length > 0 ? attachments.map((f) => `- ${f}`).join("\n") : "(none)"
2415
+ attachments.length > 0 ? attachments.map((f) => `- ${f}`).join("\n") : "(none)",
2416
+ ...attachmentContentSection()
2407
2417
  ].join("\n")
2408
2418
  }] };
2409
2419
  const exported = {
@@ -2412,7 +2422,8 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$46) {
2412
2422
  mainFacts,
2413
2423
  interactionsCount,
2414
2424
  pipeline,
2415
- attachments
2425
+ attachments,
2426
+ ...includeAttachmentContent ? { attachmentContents } : {}
2416
2427
  };
2417
2428
  return { content: [{
2418
2429
  type: "text",
@@ -2422,29 +2433,34 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$46) {
2422
2433
  function registerExportCustomer(server) {
2423
2434
  server.registerTool("export_customer", {
2424
2435
  title: "Export Customer",
2425
- description: `Export all customer data (main_facts + interactions count + pipeline deals).
2426
- Useful for reporting, audits, or creating backups.
2436
+ description: `Export all customer data (main_facts + interactions + pipeline deals + attachments).
2437
+ Useful for reporting, audits, handoffs, or creating a complete sendable bundle
2438
+ of every conversation and document for a customer.
2427
2439
 
2428
2440
  Args:
2429
2441
  slug: Customer ID (e.g. "acme-corp")
2430
2442
  format: Output format — "json" (default) or "markdown"
2443
+ includeAttachmentContent: Inline the converted Markdown of every attachment
2444
+ (default false). Use this to produce a single self-contained bundle.
2431
2445
 
2432
2446
  Returns:
2433
- JSON: { slug, exportedAt, mainFacts, interactionsCount, pipeline }
2434
- Markdown: Formatted document with all sections`,
2447
+ JSON: { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments[, attachmentContents] }
2448
+ Markdown: Formatted document with all sections (and attachment contents when requested)`,
2435
2449
  inputSchema: z.object({
2436
2450
  slug: z.string().describe("Customer slug (e.g. 'acme-corp')"),
2437
- format: z.enum(["json", "markdown"]).optional().describe("Output format: 'json' (default) or 'markdown'")
2451
+ format: z.enum(["json", "markdown"]).optional().describe("Output format: 'json' (default) or 'markdown'"),
2452
+ includeAttachmentContent: z.boolean().optional().describe("Inline converted attachment Markdown into the export (default false)")
2438
2453
  })
2439
- }, async ({ slug, format }) => handleExportCustomer({
2454
+ }, async ({ slug, format, includeAttachmentContent }) => handleExportCustomer({
2440
2455
  slug,
2441
- ...format !== void 0 ? { format } : {}
2456
+ ...format !== void 0 ? { format } : {},
2457
+ ...includeAttachmentContent !== void 0 ? { includeAttachmentContent } : {}
2442
2458
  }));
2443
2459
  }
2444
2460
  //#endregion
2445
2461
  //#region src/mcp/tools/update-customer-facts.ts
2446
- const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2447
- async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$45) {
2462
+ const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2463
+ async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$46) {
2448
2464
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2449
2465
  try {
2450
2466
  enforceRbac(dataDir, "update_customer_facts");
@@ -2594,10 +2610,36 @@ function scoreDeal(deal, signals) {
2594
2610
  warnings
2595
2611
  };
2596
2612
  }
2613
+ const MS_PER_DAY = 864e5;
2614
+ /**
2615
+ * Derive activity/close timing for a deal relative to `todayDate`. Centralizes
2616
+ * the day-diff math that deal-room and deal-agent each computed identically.
2617
+ * A blank/whitespace close_date yields `undefined` (not a NaN day count).
2618
+ */
2619
+ function deriveDealTiming(deal, todayDate) {
2620
+ const updatedDate = deal.updated ? new Date(deal.updated) : todayDate;
2621
+ const daysSinceLastActivity = Math.floor((todayDate.getTime() - updatedDate.getTime()) / MS_PER_DAY);
2622
+ const timing = {
2623
+ daysSinceLastActivity,
2624
+ daysInCurrentStage: daysSinceLastActivity
2625
+ };
2626
+ if (deal.close_date && deal.close_date.trim() !== "") timing.daysToClose = Math.floor((new Date(deal.close_date).getTime() - todayDate.getTime()) / MS_PER_DAY);
2627
+ return timing;
2628
+ }
2629
+ /** Score a deal using timing derived from `todayDate` plus the deal's probability. */
2630
+ function scoreDealForToday(deal, todayDate) {
2631
+ const timing = deriveDealTiming(deal, todayDate);
2632
+ return scoreDeal(deal, {
2633
+ daysSinceLastActivity: timing.daysSinceLastActivity,
2634
+ daysInCurrentStage: timing.daysInCurrentStage,
2635
+ ...timing.daysToClose !== void 0 ? { daysToClose: timing.daysToClose } : {},
2636
+ ...deal.probability !== void 0 ? { probability: deal.probability } : {}
2637
+ });
2638
+ }
2597
2639
  //#endregion
2598
2640
  //#region src/mcp/tools/get-deal-health.ts
2599
- const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2600
- async function handleGetDealHealth(input, dataDir = DATA_DIR$44) {
2641
+ const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2642
+ async function handleGetDealHealth(input, dataDir = DATA_DIR$45) {
2601
2643
  try {
2602
2644
  const deals = await readPipeline(dataDir, input.slug);
2603
2645
  const today = /* @__PURE__ */ new Date();
@@ -2646,28 +2688,13 @@ Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,
2646
2688
  }
2647
2689
  //#endregion
2648
2690
  //#region src/mcp/tools/get-pipeline-forecast.ts
2649
- const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2650
- async function handleGetPipelineForecast(input, dataDir = DATA_DIR$43) {
2691
+ const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2692
+ async function handleGetPipelineForecast(input, dataDir = DATA_DIR$44) {
2651
2693
  try {
2652
- const customersDir = path.join(dataDir, "customers");
2653
- if (!fs.existsSync(customersDir)) return { content: [{
2654
- type: "text",
2655
- text: JSON.stringify({
2656
- deals: [],
2657
- totalWeightedValue: 0,
2658
- byStage: {}
2659
- }, null, 2)
2660
- }] };
2661
- const slugs = fs.readdirSync(customersDir).filter((d) => {
2662
- if (input.filter && !d.includes(input.filter)) return false;
2663
- return fs.statSync(path.join(customersDir, d)).isDirectory();
2664
- });
2694
+ const slugs = listCustomerSlugs(dataDir).filter((d) => !input.filter || d.includes(input.filter));
2665
2695
  const allDeals = [];
2666
2696
  for (const slug of slugs) {
2667
- const pipelinePath = path.join(customersDir, slug, "pipeline.md");
2668
- if (!fs.existsSync(pipelinePath)) continue;
2669
- const { readPipeline } = await import("./pipeline-writer-eufx_0o1.js").then((n) => n.t);
2670
- const deals = await readPipeline(dataDir, slug).catch(() => []);
2697
+ const deals = readPipelineSync(dataDir, slug);
2671
2698
  for (const deal of deals) {
2672
2699
  if (deal.stage === "won" || deal.stage === "lost") continue;
2673
2700
  const prob = deal.probability ?? 50;
@@ -2723,13 +2750,13 @@ Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count,
2723
2750
  }
2724
2751
  //#endregion
2725
2752
  //#region src/mcp/tools/summarize-meeting.ts
2726
- const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2727
- async function handleSummarizeMeeting(input, dataDir = DATA_DIR$42) {
2753
+ const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2754
+ async function handleSummarizeMeeting(input, dataDir = DATA_DIR$43) {
2728
2755
  try {
2729
2756
  let summary = input.transcript.slice(0, 400);
2730
2757
  let nextSteps = [];
2731
2758
  try {
2732
- const { callLlm } = await import("./llm-Z8RIYkpF.js").then((n) => n.n);
2759
+ const { callLlm } = await import("./llm-BnSUBisu.js").then((n) => n.n);
2733
2760
  const response = await callLlm(`Summarize this meeting transcript in 3-5 sentences and extract action items.\n\nTranscript:\n${input.transcript.slice(0, 3e3)}\n\nRespond as JSON: { "summary": "...", "nextSteps": ["..."] }`);
2734
2761
  const parsed = JSON.parse(response);
2735
2762
  summary = parsed.summary ?? summary;
@@ -2843,18 +2870,12 @@ function stagesPath(dataDir) {
2843
2870
  return path.join(dataDir, ".agentic", "pipeline-stages.json");
2844
2871
  }
2845
2872
  function getPipelineStages(dataDir) {
2846
- const p = stagesPath(dataDir);
2847
- if (!fs.existsSync(p)) return DEFAULT_STAGES;
2848
- try {
2849
- return JSON.parse(fs.readFileSync(p, "utf-8"));
2850
- } catch {
2851
- return DEFAULT_STAGES;
2852
- }
2873
+ return readJsonFile(stagesPath(dataDir), DEFAULT_STAGES);
2853
2874
  }
2854
2875
  //#endregion
2855
2876
  //#region src/mcp/tools/get-pipeline-stages.ts
2856
- const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2857
- async function handleGetPipelineStages(_input, dataDir = DATA_DIR$41) {
2877
+ const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2878
+ async function handleGetPipelineStages(_input, dataDir = DATA_DIR$42) {
2858
2879
  const stages = getPipelineStages(dataDir);
2859
2880
  return { content: [{
2860
2881
  type: "text",
@@ -2872,21 +2893,18 @@ function registerGetPipelineStages(server) {
2872
2893
  //#region src/core/cross-customer.ts
2873
2894
  async function searchAcrossCustomers(dataDir, query, limit = 5, excludeSlug) {
2874
2895
  const slugs = listCustomerSlugs(dataDir).filter((d) => d !== excludeSlug);
2875
- const allResults = [];
2876
- for (const slug of slugs) {
2877
- const results = await searchKnowledge(dataDir, slug, query, 2);
2878
- for (const r of results) allResults.push({
2896
+ return (await Promise.all(slugs.map(async (slug) => {
2897
+ return (await searchKnowledge(dataDir, slug, query, 2)).map((r) => ({
2879
2898
  slug,
2880
2899
  relevantContent: r.content.slice(0, 200),
2881
2900
  score: r.score
2882
- });
2883
- }
2884
- return allResults.sort((a, b) => b.score - a.score).slice(0, limit);
2901
+ }));
2902
+ }))).flat().sort((a, b) => b.score - a.score).slice(0, limit);
2885
2903
  }
2886
2904
  //#endregion
2887
2905
  //#region src/mcp/tools/get-market-intelligence.ts
2888
- const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2889
- async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$40) {
2906
+ const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2907
+ async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$41) {
2890
2908
  const excludeSlug = input.excludeCurrentCustomer ? input.slug : void 0;
2891
2909
  const all = listCustomerSlugs(dataDir);
2892
2910
  const totalCustomersSearched = excludeSlug ? all.filter((s) => s !== excludeSlug).length : all.length;
@@ -2917,7 +2935,7 @@ function registerGetMarketIntelligence(server) {
2917
2935
  }
2918
2936
  //#endregion
2919
2937
  //#region src/mcp/tools/get-relationship-graph.ts
2920
- const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2938
+ const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2921
2939
  function summarizeNode(n) {
2922
2940
  return {
2923
2941
  id: n.id,
@@ -2925,7 +2943,7 @@ function summarizeNode(n) {
2925
2943
  email: n.properties["email"]
2926
2944
  };
2927
2945
  }
2928
- async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$39) {
2946
+ async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$40) {
2929
2947
  try {
2930
2948
  const graph = readGraph(dataDir, input.slug);
2931
2949
  const stakeholders = getStakeholders(graph);
@@ -2993,9 +3011,9 @@ Returns: {
2993
3011
  }
2994
3012
  //#endregion
2995
3013
  //#region src/mcp/tools/get-relationship-health.ts
2996
- const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3014
+ const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2997
3015
  const MAX_HEALTH_AGE_MS = 3600 * 1e3;
2998
- async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$38) {
3016
+ async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$39) {
2999
3017
  try {
3000
3018
  let health = readHealth(dataDir, input.slug);
3001
3019
  if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {
@@ -3075,8 +3093,7 @@ async function writePlaybook(dataDir, slug, playbook) {
3075
3093
  const filePath = path.join(dir, `${playbook.name}.md`);
3076
3094
  await withFileQueue(filePath, async () => {
3077
3095
  fs.mkdirSync(dir, { recursive: true });
3078
- const raw = matter.stringify(playbook.content, playbook.frontmatter);
3079
- fs.writeFileSync(filePath, raw, "utf-8");
3096
+ writeFileAtomic(filePath, matter.stringify(playbook.content, playbook.frontmatter));
3080
3097
  });
3081
3098
  }
3082
3099
  function toKebabCase(name) {
@@ -3278,11 +3295,10 @@ function writeAgentQueue(dataDir, slug, queue) {
3278
3295
  const p = agentQueuePath(dataDir, slug);
3279
3296
  const dir = path.dirname(p);
3280
3297
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
3281
- const updated = {
3298
+ writeJsonFile(p, {
3282
3299
  ...queue,
3283
3300
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3284
- };
3285
- fs.writeFileSync(p, JSON.stringify(updated, null, 2), "utf-8");
3301
+ });
3286
3302
  }
3287
3303
  function makeActionId() {
3288
3304
  return `da_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
@@ -3317,17 +3333,9 @@ async function observeDeal(dataDir, slug, dealName, today) {
3317
3333
  const deal = (await readPipeline(dataDir, slug).catch(() => [])).find((d) => d.name.toLowerCase() === dealName.toLowerCase());
3318
3334
  if (!deal) return null;
3319
3335
  const todayDate = new Date(today);
3320
- const updatedDate = deal.updated ? new Date(deal.updated) : todayDate;
3321
- const daysSinceLastActivity = Math.floor((todayDate.getTime() - updatedDate.getTime()) / 864e5);
3322
- const daysInCurrentStage = daysSinceLastActivity;
3323
- const daysToClose = deal.close_date && deal.close_date.trim() !== "" ? Math.floor((new Date(deal.close_date).getTime() - todayDate.getTime()) / 864e5) : void 0;
3324
- const dealHealthScore = scoreDeal(deal, {
3325
- daysSinceLastActivity,
3326
- daysInCurrentStage,
3327
- ...daysToClose !== void 0 ? { daysToClose } : {},
3328
- ...deal.probability !== void 0 ? { probability: deal.probability } : {}
3329
- });
3330
- const health = computeCustomerHealth(dataDir, slug, today);
3336
+ const { daysSinceLastActivity, daysInCurrentStage, daysToClose } = deriveDealTiming(deal, todayDate);
3337
+ const dealHealthScore = scoreDealForToday(deal, todayDate);
3338
+ const health = readHealth(dataDir, slug) ?? computeCustomerHealth(dataDir, slug, today);
3331
3339
  const atRiskContacts = health.contacts.filter((c) => c.riskFlags.length > 0).map((c) => c.email ?? c.contactId);
3332
3340
  const coldContacts = health.contacts.filter((c) => c.trend === "cold").map((c) => c.email ?? c.contactId);
3333
3341
  const stakeholders = getStakeholders(readGraph(dataDir, slug));
@@ -3555,7 +3563,7 @@ async function executeAction(action, dataDir) {
3555
3563
  if (!slug) return "skipped";
3556
3564
  switch (action.type) {
3557
3565
  case "log_interaction": {
3558
- const { appendInteraction } = await import("./interactions-writer-DO3KcSR3.js").then((n) => n.r);
3566
+ const { appendInteraction } = await import("./interactions-writer-ZQcpFOh9.js").then((n) => n.r);
3559
3567
  await appendInteraction(dataDir, slug, {
3560
3568
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
3561
3569
  type: action.payload["type"] ?? "Note",
@@ -3568,7 +3576,7 @@ async function executeAction(action, dataDir) {
3568
3576
  return "executed";
3569
3577
  }
3570
3578
  case "schedule_meeting": {
3571
- const { appendInteraction } = await import("./interactions-writer-DO3KcSR3.js").then((n) => n.r);
3579
+ const { appendInteraction } = await import("./interactions-writer-ZQcpFOh9.js").then((n) => n.r);
3572
3580
  await appendInteraction(dataDir, slug, {
3573
3581
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
3574
3582
  type: "Note",
@@ -3674,8 +3682,8 @@ async function runDealAgent(config, dataDir, llmFn = callLlm) {
3674
3682
  }
3675
3683
  //#endregion
3676
3684
  //#region src/mcp/tools/run-deal-agent.ts
3677
- const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3678
- async function handleRunDealAgent(input, dataDir = DATA_DIR$37) {
3685
+ const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3686
+ async function handleRunDealAgent(input, dataDir = DATA_DIR$38) {
3679
3687
  try {
3680
3688
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3681
3689
  const result = await runDealAgent({
@@ -3742,8 +3750,8 @@ Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], tr
3742
3750
  }
3743
3751
  //#endregion
3744
3752
  //#region src/mcp/tools/approve-agent-action.ts
3745
- const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3746
- async function handleApproveAgentAction(input, dataDir = DATA_DIR$36) {
3753
+ const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3754
+ async function handleApproveAgentAction(input, dataDir = DATA_DIR$37) {
3747
3755
  try {
3748
3756
  const queue = readAgentQueue(dataDir, input.slug);
3749
3757
  const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);
@@ -4003,8 +4011,8 @@ async function buildSimulationInput(dataDir, horizon, today, externalSignals = [
4003
4011
  }
4004
4012
  //#endregion
4005
4013
  //#region src/mcp/tools/simulate-revenue.ts
4006
- const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4007
- async function handleSimulateRevenue(input, dataDir = DATA_DIR$35) {
4014
+ const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4015
+ async function handleSimulateRevenue(input, dataDir = DATA_DIR$36) {
4008
4016
  try {
4009
4017
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4010
4018
  const horizon = input.horizon ?? "quarter";
@@ -4062,8 +4070,8 @@ Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMo
4062
4070
  }
4063
4071
  //#endregion
4064
4072
  //#region src/mcp/tools/get-playbook.ts
4065
- const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4066
- async function handleGetPlaybook(input, dataDir = DATA_DIR$34) {
4073
+ const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4074
+ async function handleGetPlaybook(input, dataDir = DATA_DIR$35) {
4067
4075
  try {
4068
4076
  const playbooks = listPlaybooks(dataDir, input.slug);
4069
4077
  if (!(input.stage !== void 0 || input.value !== void 0 || input.healthScore !== void 0)) return { content: [{
@@ -4148,12 +4156,12 @@ Returns: { matches: [{ name, score, trigger, successRate, usedCount, content }],
4148
4156
  ...healthScore !== void 0 ? { healthScore } : {},
4149
4157
  ...daysSinceContact !== void 0 ? { daysSinceContact } : {},
4150
4158
  ...championPresent !== void 0 ? { championPresent } : {}
4151
- }, DATA_DIR$34));
4159
+ }, DATA_DIR$35));
4152
4160
  }
4153
4161
  //#endregion
4154
4162
  //#region src/mcp/tools/create-playbook.ts
4155
- const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4156
- async function handleCreatePlaybook(input, dataDir = DATA_DIR$33) {
4163
+ const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4164
+ async function handleCreatePlaybook(input, dataDir = DATA_DIR$34) {
4157
4165
  try {
4158
4166
  const name = toKebabCase(input.name);
4159
4167
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -4226,12 +4234,12 @@ Returns: { success: true, playbook: { name, trigger, successRate, path } }`,
4226
4234
  trigger,
4227
4235
  content,
4228
4236
  ...successRate !== void 0 ? { successRate } : {}
4229
- }, DATA_DIR$33));
4237
+ }, DATA_DIR$34));
4230
4238
  }
4231
4239
  //#endregion
4232
4240
  //#region src/mcp/tools/list-playbooks.ts
4233
- const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4234
- async function handleListPlaybooks(input, dataDir = DATA_DIR$32) {
4241
+ const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4242
+ async function handleListPlaybooks(input, dataDir = DATA_DIR$33) {
4235
4243
  try {
4236
4244
  const playbooks = listPlaybooks(dataDir, input.slug);
4237
4245
  return { content: [{
@@ -4270,12 +4278,12 @@ Args:
4270
4278
 
4271
4279
  Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,
4272
4280
  inputSchema: z.object({ slug: z.string().describe("Customer ID") })
4273
- }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$32));
4281
+ }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$33));
4274
4282
  }
4275
4283
  //#endregion
4276
4284
  //#region src/mcp/tools/distill-playbook.ts
4277
- const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4278
- async function handleDistillPlaybook(input, dataDir = DATA_DIR$31, llmFn = callLlm) {
4285
+ const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4286
+ async function handleDistillPlaybook(input, dataDir = DATA_DIR$32, llmFn = callLlm) {
4279
4287
  try {
4280
4288
  const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);
4281
4289
  if (!result.ok) {
@@ -4334,7 +4342,7 @@ Returns: { success: true, playbook: { name, trigger, successRate, path }, reason
4334
4342
  slug,
4335
4343
  dealName,
4336
4344
  outcome
4337
- }, DATA_DIR$31));
4345
+ }, DATA_DIR$32));
4338
4346
  }
4339
4347
  //#endregion
4340
4348
  //#region src/core/goal-engine.ts
@@ -4552,8 +4560,8 @@ function getActiveGoals(dataDir) {
4552
4560
  }
4553
4561
  //#endregion
4554
4562
  //#region src/mcp/tools/pursue-goal.ts
4555
- const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4556
- async function handlePursueGoal(input, dataDir = DATA_DIR$30, options = {}) {
4563
+ const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4564
+ async function handlePursueGoal(input, dataDir = DATA_DIR$31, options = {}) {
4557
4565
  try {
4558
4566
  enforceRbac(dataDir, "pursue_goal");
4559
4567
  const goal = await pursueGoal(dataDir, {
@@ -4616,12 +4624,12 @@ Returns: { goalId, description, target, deadline, decomposition: { analysis, cur
4616
4624
  goal,
4617
4625
  deadline,
4618
4626
  ...context !== void 0 ? { context } : {}
4619
- }, DATA_DIR$30));
4627
+ }, DATA_DIR$31));
4620
4628
  }
4621
4629
  //#endregion
4622
4630
  //#region src/mcp/tools/get-goal-status.ts
4623
- const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4624
- async function handleGetGoalStatus(input, dataDir = DATA_DIR$29) {
4631
+ const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4632
+ async function handleGetGoalStatus(input, dataDir = DATA_DIR$30) {
4625
4633
  try {
4626
4634
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4627
4635
  const allGoals = input.goalId ? readGoals(dataDir).filter((g) => g.id === input.goalId) : getActiveGoals(dataDir);
@@ -4680,17 +4688,17 @@ Args:
4680
4688
 
4681
4689
  Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,
4682
4690
  inputSchema: z.object({ goalId: z.string().optional().describe("Specific goal ID (omit for all active goals)") })
4683
- }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$29));
4691
+ }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$30));
4684
4692
  }
4685
4693
  //#endregion
4686
4694
  //#region src/mcp/tools/register-push-subscription.ts
4687
- const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4695
+ const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4688
4696
  const VALID_PROVIDERS = [
4689
4697
  "gmail",
4690
4698
  "microsoft-graph",
4691
4699
  "slack"
4692
4700
  ];
4693
- async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$28) {
4701
+ async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$29) {
4694
4702
  try {
4695
4703
  if (!VALID_PROVIDERS.includes(input.provider)) return { content: [{
4696
4704
  type: "text",
@@ -4776,12 +4784,12 @@ Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning
4776
4784
  ...microsoftResource !== void 0 ? { microsoftResource } : {},
4777
4785
  ...slackTeamId !== void 0 ? { slackTeamId } : {},
4778
4786
  ...slackChannelId !== void 0 ? { slackChannelId } : {}
4779
- }, DATA_DIR$28));
4787
+ }, DATA_DIR$29));
4780
4788
  }
4781
4789
  //#endregion
4782
4790
  //#region src/mcp/tools/get-push-status.ts
4783
- const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4784
- async function handleGetPushStatus(input, dataDir = DATA_DIR$27) {
4791
+ const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4792
+ async function handleGetPushStatus(input, dataDir = DATA_DIR$28) {
4785
4793
  try {
4786
4794
  let subs = await readSubscriptions(dataDir);
4787
4795
  if (input.slug) subs = subs.filter((s) => s.slug === input.slug);
@@ -4853,7 +4861,7 @@ Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHou
4853
4861
  }, async ({ slug, provider }) => handleGetPushStatus({
4854
4862
  ...slug !== void 0 ? { slug } : {},
4855
4863
  ...provider !== void 0 ? { provider } : {}
4856
- }, DATA_DIR$27));
4864
+ }, DATA_DIR$28));
4857
4865
  }
4858
4866
  //#endregion
4859
4867
  //#region src/core/org-intelligence.ts
@@ -4919,8 +4927,8 @@ function deriveRecommendation(people, missingRoles) {
4919
4927
  }
4920
4928
  //#endregion
4921
4929
  //#region src/mcp/tools/get-org-intelligence.ts
4922
- const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4923
- async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$26) {
4930
+ const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4931
+ async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$27) {
4924
4932
  try {
4925
4933
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4926
4934
  const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);
@@ -4971,15 +4979,7 @@ async function buildDealRoom(dataDir, slug, dealName, today) {
4971
4979
  });
4972
4980
  const todayDate = new Date(today);
4973
4981
  const dealHealth = pipelineDeals.filter((d) => d.stage !== "won" && d.stage !== "lost").map((deal) => {
4974
- const updatedDate = deal.updated ? new Date(deal.updated) : todayDate;
4975
- const daysSinceLastActivity = Math.floor((todayDate.getTime() - updatedDate.getTime()) / 864e5);
4976
- const daysToClose = deal.close_date ? Math.floor((new Date(deal.close_date).getTime() - todayDate.getTime()) / 864e5) : void 0;
4977
- const scored = scoreDeal(deal, {
4978
- daysSinceLastActivity,
4979
- daysInCurrentStage: daysSinceLastActivity,
4980
- ...daysToClose !== void 0 ? { daysToClose } : {},
4981
- ...deal.probability !== void 0 ? { probability: deal.probability } : {}
4982
- });
4982
+ const scored = scoreDealForToday(deal, todayDate);
4983
4983
  return {
4984
4984
  deal: deal.name,
4985
4985
  stage: deal.stage,
@@ -5061,8 +5061,8 @@ function buildExecutiveSummary(slug, dealName, stakeholders, overallHealth, sim,
5061
5061
  }
5062
5062
  //#endregion
5063
5063
  //#region src/mcp/tools/open-deal-room.ts
5064
- const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5065
- async function handleOpenDealRoom(input, dataDir = DATA_DIR$25) {
5064
+ const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5065
+ async function handleOpenDealRoom(input, dataDir = DATA_DIR$26) {
5066
5066
  try {
5067
5067
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5068
5068
  const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);
@@ -5145,8 +5145,8 @@ async function buildDailyBriefing(dataDir, today) {
5145
5145
  }
5146
5146
  //#endregion
5147
5147
  //#region src/mcp/tools/get-proactive-briefing.ts
5148
- const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5149
- async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$24) {
5148
+ const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5149
+ async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$25) {
5150
5150
  try {
5151
5151
  const briefing = await buildDailyBriefing(dataDir, input.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
5152
5152
  return { content: [{
@@ -5246,15 +5246,15 @@ function getTemplate(dataDir, id) {
5246
5246
  }
5247
5247
  //#endregion
5248
5248
  //#region src/mcp/tools/list-email-templates.ts
5249
- const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5250
- async function handleListEmailTemplates(input, dataDir = DATA_DIR$23) {
5249
+ const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5250
+ async function handleListEmailTemplates(input, dataDir = DATA_DIR$24) {
5251
5251
  const summary = listTemplates(dataDir, input.category ? { category: input.category } : {}).map(({ body: _body, ...meta }) => meta);
5252
5252
  return { content: [{
5253
5253
  type: "text",
5254
5254
  text: JSON.stringify(summary, null, 2)
5255
5255
  }] };
5256
5256
  }
5257
- function registerListEmailTemplates(server, dataDir = DATA_DIR$23) {
5257
+ function registerListEmailTemplates(server, dataDir = DATA_DIR$24) {
5258
5258
  server.registerTool("list_email_templates", {
5259
5259
  description: "List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').",
5260
5260
  inputSchema: z.object({ category: z.string().optional().describe("Filter by category") })
@@ -5288,8 +5288,8 @@ async function buildVariablesFromCustomer(dataDir, slug) {
5288
5288
  }
5289
5289
  //#endregion
5290
5290
  //#region src/mcp/tools/get-email-template.ts
5291
- const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5292
- async function handleGetEmailTemplate(input, dataDir = DATA_DIR$22) {
5291
+ const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5292
+ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$23) {
5293
5293
  const tmpl = getTemplate(dataDir, input.id);
5294
5294
  if (!tmpl) return { content: [{
5295
5295
  type: "text",
@@ -5305,7 +5305,7 @@ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$22) {
5305
5305
  }, null, 2)
5306
5306
  }] };
5307
5307
  }
5308
- function registerGetEmailTemplate(server, dataDir = DATA_DIR$22) {
5308
+ function registerGetEmailTemplate(server, dataDir = DATA_DIR$23) {
5309
5309
  server.registerTool("get_email_template", {
5310
5310
  description: "Get a specific email template by ID, including its body and detected variables.",
5311
5311
  inputSchema: z.object({ id: z.string().describe("Template ID (e.g. 'enterprise-intro')") })
@@ -5313,8 +5313,8 @@ function registerGetEmailTemplate(server, dataDir = DATA_DIR$22) {
5313
5313
  }
5314
5314
  //#endregion
5315
5315
  //#region src/mcp/tools/draft-email.ts
5316
- const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5317
- async function handleDraftEmail(input, dataDir = DATA_DIR$21) {
5316
+ const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5317
+ async function handleDraftEmail(input, dataDir = DATA_DIR$22) {
5318
5318
  const tmpl = getTemplate(dataDir, input.templateId);
5319
5319
  if (!tmpl) return { content: [{
5320
5320
  type: "text",
@@ -5328,17 +5328,17 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$21) {
5328
5328
  const interpolatedBody = interpolate(tmpl.body, vars);
5329
5329
  let effectiveTone = input.tone;
5330
5330
  if (!effectiveTone) {
5331
- const { resolveTone, toneInstruction } = await import("./tone-vNb2DAAD.js");
5331
+ const { resolveTone, toneInstruction } = await import("./tone-mXSftvTn.js");
5332
5332
  const instr = toneInstruction(resolveTone(dataDir, input.slug));
5333
5333
  if (instr) effectiveTone = instr;
5334
5334
  }
5335
5335
  let body = interpolatedBody;
5336
5336
  let polished = false;
5337
5337
  if (effectiveTone) try {
5338
- const { callLlm } = await import("./llm-Z8RIYkpF.js").then((n) => n.n);
5338
+ const { callLlm } = await import("./llm-BnSUBisu.js").then((n) => n.n);
5339
5339
  const refined = await callLlm(`Rewrite the following email in a ${effectiveTone} tone. Keep the same language, preserve all names and facts, and do not invent details. Return ONLY the rewritten email body, no preamble.\n\n---\n${interpolatedBody}`);
5340
5340
  if (refined && refined.trim()) {
5341
- const { labelAiContent } = await import("./compliance-B1kk5-YS.js").then((n) => n.t);
5341
+ const { labelAiContent } = await import("./compliance-kq0xHRw3.js").then((n) => n.t);
5342
5342
  body = labelAiContent(refined.trim());
5343
5343
  polished = true;
5344
5344
  }
@@ -5358,7 +5358,7 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$21) {
5358
5358
  }, null, 2)
5359
5359
  }] };
5360
5360
  }
5361
- function registerDraftEmail(server, dataDir = DATA_DIR$21) {
5361
+ function registerDraftEmail(server, dataDir = DATA_DIR$22) {
5362
5362
  server.registerTool("draft_email", {
5363
5363
  description: `Draft a personalized email for a customer using a stored template.
5364
5364
  Variables are auto-filled from the customer's main_facts.md. Override any variable manually.
@@ -5466,8 +5466,8 @@ async function updateEnrollment(dataDir, id, updates) {
5466
5466
  }
5467
5467
  //#endregion
5468
5468
  //#region src/mcp/tools/enroll-in-sequence.ts
5469
- const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5470
- async function handleEnrollInSequence(input, dataDir = DATA_DIR$20) {
5469
+ const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5470
+ async function handleEnrollInSequence(input, dataDir = DATA_DIR$21) {
5471
5471
  const sequence = getSequence(dataDir, input.sequenceId);
5472
5472
  if (!sequence) return { content: [{
5473
5473
  type: "text",
@@ -5499,7 +5499,7 @@ async function handleEnrollInSequence(input, dataDir = DATA_DIR$20) {
5499
5499
  })
5500
5500
  }] };
5501
5501
  }
5502
- function registerEnrollInSequence(server, dataDir = DATA_DIR$20) {
5502
+ function registerEnrollInSequence(server, dataDir = DATA_DIR$21) {
5503
5503
  server.registerTool("enroll_in_sequence", {
5504
5504
  description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.
5505
5505
  Returns: { enrollmentId, sequenceName, totalSteps }`,
@@ -5516,8 +5516,8 @@ Returns: { enrollmentId, sequenceName, totalSteps }`,
5516
5516
  }
5517
5517
  //#endregion
5518
5518
  //#region src/mcp/tools/list-sequence-enrollments.ts
5519
- const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5520
- async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$19) {
5519
+ const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5520
+ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$20) {
5521
5521
  let enrollments = readEnrollments(dataDir);
5522
5522
  if (input.slug !== void 0) enrollments = enrollments.filter((e) => e.slug === input.slug);
5523
5523
  if (input.status !== void 0) enrollments = enrollments.filter((e) => e.status === input.status);
@@ -5526,7 +5526,7 @@ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$19) {
5526
5526
  text: JSON.stringify({ enrollments }, null, 2)
5527
5527
  }] };
5528
5528
  }
5529
- function registerListSequenceEnrollments(server, dataDir = DATA_DIR$19) {
5529
+ function registerListSequenceEnrollments(server, dataDir = DATA_DIR$20) {
5530
5530
  server.registerTool("list_sequence_enrollments", {
5531
5531
  description: `List email sequence enrollments. Filter by customer slug or status.
5532
5532
  Returns: { enrollments: SequenceEnrollment[] }`,
@@ -5545,8 +5545,8 @@ Returns: { enrollments: SequenceEnrollment[] }`,
5545
5545
  }
5546
5546
  //#endregion
5547
5547
  //#region src/mcp/tools/unenroll-from-sequence.ts
5548
- const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5549
- async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$18) {
5548
+ const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5549
+ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$19) {
5550
5550
  if (!await updateEnrollment(dataDir, input.enrollmentId, { status: "paused" })) return { content: [{
5551
5551
  type: "text",
5552
5552
  text: JSON.stringify({
@@ -5559,7 +5559,7 @@ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$18) {
5559
5559
  text: JSON.stringify({ success: true })
5560
5560
  }] };
5561
5561
  }
5562
- function registerUnenrollFromSequence(server, dataDir = DATA_DIR$18) {
5562
+ function registerUnenrollFromSequence(server, dataDir = DATA_DIR$19) {
5563
5563
  server.registerTool("unenroll_from_sequence", {
5564
5564
  description: `Unenroll (pause) a contact from an email sequence. Sets status to "paused" (soft delete).
5565
5565
  Returns: { success: boolean }`,
@@ -5568,8 +5568,8 @@ Returns: { success: boolean }`,
5568
5568
  }
5569
5569
  //#endregion
5570
5570
  //#region src/mcp/tools/list-sequences.ts
5571
- const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5572
- async function handleListSequences(_input, dataDir = DATA_DIR$17) {
5571
+ const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5572
+ async function handleListSequences(_input, dataDir = DATA_DIR$18) {
5573
5573
  const sequences = listSequences(dataDir);
5574
5574
  const enrollments = readEnrollments(dataDir);
5575
5575
  const result = sequences.map((seq) => ({
@@ -5583,7 +5583,7 @@ async function handleListSequences(_input, dataDir = DATA_DIR$17) {
5583
5583
  text: JSON.stringify({ sequences: result }, null, 2)
5584
5584
  }] };
5585
5585
  }
5586
- function registerListSequences(server, dataDir = DATA_DIR$17) {
5586
+ function registerListSequences(server, dataDir = DATA_DIR$18) {
5587
5587
  server.registerTool("list_sequences", {
5588
5588
  description: `List all email sequences with step count and enrollment count.
5589
5589
  Returns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,
@@ -5633,7 +5633,7 @@ function buildHtml(quote, config, customerName) {
5633
5633
  <p><strong>${config.companyName ?? ""}</strong><br>${config.companyAddress ?? ""}<br>${config.vatId ? `USt-IdNr.: ${config.vatId}` : ""}</p>
5634
5634
  <hr>
5635
5635
  <p><strong>An:</strong> ${customerName}</p>
5636
- <p><strong>Datum:</strong> ${quote.createdAt.slice(0, 10)} &nbsp;&nbsp; <strong>Gültig bis:</strong> ${quote.validUntil}</p>
5636
+ <p><strong>Date:</strong> ${quote.createdAt.slice(0, 10)} &nbsp;&nbsp; <strong>Valid until:</strong> ${quote.validUntil}</p>
5637
5637
  <h2>Leistungen</h2>
5638
5638
  <table>
5639
5639
  <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>
@@ -5712,15 +5712,14 @@ async function generateQuote(dataDir, input) {
5712
5712
  status: "draft",
5713
5713
  htmlPath
5714
5714
  };
5715
- fs.writeFileSync(path.join(dir, `${quoteNumber}.json`), JSON.stringify(quote, null, 2), "utf-8");
5716
- const html = buildHtml(quote, config, readCustomerName(dataDir, input.slug));
5717
- fs.writeFileSync(htmlPath, html, "utf-8");
5715
+ writeFileAtomic(path.join(dir, `${quoteNumber}.json`), JSON.stringify(quote, null, 2));
5716
+ writeFileAtomic(htmlPath, buildHtml(quote, config, readCustomerName(dataDir, input.slug)));
5718
5717
  return quote;
5719
5718
  }
5720
5719
  //#endregion
5721
5720
  //#region src/mcp/tools/generate-quote.ts
5722
- const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5723
- async function handleGenerateQuote(input, dataDir = DATA_DIR$16) {
5721
+ const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5722
+ async function handleGenerateQuote(input, dataDir = DATA_DIR$17) {
5724
5723
  try {
5725
5724
  const quote = await generateQuote(dataDir, input);
5726
5725
  return { content: [{
@@ -5744,7 +5743,7 @@ async function handleGenerateQuote(input, dataDir = DATA_DIR$16) {
5744
5743
  }] };
5745
5744
  }
5746
5745
  }
5747
- function registerGenerateQuote(server, dataDir = DATA_DIR$16) {
5746
+ function registerGenerateQuote(server, dataDir = DATA_DIR$17) {
5748
5747
  server.registerTool("generate_quote", {
5749
5748
  description: `Generate a professional HTML quote/offer for a customer deal.
5750
5749
  Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
@@ -5772,8 +5771,8 @@ Returns: { quoteNumber, htmlPath, total, currency, validUntil }`,
5772
5771
  }
5773
5772
  //#endregion
5774
5773
  //#region src/mcp/tools/get-quote-status.ts
5775
- const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5776
- async function handleGetQuoteStatus(input, dataDir = DATA_DIR$15) {
5774
+ const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5775
+ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$16) {
5777
5776
  if (input.quoteNumber) {
5778
5777
  const quote = readQuote(dataDir, input.quoteNumber);
5779
5778
  if (!quote) return { content: [{
@@ -5791,7 +5790,7 @@ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$15) {
5791
5790
  text: JSON.stringify({ quotes }, null, 2)
5792
5791
  }] };
5793
5792
  }
5794
- function registerGetQuoteStatus(server, dataDir = DATA_DIR$15) {
5793
+ function registerGetQuoteStatus(server, dataDir = DATA_DIR$16) {
5795
5794
  server.registerTool("get_quote_status", {
5796
5795
  description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).
5797
5796
  Returns quote with status: draft | sent | viewed | accepted | declined`,
@@ -5806,7 +5805,7 @@ Returns quote with status: draft | sent | viewed | accepted | declined`,
5806
5805
  }
5807
5806
  //#endregion
5808
5807
  //#region src/mcp/tools/get-booking-link.ts
5809
- const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5808
+ const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5810
5809
  function loadCalendlyConfig(dataDir) {
5811
5810
  const p = path.join(dataDir, ".agentic", "integrations", "calendly.yaml");
5812
5811
  if (!fs.existsSync(p)) return {};
@@ -5829,7 +5828,7 @@ function readCustomerFacts(dataDir, slug) {
5829
5828
  ...email ? { email } : {}
5830
5829
  };
5831
5830
  }
5832
- async function handleGetBookingLink(input, dataDir = DATA_DIR$14) {
5831
+ async function handleGetBookingLink(input, dataDir = DATA_DIR$15) {
5833
5832
  const config = loadCalendlyConfig(dataDir);
5834
5833
  const apiKey = config.apiKey ?? process.env["CALENDLY_API_KEY"] ?? "";
5835
5834
  if (!apiKey) return { content: [{
@@ -5857,7 +5856,7 @@ async function handleGetBookingLink(input, dataDir = DATA_DIR$14) {
5857
5856
  }] };
5858
5857
  }
5859
5858
  }
5860
- function registerGetBookingLink(server, dataDir = DATA_DIR$14) {
5859
+ function registerGetBookingLink(server, dataDir = DATA_DIR$15) {
5861
5860
  server.registerTool("get_booking_link", {
5862
5861
  description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
5863
5862
  Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
@@ -5905,6 +5904,7 @@ const TICKET_HEADER = "# Tickets\n\n";
5905
5904
  const TABLE_HEADER = `| ID | Title | Status | Priority | Assignee | Created | SLA Due | Resolved |
5906
5905
  |----|-------|--------|----------|----------|---------|---------|---------|`;
5907
5906
  function ticketsPath(dataDir, slug) {
5907
+ assertSafeSlug(slug);
5908
5908
  return path.join(dataDir, "customers", slug, "tickets.md");
5909
5909
  }
5910
5910
  function escapeMd(s) {
@@ -5957,7 +5957,7 @@ async function upsertTicket(dataDir, slug, ticket) {
5957
5957
  const idx = existing.findIndex((t) => t.id === ticket.id);
5958
5958
  if (idx >= 0) existing[idx] = ticket;
5959
5959
  else existing.push(ticket);
5960
- fs.writeFileSync(p, serializeTickets(existing), "utf-8");
5960
+ writeFileAtomic(p, serializeTickets(existing));
5961
5961
  }
5962
5962
  function nextTicketId(tickets) {
5963
5963
  const nums = tickets.map((t) => parseInt(t.id.replace("T-", ""), 10)).filter((n) => !isNaN(n));
@@ -5965,28 +5965,13 @@ function nextTicketId(tickets) {
5965
5965
  return `T-${String(max + 1).padStart(3, "0")}`;
5966
5966
  }
5967
5967
  async function listAllTickets(dataDir, filter) {
5968
- const customersDir = path.join(dataDir, "customers");
5969
- if (!fs.existsSync(customersDir)) return [];
5970
- const slugs = filter?.slug ? [filter.slug] : fs.readdirSync(customersDir).filter((s) => {
5971
- try {
5972
- return fs.statSync(path.join(customersDir, s)).isDirectory();
5973
- } catch {
5974
- return false;
5975
- }
5976
- });
5977
- const results = [];
5978
- for (const slug of slugs) {
5979
- const tickets = await readTickets(dataDir, slug);
5980
- for (const ticket of tickets) {
5981
- if (filter?.status && ticket.status !== filter.status) continue;
5982
- if (filter?.priority && ticket.priority !== filter.priority) continue;
5983
- if (filter?.assignee && ticket.assignee !== filter.assignee) continue;
5984
- results.push({
5985
- slug,
5986
- ticket
5987
- });
5988
- }
5989
- }
5968
+ const slugs = filter?.slug ? [filter.slug] : listCustomerSlugs(dataDir);
5969
+ const results = (await Promise.all(slugs.map(async (slug) => {
5970
+ return (await readTickets(dataDir, slug)).filter((ticket) => (!filter?.status || ticket.status === filter.status) && (!filter?.priority || ticket.priority === filter.priority) && (!filter?.assignee || ticket.assignee === filter.assignee)).map((ticket) => ({
5971
+ slug,
5972
+ ticket
5973
+ }));
5974
+ }))).flat();
5990
5975
  const priorityOrder = {
5991
5976
  urgent: 0,
5992
5977
  high: 1,
@@ -6041,8 +6026,8 @@ function calcSlaDue(createdDate, priority, rules) {
6041
6026
  }
6042
6027
  //#endregion
6043
6028
  //#region src/mcp/tools/create-ticket.ts
6044
- const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6045
- async function handleCreateTicket(input, dataDir = DATA_DIR$13) {
6029
+ const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6030
+ async function handleCreateTicket(input, dataDir = DATA_DIR$14) {
6046
6031
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6047
6032
  const rules = loadSlaRules(dataDir);
6048
6033
  const priority = input.priority ?? "normal";
@@ -6064,7 +6049,7 @@ async function handleCreateTicket(input, dataDir = DATA_DIR$13) {
6064
6049
  text: JSON.stringify({ ticket }, null, 2)
6065
6050
  }] };
6066
6051
  }
6067
- function registerCreateTicket(server, dataDir = DATA_DIR$13) {
6052
+ function registerCreateTicket(server, dataDir = DATA_DIR$14) {
6068
6053
  server.registerTool("create_ticket", {
6069
6054
  description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.
6070
6055
  Returns: { ticket } with id T-NNN, status=open, slaDue`,
@@ -6090,8 +6075,8 @@ Returns: { ticket } with id T-NNN, status=open, slaDue`,
6090
6075
  }
6091
6076
  //#endregion
6092
6077
  //#region src/mcp/tools/update-ticket.ts
6093
- const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6094
- async function handleUpdateTicket(input, dataDir = DATA_DIR$12) {
6078
+ const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6079
+ async function handleUpdateTicket(input, dataDir = DATA_DIR$13) {
6095
6080
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6096
6081
  if (!ticket) return { content: [{
6097
6082
  type: "text",
@@ -6110,7 +6095,7 @@ async function handleUpdateTicket(input, dataDir = DATA_DIR$12) {
6110
6095
  text: JSON.stringify({ ticket: updated }, null, 2)
6111
6096
  }] };
6112
6097
  }
6113
- function registerUpdateTicket(server, dataDir = DATA_DIR$12) {
6098
+ function registerUpdateTicket(server, dataDir = DATA_DIR$13) {
6114
6099
  server.registerTool("update_ticket", {
6115
6100
  description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
6116
6101
  Returns: { ticket }`,
@@ -6135,8 +6120,8 @@ Returns: { ticket }`,
6135
6120
  }
6136
6121
  //#endregion
6137
6122
  //#region src/mcp/tools/list-tickets.ts
6138
- const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6139
- async function handleListTickets(input, dataDir = DATA_DIR$11) {
6123
+ const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6124
+ async function handleListTickets(input, dataDir = DATA_DIR$12) {
6140
6125
  const results = await listAllTickets(dataDir, {
6141
6126
  ...input.slug !== void 0 ? { slug: input.slug } : {},
6142
6127
  ...input.status !== void 0 ? { status: input.status } : {},
@@ -6148,7 +6133,7 @@ async function handleListTickets(input, dataDir = DATA_DIR$11) {
6148
6133
  text: JSON.stringify({ tickets: results }, null, 2)
6149
6134
  }] };
6150
6135
  }
6151
- function registerListTickets(server, dataDir = DATA_DIR$11) {
6136
+ function registerListTickets(server, dataDir = DATA_DIR$12) {
6152
6137
  server.registerTool("list_tickets", {
6153
6138
  description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.
6154
6139
  Returns: { tickets: Array<{ slug, ticket }> }`,
@@ -6178,8 +6163,8 @@ Returns: { tickets: Array<{ slug, ticket }> }`,
6178
6163
  }
6179
6164
  //#endregion
6180
6165
  //#region src/mcp/tools/close-ticket.ts
6181
- const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6182
- async function handleCloseTicket(input, dataDir = DATA_DIR$10) {
6166
+ const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6167
+ async function handleCloseTicket(input, dataDir = DATA_DIR$11) {
6183
6168
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6184
6169
  if (!ticket) return { content: [{
6185
6170
  type: "text",
@@ -6206,7 +6191,7 @@ async function handleCloseTicket(input, dataDir = DATA_DIR$10) {
6206
6191
  text: JSON.stringify({ ticket: updated }, null, 2)
6207
6192
  }] };
6208
6193
  }
6209
- function registerCloseTicket(server, dataDir = DATA_DIR$10) {
6194
+ function registerCloseTicket(server, dataDir = DATA_DIR$11) {
6210
6195
  server.registerTool("close_ticket", {
6211
6196
  description: `Close a support ticket. Optionally logs the resolution as an interaction.
6212
6197
  Returns: { ticket } with status=closed`,
@@ -6316,7 +6301,7 @@ async function recordSurveyResponse(dataDir, token, score, comment) {
6316
6301
  const dir = responsesDir(dataDir, pending.surveyId);
6317
6302
  fs.mkdirSync(dir, { recursive: true });
6318
6303
  const filename = `${pending.slug}_${pending.contactEmail.replace("@", "_at_")}_${Date.now()}.json`;
6319
- fs.writeFileSync(path.join(dir, filename), JSON.stringify(response, null, 2), "utf-8");
6304
+ writeFileAtomic(path.join(dir, filename), JSON.stringify(response, null, 2));
6320
6305
  fs.unlinkSync(path.join(pendingDir, file));
6321
6306
  return response;
6322
6307
  } catch {
@@ -6356,12 +6341,12 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
6356
6341
  contactEmail,
6357
6342
  sentAt: (/* @__PURE__ */ new Date()).toISOString()
6358
6343
  };
6359
- fs.writeFileSync(path.join(pendingDir, filename), JSON.stringify(pending, null, 2), "utf-8");
6344
+ writeFileAtomic(path.join(pendingDir, filename), JSON.stringify(pending, null, 2));
6360
6345
  }
6361
6346
  //#endregion
6362
6347
  //#region src/mcp/tools/send-nps-survey.ts
6363
- const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6364
- async function handleSendNpsSurvey(input, dataDir = DATA_DIR$9) {
6348
+ const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6349
+ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$10) {
6365
6350
  const survey = getSurvey(dataDir, input.surveyId);
6366
6351
  if (!survey) return { content: [{
6367
6352
  type: "text",
@@ -6382,7 +6367,7 @@ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$9) {
6382
6367
  }, null, 2)
6383
6368
  }] };
6384
6369
  }
6385
- function registerSendNpsSurvey(server, dataDir = DATA_DIR$9) {
6370
+ function registerSendNpsSurvey(server, dataDir = DATA_DIR$10) {
6386
6371
  server.registerTool("send_nps_survey", {
6387
6372
  description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.
6388
6373
  Does NOT send automatically — returns draft for review.
@@ -6402,8 +6387,8 @@ Returns: { token, subject, body, surveyUrl }`,
6402
6387
  }
6403
6388
  //#endregion
6404
6389
  //#region src/mcp/tools/get-survey-results.ts
6405
- const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6406
- async function handleGetSurveyResults(input, dataDir = DATA_DIR$8) {
6390
+ const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6391
+ async function handleGetSurveyResults(input, dataDir = DATA_DIR$9) {
6407
6392
  const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);
6408
6393
  const nps = calcNpsScore(responses);
6409
6394
  const promoters = responses.filter((r) => r.score >= 9).length;
@@ -6429,7 +6414,7 @@ async function handleGetSurveyResults(input, dataDir = DATA_DIR$8) {
6429
6414
  }, null, 2)
6430
6415
  }] };
6431
6416
  }
6432
- function registerGetSurveyResults(server, dataDir = DATA_DIR$8) {
6417
+ function registerGetSurveyResults(server, dataDir = DATA_DIR$9) {
6433
6418
  server.registerTool("get_survey_results", {
6434
6419
  description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.
6435
6420
  Returns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,
@@ -6459,18 +6444,21 @@ const KbArticleSchema = z.object({
6459
6444
  function kbDir(dataDir) {
6460
6445
  return path.join(dataDir, ".agentic", "knowledge-base");
6461
6446
  }
6462
- function listKbArticles(dataDir, opts) {
6463
- const dir = kbDir(dataDir);
6447
+ /** Category subdirectories of the knowledge base. */
6448
+ function kbCategories(dir) {
6464
6449
  if (!fs.existsSync(dir)) return [];
6465
- const results = [];
6466
- const categories = fs.readdirSync(dir).filter((f) => {
6450
+ return fs.readdirSync(dir).filter((f) => {
6467
6451
  try {
6468
6452
  return fs.statSync(path.join(dir, f)).isDirectory();
6469
6453
  } catch {
6470
6454
  return false;
6471
6455
  }
6472
6456
  });
6473
- for (const cat of categories) {
6457
+ }
6458
+ function listKbArticles(dataDir, opts) {
6459
+ const dir = kbDir(dataDir);
6460
+ const results = [];
6461
+ for (const cat of kbCategories(dir)) {
6474
6462
  const catDir = path.join(dir, cat);
6475
6463
  const files = fs.readdirSync(catDir).filter((f) => f.endsWith(".md"));
6476
6464
  for (const file of files) try {
@@ -6490,14 +6478,31 @@ function listKbArticles(dataDir, opts) {
6490
6478
  return results;
6491
6479
  }
6492
6480
  function getKbArticle(dataDir, id) {
6493
- return listKbArticles(dataDir).find((a) => a.id === id) ?? null;
6481
+ if (!isSafePathSegment(id)) return null;
6482
+ const dir = kbDir(dataDir);
6483
+ for (const cat of kbCategories(dir)) {
6484
+ const filePath = path.join(dir, cat, `${id}.md`);
6485
+ if (!fs.existsSync(filePath)) continue;
6486
+ try {
6487
+ const parsed = matter(fs.readFileSync(filePath, "utf-8"));
6488
+ const meta = KbArticleSchema.safeParse(parsed.data);
6489
+ if (!meta.success) return null;
6490
+ return {
6491
+ ...meta.data,
6492
+ body: parsed.content.trim()
6493
+ };
6494
+ } catch {
6495
+ return null;
6496
+ }
6497
+ }
6498
+ return null;
6494
6499
  }
6495
6500
  function writeKbArticle(dataDir, article) {
6496
- const dir = path.join(kbDir(dataDir), article.category);
6497
- fs.mkdirSync(dir, { recursive: true });
6501
+ assertSafePathSegment(article.category, "knowledge-base category");
6502
+ assertSafePathSegment(article.id, "knowledge-base article id");
6498
6503
  const { body, ...meta } = article;
6499
6504
  const content = matter.stringify(body, meta);
6500
- fs.writeFileSync(path.join(dir, `${article.id}.md`), content, "utf-8");
6505
+ writeFileAtomic(path.join(kbDir(dataDir), article.category, `${article.id}.md`), content);
6501
6506
  }
6502
6507
  function searchKbSimple(dataDir, query, opts) {
6503
6508
  const all = listKbArticles(dataDir, opts?.publicOnly ? { publicOnly: true } : {});
@@ -6510,8 +6515,8 @@ function getKbMetaForExport(article) {
6510
6515
  }
6511
6516
  //#endregion
6512
6517
  //#region src/mcp/tools/search-knowledge-base.ts
6513
- const DATA_DIR$7 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6514
- async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$7) {
6518
+ const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6519
+ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$8) {
6515
6520
  const results = searchKbSimple(dataDir, input.query, { ...input.publicOnly ? { publicOnly: true } : {} });
6516
6521
  const limited = (input.category ? results.filter((a) => a.category === input.category) : results).slice(0, input.limit ?? 10);
6517
6522
  return { content: [{
@@ -6526,7 +6531,7 @@ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$7) {
6526
6531
  }, null, 2)
6527
6532
  }] };
6528
6533
  }
6529
- function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$7) {
6534
+ function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$8) {
6530
6535
  server.registerTool("search_knowledge_base", {
6531
6536
  description: `Search the knowledge base for articles. Text search on title, body, and tags.
6532
6537
  Returns: { count, articles[] } with excerpts`,
@@ -6545,8 +6550,8 @@ Returns: { count, articles[] } with excerpts`,
6545
6550
  }
6546
6551
  //#endregion
6547
6552
  //#region src/mcp/tools/create-kb-article.ts
6548
- const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6549
- async function handleCreateKbArticle(input, dataDir = DATA_DIR$6) {
6553
+ const DATA_DIR$7 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6554
+ async function handleCreateKbArticle(input, dataDir = DATA_DIR$7) {
6550
6555
  if (getKbArticle(dataDir, input.id)) return { content: [{
6551
6556
  type: "text",
6552
6557
  text: JSON.stringify({ error: `Article '${input.id}' already exists` })
@@ -6574,7 +6579,7 @@ async function handleCreateKbArticle(input, dataDir = DATA_DIR$6) {
6574
6579
  }, null, 2)
6575
6580
  }] };
6576
6581
  }
6577
- function registerCreateKbArticle(server, dataDir = DATA_DIR$6) {
6582
+ function registerCreateKbArticle(server, dataDir = DATA_DIR$7) {
6578
6583
  server.registerTool("create_kb_article", {
6579
6584
  description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.
6580
6585
  Returns: { id, title, category, path }`,
@@ -6599,8 +6604,8 @@ Returns: { id, title, category, path }`,
6599
6604
  }
6600
6605
  //#endregion
6601
6606
  //#region src/mcp/tools/backup-now.ts
6602
- const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6603
- async function handleBackupNow(input, dataDir = DATA_DIR$5) {
6607
+ const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6608
+ async function handleBackupNow(input, dataDir = DATA_DIR$6) {
6604
6609
  const zipPath = path.join(dataDir, `dxcrm-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.zip`);
6605
6610
  const manifest = await runBackup(zipPath, dataDir, { ...input.remote ? { remote: input.remote } : {} }).catch(() => null);
6606
6611
  if (!manifest) return { content: [{
@@ -6637,8 +6642,8 @@ function registerBackupNow(server) {
6637
6642
  }
6638
6643
  //#endregion
6639
6644
  //#region src/mcp/tools/list-backups.ts
6640
- const DATA_DIR$4 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6641
- async function handleListBackups(input, dataDir = DATA_DIR$4) {
6645
+ const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6646
+ async function handleListBackups(input, dataDir = DATA_DIR$5) {
6642
6647
  const logEntries = readBackupLog(dataDir);
6643
6648
  const fileEntries = listBackupsInDir(dataDir);
6644
6649
  const entries = logEntries.length > 0 ? logEntries : fileEntries;
@@ -6672,8 +6677,8 @@ function registerListBackups(server) {
6672
6677
  }
6673
6678
  //#endregion
6674
6679
  //#region src/mcp/tools/trigger-sync.ts
6675
- const DATA_DIR$3 = process.cwd();
6676
- async function handleTriggerSync(input, dataDir = DATA_DIR$3) {
6680
+ const DATA_DIR$4 = process.cwd();
6681
+ async function handleTriggerSync(input, dataDir = DATA_DIR$4) {
6677
6682
  const auth = getGmailAuth();
6678
6683
  if (!auth) return { content: [{
6679
6684
  type: "text",
@@ -6708,7 +6713,7 @@ async function handleTriggerSync(input, dataDir = DATA_DIR$3) {
6708
6713
  try {
6709
6714
  const sources = JSON.parse(fs.readFileSync(sourcesPath, "utf-8"));
6710
6715
  if (!sources.gmail?.enabled || !sources.gmail.query) continue;
6711
- const { syncGmail } = await import("./gmail-sync-rQaVqKWd.js");
6716
+ const { syncGmail } = await import("./gmail-sync-DIbrPnTK.js");
6712
6717
  const result = await syncGmail({
6713
6718
  slug,
6714
6719
  dataDir,
@@ -6767,8 +6772,8 @@ Returns: { success: boolean, synced: number, skipped: number, customers: [...],
6767
6772
  }
6768
6773
  //#endregion
6769
6774
  //#region src/mcp/tools/get-audit-log.ts
6770
- const DATA_DIR$2 = process.cwd();
6771
- async function handleGetAuditLog(input, dataDir = DATA_DIR$2) {
6775
+ const DATA_DIR$3 = process.cwd();
6776
+ async function handleGetAuditLog(input, dataDir = DATA_DIR$3) {
6772
6777
  const entries = readAuditLog(dataDir);
6773
6778
  const filterOpts = { limit: input.limit ?? 50 };
6774
6779
  if (input.slug !== void 0) filterOpts.slug = input.slug;
@@ -6809,6 +6814,69 @@ Returns: { total: number, returned: number, entries: [{timestamp, actor, tool, s
6809
6814
  });
6810
6815
  }
6811
6816
  //#endregion
6817
+ //#region src/mcp/tools/get-logs.ts
6818
+ const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6819
+ async function handleGetLogs(input, dataDir = DATA_DIR$2) {
6820
+ const query = {
6821
+ ...input.level !== void 0 ? { level: input.level } : {},
6822
+ ...input.component !== void 0 ? { component: input.component } : {},
6823
+ ...input.since !== void 0 ? { since: input.since } : {},
6824
+ ...input.contains !== void 0 ? { contains: input.contains } : {},
6825
+ limit: input.limit ?? 100
6826
+ };
6827
+ const payload = input.summary ? summarizeLogs(dataDir, query) : (() => {
6828
+ const entries = queryLogs(dataDir, query);
6829
+ return {
6830
+ returned: entries.length,
6831
+ entries
6832
+ };
6833
+ })();
6834
+ return { content: [{
6835
+ type: "text",
6836
+ text: JSON.stringify(payload, null, 2)
6837
+ }] };
6838
+ }
6839
+ function registerGetLogs(server) {
6840
+ server.registerTool("get_logs", {
6841
+ title: "Get Logs",
6842
+ description: `Read and analyze the structured application log (.agentic/logs.ndjson).
6843
+ Use to answer "what went wrong recently?", "show errors from gmail sync", or "summarize today's activity".
6844
+
6845
+ Args:
6846
+ level: Minimum level to include — debug | info | warn | error (optional)
6847
+ component: Filter by component, e.g. "gmail-sync", "lancedb" (optional)
6848
+ since: ISO timestamp; only entries at or after it (optional)
6849
+ contains: Case-insensitive substring of the message (optional)
6850
+ limit: Max entries to return (default 100, most recent)
6851
+ summary: When true, return aggregated counts (by level + component) and recent errors instead of raw entries
6852
+
6853
+ Returns (entries): { returned: number, entries: [{ts, level, component, message, context?}] }
6854
+ Returns (summary): { total, byLevel, byComponent, firstTs, lastTs, recentErrors }`,
6855
+ inputSchema: z.object({
6856
+ level: z.enum([
6857
+ "debug",
6858
+ "info",
6859
+ "warn",
6860
+ "error"
6861
+ ]).optional().describe("Minimum level"),
6862
+ component: z.string().optional().describe("Filter by component"),
6863
+ since: z.string().optional().describe("ISO timestamp lower bound"),
6864
+ contains: z.string().optional().describe("Message substring filter"),
6865
+ limit: z.number().int().min(1).max(1e3).optional().describe("Max entries (default 100)"),
6866
+ summary: z.boolean().optional().describe("Return aggregated summary instead of entries")
6867
+ })
6868
+ }, async ({ level, component, since, contains, limit, summary }) => {
6869
+ const input = {};
6870
+ if (level !== void 0) input.level = level;
6871
+ if (component !== void 0) input.component = component;
6872
+ if (since !== void 0) input.since = since;
6873
+ if (contains !== void 0) input.contains = contains;
6874
+ if (limit !== void 0) input.limit = limit;
6875
+ if (summary !== void 0) input.summary = summary;
6876
+ return handleGetLogs(input);
6877
+ });
6878
+ }
6879
+ //#endregion
6812
6880
  //#region src/mcp/prompts.ts
6813
6881
  /**
6814
6882
  * CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.
@@ -6890,7 +6958,7 @@ function registerResources(server, dataDir = DATA_DIR$1) {
6890
6958
  description: "Open and closed deals for a customer",
6891
6959
  mimeType: "application/json"
6892
6960
  }, async (uri, variables) => {
6893
- const { readPipeline } = await import("./pipeline-writer-eufx_0o1.js").then((n) => n.t);
6961
+ const { readPipeline } = await import("./pipeline-writer-rDj-ni6q.js").then((n) => n.t);
6894
6962
  const deals = await readPipeline(dataDir, String(variables["slug"]));
6895
6963
  return { contents: [{
6896
6964
  uri: uri.href,
@@ -6903,7 +6971,7 @@ function registerResources(server, dataDir = DATA_DIR$1) {
6903
6971
  description: "Newest-first interaction history for a customer",
6904
6972
  mimeType: "text/markdown"
6905
6973
  }, async (uri, variables) => {
6906
- const { readInteractions } = await import("./interactions-writer-DO3KcSR3.js").then((n) => n.r);
6974
+ const { readInteractions } = await import("./interactions-writer-ZQcpFOh9.js").then((n) => n.r);
6907
6975
  const text = await readInteractions(dataDir, String(variables["slug"]));
6908
6976
  return { contents: [{
6909
6977
  uri: uri.href,
@@ -6962,46 +7030,30 @@ function objectsSchemaPath(dataDir) {
6962
7030
  return path.join(dataDir, ".agentic", "schema", "custom-objects.json");
6963
7031
  }
6964
7032
  function recordsPath(dataDir, name) {
7033
+ assertSafePathSegment(name, "custom object name");
6965
7034
  return path.join(dataDir, ".agentic", "objects", `${name}.json`);
6966
7035
  }
6967
7036
  function loadCustomObjects(dataDir) {
6968
- const p = objectsSchemaPath(dataDir);
6969
- if (!fs.existsSync(p)) return [];
6970
- try {
6971
- const data = JSON.parse(fs.readFileSync(p, "utf-8"));
6972
- return Array.isArray(data.objects) ? data.objects : [];
6973
- } catch {
6974
- return [];
6975
- }
7037
+ return readJsonArray(objectsSchemaPath(dataDir), "objects");
6976
7038
  }
6977
7039
  function getObjectDefinition(dataDir, name) {
6978
7040
  return loadCustomObjects(dataDir).find((o) => o.name === name);
6979
7041
  }
6980
7042
  /** Add or update (by name) a custom object definition. */
6981
7043
  function defineCustomObject(dataDir, def) {
7044
+ assertSafePathSegment(def.name, "custom object name");
6982
7045
  const objs = loadCustomObjects(dataDir);
6983
7046
  const idx = objs.findIndex((o) => o.name === def.name);
6984
7047
  if (idx >= 0) objs[idx] = def;
6985
7048
  else objs.push(def);
6986
- const p = objectsSchemaPath(dataDir);
6987
- fs.mkdirSync(path.dirname(p), { recursive: true });
6988
- fs.writeFileSync(p, JSON.stringify({ objects: objs }, null, 2), "utf-8");
7049
+ writeJsonArray(objectsSchemaPath(dataDir), "objects", objs);
6989
7050
  return objs;
6990
7051
  }
6991
7052
  function listRecords(dataDir, name) {
6992
- const p = recordsPath(dataDir, name);
6993
- if (!fs.existsSync(p)) return [];
6994
- try {
6995
- const data = JSON.parse(fs.readFileSync(p, "utf-8"));
6996
- return Array.isArray(data.records) ? data.records : [];
6997
- } catch {
6998
- return [];
6999
- }
7053
+ return readJsonArray(recordsPath(dataDir, name), "records");
7000
7054
  }
7001
7055
  function writeRecords(dataDir, name, records) {
7002
- const p = recordsPath(dataDir, name);
7003
- fs.mkdirSync(path.dirname(p), { recursive: true });
7004
- fs.writeFileSync(p, JSON.stringify({ records }, null, 2), "utf-8");
7056
+ writeJsonArray(recordsPath(dataDir, name), "records", records);
7005
7057
  }
7006
7058
  function createRecord(dataDir, name, values) {
7007
7059
  const def = getObjectDefinition(dataDir, name);
@@ -7059,7 +7111,7 @@ function handleCreateRecord(input, dataDir = DATA_DIR) {
7059
7111
  enforceRbac(dataDir, "create_record");
7060
7112
  const res = createRecord(dataDir, input.object, input.values);
7061
7113
  if (!res.ok) return json({ error: (res.errors ?? []).join("; ") });
7062
- import("./webhooks-BO2UAnmn.js").then(({ emitEvent }) => emitEvent(dataDir, "record.created", {
7114
+ import("./webhooks-sWZ8CJtR.js").then(({ emitEvent }) => emitEvent(dataDir, "record.created", {
7063
7115
  object: input.object,
7064
7116
  record: res.record
7065
7117
  }));
@@ -7121,14 +7173,7 @@ function hashToken(token) {
7121
7173
  return createHash("sha256").update(token).digest("hex");
7122
7174
  }
7123
7175
  function loadMcpTokens(dataDir) {
7124
- const p = tokensPath(dataDir);
7125
- if (!fs.existsSync(p)) return [];
7126
- try {
7127
- const data = JSON.parse(fs.readFileSync(p, "utf-8"));
7128
- return Array.isArray(data.tokens) ? data.tokens : [];
7129
- } catch {
7130
- return [];
7131
- }
7176
+ return readJsonArray(tokensPath(dataDir), "tokens");
7132
7177
  }
7133
7178
  /**
7134
7179
  * Whether the HTTP MCP endpoint must require a bearer token.
@@ -7247,6 +7292,7 @@ function createMcpServer() {
7247
7292
  registerListBackups(server);
7248
7293
  registerTriggerSync(server);
7249
7294
  registerGetAuditLog(server);
7295
+ registerGetLogs(server);
7250
7296
  registerCustomObjectTools(server);
7251
7297
  registerPrompts(server);
7252
7298
  registerResources(server);
@@ -7257,7 +7303,7 @@ async function startStdio() {
7257
7303
  const server = createMcpServer();
7258
7304
  const transport = new StdioServerTransport();
7259
7305
  await server.connect(transport);
7260
- console.error("DatasynxOpenCRM MCP Server running via stdio");
7306
+ logger.info("mcp-server", "running via stdio");
7261
7307
  }
7262
7308
  async function startHttp(port = 3847) {
7263
7309
  await initOAuthFromDisk(process.cwd());
@@ -7297,7 +7343,7 @@ async function startHttp(port = 3847) {
7297
7343
  });
7298
7344
  app.get("/sessions", async (_req, res) => {
7299
7345
  try {
7300
- const { readAllSessions } = await import("./session-D9ub6Wl1.js");
7346
+ const { readAllSessions } = await import("./session-B6XaP83h.js");
7301
7347
  const sessions = readAllSessions(dataDir);
7302
7348
  res.json({ sessions });
7303
7349
  } catch {
@@ -7431,15 +7477,15 @@ button{margin-top:12px;padding:12px 28px;background:#1a1a2e;color:#fff;border:no
7431
7477
  res.send(surveyThankYouPage(numScore, commentText));
7432
7478
  });
7433
7479
  app.listen(port, () => {
7434
- console.error(`DatasynxOpenCRM MCP Server running on http://0.0.0.0:${port}/mcp`);
7480
+ logger.info("mcp-server", "running over http", { url: `http://0.0.0.0:${port}/mcp` });
7435
7481
  });
7436
7482
  }
7437
7483
  if ((process.env["DXCRM_MCP_MODE"] ?? "stdio") === "http") startHttp(parseInt(process.env["DXCRM_MCP_PORT"] ?? "3847", 10)).catch((err) => {
7438
- console.error("MCP Server fatal error:", err.message);
7484
+ logger.error("mcp-server", "fatal error", { error: err.message });
7439
7485
  process.exit(1);
7440
7486
  });
7441
7487
  else startStdio().catch((err) => {
7442
- console.error("MCP Server fatal error:", err.message);
7488
+ logger.error("mcp-server", "fatal error", { error: err.message });
7443
7489
  process.exit(1);
7444
7490
  });
7445
7491
  //#endregion