@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.
- package/README.md +270 -669
- package/dist/{approvals-DpjxGHFp.js → approvals-CmDT2eUg.js} +7 -24
- package/dist/approvals-CmDT2eUg.js.map +1 -0
- package/dist/{ask-CID3jnuL.js → ask-CDysGnRg.js} +6 -6
- package/dist/{ask-CID3jnuL.js.map → ask-CDysGnRg.js.map} +1 -1
- package/dist/atomic-write-8yjqqLtS.js +29 -0
- package/dist/atomic-write-8yjqqLtS.js.map +1 -0
- package/dist/atomic-write-BYmF-ThH.cjs +37 -0
- package/dist/atomic-write-BYmF-ThH.cjs.map +1 -0
- package/dist/attachments-CX2GAtsw.cjs +517 -0
- package/dist/attachments-CX2GAtsw.cjs.map +1 -0
- package/dist/attachments-D207gXfN.js +514 -0
- package/dist/attachments-D207gXfN.js.map +1 -0
- package/dist/attachments-rLa96rOK.js +514 -0
- package/dist/attachments-rLa96rOK.js.map +1 -0
- package/dist/auth-B5DcjJ_6.js +2 -0
- package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
- package/dist/auth-DDXZTwS0.js.map +1 -0
- package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
- package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
- package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
- package/dist/backup-CTlIxUdO.js.map +1 -0
- package/dist/backup-LFnC09oV.js +2 -0
- package/dist/chunk-BfDYWZQ8.cjs +32 -0
- package/dist/chunk-BfDYWZQ8.cjs.map +1 -0
- package/dist/chunk-BhUZmQg5.js +32 -0
- package/dist/chunk-BhUZmQg5.js.map +1 -0
- package/dist/chunk-ChC83jai.js +2 -0
- package/dist/chunk-e_w8qqtP.js +32 -0
- package/dist/chunk-e_w8qqtP.js.map +1 -0
- package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
- package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
- package/dist/cli.js +285 -186
- package/dist/cli.js.map +1 -1
- package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
- package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
- package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
- package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
- package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
- package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
- package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
- package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
- package/dist/context-builder-7Uab5-G4.js.map +1 -0
- package/dist/context-builder-hmOPvgso.js +2 -0
- package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
- package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
- package/dist/custom-fields-GzpOHW_2.js.map +1 -0
- package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
- package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
- package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
- package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
- package/dist/customer-dir-CkMMXhb0.js.map +1 -0
- package/dist/daemon/worker.js +66 -40
- package/dist/daemon/worker.js.map +1 -1
- package/dist/doctor-C14-vnJ1.js +103 -0
- package/dist/doctor-C14-vnJ1.js.map +1 -0
- package/dist/email-body-BFSRa0AW.cjs +42 -0
- package/dist/email-body-BFSRa0AW.cjs.map +1 -0
- package/dist/email-body-BOd7U-D2.js +42 -0
- package/dist/email-body-BOd7U-D2.js.map +1 -0
- package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
- package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
- package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
- package/dist/file-lock-CcHotQkZ.js.map +1 -0
- package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-B4Iu3AQb.js} +56 -22
- package/dist/gmail-sync-B4Iu3AQb.js.map +1 -0
- package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-BpSVESSe.cjs} +55 -21
- package/dist/gmail-sync-BpSVESSe.cjs.map +1 -0
- package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-DIbrPnTK.js} +55 -21
- package/dist/gmail-sync-DIbrPnTK.js.map +1 -0
- package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-BzOFbvgh.js} +4 -4
- package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-BzOFbvgh.js.map} +1 -1
- package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-CvSDW_Js.js} +2 -2
- package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
- package/dist/goal-engine-BbroPhqm.js.map +1 -0
- package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
- package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-B_I1d54Y.js} +3 -3
- package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-B_I1d54Y.js.map} +1 -1
- package/dist/html-BaeOCZKE.js +36 -0
- package/dist/html-BaeOCZKE.js.map +1 -0
- package/dist/html-CmOku6jS.cjs +47 -0
- package/dist/html-CmOku6jS.cjs.map +1 -0
- package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
- package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
- package/dist/identity-CB7j-Zr1.js +2 -0
- package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
- package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
- package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-CTId9IGV.js} +51 -45
- package/dist/import-hubspot-CTId9IGV.js.map +1 -0
- package/dist/{index-YqwMd6aQ.d.cts → index-BAutNcAT.d.cts} +20 -12
- package/dist/index-BAutNcAT.d.cts.map +1 -0
- package/dist/{index-V8BFaH-b.d.ts → index-FzDsNSSb.d.ts} +12 -4
- package/dist/index-FzDsNSSb.d.ts.map +1 -0
- package/dist/index.cjs +19 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -12
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -21
- package/dist/index.js.map +1 -1
- package/dist/interactions-writer-B2y-73lh.js +2 -0
- package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-B8XAzdqR.js} +34 -4
- package/dist/interactions-writer-B8XAzdqR.js.map +1 -0
- package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-BRJNrefF.cjs} +7 -3
- package/dist/interactions-writer-BRJNrefF.cjs.map +1 -0
- package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-ZQcpFOh9.js} +7 -3
- package/dist/interactions-writer-ZQcpFOh9.js.map +1 -0
- package/dist/json-store-WWsFzXub.js +43 -0
- package/dist/json-store-WWsFzXub.js.map +1 -0
- package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base--063Kpa3.js} +51 -22
- package/dist/knowledge-base--063Kpa3.js.map +1 -0
- package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
- package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
- package/dist/lancedb-CuHKNsNZ.js.map +1 -0
- package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
- package/dist/lead-model-CEmx7te7.js.map +1 -0
- package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
- package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
- package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
- package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
- package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
- package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
- package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
- package/dist/logger-BkInaGoV.cjs +167 -0
- package/dist/logger-BkInaGoV.cjs.map +1 -0
- package/dist/logger-Dyl4VcLO.js +147 -0
- package/dist/logger-Dyl4VcLO.js.map +1 -0
- package/dist/logger-UaF5p9d1.js +147 -0
- package/dist/logger-UaF5p9d1.js.map +1 -0
- package/dist/logger-vKQS34w9.js +2 -0
- package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
- package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
- package/dist/mcp.cjs +365 -319
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.d.cts.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +365 -319
- package/dist/mcp.js.map +1 -1
- package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
- package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
- package/dist/memory-Dzr9dXSM.js.map +1 -0
- package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-BgVR8GDv.js} +4 -4
- package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-BgVR8GDv.js.map} +1 -1
- package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-D30_XksI.js} +5 -5
- package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-D30_XksI.js.map} +1 -1
- package/dist/{nba-3wanmJ0U.js → nba-DwdfM93s.js} +3 -3
- package/dist/{nba-3wanmJ0U.js.map → nba-DwdfM93s.js.map} +1 -1
- package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
- package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
- package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
- package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
- package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
- package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
- package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
- package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
- package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
- package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
- package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
- package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
- package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
- package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
- package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
- package/dist/push-manager-C0ECQgva.js.map +1 -0
- package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
- package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
- package/dist/quote-generator-CTdR8eEI.js.map +1 -0
- package/dist/rbac-DzbyFhVH.js +2 -0
- package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
- package/dist/rbac-msmBc_tK.js.map +1 -0
- package/dist/regex-Jt5DatPi.js +13 -0
- package/dist/regex-Jt5DatPi.js.map +1 -0
- package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
- package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
- package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
- package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
- package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
- package/dist/safe-path-mpp0dKtO.js +18 -0
- package/dist/safe-path-mpp0dKtO.js.map +1 -0
- package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
- package/dist/segments-DI3LOQNe.js.map +1 -0
- package/dist/sequence-engine-C6nnewHX.js +2 -0
- package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
- package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
- package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
- package/dist/sequence-store-CmYb6s0g.js.map +1 -0
- package/dist/{server-Dyva03K8.js → server-DoRPPOeR.js} +308 -230
- package/dist/server-DoRPPOeR.js.map +1 -0
- package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
- package/dist/session-B6XaP83h.js.map +1 -0
- package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
- package/dist/session-BgGDyP2C.js.map +1 -0
- package/dist/session-Bp4zTh4l.js +2 -0
- package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
- package/dist/session-Mm7GQbSH.cjs.map +1 -0
- package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
- package/dist/session-store-DWxJ5Pof.js.map +1 -0
- package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
- package/dist/session-store-yfwnj0OC.cjs.map +1 -0
- package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
- package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
- package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
- package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
- package/dist/sop-BV7ICAFR.js.map +1 -0
- package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
- package/dist/survey-engine-DKctGcLQ.js +2 -0
- package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
- package/dist/survey-engine-DngXBv47.js.map +1 -0
- package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
- package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
- package/dist/sync-state-DMZgzpez.js.map +1 -0
- package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
- package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
- package/dist/ticket-writer-a9on36Wb.js.map +1 -0
- package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
- package/dist/tone-C7bqK69y.js.map +1 -0
- package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
- package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
- package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
- package/dist/tone-mXSftvTn.js.map +1 -0
- package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-BoClrJAz.js} +18 -11
- package/dist/transcript-watcher-BoClrJAz.js.map +1 -0
- package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
- package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
- package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
- package/dist/update-deal-CWy1eLJI.js +2 -0
- package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
- package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
- package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
- package/dist/usage-BVlFlKW_.js.map +1 -0
- package/dist/usage-CClTf5e6.cjs.map +1 -1
- package/dist/usage-D0u9a-lV.js.map +1 -1
- package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
- package/dist/vault-CfwZdNzC.js.map +1 -0
- package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
- package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
- package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
- package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
- package/dist/webhooks-DXr1IoKn.js.map +1 -0
- package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
- package/dist/webhooks-sWZ8CJtR.js.map +1 -0
- package/package.json +22 -2
- package/dist/approvals-DpjxGHFp.js.map +0 -1
- package/dist/auth-CyFuu9X_.js +0 -2
- package/dist/auth-DFWwWcYD.js.map +0 -1
- package/dist/backup-CeMk9z86.js.map +0 -1
- package/dist/backup-f_hC7rBV.js +0 -2
- package/dist/context-builder-BzWAp3Zs.js.map +0 -1
- package/dist/context-builder-DlrRcqmJ.js +0 -2
- package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
- package/dist/custom-objects-BHgn1GEX.js.map +0 -1
- package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
- package/dist/file-lock-B_zi7NQl.js.map +0 -1
- package/dist/gmail-sync-DIaxInDT.js.map +0 -1
- package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
- package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
- package/dist/goal-engine-KpBftn4V.js.map +0 -1
- package/dist/identity-gyfWdrcX.js +0 -2
- package/dist/import-hubspot-BaK71U_K.js.map +0 -1
- package/dist/index-V8BFaH-b.d.ts.map +0 -1
- package/dist/index-YqwMd6aQ.d.cts.map +0 -1
- package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
- package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
- package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
- package/dist/interactions-writer-dSPy1XfO.js +0 -2
- package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
- package/dist/lancedb-rlvWoPwl.js.map +0 -1
- package/dist/lead-model-BCFzyktm.js.map +0 -1
- package/dist/memory-Bb6ky3kb.js.map +0 -1
- package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
- package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
- package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
- package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
- package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
- package/dist/push-manager-CdqIIkuh.js.map +0 -1
- package/dist/quote-generator-BfwENXzg.js.map +0 -1
- package/dist/rbac-C7c8tcES.js +0 -2
- package/dist/rbac-CTIktZaC.js.map +0 -1
- package/dist/relationship-health-odxEoQdJ.js.map +0 -1
- package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
- package/dist/segments-BqcD5HIl.js.map +0 -1
- package/dist/sequence-engine-CCTHEBgi.js +0 -2
- package/dist/sequence-engine-J1lTW_in.js.map +0 -1
- package/dist/sequence-store-DaaWr0Os.js.map +0 -1
- package/dist/server-Dyva03K8.js.map +0 -1
- package/dist/session-B9AilxOE.js.map +0 -1
- package/dist/session-D0qFkBla.cjs.map +0 -1
- package/dist/session-D9ub6Wl1.js.map +0 -1
- package/dist/session-mWHA71Lw.js +0 -2
- package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
- package/dist/session-store-C8tEvMPw.js.map +0 -1
- package/dist/sop-Vp0UPWFW.js.map +0 -1
- package/dist/survey-engine-C06hcQt3.js +0 -2
- package/dist/survey-engine-DBjCYqCv.js.map +0 -1
- package/dist/sync-state-ChaLbamC.js.map +0 -1
- package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
- package/dist/tone-Bdm5uaht.js.map +0 -1
- package/dist/tone-DRKlZgPr.cjs.map +0 -1
- package/dist/tone-vNb2DAAD.js.map +0 -1
- package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
- package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
- package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
- package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
- package/dist/update-deal-BNwPGaTV.js +0 -2
- package/dist/usage-D0-TYJkw.js.map +0 -1
- package/dist/vault-DXCg29W-.js.map +0 -1
- package/dist/webhooks-7EpA05Qr.js.map +0 -1
- package/dist/webhooks-BO2UAnmn.js.map +0 -1
- 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
|
|
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-
|
|
5
|
-
import { n as
|
|
6
|
-
import { i as
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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$
|
|
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-
|
|
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$
|
|
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
|
-
|
|
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$
|
|
1576
|
-
async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
2155
|
-
async function handleLogInteraction(input, dataDir = DATA_DIR$
|
|
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
|
-
|
|
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$
|
|
2265
|
-
async function handleUpdateDeal(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
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())
|
|
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
|
|
2426
|
-
Useful for reporting, audits, or creating
|
|
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$
|
|
2447
|
-
async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$
|
|
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$
|
|
2600
|
-
async function handleGetDealHealth(input, dataDir = DATA_DIR$
|
|
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$
|
|
2650
|
-
async function handleGetPipelineForecast(input, dataDir = DATA_DIR$
|
|
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
|
|
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
|
|
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$
|
|
2727
|
-
async function handleSummarizeMeeting(input, dataDir = DATA_DIR$
|
|
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-
|
|
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
|
-
|
|
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$
|
|
2857
|
-
async function handleGetPipelineStages(_input, dataDir = DATA_DIR$
|
|
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
|
-
|
|
2876
|
-
|
|
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$
|
|
2889
|
-
async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3321
|
-
const
|
|
3322
|
-
const
|
|
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-
|
|
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-
|
|
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$
|
|
3678
|
-
async function handleRunDealAgent(input, dataDir = DATA_DIR$
|
|
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$
|
|
3746
|
-
async function handleApproveAgentAction(input, dataDir = DATA_DIR$
|
|
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$
|
|
4007
|
-
async function handleSimulateRevenue(input, dataDir = DATA_DIR$
|
|
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$
|
|
4066
|
-
async function handleGetPlaybook(input, dataDir = DATA_DIR$
|
|
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$
|
|
4159
|
+
}, DATA_DIR$35));
|
|
4152
4160
|
}
|
|
4153
4161
|
//#endregion
|
|
4154
4162
|
//#region src/mcp/tools/create-playbook.ts
|
|
4155
|
-
const DATA_DIR$
|
|
4156
|
-
async function handleCreatePlaybook(input, dataDir = DATA_DIR$
|
|
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$
|
|
4237
|
+
}, DATA_DIR$34));
|
|
4230
4238
|
}
|
|
4231
4239
|
//#endregion
|
|
4232
4240
|
//#region src/mcp/tools/list-playbooks.ts
|
|
4233
|
-
const DATA_DIR$
|
|
4234
|
-
async function handleListPlaybooks(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
4278
|
-
async function handleDistillPlaybook(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
4556
|
-
async function handlePursueGoal(input, dataDir = DATA_DIR$
|
|
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$
|
|
4627
|
+
}, DATA_DIR$31));
|
|
4620
4628
|
}
|
|
4621
4629
|
//#endregion
|
|
4622
4630
|
//#region src/mcp/tools/get-goal-status.ts
|
|
4623
|
-
const DATA_DIR$
|
|
4624
|
-
async function handleGetGoalStatus(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
4787
|
+
}, DATA_DIR$29));
|
|
4780
4788
|
}
|
|
4781
4789
|
//#endregion
|
|
4782
4790
|
//#region src/mcp/tools/get-push-status.ts
|
|
4783
|
-
const DATA_DIR$
|
|
4784
|
-
async function handleGetPushStatus(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
4923
|
-
async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$
|
|
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
|
|
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$
|
|
5065
|
-
async function handleOpenDealRoom(input, dataDir = DATA_DIR$
|
|
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$
|
|
5149
|
-
async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$
|
|
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$
|
|
5250
|
-
async function handleListEmailTemplates(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
5292
|
-
async function handleGetEmailTemplate(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
5317
|
-
async function handleDraftEmail(input, dataDir = DATA_DIR$
|
|
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-
|
|
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-
|
|
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-
|
|
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$
|
|
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$
|
|
5470
|
-
async function handleEnrollInSequence(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
5520
|
-
async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
5549
|
-
async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
5572
|
-
async function handleListSequences(_input, dataDir = DATA_DIR$
|
|
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$
|
|
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>
|
|
5636
|
+
<p><strong>Date:</strong> ${quote.createdAt.slice(0, 10)} <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
|
-
|
|
5716
|
-
|
|
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$
|
|
5723
|
-
async function handleGenerateQuote(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
5776
|
-
async function handleGetQuoteStatus(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
}
|
|
5974
|
-
|
|
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$
|
|
6045
|
-
async function handleCreateTicket(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
6094
|
-
async function handleUpdateTicket(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
6139
|
-
async function handleListTickets(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
6182
|
-
async function handleCloseTicket(input, dataDir = DATA_DIR$
|
|
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$
|
|
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
|
-
|
|
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
|
-
|
|
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$
|
|
6364
|
-
async function handleSendNpsSurvey(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
6406
|
-
async function handleGetSurveyResults(input, dataDir = DATA_DIR$
|
|
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$
|
|
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
|
-
|
|
6463
|
-
|
|
6447
|
+
/** Category subdirectories of the knowledge base. */
|
|
6448
|
+
function kbCategories(dir) {
|
|
6464
6449
|
if (!fs.existsSync(dir)) return [];
|
|
6465
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6497
|
-
|
|
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
|
-
|
|
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$
|
|
6514
|
-
async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
6549
|
-
async function handleCreateKbArticle(input, dataDir = DATA_DIR$
|
|
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$
|
|
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$
|
|
6603
|
-
async function handleBackupNow(input, dataDir = DATA_DIR$
|
|
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$
|
|
6641
|
-
async function handleListBackups(input, dataDir = DATA_DIR$
|
|
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$
|
|
6676
|
-
async function handleTriggerSync(input, dataDir = DATA_DIR$
|
|
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-
|
|
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$
|
|
6771
|
-
async function handleGetAuditLog(input, dataDir = DATA_DIR$
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
7484
|
+
logger.error("mcp-server", "fatal error", { error: err.message });
|
|
7439
7485
|
process.exit(1);
|
|
7440
7486
|
});
|
|
7441
7487
|
else startStdio().catch((err) => {
|
|
7442
|
-
|
|
7488
|
+
logger.error("mcp-server", "fatal error", { error: err.message });
|
|
7443
7489
|
process.exit(1);
|
|
7444
7490
|
});
|
|
7445
7491
|
//#endregion
|