@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,58 @@
1
+ import { t as hybridSearch } from "./hybrid-search-BmHttLrR.js";
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import { randomBytes } from "crypto";
5
+ //#region src/core/memory.ts
6
+ function globalPath(dataDir) {
7
+ return path.join(dataDir, ".agentic", "memory", "global.json");
8
+ }
9
+ function customerPath(dataDir, slug) {
10
+ return path.join(dataDir, "customers", slug, "memory.json");
11
+ }
12
+ function readFile(p) {
13
+ if (!fs.existsSync(p)) return [];
14
+ try {
15
+ const data = JSON.parse(fs.readFileSync(p, "utf-8"));
16
+ return Array.isArray(data.memories) ? data.memories : [];
17
+ } catch {
18
+ return [];
19
+ }
20
+ }
21
+ function writeFile(p, memories) {
22
+ fs.mkdirSync(path.dirname(p), { recursive: true });
23
+ fs.writeFileSync(p, JSON.stringify({ memories }, null, 2), "utf-8");
24
+ }
25
+ function addMemory(dataDir, m) {
26
+ const entry = {
27
+ id: `mem_${randomBytes(5).toString("hex")}`,
28
+ scope: m.scope,
29
+ ...m.slug ? { slug: m.slug } : {},
30
+ type: m.type,
31
+ text: m.text,
32
+ ...typeof m.confidence === "number" ? { confidence: m.confidence } : {},
33
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
34
+ };
35
+ const p = m.scope === "global" ? globalPath(dataDir) : customerPath(dataDir, m.slug ?? "");
36
+ writeFile(p, [...readFile(p), entry]);
37
+ return entry;
38
+ }
39
+ /** Load memories: global always; plus the customer's own when a slug is given. */
40
+ function loadMemories(dataDir, slug) {
41
+ const global = readFile(globalPath(dataDir));
42
+ if (!slug) return global;
43
+ return [...global, ...readFile(customerPath(dataDir, slug))];
44
+ }
45
+ /** Search memories by relevance (hybrid keyword ranking over memory texts). */
46
+ async function searchMemory(dataDir, query, slug, limit = 5) {
47
+ const memories = loadMemories(dataDir, slug);
48
+ const ranked = hybridSearch(query, memories.map((m) => ({
49
+ id: m.id,
50
+ text: m.text
51
+ })), { limit });
52
+ const byId = new Map(memories.map((m) => [m.id, m]));
53
+ return ranked.map((r) => byId.get(r.id)).filter(Boolean);
54
+ }
55
+ //#endregion
56
+ export { loadMemories as n, searchMemory as r, addMemory as t };
57
+
58
+ //# sourceMappingURL=memory-Bb6ky3kb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-Bb6ky3kb.js","names":[],"sources":["../src/core/memory.ts"],"sourcesContent":["import { randomBytes } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport { hybridSearch } from \"./hybrid-search.js\";\n\n/**\n * Agent memory (domino D6 / F4): persistent, typed memories per customer and\n * global, retrievable via hybrid search. Injected into context so the agent\n * gets durably smarter across every interaction (CoALA: semantic/procedural).\n */\nexport type MemoryType = \"fact\" | \"preference\" | \"learning\" | \"instruction\";\n\nexport interface MemoryEntry {\n id: string;\n scope: \"global\" | \"customer\";\n slug?: string;\n type: MemoryType;\n text: string;\n confidence?: number;\n createdAt: string;\n}\n\nfunction globalPath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"memory\", \"global.json\");\n}\nfunction customerPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \"customers\", slug, \"memory.json\");\n}\n\nfunction readFile(p: string): MemoryEntry[] {\n if (!fs.existsSync(p)) return [];\n try {\n const data = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as { memories?: MemoryEntry[] };\n return Array.isArray(data.memories) ? data.memories : [];\n } catch {\n return [];\n }\n}\nfunction writeFile(p: string, memories: MemoryEntry[]): void {\n fs.mkdirSync(path.dirname(p), { recursive: true });\n fs.writeFileSync(p, JSON.stringify({ memories }, null, 2), \"utf-8\");\n}\n\nexport function addMemory(\n dataDir: string,\n m: {\n scope: \"global\" | \"customer\";\n slug?: string;\n type: MemoryType;\n text: string;\n confidence?: number;\n }\n): MemoryEntry {\n const entry: MemoryEntry = {\n id: `mem_${randomBytes(5).toString(\"hex\")}`,\n scope: m.scope,\n ...(m.slug ? { slug: m.slug } : {}),\n type: m.type,\n text: m.text,\n ...(typeof m.confidence === \"number\" ? { confidence: m.confidence } : {}),\n createdAt: new Date().toISOString(),\n };\n const p = m.scope === \"global\" ? globalPath(dataDir) : customerPath(dataDir, m.slug ?? \"\");\n writeFile(p, [...readFile(p), entry]);\n return entry;\n}\n\n/** Load memories: global always; plus the customer's own when a slug is given. */\nexport function loadMemories(dataDir: string, slug?: string): MemoryEntry[] {\n const global = readFile(globalPath(dataDir));\n if (!slug) return global;\n return [...global, ...readFile(customerPath(dataDir, slug))];\n}\n\n/** Search memories by relevance (hybrid keyword ranking over memory texts). */\nexport async function searchMemory(\n dataDir: string,\n query: string,\n slug?: string,\n limit = 5\n): Promise<MemoryEntry[]> {\n const memories = loadMemories(dataDir, slug);\n const docs = memories.map((m) => ({ id: m.id, text: m.text }));\n const ranked = hybridSearch(query, docs, { limit });\n const byId = new Map(memories.map((m) => [m.id, m]));\n return ranked.map((r) => byId.get(r.id)!).filter(Boolean);\n}\n"],"mappings":";;;;;AAsBA,SAAS,WAAW,SAAyB;CAC3C,OAAO,KAAK,KAAK,SAAS,YAAY,UAAU,aAAa;AAC/D;AACA,SAAS,aAAa,SAAiB,MAAsB;CAC3D,OAAO,KAAK,KAAK,SAAS,aAAa,MAAM,aAAa;AAC5D;AAEA,SAAS,SAAS,GAA0B;CAC1C,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,QAAQ,IAAI,KAAK,WAAW,CAAC;CACzD,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AACA,SAAS,UAAU,GAAW,UAA+B;CAC3D,GAAG,UAAU,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CACjD,GAAG,cAAc,GAAG,KAAK,UAAU,EAAE,SAAS,GAAG,MAAM,CAAC,GAAG,OAAO;AACpE;AAEA,SAAgB,UACd,SACA,GAOa;CACb,MAAM,QAAqB;EACzB,IAAI,OAAO,YAAY,CAAC,EAAE,SAAS,KAAK;EACxC,OAAO,EAAE;EACT,GAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;EACjC,MAAM,EAAE;EACR,MAAM,EAAE;EACR,GAAI,OAAO,EAAE,eAAe,WAAW,EAAE,YAAY,EAAE,WAAW,IAAI,CAAC;EACvE,4BAAW,IAAI,KAAK,GAAE,YAAY;CACpC;CACA,MAAM,IAAI,EAAE,UAAU,WAAW,WAAW,OAAO,IAAI,aAAa,SAAS,EAAE,QAAQ,EAAE;CACzF,UAAU,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,KAAK,CAAC;CACpC,OAAO;AACT;;AAGA,SAAgB,aAAa,SAAiB,MAA8B;CAC1E,MAAM,SAAS,SAAS,WAAW,OAAO,CAAC;CAC3C,IAAI,CAAC,MAAM,OAAO;CAClB,OAAO,CAAC,GAAG,QAAQ,GAAG,SAAS,aAAa,SAAS,IAAI,CAAC,CAAC;AAC7D;;AAGA,eAAsB,aACpB,SACA,OACA,MACA,QAAQ,GACgB;CACxB,MAAM,WAAW,aAAa,SAAS,IAAI;CAE3C,MAAM,SAAS,aAAa,OADf,SAAS,KAAK,OAAO;EAAE,IAAI,EAAE;EAAI,MAAM,EAAE;CAAK,EACrB,GAAG,EAAE,MAAM,CAAC;CAClD,MAAM,OAAO,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;CACnD,OAAO,OAAO,KAAK,MAAM,KAAK,IAAI,EAAE,EAAE,CAAE,EAAE,OAAO,OAAO;AAC1D"}
@@ -0,0 +1,2 @@
1
+ import { n as loadMemories, r as searchMemory, t as addMemory } from "./memory-Bb6ky3kb.js";
2
+ export { addMemory, loadMemories, searchMemory };
@@ -0,0 +1,26 @@
1
+ import { r as readAuditLog } from "./audit-log-DNMY9mUZ.js";
2
+ //#region src/core/metrics.ts
3
+ function computeAuditMetrics(dataDir) {
4
+ const entries = readAuditLog(dataDir);
5
+ const byTool = {};
6
+ const byActor = {};
7
+ const slugs = /* @__PURE__ */ new Set();
8
+ let systemOps = 0;
9
+ for (const e of entries) {
10
+ byTool[e.tool] = (byTool[e.tool] ?? 0) + 1;
11
+ byActor[e.actor] = (byActor[e.actor] ?? 0) + 1;
12
+ if (e.slug) slugs.add(e.slug);
13
+ if (e.actor === "system") systemOps++;
14
+ }
15
+ return {
16
+ totalOperations: entries.length,
17
+ byTool,
18
+ byActor,
19
+ customersTouched: slugs.size,
20
+ automationRate: entries.length > 0 ? systemOps / entries.length : 0
21
+ };
22
+ }
23
+ //#endregion
24
+ export { computeAuditMetrics };
25
+
26
+ //# sourceMappingURL=metrics-DH8wHvya.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-DH8wHvya.js","names":[],"sources":["../src/core/metrics.ts"],"sourcesContent":["import { readAuditLog } from \"../fs/audit-log.js\";\n\n/**\n * Command-center observability (N6-3 v1): operational metrics derived from the\n * append-only audit trail — total operations, breakdown by tool/actor, distinct\n * customers touched, and an automation rate (share performed by the system\n * actor, a proxy for autonomous/contained operations).\n */\nexport interface AuditMetrics {\n totalOperations: number;\n byTool: Record<string, number>;\n byActor: Record<string, number>;\n customersTouched: number;\n automationRate: number;\n}\n\nexport function computeAuditMetrics(dataDir: string): AuditMetrics {\n const entries = readAuditLog(dataDir);\n const byTool: Record<string, number> = {};\n const byActor: Record<string, number> = {};\n const slugs = new Set<string>();\n let systemOps = 0;\n\n for (const e of entries) {\n byTool[e.tool] = (byTool[e.tool] ?? 0) + 1;\n byActor[e.actor] = (byActor[e.actor] ?? 0) + 1;\n if (e.slug) slugs.add(e.slug);\n if (e.actor === \"system\") systemOps++;\n }\n\n return {\n totalOperations: entries.length,\n byTool,\n byActor,\n customersTouched: slugs.size,\n automationRate: entries.length > 0 ? systemOps / entries.length : 0,\n };\n}\n"],"mappings":";;AAgBA,SAAgB,oBAAoB,SAA+B;CACjE,MAAM,UAAU,aAAa,OAAO;CACpC,MAAM,SAAiC,CAAC;CACxC,MAAM,UAAkC,CAAC;CACzC,MAAM,wBAAQ,IAAI,IAAY;CAC9B,IAAI,YAAY;CAEhB,KAAK,MAAM,KAAK,SAAS;EACvB,OAAO,EAAE,SAAS,OAAO,EAAE,SAAS,KAAK;EACzC,QAAQ,EAAE,UAAU,QAAQ,EAAE,UAAU,KAAK;EAC7C,IAAI,EAAE,MAAM,MAAM,IAAI,EAAE,IAAI;EAC5B,IAAI,EAAE,UAAU,UAAU;CAC5B;CAEA,OAAO;EACL,iBAAiB,QAAQ;EACzB;EACA;EACA,kBAAkB,MAAM;EACxB,gBAAgB,QAAQ,SAAS,IAAI,YAAY,QAAQ,SAAS;CACpE;AACF"}
@@ -0,0 +1,17 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ //#region src/sync/microsoft-auth.ts
4
+ async function getMicrosoftToken(dataDir) {
5
+ const tokenPath = path.join(dataDir, ".agentic", "microsoft-token.json");
6
+ if (!fs.existsSync(tokenPath)) return null;
7
+ try {
8
+ const raw = JSON.parse(fs.readFileSync(tokenPath, "utf-8"));
9
+ return raw.accessToken ?? raw.access_token ?? null;
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+ //#endregion
15
+ export { getMicrosoftToken };
16
+
17
+ //# sourceMappingURL=microsoft-auth-B8_S45gh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microsoft-auth-B8_S45gh.js","names":[],"sources":["../src/sync/microsoft-auth.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport async function getMicrosoftToken(dataDir: string): Promise<string | null> {\n const tokenPath = path.join(dataDir, \".agentic\", \"microsoft-token.json\");\n if (!fs.existsSync(tokenPath)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(tokenPath, \"utf-8\") as string) as {\n accessToken?: string;\n access_token?: string;\n };\n return raw.accessToken ?? raw.access_token ?? null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;AAGA,eAAsB,kBAAkB,SAAyC;CAC/E,MAAM,YAAY,KAAK,KAAK,SAAS,YAAY,sBAAsB;CACvE,IAAI,CAAC,GAAG,WAAW,SAAS,GAAG,OAAO;CACtC,IAAI;EACF,MAAM,MAAM,KAAK,MAAM,GAAG,aAAa,WAAW,OAAO,CAAW;EAIpE,OAAO,IAAI,eAAe,IAAI,gBAAgB;CAChD,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -0,0 +1,67 @@
1
+ import { r as updateSlugSyncState } from "./sync-state-ChaLbamC.js";
2
+ import { t as appendInteraction } from "./interactions-writer-SLHnoEeE.js";
3
+ //#region src/sync/microsoft-calendar.ts
4
+ async function syncMicrosoftCalendar(opts) {
5
+ const result = {
6
+ synced: 0,
7
+ skipped: 0,
8
+ errors: []
9
+ };
10
+ const since = opts.since ?? /* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3);
11
+ const until = /* @__PURE__ */ new Date();
12
+ const top = opts.maxResults ?? 50;
13
+ const startStr = since.toISOString();
14
+ const endStr = until.toISOString();
15
+ const { readInteractions } = await import("./interactions-writer-dSPy1XfO.js");
16
+ const existing = await readInteractions(opts.dataDir, opts.slug).catch(() => "");
17
+ let url = `https://graph.microsoft.com/v1.0/me/calendarView?startDateTime=${startStr}&endDateTime=${endStr}&$top=${top}&$select=id,subject,bodyPreview,start,end,attendees,organizer`;
18
+ while (url) {
19
+ let events;
20
+ let nextLink;
21
+ try {
22
+ const res = await fetch(url, { headers: { Authorization: `Bearer ${opts.accessToken}` } });
23
+ if (!res.ok) {
24
+ result.errors.push(`Graph Calendar API error: ${res.status} ${res.statusText}`);
25
+ break;
26
+ }
27
+ const data = await res.json();
28
+ events = data.value ?? [];
29
+ nextLink = data["@odata.nextLink"];
30
+ } catch (err) {
31
+ result.errors.push(`Network error: ${err.message}`);
32
+ break;
33
+ }
34
+ for (const event of events) {
35
+ const sourceRef = `microsoft://calendar/${event.id}`;
36
+ if (existing.includes(sourceRef)) {
37
+ result.skipped++;
38
+ continue;
39
+ }
40
+ const date = event.start?.dateTime ? new Date(event.start.dateTime).toISOString().slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
41
+ const attendeeNames = (event.attendees ?? []).map((a) => a.emailAddress?.name ?? a.emailAddress?.address ?? "unknown").join(", ");
42
+ const organizer = event.organizer?.emailAddress?.name ?? event.organizer?.emailAddress?.address ?? "unknown";
43
+ try {
44
+ await appendInteraction(opts.dataDir, opts.slug, {
45
+ date,
46
+ type: "Meeting",
47
+ with: attendeeNames || organizer,
48
+ summary: `${event.subject ?? "(no subject)"}: ${event.bodyPreview?.slice(0, 200) ?? ""}`,
49
+ nextSteps: [],
50
+ sourceRef,
51
+ synced: (/* @__PURE__ */ new Date()).toISOString(),
52
+ direction: "inbound"
53
+ });
54
+ result.synced++;
55
+ } catch (err) {
56
+ result.errors.push(`Failed to append ${event.id}: ${err.message}`);
57
+ }
58
+ }
59
+ url = nextLink;
60
+ }
61
+ if (result.synced > 0) updateSlugSyncState(opts.dataDir, opts.slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() });
62
+ return result;
63
+ }
64
+ //#endregion
65
+ export { syncMicrosoftCalendar };
66
+
67
+ //# sourceMappingURL=microsoft-calendar-B6MMtUQK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microsoft-calendar-B6MMtUQK.js","names":[],"sources":["../src/sync/microsoft-calendar.ts"],"sourcesContent":["import { appendInteraction } from \"../fs/interactions-writer.js\";\nimport { updateSlugSyncState } from \"../fs/sync-state.js\";\n\nexport interface CalendarSyncOptions {\n slug: string;\n dataDir: string;\n accessToken: string;\n since?: Date;\n maxResults?: number;\n}\n\nexport interface CalendarSyncResult {\n synced: number;\n skipped: number;\n errors: string[];\n}\n\ninterface GraphEvent {\n id: string;\n subject?: string;\n bodyPreview?: string;\n start?: { dateTime?: string };\n end?: { dateTime?: string };\n attendees?: Array<{ emailAddress?: { name?: string; address?: string } }>;\n organizer?: { emailAddress?: { name?: string; address?: string } };\n}\n\ninterface GraphEventsResponse {\n value: GraphEvent[];\n \"@odata.nextLink\"?: string;\n}\n\nexport async function syncMicrosoftCalendar(\n opts: CalendarSyncOptions\n): Promise<CalendarSyncResult> {\n const result: CalendarSyncResult = { synced: 0, skipped: 0, errors: [] };\n const since = opts.since ?? new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);\n const until = new Date();\n const top = opts.maxResults ?? 50;\n\n const startStr = since.toISOString();\n const endStr = until.toISOString();\n\n const { readInteractions } = await import(\"../fs/interactions-writer.js\");\n const existing = await readInteractions(opts.dataDir, opts.slug).catch(() => \"\");\n\n let url: string | undefined =\n `https://graph.microsoft.com/v1.0/me/calendarView?startDateTime=${startStr}&endDateTime=${endStr}&$top=${top}&$select=id,subject,bodyPreview,start,end,attendees,organizer`;\n\n while (url) {\n let events: GraphEvent[];\n let nextLink: string | undefined;\n try {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${opts.accessToken}` },\n });\n if (!res.ok) {\n result.errors.push(`Graph Calendar API error: ${res.status} ${res.statusText}`);\n break;\n }\n const data = (await res.json()) as GraphEventsResponse;\n events = data.value ?? [];\n nextLink = data[\"@odata.nextLink\"];\n } catch (err) {\n result.errors.push(`Network error: ${(err as Error).message}`);\n break;\n }\n\n for (const event of events) {\n const sourceRef = `microsoft://calendar/${event.id}`;\n if (existing.includes(sourceRef)) {\n result.skipped++;\n continue;\n }\n\n const date = event.start?.dateTime\n ? new Date(event.start.dateTime).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10);\n\n const attendeeNames = (event.attendees ?? [])\n .map((a) => a.emailAddress?.name ?? a.emailAddress?.address ?? \"unknown\")\n .join(\", \");\n\n const organizer =\n event.organizer?.emailAddress?.name ?? event.organizer?.emailAddress?.address ?? \"unknown\";\n\n try {\n await appendInteraction(opts.dataDir, opts.slug, {\n date,\n type: \"Meeting\",\n with: attendeeNames || organizer,\n summary: `${event.subject ?? \"(no subject)\"}: ${event.bodyPreview?.slice(0, 200) ?? \"\"}`,\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n direction: \"inbound\",\n });\n result.synced++;\n } catch (err) {\n result.errors.push(`Failed to append ${event.id}: ${(err as Error).message}`);\n }\n }\n\n url = nextLink;\n }\n\n if (result.synced > 0) {\n updateSlugSyncState(opts.dataDir, opts.slug, {\n lastGmailSync: new Date().toISOString(),\n });\n }\n\n return result;\n}\n"],"mappings":";;;AAgCA,eAAsB,sBACpB,MAC6B;CAC7B,MAAM,SAA6B;EAAE,QAAQ;EAAG,SAAS;EAAG,QAAQ,CAAC;CAAE;CACvE,MAAM,QAAQ,KAAK,yBAAS,IAAI,KAAK,KAAK,IAAI,IAAI,MAAU,KAAK,KAAK,GAAI;CAC1E,MAAM,wBAAQ,IAAI,KAAK;CACvB,MAAM,MAAM,KAAK,cAAc;CAE/B,MAAM,WAAW,MAAM,YAAY;CACnC,MAAM,SAAS,MAAM,YAAY;CAEjC,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,WAAW,MAAM,iBAAiB,KAAK,SAAS,KAAK,IAAI,EAAE,YAAY,EAAE;CAE/E,IAAI,MACF,kEAAkE,SAAS,eAAe,OAAO,QAAQ,IAAI;CAE/G,OAAO,KAAK;EACV,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAC3B,SAAS,EAAE,eAAe,UAAU,KAAK,cAAc,EACzD,CAAC;GACD,IAAI,CAAC,IAAI,IAAI;IACX,OAAO,OAAO,KAAK,6BAA6B,IAAI,OAAO,GAAG,IAAI,YAAY;IAC9E;GACF;GACA,MAAM,OAAQ,MAAM,IAAI,KAAK;GAC7B,SAAS,KAAK,SAAS,CAAC;GACxB,WAAW,KAAK;EAClB,SAAS,KAAK;GACZ,OAAO,OAAO,KAAK,kBAAmB,IAAc,SAAS;GAC7D;EACF;EAEA,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,YAAY,wBAAwB,MAAM;GAChD,IAAI,SAAS,SAAS,SAAS,GAAG;IAChC,OAAO;IACP;GACF;GAEA,MAAM,OAAO,MAAM,OAAO,WACtB,IAAI,KAAK,MAAM,MAAM,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,qBACxD,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;GAExC,MAAM,iBAAiB,MAAM,aAAa,CAAC,GACxC,KAAK,MAAM,EAAE,cAAc,QAAQ,EAAE,cAAc,WAAW,SAAS,EACvE,KAAK,IAAI;GAEZ,MAAM,YACJ,MAAM,WAAW,cAAc,QAAQ,MAAM,WAAW,cAAc,WAAW;GAEnF,IAAI;IACF,MAAM,kBAAkB,KAAK,SAAS,KAAK,MAAM;KAC/C;KACA,MAAM;KACN,MAAM,iBAAiB;KACvB,SAAS,GAAG,MAAM,WAAW,eAAe,IAAI,MAAM,aAAa,MAAM,GAAG,GAAG,KAAK;KACpF,WAAW,CAAC;KACZ;KACA,yBAAQ,IAAI,KAAK,GAAE,YAAY;KAC/B,WAAW;IACb,CAAC;IACD,OAAO;GACT,SAAS,KAAK;IACZ,OAAO,OAAO,KAAK,oBAAoB,MAAM,GAAG,IAAK,IAAc,SAAS;GAC9E;EACF;EAEA,MAAM;CACR;CAEA,IAAI,OAAO,SAAS,GAClB,oBAAoB,KAAK,SAAS,KAAK,MAAM,EAC3C,gCAAe,IAAI,KAAK,GAAE,YAAY,EACxC,CAAC;CAGH,OAAO;AACT"}
@@ -0,0 +1,68 @@
1
+ import { r as updateSlugSyncState } from "./sync-state-ChaLbamC.js";
2
+ import { t as appendInteraction } from "./interactions-writer-SLHnoEeE.js";
3
+ //#region src/sync/microsoft-sync.ts
4
+ async function syncMicrosoft(opts) {
5
+ const result = {
6
+ synced: 0,
7
+ skipped: 0,
8
+ errors: []
9
+ };
10
+ const maxResults = opts.maxResults ?? 50;
11
+ let filter = "";
12
+ if (opts.since) filter = `&$filter=receivedDateTime ge ${opts.since.toISOString()}`;
13
+ if (opts.query) filter += (filter ? " and " : "&$filter=") + opts.query;
14
+ const url = `https://graph.microsoft.com/v1.0/me/messages?$top=${maxResults}${filter}`;
15
+ let messages;
16
+ try {
17
+ const res = await fetch(url, { headers: { Authorization: `Bearer ${opts.accessToken}` } });
18
+ if (!res.ok) {
19
+ result.errors.push(`Graph API error: ${res.status} ${res.statusText}`);
20
+ return result;
21
+ }
22
+ messages = (await res.json()).value ?? [];
23
+ } catch (err) {
24
+ result.errors.push(`Network error: ${err.message}`);
25
+ return result;
26
+ }
27
+ const { readInteractions } = await import("./interactions-writer-dSPy1XfO.js");
28
+ const existing = await readInteractions(opts.dataDir, opts.slug).catch(() => "");
29
+ for (const msg of messages) {
30
+ const sourceRef = `microsoft://message/${msg.id}`;
31
+ if (existing.includes(sourceRef)) {
32
+ result.skipped++;
33
+ continue;
34
+ }
35
+ const date = msg.receivedDateTime ? new Date(msg.receivedDateTime).toISOString().slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
36
+ const from = msg.from?.emailAddress?.name ?? msg.from?.emailAddress?.address ?? "unknown";
37
+ const subject = msg.subject ?? "(no subject)";
38
+ const preview = msg.bodyPreview ?? "";
39
+ let summary = preview.slice(0, 300);
40
+ let nextSteps = [];
41
+ try {
42
+ const { summarizeEmail } = await import("./llm-DEjWcqmW.js");
43
+ const llmResult = await summarizeEmail(subject, preview, from);
44
+ summary = llmResult.summary;
45
+ nextSteps = llmResult.nextSteps;
46
+ } catch {}
47
+ try {
48
+ await appendInteraction(opts.dataDir, opts.slug, {
49
+ date,
50
+ type: "Email",
51
+ with: from,
52
+ summary: `${subject}: ${summary}`,
53
+ nextSteps,
54
+ sourceRef,
55
+ synced: (/* @__PURE__ */ new Date()).toISOString()
56
+ });
57
+ result.synced++;
58
+ } catch (err) {
59
+ result.errors.push(`Failed to append ${msg.id}: ${err.message}`);
60
+ }
61
+ }
62
+ if (result.synced > 0) updateSlugSyncState(opts.dataDir, opts.slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() });
63
+ return result;
64
+ }
65
+ //#endregion
66
+ export { syncMicrosoft };
67
+
68
+ //# sourceMappingURL=microsoft-sync-CpZVoSuq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microsoft-sync-CpZVoSuq.js","names":[],"sources":["../src/sync/microsoft-sync.ts"],"sourcesContent":["import { appendInteraction } from \"../fs/interactions-writer.js\";\nimport { updateSlugSyncState } from \"../fs/sync-state.js\";\n\nexport interface MicrosoftSyncOptions {\n slug: string;\n dataDir: string;\n accessToken: string;\n query?: string;\n since?: Date;\n maxResults?: number;\n}\n\nexport interface MicrosoftSyncResult {\n synced: number;\n skipped: number;\n errors: string[];\n}\n\ninterface GraphMessage {\n id: string;\n subject?: string;\n bodyPreview?: string;\n receivedDateTime?: string;\n from?: { emailAddress?: { address?: string; name?: string } };\n}\n\ninterface GraphResponse {\n value: GraphMessage[];\n}\n\nexport async function syncMicrosoft(opts: MicrosoftSyncOptions): Promise<MicrosoftSyncResult> {\n const result: MicrosoftSyncResult = { synced: 0, skipped: 0, errors: [] };\n const maxResults = opts.maxResults ?? 50;\n\n let filter = \"\";\n if (opts.since) {\n filter = `&$filter=receivedDateTime ge ${opts.since.toISOString()}`;\n }\n if (opts.query) {\n filter += (filter ? \" and \" : \"&$filter=\") + opts.query;\n }\n\n const url = `https://graph.microsoft.com/v1.0/me/messages?$top=${maxResults}${filter}`;\n\n let messages: GraphMessage[];\n try {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${opts.accessToken}` },\n });\n if (!res.ok) {\n result.errors.push(`Graph API error: ${res.status} ${res.statusText}`);\n return result;\n }\n const data = (await res.json()) as GraphResponse;\n messages = data.value ?? [];\n } catch (err) {\n result.errors.push(`Network error: ${(err as Error).message}`);\n return result;\n }\n\n const { readInteractions } = await import(\"../fs/interactions-writer.js\");\n const existing = await readInteractions(opts.dataDir, opts.slug).catch(() => \"\");\n\n for (const msg of messages) {\n const sourceRef = `microsoft://message/${msg.id}`;\n if (existing.includes(sourceRef)) {\n result.skipped++;\n continue;\n }\n\n const date = msg.receivedDateTime\n ? new Date(msg.receivedDateTime).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10);\n\n const from = msg.from?.emailAddress?.name ?? msg.from?.emailAddress?.address ?? \"unknown\";\n const subject = msg.subject ?? \"(no subject)\";\n const preview = msg.bodyPreview ?? \"\";\n\n let summary = preview.slice(0, 300);\n let nextSteps: string[] = [];\n\n try {\n const { summarizeEmail } = await import(\"../core/llm.js\");\n const llmResult = await summarizeEmail(subject, preview, from);\n summary = llmResult.summary;\n nextSteps = llmResult.nextSteps;\n } catch {\n // LLM unavailable — use raw preview\n }\n\n try {\n await appendInteraction(opts.dataDir, opts.slug, {\n date,\n type: \"Email\",\n with: from,\n summary: `${subject}: ${summary}`,\n nextSteps,\n sourceRef,\n synced: new Date().toISOString(),\n });\n result.synced++;\n } catch (err) {\n result.errors.push(`Failed to append ${msg.id}: ${(err as Error).message}`);\n }\n }\n\n if (result.synced > 0) {\n updateSlugSyncState(opts.dataDir, opts.slug, {\n lastGmailSync: new Date().toISOString(),\n });\n }\n\n return result;\n}\n"],"mappings":";;;AA8BA,eAAsB,cAAc,MAA0D;CAC5F,MAAM,SAA8B;EAAE,QAAQ;EAAG,SAAS;EAAG,QAAQ,CAAC;CAAE;CACxE,MAAM,aAAa,KAAK,cAAc;CAEtC,IAAI,SAAS;CACb,IAAI,KAAK,OACP,SAAS,gCAAgC,KAAK,MAAM,YAAY;CAElE,IAAI,KAAK,OACP,WAAW,SAAS,UAAU,eAAe,KAAK;CAGpD,MAAM,MAAM,qDAAqD,aAAa;CAE9E,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,MAAM,MAAM,KAAK,EAC3B,SAAS,EAAE,eAAe,UAAU,KAAK,cAAc,EACzD,CAAC;EACD,IAAI,CAAC,IAAI,IAAI;GACX,OAAO,OAAO,KAAK,oBAAoB,IAAI,OAAO,GAAG,IAAI,YAAY;GACrE,OAAO;EACT;EAEA,YAAW,MADS,IAAI,KAAK,GACb,SAAS,CAAC;CAC5B,SAAS,KAAK;EACZ,OAAO,OAAO,KAAK,kBAAmB,IAAc,SAAS;EAC7D,OAAO;CACT;CAEA,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,WAAW,MAAM,iBAAiB,KAAK,SAAS,KAAK,IAAI,EAAE,YAAY,EAAE;CAE/E,KAAK,MAAM,OAAO,UAAU;EAC1B,MAAM,YAAY,uBAAuB,IAAI;EAC7C,IAAI,SAAS,SAAS,SAAS,GAAG;GAChC,OAAO;GACP;EACF;EAEA,MAAM,OAAO,IAAI,mBACb,IAAI,KAAK,IAAI,gBAAgB,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,qBACxD,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;EAExC,MAAM,OAAO,IAAI,MAAM,cAAc,QAAQ,IAAI,MAAM,cAAc,WAAW;EAChF,MAAM,UAAU,IAAI,WAAW;EAC/B,MAAM,UAAU,IAAI,eAAe;EAEnC,IAAI,UAAU,QAAQ,MAAM,GAAG,GAAG;EAClC,IAAI,YAAsB,CAAC;EAE3B,IAAI;GACF,MAAM,EAAE,mBAAmB,MAAM,OAAO;GACxC,MAAM,YAAY,MAAM,eAAe,SAAS,SAAS,IAAI;GAC7D,UAAU,UAAU;GACpB,YAAY,UAAU;EACxB,QAAQ,CAER;EAEA,IAAI;GACF,MAAM,kBAAkB,KAAK,SAAS,KAAK,MAAM;IAC/C;IACA,MAAM;IACN,MAAM;IACN,SAAS,GAAG,QAAQ,IAAI;IACxB;IACA;IACA,yBAAQ,IAAI,KAAK,GAAE,YAAY;GACjC,CAAC;GACD,OAAO;EACT,SAAS,KAAK;GACZ,OAAO,OAAO,KAAK,oBAAoB,IAAI,GAAG,IAAK,IAAc,SAAS;EAC5E;CACF;CAEA,IAAI,OAAO,SAAS,GAClB,oBAAoB,KAAK,SAAS,KAAK,MAAM,EAC3C,gCAAe,IAAI,KAAK,GAAE,YAAY,EACxC,CAAC;CAGH,OAAO;AACT"}
@@ -0,0 +1,48 @@
1
+ import { r as readInteractions } from "./interactions-writer-SLHnoEeE.js";
2
+ import { t as readPipeline } from "./pipeline-writer-BvVquKIe.js";
3
+ import { t as scoreOpportunity } from "./opportunity-score-BTMOQSTV.js";
4
+ //#region src/core/nba.ts
5
+ const STAGE_ACTION = {
6
+ lead: "Qualify the lead and book a discovery call",
7
+ qualified: "Send a tailored proposal",
8
+ proposal: "Follow up on the open proposal",
9
+ negotiation: "Address objections and push to close",
10
+ won: "Kick off onboarding",
11
+ lost: "Run a loss post-mortem"
12
+ };
13
+ const RANK = {
14
+ high: 0,
15
+ medium: 1,
16
+ low: 2
17
+ };
18
+ async function nextBestAction(dataDir, slug) {
19
+ const actions = [];
20
+ const deals = await readPipeline(dataDir, slug).catch(() => []);
21
+ const open = deals.filter((d) => d.stage !== "won" && d.stage !== "lost");
22
+ for (const d of open) {
23
+ const { score } = scoreOpportunity(d);
24
+ const priority = score >= 70 ? "high" : score >= 40 ? "medium" : "low";
25
+ actions.push({
26
+ action: STAGE_ACTION[d.stage],
27
+ reason: `Deal '${d.name}' is in ${d.stage} (win-likelihood ${score})`,
28
+ priority,
29
+ deal: d.name
30
+ });
31
+ }
32
+ const interactions = await readInteractions(dataDir, slug).catch(() => "");
33
+ if (!/## \d{4}-\d{2}-\d{2}/.test(interactions)) actions.push({
34
+ action: "Re-engage — no logged interactions yet",
35
+ reason: "No interaction history for this customer",
36
+ priority: "medium"
37
+ });
38
+ if (open.length === 0 && deals.length === 0) actions.push({
39
+ action: "Create an opportunity",
40
+ reason: "No pipeline deals exist for this customer",
41
+ priority: "low"
42
+ });
43
+ return actions.sort((a, b) => RANK[a.priority] - RANK[b.priority]);
44
+ }
45
+ //#endregion
46
+ export { nextBestAction };
47
+
48
+ //# sourceMappingURL=nba-3wanmJ0U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nba-3wanmJ0U.js","names":[],"sources":["../src/core/nba.ts"],"sourcesContent":["import { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { readInteractions } from \"../fs/interactions-writer.js\";\nimport { scoreOpportunity } from \"./opportunity-score.js\";\nimport type { PipelineDeal } from \"../schemas/pipeline.js\";\n\n/**\n * Next-Best-Action engine (domino D11 / C3): from a customer's open pipeline +\n * engagement recency, recommend prioritized next steps with rationale. Builds\n * on opportunity scoring; actions are meant to run through the D4 approval gate.\n */\nexport type Priority = \"high\" | \"medium\" | \"low\";\n\nexport interface NbaAction {\n action: string;\n reason: string;\n priority: Priority;\n deal?: string;\n}\n\nconst STAGE_ACTION: Record<PipelineDeal[\"stage\"], string> = {\n lead: \"Qualify the lead and book a discovery call\",\n qualified: \"Send a tailored proposal\",\n proposal: \"Follow up on the open proposal\",\n negotiation: \"Address objections and push to close\",\n won: \"Kick off onboarding\",\n lost: \"Run a loss post-mortem\",\n};\n\nconst RANK: Record<Priority, number> = { high: 0, medium: 1, low: 2 };\n\nexport async function nextBestAction(dataDir: string, slug: string): Promise<NbaAction[]> {\n const actions: NbaAction[] = [];\n\n const deals = await readPipeline(dataDir, slug).catch(() => [] as PipelineDeal[]);\n const open = deals.filter((d) => d.stage !== \"won\" && d.stage !== \"lost\");\n for (const d of open) {\n const { score } = scoreOpportunity(d);\n const priority: Priority = score >= 70 ? \"high\" : score >= 40 ? \"medium\" : \"low\";\n actions.push({\n action: STAGE_ACTION[d.stage],\n reason: `Deal '${d.name}' is in ${d.stage} (win-likelihood ${score})`,\n priority,\n deal: d.name,\n });\n }\n\n const interactions = await readInteractions(dataDir, slug).catch(() => \"\");\n if (!/## \\d{4}-\\d{2}-\\d{2}/.test(interactions)) {\n actions.push({\n action: \"Re-engage — no logged interactions yet\",\n reason: \"No interaction history for this customer\",\n priority: \"medium\",\n });\n }\n\n if (open.length === 0 && deals.length === 0) {\n actions.push({\n action: \"Create an opportunity\",\n reason: \"No pipeline deals exist for this customer\",\n priority: \"low\",\n });\n }\n\n return actions.sort((a, b) => RANK[a.priority] - RANK[b.priority]);\n}\n"],"mappings":";;;;AAmBA,MAAM,eAAsD;CAC1D,MAAM;CACN,WAAW;CACX,UAAU;CACV,aAAa;CACb,KAAK;CACL,MAAM;AACR;AAEA,MAAM,OAAiC;CAAE,MAAM;CAAG,QAAQ;CAAG,KAAK;AAAE;AAEpE,eAAsB,eAAe,SAAiB,MAAoC;CACxF,MAAM,UAAuB,CAAC;CAE9B,MAAM,QAAQ,MAAM,aAAa,SAAS,IAAI,EAAE,YAAY,CAAC,CAAmB;CAChF,MAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,UAAU,SAAS,EAAE,UAAU,MAAM;CACxE,KAAK,MAAM,KAAK,MAAM;EACpB,MAAM,EAAE,UAAU,iBAAiB,CAAC;EACpC,MAAM,WAAqB,SAAS,KAAK,SAAS,SAAS,KAAK,WAAW;EAC3E,QAAQ,KAAK;GACX,QAAQ,aAAa,EAAE;GACvB,QAAQ,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,MAAM;GACnE;GACA,MAAM,EAAE;EACV,CAAC;CACH;CAEA,MAAM,eAAe,MAAM,iBAAiB,SAAS,IAAI,EAAE,YAAY,EAAE;CACzE,IAAI,CAAC,uBAAuB,KAAK,YAAY,GAC3C,QAAQ,KAAK;EACX,QAAQ;EACR,QAAQ;EACR,UAAU;CACZ,CAAC;CAGH,IAAI,KAAK,WAAW,KAAK,MAAM,WAAW,GACxC,QAAQ,KAAK;EACX,QAAQ;EACR,QAAQ;EACR,UAAU;CACZ,CAAC;CAGH,OAAO,QAAQ,MAAM,GAAG,MAAM,KAAK,EAAE,YAAY,KAAK,EAAE,SAAS;AACnE"}
@@ -0,0 +1,97 @@
1
+ import { i as readQueue, r as markTaskDone } from "./proactive-agent-BgQXw3ac.js";
2
+ import https from "https";
3
+ //#region src/core/notification-dispatcher.ts
4
+ async function sendTelegram(token, chatId, text) {
5
+ const body = JSON.stringify({
6
+ chat_id: chatId,
7
+ text,
8
+ parse_mode: "Markdown"
9
+ });
10
+ await new Promise((resolve, reject) => {
11
+ const req = https.request(`https://api.telegram.org/bot${token}/sendMessage`, {
12
+ method: "POST",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ "Content-Length": Buffer.byteLength(body)
16
+ }
17
+ }, (res) => {
18
+ res.resume();
19
+ resolve();
20
+ });
21
+ req.on("error", reject);
22
+ req.write(body);
23
+ req.end();
24
+ });
25
+ }
26
+ async function sendSlack(webhookUrl, text) {
27
+ const body = JSON.stringify({ text });
28
+ const url = new URL(webhookUrl);
29
+ await new Promise((resolve, reject) => {
30
+ const req = https.request({
31
+ hostname: url.hostname,
32
+ path: url.pathname + url.search,
33
+ method: "POST",
34
+ headers: {
35
+ "Content-Type": "application/json",
36
+ "Content-Length": Buffer.byteLength(body)
37
+ }
38
+ }, (res) => {
39
+ res.resume();
40
+ resolve();
41
+ });
42
+ req.on("error", reject);
43
+ req.write(body);
44
+ req.end();
45
+ });
46
+ }
47
+ function formatTaskMessage(task) {
48
+ const payload = task.payload;
49
+ const slug = task.slug ?? "";
50
+ switch (task.type) {
51
+ case "daily_briefing": {
52
+ const b = payload;
53
+ const lines = ["šŸ“‹ *Daily CRM Briefing*", ""];
54
+ if (b.urgent?.length) {
55
+ lines.push("🚨 *Urgent:*");
56
+ b.urgent.slice(0, 3).forEach((u) => lines.push(`• ${u}`));
57
+ lines.push("");
58
+ }
59
+ if (b.forecast) lines.push(`šŸ“Š ${b.forecast}`);
60
+ if (b.topAction) lines.push(`\n⚔ *Top Action:* ${b.topAction}`);
61
+ return lines.join("\n");
62
+ }
63
+ case "relationship_decay_alert": return `āš ļø *Relationship Alert: ${slug}*\n${String(payload["name"] ?? "")} — ${String(payload["daysSinceContact"] ?? "?")} days silent, grade ${String(payload["grade"] ?? "?")}`;
64
+ case "deal_risk_alert": return `šŸ”“ *Deal Risk: ${slug}*\n"${String(payload["dealName"] ?? "")}" closes in ${String(payload["daysToClose"] ?? "?")} days`;
65
+ case "external_signal_alert": return `šŸ’” *Signal: ${slug}*\n${String(payload["summary"] ?? "")}`;
66
+ case "follow_up_nudge": return `šŸ“ž *Follow-up: ${slug}*\n${String(payload["message"] ?? "")}`;
67
+ default: return `šŸ“Œ CRM Task (${task.type})\n${JSON.stringify(payload).slice(0, 200)}`;
68
+ }
69
+ }
70
+ async function drainProactiveQueue(dataDir) {
71
+ const token = process.env["TELEGRAM_BOT_TOKEN"];
72
+ const chatId = process.env["TELEGRAM_CHAT_ID"];
73
+ const slackUrl = process.env["SLACK_WEBHOOK_URL"];
74
+ const tasks = readQueue(dataDir).filter((t) => t.status === "pending");
75
+ let sent = 0;
76
+ let failed = 0;
77
+ for (const task of tasks) {
78
+ const message = formatTaskMessage(task);
79
+ try {
80
+ if (task.channel === "telegram" && token && chatId) await sendTelegram(token, chatId, message);
81
+ else if (task.channel === "slack" && slackUrl) await sendSlack(slackUrl, message);
82
+ await markTaskDone(dataDir, task.id, "dispatched");
83
+ sent++;
84
+ } catch (err) {
85
+ failed++;
86
+ process.stderr.write(`[dispatch] Task ${task.id} failed: ${err.message}\n`);
87
+ }
88
+ }
89
+ return {
90
+ sent,
91
+ failed
92
+ };
93
+ }
94
+ //#endregion
95
+ export { drainProactiveQueue };
96
+
97
+ //# sourceMappingURL=notification-dispatcher-0vYNngWe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-dispatcher-0vYNngWe.js","names":[],"sources":["../src/core/notification-dispatcher.ts"],"sourcesContent":["// src/core/notification-dispatcher.ts\n// Drains agent-queue.json and dispatches pending tasks to Telegram, Slack, or\n// marks them done for mcp_tool_response (consumed by get_proactive_briefing).\nimport https from \"https\";\nimport { readQueue, markTaskDone, type AgentTask } from \"./proactive-agent.js\";\n\n// ─── Transport helpers ────────────────────────────────────────────────────────\n\nexport async function sendTelegram(token: string, chatId: string, text: string): Promise<void> {\n const body = JSON.stringify({ chat_id: chatId, text, parse_mode: \"Markdown\" });\n await new Promise<void>((resolve, reject) => {\n const req = https.request(\n `https://api.telegram.org/bot${token}/sendMessage`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n res.resume();\n resolve();\n }\n );\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\nexport async function sendSlack(webhookUrl: string, text: string): Promise<void> {\n const body = JSON.stringify({ text });\n const url = new URL(webhookUrl);\n await new Promise<void>((resolve, reject) => {\n const req = https.request(\n {\n hostname: url.hostname,\n path: url.pathname + url.search,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n },\n },\n (res) => {\n res.resume();\n resolve();\n }\n );\n req.on(\"error\", reject);\n req.write(body);\n req.end();\n });\n}\n\n// ─── Message formatting ───────────────────────────────────────────────────────\n\nexport function formatTaskMessage(task: AgentTask): string {\n const payload = task.payload as Record<string, unknown>;\n const slug = task.slug ?? \"\";\n\n switch (task.type) {\n case \"daily_briefing\": {\n const b = payload as { urgent?: string[]; forecast?: string; topAction?: string };\n const lines: string[] = [\"šŸ“‹ *Daily CRM Briefing*\", \"\"];\n if (b.urgent?.length) {\n lines.push(\"🚨 *Urgent:*\");\n b.urgent.slice(0, 3).forEach((u) => lines.push(`• ${u}`));\n lines.push(\"\");\n }\n if (b.forecast) lines.push(`šŸ“Š ${b.forecast}`);\n if (b.topAction) lines.push(`\\n⚔ *Top Action:* ${b.topAction}`);\n return lines.join(\"\\n\");\n }\n case \"relationship_decay_alert\":\n return `āš ļø *Relationship Alert: ${slug}*\\n${String(payload[\"name\"] ?? \"\")} — ${String(payload[\"daysSinceContact\"] ?? \"?\")} days silent, grade ${String(payload[\"grade\"] ?? \"?\")}`;\n case \"deal_risk_alert\":\n return `šŸ”“ *Deal Risk: ${slug}*\\n\"${String(payload[\"dealName\"] ?? \"\")}\" closes in ${String(payload[\"daysToClose\"] ?? \"?\")} days`;\n case \"external_signal_alert\":\n return `šŸ’” *Signal: ${slug}*\\n${String(payload[\"summary\"] ?? \"\")}`;\n case \"follow_up_nudge\":\n return `šŸ“ž *Follow-up: ${slug}*\\n${String(payload[\"message\"] ?? \"\")}`;\n default:\n return `šŸ“Œ CRM Task (${task.type})\\n${JSON.stringify(payload).slice(0, 200)}`;\n }\n}\n\n// ─── Queue drain ──────────────────────────────────────────────────────────────\n\nexport interface DrainResult {\n sent: number;\n failed: number;\n}\n\nexport async function drainProactiveQueue(dataDir: string): Promise<DrainResult> {\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n const chatId = process.env[\"TELEGRAM_CHAT_ID\"];\n const slackUrl = process.env[\"SLACK_WEBHOOK_URL\"];\n\n const tasks = readQueue(dataDir).filter((t) => t.status === \"pending\");\n let sent = 0;\n let failed = 0;\n\n for (const task of tasks) {\n const message = formatTaskMessage(task);\n try {\n if (task.channel === \"telegram\" && token && chatId) {\n await sendTelegram(token, chatId, message);\n } else if (task.channel === \"slack\" && slackUrl) {\n await sendSlack(slackUrl, message);\n }\n // mcp_tool_response tasks: consumed by get_proactive_briefing — just mark done\n await markTaskDone(dataDir, task.id, \"dispatched\");\n sent++;\n } catch (err) {\n failed++;\n process.stderr.write(`[dispatch] Task ${task.id} failed: ${(err as Error).message}\\n`);\n }\n }\n\n return { sent, failed };\n}\n"],"mappings":";;;AAQA,eAAsB,aAAa,OAAe,QAAgB,MAA6B;CAC7F,MAAM,OAAO,KAAK,UAAU;EAAE,SAAS;EAAQ;EAAM,YAAY;CAAW,CAAC;CAC7E,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,MAAM,MAAM,MAAM,QAChB,+BAA+B,MAAM,eACrC;GACE,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,kBAAkB,OAAO,WAAW,IAAI;GAC1C;EACF,IACC,QAAQ;GACP,IAAI,OAAO;GACX,QAAQ;EACV,CACF;EACA,IAAI,GAAG,SAAS,MAAM;EACtB,IAAI,MAAM,IAAI;EACd,IAAI,IAAI;CACV,CAAC;AACH;AAEA,eAAsB,UAAU,YAAoB,MAA6B;CAC/E,MAAM,OAAO,KAAK,UAAU,EAAE,KAAK,CAAC;CACpC,MAAM,MAAM,IAAI,IAAI,UAAU;CAC9B,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,MAAM,MAAM,MAAM,QAChB;GACE,UAAU,IAAI;GACd,MAAM,IAAI,WAAW,IAAI;GACzB,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,kBAAkB,OAAO,WAAW,IAAI;GAC1C;EACF,IACC,QAAQ;GACP,IAAI,OAAO;GACX,QAAQ;EACV,CACF;EACA,IAAI,GAAG,SAAS,MAAM;EACtB,IAAI,MAAM,IAAI;EACd,IAAI,IAAI;CACV,CAAC;AACH;AAIA,SAAgB,kBAAkB,MAAyB;CACzD,MAAM,UAAU,KAAK;CACrB,MAAM,OAAO,KAAK,QAAQ;CAE1B,QAAQ,KAAK,MAAb;EACE,KAAK,kBAAkB;GACrB,MAAM,IAAI;GACV,MAAM,QAAkB,CAAC,2BAA2B,EAAE;GACtD,IAAI,EAAE,QAAQ,QAAQ;IACpB,MAAM,KAAK,cAAc;IACzB,EAAE,OAAO,MAAM,GAAG,CAAC,EAAE,SAAS,MAAM,MAAM,KAAK,KAAK,GAAG,CAAC;IACxD,MAAM,KAAK,EAAE;GACf;GACA,IAAI,EAAE,UAAU,MAAM,KAAK,MAAM,EAAE,UAAU;GAC7C,IAAI,EAAE,WAAW,MAAM,KAAK,qBAAqB,EAAE,WAAW;GAC9D,OAAO,MAAM,KAAK,IAAI;EACxB;EACA,KAAK,4BACH,OAAO,2BAA2B,KAAK,KAAK,OAAO,QAAQ,WAAW,EAAE,EAAE,KAAK,OAAO,QAAQ,uBAAuB,GAAG,EAAE,sBAAsB,OAAO,QAAQ,YAAY,GAAG;EAChL,KAAK,mBACH,OAAO,kBAAkB,KAAK,MAAM,OAAO,QAAQ,eAAe,EAAE,EAAE,cAAc,OAAO,QAAQ,kBAAkB,GAAG,EAAE;EAC5H,KAAK,yBACH,OAAO,eAAe,KAAK,KAAK,OAAO,QAAQ,cAAc,EAAE;EACjE,KAAK,mBACH,OAAO,kBAAkB,KAAK,KAAK,OAAO,QAAQ,cAAc,EAAE;EACpE,SACE,OAAO,gBAAgB,KAAK,KAAK,KAAK,KAAK,UAAU,OAAO,EAAE,MAAM,GAAG,GAAG;CAC9E;AACF;AASA,eAAsB,oBAAoB,SAAuC;CAC/E,MAAM,QAAQ,QAAQ,IAAI;CAC1B,MAAM,SAAS,QAAQ,IAAI;CAC3B,MAAM,WAAW,QAAQ,IAAI;CAE7B,MAAM,QAAQ,UAAU,OAAO,EAAE,QAAQ,MAAM,EAAE,WAAW,SAAS;CACrE,IAAI,OAAO;CACX,IAAI,SAAS;CAEb,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,kBAAkB,IAAI;EACtC,IAAI;GACF,IAAI,KAAK,YAAY,cAAc,SAAS,QAC1C,MAAM,aAAa,OAAO,QAAQ,OAAO;QACpC,IAAI,KAAK,YAAY,WAAW,UACrC,MAAM,UAAU,UAAU,OAAO;GAGnC,MAAM,aAAa,SAAS,KAAK,IAAI,YAAY;GACjD;EACF,SAAS,KAAK;GACZ;GACA,QAAQ,OAAO,MAAM,mBAAmB,KAAK,GAAG,WAAY,IAAc,QAAQ,GAAG;EACvF;CACF;CAEA,OAAO;EAAE;EAAM;CAAO;AACxB"}
@@ -0,0 +1,47 @@
1
+ //#region src/core/opportunity-score.ts
2
+ /**
3
+ * Opportunity (win-likelihood) scoring (N2-1): a deterministic 0–100 score that
4
+ * blends the deal's pipeline stage with its probability, plus an A–F grade.
5
+ * Complements get_deal_health (activity-based) with a forecast-oriented view.
6
+ */
7
+ const STAGE_WEIGHT = {
8
+ lead: 10,
9
+ qualified: 30,
10
+ proposal: 55,
11
+ negotiation: 75,
12
+ won: 100,
13
+ lost: 0
14
+ };
15
+ function grade(score) {
16
+ if (score >= 85) return "A";
17
+ if (score >= 70) return "B";
18
+ if (score >= 50) return "C";
19
+ if (score >= 30) return "D";
20
+ return "F";
21
+ }
22
+ function scoreOpportunity(deal) {
23
+ const stageWeight = STAGE_WEIGHT[deal.stage] ?? 10;
24
+ if (deal.stage === "won") return {
25
+ score: 100,
26
+ grade: "A",
27
+ factors: { stageWeight }
28
+ };
29
+ if (deal.stage === "lost") return {
30
+ score: 0,
31
+ grade: "F",
32
+ factors: { stageWeight }
33
+ };
34
+ const score = typeof deal.probability === "number" ? Math.round((stageWeight + deal.probability) / 2) : stageWeight;
35
+ return {
36
+ score,
37
+ grade: grade(score),
38
+ factors: {
39
+ stageWeight,
40
+ ...typeof deal.probability === "number" ? { probability: deal.probability } : {}
41
+ }
42
+ };
43
+ }
44
+ //#endregion
45
+ export { scoreOpportunity as t };
46
+
47
+ //# sourceMappingURL=opportunity-score-BTMOQSTV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity-score-BTMOQSTV.js","names":[],"sources":["../src/core/opportunity-score.ts"],"sourcesContent":["import type { PipelineDeal } from \"../schemas/pipeline.js\";\n\n/**\n * Opportunity (win-likelihood) scoring (N2-1): a deterministic 0–100 score that\n * blends the deal's pipeline stage with its probability, plus an A–F grade.\n * Complements get_deal_health (activity-based) with a forecast-oriented view.\n */\nconst STAGE_WEIGHT: Record<PipelineDeal[\"stage\"], number> = {\n lead: 10,\n qualified: 30,\n proposal: 55,\n negotiation: 75,\n won: 100,\n lost: 0,\n};\n\nexport interface OpportunityScore {\n score: number;\n grade: \"A\" | \"B\" | \"C\" | \"D\" | \"F\";\n factors: { stageWeight: number; probability?: number };\n}\n\nfunction grade(score: number): OpportunityScore[\"grade\"] {\n if (score >= 85) return \"A\";\n if (score >= 70) return \"B\";\n if (score >= 50) return \"C\";\n if (score >= 30) return \"D\";\n return \"F\";\n}\n\nexport function scoreOpportunity(deal: PipelineDeal): OpportunityScore {\n const stageWeight = STAGE_WEIGHT[deal.stage] ?? 10;\n // Closed deals are definitive.\n if (deal.stage === \"won\") return { score: 100, grade: \"A\", factors: { stageWeight } };\n if (deal.stage === \"lost\") return { score: 0, grade: \"F\", factors: { stageWeight } };\n\n const score =\n typeof deal.probability === \"number\"\n ? Math.round((stageWeight + deal.probability) / 2)\n : stageWeight;\n\n return {\n score,\n grade: grade(score),\n factors: {\n stageWeight,\n ...(typeof deal.probability === \"number\" ? { probability: deal.probability } : {}),\n },\n };\n}\n"],"mappings":";;;;;;AAOA,MAAM,eAAsD;CAC1D,MAAM;CACN,WAAW;CACX,UAAU;CACV,aAAa;CACb,KAAK;CACL,MAAM;AACR;AAQA,SAAS,MAAM,OAA0C;CACvD,IAAI,SAAS,IAAI,OAAO;CACxB,IAAI,SAAS,IAAI,OAAO;CACxB,IAAI,SAAS,IAAI,OAAO;CACxB,IAAI,SAAS,IAAI,OAAO;CACxB,OAAO;AACT;AAEA,SAAgB,iBAAiB,MAAsC;CACrE,MAAM,cAAc,aAAa,KAAK,UAAU;CAEhD,IAAI,KAAK,UAAU,OAAO,OAAO;EAAE,OAAO;EAAK,OAAO;EAAK,SAAS,EAAE,YAAY;CAAE;CACpF,IAAI,KAAK,UAAU,QAAQ,OAAO;EAAE,OAAO;EAAG,OAAO;EAAK,SAAS,EAAE,YAAY;CAAE;CAEnF,MAAM,QACJ,OAAO,KAAK,gBAAgB,WACxB,KAAK,OAAO,cAAc,KAAK,eAAe,CAAC,IAC/C;CAEN,OAAO;EACL;EACA,OAAO,MAAM,KAAK;EAClB,SAAS;GACP;GACA,GAAI,OAAO,KAAK,gBAAgB,WAAW,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;EAClF;CACF;AACF"}
@@ -0,0 +1,17 @@
1
+ //#region src/sync/pipedrive-client.ts
2
+ async function pipedriveGet(instanceUrl, token, path) {
3
+ const url = `${instanceUrl.replace(/\/$/, "")}/api/v1${path}?limit=500&api_token=${token}`;
4
+ const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
5
+ if (!res.ok) throw new Error(`Pipedrive API error: ${res.status} ${res.statusText}`);
6
+ return (await res.json()).data ?? [];
7
+ }
8
+ async function fetchPipedrivePersons(instanceUrl, token) {
9
+ return pipedriveGet(instanceUrl, token, "/persons");
10
+ }
11
+ async function fetchPipedriveActivities(instanceUrl, token) {
12
+ return pipedriveGet(instanceUrl, token, "/activities");
13
+ }
14
+ //#endregion
15
+ export { fetchPipedriveActivities, fetchPipedrivePersons };
16
+
17
+ //# sourceMappingURL=pipedrive-client-CdGKpH9b.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipedrive-client-CdGKpH9b.js","names":[],"sources":["../src/sync/pipedrive-client.ts"],"sourcesContent":["export interface PipedrivePerson {\n id: number;\n name: string;\n primary_email?: string;\n org_name?: string;\n org_id?: { value: number };\n}\n\nexport interface PipedriveActivity {\n id: number;\n type?: string;\n subject?: string;\n note?: string;\n due_date?: string;\n person_id?: number;\n org_id?: number;\n}\n\ninterface PipedriveResponse<T> {\n data: T[];\n additional_data?: { pagination?: { more_items_in_collection?: boolean } };\n}\n\nasync function pipedriveGet<T>(instanceUrl: string, token: string, path: string): Promise<T[]> {\n const url = `${instanceUrl.replace(/\\/$/, \"\")}/api/v1${path}?limit=500&api_token=${token}`;\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) {\n throw new Error(`Pipedrive API error: ${res.status} ${res.statusText}`);\n }\n const data = (await res.json()) as PipedriveResponse<T>;\n return data.data ?? [];\n}\n\nexport async function fetchPipedrivePersons(\n instanceUrl: string,\n token: string\n): Promise<PipedrivePerson[]> {\n return pipedriveGet<PipedrivePerson>(instanceUrl, token, \"/persons\");\n}\n\nexport async function fetchPipedriveActivities(\n instanceUrl: string,\n token: string\n): Promise<PipedriveActivity[]> {\n return pipedriveGet<PipedriveActivity>(instanceUrl, token, \"/activities\");\n}\n\nasync function* pipedriveGetAll<T>(\n instanceUrl: string,\n token: string,\n endpoint: string\n): AsyncGenerator<T> {\n let cursor: string | undefined;\n do {\n const params = new URLSearchParams({ limit: \"500\", api_token: token });\n if (cursor) params.set(\"cursor\", cursor);\n const url = `${instanceUrl.replace(/\\/$/, \"\")}/api/v2${endpoint}?${params.toString()}`;\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`Pipedrive API error: ${res.status}`);\n const data = (await res.json()) as { data?: T[]; additional_data?: { next_cursor?: string } };\n for (const item of data.data ?? []) yield item;\n cursor = data.additional_data?.next_cursor;\n } while (cursor);\n}\n\nexport async function fetchPipedrivePersonsAll(\n instanceUrl: string,\n token: string\n): Promise<PipedrivePerson[]> {\n const result: PipedrivePerson[] = [];\n for await (const p of pipedriveGetAll<PipedrivePerson>(instanceUrl, token, \"/persons\")) {\n result.push(p);\n }\n return result;\n}\n\nexport async function fetchPipedriveActivitiesAll(\n instanceUrl: string,\n token: string\n): Promise<PipedriveActivity[]> {\n const result: PipedriveActivity[] = [];\n for await (const a of pipedriveGetAll<PipedriveActivity>(instanceUrl, token, \"/activities\")) {\n result.push(a);\n }\n return result;\n}\n"],"mappings":";AAuBA,eAAe,aAAgB,aAAqB,OAAe,MAA4B;CAC7F,MAAM,MAAM,GAAG,YAAY,QAAQ,OAAO,EAAE,EAAE,SAAS,KAAK,uBAAuB;CACnF,MAAM,MAAM,MAAM,MAAM,KAAK,EAC3B,SAAS,EAAE,eAAe,UAAU,QAAQ,EAC9C,CAAC;CACD,IAAI,CAAC,IAAI,IACP,MAAM,IAAI,MAAM,wBAAwB,IAAI,OAAO,GAAG,IAAI,YAAY;CAGxE,QAAO,MADa,IAAI,KAAK,GACjB,QAAQ,CAAC;AACvB;AAEA,eAAsB,sBACpB,aACA,OAC4B;CAC5B,OAAO,aAA8B,aAAa,OAAO,UAAU;AACrE;AAEA,eAAsB,yBACpB,aACA,OAC8B;CAC9B,OAAO,aAAgC,aAAa,OAAO,aAAa;AAC1E"}
@@ -0,0 +1,2 @@
1
+ import { n as readPipelineSync, r as upsertDeal, t as readPipeline } from "./pipeline-writer-BvVquKIe.js";
2
+ export { readPipeline, readPipelineSync, upsertDeal };