@datasynx/agentic-crm 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cli.js +71 -8
  2. package/dist/cli.js.map +1 -1
  3. package/dist/daemon/worker.js +3 -3
  4. package/dist/funnel-B2mwpZE1.js +89 -0
  5. package/dist/funnel-B2mwpZE1.js.map +1 -0
  6. package/dist/funnel-CJ7fy7hG.js +2 -0
  7. package/dist/{index-Ewy4f1XW.d.cts → index-BAutNcAT.d.cts} +17 -17
  8. package/dist/index-BAutNcAT.d.cts.map +1 -0
  9. package/dist/{index-DoYT-azq.d.ts → index-BBAlKZg6.d.ts} +8 -8
  10. package/dist/index-BBAlKZg6.d.ts.map +1 -0
  11. package/dist/index.d.cts +17 -17
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.ts +8 -8
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/{knowledge-base-Byo0zwM5.js → knowledge-base-yo-BLUcB.js} +2 -1
  16. package/dist/knowledge-base-yo-BLUcB.js.map +1 -0
  17. package/dist/{login-yt9OOQQk.js → login-dc_Hqosw.js} +2 -2
  18. package/dist/{login-yt9OOQQk.js.map → login-dc_Hqosw.js.map} +1 -1
  19. package/dist/mailbox-config-BU3vib2T.js +2 -0
  20. package/dist/{mailbox-config-Dn2xTn9N.js → mailbox-config-trjLPHPG.js} +2 -2
  21. package/dist/{mailbox-config-Dn2xTn9N.js.map → mailbox-config-trjLPHPG.js.map} +1 -1
  22. package/dist/{mailbox-poll-B8dvFAXT.js → mailbox-poll-Ban7C3X0.js} +3 -3
  23. package/dist/{mailbox-poll-B8dvFAXT.js.map → mailbox-poll-Ban7C3X0.js.map} +1 -1
  24. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  25. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  26. package/dist/mcp.cjs +246 -134
  27. package/dist/mcp.cjs.map +1 -1
  28. package/dist/mcp.d.cts.map +1 -1
  29. package/dist/mcp.d.ts.map +1 -1
  30. package/dist/mcp.js +246 -134
  31. package/dist/mcp.js.map +1 -1
  32. package/dist/{server-C0XkJQBo.js → server-DAcwmRPE.js} +162 -135
  33. package/dist/server-DAcwmRPE.js.map +1 -0
  34. package/dist/{token-resolver-D98qPOOf.js → token-resolver-CL8-CSgZ.js} +2 -2
  35. package/dist/{token-resolver-D98qPOOf.js.map → token-resolver-CL8-CSgZ.js.map} +1 -1
  36. package/dist/{token-store-B0h0USqe.js → token-store-BPDwePNT.js} +13 -2
  37. package/dist/token-store-BPDwePNT.js.map +1 -0
  38. package/dist/token-store-D3HCeXmE.js +2 -0
  39. package/package.json +1 -1
  40. package/dist/index-DoYT-azq.d.ts.map +0 -1
  41. package/dist/index-Ewy4f1XW.d.cts.map +0 -1
  42. package/dist/knowledge-base-Byo0zwM5.js.map +0 -1
  43. package/dist/mailbox-config-Bu-J1O4I.js +0 -2
  44. package/dist/server-C0XkJQBo.js.map +0 -1
  45. package/dist/token-store-B0h0USqe.js.map +0 -1
  46. package/dist/token-store-CEmz8d-0.js +0 -2
package/dist/mcp.js CHANGED
@@ -446,6 +446,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
446
446
  | get_diagnostics | Self-diagnostic health check (data integrity, temp files, log errors, backups) | admin |
447
447
  | get_pipeline_changes | Pipeline time-travel: what changed (won/lost/moved/value) since a date | any |
448
448
  | get_pipeline_velocity | Stage dwell times, sales cycle, and stalled deals from snapshot history | any |
449
+ | get_pipeline_funnel | Conversion funnel & win rate: where deals leak out of the pipeline | any |
449
450
  | define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
450
451
  | create_record | Create a record of a custom object (validated against its schema) | rep+ |
451
452
  | list_records | List records of a custom object | any |
@@ -1424,7 +1425,7 @@ async function buildContext(dataDir, slug) {
1424
1425
  }
1425
1426
  //#endregion
1426
1427
  //#region src/mcp/tools/get-customer-context.ts
1427
- const DATA_DIR$55 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1428
+ const DATA_DIR$56 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1428
1429
  function triggerOnQuerySync(dataDir, slug) {
1429
1430
  const auth = getGmailAuth();
1430
1431
  if (!auth) return;
@@ -1445,7 +1446,7 @@ function triggerOnQuerySync(dataDir, slug) {
1445
1446
  }).then(() => updateSlugSyncState(dataDir, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() })).catch(() => {})).catch(() => {});
1446
1447
  } catch {}
1447
1448
  }
