@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,14 @@
1
+ import { z } from "zod";
2
+ //#region src/schemas/agent-config.ts
3
+ const AgentConfigSchema = z.object({
4
+ slug: z.string().min(1),
5
+ channel: z.enum(["telegram"]),
6
+ wakeOn: z.array(z.enum(["email", "calendar"])).default(["email"]),
7
+ createdAt: z.string(),
8
+ lastWake: z.string().nullable().default(null),
9
+ telegramChatId: z.string().optional()
10
+ });
11
+ //#endregion
12
+ export { AgentConfigSchema as t };
13
+
14
+ //# sourceMappingURL=agent-config-zPvcqu07.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-config-zPvcqu07.js","names":[],"sources":["../src/schemas/agent-config.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const AgentConfigSchema = z.object({\n slug: z.string().min(1),\n channel: z.enum([\"telegram\"]),\n wakeOn: z.array(z.enum([\"email\", \"calendar\"])).default([\"email\"]),\n createdAt: z.string(),\n lastWake: z.string().nullable().default(null),\n telegramChatId: z.string().optional(),\n});\n\nexport type AgentConfig = z.infer<typeof AgentConfigSchema>;\n"],"mappings":";;AAEA,MAAa,oBAAoB,EAAE,OAAO;CACxC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;CACtB,SAAS,EAAE,KAAK,CAAC,UAAU,CAAC;CAC5B,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,SAAS,UAAU,CAAC,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;CAChE,WAAW,EAAE,OAAO;CACpB,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,IAAI;CAC5C,gBAAgB,EAAE,OAAO,EAAE,SAAS;AACtC,CAAC"}
@@ -0,0 +1,67 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import "crypto";
4
+ //#region src/core/approvals.ts
5
+ function policyPath(dataDir) {
6
+ return path.join(dataDir, ".agentic", "policy.json");
7
+ }
8
+ function approvalsPath(dataDir) {
9
+ return path.join(dataDir, ".agentic", "approvals.json");
10
+ }
11
+ function loadPolicyConfig(dataDir) {
12
+ const p = policyPath(dataDir);
13
+ if (!fs.existsSync(p)) return {};
14
+ try {
15
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
16
+ } catch {
17
+ return {};
18
+ }
19
+ }
20
+ function setPolicy(dataDir, tool, policy, slug) {
21
+ const cfg = loadPolicyConfig(dataDir);
22
+ if (slug) {
23
+ cfg.customers = cfg.customers ?? {};
24
+ cfg.customers[slug] = {
25
+ ...cfg.customers[slug] ?? {},
26
+ [tool]: policy
27
+ };
28
+ } else cfg.tools = {
29
+ ...cfg.tools ?? {},
30
+ [tool]: policy
31
+ };
32
+ const p = policyPath(dataDir);
33
+ fs.mkdirSync(path.dirname(p), { recursive: true });
34
+ fs.writeFileSync(p, JSON.stringify(cfg, null, 2), "utf-8");
35
+ }
36
+ function listApprovals(dataDir, status) {
37
+ const p = approvalsPath(dataDir);
38
+ if (!fs.existsSync(p)) return [];
39
+ try {
40
+ const all = JSON.parse(fs.readFileSync(p, "utf-8")).approvals;
41
+ const list = Array.isArray(all) ? all : [];
42
+ return status ? list.filter((a) => a.status === status) : list;
43
+ } catch {
44
+ return [];
45
+ }
46
+ }
47
+ function writeApprovals(dataDir, approvals) {
48
+ const p = approvalsPath(dataDir);
49
+ fs.mkdirSync(path.dirname(p), { recursive: true });
50
+ fs.writeFileSync(p, JSON.stringify({ approvals }, null, 2), "utf-8");
51
+ }
52
+ function decideApproval(dataDir, id, decision) {
53
+ const all = listApprovals(dataDir);
54
+ const idx = all.findIndex((a) => a.id === id);
55
+ if (idx < 0) return false;
56
+ all[idx] = {
57
+ ...all[idx],
58
+ status: decision,
59
+ decidedAt: (/* @__PURE__ */ new Date()).toISOString()
60
+ };
61
+ writeApprovals(dataDir, all);
62
+ return true;
63
+ }
64
+ //#endregion
65
+ export { decideApproval, listApprovals, setPolicy };
66
+
67
+ //# sourceMappingURL=approvals-DpjxGHFp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals-DpjxGHFp.js","names":[],"sources":["../src/core/approvals.ts"],"sourcesContent":["import { randomBytes } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Human-in-the-loop / approval gate (domino D4 / F3). A per-tool, per-customer\n * autonomy policy (auto | approve | block) decides whether an agent action runs\n * immediately, is queued for human approval, or is blocked. The generic gate\n * wraps any write/automation action so later features get HITL for free.\n */\nexport type Policy = \"auto\" | \"approve\" | \"block\";\n\ninterface PolicyConfig {\n default?: Policy;\n tools?: Record<string, Policy>;\n customers?: Record<string, Record<string, Policy>>;\n}\n\nexport interface Approval {\n id: string;\n tool: string;\n slug?: string;\n payload: unknown;\n status: \"pending\" | \"approved\" | \"rejected\";\n requestedAt: string;\n decidedAt?: string;\n}\n\nfunction policyPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"policy.json\");\n}\nfunction approvalsPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"approvals.json\");\n}\n\nfunction loadPolicyConfig(dataDir: string): PolicyConfig {\n const p = policyPath(dataDir);\n if (!fs.existsSync(p)) return {};\n try {\n return JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as PolicyConfig;\n } catch {\n return {};\n }\n}\n\n/** Resolve the effective policy: customer-specific → global tool → default (auto). */\nexport function getPolicy(dataDir: string, tool: string, slug?: string): Policy {\n const cfg = loadPolicyConfig(dataDir);\n if (slug && cfg.customers?.[slug]?.[tool]) return cfg.customers[slug]![tool]!;\n if (cfg.tools?.[tool]) return cfg.tools[tool]!;\n return cfg.default ?? \"auto\";\n}\n\nexport function setPolicy(dataDir: string, tool: string, policy: Policy, slug?: string): void {\n const cfg = loadPolicyConfig(dataDir);\n if (slug) {\n cfg.customers = cfg.customers ?? {};\n cfg.customers[slug] = { ...(cfg.customers[slug] ?? {}), [tool]: policy };\n } else {\n cfg.tools = { ...(cfg.tools ?? {}), [tool]: policy };\n }\n const p = policyPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify(cfg, null, 2), \"utf-8\");\n}\n\nexport function listApprovals(dataDir: string, status?: Approval[\"status\"]): Approval[] {\n const p = approvalsPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const all = (JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as { approvals?: Approval[] })\n .approvals;\n const list = Array.isArray(all) ? all : [];\n return status ? list.filter((a) => a.status === status) : list;\n } catch {\n return [];\n }\n}\n\nfunction writeApprovals(dataDir: string, approvals: Approval[]): void {\n const p = approvalsPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ approvals }, null, 2), \"utf-8\");\n}\n\nexport function requestApproval(\n dataDir: string,\n req: { tool: string; slug?: string; payload: unknown }\n): Approval {\n const approval: Approval = {\n id: `apr_${randomBytes(5).toString(\"hex\")}`,\n tool: req.tool,\n ...(req.slug ? { slug: req.slug } : {}),\n payload: req.payload,\n status: \"pending\",\n requestedAt: new Date().toISOString(),\n };\n writeApprovals(dataDir, [...listApprovals(dataDir), approval]);\n return approval;\n}\n\nexport function decideApproval(\n dataDir: string,\n id: string,\n decision: \"approved\" | \"rejected\"\n): boolean {\n const all = listApprovals(dataDir);\n const idx = all.findIndex((a) => a.id === id);\n if (idx < 0) return false;\n all[idx] = { ...all[idx]!, status: decision, decidedAt: new Date().toISOString() };\n writeApprovals(dataDir, all);\n return true;\n}\n\nexport interface GateResult<T> {\n status: \"executed\" | \"pending\" | \"blocked\";\n result?: T;\n approvalId?: string;\n}\n\n/** Gate an action by the effective policy: run, queue for approval, or block. */\nexport async function gateAction<T>(\n dataDir: string,\n action: { tool: string; slug?: string; payload: unknown },\n execute: () => T | Promise<T>\n): Promise<GateResult<T>> {\n const policy = getPolicy(dataDir, action.tool, action.slug);\n if (policy === \"block\") return { status: \"blocked\" };\n if (policy === \"approve\") {\n const approval = requestApproval(dataDir, action);\n return { status: \"pending\", approvalId: approval.id };\n }\n return { status: \"executed\", result: await execute() };\n}\n"],"mappings":";;;;AA4BA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,aAAa;AACrD;AACA,SAAS,cAAc,SAAyB;CAC9C,OAAO,KAAK,KAAK,SAAS,YAAY,gBAAgB;AACxD;AAEA,SAAS,iBAAiB,SAA+B;CACvD,MAAM,IAAI,WAAW,OAAO;CAC5B,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,UAAU,SAAiB,MAAc,QAAgB,MAAqB;CAC5F,MAAM,MAAM,iBAAiB,OAAO;CACpC,IAAI,MAAM;EACR,IAAI,YAAY,IAAI,aAAa,CAAC;EAClC,IAAI,UAAU,QAAQ;GAAE,GAAI,IAAI,UAAU,SAAS,CAAC;IAAK,OAAO;EAAO;CACzE,OACE,IAAI,QAAQ;EAAE,GAAI,IAAI,SAAS,CAAC;GAAK,OAAO;CAAO;CAErD,MAAM,IAAI,WAAW,OAAO;CAC5B,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,GAAG,OAAO;AAC3D;AAEA,SAAgB,cAAc,SAAiB,QAAyC;CACtF,MAAM,IAAI,cAAc,OAAO;CAC/B,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,MAAM,MAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW,EAC1D;EACH,MAAM,OAAO,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC;EACzC,OAAO,SAAS,KAAK,QAAQ,MAAM,EAAE,WAAW,MAAM,IAAI;CAC5D,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAS,eAAe,SAAiB,WAA6B;CACpE,MAAM,IAAI,cAAc,OAAO;CAC/B,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,EAAE,UAAU,GAAG,MAAM,CAAC,GAAG,OAAO;AACrE;AAkBA,SAAgB,eACd,SACA,IACA,UACS;CACT,MAAM,MAAM,cAAc,OAAO;CACjC,MAAM,MAAM,IAAI,WAAW,MAAM,EAAE,OAAO,EAAE;CAC5C,IAAI,MAAM,GAAG,OAAO;CACpB,IAAI,OAAO;EAAE,GAAG,IAAI;EAAO,QAAQ;EAAU,4BAAW,IAAI,KAAK,GAAE,YAAY;CAAE;CACjF,eAAe,SAAS,GAAG;CAC3B,OAAO;AACT"}
@@ -0,0 +1,52 @@
1
+ import { r as readInteractions } from "./interactions-writer-SLHnoEeE.js";
2
+ import { t as readPipeline } from "./pipeline-writer-BvVquKIe.js";
3
+ import { t as hybridSearch } from "./hybrid-search-BmHttLrR.js";
4
+ import { n as loadMemories } from "./memory-Bb6ky3kb.js";
5
+ import { r as loadSops } from "./sop-Vp0UPWFW.js";
6
+ //#region src/core/ask.ts
7
+ async function gatherCorpus(dataDir, slug) {
8
+ const docs = [];
9
+ for (const m of loadMemories(dataDir, slug)) docs.push({
10
+ id: `mem:${m.id}`,
11
+ text: m.text
12
+ });
13
+ for (const s of loadSops(dataDir, slug)) docs.push({
14
+ id: `sop:${s.id}`,
15
+ text: `${s.title} ${s.triggers.join(" ")} ${s.body}`
16
+ });
17
+ if (slug) {
18
+ (await readInteractions(dataDir, slug).catch(() => "")).split(/(?=^## )/m).map((e) => e.trim()).filter((e) => e && !e.startsWith("# ")).forEach((e, i) => docs.push({
19
+ id: `int:${slug}:${i}`,
20
+ text: e
21
+ }));
22
+ const deals = await readPipeline(dataDir, slug).catch(() => []);
23
+ for (const d of deals) docs.push({
24
+ id: `deal:${d.name}`,
25
+ text: `${d.name} stage ${d.stage} value ${d.value ?? ""} ${d.notes ?? ""}`
26
+ });
27
+ }
28
+ return docs;
29
+ }
30
+ async function askCrm(dataDir, question, slug) {
31
+ const corpus = await gatherCorpus(dataDir, slug);
32
+ const ranked = hybridSearch(question, corpus, { limit: 6 });
33
+ const byId = new Map(corpus.map((d) => [d.id, d]));
34
+ const sources = ranked.map((r) => byId.get(r.id)).filter(Boolean);
35
+ if (sources.length === 0) return { sources: [] };
36
+ try {
37
+ const { callLlm } = await import("./llm-DEjWcqmW.js");
38
+ return {
39
+ answer: await callLlm(`Answer the question using ONLY the context. Cite snippet numbers. If unknown, say so.\n\nQuestion: ${question}\n\nContext:\n${sources.map((s, i) => `[${i + 1}] ${s.text}`).join("\n")}`, {
40
+ tool: "ask_crm",
41
+ ...slug ? { slug } : {}
42
+ }),
43
+ sources
44
+ };
45
+ } catch {
46
+ return { sources };
47
+ }
48
+ }
49
+ //#endregion
50
+ export { askCrm };
51
+
52
+ //# sourceMappingURL=ask-CID3jnuL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask-CID3jnuL.js","names":[],"sources":["../src/core/ask.ts"],"sourcesContent":["import { hybridSearch, type HybridDoc } from \"./hybrid-search.js\";\nimport { loadMemories } from \"./memory.js\";\nimport { loadSops } from \"./sop.js\";\nimport { readInteractions } from \"../fs/interactions-writer.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\n\n/**\n * Ask-your-CRM (domino D10 / C2): natural-language Q&A over CRM data. Gathers a\n * corpus (interactions, pipeline, memories, SOPs), retrieves relevant snippets\n * via hybrid search, and — when an LLM is available — synthesizes a grounded\n * answer. Without an LLM it returns the ranked sources (still useful).\n */\nexport interface AskResult {\n answer?: string;\n sources: Array<{ id: string; text: string }>;\n}\n\nexport async function gatherCorpus(dataDir: string, slug?: string): Promise<HybridDoc[]> {\n const docs: HybridDoc[] = [];\n\n for (const m of loadMemories(dataDir, slug)) docs.push({ id: `mem:${m.id}`, text: m.text });\n for (const s of loadSops(dataDir, slug))\n docs.push({ id: `sop:${s.id}`, text: `${s.title} ${s.triggers.join(\" \")} ${s.body}` });\n\n if (slug) {\n const interactions = await readInteractions(dataDir, slug).catch(() => \"\");\n interactions\n .split(/(?=^## )/m)\n .map((e) => e.trim())\n .filter((e) => e && !e.startsWith(\"# \"))\n .forEach((e, i) => docs.push({ id: `int:${slug}:${i}`, text: e }));\n\n const deals = await readPipeline(dataDir, slug).catch(() => []);\n for (const d of deals)\n docs.push({\n id: `deal:${d.name}`,\n text: `${d.name} stage ${d.stage} value ${d.value ?? \"\"} ${d.notes ?? \"\"}`,\n });\n }\n\n return docs;\n}\n\nexport async function askCrm(dataDir: string, question: string, slug?: string): Promise<AskResult> {\n const corpus = await gatherCorpus(dataDir, slug);\n const ranked = hybridSearch(question, corpus, { limit: 6 });\n const byId = new Map(corpus.map((d) => [d.id, d]));\n const sources = ranked.map((r) => byId.get(r.id)!).filter(Boolean);\n\n if (sources.length === 0) return { sources: [] };\n\n try {\n const { callLlm } = await import(\"./llm.js\");\n const context = sources.map((s, i) => `[${i + 1}] ${s.text}`).join(\"\\n\");\n const answer = await callLlm(\n `Answer the question using ONLY the context. Cite snippet numbers. If unknown, say so.\\n\\n` +\n `Question: ${question}\\n\\nContext:\\n${context}`,\n { tool: \"ask_crm\", ...(slug ? { slug } : {}) }\n );\n return { answer, sources };\n } catch {\n return { sources };\n }\n}\n"],"mappings":";;;;;;AAiBA,eAAsB,aAAa,SAAiB,MAAqC;CACvF,MAAM,OAAoB,CAAC;CAE3B,KAAK,MAAM,KAAK,aAAa,SAAS,IAAI,GAAG,KAAK,KAAK;EAAE,IAAI,OAAO,EAAE;EAAM,MAAM,EAAE;CAAK,CAAC;CAC1F,KAAK,MAAM,KAAK,SAAS,SAAS,IAAI,GACpC,KAAK,KAAK;EAAE,IAAI,OAAO,EAAE;EAAM,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,KAAK,GAAG,EAAE,GAAG,EAAE;CAAO,CAAC;CAEvF,IAAI,MAAM;EAER,CAAA,MAD2B,iBAAiB,SAAS,IAAI,EAAE,YAAY,EAAE,GAEtE,MAAM,WAAW,EACjB,KAAK,MAAM,EAAE,KAAK,CAAC,EACnB,QAAQ,MAAM,KAAK,CAAC,EAAE,WAAW,IAAI,CAAC,EACtC,SAAS,GAAG,MAAM,KAAK,KAAK;GAAE,IAAI,OAAO,KAAK,GAAG;GAAK,MAAM;EAAE,CAAC,CAAC;EAEnE,MAAM,QAAQ,MAAM,aAAa,SAAS,IAAI,EAAE,YAAY,CAAC,CAAC;EAC9D,KAAK,MAAM,KAAK,OACd,KAAK,KAAK;GACR,IAAI,QAAQ,EAAE;GACd,MAAM,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,EAAE,SAAS,GAAG,GAAG,EAAE,SAAS;EACxE,CAAC;CACL;CAEA,OAAO;AACT;AAEA,eAAsB,OAAO,SAAiB,UAAkB,MAAmC;CACjG,MAAM,SAAS,MAAM,aAAa,SAAS,IAAI;CAC/C,MAAM,SAAS,aAAa,UAAU,QAAQ,EAAE,OAAO,EAAE,CAAC;CAC1D,MAAM,OAAO,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CACjD,MAAM,UAAU,OAAO,KAAK,MAAM,KAAK,IAAI,EAAE,EAAE,CAAE,EAAE,OAAO,OAAO;CAEjE,IAAI,QAAQ,WAAW,GAAG,OAAO,EAAE,SAAS,CAAC,EAAE;CAE/C,IAAI;EACF,MAAM,EAAE,YAAY,MAAM,OAAO;EAOjC,OAAO;GAAE,QAAA,MALY,QACnB,sGACe,SAAS,gBAHV,QAAQ,KAAK,GAAG,MAAM,IAAI,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,IAGnB,KAC9C;IAAE,MAAM;IAAW,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;GAAG,CAC/C;GACiB;EAAQ;CAC3B,QAAQ;EACN,OAAO,EAAE,QAAQ;CACnB;AACF"}
@@ -0,0 +1,49 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ //#region src/fs/audit-log.ts
4
+ const AUDIT_LOG_PATH = ".agentic/audit.log";
5
+ function getActor() {
6
+ const actor = process.env["DXCRM_ACTOR"];
7
+ return actor && actor.trim().length > 0 ? actor.trim() : "system";
8
+ }
9
+ function writeAuditEntry(dataDir, entry) {
10
+ const logPath = path.join(dataDir, AUDIT_LOG_PATH);
11
+ const logDir = path.dirname(logPath);
12
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
13
+ const truncatedSummary = entry.summary.slice(0, 120);
14
+ const line = `${entry.timestamp} | ${entry.actor} | ${entry.tool} | ${entry.slug} | ${truncatedSummary}\n`;
15
+ fs.appendFileSync(logPath, line, "utf-8");
16
+ }
17
+ function readAuditLog(dataDir) {
18
+ const logPath = path.join(dataDir, AUDIT_LOG_PATH);
19
+ if (!fs.existsSync(logPath)) return [];
20
+ const lines = fs.readFileSync(logPath, "utf-8").split("\n");
21
+ const entries = [];
22
+ for (const line of lines) {
23
+ const trimmed = line.trim();
24
+ if (!trimmed) continue;
25
+ const parts = trimmed.split(" | ");
26
+ if (parts.length < 5) continue;
27
+ const [timestamp, actor, tool, slug, ...summaryParts] = parts;
28
+ const summary = summaryParts.join(" | ");
29
+ if (timestamp && actor && tool && slug) entries.push({
30
+ timestamp,
31
+ actor,
32
+ tool,
33
+ slug,
34
+ summary: summary ?? ""
35
+ });
36
+ }
37
+ return entries;
38
+ }
39
+ function filterAuditLog(entries, opts) {
40
+ let filtered = entries;
41
+ if (opts.slug !== void 0) filtered = filtered.filter((e) => e.slug === opts.slug);
42
+ if (opts.actor !== void 0) filtered = filtered.filter((e) => e.actor === opts.actor);
43
+ if (opts.limit !== void 0) filtered = filtered.slice(-opts.limit);
44
+ return filtered;
45
+ }
46
+ //#endregion
47
+ export { writeAuditEntry as i, getActor as n, readAuditLog as r, filterAuditLog as t };
48
+
49
+ //# sourceMappingURL=audit-log-DNMY9mUZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-log-DNMY9mUZ.js","names":[],"sources":["../src/fs/audit-log.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport interface AuditEntry {\n timestamp: string; // ISO 8601\n actor: string; // DXCRM_ACTOR env var, or \"system\"\n tool: string; // \"log_interaction\" | \"update_deal\" | \"update_customer_facts\" | etc.\n slug: string; // customer slug\n summary: string; // short description (first 120 chars of summary/deal name)\n}\n\n// File format (one line per entry, append-only):\n// 2026-06-01T09:14:00Z | alice | log_interaction | acme-corp | Called about Q3 renewal...\n\nconst AUDIT_LOG_PATH = \".agentic/audit.log\";\n\nexport function getActor(): string {\n const actor = process.env[\"DXCRM_ACTOR\"];\n return actor && actor.trim().length > 0 ? actor.trim() : \"system\";\n}\n\nexport function writeAuditEntry(dataDir: string, entry: AuditEntry): void {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n const logDir = path.dirname(logPath);\n\n if (!fs.existsSync(logDir)) {\n fs.mkdirSync(logDir, { recursive: true });\n }\n\n const truncatedSummary = entry.summary.slice(0, 120);\n const line = `${entry.timestamp} | ${entry.actor} | ${entry.tool} | ${entry.slug} | ${truncatedSummary}\\n`;\n\n fs.appendFileSync(logPath, line, \"utf-8\");\n}\n\nexport function readAuditLog(dataDir: string): AuditEntry[] {\n const logPath = path.join(dataDir, AUDIT_LOG_PATH);\n\n if (!fs.existsSync(logPath)) {\n return [];\n }\n\n const content = fs.readFileSync(logPath, \"utf-8\") as string;\n const lines = content.split(\"\\n\");\n\n const entries: AuditEntry[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n const parts = trimmed.split(\" | \");\n if (parts.length < 5) continue;\n\n const [timestamp, actor, tool, slug, ...summaryParts] = parts;\n const summary = summaryParts.join(\" | \");\n\n if (timestamp && actor && tool && slug) {\n entries.push({ timestamp, actor, tool, slug, summary: summary ?? \"\" });\n }\n }\n\n return entries;\n}\n\nexport function filterAuditLog(\n entries: AuditEntry[],\n opts: { slug?: string; actor?: string; limit?: number }\n): AuditEntry[] {\n let filtered = entries;\n\n if (opts.slug !== undefined) {\n filtered = filtered.filter((e) => e.slug === opts.slug);\n }\n\n if (opts.actor !== undefined) {\n filtered = filtered.filter((e) => e.actor === opts.actor);\n }\n\n if (opts.limit !== undefined) {\n filtered = filtered.slice(-opts.limit);\n }\n\n return filtered;\n}\n"],"mappings":";;;AAcA,MAAM,iBAAiB;AAEvB,SAAgB,WAAmB;CACjC,MAAM,QAAQ,QAAQ,IAAI;CAC1B,OAAO,SAAS,MAAM,KAAK,EAAE,SAAS,IAAI,MAAM,KAAK,IAAI;AAC3D;AAEA,SAAgB,gBAAgB,SAAiB,OAAyB;CACxE,MAAM,UAAU,KAAK,KAAK,SAAS,cAAc;CACjD,MAAM,SAAS,KAAK,QAAQ,OAAO;CAEnC,IAAI,CAAC,GAAG,WAAW,MAAM,GACvB,GAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;CAG1C,MAAM,mBAAmB,MAAM,QAAQ,MAAM,GAAG,GAAG;CACnD,MAAM,OAAO,GAAG,MAAM,UAAU,KAAK,MAAM,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,KAAK,iBAAiB;CAEvG,GAAG,eAAe,SAAS,MAAM,OAAO;AAC1C;AAEA,SAAgB,aAAa,SAA+B;CAC1D,MAAM,UAAU,KAAK,KAAK,SAAS,cAAc;CAEjD,IAAI,CAAC,GAAG,WAAW,OAAO,GACxB,OAAO,CAAC;CAIV,MAAM,QADU,GAAG,aAAa,SAAS,OACrB,EAAE,MAAM,IAAI;CAEhC,MAAM,UAAwB,CAAC;CAC/B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,SAAS;EAEd,MAAM,QAAQ,QAAQ,MAAM,KAAK;EACjC,IAAI,MAAM,SAAS,GAAG;EAEtB,MAAM,CAAC,WAAW,OAAO,MAAM,MAAM,GAAG,gBAAgB;EACxD,MAAM,UAAU,aAAa,KAAK,KAAK;EAEvC,IAAI,aAAa,SAAS,QAAQ,MAChC,QAAQ,KAAK;GAAE;GAAW;GAAO;GAAM;GAAM,SAAS,WAAW;EAAG,CAAC;CAEzE;CAEA,OAAO;AACT;AAEA,SAAgB,eACd,SACA,MACc;CACd,IAAI,WAAW;CAEf,IAAI,KAAK,SAAS,KAAA,GAChB,WAAW,SAAS,QAAQ,MAAM,EAAE,SAAS,KAAK,IAAI;CAGxD,IAAI,KAAK,UAAU,KAAA,GACjB,WAAW,SAAS,QAAQ,MAAM,EAAE,UAAU,KAAK,KAAK;CAG1D,IAAI,KAAK,UAAU,KAAA,GACjB,WAAW,SAAS,MAAM,CAAC,KAAK,KAAK;CAGvC,OAAO;AACT"}
@@ -0,0 +1,2 @@
1
+ import { t as createMcpToken } from "./auth-DFWwWcYD.js";
2
+ export { createMcpToken };
@@ -0,0 +1,93 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { createHash, randomBytes, timingSafeEqual } from "crypto";
4
+ //#region src/mcp/auth.ts
5
+ function tokensPath(dataDir) {
6
+ return path.join(dataDir, ".agentic", "mcp-tokens.json");
7
+ }
8
+ /** SHA-256 hex of a token. Only hashes are ever persisted. */
9
+ function hashToken(token) {
10
+ return createHash("sha256").update(token).digest("hex");
11
+ }
12
+ function loadMcpTokens(dataDir) {
13
+ const p = tokensPath(dataDir);
14
+ if (!fs.existsSync(p)) return [];
15
+ try {
16
+ const data = JSON.parse(fs.readFileSync(p, "utf-8"));
17
+ return Array.isArray(data.tokens) ? data.tokens : [];
18
+ } catch {
19
+ return [];
20
+ }
21
+ }
22
+ /**
23
+ * Whether the HTTP MCP endpoint must require a bearer token.
24
+ * - `DXCRM_MCP_AUTH=required` forces auth on (even with no tokens yet).
25
+ * - `DXCRM_MCP_AUTH=off` forces it off.
26
+ * - Otherwise: on as soon as at least one token is configured (opt-in by
27
+ * provisioning a token; stays open for local/firewalled dev by default).
28
+ */
29
+ function isAuthRequired(dataDir) {
30
+ const mode = process.env["DXCRM_MCP_AUTH"];
31
+ if (mode === "required") return true;
32
+ if (mode === "off") return false;
33
+ return loadMcpTokens(dataDir).length > 0;
34
+ }
35
+ /** Validate an `Authorization: Bearer <token>` header against stored hashes. */
36
+ function verifyBearer(authHeader, dataDir) {
37
+ if (!authHeader || !authHeader.startsWith("Bearer ")) return { ok: false };
38
+ const token = authHeader.slice(7).trim();
39
+ if (!token) return { ok: false };
40
+ const candidate = hashToken(token);
41
+ const candidateBuf = Buffer.from(candidate, "hex");
42
+ for (const rec of loadMcpTokens(dataDir)) {
43
+ if (rec.hash.length !== candidate.length) continue;
44
+ let recBuf;
45
+ try {
46
+ recBuf = Buffer.from(rec.hash, "hex");
47
+ } catch {
48
+ continue;
49
+ }
50
+ if (recBuf.length === candidateBuf.length && timingSafeEqual(recBuf, candidateBuf)) return {
51
+ ok: true,
52
+ actor: rec.actor,
53
+ role: rec.role
54
+ };
55
+ }
56
+ return { ok: false };
57
+ }
58
+ /**
59
+ * Mint a new token: generates a random secret, persists only its hash mapped
60
+ * to an actor/role, and returns the plaintext ONCE (never stored).
61
+ */
62
+ function createMcpToken(dataDir, actor, role, label) {
63
+ const token = randomBytes(24).toString("base64url");
64
+ const records = loadMcpTokens(dataDir);
65
+ records.push({
66
+ hash: hashToken(token),
67
+ actor,
68
+ role,
69
+ ...label ? { label } : {},
70
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
71
+ });
72
+ const p = tokensPath(dataDir);
73
+ fs.mkdirSync(path.dirname(p), { recursive: true });
74
+ fs.writeFileSync(p, JSON.stringify({ tokens: records }, null, 2), "utf-8");
75
+ return token;
76
+ }
77
+ /** RFC 9728 OAuth 2.0 Protected Resource Metadata document. */
78
+ function protectedResourceMetadata(resourceUrl) {
79
+ return {
80
+ resource: resourceUrl,
81
+ authorization_servers: [],
82
+ bearer_methods_supported: ["header"],
83
+ scopes_supported: ["crm:read", "crm:write"]
84
+ };
85
+ }
86
+ /** Value for the `WWW-Authenticate` header on a 401 (RFC 9728 §5.1). */
87
+ function wwwAuthenticateHeader(metadataUrl) {
88
+ return `Bearer resource_metadata="${metadataUrl}"`;
89
+ }
90
+ //#endregion
91
+ export { protectedResourceMetadata as a, loadMcpTokens as i, hashToken as n, verifyBearer as o, isAuthRequired as r, wwwAuthenticateHeader as s, createMcpToken as t };
92
+
93
+ //# sourceMappingURL=auth-DFWwWcYD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-DFWwWcYD.js","names":[],"sources":["../src/mcp/auth.ts"],"sourcesContent":["import { createHash, randomBytes, timingSafeEqual } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\n\nexport type McpRole = \"admin\" | \"manager\" | \"rep\";\n\nexport interface McpTokenRecord {\n hash: string;\n actor: string;\n role: McpRole;\n label?: string;\n createdAt?: string;\n}\n\nexport interface AuthResult {\n ok: boolean;\n actor?: string;\n role?: McpRole;\n}\n\nfunction tokensPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"mcp-tokens.json\");\n}\n\n/** SHA-256 hex of a token. Only hashes are ever persisted. */\nexport function hashToken(token: string): string {\n return createHash(\"sha256\").update(token).digest(\"hex\");\n}\n\nexport function loadMcpTokens(dataDir: string): McpTokenRecord[] {\n const p = tokensPath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as { tokens?: McpTokenRecord[] };\n return Array.isArray(data.tokens) ? data.tokens : [];\n } catch {\n return [];\n }\n}\n\n/**\n * Whether the HTTP MCP endpoint must require a bearer token.\n * - `DXCRM_MCP_AUTH=required` forces auth on (even with no tokens yet).\n * - `DXCRM_MCP_AUTH=off` forces it off.\n * - Otherwise: on as soon as at least one token is configured (opt-in by\n * provisioning a token; stays open for local/firewalled dev by default).\n */\nexport function isAuthRequired(dataDir: string): boolean {\n const mode = process.env[\"DXCRM_MCP_AUTH\"];\n if (mode === \"required\") return true;\n if (mode === \"off\") return false;\n return loadMcpTokens(dataDir).length > 0;\n}\n\n/** Validate an `Authorization: Bearer <token>` header against stored hashes. */\nexport function verifyBearer(authHeader: string | undefined, dataDir: string): AuthResult {\n if (!authHeader || !authHeader.startsWith(\"Bearer \")) return { ok: false };\n const token = authHeader.slice(\"Bearer \".length).trim();\n if (!token) return { ok: false };\n\n const candidate = hashToken(token);\n const candidateBuf = Buffer.from(candidate, \"hex\");\n for (const rec of loadMcpTokens(dataDir)) {\n if (rec.hash.length !== candidate.length) continue;\n let recBuf: Buffer;\n try {\n recBuf = Buffer.from(rec.hash, \"hex\");\n } catch {\n continue;\n }\n if (recBuf.length === candidateBuf.length && timingSafeEqual(recBuf, candidateBuf)) {\n return { ok: true, actor: rec.actor, role: rec.role };\n }\n }\n return { ok: false };\n}\n\n/**\n * Mint a new token: generates a random secret, persists only its hash mapped\n * to an actor/role, and returns the plaintext ONCE (never stored).\n */\nexport function createMcpToken(\n dataDir: string,\n actor: string,\n role: McpRole,\n label?: string\n): string {\n const token = randomBytes(24).toString(\"base64url\");\n const records = loadMcpTokens(dataDir);\n records.push({\n hash: hashToken(token),\n actor,\n role,\n ...(label ? { label } : {}),\n createdAt: new Date().toISOString(),\n });\n const p = tokensPath(dataDir);\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ tokens: records }, null, 2), \"utf-8\");\n return token;\n}\n\n/** RFC 9728 OAuth 2.0 Protected Resource Metadata document. */\nexport function protectedResourceMetadata(resourceUrl: string): Record<string, unknown> {\n return {\n resource: resourceUrl,\n // Self-hosted default: tokens are provisioned out-of-band (createMcpToken).\n // Populate with an external Authorization Server to enable full OAuth flows.\n authorization_servers: [] as string[],\n bearer_methods_supported: [\"header\"],\n scopes_supported: [\"crm:read\", \"crm:write\"],\n };\n}\n\n/** Value for the `WWW-Authenticate` header on a 401 (RFC 9728 §5.1). */\nexport function wwwAuthenticateHeader(metadataUrl: string): string {\n return `Bearer resource_metadata=\"${metadataUrl}\"`;\n}\n"],"mappings":";;;;AAoBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,iBAAiB;AACzD;;AAGA,SAAgB,UAAU,OAAuB;CAC/C,OAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;AAEA,SAAgB,cAAc,SAAmC;CAC/D,MAAM,IAAI,WAAW,OAAO;CAC5B,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC;CAC/B,IAAI;EACF,MAAM,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;EAC7D,OAAO,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC;CACrD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;;;;;;;AASA,SAAgB,eAAe,SAA0B;CACvD,MAAM,OAAO,QAAQ,IAAI;CACzB,IAAI,SAAS,YAAY,OAAO;CAChC,IAAI,SAAS,OAAO,OAAO;CAC3B,OAAO,cAAc,OAAO,EAAE,SAAS;AACzC;;AAGA,SAAgB,aAAa,YAAgC,SAA6B;CACxF,IAAI,CAAC,cAAc,CAAC,WAAW,WAAW,SAAS,GAAG,OAAO,EAAE,IAAI,MAAM;CACzE,MAAM,QAAQ,WAAW,MAAM,CAAgB,EAAE,KAAK;CACtD,IAAI,CAAC,OAAO,OAAO,EAAE,IAAI,MAAM;CAE/B,MAAM,YAAY,UAAU,KAAK;CACjC,MAAM,eAAe,OAAO,KAAK,WAAW,KAAK;CACjD,KAAK,MAAM,OAAO,cAAc,OAAO,GAAG;EACxC,IAAI,IAAI,KAAK,WAAW,UAAU,QAAQ;EAC1C,IAAI;EACJ,IAAI;GACF,SAAS,OAAO,KAAK,IAAI,MAAM,KAAK;EACtC,QAAQ;GACN;EACF;EACA,IAAI,OAAO,WAAW,aAAa,UAAU,gBAAgB,QAAQ,YAAY,GAC/E,OAAO;GAAE,IAAI;GAAM,OAAO,IAAI;GAAO,MAAM,IAAI;EAAK;CAExD;CACA,OAAO,EAAE,IAAI,MAAM;AACrB;;;;;AAMA,SAAgB,eACd,SACA,OACA,MACA,OACQ;CACR,MAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,WAAW;CAClD,MAAM,UAAU,cAAc,OAAO;CACrC,QAAQ,KAAK;EACX,MAAM,UAAU,KAAK;EACrB;EACA;EACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;EACzB,4BAAW,IAAI,KAAK,GAAE,YAAY;CACpC,CAAC;CACD,MAAM,IAAI,WAAW,OAAO;CAC5B,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,EAAE,QAAQ,QAAQ,GAAG,MAAM,CAAC,GAAG,OAAO;CACzE,OAAO;AACT;;AAGA,SAAgB,0BAA0B,aAA8C;CACtF,OAAO;EACL,UAAU;EAGV,uBAAuB,CAAC;EACxB,0BAA0B,CAAC,QAAQ;EACnC,kBAAkB,CAAC,YAAY,WAAW;CAC5C;AACF;;AAGA,SAAgB,sBAAsB,aAA6B;CACjE,OAAO,6BAA6B,YAAY;AAClD"}
@@ -0,0 +1,51 @@
1
+ //#region src/core/autofill.ts
2
+ const NEXT_STEP_RE = /^(?:-?\s*\[ \]|next step|todo|follow.?up|action item)\b[:\-\s]*/i;
3
+ const OBJECTION_RE = /\b(concern|worried|too expensive|expensive|hesitant|however|push back)\b/i;
4
+ function detectStage(text) {
5
+ const t = text.toLowerCase();
6
+ if (t.includes("closed won") || t.includes("signed")) return "won";
7
+ if (t.includes("closed lost") || t.includes("lost the deal")) return "lost";
8
+ if (t.includes("negotiat")) return "negotiation";
9
+ if (t.includes("proposal") || t.includes("quote")) return "proposal";
10
+ if (t.includes("qualif")) return "qualified";
11
+ }
12
+ function heuristicExtract(transcript) {
13
+ const lines = transcript.split("\n").map((l) => l.trim()).filter(Boolean);
14
+ const nextSteps = lines.filter((l) => NEXT_STEP_RE.test(l)).map((l) => l.replace(NEXT_STEP_RE, "").trim()).filter(Boolean).slice(0, 10);
15
+ const objections = lines.filter((l) => OBJECTION_RE.test(l)).slice(0, 10);
16
+ const base = {
17
+ summary: transcript.slice(0, 400),
18
+ nextSteps,
19
+ objections
20
+ };
21
+ const stage = detectStage(transcript);
22
+ return stage === void 0 ? base : {
23
+ ...base,
24
+ stage
25
+ };
26
+ }
27
+ async function extractAutofill(transcript, ctx) {
28
+ try {
29
+ const { callLlm } = await import("./llm-DEjWcqmW.js");
30
+ const raw = await callLlm(`Extract CRM fields from this meeting transcript. Return ONLY JSON: { "summary": string, "nextSteps": string[], "objections": string[], "stage": "lead"|"qualified"|"proposal"|"negotiation"|"won"|"lost"|null }.\n\n${transcript}`, {
31
+ tool: "autofill",
32
+ ...ctx?.slug ? { slug: ctx.slug } : {}
33
+ });
34
+ const parsed = JSON.parse(raw);
35
+ const base = {
36
+ summary: parsed.summary ?? transcript.slice(0, 400),
37
+ nextSteps: Array.isArray(parsed.nextSteps) ? parsed.nextSteps : [],
38
+ objections: Array.isArray(parsed.objections) ? parsed.objections : []
39
+ };
40
+ return parsed.stage ? {
41
+ ...base,
42
+ stage: parsed.stage
43
+ } : base;
44
+ } catch {
45
+ return heuristicExtract(transcript);
46
+ }
47
+ }
48
+ //#endregion
49
+ export { extractAutofill };
50
+
51
+ //# sourceMappingURL=autofill-Di_-SP7t.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autofill-Di_-SP7t.js","names":[],"sources":["../src/core/autofill.ts"],"sourcesContent":["/**\n * Call/Meeting → CRM autofill (domino D9 / C1): extract structured fields from a\n * transcript so they can be written to the CRM (through the D4 approval gate).\n * Uses the LLM when available, with a deterministic heuristic fallback so it\n * always returns something useful and is testable offline.\n */\nexport type StageName = \"lead\" | \"qualified\" | \"proposal\" | \"negotiation\" | \"won\" | \"lost\";\n\nexport interface AutofillResult {\n summary: string;\n nextSteps: string[];\n objections: string[];\n stage?: StageName;\n}\n\nconst NEXT_STEP_RE = /^(?:-?\\s*\\[ \\]|next step|todo|follow.?up|action item)\\b[:\\-\\s]*/i;\nconst OBJECTION_RE = /\\b(concern|worried|too expensive|expensive|hesitant|however|push back)\\b/i;\n\nfunction detectStage(text: string): StageName | undefined {\n const t = text.toLowerCase();\n if (t.includes(\"closed won\") || t.includes(\"signed\")) return \"won\";\n if (t.includes(\"closed lost\") || t.includes(\"lost the deal\")) return \"lost\";\n if (t.includes(\"negotiat\")) return \"negotiation\";\n if (t.includes(\"proposal\") || t.includes(\"quote\")) return \"proposal\";\n if (t.includes(\"qualif\")) return \"qualified\";\n return undefined;\n}\n\nexport function heuristicExtract(transcript: string): AutofillResult {\n const lines = transcript\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter(Boolean);\n\n const nextSteps = lines\n .filter((l) => NEXT_STEP_RE.test(l))\n .map((l) => l.replace(NEXT_STEP_RE, \"\").trim())\n .filter(Boolean)\n .slice(0, 10);\n\n const objections = lines.filter((l) => OBJECTION_RE.test(l)).slice(0, 10);\n\n const base = { summary: transcript.slice(0, 400), nextSteps, objections };\n const stage = detectStage(transcript);\n return stage === undefined ? base : { ...base, stage };\n}\n\nexport async function extractAutofill(\n transcript: string,\n ctx?: { slug?: string }\n): Promise<AutofillResult> {\n try {\n const { callLlm } = await import(\"./llm.js\");\n const prompt =\n `Extract CRM fields from this meeting transcript. Return ONLY JSON: ` +\n `{ \"summary\": string, \"nextSteps\": string[], \"objections\": string[], ` +\n `\"stage\": \"lead\"|\"qualified\"|\"proposal\"|\"negotiation\"|\"won\"|\"lost\"|null }.\\n\\n${transcript}`;\n const raw = await callLlm(prompt, {\n tool: \"autofill\",\n ...(ctx?.slug ? { slug: ctx.slug } : {}),\n });\n const parsed = JSON.parse(raw) as Partial<AutofillResult> & { stage?: string | null };\n const base = {\n summary: parsed.summary ?? transcript.slice(0, 400),\n nextSteps: Array.isArray(parsed.nextSteps) ? parsed.nextSteps : [],\n objections: Array.isArray(parsed.objections) ? parsed.objections : [],\n };\n return parsed.stage ? { ...base, stage: parsed.stage as StageName } : base;\n } catch {\n return heuristicExtract(transcript);\n }\n}\n"],"mappings":";AAeA,MAAM,eAAe;AACrB,MAAM,eAAe;AAErB,SAAS,YAAY,MAAqC;CACxD,MAAM,IAAI,KAAK,YAAY;CAC3B,IAAI,EAAE,SAAS,YAAY,KAAK,EAAE,SAAS,QAAQ,GAAG,OAAO;CAC7D,IAAI,EAAE,SAAS,aAAa,KAAK,EAAE,SAAS,eAAe,GAAG,OAAO;CACrE,IAAI,EAAE,SAAS,UAAU,GAAG,OAAO;CACnC,IAAI,EAAE,SAAS,UAAU,KAAK,EAAE,SAAS,OAAO,GAAG,OAAO;CAC1D,IAAI,EAAE,SAAS,QAAQ,GAAG,OAAO;AAEnC;AAEA,SAAgB,iBAAiB,YAAoC;CACnE,MAAM,QAAQ,WACX,MAAM,IAAI,EACV,KAAK,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;CAEjB,MAAM,YAAY,MACf,QAAQ,MAAM,aAAa,KAAK,CAAC,CAAC,EAClC,KAAK,MAAM,EAAE,QAAQ,cAAc,EAAE,EAAE,KAAK,CAAC,EAC7C,OAAO,OAAO,EACd,MAAM,GAAG,EAAE;CAEd,MAAM,aAAa,MAAM,QAAQ,MAAM,aAAa,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,EAAE;CAExE,MAAM,OAAO;EAAE,SAAS,WAAW,MAAM,GAAG,GAAG;EAAG;EAAW;CAAW;CACxE,MAAM,QAAQ,YAAY,UAAU;CACpC,OAAO,UAAU,KAAA,IAAY,OAAO;EAAE,GAAG;EAAM;CAAM;AACvD;AAEA,eAAsB,gBACpB,YACA,KACyB;CACzB,IAAI;EACF,MAAM,EAAE,YAAY,MAAM,OAAO;EAKjC,MAAM,MAAM,MAAM,QAAQ,uNADwD,cAChD;GAChC,MAAM;GACN,GAAI,KAAK,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;EACxC,CAAC;EACD,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,MAAM,OAAO;GACX,SAAS,OAAO,WAAW,WAAW,MAAM,GAAG,GAAG;GAClD,WAAW,MAAM,QAAQ,OAAO,SAAS,IAAI,OAAO,YAAY,CAAC;GACjE,YAAY,MAAM,QAAQ,OAAO,UAAU,IAAI,OAAO,aAAa,CAAC;EACtE;EACA,OAAO,OAAO,QAAQ;GAAE,GAAG;GAAM,OAAO,OAAO;EAAmB,IAAI;CACxE,QAAQ;EACN,OAAO,iBAAiB,UAAU;CACpC;AACF"}