@datasynx/agentic-crm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +767 -0
  3. package/dist/agent-config-zPvcqu07.js +14 -0
  4. package/dist/agent-config-zPvcqu07.js.map +1 -0
  5. package/dist/approvals-DpjxGHFp.js +67 -0
  6. package/dist/approvals-DpjxGHFp.js.map +1 -0
  7. package/dist/ask-CID3jnuL.js +52 -0
  8. package/dist/ask-CID3jnuL.js.map +1 -0
  9. package/dist/audit-log-DNMY9mUZ.js +49 -0
  10. package/dist/audit-log-DNMY9mUZ.js.map +1 -0
  11. package/dist/auth-CyFuu9X_.js +2 -0
  12. package/dist/auth-DFWwWcYD.js +93 -0
  13. package/dist/auth-DFWwWcYD.js.map +1 -0
  14. package/dist/autofill-Di_-SP7t.js +51 -0
  15. package/dist/autofill-Di_-SP7t.js.map +1 -0
  16. package/dist/backup-CeMk9z86.js +417 -0
  17. package/dist/backup-CeMk9z86.js.map +1 -0
  18. package/dist/backup-f_hC7rBV.js +2 -0
  19. package/dist/calendly-Bft_wwji.js +52 -0
  20. package/dist/calendly-Bft_wwji.js.map +1 -0
  21. package/dist/calendly-D3coO92o.cjs +53 -0
  22. package/dist/calendly-D3coO92o.cjs.map +1 -0
  23. package/dist/chunk-DakpK96I.cjs +43 -0
  24. package/dist/churn-C28IgnAj.js +54 -0
  25. package/dist/churn-C28IgnAj.js.map +1 -0
  26. package/dist/cli.js +4396 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/colors-BG07TZQz.js +11 -0
  29. package/dist/colors-BG07TZQz.js.map +1 -0
  30. package/dist/compliance-B1kk5-YS.js +115 -0
  31. package/dist/compliance-B1kk5-YS.js.map +1 -0
  32. package/dist/compliance-B91zNvCR.cjs +156 -0
  33. package/dist/compliance-B91zNvCR.cjs.map +1 -0
  34. package/dist/compliance-CKSBoQUe.js +118 -0
  35. package/dist/compliance-CKSBoQUe.js.map +1 -0
  36. package/dist/compliance-CujOqAKk.js +2 -0
  37. package/dist/context-builder-BzWAp3Zs.js +96 -0
  38. package/dist/context-builder-BzWAp3Zs.js.map +1 -0
  39. package/dist/context-builder-DlrRcqmJ.js +2 -0
  40. package/dist/conversation-intel-mm7Lhemh.js +72 -0
  41. package/dist/conversation-intel-mm7Lhemh.js.map +1 -0
  42. package/dist/custom-fields-CzNeD3_v.js +2 -0
  43. package/dist/custom-fields-Pl2t9xzp.js +73 -0
  44. package/dist/custom-fields-Pl2t9xzp.js.map +1 -0
  45. package/dist/custom-objects-BHgn1GEX.js +78 -0
  46. package/dist/custom-objects-BHgn1GEX.js.map +1 -0
  47. package/dist/custom-objects-CIFrmQ2V.js +2 -0
  48. package/dist/customer-dir-DIylZ8Q6.js +75 -0
  49. package/dist/customer-dir-DIylZ8Q6.js.map +1 -0
  50. package/dist/daemon/worker.js +207 -0
  51. package/dist/daemon/worker.js.map +1 -0
  52. package/dist/enrichment-3XvgGDfB.js +103 -0
  53. package/dist/enrichment-3XvgGDfB.js.map +1 -0
  54. package/dist/file-lock-B_zi7NQl.js +22 -0
  55. package/dist/file-lock-B_zi7NQl.js.map +1 -0
  56. package/dist/gmail-auth-BP6cJwfw.js +40 -0
  57. package/dist/gmail-auth-BP6cJwfw.js.map +1 -0
  58. package/dist/gmail-auth-DxakCtGm.cjs +44 -0
  59. package/dist/gmail-auth-DxakCtGm.cjs.map +1 -0
  60. package/dist/gmail-auth-OComS92L.js +40 -0
  61. package/dist/gmail-auth-OComS92L.js.map +1 -0
  62. package/dist/gmail-push-watch-DELQFMPk.js +20 -0
  63. package/dist/gmail-push-watch-DELQFMPk.js.map +1 -0
  64. package/dist/gmail-sender-StTpJ9Ub.js +32 -0
  65. package/dist/gmail-sender-StTpJ9Ub.js.map +1 -0
  66. package/dist/gmail-sync-DIaxInDT.js +204 -0
  67. package/dist/gmail-sync-DIaxInDT.js.map +1 -0
  68. package/dist/gmail-sync-hHm9gaWd.cjs +218 -0
  69. package/dist/gmail-sync-hHm9gaWd.cjs.map +1 -0
  70. package/dist/gmail-sync-rQaVqKWd.js +214 -0
  71. package/dist/gmail-sync-rQaVqKWd.js.map +1 -0
  72. package/dist/gmail-webhook-handler-DS7OlRPX.js +3 -0
  73. package/dist/gmail-webhook-handler-e5Od25FX.js +97 -0
  74. package/dist/gmail-webhook-handler-e5Od25FX.js.map +1 -0
  75. package/dist/goal-engine-CUZSpERI.js +2 -0
  76. package/dist/goal-engine-KpBftn4V.js +295 -0
  77. package/dist/goal-engine-KpBftn4V.js.map +1 -0
  78. package/dist/google-drive-sync-DEPcqFca.js +105 -0
  79. package/dist/google-drive-sync-DEPcqFca.js.map +1 -0
  80. package/dist/hybrid-search-BmHttLrR.js +40 -0
  81. package/dist/hybrid-search-BmHttLrR.js.map +1 -0
  82. package/dist/hygiene-DZqfYpFf.js +38 -0
  83. package/dist/hygiene-DZqfYpFf.js.map +1 -0
  84. package/dist/identity-CI6olMNm.js +41 -0
  85. package/dist/identity-CI6olMNm.js.map +1 -0
  86. package/dist/identity-gyfWdrcX.js +2 -0
  87. package/dist/import-hubspot-BaK71U_K.js +588 -0
  88. package/dist/import-hubspot-BaK71U_K.js.map +1 -0
  89. package/dist/index-V8BFaH-b.d.ts +539 -0
  90. package/dist/index-V8BFaH-b.d.ts.map +1 -0
  91. package/dist/index-YqwMd6aQ.d.cts +538 -0
  92. package/dist/index-YqwMd6aQ.d.cts.map +1 -0
  93. package/dist/index.cjs +185 -0
  94. package/dist/index.cjs.map +1 -0
  95. package/dist/index.d.cts +538 -0
  96. package/dist/index.d.cts.map +1 -0
  97. package/dist/index.d.ts +539 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +165 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/interactions-writer-CrPStUll.cjs +77 -0
  102. package/dist/interactions-writer-CrPStUll.cjs.map +1 -0
  103. package/dist/interactions-writer-DO3KcSR3.js +52 -0
  104. package/dist/interactions-writer-DO3KcSR3.js.map +1 -0
  105. package/dist/interactions-writer-SLHnoEeE.js +46 -0
  106. package/dist/interactions-writer-SLHnoEeE.js.map +1 -0
  107. package/dist/interactions-writer-dSPy1XfO.js +2 -0
  108. package/dist/knowledge-base-D0Fh40kc.js +1013 -0
  109. package/dist/knowledge-base-D0Fh40kc.js.map +1 -0
  110. package/dist/lancedb-CCBbpulq.js +2 -0
  111. package/dist/lancedb-rlvWoPwl.js +98 -0
  112. package/dist/lancedb-rlvWoPwl.js.map +1 -0
  113. package/dist/lead-model-BCFzyktm.js +109 -0
  114. package/dist/lead-model-BCFzyktm.js.map +1 -0
  115. package/dist/llm-DEjWcqmW.js +2 -0
  116. package/dist/llm-DvzZqva0.js +372 -0
  117. package/dist/llm-DvzZqva0.js.map +1 -0
  118. package/dist/llm-Z8RIYkpF.js +174 -0
  119. package/dist/llm-Z8RIYkpF.js.map +1 -0
  120. package/dist/llm-iijeXmgq.cjs +198 -0
  121. package/dist/llm-iijeXmgq.cjs.map +1 -0
  122. package/dist/mcp-CdTJWTJf.d.cts +12 -0
  123. package/dist/mcp-CdTJWTJf.d.cts.map +1 -0
  124. package/dist/mcp-CdTJWTJf.d.ts +12 -0
  125. package/dist/mcp-CdTJWTJf.d.ts.map +1 -0
  126. package/dist/mcp.cjs +7464 -0
  127. package/dist/mcp.cjs.map +1 -0
  128. package/dist/mcp.d.cts +12 -0
  129. package/dist/mcp.d.cts.map +1 -0
  130. package/dist/mcp.d.ts +12 -0
  131. package/dist/mcp.d.ts.map +1 -0
  132. package/dist/mcp.js +7448 -0
  133. package/dist/mcp.js.map +1 -0
  134. package/dist/memory-Bb6ky3kb.js +58 -0
  135. package/dist/memory-Bb6ky3kb.js.map +1 -0
  136. package/dist/memory-Cy6-Tbyl.js +2 -0
  137. package/dist/metrics-DH8wHvya.js +26 -0
  138. package/dist/metrics-DH8wHvya.js.map +1 -0
  139. package/dist/microsoft-auth-B8_S45gh.js +17 -0
  140. package/dist/microsoft-auth-B8_S45gh.js.map +1 -0
  141. package/dist/microsoft-calendar-B6MMtUQK.js +67 -0
  142. package/dist/microsoft-calendar-B6MMtUQK.js.map +1 -0
  143. package/dist/microsoft-sync-CpZVoSuq.js +68 -0
  144. package/dist/microsoft-sync-CpZVoSuq.js.map +1 -0
  145. package/dist/nba-3wanmJ0U.js +48 -0
  146. package/dist/nba-3wanmJ0U.js.map +1 -0
  147. package/dist/notification-dispatcher-0vYNngWe.js +97 -0
  148. package/dist/notification-dispatcher-0vYNngWe.js.map +1 -0
  149. package/dist/opportunity-score-BTMOQSTV.js +47 -0
  150. package/dist/opportunity-score-BTMOQSTV.js.map +1 -0
  151. package/dist/pipedrive-client-CdGKpH9b.js +17 -0
  152. package/dist/pipedrive-client-CdGKpH9b.js.map +1 -0
  153. package/dist/pipeline-writer-BqBrYrQc.js +2 -0
  154. package/dist/pipeline-writer-BvVquKIe.js +96 -0
  155. package/dist/pipeline-writer-BvVquKIe.js.map +1 -0
  156. package/dist/pipeline-writer-N2omexxp.cjs +121 -0
  157. package/dist/pipeline-writer-N2omexxp.cjs.map +1 -0
  158. package/dist/pipeline-writer-eufx_0o1.js +102 -0
  159. package/dist/pipeline-writer-eufx_0o1.js.map +1 -0
  160. package/dist/proactive-agent-BgQXw3ac.js +96 -0
  161. package/dist/proactive-agent-BgQXw3ac.js.map +1 -0
  162. package/dist/proactive-worker-BrLHNhjH.js +229 -0
  163. package/dist/proactive-worker-BrLHNhjH.js.map +1 -0
  164. package/dist/push-manager-CdqIIkuh.js +108 -0
  165. package/dist/push-manager-CdqIIkuh.js.map +1 -0
  166. package/dist/push-manager-CowY-0IK.js +2 -0
  167. package/dist/quote-generator-BfwENXzg.js +133 -0
  168. package/dist/quote-generator-BfwENXzg.js.map +1 -0
  169. package/dist/quote-generator-OhSFsi3x.js +2 -0
  170. package/dist/rbac-C7c8tcES.js +2 -0
  171. package/dist/rbac-CTIktZaC.js +91 -0
  172. package/dist/rbac-CTIktZaC.js.map +1 -0
  173. package/dist/relationship-health-odxEoQdJ.js +454 -0
  174. package/dist/relationship-health-odxEoQdJ.js.map +1 -0
  175. package/dist/revenue-simulation-BJdRTEHc.js +2 -0
  176. package/dist/revenue-simulation-Bqf2DLVB.js +251 -0
  177. package/dist/revenue-simulation-Bqf2DLVB.js.map +1 -0
  178. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  179. package/dist/salesforce-client-rhZFa_p5.js +51 -0
  180. package/dist/salesforce-client-rhZFa_p5.js.map +1 -0
  181. package/dist/segments-BqcD5HIl.js +61 -0
  182. package/dist/segments-BqcD5HIl.js.map +1 -0
  183. package/dist/sequence-engine-CCTHEBgi.js +2 -0
  184. package/dist/sequence-engine-J1lTW_in.js +91 -0
  185. package/dist/sequence-engine-J1lTW_in.js.map +1 -0
  186. package/dist/sequence-store-DaaWr0Os.js +221 -0
  187. package/dist/sequence-store-DaaWr0Os.js.map +1 -0
  188. package/dist/server-Dyva03K8.js +4287 -0
  189. package/dist/server-Dyva03K8.js.map +1 -0
  190. package/dist/session-B9AilxOE.js +81 -0
  191. package/dist/session-B9AilxOE.js.map +1 -0
  192. package/dist/session-D0qFkBla.cjs +82 -0
  193. package/dist/session-D0qFkBla.cjs.map +1 -0
  194. package/dist/session-D9ub6Wl1.js +79 -0
  195. package/dist/session-D9ub6Wl1.js.map +1 -0
  196. package/dist/session-mWHA71Lw.js +2 -0
  197. package/dist/session-store-B0QZE8Bx.cjs +697 -0
  198. package/dist/session-store-B0QZE8Bx.cjs.map +1 -0
  199. package/dist/session-store-C8tEvMPw.js +543 -0
  200. package/dist/session-store-C8tEvMPw.js.map +1 -0
  201. package/dist/session-store-CEa39Dxs.js +15 -0
  202. package/dist/session-store-CEa39Dxs.js.map +1 -0
  203. package/dist/sla-engine-5IhTsBUR.js +2 -0
  204. package/dist/sla-engine-BqX-7u-7.js +53 -0
  205. package/dist/sla-engine-BqX-7u-7.js.map +1 -0
  206. package/dist/sop-DkhVChGy.js +2 -0
  207. package/dist/sop-Vp0UPWFW.js +70 -0
  208. package/dist/sop-Vp0UPWFW.js.map +1 -0
  209. package/dist/survey-engine-C06hcQt3.js +2 -0
  210. package/dist/survey-engine-DBjCYqCv.js +147 -0
  211. package/dist/survey-engine-DBjCYqCv.js.map +1 -0
  212. package/dist/sync-state-ChaLbamC.js +33 -0
  213. package/dist/sync-state-ChaLbamC.js.map +1 -0
  214. package/dist/sync-state-CwLSt_1m.js +2 -0
  215. package/dist/ticket-writer-CjqKeIRD.js +2 -0
  216. package/dist/ticket-writer-j2oX_Wal.js +134 -0
  217. package/dist/ticket-writer-j2oX_Wal.js.map +1 -0
  218. package/dist/tone-Bdm5uaht.js +48 -0
  219. package/dist/tone-Bdm5uaht.js.map +1 -0
  220. package/dist/tone-DRKlZgPr.cjs +43 -0
  221. package/dist/tone-DRKlZgPr.cjs.map +1 -0
  222. package/dist/tone-vNb2DAAD.js +39 -0
  223. package/dist/tone-vNb2DAAD.js.map +1 -0
  224. package/dist/transcript-watcher-CL2QUygI.js +132 -0
  225. package/dist/transcript-watcher-CL2QUygI.js.map +1 -0
  226. package/dist/unmatched-transcripts-BsH5bhkU.js +26 -0
  227. package/dist/unmatched-transcripts-BsH5bhkU.js.map +1 -0
  228. package/dist/unmatched-transcripts-D0PrJ9iz.js +2 -0
  229. package/dist/update-deal-BNwPGaTV.js +2 -0
  230. package/dist/update-deal-DKC79skb.js +91 -0
  231. package/dist/update-deal-DKC79skb.js.map +1 -0
  232. package/dist/usage-CClTf5e6.cjs +57 -0
  233. package/dist/usage-CClTf5e6.cjs.map +1 -0
  234. package/dist/usage-D0-TYJkw.js +93 -0
  235. package/dist/usage-D0-TYJkw.js.map +1 -0
  236. package/dist/usage-D0u9a-lV.js +54 -0
  237. package/dist/usage-D0u9a-lV.js.map +1 -0
  238. package/dist/vault-C1D3zScD.js +2 -0
  239. package/dist/vault-DXCg29W-.js +86 -0
  240. package/dist/vault-DXCg29W-.js.map +1 -0
  241. package/dist/webhooks-7EpA05Qr.js +138 -0
  242. package/dist/webhooks-7EpA05Qr.js.map +1 -0
  243. package/dist/webhooks-BO2UAnmn.js +94 -0
  244. package/dist/webhooks-BO2UAnmn.js.map +1 -0
  245. package/dist/webhooks-Xn6zO6kd.cjs +97 -0
  246. package/dist/webhooks-Xn6zO6kd.cjs.map +1 -0
  247. package/dist/write-queue-BDolUxfs.cjs +26 -0
  248. package/dist/write-queue-BDolUxfs.cjs.map +1 -0
  249. package/dist/write-queue-IbsAjUnh.js +21 -0
  250. package/dist/write-queue-IbsAjUnh.js.map +1 -0
  251. package/package.json +142 -0