1448
- async function handleGetCustomerContext(input, dataDir = DATA_DIR$55) {
1449
+ async function handleGetCustomerContext(input, dataDir = DATA_DIR$56) {
1449
1450
  const targetSlug = input.slug ?? getSession()?.customerSlug;
1450
1451
  if (!targetSlug) return {
1451
1452
  content: [{
@@ -1581,8 +1582,8 @@ async function searchKnowledge(dataDir, slug, query, limit) {
1581
1582
  }
1582
1583
  //#endregion
1583
1584
  //#region src/mcp/tools/search-customer-knowledge.ts
1584
- const DATA_DIR$54 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1585
- async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$54) {
1585
+ const DATA_DIR$55 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1586
+ async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$55) {
1586
1587
  const limit = input.limit ?? 5;
1587
1588
  try {
1588
1589
  const results = await searchKnowledge(dataDir, input.slug, input.query, limit);
@@ -1630,14 +1631,14 @@ If no results: returns empty array with a helpful sync suggestion.`,
1630
1631
  }
1631
1632
  //#endregion
1632
1633
  //#region src/mcp/tools/list-customers.ts
1633
- const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1634
+ const DATA_DIR$54 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1634
1635
  function extractLastInteractionDate(interactionsPath) {
1635
1636
  if (!fs.existsSync(interactionsPath)) return void 0;
1636
1637
  const content = fs.readFileSync(interactionsPath, "utf-8");
1637
1638
  const match = /^## (\d{4}-\d{2}-\d{2})/m.exec(content);
1638
1639
  return match ? match[1] : void 0;
1639
1640
  }
1640
- async function handleListCustomers(input, dataDir = DATA_DIR$53) {
1641
+ async function handleListCustomers(input, dataDir = DATA_DIR$54) {
1641
1642
  const customersDir = path.join(dataDir, "customers");
1642
1643
  const customers = [];
1643
1644
  if (!fs.existsSync(customersDir)) return { content: [{
@@ -2150,8 +2151,8 @@ async function updateHealthFromInteraction(dataDir, slug) {
2150
2151
  }
2151
2152
  //#endregion
2152
2153
  //#region src/mcp/tools/log-interaction.ts
2153
- const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2154
- async function handleLogInteraction(input, dataDir = DATA_DIR$52) {
2154
+ const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2155
+ async function handleLogInteraction(input, dataDir = DATA_DIR$53) {
2155
2156
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2156
2157
  const interactionDate = input.date ?? today;
2157
2158
  const sourceRef = input.source ?? `agent://log/${Date.now()}`;
@@ -2260,8 +2261,8 @@ var update_deal_exports = /* @__PURE__ */ __exportAll({
2260
2261
  handleUpdateDeal: () => handleUpdateDeal,
2261
2262
  registerUpdateDeal: () => registerUpdateDeal
2262
2263
  });
2263
- const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2264
- async function handleUpdateDeal(input, dataDir = DATA_DIR$51) {
2264
+ const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2265
+ async function handleUpdateDeal(input, dataDir = DATA_DIR$52) {
2265
2266
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2266
2267
  const deal = {
2267
2268
  name: input.dealName,
@@ -2344,12 +2345,12 @@ Returns: { success: boolean, deal: object }`,
2344
2345
  }
2345
2346
  //#endregion
2346
2347
  //#region src/mcp/tools/export-customer.ts
2347
- const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2348
+ const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2348
2349
  function countInteractions(content) {
2349
2350
  const matches = content.match(/^## \d{4}-\d{2}-\d{2}/gm);
2350
2351
  return matches ? matches.length : 0;
2351
2352
  }
2352
- async function handleExportCustomer(input, dataDir = DATA_DIR$50) {
2353
+ async function handleExportCustomer(input, dataDir = DATA_DIR$51) {
2353
2354
  enforceRbac(dataDir, "export_customer");
2354
2355
  const customerDir = path.join(dataDir, "customers", input.slug);
2355
2356
  if (!fs.existsSync(customerDir)) return {
@@ -2462,8 +2463,8 @@ Returns:
2462
2463
  }
2463
2464
  //#endregion
2464
2465
  //#region src/mcp/tools/update-customer-facts.ts
2465
- const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2466
- async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$49) {
2466
+ const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2467
+ async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$50) {
2467
2468
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2468
2469
  try {
2469
2470
  enforceRbac(dataDir, "update_customer_facts");
@@ -2641,8 +2642,8 @@ function scoreDealForToday(deal, todayDate) {
2641
2642
  }
2642
2643
  //#endregion
2643
2644
  //#region src/mcp/tools/get-deal-health.ts
2644
- const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2645
- async function handleGetDealHealth(input, dataDir = DATA_DIR$48) {
2645
+ const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2646
+ async function handleGetDealHealth(input, dataDir = DATA_DIR$49) {
2646
2647
  try {
2647
2648
  const deals = await readPipeline(dataDir, input.slug);
2648
2649
  const today = /* @__PURE__ */ new Date();
@@ -2691,8 +2692,8 @@ Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,
2691
2692
  }
2692
2693
  //#endregion
2693
2694
  //#region src/mcp/tools/get-pipeline-forecast.ts
2694
- const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2695
- async function handleGetPipelineForecast(input, dataDir = DATA_DIR$47) {
2695
+ const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2696
+ async function handleGetPipelineForecast(input, dataDir = DATA_DIR$48) {
2696
2697
  try {
2697
2698
  const slugs = listCustomerSlugs(dataDir).filter((d) => !input.filter || d.includes(input.filter));
2698
2699
  const allDeals = [];
@@ -2753,8 +2754,8 @@ Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count,
2753
2754
  }
2754
2755
  //#endregion
2755
2756
  //#region src/mcp/tools/summarize-meeting.ts
2756
- const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2757
- async function handleSummarizeMeeting(input, dataDir = DATA_DIR$46) {
2757
+ const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2758
+ async function handleSummarizeMeeting(input, dataDir = DATA_DIR$47) {
2758
2759
  try {
2759
2760
  let summary = input.transcript.slice(0, 400);
2760
2761
  let nextSteps = [];
@@ -2877,8 +2878,8 @@ function getPipelineStages(dataDir) {
2877
2878
  }
2878
2879
  //#endregion
2879
2880
  //#region src/mcp/tools/get-pipeline-stages.ts
2880
- const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2881
- async function handleGetPipelineStages(_input, dataDir = DATA_DIR$45) {
2881
+ const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2882
+ async function handleGetPipelineStages(_input, dataDir = DATA_DIR$46) {
2882
2883
  const stages = getPipelineStages(dataDir);
2883
2884
  return { content: [{
2884
2885
  type: "text",
@@ -2906,8 +2907,8 @@ async function searchAcrossCustomers(dataDir, query, limit = 5, excludeSlug) {
2906
2907
  }
2907
2908
  //#endregion
2908
2909
  //#region src/mcp/tools/get-market-intelligence.ts
2909
- const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2910
- async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$44) {
2910
+ const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2911
+ async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$45) {
2911
2912
  const excludeSlug = input.excludeCurrentCustomer ? input.slug : void 0;
2912
2913
  const all = listCustomerSlugs(dataDir);
2913
2914
  const totalCustomersSearched = excludeSlug ? all.filter((s) => s !== excludeSlug).length : all.length;
@@ -2938,7 +2939,7 @@ function registerGetMarketIntelligence(server) {
2938
2939
  }
2939
2940
  //#endregion
2940
2941
  //#region src/mcp/tools/get-relationship-graph.ts
2941
- const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2942
+ const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2942
2943
  function summarizeNode(n) {
2943
2944
  return {
2944
2945
  id: n.id,
@@ -2946,7 +2947,7 @@ function summarizeNode(n) {
2946
2947
  email: n.properties["email"]
2947
2948
  };
2948
2949
  }
2949
- async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$43) {
2950
+ async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$44) {
2950
2951
  try {
2951
2952
  const graph = readGraph(dataDir, input.slug);
2952
2953
  const stakeholders = getStakeholders(graph);
@@ -3014,9 +3015,9 @@ Returns: {
3014
3015
  }
3015
3016
  //#endregion
3016
3017
  //#region src/mcp/tools/get-relationship-health.ts
3017
- const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3018
+ const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3018
3019
  const MAX_HEALTH_AGE_MS = 3600 * 1e3;
3019
- async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$42) {
3020
+ async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$43) {
3020
3021
  try {
3021
3022
  let health = readHealth(dataDir, input.slug);
3022
3023
  if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {
@@ -3685,8 +3686,8 @@ async function runDealAgent(config, dataDir, llmFn = callLlm) {
3685
3686
  }
3686
3687
  //#endregion
3687
3688
  //#region src/mcp/tools/run-deal-agent.ts
3688
- const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3689
- async function handleRunDealAgent(input, dataDir = DATA_DIR$41) {
3689
+ const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3690
+ async function handleRunDealAgent(input, dataDir = DATA_DIR$42) {
3690
3691
  try {
3691
3692
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3692
3693
  const result = await runDealAgent({
@@ -3753,8 +3754,8 @@ Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], tr
3753
3754
  }
3754
3755
  //#endregion
3755
3756
  //#region src/mcp/tools/approve-agent-action.ts
3756
- const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3757
- async function handleApproveAgentAction(input, dataDir = DATA_DIR$40) {
3757
+ const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3758
+ async function handleApproveAgentAction(input, dataDir = DATA_DIR$41) {
3758
3759
  try {
3759
3760
  const queue = readAgentQueue(dataDir, input.slug);
3760
3761
  const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);
@@ -4014,8 +4015,8 @@ async function buildSimulationInput(dataDir, horizon, today, externalSignals = [
4014
4015
  }
4015
4016
  //#endregion
4016
4017
  //#region src/mcp/tools/simulate-revenue.ts
4017
- const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4018
- async function handleSimulateRevenue(input, dataDir = DATA_DIR$39) {
4018
+ const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4019
+ async function handleSimulateRevenue(input, dataDir = DATA_DIR$40) {
4019
4020
  try {
4020
4021
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4021
4022
  const horizon = input.horizon ?? "quarter";
@@ -4073,8 +4074,8 @@ Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMo
4073
4074
  }
4074
4075
  //#endregion
4075
4076
  //#region src/mcp/tools/get-playbook.ts
4076
- const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4077
- async function handleGetPlaybook(input, dataDir = DATA_DIR$38) {
4077
+ const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4078
+ async function handleGetPlaybook(input, dataDir = DATA_DIR$39) {
4078
4079
  try {
4079
4080
  const playbooks = listPlaybooks(dataDir, input.slug);
4080
4081
  if (!(input.stage !== void 0 || input.value !== void 0 || input.healthScore !== void 0)) return { content: [{
@@ -4159,12 +4160,12 @@ Returns: { matches: [{ name, score, trigger, successRate, usedCount, content }],
4159
4160
  ...healthScore !== void 0 ? { healthScore } : {},
4160
4161
  ...daysSinceContact !== void 0 ? { daysSinceContact } : {},
4161
4162
  ...championPresent !== void 0 ? { championPresent } : {}
4162
- }, DATA_DIR$38));
4163
+ }, DATA_DIR$39));
4163
4164
  }
4164
4165
  //#endregion
4165
4166
  //#region src/mcp/tools/create-playbook.ts
4166
- const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4167
- async function handleCreatePlaybook(input, dataDir = DATA_DIR$37) {
4167
+ const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4168
+ async function handleCreatePlaybook(input, dataDir = DATA_DIR$38) {
4168
4169
  try {
4169
4170
  const name = toKebabCase(input.name);
4170
4171
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -4237,12 +4238,12 @@ Returns: { success: true, playbook: { name, trigger, successRate, path } }`,
4237
4238
  trigger,
4238
4239
  content,
4239
4240
  ...successRate !== void 0 ? { successRate } : {}
4240
- }, DATA_DIR$37));
4241
+ }, DATA_DIR$38));
4241
4242
  }
4242
4243
  //#endregion
4243
4244
  //#region src/mcp/tools/list-playbooks.ts
4244
- const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4245
- async function handleListPlaybooks(input, dataDir = DATA_DIR$36) {
4245
+ const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4246
+ async function handleListPlaybooks(input, dataDir = DATA_DIR$37) {
4246
4247
  try {
4247
4248
  const playbooks = listPlaybooks(dataDir, input.slug);
4248
4249
  return { content: [{
@@ -4281,12 +4282,12 @@ Args:
4281
4282
 
4282
4283
  Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,
4283
4284
  inputSchema: z.object({ slug: z.string().describe("Customer ID") })
4284
- }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$36));
4285
+ }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$37));
4285
4286
  }
4286
4287
  //#endregion
4287
4288
  //#region src/mcp/tools/distill-playbook.ts
4288
- const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4289
- async function handleDistillPlaybook(input, dataDir = DATA_DIR$35, llmFn = callLlm) {
4289
+ const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4290
+ async function handleDistillPlaybook(input, dataDir = DATA_DIR$36, llmFn = callLlm) {
4290
4291
  try {
4291
4292
  const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);
4292
4293
  if (!result.ok) {
@@ -4345,7 +4346,7 @@ Returns: { success: true, playbook: { name, trigger, successRate, path }, reason
4345
4346
  slug,
4346
4347
  dealName,
4347
4348
  outcome
4348
- }, DATA_DIR$35));
4349
+ }, DATA_DIR$36));
4349
4350
  }
