@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,134 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { z } from "zod";
4
+ //#region src/schemas/ticket.ts
5
+ const TicketStatusSchema = z.enum([
6
+ "open",
7
+ "in-progress",
8
+ "waiting",
9
+ "resolved",
10
+ "closed"
11
+ ]);
12
+ const TicketPrioritySchema = z.enum([
13
+ "urgent",
14
+ "high",
15
+ "normal",
16
+ "low"
17
+ ]);
18
+ const TicketSchema = z.object({
19
+ id: z.string().regex(/^T-\d{3,}$/),
20
+ title: z.string().min(1),
21
+ status: TicketStatusSchema,
22
+ priority: TicketPrioritySchema.default("normal"),
23
+ assignee: z.string().optional(),
24
+ created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
25
+ slaDue: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
26
+ resolved: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
27
+ description: z.string().optional()
28
+ });
29
+ //#endregion
30
+ //#region src/fs/ticket-writer.ts
31
+ const TICKET_HEADER = "# Tickets\n\n";
32
+ const TABLE_HEADER = `| ID | Title | Status | Priority | Assignee | Created | SLA Due | Resolved |
33
+ |----|-------|--------|----------|----------|---------|---------|---------|`;
34
+ function ticketsPath(dataDir, slug) {
35
+ return path.join(dataDir, "customers", slug, "tickets.md");
36
+ }
37
+ function escapeMd(s) {
38
+ return (s ?? "").replace(/\|/g, "\\|").replace(/\n/g, " ");
39
+ }
40
+ function parseTicketsFromMarkdown(content) {
41
+ const tickets = [];
42
+ const lines = content.split("\n");
43
+ let inTable = false;
44
+ for (const line of lines) {
45
+ if (line.startsWith("| ID |") || line.startsWith("|----")) {
46
+ inTable = true;
47
+ continue;
48
+ }
49
+ if (!inTable) continue;
50
+ if (!line.startsWith("|")) {
51
+ inTable = false;
52
+ continue;
53
+ }
54
+ const cols = line.split("|").slice(1, -1).map((c) => c.trim());
55
+ if (cols.length < 8) continue;
56
+ const [id, title, status, priority, assignee, created, slaDue, resolved] = cols;
57
+ if (!id || !title || id === "ID") continue;
58
+ const raw = {
59
+ id,
60
+ title,
61
+ status: status || "open",
62
+ priority: priority || "normal",
63
+ ...assignee ? { assignee } : {},
64
+ created: created || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
65
+ ...slaDue ? { slaDue } : {},
66
+ ...resolved ? { resolved } : {}
67
+ };
68
+ const parsed = TicketSchema.safeParse(raw);
69
+ if (parsed.success) tickets.push(parsed.data);
70
+ }
71
+ return tickets;
72
+ }
73
+ function serializeTickets(tickets) {
74
+ return `${TICKET_HEADER}${TABLE_HEADER}\n${tickets.map((t) => `| ${t.id} | ${escapeMd(t.title)} | ${t.status} | ${t.priority} | ${t.assignee ?? ""} | ${t.created} | ${t.slaDue ?? ""} | ${t.resolved ?? ""} |`).join("\n")}\n`;
75
+ }
76
+ async function readTickets(dataDir, slug) {
77
+ const p = ticketsPath(dataDir, slug);
78
+ if (!fs.existsSync(p)) return [];
79
+ return parseTicketsFromMarkdown(fs.readFileSync(p, "utf-8"));
80
+ }
81
+ async function upsertTicket(dataDir, slug, ticket) {
82
+ const p = ticketsPath(dataDir, slug);
83
+ const existing = await readTickets(dataDir, slug);
84
+ const idx = existing.findIndex((t) => t.id === ticket.id);
85
+ if (idx >= 0) existing[idx] = ticket;
86
+ else existing.push(ticket);
87
+ fs.writeFileSync(p, serializeTickets(existing), "utf-8");
88
+ }
89
+ function nextTicketId(tickets) {
90
+ const nums = tickets.map((t) => parseInt(t.id.replace("T-", ""), 10)).filter((n) => !isNaN(n));
91
+ const max = nums.length > 0 ? Math.max(...nums) : 0;
92
+ return `T-${String(max + 1).padStart(3, "0")}`;
93
+ }
94
+ async function listAllTickets(dataDir, filter) {
95
+ const customersDir = path.join(dataDir, "customers");
96
+ if (!fs.existsSync(customersDir)) return [];
97
+ const slugs = filter?.slug ? [filter.slug] : fs.readdirSync(customersDir).filter((s) => {
98
+ try {
99
+ return fs.statSync(path.join(customersDir, s)).isDirectory();
100
+ } catch {
101
+ return false;
102
+ }
103
+ });
104
+ const results = [];
105
+ for (const slug of slugs) {
106
+ const tickets = await readTickets(dataDir, slug);
107
+ for (const ticket of tickets) {
108
+ if (filter?.status && ticket.status !== filter.status) continue;
109
+ if (filter?.priority && ticket.priority !== filter.priority) continue;
110
+ if (filter?.assignee && ticket.assignee !== filter.assignee) continue;
111
+ results.push({
112
+ slug,
113
+ ticket
114
+ });
115
+ }
116
+ }
117
+ const priorityOrder = {
118
+ urgent: 0,
119
+ high: 1,
120
+ normal: 2,
121
+ low: 3
122
+ };
123
+ results.sort((a, b) => {
124
+ const pa = priorityOrder[a.ticket.priority] ?? 2;
125
+ const pb = priorityOrder[b.ticket.priority] ?? 2;
126
+ if (pa !== pb) return pa - pb;
127
+ return a.ticket.created.localeCompare(b.ticket.created);
128
+ });
129
+ return results;
130
+ }
131
+ //#endregion
132
+ export { upsertTicket as i, nextTicketId as n, readTickets as r, listAllTickets as t };
133
+
134
+ //# sourceMappingURL=ticket-writer-j2oX_Wal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-writer-j2oX_Wal.js","names":[],"sources":["../src/schemas/ticket.ts","../src/fs/ticket-writer.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const TicketStatusSchema = z.enum([\"open\", \"in-progress\", \"waiting\", \"resolved\", \"closed\"]);\nexport const TicketPrioritySchema = z.enum([\"urgent\", \"high\", \"normal\", \"low\"]);\n\nexport const TicketSchema = z.object({\n id: z.string().regex(/^T-\\d{3,}$/),\n title: z.string().min(1),\n status: TicketStatusSchema,\n priority: TicketPrioritySchema.default(\"normal\"),\n assignee: z.string().optional(),\n created: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n slaDue: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n resolved: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional(),\n description: z.string().optional(),\n});\n\nexport type Ticket = z.infer<typeof TicketSchema>;\nexport type TicketStatus = z.infer<typeof TicketStatusSchema>;\nexport type TicketPriority = z.infer<typeof TicketPrioritySchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { TicketSchema, type Ticket } from \"../schemas/ticket.js\";\n\nconst TICKET_HEADER = \"# Tickets\\n\\n\";\nconst TABLE_HEADER = `| ID | Title | Status | Priority | Assignee | Created | SLA Due | Resolved |\n|----|-------|--------|----------|----------|---------|---------|---------|`;\n\nfunction ticketsPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tickets.md\");\n}\n\nfunction escapeMd(s: string | undefined): string {\n return (s ?? \"\").replace(/\\|/g, \"\\\\|\").replace(/\\n/g, \" \");\n}\n\nfunction parseTicketsFromMarkdown(content: string): Ticket[] {\n const tickets: Ticket[] = [];\n const lines = content.split(\"\\n\");\n let inTable = false;\n\n for (const line of lines) {\n if (line.startsWith(\"| ID |\") || line.startsWith(\"|----\")) {\n inTable = true;\n continue;\n }\n if (!inTable) continue;\n if (!line.startsWith(\"|\")) {\n inTable = false;\n continue;\n }\n\n const cols = line\n .split(\"|\")\n .slice(1, -1)\n .map((c) => c.trim());\n if (cols.length < 8) continue;\n const [id, title, status, priority, assignee, created, slaDue, resolved] = cols;\n if (!id || !title || id === \"ID\") continue;\n\n const raw = {\n id,\n title,\n status: status || \"open\",\n priority: priority || \"normal\",\n ...(assignee ? { assignee } : {}),\n created: created || new Date().toISOString().slice(0, 10),\n ...(slaDue ? { slaDue } : {}),\n ...(resolved ? { resolved } : {}),\n };\n\n const parsed = TicketSchema.safeParse(raw);\n if (parsed.success) tickets.push(parsed.data);\n }\n return tickets;\n}\n\nfunction serializeTickets(tickets: Ticket[]): string {\n const rows = tickets.map(\n (t) =>\n `| ${t.id} | ${escapeMd(t.title)} | ${t.status} | ${t.priority} | ${t.assignee ?? \"\"} | ${t.created} | ${t.slaDue ?? \"\"} | ${t.resolved ?? \"\"} |`\n );\n return `${TICKET_HEADER}${TABLE_HEADER}\\n${rows.join(\"\\n\")}\\n`;\n}\n\nexport async function readTickets(dataDir: string, slug: string): Promise<Ticket[]> {\n const p = ticketsPath(dataDir, slug);\n if (!fs.existsSync(p)) return [];\n return parseTicketsFromMarkdown(fs.readFileSync(p, \"utf-8\") as string);\n}\n\nexport async function upsertTicket(dataDir: string, slug: string, ticket: Ticket): Promise<void> {\n const p = ticketsPath(dataDir, slug);\n const existing = await readTickets(dataDir, slug);\n const idx = existing.findIndex((t) => t.id === ticket.id);\n if (idx >= 0) {\n existing[idx] = ticket;\n } else {\n existing.push(ticket);\n }\n fs.writeFileSync(p, serializeTickets(existing), \"utf-8\");\n}\n\nexport function nextTicketId(tickets: Ticket[]): string {\n const nums = tickets.map((t) => parseInt(t.id.replace(\"T-\", \"\"), 10)).filter((n) => !isNaN(n));\n const max = nums.length > 0 ? Math.max(...nums) : 0;\n return `T-${String(max + 1).padStart(3, \"0\")}`;\n}\n\nexport async function listAllTickets(\n dataDir: string,\n filter?: { slug?: string; status?: string; priority?: string; assignee?: string }\n): Promise<Array<{ slug: string; ticket: Ticket }>> {\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) return [];\n\n const slugs = filter?.slug\n ? [filter.slug]\n : fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const results: Array<{ slug: string; ticket: Ticket }> = [];\n for (const slug of slugs) {\n const tickets = await readTickets(dataDir, slug);\n for (const ticket of tickets) {\n if (filter?.status && ticket.status !== filter.status) continue;\n if (filter?.priority && ticket.priority !== filter.priority) continue;\n if (filter?.assignee && ticket.assignee !== filter.assignee) continue;\n results.push({ slug, ticket });\n }\n }\n\n // Sort: urgent first, then by created date\n const priorityOrder: Record<string, number> = { urgent: 0, high: 1, normal: 2, low: 3 };\n results.sort((a, b) => {\n const pa = priorityOrder[a.ticket.priority] ?? 2;\n const pb = priorityOrder[b.ticket.priority] ?? 2;\n if (pa !== pb) return pa - pb;\n return a.ticket.created.localeCompare(b.ticket.created);\n });\n\n return results;\n}\n"],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ //#region src/core/tone.ts
4
+ function globalPath(dataDir) {
5
+ return path.join(dataDir, ".agentic", "tone.json");
6
+ }
7
+ function customerPath(dataDir, slug) {
8
+ return path.join(dataDir, "customers", slug, "tone.json");
9
+ }
10
+ function readProfile(p) {
11
+ if (!fs.existsSync(p)) return {};
12
+ try {
13
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
14
+ } catch {
15
+ return {};
16
+ }
17
+ }
18
+ function setTone(dataDir, profile, slug) {
19
+ const p = slug ? customerPath(dataDir, slug) : globalPath(dataDir);
20
+ const merged = {
21
+ ...readProfile(p),
22
+ ...profile
23
+ };
24
+ fs.mkdirSync(path.dirname(p), { recursive: true });
25
+ fs.writeFileSync(p, JSON.stringify(merged, null, 2), "utf-8");
26
+ }
27
+ /** Effective profile: global as base, customer fields override. */
28
+ function resolveTone(dataDir, slug) {
29
+ const global = readProfile(globalPath(dataDir));
30
+ if (!slug) return global;
31
+ return {
32
+ ...global,
33
+ ...readProfile(customerPath(dataDir, slug))
34
+ };
35
+ }
36
+ /** Render a tone profile into an instruction string ("" when blank). */
37
+ function toneInstruction(profile) {
38
+ const parts = [];
39
+ if (profile.formality) parts.push(`tone: ${profile.formality}`);
40
+ if (profile.language) parts.push(`language: ${profile.language}`);
41
+ if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(", ")}`);
42
+ if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(", ")}`);
43
+ return parts.join("; ");
44
+ }
45
+ //#endregion
46
+ export { resolveTone, setTone, toneInstruction };
47
+
48
+ //# sourceMappingURL=tone-Bdm5uaht.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tone-Bdm5uaht.js","names":[],"sources":["../src/core/tone.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Customer tonality (domino D8 / F2): per-customer (and global) tone profiles\n * applied automatically to generated communication. Customer profile fields\n * override the global default; the merged profile is rendered into a tone\n * instruction for the LLM (used by draft_email / sequences / journeys).\n */\nexport interface ToneProfile {\n formality?: string; // e.g. formal | casual | friendly\n language?: string; // e.g. de | en\n dos?: string[];\n donts?: string[];\n examples?: string[];\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"tone.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tone.json\");\n}\n\nfunction readProfile(p: string): ToneProfile {\n if (!fs.existsSync(p)) return {};\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as ToneProfile;\n } catch {\n return {};\n }\n}\n\nexport function setTone(dataDir: string, profile: ToneProfile, slug?: string): void {\n const p = slug ? customerPath(dataDir, slug) : globalPath(dataDir);\n const merged = { ...readProfile(p), ...profile };\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify(merged, null, 2), \"utf-8\");\n}\n\n/** Effective profile: global as base, customer fields override. */\nexport function resolveTone(dataDir: string, slug?: string): ToneProfile {\n const global = readProfile(globalPath(dataDir));\n if (!slug) return global;\n return { ...global, ...readProfile(customerPath(dataDir, slug)) };\n}\n\n/** Render a tone profile into an instruction string (\"\" when blank). */\nexport function toneInstruction(profile: ToneProfile): string {\n const parts: string[] = [];\n if (profile.formality) parts.push(`tone: ${profile.formality}`);\n if (profile.language) parts.push(`language: ${profile.language}`);\n if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(\", \")}`);\n if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(\", \")}`);\n return parts.join(\"; \");\n}\n"],"mappings":";;;AAiBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,YAAY,GAAwB;CAC3C,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,QAAQ,SAAiB,SAAsB,MAAqB;CAClF,MAAM,IAAI,OAAO,aAAa,SAAS,IAAI,IAAI,WAAW,OAAO;CACjE,MAAM,SAAS;EAAE,GAAG,YAAY,CAAC;EAAG,GAAG;CAAQ;CAC/C,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,OAAO;AAC9D;;AAGA,SAAgB,YAAY,SAAiB,MAA4B;CACvE,MAAM,SAAS,YAAY,WAAW,OAAO,CAAC;CAC9C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO;EAAE,GAAG;EAAQ,GAAG,YAAY,aAAa,SAAS,IAAI,CAAC;CAAE;AAClE;;AAGA,SAAgB,gBAAgB,SAA8B;CAC5D,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ,WAAW,MAAM,KAAK,SAAS,QAAQ,WAAW;CAC9D,IAAI,QAAQ,UAAU,MAAM,KAAK,aAAa,QAAQ,UAAU;CAChE,IAAI,QAAQ,OAAO,QAAQ,IAAI,QAAQ,MAAM,KAAK,WAAW,QAAQ,IAAI,KAAK,IAAI,GAAG;CACrF,IAAI,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,KAAK,IAAI,GAAG;CAC1F,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -0,0 +1,43 @@
1
+ const require_chunk = require("./chunk-DakpK96I.cjs");
2
+ let path = require("path");
3
+ path = require_chunk.__toESM(path, 1);
4
+ let fs = require("fs");
5
+ fs = require_chunk.__toESM(fs, 1);
6
+ //#region src/core/tone.ts
7
+ function globalPath(dataDir) {
8
+ return path.default.join(dataDir, ".agentic", "tone.json");
9
+ }
10
+ function customerPath(dataDir, slug) {
11
+ return path.default.join(dataDir, "customers", slug, "tone.json");
12
+ }
13
+ function readProfile(p) {
14
+ if (!fs.default.existsSync(p)) return {};
15
+ try {
16
+ return JSON.parse(fs.default.readFileSync(p, "utf-8"));
17
+ } catch {
18
+ return {};
19
+ }
20
+ }
21
+ /** Effective profile: global as base, customer fields override. */
22
+ function resolveTone(dataDir, slug) {
23
+ const global = readProfile(globalPath(dataDir));
24
+ if (!slug) return global;
25
+ return {
26
+ ...global,
27
+ ...readProfile(customerPath(dataDir, slug))
28
+ };
29
+ }
30
+ /** Render a tone profile into an instruction string ("" when blank). */
31
+ function toneInstruction(profile) {
32
+ const parts = [];
33
+ if (profile.formality) parts.push(`tone: ${profile.formality}`);
34
+ if (profile.language) parts.push(`language: ${profile.language}`);
35
+ if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(", ")}`);
36
+ if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(", ")}`);
37
+ return parts.join("; ");
38
+ }
39
+ //#endregion
40
+ exports.resolveTone = resolveTone;
41
+ exports.toneInstruction = toneInstruction;
42
+
43
+ //# sourceMappingURL=tone-DRKlZgPr.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tone-DRKlZgPr.cjs","names":[],"sources":["../src/core/tone.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Customer tonality (domino D8 / F2): per-customer (and global) tone profiles\n * applied automatically to generated communication. Customer profile fields\n * override the global default; the merged profile is rendered into a tone\n * instruction for the LLM (used by draft_email / sequences / journeys).\n */\nexport interface ToneProfile {\n formality?: string; // e.g. formal | casual | friendly\n language?: string; // e.g. de | en\n dos?: string[];\n donts?: string[];\n examples?: string[];\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"tone.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tone.json\");\n}\n\nfunction readProfile(p: string): ToneProfile {\n if (!fs.existsSync(p)) return {};\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as ToneProfile;\n } catch {\n return {};\n }\n}\n\nexport function setTone(dataDir: string, profile: ToneProfile, slug?: string): void {\n const p = slug ? customerPath(dataDir, slug) : globalPath(dataDir);\n const merged = { ...readProfile(p), ...profile };\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify(merged, null, 2), \"utf-8\");\n}\n\n/** Effective profile: global as base, customer fields override. */\nexport function resolveTone(dataDir: string, slug?: string): ToneProfile {\n const global = readProfile(globalPath(dataDir));\n if (!slug) return global;\n return { ...global, ...readProfile(customerPath(dataDir, slug)) };\n}\n\n/** Render a tone profile into an instruction string (\"\" when blank). */\nexport function toneInstruction(profile: ToneProfile): string {\n const parts: string[] = [];\n if (profile.formality) parts.push(`tone: ${profile.formality}`);\n if (profile.language) parts.push(`language: ${profile.language}`);\n if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(\", \")}`);\n if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(\", \")}`);\n return parts.join(\"; \");\n}\n"],"mappings":";;;;;;AAiBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAA,QAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAA,QAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,YAAY,GAAwB;CAC3C,IAAI,CAAC,GAAA,QAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,OAAO,KAAK,MAAM,GAAA,QAAG,aAAa,GAAG,OAAO,CAAW;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;AAUA,SAAgB,YAAY,SAAiB,MAA4B;CACvE,MAAM,SAAS,YAAY,WAAW,OAAO,CAAC;CAC9C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO;EAAE,GAAG;EAAQ,GAAG,YAAY,aAAa,SAAS,IAAI,CAAC;CAAE;AAClE;;AAGA,SAAgB,gBAAgB,SAA8B;CAC5D,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ,WAAW,MAAM,KAAK,SAAS,QAAQ,WAAW;CAC9D,IAAI,QAAQ,UAAU,MAAM,KAAK,aAAa,QAAQ,UAAU;CAChE,IAAI,QAAQ,OAAO,QAAQ,IAAI,QAAQ,MAAM,KAAK,WAAW,QAAQ,IAAI,KAAK,IAAI,GAAG;CACrF,IAAI,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,KAAK,IAAI,GAAG;CAC1F,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -0,0 +1,39 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ //#region src/core/tone.ts
4
+ function globalPath(dataDir) {
5
+ return path.join(dataDir, ".agentic", "tone.json");
6
+ }
7
+ function customerPath(dataDir, slug) {
8
+ return path.join(dataDir, "customers", slug, "tone.json");
9
+ }
10
+ function readProfile(p) {
11
+ if (!fs.existsSync(p)) return {};
12
+ try {
13
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
14
+ } catch {
15
+ return {};
16
+ }
17
+ }
18
+ /** Effective profile: global as base, customer fields override. */
19
+ function resolveTone(dataDir, slug) {
20
+ const global = readProfile(globalPath(dataDir));
21
+ if (!slug) return global;
22
+ return {
23
+ ...global,
24
+ ...readProfile(customerPath(dataDir, slug))
25
+ };
26
+ }
27
+ /** Render a tone profile into an instruction string ("" when blank). */
28
+ function toneInstruction(profile) {
29
+ const parts = [];
30
+ if (profile.formality) parts.push(`tone: ${profile.formality}`);
31
+ if (profile.language) parts.push(`language: ${profile.language}`);
32
+ if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(", ")}`);
33
+ if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(", ")}`);
34
+ return parts.join("; ");
35
+ }
36
+ //#endregion
37
+ export { resolveTone, toneInstruction };
38
+
39
+ //# sourceMappingURL=tone-vNb2DAAD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tone-vNb2DAAD.js","names":[],"sources":["../src/core/tone.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Customer tonality (domino D8 / F2): per-customer (and global) tone profiles\n * applied automatically to generated communication. Customer profile fields\n * override the global default; the merged profile is rendered into a tone\n * instruction for the LLM (used by draft_email / sequences / journeys).\n */\nexport interface ToneProfile {\n formality?: string; // e.g. formal | casual | friendly\n language?: string; // e.g. de | en\n dos?: string[];\n donts?: string[];\n examples?: string[];\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"tone.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"tone.json\");\n}\n\nfunction readProfile(p: string): ToneProfile {\n if (!fs.existsSync(p)) return {};\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as ToneProfile;\n } catch {\n return {};\n }\n}\n\nexport function setTone(dataDir: string, profile: ToneProfile, slug?: string): void {\n const p = slug ? customerPath(dataDir, slug) : globalPath(dataDir);\n const merged = { ...readProfile(p), ...profile };\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify(merged, null, 2), \"utf-8\");\n}\n\n/** Effective profile: global as base, customer fields override. */\nexport function resolveTone(dataDir: string, slug?: string): ToneProfile {\n const global = readProfile(globalPath(dataDir));\n if (!slug) return global;\n return { ...global, ...readProfile(customerPath(dataDir, slug)) };\n}\n\n/** Render a tone profile into an instruction string (\"\" when blank). */\nexport function toneInstruction(profile: ToneProfile): string {\n const parts: string[] = [];\n if (profile.formality) parts.push(`tone: ${profile.formality}`);\n if (profile.language) parts.push(`language: ${profile.language}`);\n if (profile.dos && profile.dos.length) parts.push(`prefer: ${profile.dos.join(\", \")}`);\n if (profile.donts && profile.donts.length) parts.push(`avoid: ${profile.donts.join(\", \")}`);\n return parts.join(\"; \");\n}\n"],"mappings":";;;AAiBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,WAAW;AACnD;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAK,KAAK,SAAS,aAAa,MAAM,WAAW;AAC1D;AAEA,SAAS,YAAY,GAAwB;CAC3C,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;AAUA,SAAgB,YAAY,SAAiB,MAA4B;CACvE,MAAM,SAAS,YAAY,WAAW,OAAO,CAAC;CAC9C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO;EAAE,GAAG;EAAQ,GAAG,YAAY,aAAa,SAAS,IAAI,CAAC;CAAE;AAClE;;AAGA,SAAgB,gBAAgB,SAA8B;CAC5D,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ,WAAW,MAAM,KAAK,SAAS,QAAQ,WAAW;CAC9D,IAAI,QAAQ,UAAU,MAAM,KAAK,aAAa,QAAQ,UAAU;CAChE,IAAI,QAAQ,OAAO,QAAQ,IAAI,QAAQ,MAAM,KAAK,WAAW,QAAQ,IAAI,KAAK,IAAI,GAAG;CACrF,IAAI,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,KAAK,IAAI,GAAG;CAC1F,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -0,0 +1,132 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import matter from "gray-matter";
4
+ import chokidar from "chokidar";
5
+ //#region src/sync/transcript-watcher.ts
6
+ function watchTranscripts(opts) {
7
+ const { paths, extensions, onFile } = opts;
8
+ const watcher = chokidar.watch(paths, {
9
+ ignored: (p, stats) => {
10
+ if (stats?.isDirectory()) return false;
11
+ return !extensions.some((ext) => p.endsWith(ext));
12
+ },
13
+ awaitWriteFinish: {
14
+ stabilityThreshold: 2e3,
15
+ pollInterval: 100
16
+ },
17
+ ignoreInitial: false,
18
+ persistent: true
19
+ });
20
+ watcher.on("add", (filePath) => {
21
+ onFile(filePath).catch((err) => {
22
+ console.error(`[transcript-watcher] Error processing ${filePath}:`, err.message);
23
+ });
24
+ });
25
+ return watcher;
26
+ }
27
+ async function processTranscriptFile(filePath, slug, dataDir) {
28
+ const source = `file://${filePath}`;
29
+ const { readInteractions, appendInteraction } = await import("./interactions-writer-dSPy1XfO.js");
30
+ if ((await readInteractions(dataDir, slug)).includes(source)) return;
31
+ const content = fs.readFileSync(filePath, "utf-8");
32
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
33
+ const filename = filePath.split("/").pop() ?? filePath;
34
+ await appendInteraction(dataDir, slug, {
35
+ date,
36
+ type: "Meeting",
37
+ with: filename,
38
+ subject: filename,
39
+ summary: content.slice(0, 500) + (content.length > 500 ? "..." : ""),
40
+ nextSteps: [],
41
+ sourceRef: source,
42
+ synced: (/* @__PURE__ */ new Date()).toISOString()
43
+ });
44
+ const { indexInLanceDB } = await import("./lancedb-CCBbpulq.js");
45
+ await indexInLanceDB(dataDir, slug, content.slice(0, 2e3), source, {
46
+ date,
47
+ type: "Meeting"
48
+ }).catch((err) => {
49
+ process.stderr.write(`[transcript-watcher] LanceDB index failed: ${err.message}\n`);
50
+ });
51
+ }
52
+ function readCustomerName(customersDir, slug) {
53
+ const mainFactsPath = path.join(customersDir, slug, "main_facts.md");
54
+ if (!fs.existsSync(mainFactsPath)) return slug;
55
+ try {
56
+ const raw = matter(fs.readFileSync(mainFactsPath, "utf-8"));
57
+ return typeof raw.data["name"] === "string" ? raw.data["name"] : slug;
58
+ } catch {
59
+ return slug;
60
+ }
61
+ }
62
+ function fuzzyMatchCustomer(filePath, content, candidates) {
63
+ const filename = path.basename(filePath).toLowerCase();
64
+ const contentPreview = content.toLowerCase().slice(0, 5e3);
65
+ let best = null;
66
+ for (const { slug, name } of candidates) {
67
+ let score = 0;
68
+ const nameLower = name.toLowerCase();
69
+ const slugLower = slug.toLowerCase();
70
+ if (filename.includes(slugLower) || filename.includes(nameLower.replace(/\s+/g, "-"))) score += 10;
71
+ const escaped = nameLower.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
72
+ score += contentPreview.match(new RegExp(escaped, "g"))?.length ?? 0;
73
+ if (score > 0 && (!best || score > best.score)) best = {
74
+ slug,
75
+ score
76
+ };
77
+ }
78
+ return best ? { slug: best.slug } : null;
79
+ }
80
+ async function processTranscriptFileAutoMatch(filePath, dataDir) {
81
+ const customersDir = path.join(dataDir, "customers");
82
+ if (!fs.existsSync(customersDir)) {
83
+ await recordUnmatched(dataDir, filePath, "no_customers_defined");
84
+ return;
85
+ }
86
+ const slugs = fs.readdirSync(customersDir).filter((s) => {
87
+ try {
88
+ return fs.statSync(path.join(customersDir, s)).isDirectory();
89
+ } catch {
90
+ return false;
91
+ }
92
+ });
93
+ if (slugs.length === 0) {
94
+ await recordUnmatched(dataDir, filePath, "no_customers_defined");
95
+ return;
96
+ }
97
+ const matchedSlug = await matchCustomer(filePath, fs.readFileSync(filePath, "utf-8"), slugs.map((slug) => ({
98
+ slug,
99
+ name: readCustomerName(customersDir, slug)
100
+ })));
101
+ if (matchedSlug) await processTranscriptFile(filePath, matchedSlug, dataDir);
102
+ else await recordUnmatched(dataDir, filePath, "no_customer_match");
103
+ }
104
+ /**
105
+ * Resolve a transcript to a customer slug. Prefers LLM recognition (when an
106
+ * ANTHROPIC_API_KEY is configured) and falls back to the filename/content
107
+ * heuristic. The LLM result is only trusted when it names a known candidate
108
+ * with at least medium confidence — guarding against hallucinated slugs.
109
+ */
110
+ async function matchCustomer(filePath, content, candidates) {
111
+ try {
112
+ const { recognizeCustomer } = await import("./llm-DEjWcqmW.js");
113
+ const llm = await recognizeCustomer(content, candidates);
114
+ if (llm.slug && llm.confidence !== "low" && candidates.some((c) => c.slug === llm.slug)) return llm.slug;
115
+ } catch (err) {
116
+ process.stderr.write(`[transcript-watcher] LLM recognition failed, using heuristic: ${err.message}\n`);
117
+ }
118
+ return fuzzyMatchCustomer(filePath, content, candidates)?.slug ?? null;
119
+ }
120
+ async function recordUnmatched(dataDir, filePath, reason) {
121
+ const { appendUnmatched } = await import("./unmatched-transcripts-D0PrJ9iz.js");
122
+ appendUnmatched(dataDir, {
123
+ filePath,
124
+ addedAt: (/* @__PURE__ */ new Date()).toISOString(),
125
+ reason
126
+ });
127
+ process.stderr.write(`[transcript-watcher] Unmatched: ${filePath} (${reason})\n`);
128
+ }
129
+ //#endregion
130
+ export { processTranscriptFile, processTranscriptFileAutoMatch, watchTranscripts };
131
+
132
+ //# sourceMappingURL=transcript-watcher-CL2QUygI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript-watcher-CL2QUygI.js","names":[],"sources":["../src/sync/transcript-watcher.ts"],"sourcesContent":["// src/sync/transcript-watcher.ts\n// chokidar v4 — NO glob support in watch(), use ignored as a function\nimport chokidar, { type FSWatcher } from \"chokidar\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport matter from \"gray-matter\";\n\ninterface WatchOptions {\n paths: string[];\n extensions: string[];\n dataDir: string;\n onFile: (filePath: string) => Promise<void>;\n}\n\nexport function watchTranscripts(opts: WatchOptions): FSWatcher {\n const { paths, extensions, onFile } = opts;\n\n const watcher = chokidar.watch(paths, {\n // v4: ignored is a function (no glob strings)\n ignored: (p: string, stats?: fs.Stats) => {\n if (stats?.isDirectory()) return false;\n return !extensions.some((ext) => p.endsWith(ext));\n },\n awaitWriteFinish: { stabilityThreshold: 2000, pollInterval: 100 },\n ignoreInitial: false,\n persistent: true,\n });\n\n watcher.on(\"add\", (filePath) => {\n onFile(filePath).catch((err: unknown) => {\n console.error(`[transcript-watcher] Error processing ${filePath}:`, (err as Error).message);\n });\n });\n\n return watcher;\n}\n\nexport async function processTranscriptFile(\n filePath: string,\n slug: string,\n dataDir: string\n): Promise<void> {\n const source = `file://${filePath}`;\n\n const { readInteractions, appendInteraction } = await import(\"../fs/interactions-writer.js\");\n const existing = await readInteractions(dataDir, slug);\n if (existing.includes(source)) return;\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n const date = new Date().toISOString().slice(0, 10);\n const filename = filePath.split(\"/\").pop() ?? filePath;\n\n await appendInteraction(dataDir, slug, {\n date,\n type: \"Meeting\",\n with: filename,\n subject: filename,\n summary: content.slice(0, 500) + (content.length > 500 ? \"...\" : \"\"),\n nextSteps: [],\n sourceRef: source,\n synced: new Date().toISOString(),\n });\n\n const { indexInLanceDB } = await import(\"../core/lancedb.js\");\n await indexInLanceDB(dataDir, slug, content.slice(0, 2000), source, {\n date,\n type: \"Meeting\",\n }).catch((err: unknown) => {\n process.stderr.write(`[transcript-watcher] LanceDB index failed: ${(err as Error).message}\\n`);\n });\n}\n\nfunction readCustomerName(customersDir: string, slug: string): string {\n const mainFactsPath = path.join(customersDir, slug, \"main_facts.md\");\n if (!fs.existsSync(mainFactsPath)) return slug;\n try {\n const raw = matter(fs.readFileSync(mainFactsPath, \"utf-8\"));\n return typeof raw.data[\"name\"] === \"string\" ? raw.data[\"name\"] : slug;\n } catch {\n return slug;\n }\n}\n\nfunction fuzzyMatchCustomer(\n filePath: string,\n content: string,\n candidates: Array<{ slug: string; name: string }>\n): { slug: string } | null {\n const filename = path.basename(filePath).toLowerCase();\n const contentPreview = content.toLowerCase().slice(0, 5_000);\n\n let best: { slug: string; score: number } | null = null;\n\n for (const { slug, name } of candidates) {\n let score = 0;\n const nameLower = name.toLowerCase();\n const slugLower = slug.toLowerCase();\n\n // Filename match is the strongest signal\n if (filename.includes(slugLower) || filename.includes(nameLower.replace(/\\s+/g, \"-\"))) {\n score += 10;\n }\n\n // Count name occurrences in content\n const escaped = nameLower.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n score += contentPreview.match(new RegExp(escaped, \"g\"))?.length ?? 0;\n\n if (score > 0 && (!best || score > best.score)) {\n best = { slug, score };\n }\n }\n\n return best ? { slug: best.slug } : null;\n}\n\nexport async function processTranscriptFileAutoMatch(\n filePath: string,\n dataDir: string\n): Promise<void> {\n const customersDir = path.join(dataDir, \"customers\");\n if (!fs.existsSync(customersDir)) {\n await recordUnmatched(dataDir, filePath, \"no_customers_defined\");\n return;\n }\n\n const slugs = fs.readdirSync(customersDir).filter((s) => {\n try {\n return fs.statSync(path.join(customersDir, s)).isDirectory();\n } catch {\n return false;\n }\n });\n\n if (slugs.length === 0) {\n await recordUnmatched(dataDir, filePath, \"no_customers_defined\");\n return;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n const candidates = slugs.map((slug) => ({\n slug,\n name: readCustomerName(customersDir, slug),\n }));\n\n const matchedSlug = await matchCustomer(filePath, content, candidates);\n\n if (matchedSlug) {\n await processTranscriptFile(filePath, matchedSlug, dataDir);\n } else {\n await recordUnmatched(dataDir, filePath, \"no_customer_match\");\n }\n}\n\n/**\n * Resolve a transcript to a customer slug. Prefers LLM recognition (when an\n * ANTHROPIC_API_KEY is configured) and falls back to the filename/content\n * heuristic. The LLM result is only trusted when it names a known candidate\n * with at least medium confidence — guarding against hallucinated slugs.\n */\nasync function matchCustomer(\n filePath: string,\n content: string,\n candidates: Array<{ slug: string; name: string }>\n): Promise<string | null> {\n try {\n const { recognizeCustomer } = await import(\"../core/llm.js\");\n const llm = await recognizeCustomer(content, candidates);\n if (llm.slug && llm.confidence !== \"low\" && candidates.some((c) => c.slug === llm.slug)) {\n return llm.slug;\n }\n } catch (err: unknown) {\n process.stderr.write(\n `[transcript-watcher] LLM recognition failed, using heuristic: ${(err as Error).message}\\n`\n );\n }\n\n return fuzzyMatchCustomer(filePath, content, candidates)?.slug ?? null;\n}\n\nasync function recordUnmatched(\n dataDir: string,\n filePath: string,\n reason: \"no_customer_match\" | \"no_customers_defined\"\n): Promise<void> {\n const { appendUnmatched } = await import(\"../fs/unmatched-transcripts.js\");\n appendUnmatched(dataDir, { filePath, addedAt: new Date().toISOString(), reason });\n process.stderr.write(`[transcript-watcher] Unmatched: ${filePath} (${reason})\\n`);\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ //#region src/fs/unmatched-transcripts.ts
4
+ function getUnmatchedPath(dataDir) {
5
+ return path.join(dataDir, ".agentic", "unmatched-transcripts.json");
6
+ }
7
+ function readUnmatched(dataDir) {
8
+ const filePath = getUnmatchedPath(dataDir);
9
+ if (!fs.existsSync(filePath)) return [];
10
+ try {
11
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
12
+ } catch {
13
+ return [];
14
+ }
15
+ }
16
+ function appendUnmatched(dataDir, entry) {
17
+ const filePath = getUnmatchedPath(dataDir);
18
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
19
+ const existing = readUnmatched(dataDir);
20
+ existing.push(entry);
21
+ fs.writeFileSync(filePath, JSON.stringify(existing, null, 2), "utf-8");
22
+ }
23
+ //#endregion
24
+ export { readUnmatched as n, appendUnmatched as t };
25
+
26
+ //# sourceMappingURL=unmatched-transcripts-BsH5bhkU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmatched-transcripts-BsH5bhkU.js","names":[],"sources":["../src/fs/unmatched-transcripts.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport interface UnmatchedTranscript {\n filePath: string;\n addedAt: string; // ISO timestamp\n reason: \"no_customer_match\" | \"no_customers_defined\";\n}\n\nfunction getUnmatchedPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"unmatched-transcripts.json\");\n}\n\nexport function readUnmatched(dataDir: string): UnmatchedTranscript[] {\n const filePath = getUnmatchedPath(dataDir);\n if (!fs.existsSync(filePath)) return [];\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as UnmatchedTranscript[];\n } catch {\n return [];\n }\n}\n\nexport function appendUnmatched(dataDir: string, entry: UnmatchedTranscript): void {\n const filePath = getUnmatchedPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n const existing = readUnmatched(dataDir);\n existing.push(entry);\n fs.writeFileSync(filePath, JSON.stringify(existing, null, 2), \"utf-8\");\n}\n\nexport function clearUnmatched(dataDir: string): void {\n const filePath = getUnmatchedPath(dataDir);\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, \"[]\", \"utf-8\");\n}\n"],"mappings":";;;AASA,SAAS,iBAAiB,SAAyB;CACjD,OAAO,KAAK,KAAK,SAAS,YAAY,4BAA4B;AACpE;AAEA,SAAgB,cAAc,SAAwC;CACpE,MAAM,WAAW,iBAAiB,OAAO;CACzC,IAAI,CAAC,GAAG,WAAW,QAAQ,GAAG,OAAO,CAAC;CACtC,IAAI;EACF,OAAO,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAW;CAChE,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAgB,gBAAgB,SAAiB,OAAkC;CACjF,MAAM,WAAW,iBAAiB,OAAO;CACzC,GAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;CACxD,MAAM,WAAW,cAAc,OAAO;CACtC,SAAS,KAAK,KAAK;CACnB,GAAG,cAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACvE"}
@@ -0,0 +1,2 @@
1
+ import { t as appendUnmatched } from "./unmatched-transcripts-BsH5bhkU.js";
2
+ export { appendUnmatched };
@@ -0,0 +1,2 @@
1
+ import { t as handleUpdateDeal } from "./update-deal-DKC79skb.js";
2
+ export { handleUpdateDeal };
@@ -0,0 +1,91 @@
1
+ import { i as writeAuditEntry, n as getActor } from "./audit-log-DNMY9mUZ.js";
2
+ import { a as enforceRbac } from "./rbac-CTIktZaC.js";
3
+ import { r as upsertDeal } from "./pipeline-writer-BvVquKIe.js";
4
+ import { z } from "zod";
5
+ //#region src/mcp/tools/update-deal.ts
6
+ const DATA_DIR = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7
+ async function handleUpdateDeal(input, dataDir = DATA_DIR) {
8
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9
+ const deal = {
10
+ name: input.dealName,
11
+ stage: input.stage ?? "lead",
12
+ currency: "EUR",
13
+ updated: today,
14
+ ...input.value !== void 0 ? { value: input.value } : {},
15
+ ...input.probability !== void 0 ? { probability: input.probability } : {},
16
+ ...input.closeDate !== void 0 ? { close_date: input.closeDate } : {},
17
+ ...input.notes !== void 0 ? { notes: input.notes } : {}
18
+ };
19
+ try {
20
+ enforceRbac(dataDir, "update_deal");
21
+ await upsertDeal(dataDir, input.slug, deal);
22
+ writeAuditEntry(dataDir, {
23
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
24
+ actor: getActor(),
25
+ tool: "update_deal",
26
+ slug: input.slug,
27
+ summary: input.dealName
28
+ });
29
+ return { content: [{
30
+ type: "text",
31
+ text: JSON.stringify({
32
+ success: true,
33
+ deal
34
+ }, null, 2)
35
+ }] };
36
+ } catch (err) {
37
+ return { content: [{
38
+ type: "text",
39
+ text: JSON.stringify({
40
+ success: false,
41
+ error: err.message
42
+ }, null, 2)
43
+ }] };
44
+ }
45
+ }
46
+ function registerUpdateDeal(server) {
47
+ server.registerTool("update_deal", {
48
+ title: "Update Deal",
49
+ description: `Update or create a deal in the customer's pipeline. Upserts by deal name.
50
+ Use after pipeline discussions or when deal status changes.
51
+
52
+ Args:
53
+ slug: Customer ID
54
+ dealName: Deal name (used as unique key for upsert)
55
+ stage: Deal stage ("lead" | "qualified" | "proposal" | "negotiation" | "won" | "lost")
56
+ value: Deal value in euros
57
+ probability: Win probability percentage (0-100)
58
+ closeDate: Expected close date (YYYY-MM-DD)
59
+ notes: Free-text notes about the deal
60
+
61
+ Returns: { success: boolean, deal: object }`,
62
+ inputSchema: z.object({
63
+ slug: z.string().describe("Customer slug (e.g. 'acme-corp')"),
64
+ dealName: z.string().describe("Deal name (unique identifier for upsert)"),
65
+ stage: z.enum([
66
+ "lead",
67
+ "qualified",
68
+ "proposal",
69
+ "negotiation",
70
+ "won",
71
+ "lost"
72
+ ]).optional().describe("Deal stage"),
73
+ value: z.number().optional().describe("Deal value in euros"),
74
+ probability: z.number().min(0).max(100).optional().describe("Win probability (0-100)"),
75
+ closeDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("Expected close date (YYYY-MM-DD)"),
76
+ notes: z.string().optional().describe("Free-text notes about the deal")
77
+ })
78
+ }, async ({ slug, dealName, stage, value, probability, closeDate, notes }) => handleUpdateDeal({
79
+ slug,
80
+ dealName,
81
+ ...stage !== void 0 ? { stage } : {},
82
+ ...value !== void 0 ? { value } : {},
83
+ ...probability !== void 0 ? { probability } : {},
84
+ ...closeDate !== void 0 ? { closeDate } : {},
85
+ ...notes !== void 0 ? { notes } : {}
86
+ }));
87
+ }
88
+ //#endregion
89
+ export { registerUpdateDeal as n, handleUpdateDeal as t };
90
+
91
+ //# sourceMappingURL=update-deal-DKC79skb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-deal-DKC79skb.js","names":[],"sources":["../src/mcp/tools/update-deal.ts"],"sourcesContent":["import { type McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { upsertDeal } from \"../../fs/pipeline-writer.js\";\nimport type { PipelineDeal } from \"../../schemas/pipeline.js\";\nimport { writeAuditEntry, getActor } from \"../../fs/audit-log.js\";\nimport { enforceRbac } from \"../../core/rbac.js\";\n\nconst DATA_DIR = process.env[\"DXCRM_DATA_DIR\"] ?? process.cwd();\n\nexport async function handleUpdateDeal(\n input: {\n slug: string;\n dealName: string;\n stage?: string;\n value?: number;\n probability?: number;\n closeDate?: string;\n notes?: string;\n },\n dataDir: string = DATA_DIR\n): Promise<{\n content: Array<{ type: \"text\"; text: string }>;\n}> {\n const today = new Date().toISOString().split(\"T\")[0] as string;\n\n const deal: PipelineDeal = {\n name: input.dealName,\n stage: (input.stage as PipelineDeal[\"stage\"]) ?? \"lead\",\n currency: \"EUR\",\n updated: today,\n ...(input.value !== undefined ? { value: input.value } : {}),\n ...(input.probability !== undefined ? { probability: input.probability } : {}),\n ...(input.closeDate !== undefined ? { close_date: input.closeDate } : {}),\n ...(input.notes !== undefined ? { notes: input.notes } : {}),\n };\n\n try {\n enforceRbac(dataDir, \"update_deal\");\n\n await upsertDeal(dataDir, input.slug, deal);\n\n writeAuditEntry(dataDir, {\n timestamp: new Date().toISOString(),\n actor: getActor(),\n tool: \"update_deal\",\n slug: input.slug,\n summary: input.dealName,\n });\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n deal,\n },\n null,\n 2\n ),\n },\n ],\n };\n } catch (err) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: false,\n error: (err as Error).message,\n },\n null,\n 2\n ),\n },\n ],\n };\n }\n}\n\nexport function registerUpdateDeal(server: McpServer): void {\n server.registerTool(\n \"update_deal\",\n {\n title: \"Update Deal\",\n description: `Update or create a deal in the customer's pipeline. Upserts by deal name.\nUse after pipeline discussions or when deal status changes.\n\nArgs:\n slug: Customer ID\n dealName: Deal name (used as unique key for upsert)\n stage: Deal stage (\"lead\" | \"qualified\" | \"proposal\" | \"negotiation\" | \"won\" | \"lost\")\n value: Deal value in euros\n probability: Win probability percentage (0-100)\n closeDate: Expected close date (YYYY-MM-DD)\n notes: Free-text notes about the deal\n\nReturns: { success: boolean, deal: object }`,\n inputSchema: z.object({\n slug: z.string().describe(\"Customer slug (e.g. 'acme-corp')\"),\n dealName: z.string().describe(\"Deal name (unique identifier for upsert)\"),\n stage: z\n .enum([\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"])\n .optional()\n .describe(\"Deal stage\"),\n value: z.number().optional().describe(\"Deal value in euros\"),\n probability: z.number().min(0).max(100).optional().describe(\"Win probability (0-100)\"),\n closeDate: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n .optional()\n .describe(\"Expected close date (YYYY-MM-DD)\"),\n notes: z.string().optional().describe(\"Free-text notes about the deal\"),\n }),\n },\n async ({ slug, dealName, stage, value, probability, closeDate, notes }) =>\n handleUpdateDeal({\n slug,\n dealName,\n ...(stage !== undefined ? { stage } : {}),\n ...(value !== undefined ? { value } : {}),\n ...(probability !== undefined ? { probability } : {}),\n ...(closeDate !== undefined ? { closeDate } : {}),\n ...(notes !== undefined ? { notes } : {}),\n })\n );\n}\n"],"mappings":";;;;;AAOA,MAAM,WAAW,QAAQ,IAAI,qBAAqB,QAAQ,IAAI;AAE9D,eAAsB,iBACpB,OASA,UAAkB,UAGjB;CACD,MAAM,yBAAQ,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;CAElD,MAAM,OAAqB;EACzB,MAAM,MAAM;EACZ,OAAQ,MAAM,SAAmC;EACjD,UAAU;EACV,SAAS;EACT,GAAI,MAAM,UAAU,KAAA,IAAY,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;EAC1D,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;EAC5E,GAAI,MAAM,cAAc,KAAA,IAAY,EAAE,YAAY,MAAM,UAAU,IAAI,CAAC;EACvE,GAAI,MAAM,UAAU,KAAA,IAAY,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;CAC5D;CAEA,IAAI;EACF,YAAY,SAAS,aAAa;EAElC,MAAM,WAAW,SAAS,MAAM,MAAM,IAAI;EAE1C,gBAAgB,SAAS;GACvB,4BAAW,IAAI,KAAK,GAAE,YAAY;GAClC,OAAO,SAAS;GAChB,MAAM;GACN,MAAM,MAAM;GACZ,SAAS,MAAM;EACjB,CAAC;EAED,OAAO,EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM,KAAK,UACT;IACE,SAAS;IACT;GACF,GACA,MACA,CACF;EACF,CACF,EACF;CACF,SAAS,KAAK;EACZ,OAAO,EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM,KAAK,UACT;IACE,SAAS;IACT,OAAQ,IAAc;GACxB,GACA,MACA,CACF;EACF,CACF,EACF;CACF;AACF;AAEA,SAAgB,mBAAmB,QAAyB;CAC1D,OAAO,aACL,eACA;EACE,OAAO;EACP,aAAa;;;;;;;;;;;;;EAab,aAAa,EAAE,OAAO;GACpB,MAAM,EAAE,OAAO,EAAE,SAAS,kCAAkC;GAC5D,UAAU,EAAE,OAAO,EAAE,SAAS,0CAA0C;GACxE,OAAO,EACJ,KAAK;IAAC;IAAQ;IAAa;IAAY;IAAe;IAAO;GAAM,CAAC,EACpE,SAAS,EACT,SAAS,YAAY;GACxB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qBAAqB;GAC3D,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,yBAAyB;GACrF,WAAW,EACR,OAAO,EACP,MAAM,qBAAqB,EAC3B,SAAS,EACT,SAAS,kCAAkC;GAC9C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gCAAgC;EACxE,CAAC;CACH,GACA,OAAO,EAAE,MAAM,UAAU,OAAO,OAAO,aAAa,WAAW,YAC7D,iBAAiB;EACf;EACA;EACA,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;EACvC,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;EACvC,GAAI,gBAAgB,KAAA,IAAY,EAAE,YAAY,IAAI,CAAC;EACnD,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;EAC/C,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;CACzC,CAAC,CACL;AACF"}