@@ -0,0 +1,251 @@
1
+ import { r as listCustomerSlugs } from "./customer-dir-DIylZ8Q6.js";
2
+ import { t as readPipeline } from "./pipeline-writer-BvVquKIe.js";
3
+ import { c as readGraph, n as readHealth, s as getStakeholders, t as computeCustomerHealth } from "./relationship-health-odxEoQdJ.js";
4
+ import path from "path";
5
+ import fs from "fs";
6
+ //#region src/core/pipeline-stages.ts
7
+ const DEFAULT_STAGES = [
8
+ {
9
+ id: "lead",
10
+ label: "Lead",
11
+ order: 1,
12
+ probability: 10
13
+ },
14
+ {
15
+ id: "qualified",
16
+ label: "Qualified",
17
+ order: 2,
18
+ probability: 30
19
+ },
20
+ {
21
+ id: "proposal",
22
+ label: "Proposal",
23
+ order: 3,
24
+ probability: 50
25
+ },
26
+ {
27
+ id: "negotiation",
28
+ label: "Negotiation",
29
+ order: 4,
30
+ probability: 75
31
+ },
32
+ {
33
+ id: "won",
34
+ label: "Won",
35
+ order: 5,
36
+ isFinal: true,
37
+ probability: 100
38
+ },
39
+ {
40
+ id: "lost",
41
+ label: "Lost",
42
+ order: 6,
43
+ isFinal: true,
44
+ probability: 0
45
+ }
46
+ ];
47
+ function stagesPath(dataDir) {
48
+ return path.join(dataDir, ".agentic", "pipeline-stages.json");
49
+ }
50
+ function getPipelineStages(dataDir) {
51
+ const p = stagesPath(dataDir);
52
+ if (!fs.existsSync(p)) return DEFAULT_STAGES;
53
+ try {
54
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
55
+ } catch {
56
+ return DEFAULT_STAGES;
57
+ }
58
+ }
59
+ function setPipelineStage(dataDir, stage) {
60
+ const stages = getPipelineStages(dataDir);
61
+ const idx = stages.findIndex((s) => s.id === stage.id);
62
+ if (idx >= 0) stages[idx] = stage;
63
+ else stages.push(stage);
64
+ stages.sort((a, b) => a.order - b.order);
65
+ fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });
66
+ fs.writeFileSync(stagesPath(dataDir), JSON.stringify(stages, null, 2));
67
+ }
68
+ function deletePipelineStage(dataDir, id) {
69
+ const stages = getPipelineStages(dataDir).filter((s) => s.id !== id);
70
+ fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });
71
+ fs.writeFileSync(stagesPath(dataDir), JSON.stringify(stages, null, 2));
72
+ }
73
+ function resetToDefaults(dataDir) {
74
+ fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });
75
+ fs.writeFileSync(stagesPath(dataDir), JSON.stringify(DEFAULT_STAGES, null, 2));
76
+ }
77
+ //#endregion
78
+ //#region src/core/revenue-simulation.ts
79
+ function percentile(sorted, p) {
80
+ if (sorted.length === 0) return 0;
81
+ return sorted[Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)];
82
+ }
83
+ function mean(values) {
84
+ if (values.length === 0) return 0;
85
+ return values.reduce((s, v) => s + v, 0) / values.length;
86
+ }
87
+ function stdDevFn(values, m) {
88
+ if (values.length < 2) return 0;
89
+ const variance = values.reduce((s, v) => s + (v - m) ** 2, 0) / values.length;
90
+ return Math.sqrt(variance);
91
+ }
92
+ function adjustProbability(deal, signals = []) {
93
+ let prob = deal.probability / 100;
94
+ const healthAdj = (deal.healthScore - 60) / 100 * .2;
95
+ prob += healthAdj;
96
+ if (deal.championPresent) prob += .05;
97
+ for (const signal of signals) if (signal.slug === deal.slug) {
98
+ if (signal.impact === "positive") prob += .05 * signal.magnitude;
99
+ if (signal.impact === "negative") prob -= .1 * signal.magnitude;
100
+ }
101
+ return Math.max(.02, Math.min(.98, prob));
102
+ }
103
+ function closeVarianceFn(deal, randFn, todayMs = Date.now()) {
104
+ const variance = (deal.closeDate ? Math.max(0, Math.floor((new Date(deal.closeDate).getTime() - todayMs) / 864e5)) : 90) < 30 ? .05 : .15;
105
+ return 1 + (randFn() - .5) * 2 * variance;
106
+ }
107
+ function buildSensitivityMap(deals, signals) {
108
+ const map = {};
109
+ for (const deal of deals) map[deal.name] = Math.round(deal.value * adjustProbability(deal, signals));
110
+ return map;
111
+ }
112
+ function buildTopRisks(deals, signals, sensitivityMap) {
113
+ return deals.filter((d) => d.healthScore < 60 || d.daysSinceContact > 14).sort((a, b) => (sensitivityMap[b.name] ?? 0) - (sensitivityMap[a.name] ?? 0)).slice(0, 5).map((d) => {
114
+ const reasons = [];
115
+ if (d.healthScore < 60) reasons.push(`health ${d.healthScore}`);
116
+ if (d.daysSinceContact > 14) reasons.push(`${d.daysSinceContact}d no contact`);
117
+ if (!d.championPresent) reasons.push("no champion");
118
+ return `${d.slug}/${d.name}: ${reasons.join(", ")} — €${d.value} at risk`;
119
+ });
120
+ }
121
+ const MAX_ITERATIONS = 5e4;
122
+ function runSimulation(input, randFn = Math.random) {
123
+ const { deals, externalSignals } = input;
124
+ const iterations = Math.min(input.iterations, MAX_ITERATIONS);
125
+ if (deals.length === 0) return {
126
+ p10: 0,
127
+ p50: 0,
128
+ p90: 0,
129
+ expected: 0,
130
+ stdDev: 0,
131
+ atRiskRevenue: 0,
132
+ byCloseMonth: {},
133
+ topRisks: [],
134
+ sensitivityMap: {}
135
+ };
136
+ const todayMs = new Date(input.today).getTime();
137
+ const adjustedProbs = deals.map((d) => adjustProbability(d, externalSignals));
138
+ const outcomes = [];
139
+ const byMonthOutcomes = {};
140
+ for (let i = 0; i < iterations; i++) {
141
+ let total = 0;
142
+ const monthTotals = {};
143
+ for (let j = 0; j < deals.length; j++) {
144
+ const deal = deals[j];
145
+ const prob = adjustedProbs[j];
146
+ if (randFn() < prob) {
147
+ const closedValue = Math.round(deal.value * closeVarianceFn(deal, randFn, todayMs));
148
+ total += closedValue;
149
+ if (deal.closeDate) {
150
+ const month = deal.closeDate.slice(0, 7);
151
+ monthTotals[month] = (monthTotals[month] ?? 0) + closedValue;
152
+ }
153
+ }
154
+ }
155
+ outcomes.push(total);
156
+ for (const [month, val] of Object.entries(monthTotals)) if (val > 0) {
157
+ byMonthOutcomes[month] ??= [];
158
+ byMonthOutcomes[month].push(val);
159
+ }
160
+ }
161
+ outcomes.sort((a, b) => a - b);
162
+ const exp = Math.round(mean(outcomes));
163
+ const sd = Math.round(stdDevFn(outcomes, exp));
164
+ const byCloseMonth = {};
165
+ for (const [month, vals] of Object.entries(byMonthOutcomes)) {
166
+ const sorted = [...vals].sort((a, b) => a - b);
167
+ byCloseMonth[month] = {
168
+ p50: Math.round(percentile(sorted, 50)),
169
+ range: [Math.round(percentile(sorted, 10)), Math.round(percentile(sorted, 90))]
170
+ };
171
+ }
172
+ const sensitivityMap = buildSensitivityMap(deals, externalSignals);
173
+ const topRisks = buildTopRisks(deals, externalSignals, sensitivityMap);
174
+ const atRiskRevenue = deals.filter((d) => d.healthScore < 60).reduce((s, d) => s + d.value, 0);
175
+ return {
176
+ p10: Math.round(percentile(outcomes, 10)),
177
+ p50: Math.round(percentile(outcomes, 50)),
178
+ p90: Math.round(percentile(outcomes, 90)),
179
+ expected: exp,
180
+ stdDev: sd,
181
+ atRiskRevenue,
182
+ byCloseMonth,
183
+ topRisks,
184
+ sensitivityMap
185
+ };
186
+ }
187
+ function buildConfidenceMessage(result, dealCount) {
188
+ const range = result.p90 - result.p10;
189
+ const atRiskPct = result.expected > 0 ? Math.round(result.atRiskRevenue / result.expected * 100) : 0;
190
+ return `P50 forecast: €${(result.p50 / 1e3).toFixed(1)}k with ±€${(range / 2 / 1e3).toFixed(1)}k uncertainty (P10–P90 range). ${atRiskPct}% of pipeline is at risk. ${dealCount} deals simulated.`;
191
+ }
192
+ function getQuarterEnd(date) {
193
+ const month = date.getMonth();
194
+ const quarterEndMonth = Math.floor(month / 3) * 3 + 2;
195
+ return new Date(date.getFullYear(), quarterEndMonth + 1, 0);
196
+ }
197
+ async function buildSimulationInput(dataDir, horizon, today, externalSignals = []) {
198
+ const slugs = listCustomerSlugs(dataDir);
199
+ if (slugs.length === 0) return {
200
+ deals: [],
201
+ externalSignals,
202
+ iterations: 1e4,
203
+ horizon,
204
+ today
205
+ };
206
+ const stages = getPipelineStages(dataDir);
207
+ const stageProb = {};
208
+ for (const s of stages) stageProb[s.id] = s.probability ?? 50;
209
+ const deals = [];
210
+ const todayDate = new Date(today);
211
+ const horizonEnd = horizon === "quarter" ? getQuarterEnd(todayDate) : new Date(todayDate.getFullYear(), 11, 31);
212
+ for (const slug of slugs) {
213
+ const pipelineDeals = await readPipeline(dataDir, slug).catch(() => []);
214
+ if (pipelineDeals.length === 0) continue;
215
+ const health = readHealth(dataDir, slug) ?? computeCustomerHealth(dataDir, slug, today);
216
+ const healthScore = health.overallHealth;
217
+ const championPresent = getStakeholders(readGraph(dataDir, slug)).champions.length > 0;
218
+ const lastContact = health.contacts.map((c) => c.lastContact).filter((lc) => !!lc).sort().pop();
219
+ const daysSinceContact = lastContact ? Math.floor((todayDate.getTime() - new Date(lastContact).getTime()) / 864e5) : 999;
220
+ for (const deal of pipelineDeals) {
221
+ if (deal.stage === "won" || deal.stage === "lost") continue;
222
+ if (deal.close_date && deal.close_date.trim() !== "") {
223
+ if (new Date(deal.close_date) > horizonEnd) continue;
224
+ }
225
+ const probability = deal.probability ?? stageProb[deal.stage] ?? 50;
226
+ const snapshot = {
227
+ slug,
228
+ name: deal.name,
229
+ stage: deal.stage,
230
+ value: deal.value ?? 0,
231
+ probability,
232
+ healthScore,
233
+ daysSinceContact,
234
+ championPresent
235
+ };
236
+ if (deal.close_date && deal.close_date.trim() !== "") snapshot.closeDate = deal.close_date;
237
+ deals.push(snapshot);
238
+ }
239
+ }
240
+ return {
241
+ deals,
242
+ externalSignals,
243
+ iterations: 1e4,
244
+ horizon,
245
+ today
246
+ };
247
+ }
248
+ //#endregion
249
+ export { buildTopRisks as a, percentile as c, deletePipelineStage as d, getPipelineStages as f, buildSimulationInput as i, runSimulation as l, setPipelineStage as m, buildConfidenceMessage as n, closeVarianceFn as o, resetToDefaults as p, buildSensitivityMap as r, mean as s, adjustProbability as t, stdDevFn as u };
250
+
251
+ //# sourceMappingURL=revenue-simulation-Bqf2DLVB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revenue-simulation-Bqf2DLVB.js","names":[],"sources":["../src/core/pipeline-stages.ts","../src/core/revenue-simulation.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport interface PipelineStage {\n id: string;\n label: string;\n color?: string;\n order: number;\n isFinal?: boolean;\n probability?: number;\n}\n\nexport const DEFAULT_STAGES: PipelineStage[] = [\n { id: \"lead\", label: \"Lead\", order: 1, probability: 10 },\n { id: \"qualified\", label: \"Qualified\", order: 2, probability: 30 },\n { id: \"proposal\", label: \"Proposal\", order: 3, probability: 50 },\n { id: \"negotiation\", label: \"Negotiation\", order: 4, probability: 75 },\n { id: \"won\", label: \"Won\", order: 5, isFinal: true, probability: 100 },\n { id: \"lost\", label: \"Lost\", order: 6, isFinal: true, probability: 0 },\n];\n\nfunction stagesPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"pipeline-stages.json\");\n}\n\nexport function getPipelineStages(dataDir: string): PipelineStage[] {\n const p = stagesPath(dataDir);\n if (!fs.existsSync(p)) return DEFAULT_STAGES;\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as PipelineStage[];\n } catch {\n return DEFAULT_STAGES;\n }\n}\n\nexport function setPipelineStage(dataDir: string, stage: PipelineStage): void {\n const stages = getPipelineStages(dataDir);\n const idx = stages.findIndex((s) => s.id === stage.id);\n if (idx >= 0) stages[idx] = stage;\n else stages.push(stage);\n stages.sort((a, b) => a.order - b.order);\n fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });\n fs.writeFileSync(stagesPath(dataDir), JSON.stringify(stages, null, 2));\n}\n\nexport function deletePipelineStage(dataDir: string, id: string): void {\n const stages = getPipelineStages(dataDir).filter((s) => s.id !== id);\n fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });\n fs.writeFileSync(stagesPath(dataDir), JSON.stringify(stages, null, 2));\n}\n\nexport function resetToDefaults(dataDir: string): void {\n fs.mkdirSync(path.dirname(stagesPath(dataDir)), { recursive: true });\n fs.writeFileSync(stagesPath(dataDir), JSON.stringify(DEFAULT_STAGES, null, 2));\n}\n","import { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport { readHealth, computeCustomerHealth } from \"./relationship-health.js\";\nimport { readGraph, getStakeholders } from \"./graph.js\";\nimport { getPipelineStages } from \"./pipeline-stages.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ExternalSignal {\n slug: string;\n type: \"funding_round\" | \"leadership_change\" | \"news_positive\" | \"news_negative\";\n impact: \"positive\" | \"negative\" | \"neutral\";\n magnitude: number; // 0.0–1.0\n summary: string;\n}\n\nexport interface DealSnapshot {\n slug: string;\n name: string;\n stage: string;\n value: number;\n probability: number;\n closeDate?: string;\n healthScore: number;\n daysSinceContact: number;\n championPresent: boolean;\n}\n\nexport interface SimulationInput {\n deals: DealSnapshot[];\n externalSignals: ExternalSignal[];\n iterations: number;\n horizon: \"quarter\" | \"year\";\n today: string;\n}\n\nexport interface MonthForecast {\n p50: number;\n range: [number, number];\n}\n\nexport interface SimulationResult {\n p10: number;\n p50: number;\n p90: number;\n expected: number;\n stdDev: number;\n atRiskRevenue: number;\n byCloseMonth: Record<string, MonthForecast>;\n topRisks: string[];\n sensitivityMap: Record<string, number>;\n}\n\n// ─── Pure helpers ─────────────────────────────────────────────────────────────\n\nexport function percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0;\n const idx = Math.max(0, Math.ceil((p / 100) * sorted.length) - 1);\n return sorted[idx]!;\n}\n\nexport function mean(values: number[]): number {\n if (values.length === 0) return 0;\n return values.reduce((s, v) => s + v, 0) / values.length;\n}\n\nexport function stdDevFn(values: number[], m: number): number {\n if (values.length < 2) return 0;\n const variance = values.reduce((s, v) => s + (v - m) ** 2, 0) / values.length;\n return Math.sqrt(variance);\n}\n\nexport function adjustProbability(deal: DealSnapshot, signals: ExternalSignal[] = []): number {\n let prob = deal.probability / 100;\n\n // Health adjustment: health 60 = neutral, range -0.12 to +0.08\n const healthAdj = ((deal.healthScore - 60) / 100) * 0.2;\n prob += healthAdj;\n\n // Champion bonus\n if (deal.championPresent) prob += 0.05;\n\n // External signals (D18-ready)\n for (const signal of signals) {\n if (signal.slug === deal.slug) {\n if (signal.impact === \"positive\") prob += 0.05 * signal.magnitude;\n if (signal.impact === \"negative\") prob -= 0.1 * signal.magnitude;\n }\n }\n\n return Math.max(0.02, Math.min(0.98, prob));\n}\n\nexport function closeVarianceFn(\n deal: DealSnapshot,\n randFn: () => number,\n todayMs: number = Date.now()\n): number {\n const daysToClose = deal.closeDate\n ? Math.max(0, Math.floor((new Date(deal.closeDate).getTime() - todayMs) / 86_400_000))\n : 90;\n const variance = daysToClose < 30 ? 0.05 : 0.15;\n return 1 + (randFn() - 0.5) * 2 * variance;\n}\n\nexport function buildSensitivityMap(\n deals: DealSnapshot[],\n signals: ExternalSignal[]\n): Record<string, number> {\n const map: Record<string, number> = {};\n for (const deal of deals) {\n map[deal.name] = Math.round(deal.value * adjustProbability(deal, signals));\n }\n return map;\n}\n\nexport function buildTopRisks(\n deals: DealSnapshot[],\n signals: ExternalSignal[],\n sensitivityMap: Record<string, number>\n): string[] {\n const atRisk = deals.filter((d) => d.healthScore < 60 || d.daysSinceContact > 14);\n return atRisk\n .sort((a, b) => (sensitivityMap[b.name] ?? 0) - (sensitivityMap[a.name] ?? 0))\n .slice(0, 5)\n .map((d) => {\n const reasons: string[] = [];\n if (d.healthScore < 60) reasons.push(`health ${d.healthScore}`);\n if (d.daysSinceContact > 14) reasons.push(`${d.daysSinceContact}d no contact`);\n if (!d.championPresent) reasons.push(\"no champion\");\n return `${d.slug}/${d.name}: ${reasons.join(\", \")} — €${d.value} at risk`;\n });\n}\n\n// ─── Monte Carlo Core ─────────────────────────────────────────────────────────\n\nconst MAX_ITERATIONS = 50_000;\n\nexport function runSimulation(\n input: SimulationInput,\n randFn: () => number = Math.random\n): SimulationResult {\n const { deals, externalSignals } = input;\n const iterations = Math.min(input.iterations, MAX_ITERATIONS);\n\n if (deals.length === 0) {\n return {\n p10: 0,\n p50: 0,\n p90: 0,\n expected: 0,\n stdDev: 0,\n atRiskRevenue: 0,\n byCloseMonth: {},\n topRisks: [],\n sensitivityMap: {},\n };\n }\n\n const todayMs = new Date(input.today).getTime();\n const adjustedProbs = deals.map((d) => adjustProbability(d, externalSignals));\n const outcomes: number[] = [];\n const byMonthOutcomes: Record<string, number[]> = {};\n\n for (let i = 0; i < iterations; i++) {\n let total = 0;\n const monthTotals: Record<string, number> = {};\n\n for (let j = 0; j < deals.length; j++) {\n const deal = deals[j]!;\n const prob = adjustedProbs[j]!;\n if (randFn() < prob) {\n const closedValue = Math.round(deal.value * closeVarianceFn(deal, randFn, todayMs));\n total += closedValue;\n if (deal.closeDate) {\n const month = deal.closeDate.slice(0, 7);\n monthTotals[month] = (monthTotals[month] ?? 0) + closedValue;\n }\n }\n }\n\n outcomes.push(total);\n // Winning-only: only record months where at least one deal closed in this iteration\n for (const [month, val] of Object.entries(monthTotals)) {\n if (val > 0) {\n byMonthOutcomes[month] ??= [];\n byMonthOutcomes[month]!.push(val);\n }\n }\n }\n\n outcomes.sort((a, b) => a - b);\n const exp = Math.round(mean(outcomes));\n const sd = Math.round(stdDevFn(outcomes, exp));\n\n const byCloseMonth: Record<string, MonthForecast> = {};\n for (const [month, vals] of Object.entries(byMonthOutcomes)) {\n const sorted = [...vals].sort((a, b) => a - b);\n byCloseMonth[month] = {\n p50: Math.round(percentile(sorted, 50)),\n range: [Math.round(percentile(sorted, 10)), Math.round(percentile(sorted, 90))],\n };\n }\n\n const sensitivityMap = buildSensitivityMap(deals, externalSignals);\n const topRisks = buildTopRisks(deals, externalSignals, sensitivityMap);\n const atRiskRevenue = deals.filter((d) => d.healthScore < 60).reduce((s, d) => s + d.value, 0);\n\n return {\n p10: Math.round(percentile(outcomes, 10)),\n p50: Math.round(percentile(outcomes, 50)),\n p90: Math.round(percentile(outcomes, 90)),\n expected: exp,\n stdDev: sd,\n atRiskRevenue,\n byCloseMonth,\n topRisks,\n sensitivityMap,\n };\n}\n\n// ─── Confidence message ───────────────────────────────────────────────────────\n\nexport function buildConfidenceMessage(result: SimulationResult, dealCount: number): string {\n const range = result.p90 - result.p10;\n const atRiskPct =\n result.expected > 0 ? Math.round((result.atRiskRevenue / result.expected) * 100) : 0;\n return `P50 forecast: €${(result.p50 / 1000).toFixed(1)}k with ±€${(range / 2 / 1000).toFixed(1)}k uncertainty (P10–P90 range). ${atRiskPct}% of pipeline is at risk. ${dealCount} deals simulated.`;\n}\n\n// ─── Quarter helper ───────────────────────────────────────────────────────────\n\nfunction getQuarterEnd(date: Date): Date {\n const month = date.getMonth();\n const quarterEndMonth = Math.floor(month / 3) * 3 + 2;\n return new Date(date.getFullYear(), quarterEndMonth + 1, 0);\n}\n\n// ─── Data aggregation ─────────────────────────────────────────────────────────\n\nexport async function buildSimulationInput(\n dataDir: string,\n horizon: \"quarter\" | \"year\",\n today: string,\n externalSignals: ExternalSignal[] = []\n): Promise<SimulationInput> {\n const slugs = listCustomerSlugs(dataDir);\n if (slugs.length === 0) {\n return { deals: [], externalSignals, iterations: 10_000, horizon, today };\n }\n\n const stages = getPipelineStages(dataDir);\n const stageProb: Record<string, number> = {};\n for (const s of stages) {\n stageProb[s.id] = s.probability ?? 50;\n }\n\n const deals: DealSnapshot[] = [];\n const todayDate = new Date(today);\n const horizonEnd =\n horizon === \"quarter\" ? getQuarterEnd(todayDate) : new Date(todayDate.getFullYear(), 11, 31);\n\n for (const slug of slugs) {\n const pipelineDeals = await readPipeline(dataDir, slug).catch(() => []);\n if (pipelineDeals.length === 0) continue;\n\n const health = readHealth(dataDir, slug) ?? computeCustomerHealth(dataDir, slug, today);\n const healthScore = health.overallHealth;\n\n const graph = readGraph(dataDir, slug);\n const stakeholders = getStakeholders(graph);\n const championPresent = stakeholders.champions.length > 0;\n\n const lastContact = health.contacts\n .map((c) => c.lastContact)\n .filter((lc): lc is string => !!lc)\n .sort()\n .pop();\n const daysSinceContact = lastContact\n ? Math.floor((todayDate.getTime() - new Date(lastContact).getTime()) / 86_400_000)\n : 999;\n\n for (const deal of pipelineDeals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n const closeDate = new Date(deal.close_date);\n if (closeDate > horizonEnd) continue;\n }\n\n const probability = deal.probability ?? stageProb[deal.stage] ?? 50;\n const snapshot: DealSnapshot = {\n slug,\n name: deal.name,\n stage: deal.stage,\n value: deal.value ?? 0,\n probability,\n healthScore,\n daysSinceContact,\n championPresent,\n };\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n snapshot.closeDate = deal.close_date;\n }\n deals.push(snapshot);\n }\n }\n\n return { deals, externalSignals, iterations: 10_000, horizon, today };\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -0,0 +1,51 @@
1
+ //#region src/sync/salesforce-client.ts
2
+ /**
3
+ * Run a SOQL query and return ALL records, following Salesforce's
4
+ * `nextRecordsUrl` so large orgs are imported completely (no LIMIT cap).
5
+ */
6
+ async function soqlQueryAll(instanceUrl, token, soql) {
7
+ let url = `${instanceUrl}/services/data/v58.0/query?q=${soql}`;
8
+ const all = [];
9
+ while (url) {
10
+ const res = await fetch(url, { headers: {
11
+ Authorization: `Bearer ${token}`,
12
+ Accept: "application/json"
13
+ } });
14
+ if (!res.ok) throw new Error(`Salesforce API error: ${res.status} ${res.statusText}`);
15
+ const data = await res.json();
16
+ all.push(...data.records);
17
+ url = data.nextRecordsUrl ? `${instanceUrl}${data.nextRecordsUrl}` : null;
18
+ }
19
+ return all;
20
+ }
21
+ async function fetchSalesforceContacts(instanceUrl, token) {
22
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,Name,Email,Account.Website+FROM+Contact");
23
+ }
24
+ async function fetchSalesforceTasks(instanceUrl, token) {
25
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,Subject,Description,ActivityDate,Type,WhoId+FROM+Task");
26
+ }
27
+ async function fetchSalesforceOpportunities(instanceUrl, token) {
28
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,Name,StageName,Amount,CloseDate,Probability,Account.Name,Account.Website+FROM+Opportunity");
29
+ }
30
+ async function fetchSalesforceLeads(instanceUrl, token) {
31
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,Name,Company,Email,Title,Phone,Status,Website+FROM+Lead");
32
+ }
33
+ async function fetchSalesforceEvents(instanceUrl, token) {
34
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,Subject,Description,ActivityDate,StartDateTime,WhoId,WhatId+FROM+Event");
35
+ }
36
+ async function fetchSalesforceCases(instanceUrl, token) {
37
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,CaseNumber,Subject,Description,Status,Priority,Account.Name,AccountId,ContactId,CreatedDate,ClosedDate+FROM+Case");
38
+ }
39
+ async function fetchSalesforceLineItems(instanceUrl, token) {
40
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,OpportunityId,Quantity,UnitPrice,TotalPrice,Description,Product2.Name+FROM+OpportunityLineItem");
41
+ }
42
+ async function fetchSalesforceNotes(instanceUrl, token) {
43
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,Title,Body,ParentId,CreatedDate+FROM+Note");
44
+ }
45
+ async function fetchSalesforceCampaignMembers(instanceUrl, token) {
46
+ return soqlQueryAll(instanceUrl, token, "SELECT+Id,CampaignId,Campaign.Name,ContactId,LeadId,Status,CreatedDate+FROM+CampaignMember");
47
+ }
48
+ //#endregion
49
+ export { fetchSalesforceCampaignMembers, fetchSalesforceCases, fetchSalesforceContacts, fetchSalesforceEvents, fetchSalesforceLeads, fetchSalesforceLineItems, fetchSalesforceNotes, fetchSalesforceOpportunities, fetchSalesforceTasks };
50
+
51
+ //# sourceMappingURL=salesforce-client-rhZFa_p5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"salesforce-client-rhZFa_p5.js","names":[],"sources":["../src/sync/salesforce-client.ts"],"sourcesContent":["export interface SalesforceContact {\n Id: string;\n Name: string;\n Email?: string;\n Account?: { Website?: string };\n}\n\nexport interface SalesforceTask {\n Id: string;\n Subject?: string;\n Description?: string;\n ActivityDate?: string;\n Type?: string;\n WhoId?: string;\n}\n\nexport interface SalesforceOpportunity {\n Id: string;\n Name: string;\n StageName?: string;\n Amount?: number | null;\n CloseDate?: string;\n Probability?: number | null;\n Account?: { Name?: string; Website?: string };\n}\n\nexport interface SalesforceLead {\n Id: string;\n Name: string;\n Company?: string;\n Email?: string;\n Title?: string;\n Phone?: string;\n Status?: string;\n Website?: string;\n}\n\nexport interface SalesforceCampaignMember {\n Id: string;\n CampaignId?: string;\n Campaign?: { Name?: string };\n ContactId?: string;\n LeadId?: string;\n Status?: string;\n CreatedDate?: string;\n}\n\nexport interface SalesforceNote {\n Id: string;\n Title?: string;\n Body?: string;\n ParentId?: string;\n CreatedDate?: string;\n}\n\nexport interface SalesforceLineItem {\n Id: string;\n OpportunityId?: string;\n Quantity?: number | null;\n UnitPrice?: number | null;\n TotalPrice?: number | null;\n Description?: string;\n Product2?: { Name?: string };\n}\n\nexport interface SalesforceCase {\n Id: string;\n CaseNumber?: string;\n Subject?: string;\n Description?: string;\n Status?: string;\n Priority?: string;\n Account?: { Name?: string };\n AccountId?: string;\n ContactId?: string;\n CreatedDate?: string;\n ClosedDate?: string;\n}\n\nexport interface SalesforceEvent {\n Id: string;\n Subject?: string;\n Description?: string;\n ActivityDate?: string;\n StartDateTime?: string;\n WhoId?: string;\n WhatId?: string;\n}\n\ninterface SoqlResponse<T> {\n records: T[];\n totalSize: number;\n done: boolean;\n nextRecordsUrl?: string;\n}\n\n/**\n * Run a SOQL query and return ALL records, following Salesforce's\n * `nextRecordsUrl` so large orgs are imported completely (no LIMIT cap).\n */\nasync function soqlQueryAll<T>(instanceUrl: string, token: string, soql: string): Promise<T[]> {\n let url: string | null = `${instanceUrl}/services/data/v58.0/query?q=${soql}`;\n const all: T[] = [];\n\n while (url) {\n const res: Response = await fetch(url, {\n headers: { Authorization: `Bearer ${token}`, Accept: \"application/json\" },\n });\n if (!res.ok) {\n throw new Error(`Salesforce API error: ${res.status} ${res.statusText}`);\n }\n const data = (await res.json()) as SoqlResponse<T>;\n all.push(...data.records);\n url = data.nextRecordsUrl ? `${instanceUrl}${data.nextRecordsUrl}` : null;\n }\n\n return all;\n}\n\nexport async function fetchSalesforceContacts(\n instanceUrl: string,\n token: string\n): Promise<SalesforceContact[]> {\n return soqlQueryAll<SalesforceContact>(\n instanceUrl,\n token,\n \"SELECT+Id,Name,Email,Account.Website+FROM+Contact\"\n );\n}\n\nexport async function fetchSalesforceTasks(\n instanceUrl: string,\n token: string\n): Promise<SalesforceTask[]> {\n return soqlQueryAll<SalesforceTask>(\n instanceUrl,\n token,\n \"SELECT+Id,Subject,Description,ActivityDate,Type,WhoId+FROM+Task\"\n );\n}\n\nexport async function fetchSalesforceOpportunities(\n instanceUrl: string,\n token: string\n): Promise<SalesforceOpportunity[]> {\n return soqlQueryAll<SalesforceOpportunity>(\n instanceUrl,\n token,\n \"SELECT+Id,Name,StageName,Amount,CloseDate,Probability,Account.Name,Account.Website+FROM+Opportunity\"\n );\n}\n\nexport async function fetchSalesforceLeads(\n instanceUrl: string,\n token: string\n): Promise<SalesforceLead[]> {\n return soqlQueryAll<SalesforceLead>(\n instanceUrl,\n token,\n \"SELECT+Id,Name,Company,Email,Title,Phone,Status,Website+FROM+Lead\"\n );\n}\n\nexport async function fetchSalesforceEvents(\n instanceUrl: string,\n token: string\n): Promise<SalesforceEvent[]> {\n return soqlQueryAll<SalesforceEvent>(\n instanceUrl,\n token,\n \"SELECT+Id,Subject,Description,ActivityDate,StartDateTime,WhoId,WhatId+FROM+Event\"\n );\n}\n\nexport async function fetchSalesforceCases(\n instanceUrl: string,\n token: string\n): Promise<SalesforceCase[]> {\n return soqlQueryAll<SalesforceCase>(\n instanceUrl,\n token,\n \"SELECT+Id,CaseNumber,Subject,Description,Status,Priority,Account.Name,AccountId,ContactId,CreatedDate,ClosedDate+FROM+Case\"\n );\n}\n\nexport async function fetchSalesforceLineItems(\n instanceUrl: string,\n token: string\n): Promise<SalesforceLineItem[]> {\n return soqlQueryAll<SalesforceLineItem>(\n instanceUrl,\n token,\n \"SELECT+Id,OpportunityId,Quantity,UnitPrice,TotalPrice,Description,Product2.Name+FROM+OpportunityLineItem\"\n );\n}\n\nexport async function fetchSalesforceNotes(\n instanceUrl: string,\n token: string\n): Promise<SalesforceNote[]> {\n return soqlQueryAll<SalesforceNote>(\n instanceUrl,\n token,\n \"SELECT+Id,Title,Body,ParentId,CreatedDate+FROM+Note\"\n );\n}\n\nexport async function fetchSalesforceCampaignMembers(\n instanceUrl: string,\n token: string\n): Promise<SalesforceCampaignMember[]> {\n return soqlQueryAll<SalesforceCampaignMember>(\n instanceUrl,\n token,\n \"SELECT+Id,CampaignId,Campaign.Name,ContactId,LeadId,Status,CreatedDate+FROM+CampaignMember\"\n );\n}\n\nexport interface SalesforceBulkJobStatus {\n id: string;\n state: \"Open\" | \"UploadComplete\" | \"InProgress\" | \"JobComplete\" | \"Failed\" | \"Aborted\";\n}\n\nexport async function createBulkJob(\n instanceUrl: string,\n token: string,\n soql: string\n): Promise<string> {\n const url = `${instanceUrl}/services/data/v58.0/jobs/query`;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ operation: \"query\", query: soql }),\n });\n if (!res.ok) throw new Error(`Salesforce Bulk API error: ${res.status}`);\n const data = (await res.json()) as { id: string };\n return data.id;\n}\n\nexport async function pollBulkJob(\n instanceUrl: string,\n token: string,\n jobId: string\n): Promise<SalesforceBulkJobStatus> {\n const url = `${instanceUrl}/services/data/v58.0/jobs/query/${jobId}`;\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`Salesforce Bulk poll error: ${res.status}`);\n return (await res.json()) as SalesforceBulkJobStatus;\n}\n\nexport async function* fetchBulkResults(\n instanceUrl: string,\n token: string,\n jobId: string\n): AsyncGenerator<string> {\n let locator: string | undefined;\n\n do {\n const url = locator\n ? `${instanceUrl}/services/data/v58.0/jobs/query/${jobId}/results?locator=${locator}`\n : `${instanceUrl}/services/data/v58.0/jobs/query/${jobId}/results`;\n\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`Salesforce Bulk results error: ${res.status}`);\n\n const csv = await res.text();\n yield csv;\n\n const nextLocator = res.headers.get(\"Sforce-Locator\");\n locator = nextLocator === \"null\" || !nextLocator ? undefined : nextLocator;\n } while (locator);\n}\n"],"mappings":";;;;;AAoGA,eAAe,aAAgB,aAAqB,OAAe,MAA4B;CAC7F,IAAI,MAAqB,GAAG,YAAY,+BAA+B;CACvE,MAAM,MAAW,CAAC;CAElB,OAAO,KAAK;EACV,MAAM,MAAgB,MAAM,MAAM,KAAK,EACrC,SAAS;GAAE,eAAe,UAAU;GAAS,QAAQ;EAAmB,EAC1E,CAAC;EACD,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,yBAAyB,IAAI,OAAO,GAAG,IAAI,YAAY;EAEzE,MAAM,OAAQ,MAAM,IAAI,KAAK;EAC7B,IAAI,KAAK,GAAG,KAAK,OAAO;EACxB,MAAM,KAAK,iBAAiB,GAAG,cAAc,KAAK,mBAAmB;CACvE;CAEA,OAAO;AACT;AAEA,eAAsB,wBACpB,aACA,OAC8B;CAC9B,OAAO,aACL,aACA,OACA,mDACF;AACF;AAEA,eAAsB,qBACpB,aACA,OAC2B;CAC3B,OAAO,aACL,aACA,OACA,iEACF;AACF;AAEA,eAAsB,6BACpB,aACA,OACkC;CAClC,OAAO,aACL,aACA,OACA,qGACF;AACF;AAEA,eAAsB,qBACpB,aACA,OAC2B;CAC3B,OAAO,aACL,aACA,OACA,mEACF;AACF;AAEA,eAAsB,sBACpB,aACA,OAC4B;CAC5B,OAAO,aACL,aACA,OACA,kFACF;AACF;AAEA,eAAsB,qBACpB,aACA,OAC2B;CAC3B,OAAO,aACL,aACA,OACA,4HACF;AACF;AAEA,eAAsB,yBACpB,aACA,OAC+B;CAC/B,OAAO,aACL,aACA,OACA,0GACF;AACF;AAEA,eAAsB,qBACpB,aACA,OAC2B;CAC3B,OAAO,aACL,aACA,OACA,qDACF;AACF;AAEA,eAAsB,+BACpB,aACA,OACqC;CACrC,OAAO,aACL,aACA,OACA,4FACF;AACF"}
@@ -0,0 +1,61 @@
1
+ import { i as readMainFacts, r as listCustomerSlugs } from "./customer-dir-DIylZ8Q6.js";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ //#region src/core/segments.ts
5
+ function segmentsPath(dataDir) {
6
+ return path.join(dataDir, ".agentic", "segments.json");
7
+ }
8
+ function loadSegments(dataDir) {
9
+ const p = segmentsPath(dataDir);
10
+ if (!fs.existsSync(p)) return [];
11
+ try {
12
+ const data = JSON.parse(fs.readFileSync(p, "utf-8"));
13
+ return Array.isArray(data.segments) ? data.segments : [];
14
+ } catch {
15
+ return [];
16
+ }
17
+ }
18
+ function defineSegment(dataDir, name, criteria) {
19
+ const segs = loadSegments(dataDir);
20
+ const idx = segs.findIndex((s) => s.name === name);
21
+ const def = {
22
+ name,
23
+ criteria
24
+ };
25
+ if (idx >= 0) segs[idx] = def;
26
+ else segs.push(def);
27
+ const p = segmentsPath(dataDir);
28
+ fs.mkdirSync(path.dirname(p), { recursive: true });
29
+ fs.writeFileSync(p, JSON.stringify({ segments: segs }, null, 2), "utf-8");
30
+ return segs;
31
+ }
32
+ function daysBetween(fromIso, toIso) {
33
+ const a = new Date(fromIso).getTime();
34
+ const b = new Date(toIso).getTime();
35
+ if (Number.isNaN(a) || Number.isNaN(b)) return 0;
36
+ return Math.floor((b - a) / 864e5);
37
+ }
38
+ /** Return the customer slugs matching the criteria (now defaults to today). */
39
+ async function evaluateSegment(dataDir, criteria, now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
40
+ const matches = [];
41
+ for (const slug of listCustomerSlugs(dataDir)) {
42
+ const facts = await readMainFacts(dataDir, slug).catch(() => null);
43
+ if (!facts) continue;
44
+ if (criteria.stage && facts.relationship_stage !== criteria.stage) continue;
45
+ if (criteria.minDealValue !== void 0 && !(typeof facts.deal_value === "number" && facts.deal_value >= criteria.minDealValue)) continue;
46
+ if (criteria.tags && criteria.tags.length > 0) {
47
+ const tags = facts.tags ?? [];
48
+ if (!criteria.tags.every((t) => tags.includes(t))) continue;
49
+ }
50
+ if (criteria.staleDays !== void 0) {
51
+ const lt = facts.updated;
52
+ if (!lt || daysBetween(lt, now) < criteria.staleDays) continue;
53
+ }
54
+ matches.push(slug);
55
+ }
56
+ return matches;
57
+ }
58
+ //#endregion
59
+ export { defineSegment, evaluateSegment, loadSegments };
60
+
61
+ //# sourceMappingURL=segments-BqcD5HIl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"segments-BqcD5HIl.js","names":[],"sources":["../src/core/segments.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { readMainFacts, listCustomerSlugs } from \"../fs/customer-dir.js\";\n\n/**\n * Customer segments (marketing lists, N4-1): named filter criteria over\n * customers, evaluated on demand. Definitions live in .agentic/segments.json.\n */\nexport interface SegmentCriteria {\n stage?: string;\n tags?: string[];\n minDealValue?: number;\n staleDays?: number;\n}\n\nexport interface SegmentDefinition {\n name: string;\n criteria: SegmentCriteria;\n}\n\nfunction segmentsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"segments.json\");\n}\n\nexport function loadSegments(dataDir: string): SegmentDefinition[] {\n const p = segmentsPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as {\n segments?: SegmentDefinition[];\n };\n return Array.isArray(data.segments) ? data.segments : [];\n } catch {\n return [];\n }\n}\n\nexport function defineSegment(\n dataDir: string,\n name: string,\n criteria: SegmentCriteria\n): SegmentDefinition[] {\n const segs = loadSegments(dataDir);\n const idx = segs.findIndex((s) => s.name === name);\n const def: SegmentDefinition = { name, criteria };\n if (idx >= 0) segs[idx] = def;\n else segs.push(def);\n const p = segmentsPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ segments: segs }, null, 2), \"utf-8\");\n return segs;\n}\n\nexport function removeSegment(dataDir: string, name: string): boolean {\n const segs = loadSegments(dataDir);\n const next = segs.filter((s) => s.name !== name);\n if (next.length === segs.length) return false;\n fs.writeFileSync(segmentsPath(dataDir), JSON.stringify({ segments: next }, null, 2), \"utf-8\");\n return true;\n}\n\nfunction daysBetween(fromIso: string, toIso: string): number {\n const a = new Date(fromIso).getTime();\n const b = new Date(toIso).getTime();\n if (Number.isNaN(a) || Number.isNaN(b)) return 0;\n return Math.floor((b - a) / 86_400_000);\n}\n\n/** Return the customer slugs matching the criteria (now defaults to today). */\nexport async function evaluateSegment(\n dataDir: string,\n criteria: SegmentCriteria,\n now: string = new Date().toISOString().slice(0, 10)\n): Promise<string[]> {\n const matches: string[] = [];\n for (const slug of listCustomerSlugs(dataDir)) {\n const facts = await readMainFacts(dataDir, slug).catch(() => null);\n if (!facts) continue;\n\n if (criteria.stage && facts.relationship_stage !== criteria.stage) continue;\n if (\n criteria.minDealValue !== undefined &&\n !(typeof facts.deal_value === \"number\" && facts.deal_value >= criteria.minDealValue)\n ) {\n continue;\n }\n if (criteria.tags && criteria.tags.length > 0) {\n const tags = facts.tags ?? [];\n if (!criteria.tags.every((t) => tags.includes(t))) continue;\n }\n if (criteria.staleDays !== undefined) {\n // `updated` is the recency proxy (last record change).\n const lt = facts.updated;\n if (!lt || daysBetween(lt, now) < criteria.staleDays) continue;\n }\n matches.push(slug);\n }\n return matches;\n}\n"],"mappings":";;;;AAoBA,SAAS,aAAa,SAAyB;CAC7C,OAAO,KAAK,KAAK,SAAS,YAAY,eAAe;AACvD;AAEA,SAAgB,aAAa,SAAsC;CACjE,MAAM,IAAI,aAAa,OAAO;CAC9B,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,MAAM,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;EAG7D,OAAO,MAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,WAAW,CAAC;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,cACd,SACA,MACA,UACqB;CACrB,MAAM,OAAO,aAAa,OAAO;CACjC,MAAM,MAAM,KAAK,WAAW,MAAM,EAAE,SAAS,IAAI;CACjD,MAAM,MAAyB;EAAE;EAAM;CAAS;CAChD,IAAI,OAAO,GAAG,KAAK,OAAO;MACrB,KAAK,KAAK,GAAG;CAClB,MAAM,IAAI,aAAa,OAAO;CAC9B,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,EAAE,UAAU,KAAK,GAAG,MAAM,CAAC,GAAG,OAAO;CACxE,OAAO;AACT;AAUA,SAAS,YAAY,SAAiB,OAAuB;CAC3D,MAAM,IAAI,IAAI,KAAK,OAAO,EAAE,QAAQ;CACpC,MAAM,IAAI,IAAI,KAAK,KAAK,EAAE,QAAQ;CAClC,IAAI,OAAO,MAAM,CAAC,KAAK,OAAO,MAAM,CAAC,GAAG,OAAO;CAC/C,OAAO,KAAK,OAAO,IAAI,KAAK,KAAU;AACxC;;AAGA,eAAsB,gBACpB,SACA,UACA,uBAAc,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,GAC/B;CACnB,MAAM,UAAoB,CAAC;CAC3B,KAAK,MAAM,QAAQ,kBAAkB,OAAO,GAAG;EAC7C,MAAM,QAAQ,MAAM,cAAc,SAAS,IAAI,EAAE,YAAY,IAAI;EACjE,IAAI,CAAC,OAAO;EAEZ,IAAI,SAAS,SAAS,MAAM,uBAAuB,SAAS,OAAO;EACnE,IACE,SAAS,iBAAiB,KAAA,KAC1B,EAAE,OAAO,MAAM,eAAe,YAAY,MAAM,cAAc,SAAS,eAEvE;EAEF,IAAI,SAAS,QAAQ,SAAS,KAAK,SAAS,GAAG;GAC7C,MAAM,OAAO,MAAM,QAAQ,CAAC;GAC5B,IAAI,CAAC,SAAS,KAAK,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,GAAG;EACrD;EACA,IAAI,SAAS,cAAc,KAAA,GAAW;GAEpC,MAAM,KAAK,MAAM;GACjB,IAAI,CAAC,MAAM,YAAY,IAAI,GAAG,IAAI,SAAS,WAAW;EACxD;EACA,QAAQ,KAAK,IAAI;CACnB;CACA,OAAO;AACT"}
@@ -0,0 +1,2 @@
1
+ import { r as runSequenceCycle } from "./sequence-engine-J1lTW_in.js";
2
+ export { runSequenceCycle };
@@ -0,0 +1,91 @@
1
+ import { d as getTemplate, i as updateEnrollment, l as interpolate, r as readEnrollments, s as buildVariablesFromCustomer, t as getSequence } from "./sequence-store-DaaWr0Os.js";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ //#region src/core/sequence-engine.ts
5
+ /**
6
+ * Add n days to an ISO date string (YYYY-MM-DD) and return YYYY-MM-DD.
7
+ */
8
+ function addDays(isoDateStr, n) {
9
+ const [year, month, day] = isoDateStr.split("-").map(Number);
10
+ const date = new Date(Date.UTC(year, month - 1, day));
11
+ date.setUTCDate(date.getUTCDate() + n);
12
+ return date.toISOString().slice(0, 10);
13
+ }
14
+ async function processSequenceStep(dataDir, enrollment, today) {
15
+ const sequence = getSequence(dataDir, enrollment.sequenceId);
16
+ if (!sequence) {
17
+ process.stderr.write(`[sequences] Sequence not found: ${enrollment.sequenceId}\n`);
18
+ return "no_step_due";
19
+ }
20
+ if (enrollment.currentStep >= sequence.steps.length) {
21
+ await updateEnrollment(dataDir, enrollment.id, { status: "completed" });
22
+ return "completed";
23
+ }
24
+ const step = sequence.steps[enrollment.currentStep];
25
+ if (today < addDays(enrollment.enrolledAt.slice(0, 10), step.day)) return "no_step_due";
26
+ if (step.skipIfReplied && enrollment.lastRepliedAt) {
27
+ await updateEnrollment(dataDir, enrollment.id, { currentStep: enrollment.currentStep + 1 });
28
+ return "skipped_replied";
29
+ }
30
+ const template = getTemplate(dataDir, step.templateId);
31
+ if (!template) {
32
+ process.stderr.write(`[sequences] Template not found: ${step.templateId}, skipping step\n`);
33
+ await updateEnrollment(dataDir, enrollment.id, {
34
+ currentStep: enrollment.currentStep + 1,
35
+ lastSentAt: (/* @__PURE__ */ new Date()).toISOString()
36
+ });
37
+ return "no_step_due";
38
+ }
39
+ const vars = await buildVariablesFromCustomer(dataDir, enrollment.slug);
40
+ vars["contactEmail"] = enrollment.contactEmail;
41
+ const subject = interpolate(template.subject, vars);
42
+ const body = interpolate(template.body, vars);
43
+ const tokenPath = path.join(dataDir, ".agentic", "gmail-token.json");
44
+ const credPath = path.join(dataDir, ".agentic", "gmail-credentials.json");
45
+ if (fs.existsSync(tokenPath) && fs.existsSync(credPath)) try {
46
+ const { getGmailAuth } = await import("./gmail-auth-OComS92L.js");
47
+ const { sendEmail } = await import("./gmail-sender-StTpJ9Ub.js");
48
+ await sendEmail({
49
+ auth: await getGmailAuth(credPath, tokenPath),
50
+ to: enrollment.contactEmail,
51
+ subject,
52
+ body,
53
+ isHtml: false
54
+ });
55
+ process.stderr.write(`[sequences] Sent step ${enrollment.currentStep} to ${enrollment.contactEmail}\n`);
56
+ } catch (err) {
57
+ process.stderr.write(`[sequences] Send failed: ${err.message}\n`);
58
+ }
59
+ else process.stderr.write(`[sequences] Gmail not configured, advancing step for ${enrollment.contactEmail}\n`);
60
+ await updateEnrollment(dataDir, enrollment.id, {
61
+ currentStep: enrollment.currentStep + 1,
62
+ lastSentAt: (/* @__PURE__ */ new Date()).toISOString(),
63
+ stepsCompleted: [...enrollment.stepsCompleted, enrollment.currentStep]
64
+ });
65
+ return "sent";
66
+ }
67
+ async function runSequenceCycle(dataDir, today) {
68
+ const active = readEnrollments(dataDir).filter((e) => e.status === "active");
69
+ let sent = 0;
70
+ let completed = 0;
71
+ const errors = [];
72
+ for (const enrollment of active) try {
73
+ const result = await processSequenceStep(dataDir, enrollment, today);
74
+ if (result === "sent") sent++;
75
+ if (result === "completed") completed++;
76
+ } catch (err) {
77
+ const msg = `${enrollment.id}: ${err.message}`;
78
+ errors.push(msg);
79
+ process.stderr.write(`[sequences] Error processing ${enrollment.id}: ${err.message}\n`);
80
+ }
81
+ return {
82
+ processed: active.length,
83
+ sent,
84
+ completed,
85
+ errors
86
+ };
87
+ }
88
+ //#endregion
89
+ export { processSequenceStep as n, runSequenceCycle as r, addDays as t };
90
+
91
+ //# sourceMappingURL=sequence-engine-J1lTW_in.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequence-engine-J1lTW_in.js","names":[],"sources":["../src/core/sequence-engine.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { getSequence, readEnrollments, updateEnrollment } from \"../fs/sequence-store.js\";\nimport { getTemplate } from \"../fs/template-store.js\";\nimport { interpolate, buildVariablesFromCustomer } from \"./template-engine.js\";\nimport type { SequenceEnrollment } from \"../schemas/sequence.js\";\n\n/**\n * Add n days to an ISO date string (YYYY-MM-DD) and return YYYY-MM-DD.\n */\nexport function addDays(isoDateStr: string, n: number): string {\n // Parse the date parts to avoid timezone issues\n const [year, month, day] = isoDateStr.split(\"-\").map(Number) as [number, number, number];\n const date = new Date(Date.UTC(year, month - 1, day));\n date.setUTCDate(date.getUTCDate() + n);\n return date.toISOString().slice(0, 10);\n}\n\nexport async function processSequenceStep(\n dataDir: string,\n enrollment: SequenceEnrollment,\n today: string // YYYY-MM-DD\n): Promise<\"sent\" | \"skipped_replied\" | \"completed\" | \"no_step_due\"> {\n const sequence = getSequence(dataDir, enrollment.sequenceId);\n if (!sequence) {\n process.stderr.write(`[sequences] Sequence not found: ${enrollment.sequenceId}\\n`);\n return \"no_step_due\";\n }\n\n // All steps completed\n if (enrollment.currentStep >= sequence.steps.length) {\n await updateEnrollment(dataDir, enrollment.id, { status: \"completed\" });\n return \"completed\";\n }\n\n const step = sequence.steps[enrollment.currentStep]!;\n\n // Calculate due date: enrolledAt date + step.day\n const enrolledDate = enrollment.enrolledAt.slice(0, 10); // YYYY-MM-DD\n const dueDate = addDays(enrolledDate, step.day);\n\n // Not yet due\n if (today < dueDate) {\n return \"no_step_due\";\n }\n\n // Skip if replied and skipIfReplied is true\n if (step.skipIfReplied && enrollment.lastRepliedAt) {\n await updateEnrollment(dataDir, enrollment.id, {\n currentStep: enrollment.currentStep + 1,\n });\n return \"skipped_replied\";\n }\n\n // Load template\n const template = getTemplate(dataDir, step.templateId);\n if (!template) {\n process.stderr.write(`[sequences] Template not found: ${step.templateId}, skipping step\\n`);\n await updateEnrollment(dataDir, enrollment.id, {\n currentStep: enrollment.currentStep + 1,\n lastSentAt: new Date().toISOString(),\n });\n return \"no_step_due\";\n }\n\n // Build variables\n const vars = await buildVariablesFromCustomer(dataDir, enrollment.slug);\n vars[\"contactEmail\"] = enrollment.contactEmail;\n\n // Interpolate\n const subject = interpolate(template.subject, vars);\n const body = interpolate(template.body, vars);\n\n // Try Gmail send if credentials available\n const tokenPath = path.join(dataDir, \".agentic\", \"gmail-token.json\");\n const credPath = path.join(dataDir, \".agentic\", \"gmail-credentials.json\");\n\n if (fs.existsSync(tokenPath) && fs.existsSync(credPath)) {\n try {\n const { getGmailAuth } = await import(\"../sync/gmail-auth.js\");\n const { sendEmail } = await import(\"../sync/gmail-sender.js\");\n const auth = await getGmailAuth(credPath, tokenPath);\n await sendEmail({\n auth,\n to: enrollment.contactEmail,\n subject,\n body,\n isHtml: false,\n });\n process.stderr.write(\n `[sequences] Sent step ${enrollment.currentStep} to ${enrollment.contactEmail}\\n`\n );\n } catch (err) {\n process.stderr.write(`[sequences] Send failed: ${(err as Error).message}\\n`);\n }\n } else {\n process.stderr.write(\n `[sequences] Gmail not configured, advancing step for ${enrollment.contactEmail}\\n`\n );\n }\n\n // Update enrollment\n await updateEnrollment(dataDir, enrollment.id, {\n currentStep: enrollment.currentStep + 1,\n lastSentAt: new Date().toISOString(),\n stepsCompleted: [...enrollment.stepsCompleted, enrollment.currentStep],\n });\n\n return \"sent\";\n}\n\nexport async function runSequenceCycle(\n dataDir: string,\n today: string\n): Promise<{ processed: number; sent: number; completed: number; errors: string[] }> {\n const enrollments = readEnrollments(dataDir);\n const active = enrollments.filter((e) => e.status === \"active\");\n\n let sent = 0;\n let completed = 0;\n const errors: string[] = [];\n\n for (const enrollment of active) {\n try {\n const result = await processSequenceStep(dataDir, enrollment, today);\n if (result === \"sent\") sent++;\n if (result === \"completed\") completed++;\n } catch (err) {\n const msg = `${enrollment.id}: ${(err as Error).message}`;\n errors.push(msg);\n process.stderr.write(\n `[sequences] Error processing ${enrollment.id}: ${(err as Error).message}\\n`\n );\n }\n }\n\n return { processed: active.length, sent, completed, errors };\n}\n"],"mappings":"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"}