4350
4351
  //#endregion
4351
4352
  //#region src/core/goal-engine.ts
@@ -4563,8 +4564,8 @@ function getActiveGoals(dataDir) {
4563
4564
  }
4564
4565
  //#endregion
4565
4566
  //#region src/mcp/tools/pursue-goal.ts
4566
- const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4567
- async function handlePursueGoal(input, dataDir = DATA_DIR$34, options = {}) {
4567
+ const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4568
+ async function handlePursueGoal(input, dataDir = DATA_DIR$35, options = {}) {
4568
4569
  try {
4569
4570
  enforceRbac(dataDir, "pursue_goal");
4570
4571
  const goal = await pursueGoal(dataDir, {
@@ -4627,12 +4628,12 @@ Returns: { goalId, description, target, deadline, decomposition: { analysis, cur
4627
4628
  goal,
4628
4629
  deadline,
4629
4630
  ...context !== void 0 ? { context } : {}
4630
- }, DATA_DIR$34));
4631
+ }, DATA_DIR$35));
4631
4632
  }
4632
4633
  //#endregion
4633
4634
  //#region src/mcp/tools/get-goal-status.ts
4634
- const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4635
- async function handleGetGoalStatus(input, dataDir = DATA_DIR$33) {
4635
+ const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4636
+ async function handleGetGoalStatus(input, dataDir = DATA_DIR$34) {
4636
4637
  try {
4637
4638
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4638
4639
  const allGoals = input.goalId ? readGoals(dataDir).filter((g) => g.id === input.goalId) : getActiveGoals(dataDir);
@@ -4691,17 +4692,17 @@ Args:
4691
4692
 
4692
4693
  Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,
4693
4694
  inputSchema: z.object({ goalId: z.string().optional().describe("Specific goal ID (omit for all active goals)") })
4694
- }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$33));
4695
+ }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$34));
4695
4696
  }
4696
4697
  //#endregion
4697
4698
  //#region src/mcp/tools/register-push-subscription.ts
4698
- const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4699
+ const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4699
4700
  const VALID_PROVIDERS = [
4700
4701
  "gmail",
4701
4702
  "microsoft-graph",
4702
4703
  "slack"
4703
4704
  ];
4704
- async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$32) {
4705
+ async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$33) {
4705
4706
  try {
4706
4707
  if (!VALID_PROVIDERS.includes(input.provider)) return { content: [{
4707
4708
  type: "text",
@@ -4787,12 +4788,12 @@ Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning
4787
4788
  ...microsoftResource !== void 0 ? { microsoftResource } : {},
4788
4789
  ...slackTeamId !== void 0 ? { slackTeamId } : {},
4789
4790
  ...slackChannelId !== void 0 ? { slackChannelId } : {}
4790
- }, DATA_DIR$32));
4791
+ }, DATA_DIR$33));
4791
4792
  }
4792
4793
  //#endregion
4793
4794
  //#region src/mcp/tools/get-push-status.ts
4794
- const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4795
- async function handleGetPushStatus(input, dataDir = DATA_DIR$31) {
4795
+ const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4796
+ async function handleGetPushStatus(input, dataDir = DATA_DIR$32) {
4796
4797
  try {
4797
4798
  let subs = await readSubscriptions(dataDir);
4798
4799
  if (input.slug) subs = subs.filter((s) => s.slug === input.slug);
@@ -4864,7 +4865,7 @@ Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHou
4864
4865
  }, async ({ slug, provider }) => handleGetPushStatus({
4865
4866
  ...slug !== void 0 ? { slug } : {},
4866
4867
  ...provider !== void 0 ? { provider } : {}
4867
- }, DATA_DIR$31));
4868
+ }, DATA_DIR$32));
4868
4869
  }
4869
4870
  //#endregion
4870
4871
  //#region src/core/org-intelligence.ts
@@ -4930,8 +4931,8 @@ function deriveRecommendation(people, missingRoles) {
4930
4931
  }
4931
4932
  //#endregion
4932
4933
  //#region src/mcp/tools/get-org-intelligence.ts
4933
- const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4934
- async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$30) {
4934
+ const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4935
+ async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$31) {
4935
4936
  try {
4936
4937
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4937
4938
  const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);
@@ -5064,8 +5065,8 @@ function buildExecutiveSummary(slug, dealName, stakeholders, overallHealth, sim,
5064
5065
  }
5065
5066
  //#endregion
5066
5067
  //#region src/mcp/tools/open-deal-room.ts
5067
- const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5068
- async function handleOpenDealRoom(input, dataDir = DATA_DIR$29) {
5068
+ const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5069
+ async function handleOpenDealRoom(input, dataDir = DATA_DIR$30) {
5069
5070
  try {
5070
5071
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5071
5072
  const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);
@@ -5148,8 +5149,8 @@ async function buildDailyBriefing(dataDir, today) {
5148
5149
  }
5149
5150
  //#endregion
5150
5151
  //#region src/mcp/tools/get-proactive-briefing.ts
5151
- const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5152
- async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$28) {
5152
+ const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5153
+ async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$29) {
5153
5154
  try {
5154
5155
  const briefing = await buildDailyBriefing(dataDir, input.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
5155
5156
  return { content: [{
@@ -5249,15 +5250,15 @@ function getTemplate(dataDir, id) {
5249
5250
  }
5250
5251
  //#endregion
5251
5252
  //#region src/mcp/tools/list-email-templates.ts
5252
- const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5253
- async function handleListEmailTemplates(input, dataDir = DATA_DIR$27) {
5253
+ const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5254
+ async function handleListEmailTemplates(input, dataDir = DATA_DIR$28) {
5254
5255
  const summary = listTemplates(dataDir, input.category ? { category: input.category } : {}).map(({ body: _body, ...meta }) => meta);
5255
5256
  return { content: [{
5256
5257
  type: "text",
5257
5258
  text: JSON.stringify(summary, null, 2)
5258
5259
  }] };
5259
5260
  }
5260
- function registerListEmailTemplates(server, dataDir = DATA_DIR$27) {
5261
+ function registerListEmailTemplates(server, dataDir = DATA_DIR$28) {
5261
5262
  server.registerTool("list_email_templates", {
5262
5263
  description: "List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').",
5263
5264
  inputSchema: z.object({ category: z.string().optional().describe("Filter by category") })
@@ -5291,8 +5292,8 @@ async function buildVariablesFromCustomer(dataDir, slug) {
5291
5292
  }
5292
5293
  //#endregion
5293
5294
  //#region src/mcp/tools/get-email-template.ts
5294
- const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5295
- async function handleGetEmailTemplate(input, dataDir = DATA_DIR$26) {
5295
+ const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5296
+ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$27) {
5296
5297
  const tmpl = getTemplate(dataDir, input.id);
5297
5298
  if (!tmpl) return { content: [{
5298
5299
  type: "text",
@@ -5308,7 +5309,7 @@ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$26) {
5308
5309
  }, null, 2)
5309
5310
  }] };
5310
5311
  }
5311
- function registerGetEmailTemplate(server, dataDir = DATA_DIR$26) {
5312
+ function registerGetEmailTemplate(server, dataDir = DATA_DIR$27) {
5312
5313
  server.registerTool("get_email_template", {
5313
5314
  description: "Get a specific email template by ID, including its body and detected variables.",
5314
5315
  inputSchema: z.object({ id: z.string().describe("Template ID (e.g. 'enterprise-intro')") })
@@ -5316,8 +5317,8 @@ function registerGetEmailTemplate(server, dataDir = DATA_DIR$26) {
5316
5317
  }
5317
5318
  //#endregion
5318
5319
  //#region src/mcp/tools/draft-email.ts
5319
- const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5320
- async function handleDraftEmail(input, dataDir = DATA_DIR$25) {
5320
+ const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5321
+ async function handleDraftEmail(input, dataDir = DATA_DIR$26) {
5321
5322
  const tmpl = getTemplate(dataDir, input.templateId);
5322
5323
  if (!tmpl) return { content: [{
5323
5324
  type: "text",
@@ -5361,7 +5362,7 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$25) {
5361
5362
  }, null, 2)
5362
5363
  }] };
5363
5364
  }
5364
- function registerDraftEmail(server, dataDir = DATA_DIR$25) {
5365
+ function registerDraftEmail(server, dataDir = DATA_DIR$26) {
5365
5366
  server.registerTool("draft_email", {
5366
5367
  description: `Draft a personalized email for a customer using a stored template.
5367
5368
  Variables are auto-filled from the customer's main_facts.md. Override any variable manually.
@@ -5469,8 +5470,8 @@ async function updateEnrollment(dataDir, id, updates) {
5469
5470
  }
5470
5471
  //#endregion
5471
5472
  //#region src/mcp/tools/enroll-in-sequence.ts
5472
- const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5473
- async function handleEnrollInSequence(input, dataDir = DATA_DIR$24) {
5473
+ const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5474
+ async function handleEnrollInSequence(input, dataDir = DATA_DIR$25) {
5474
5475
  const sequence = getSequence(dataDir, input.sequenceId);
5475
5476
  if (!sequence) return { content: [{
5476
5477
  type: "text",
@@ -5502,7 +5503,7 @@ async function handleEnrollInSequence(input, dataDir = DATA_DIR$24) {
5502
5503
  })
5503
5504
  }] };
5504
5505
  }
5505
- function registerEnrollInSequence(server, dataDir = DATA_DIR$24) {
5506
+ function registerEnrollInSequence(server, dataDir = DATA_DIR$25) {
5506
5507
  server.registerTool("enroll_in_sequence", {
5507
5508
  description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.
5508
5509
  Returns: { enrollmentId, sequenceName, totalSteps }`,
@@ -5519,8 +5520,8 @@ Returns: { enrollmentId, sequenceName, totalSteps }`,
5519
5520
  }
5520
5521
  //#endregion
5521
5522
  //#region src/mcp/tools/list-sequence-enrollments.ts
5522
- const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5523
- async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$23) {
5523
+ const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5524
+ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$24) {
5524
5525
  let enrollments = readEnrollments(dataDir);
5525
5526
  if (input.slug !== void 0) enrollments = enrollments.filter((e) => e.slug === input.slug);
5526
5527
  if (input.status !== void 0) enrollments = enrollments.filter((e) => e.status === input.status);
@@ -5529,7 +5530,7 @@ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$23) {
5529
5530
  text: JSON.stringify({ enrollments }, null, 2)
5530
5531
  }] };
5531
5532
  }
5532
- function registerListSequenceEnrollments(server, dataDir = DATA_DIR$23) {
5533
+ function registerListSequenceEnrollments(server, dataDir = DATA_DIR$24) {
5533
5534
  server.registerTool("list_sequence_enrollments", {
5534
5535
  description: `List email sequence enrollments. Filter by customer slug or status.
5535
5536
  Returns: { enrollments: SequenceEnrollment[] }`,
@@ -5548,8 +5549,8 @@ Returns: { enrollments: SequenceEnrollment[] }`,
5548
5549
  }
5549
5550
  //#endregion
5550
5551
  //#region src/mcp/tools/unenroll-from-sequence.ts
5551
- const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5552
- async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$22) {
5552
+ const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5553
+ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$23) {
5553
5554
  if (!await updateEnrollment(dataDir, input.enrollmentId, { status: "paused" })) return { content: [{
5554
5555
  type: "text",
5555
5556
  text: JSON.stringify({
@@ -5562,7 +5563,7 @@ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$22) {
5562
5563
  text: JSON.stringify({ success: true })
5563
5564
  }] };
5564
5565
  }
5565
- function registerUnenrollFromSequence(server, dataDir = DATA_DIR$22) {
5566
+ function registerUnenrollFromSequence(server, dataDir = DATA_DIR$23) {
5566
5567
  server.registerTool("unenroll_from_sequence", {
5567
5568
  description: `Unenroll (pause) a contact from an email sequence. Sets status to "paused" (soft delete).
5568
5569
  Returns: { success: boolean }`,
@@ -5571,8 +5572,8 @@ Returns: { success: boolean }`,
5571
5572
  }
5572
5573
  //#endregion
5573
5574
  //#region src/mcp/tools/list-sequences.ts
5574
- const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5575
- async function handleListSequences(_input, dataDir = DATA_DIR$21) {
5575
+ const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5576
+ async function handleListSequences(_input, dataDir = DATA_DIR$22) {
5576
5577
  const sequences = listSequences(dataDir);
5577
5578
  const enrollments = readEnrollments(dataDir);
5578
5579
  const result = sequences.map((seq) => ({
@@ -5586,7 +5587,7 @@ async function handleListSequences(_input, dataDir = DATA_DIR$21) {
5586
5587
  text: JSON.stringify({ sequences: result }, null, 2)
5587
5588
  }] };
5588
5589
  }
5589
- function registerListSequences(server, dataDir = DATA_DIR$21) {
5590
+ function registerListSequences(server, dataDir = DATA_DIR$22) {
5590
5591
  server.registerTool("list_sequences", {
5591
5592
  description: `List all email sequences with step count and enrollment count.
5592
5593
  Returns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,
@@ -5721,8 +5722,8 @@ async function generateQuote(dataDir, input) {
5721
5722
  }
5722
5723
  //#endregion
5723
5724
  //#region src/mcp/tools/generate-quote.ts
5724
- const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5725
- async function handleGenerateQuote(input, dataDir = DATA_DIR$20) {
5725
+ const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5726
+ async function handleGenerateQuote(input, dataDir = DATA_DIR$21) {
5726
5727
  try {
5727
5728
  const quote = await generateQuote(dataDir, input);
5728
5729
  return { content: [{
@@ -5746,7 +5747,7 @@ async function handleGenerateQuote(input, dataDir = DATA_DIR$20) {
5746
5747
  }] };
5747
5748
  }
5748
5749
  }
5749
- function registerGenerateQuote(server, dataDir = DATA_DIR$20) {
5750
+ function registerGenerateQuote(server, dataDir = DATA_DIR$21) {
5750
5751
  server.registerTool("generate_quote", {
5751
5752
  description: `Generate a professional HTML quote/offer for a customer deal.
5752
5753
  Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
@@ -5774,8 +5775,8 @@ Returns: { quoteNumber, htmlPath, total, currency, validUntil }`,
5774
5775
  }
5775
5776
  //#endregion
5776
5777
  //#region src/mcp/tools/get-quote-status.ts
5777
- const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5778
- async function handleGetQuoteStatus(input, dataDir = DATA_DIR$19) {
5778
+ const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5779
+ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$20) {
5779
5780
  if (input.quoteNumber) {
5780
5781
  const quote = readQuote(dataDir, input.quoteNumber);
5781
5782
  if (!quote) return { content: [{
@@ -5793,7 +5794,7 @@ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$19) {
5793
5794
  text: JSON.stringify({ quotes }, null, 2)
5794
5795
  }] };
5795
5796
  }
5796
- function registerGetQuoteStatus(server, dataDir = DATA_DIR$19) {
5797
+ function registerGetQuoteStatus(server, dataDir = DATA_DIR$20) {
5797
5798
  server.registerTool("get_quote_status", {
5798
5799
  description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).
5799
5800
  Returns quote with status: draft | sent | viewed | accepted | declined`,
@@ -5808,7 +5809,7 @@ Returns quote with status: draft | sent | viewed | accepted | declined`,
5808
5809
  }
5809
5810
  //#endregion
5810
5811
  //#region src/mcp/tools/get-booking-link.ts
5811
- const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5812
+ const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5812
5813
  function loadCalendlyConfig(dataDir) {
5813
5814
  const p = path.join(dataDir, ".agentic", "integrations", "calendly.yaml");
5814
5815
  if (!fs.existsSync(p)) return {};
@@ -5831,7 +5832,7 @@ function readCustomerFacts(dataDir, slug) {
5831
5832
  ...email ? { email } : {}
5832
5833
  };
5833
5834
  }
5834
- async function handleGetBookingLink(input, dataDir = DATA_DIR$18) {
5835
+ async function handleGetBookingLink(input, dataDir = DATA_DIR$19) {
5835
5836
  const config = loadCalendlyConfig(dataDir);
5836
5837
  const apiKey = config.apiKey ?? process.env["CALENDLY_API_KEY"] ?? "";
5837
5838
  if (!apiKey) return { content: [{
@@ -5859,7 +5860,7 @@ async function handleGetBookingLink(input, dataDir = DATA_DIR$18) {
5859
5860
  }] };
5860
5861
  }
5861
5862
  }
5862
- function registerGetBookingLink(server, dataDir = DATA_DIR$18) {
5863
+ function registerGetBookingLink(server, dataDir = DATA_DIR$19) {
5863
5864
  server.registerTool("get_booking_link", {
5864
5865
  description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
5865
5866
  Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
@@ -6029,8 +6030,8 @@ function calcSlaDue(createdDate, priority, rules) {
6029
6030
  }
6030
6031
  //#endregion
6031
6032
  //#region src/mcp/tools/create-ticket.ts
6032
- const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6033
- async function handleCreateTicket(input, dataDir = DATA_DIR$17) {
6033
+ const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6034
+ async function handleCreateTicket(input, dataDir = DATA_DIR$18) {
6034
6035
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6035
6036
  const rules = loadSlaRules(dataDir);
6036
6037
  const priority = input.priority ?? "normal";
@@ -6052,7 +6053,7 @@ async function handleCreateTicket(input, dataDir = DATA_DIR$17) {
6052
6053
  text: JSON.stringify({ ticket }, null, 2)
6053
6054
  }] };
6054
6055
  }
6055
- function registerCreateTicket(server, dataDir = DATA_DIR$17) {
6056
+ function registerCreateTicket(server, dataDir = DATA_DIR$18) {
6056
6057
  server.registerTool("create_ticket", {
6057
6058
  description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.
6058
6059
  Returns: { ticket } with id T-NNN, status=open, slaDue`,
@@ -6078,8 +6079,8 @@ Returns: { ticket } with id T-NNN, status=open, slaDue`,
6078
6079
  }
6079
6080
  //#endregion
6080
6081
  //#region src/mcp/tools/update-ticket.ts
6081
- const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6082
- async function handleUpdateTicket(input, dataDir = DATA_DIR$16) {
6082
+ const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6083
+ async function handleUpdateTicket(input, dataDir = DATA_DIR$17) {
6083
6084
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6084
6085
  if (!ticket) return { content: [{
6085
6086
  type: "text",
@@ -6098,7 +6099,7 @@ async function handleUpdateTicket(input, dataDir = DATA_DIR$16) {
6098
6099
  text: JSON.stringify({ ticket: updated }, null, 2)
6099
6100
  }] };
6100
6101
  }
6101
- function registerUpdateTicket(server, dataDir = DATA_DIR$16) {
6102
+ function registerUpdateTicket(server, dataDir = DATA_DIR$17) {
6102
6103
  server.registerTool("update_ticket", {
6103
6104
  description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
6104
6105
  Returns: { ticket }`,
@@ -6123,8 +6124,8 @@ Returns: { ticket }`,
6123
6124
  }
6124
6125
  //#endregion
6125
6126
  //#region src/mcp/tools/list-tickets.ts
6126
- const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6127
- async function handleListTickets(input, dataDir = DATA_DIR$15) {
6127
+ const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6128
+ async function handleListTickets(input, dataDir = DATA_DIR$16) {
6128
6129
  const results = await listAllTickets(dataDir, {
6129
6130
  ...input.slug !== void 0 ? { slug: input.slug } : {},
6130
6131
  ...input.status !== void 0 ? { status: input.status } : {},
@@ -6136,7 +6137,7 @@ async function handleListTickets(input, dataDir = DATA_DIR$15) {
6136
6137
  text: JSON.stringify({ tickets: results }, null, 2)
6137
6138
  }] };
6138
6139
  }
6139
- function registerListTickets(server, dataDir = DATA_DIR$15) {
6140
+ function registerListTickets(server, dataDir = DATA_DIR$16) {
6140
6141
  server.registerTool("list_tickets", {
6141
6142
  description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.
6142
6143
  Returns: { tickets: Array<{ slug, ticket }> }`,
@@ -6166,8 +6167,8 @@ Returns: { tickets: Array<{ slug, ticket }> }`,
6166
6167
  }
6167
6168
  //#endregion
6168
6169
  //#region src/mcp/tools/close-ticket.ts
6169
- const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6170
- async function handleCloseTicket(input, dataDir = DATA_DIR$14) {
6170
+ const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6171
+ async function handleCloseTicket(input, dataDir = DATA_DIR$15) {
6171
6172
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6172
6173
  if (!ticket) return { content: [{
6173
6174
  type: "text",
@@ -6194,7 +6195,7 @@ async function handleCloseTicket(input, dataDir = DATA_DIR$14) {
6194
6195
  text: JSON.stringify({ ticket: updated }, null, 2)
6195
6196
  }] };
6196
6197
  }
6197
- function registerCloseTicket(server, dataDir = DATA_DIR$14) {
6198
+ function registerCloseTicket(server, dataDir = DATA_DIR$15) {
6198
6199
  server.registerTool("close_ticket", {
6199
6200
  description: `Close a support ticket. Optionally logs the resolution as an interaction.
6200
6201
  Returns: { ticket } with status=closed`,
@@ -6348,8 +6349,8 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
6348
6349
  }
6349
6350
  //#endregion
6350
6351
  //#region src/mcp/tools/send-nps-survey.ts
6351
- const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6352
- async function handleSendNpsSurvey(input, dataDir = DATA_DIR$13) {
6352
+ const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6353
+ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$14) {
6353
6354
  const survey = getSurvey(dataDir, input.surveyId);
6354
6355
  if (!survey) return { content: [{
6355
6356
  type: "text",
@@ -6370,7 +6371,7 @@ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$13) {
6370
6371
  }, null, 2)
6371
6372
  }] };
6372
6373
  }
6373
- function registerSendNpsSurvey(server, dataDir = DATA_DIR$13) {
6374
+ function registerSendNpsSurvey(server, dataDir = DATA_DIR$14) {
6374
6375
  server.registerTool("send_nps_survey", {
6375
6376
  description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.
6376
6377
  Does NOT send automatically — returns draft for review.
@@ -6390,8 +6391,8 @@ Returns: { token, subject, body, surveyUrl }`,
6390
6391
  }
6391
6392
  //#endregion
6392
6393
  //#region src/mcp/tools/get-survey-results.ts
6393
- const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6394
- async function handleGetSurveyResults(input, dataDir = DATA_DIR$12) {
6394
+ const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6395
+ async function handleGetSurveyResults(input, dataDir = DATA_DIR$13) {
6395
6396
  const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);
6396
6397
  const nps = calcNpsScore(responses);
6397
6398
  const promoters = responses.filter((r) => r.score >= 9).length;
@@ -6417,7 +6418,7 @@ async function handleGetSurveyResults(input, dataDir = DATA_DIR$12) {
6417
6418
  }, null, 2)
6418
6419
  }] };
6419
6420
  }
6420
- function registerGetSurveyResults(server, dataDir = DATA_DIR$12) {
6421
+ function registerGetSurveyResults(server, dataDir = DATA_DIR$13) {
6421
6422
  server.registerTool("get_survey_results", {
6422
6423
  description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.
6423
6424
  Returns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,
@@ -6518,8 +6519,8 @@ function getKbMetaForExport(article) {
6518
6519
  }
6519
6520
  //#endregion
6520
6521
  //#region src/mcp/tools/search-knowledge-base.ts
6521
- const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6522
- async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$11) {
6522
+ const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6523
+ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$12) {
6523
6524
  const results = searchKbSimple(dataDir, input.query, { ...input.publicOnly ? { publicOnly: true } : {} });
6524
6525
  const limited = (input.category ? results.filter((a) => a.category === input.category) : results).slice(0, input.limit ?? 10);
6525
6526
  return { content: [{
@@ -6534,7 +6535,7 @@ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$11) {
6534
6535
  }, null, 2)
6535
6536
  }] };
6536
6537
  }
6537
- function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$11) {
6538
+ function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$12) {
6538
6539
  server.registerTool("search_knowledge_base", {
6539
6540
  description: `Search the knowledge base for articles. Text search on title, body, and tags.
6540
6541
  Returns: { count, articles[] } with excerpts`,
@@ -6553,8 +6554,8 @@ Returns: { count, articles[] } with excerpts`,
6553
6554
  }
6554
6555
  //#endregion
6555
6556
  //#region src/mcp/tools/create-kb-article.ts
6556
- const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6557
- async function handleCreateKbArticle(input, dataDir = DATA_DIR$10) {
6557
+ const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6558
+ async function handleCreateKbArticle(input, dataDir = DATA_DIR$11) {
6558
6559
  if (getKbArticle(dataDir, input.id)) return { content: [{
6559
6560
  type: "text",
6560
6561
  text: JSON.stringify({ error: `Article '${input.id}' already exists` })
@@ -6582,7 +6583,7 @@ async function handleCreateKbArticle(input, dataDir = DATA_DIR$10) {
6582
6583
  }, null, 2)
6583
6584
  }] };
6584
6585
  }
6585
- function registerCreateKbArticle(server, dataDir = DATA_DIR$10) {
6586
+ function registerCreateKbArticle(server, dataDir = DATA_DIR$11) {
6586
6587
  server.registerTool("create_kb_article", {
6587
6588
  description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.
6588
6589
  Returns: { id, title, category, path }`,
@@ -6607,8 +6608,8 @@ Returns: { id, title, category, path }`,
6607
6608
  }
6608
6609
  //#endregion
6609
6610
  //#region src/mcp/tools/backup-now.ts
6610
- const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6611
- async function handleBackupNow(input, dataDir = DATA_DIR$9) {
6611
+ const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6612
+ async function handleBackupNow(input, dataDir = DATA_DIR$10) {
6612
6613
  const zipPath = path.join(dataDir, `dxcrm-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.zip`);
6613
6614
  const manifest = await runBackup(zipPath, dataDir, { ...input.remote ? { remote: input.remote } : {} }).catch(() => null);
6614
6615
  if (!manifest) return { content: [{
@@ -6645,8 +6646,8 @@ function registerBackupNow(server) {
6645
6646
  }
6646
6647
  //#endregion
6647
6648
  //#region src/mcp/tools/list-backups.ts
6648
- const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6649
- async function handleListBackups(input, dataDir = DATA_DIR$8) {
6649
+ const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6650
+ async function handleListBackups(input, dataDir = DATA_DIR$9) {
6650
6651
  const logEntries = readBackupLog(dataDir);
6651
6652
  const fileEntries = listBackupsInDir(dataDir);
6652
6653
  const entries = logEntries.length > 0 ? logEntries : fileEntries;
@@ -6680,8 +6681,8 @@ function registerListBackups(server) {
6680
6681
  }
6681
6682
  //#endregion
6682
6683
  //#region src/mcp/tools/trigger-sync.ts
6683
- const DATA_DIR$7 = process.cwd();
6684
- async function handleTriggerSync(input, dataDir = DATA_DIR$7) {
6684
+ const DATA_DIR$8 = process.cwd();
6685
+ async function handleTriggerSync(input, dataDir = DATA_DIR$8) {
6685
6686
  const auth = getGmailAuth();
6686
6687
  if (!auth) return { content: [{
6687
6688
  type: "text",
@@ -6775,8 +6776,8 @@ Returns: { success: boolean, synced: number, skipped: number, customers: [...],
6775
6776
  }
6776
6777
  //#endregion
6777
6778
  //#region src/mcp/tools/get-audit-log.ts
6778
- const DATA_DIR$6 = process.cwd();
6779
- async function handleGetAuditLog(input, dataDir = DATA_DIR$6) {
6779
+ const DATA_DIR$7 = process.cwd();
6780
+ async function handleGetAuditLog(input, dataDir = DATA_DIR$7) {
6780
6781
  const entries = readAuditLog(dataDir);
6781
6782
  const filterOpts = { limit: input.limit ?? 50 };
6782
6783
  if (input.slug !== void 0) filterOpts.slug = input.slug;
@@ -6818,8 +6819,8 @@ Returns: { total: number, returned: number, entries: [{timestamp, actor, tool, s
6818
6819
  }
6819
6820
  //#endregion
6820
6821
  //#region src/mcp/tools/get-logs.ts
6821
- const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6822
- async function handleGetLogs(input, dataDir = DATA_DIR$5) {
6822
+ const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6823
+ async function handleGetLogs(input, dataDir = DATA_DIR$6) {
6823
6824
  const query = {
6824
6825
  ...input.level !== void 0 ? { level: input.level } : {},
6825
6826
  ...input.component !== void 0 ? { component: input.component } : {},
@@ -6981,8 +6982,8 @@ async function runDiagnostics(dataDir) {
6981
6982
  }
6982
6983
  //#endregion
6983
6984
  //#region src/mcp/tools/get-diagnostics.ts
6984
- const DATA_DIR$4 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6985
- async function handleGetDiagnostics(input, dataDir = DATA_DIR$4) {
6985
+ const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6986
+ async function handleGetDiagnostics(input, dataDir = DATA_DIR$5) {
6986
6987
  let cleaned = 0;
6987
6988
  if (input.fix) {
6988
6989
  const { cleanupTempFiles } = await Promise.resolve().then(() => doctor_exports);
@@ -7144,11 +7145,11 @@ function diffAgainstNow(dataDir, since, today = (/* @__PURE__ */ new Date()).toI
7144
7145
  }
7145
7146
  //#endregion
7146
7147
  //#region src/mcp/tools/get-pipeline-changes.ts
7147
- const DATA_DIR$3 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7148
+ const DATA_DIR$4 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7148
7149
  function daysAgoIso(days) {
7149
7150
  return (/* @__PURE__ */ new Date(Date.now() - days * 864e5)).toISOString().slice(0, 10);
7150
7151
  }
7151
- async function handleGetPipelineChanges(input, dataDir = DATA_DIR$3) {
7152
+ async function handleGetPipelineChanges(input, dataDir = DATA_DIR$4) {
7152
7153
  const since = input.since ?? daysAgoIso(input.days ?? 7);
7153
7154
  const diff = diffAgainstNow(dataDir, since);
7154
7155
  const payload = diff ? diff : { error: `No pipeline snapshot at or before ${since}. Snapshots accrue daily via the daemon.` };
@@ -7303,8 +7304,8 @@ function analyzeVelocity(dataDir, opts) {
7303
7304
  }
7304
7305
  //#endregion
7305
7306
  //#region src/mcp/tools/get-pipeline-velocity.ts
7306
- const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7307
- async function handleGetPipelineVelocity(input, dataDir = DATA_DIR$2) {
7307
+ const DATA_DIR$3 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7308
+ async function handleGetPipelineVelocity(input, dataDir = DATA_DIR$3) {
7308
7309
  const opts = {};
7309
7310
  if (input.stalledDays !== void 0) opts.stalledDays = input.stalledDays;
7310
7311
  const report = analyzeVelocity(dataDir, opts);
@@ -7335,6 +7336,116 @@ stalledThresholdDays }. snapshotCount is 0 until the daemon has taken snapshots.
7335
7336
  });
7336
7337
  }
7337
7338
  //#endregion
7339
+ //#region src/core/funnel.ts
7340
+ const FUNNEL_STAGES = [
7341
+ "lead",
7342
+ "qualified",
7343
+ "proposal",
7344
+ "negotiation",
7345
+ "won"
7346
+ ];
7347
+ const STAGE_INDEX = Object.fromEntries(FUNNEL_STAGES.map((s, i) => [s, i]));
7348
+ function emptyReport(snapshotCount) {
7349
+ return {
7350
+ fromId: null,
7351
+ toId: null,
7352
+ snapshotCount,
7353
+ stages: [],
7354
+ wonCount: 0,
7355
+ lostCount: 0,
7356
+ winRatePct: null,
7357
+ biggestLeak: null
7358
+ };
7359
+ }
7360
+ function analyzeFunnel(dataDir) {
7361
+ const metas = listSnapshots(dataDir);
7362
+ if (metas.length === 0) return emptyReport(0);
7363
+ const snaps = metas.flatMap((m) => {
7364
+ const s = loadSnapshot(dataDir, m.id);
7365
+ return s ? [s] : [];
7366
+ });
7367
+ const progress = /* @__PURE__ */ new Map();
7368
+ for (const snap of snaps) for (const deal of snap.deals) {
7369
+ const key = `${deal.slug}::${deal.name}`;
7370
+ const p = progress.get(key) ?? {
7371
+ maxIndex: -1,
7372
+ won: false,
7373
+ lost: false
7374
+ };
7375
+ if (deal.stage === "lost") p.lost = true;
7376
+ else {
7377
+ const idx = STAGE_INDEX[deal.stage];
7378
+ if (idx !== void 0 && idx > p.maxIndex) p.maxIndex = idx;
7379
+ if (deal.stage === "won") p.won = true;
7380
+ }
7381
+ progress.set(key, p);
7382
+ }
7383
+ const reached = new Array(FUNNEL_STAGES.length).fill(0);
7384
+ let wonCount = 0;
7385
+ let lostCount = 0;
7386
+ for (const p of progress.values()) {
7387
+ for (let i = 0; i <= p.maxIndex; i++) reached[i] = (reached[i] ?? 0) + 1;
7388
+ if (p.won) wonCount += 1;
7389
+ else if (p.lost) lostCount += 1;
7390
+ }
7391
+ const stages = FUNNEL_STAGES.map((stage, i) => {
7392
+ const here = reached[i] ?? 0;
7393
+ const next = reached[i + 1];
7394
+ return {
7395
+ stage,
7396
+ reached: here,
7397
+ conversionPctToNext: next === void 0 || here === 0 ? null : Math.round(next / here * 100)
7398
+ };
7399
+ });
7400
+ let biggestLeak = null;
7401
+ for (let i = 0; i < FUNNEL_STAGES.length - 1; i++) {
7402
+ const conv = stages[i].conversionPctToNext;
7403
+ if (conv === null) continue;
7404
+ if (biggestLeak === null || conv < biggestLeak.conversionPct) biggestLeak = {
7405
+ from: FUNNEL_STAGES[i],
7406
+ to: FUNNEL_STAGES[i + 1],
7407
+ conversionPct: conv
7408
+ };
7409
+ }
7410
+ const closed = wonCount + lostCount;
7411
+ const winRatePct = closed > 0 ? Math.round(wonCount / closed * 100) : null;
7412
+ return {
7413
+ fromId: snaps[0].id,
7414
+ toId: snaps[snaps.length - 1].id,
7415
+ snapshotCount: snaps.length,
7416
+ stages,
7417
+ wonCount,
7418
+ lostCount,
7419
+ winRatePct,
7420
+ biggestLeak
7421
+ };
7422
+ }
7423
+ //#endregion
7424
+ //#region src/mcp/tools/get-pipeline-funnel.ts
7425
+ const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7426
+ async function handleGetPipelineFunnel(_input, dataDir = DATA_DIR$2) {
7427
+ const report = analyzeFunnel(dataDir);
7428
+ return { content: [{
7429
+ type: "text",
7430
+ text: JSON.stringify(report, null, 2)
7431
+ }] };
7432
+ }
7433
+ function registerGetPipelineFunnel(server) {
7434
+ server.registerTool("get_pipeline_funnel", {
7435
+ title: "Get Pipeline Funnel",
7436
+ description: `Pipeline conversion funnel & win-rate from the daily snapshot history.
7437
+ For each deal it tracks the furthest stage reached and the win/lost outcome,
7438
+ then builds a cumulative funnel. Answers "where do deals leak out of my
7439
+ pipeline?" and "what's my win rate?".
7440
+
7441
+ Returns: { fromId, toId, snapshotCount,
7442
+ stages[{stage, reached, conversionPctToNext}], wonCount, lostCount, winRatePct,
7443
+ biggestLeak{from,to,conversionPct} }. snapshotCount is 0 until the daemon has
7444
+ taken snapshots.`,
7445
+ inputSchema: z.object({})
7446
+ }, async () => handleGetPipelineFunnel({}));
7447
+ }
7448
+ //#endregion
7338
7449
  //#region src/mcp/prompts.ts
7339
7450
  /**
7340
7451
  * CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.
@@ -7754,6 +7865,7 @@ function createMcpServer() {
7754
7865
  registerGetDiagnostics(server);
7755
7866
  registerGetPipelineChanges(server);
7756
7867
  registerGetPipelineVelocity(server);
7868
+ registerGetPipelineFunnel(server);
7757
7869
  registerCustomObjectTools(server);
7758
7870
  registerPrompts(server);
7759
7871
  registerResources(server);