@datasynx/agentic-crm 0.1.0 → 1.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 (309) hide show
  1. package/README.md +270 -669
  2. package/dist/{approvals-DpjxGHFp.js → approvals-CmDT2eUg.js} +7 -24
  3. package/dist/approvals-CmDT2eUg.js.map +1 -0
  4. package/dist/{ask-CID3jnuL.js → ask-CDysGnRg.js} +6 -6
  5. package/dist/{ask-CID3jnuL.js.map → ask-CDysGnRg.js.map} +1 -1
  6. package/dist/atomic-write-8yjqqLtS.js +29 -0
  7. package/dist/atomic-write-8yjqqLtS.js.map +1 -0
  8. package/dist/atomic-write-BYmF-ThH.cjs +37 -0
  9. package/dist/atomic-write-BYmF-ThH.cjs.map +1 -0
  10. package/dist/attachments-CX2GAtsw.cjs +517 -0
  11. package/dist/attachments-CX2GAtsw.cjs.map +1 -0
  12. package/dist/attachments-D207gXfN.js +514 -0
  13. package/dist/attachments-D207gXfN.js.map +1 -0
  14. package/dist/attachments-rLa96rOK.js +514 -0
  15. package/dist/attachments-rLa96rOK.js.map +1 -0
  16. package/dist/auth-B5DcjJ_6.js +2 -0
  17. package/dist/{auth-DFWwWcYD.js → auth-DDXZTwS0.js} +4 -13
  18. package/dist/auth-DDXZTwS0.js.map +1 -0
  19. package/dist/{autofill-Di_-SP7t.js → autofill-B9VtlR2j.js} +2 -2
  20. package/dist/{autofill-Di_-SP7t.js.map → autofill-B9VtlR2j.js.map} +1 -1
  21. package/dist/{backup-CeMk9z86.js → backup-CTlIxUdO.js} +5 -7
  22. package/dist/backup-CTlIxUdO.js.map +1 -0
  23. package/dist/backup-LFnC09oV.js +2 -0
  24. package/dist/chunk-BfDYWZQ8.cjs +32 -0
  25. package/dist/chunk-BfDYWZQ8.cjs.map +1 -0
  26. package/dist/chunk-BhUZmQg5.js +32 -0
  27. package/dist/chunk-BhUZmQg5.js.map +1 -0
  28. package/dist/chunk-ChC83jai.js +2 -0
  29. package/dist/chunk-e_w8qqtP.js +32 -0
  30. package/dist/chunk-e_w8qqtP.js.map +1 -0
  31. package/dist/{churn-C28IgnAj.js → churn-DN9WDGNM.js} +3 -3
  32. package/dist/{churn-C28IgnAj.js.map → churn-DN9WDGNM.js.map} +1 -1
  33. package/dist/cli.js +285 -186
  34. package/dist/cli.js.map +1 -1
  35. package/dist/{compliance-CKSBoQUe.js → compliance-Bc12Hn9a.js} +3 -3
  36. package/dist/{compliance-CKSBoQUe.js.map → compliance-Bc12Hn9a.js.map} +1 -1
  37. package/dist/{compliance-CujOqAKk.js → compliance-TqYQXhBj.js} +1 -1
  38. package/dist/{compliance-B1kk5-YS.js → compliance-kq0xHRw3.js} +3 -3
  39. package/dist/{compliance-B1kk5-YS.js.map → compliance-kq0xHRw3.js.map} +1 -1
  40. package/dist/{compliance-B91zNvCR.cjs → compliance-pAj9FcGI.cjs} +3 -3
  41. package/dist/{compliance-B91zNvCR.cjs.map → compliance-pAj9FcGI.cjs.map} +1 -1
  42. package/dist/{context-builder-BzWAp3Zs.js → context-builder-7Uab5-G4.js} +3 -2
  43. package/dist/context-builder-7Uab5-G4.js.map +1 -0
  44. package/dist/context-builder-hmOPvgso.js +2 -0
  45. package/dist/{custom-fields-CzNeD3_v.js → custom-fields-BMyz5Ruh.js} +1 -1
  46. package/dist/{custom-fields-Pl2t9xzp.js → custom-fields-GzpOHW_2.js} +4 -13
  47. package/dist/custom-fields-GzpOHW_2.js.map +1 -0
  48. package/dist/{custom-objects-CIFrmQ2V.js → custom-objects-BNy-ayR-.js} +1 -1
  49. package/dist/{custom-objects-BHgn1GEX.js → custom-objects-CxW1gHwJ.js} +10 -25
  50. package/dist/custom-objects-CxW1gHwJ.js.map +1 -0
  51. package/dist/{customer-dir-DIylZ8Q6.js → customer-dir-CkMMXhb0.js} +9 -4
  52. package/dist/customer-dir-CkMMXhb0.js.map +1 -0
  53. package/dist/daemon/worker.js +66 -40
  54. package/dist/daemon/worker.js.map +1 -1
  55. package/dist/doctor-C14-vnJ1.js +103 -0
  56. package/dist/doctor-C14-vnJ1.js.map +1 -0
  57. package/dist/email-body-BFSRa0AW.cjs +42 -0
  58. package/dist/email-body-BFSRa0AW.cjs.map +1 -0
  59. package/dist/email-body-BOd7U-D2.js +42 -0
  60. package/dist/email-body-BOd7U-D2.js.map +1 -0
  61. package/dist/{enrichment-3XvgGDfB.js → enrichment-CDFdWmvD.js} +3 -3
  62. package/dist/{enrichment-3XvgGDfB.js.map → enrichment-CDFdWmvD.js.map} +1 -1
  63. package/dist/{file-lock-B_zi7NQl.js → file-lock-CcHotQkZ.js} +3 -4
  64. package/dist/file-lock-CcHotQkZ.js.map +1 -0
  65. package/dist/{gmail-sync-DIaxInDT.js → gmail-sync-B4Iu3AQb.js} +56 -22
  66. package/dist/gmail-sync-B4Iu3AQb.js.map +1 -0
  67. package/dist/{gmail-sync-hHm9gaWd.cjs → gmail-sync-BpSVESSe.cjs} +55 -21
  68. package/dist/gmail-sync-BpSVESSe.cjs.map +1 -0
  69. package/dist/{gmail-sync-rQaVqKWd.js → gmail-sync-DIbrPnTK.js} +55 -21
  70. package/dist/gmail-sync-DIbrPnTK.js.map +1 -0
  71. package/dist/{gmail-webhook-handler-e5Od25FX.js → gmail-webhook-handler-BzOFbvgh.js} +4 -4
  72. package/dist/{gmail-webhook-handler-e5Od25FX.js.map → gmail-webhook-handler-BzOFbvgh.js.map} +1 -1
  73. package/dist/{gmail-webhook-handler-DS7OlRPX.js → gmail-webhook-handler-CvSDW_Js.js} +2 -2
  74. package/dist/{goal-engine-KpBftn4V.js → goal-engine-BbroPhqm.js} +10 -11
  75. package/dist/goal-engine-BbroPhqm.js.map +1 -0
  76. package/dist/{goal-engine-CUZSpERI.js → goal-engine-CfDAJTFt.js} +1 -1
  77. package/dist/{google-drive-sync-DEPcqFca.js → google-drive-sync-B_I1d54Y.js} +3 -3
  78. package/dist/{google-drive-sync-DEPcqFca.js.map → google-drive-sync-B_I1d54Y.js.map} +1 -1
  79. package/dist/html-BaeOCZKE.js +36 -0
  80. package/dist/html-BaeOCZKE.js.map +1 -0
  81. package/dist/html-CmOku6jS.cjs +47 -0
  82. package/dist/html-CmOku6jS.cjs.map +1 -0
  83. package/dist/{hygiene-DZqfYpFf.js → hygiene-DzQPnc6P.js} +3 -3
  84. package/dist/{hygiene-DZqfYpFf.js.map → hygiene-DzQPnc6P.js.map} +1 -1
  85. package/dist/identity-CB7j-Zr1.js +2 -0
  86. package/dist/{identity-CI6olMNm.js → identity-_uZ3Lbr2.js} +2 -2
  87. package/dist/{identity-CI6olMNm.js.map → identity-_uZ3Lbr2.js.map} +1 -1
  88. package/dist/{import-hubspot-BaK71U_K.js → import-hubspot-CTId9IGV.js} +51 -45
  89. package/dist/import-hubspot-CTId9IGV.js.map +1 -0
  90. package/dist/{index-YqwMd6aQ.d.cts → index-BAutNcAT.d.cts} +20 -12
  91. package/dist/index-BAutNcAT.d.cts.map +1 -0
  92. package/dist/{index-V8BFaH-b.d.ts → index-FzDsNSSb.d.ts} +12 -4
  93. package/dist/index-FzDsNSSb.d.ts.map +1 -0
  94. package/dist/index.cjs +19 -21
  95. package/dist/index.cjs.map +1 -1
  96. package/dist/index.d.cts +20 -12
  97. package/dist/index.d.cts.map +1 -1
  98. package/dist/index.d.ts +12 -4
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +19 -21
  101. package/dist/index.js.map +1 -1
  102. package/dist/interactions-writer-B2y-73lh.js +2 -0
  103. package/dist/{interactions-writer-SLHnoEeE.js → interactions-writer-B8XAzdqR.js} +34 -4
  104. package/dist/interactions-writer-B8XAzdqR.js.map +1 -0
  105. package/dist/{interactions-writer-CrPStUll.cjs → interactions-writer-BRJNrefF.cjs} +7 -3
  106. package/dist/interactions-writer-BRJNrefF.cjs.map +1 -0
  107. package/dist/{interactions-writer-DO3KcSR3.js → interactions-writer-ZQcpFOh9.js} +7 -3
  108. package/dist/interactions-writer-ZQcpFOh9.js.map +1 -0
  109. package/dist/json-store-WWsFzXub.js +43 -0
  110. package/dist/json-store-WWsFzXub.js.map +1 -0
  111. package/dist/{knowledge-base-D0Fh40kc.js → knowledge-base--063Kpa3.js} +51 -22
  112. package/dist/knowledge-base--063Kpa3.js.map +1 -0
  113. package/dist/{lancedb-CCBbpulq.js → lancedb-CswQEE5K.js} +1 -1
  114. package/dist/{lancedb-rlvWoPwl.js → lancedb-CuHKNsNZ.js} +4 -3
  115. package/dist/lancedb-CuHKNsNZ.js.map +1 -0
  116. package/dist/{lead-model-BCFzyktm.js → lead-model-CEmx7te7.js} +6 -14
  117. package/dist/lead-model-CEmx7te7.js.map +1 -0
  118. package/dist/{llm-Z8RIYkpF.js → llm-BnSUBisu.js} +2 -2
  119. package/dist/{llm-Z8RIYkpF.js.map → llm-BnSUBisu.js.map} +1 -1
  120. package/dist/{llm-iijeXmgq.cjs → llm-CXycmEl9.cjs} +2 -2
  121. package/dist/{llm-iijeXmgq.cjs.map → llm-CXycmEl9.cjs.map} +1 -1
  122. package/dist/{llm-DEjWcqmW.js → llm-DSX1-wFu.js} +1 -1
  123. package/dist/{llm-DvzZqva0.js → llm-PZzgPphl.js} +3 -3
  124. package/dist/{llm-DvzZqva0.js.map → llm-PZzgPphl.js.map} +1 -1
  125. package/dist/logger-BkInaGoV.cjs +167 -0
  126. package/dist/logger-BkInaGoV.cjs.map +1 -0
  127. package/dist/logger-Dyl4VcLO.js +147 -0
  128. package/dist/logger-Dyl4VcLO.js.map +1 -0
  129. package/dist/logger-UaF5p9d1.js +147 -0
  130. package/dist/logger-UaF5p9d1.js.map +1 -0
  131. package/dist/logger-vKQS34w9.js +2 -0
  132. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  133. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  134. package/dist/mcp.cjs +365 -319
  135. package/dist/mcp.cjs.map +1 -1
  136. package/dist/mcp.d.cts.map +1 -1
  137. package/dist/mcp.d.ts.map +1 -1
  138. package/dist/mcp.js +365 -319
  139. package/dist/mcp.js.map +1 -1
  140. package/dist/{memory-Cy6-Tbyl.js → memory-D8hmgD9d.js} +1 -1
  141. package/dist/{memory-Bb6ky3kb.js → memory-Dzr9dXSM.js} +4 -11
  142. package/dist/memory-Dzr9dXSM.js.map +1 -0
  143. package/dist/{microsoft-calendar-B6MMtUQK.js → microsoft-calendar-BgVR8GDv.js} +4 -4
  144. package/dist/{microsoft-calendar-B6MMtUQK.js.map → microsoft-calendar-BgVR8GDv.js.map} +1 -1
  145. package/dist/{microsoft-sync-CpZVoSuq.js → microsoft-sync-D30_XksI.js} +5 -5
  146. package/dist/{microsoft-sync-CpZVoSuq.js.map → microsoft-sync-D30_XksI.js.map} +1 -1
  147. package/dist/{nba-3wanmJ0U.js → nba-DwdfM93s.js} +3 -3
  148. package/dist/{nba-3wanmJ0U.js.map → nba-DwdfM93s.js.map} +1 -1
  149. package/dist/{notification-dispatcher-0vYNngWe.js → notification-dispatcher-inpKyuBz.js} +7 -3
  150. package/dist/notification-dispatcher-inpKyuBz.js.map +1 -0
  151. package/dist/{pipeline-writer-BqBrYrQc.js → pipeline-writer-0LJ6Qkat.js} +1 -1
  152. package/dist/{pipeline-writer-N2omexxp.cjs → pipeline-writer-B1tRAhuD.cjs} +11 -3
  153. package/dist/pipeline-writer-B1tRAhuD.cjs.map +1 -0
  154. package/dist/{pipeline-writer-BvVquKIe.js → pipeline-writer-CIllfnZl.js} +5 -3
  155. package/dist/pipeline-writer-CIllfnZl.js.map +1 -0
  156. package/dist/{pipeline-writer-eufx_0o1.js → pipeline-writer-rDj-ni6q.js} +6 -4
  157. package/dist/pipeline-writer-rDj-ni6q.js.map +1 -0
  158. package/dist/{proactive-agent-BgQXw3ac.js → proactive-agent-B7u3Bj_l.js} +6 -6
  159. package/dist/{proactive-agent-BgQXw3ac.js.map → proactive-agent-B7u3Bj_l.js.map} +1 -1
  160. package/dist/{proactive-worker-BrLHNhjH.js → proactive-worker-1zkm6aJD.js} +7 -8
  161. package/dist/proactive-worker-1zkm6aJD.js.map +1 -0
  162. package/dist/{push-manager-CowY-0IK.js → push-manager-BXM-IHfP.js} +1 -1
  163. package/dist/{push-manager-CdqIIkuh.js → push-manager-C0ECQgva.js} +4 -4
  164. package/dist/push-manager-C0ECQgva.js.map +1 -0
  165. package/dist/{quote-generator-OhSFsi3x.js → quote-generator-ByUyIYtw.js} +1 -1
  166. package/dist/{quote-generator-BfwENXzg.js → quote-generator-CTdR8eEI.js} +5 -5
  167. package/dist/quote-generator-CTdR8eEI.js.map +1 -0
  168. package/dist/rbac-DzbyFhVH.js +2 -0
  169. package/dist/{rbac-CTIktZaC.js → rbac-msmBc_tK.js} +19 -12
  170. package/dist/rbac-msmBc_tK.js.map +1 -0
  171. package/dist/regex-Jt5DatPi.js +13 -0
  172. package/dist/regex-Jt5DatPi.js.map +1 -0
  173. package/dist/{relationship-health-odxEoQdJ.js → relationship-health-ZZNXR1RZ.js} +8 -16
  174. package/dist/relationship-health-ZZNXR1RZ.js.map +1 -0
  175. package/dist/{revenue-simulation-Bqf2DLVB.js → revenue-simulation-D8f_YkUY.js} +9 -19
  176. package/dist/revenue-simulation-D8f_YkUY.js.map +1 -0
  177. package/dist/{revenue-simulation-BJdRTEHc.js → revenue-simulation-njJZlTqm.js} +1 -1
  178. package/dist/safe-path-mpp0dKtO.js +18 -0
  179. package/dist/safe-path-mpp0dKtO.js.map +1 -0
  180. package/dist/{segments-BqcD5HIl.js → segments-DI3LOQNe.js} +5 -14
  181. package/dist/segments-DI3LOQNe.js.map +1 -0
  182. package/dist/sequence-engine-C6nnewHX.js +2 -0
  183. package/dist/{sequence-engine-J1lTW_in.js → sequence-engine-DNTVLq7o.js} +15 -8
  184. package/dist/sequence-engine-DNTVLq7o.js.map +1 -0
  185. package/dist/{sequence-store-DaaWr0Os.js → sequence-store-CmYb6s0g.js} +6 -5
  186. package/dist/sequence-store-CmYb6s0g.js.map +1 -0
  187. package/dist/{server-Dyva03K8.js → server-DoRPPOeR.js} +308 -230
  188. package/dist/server-DoRPPOeR.js.map +1 -0
  189. package/dist/{session-D9ub6Wl1.js → session-B6XaP83h.js} +3 -3
  190. package/dist/session-B6XaP83h.js.map +1 -0
  191. package/dist/{session-B9AilxOE.js → session-BgGDyP2C.js} +3 -3
  192. package/dist/session-BgGDyP2C.js.map +1 -0
  193. package/dist/session-Bp4zTh4l.js +2 -0
  194. package/dist/{session-D0qFkBla.cjs → session-Mm7GQbSH.cjs} +3 -3
  195. package/dist/session-Mm7GQbSH.cjs.map +1 -0
  196. package/dist/{session-store-C8tEvMPw.js → session-store-DWxJ5Pof.js} +79 -17
  197. package/dist/session-store-DWxJ5Pof.js.map +1 -0
  198. package/dist/{session-store-B0QZE8Bx.cjs → session-store-yfwnj0OC.cjs} +126 -16
  199. package/dist/session-store-yfwnj0OC.cjs.map +1 -0
  200. package/dist/{sla-engine-5IhTsBUR.js → sla-engine-CP2KiKDS.js} +1 -1
  201. package/dist/{sla-engine-BqX-7u-7.js → sla-engine-O-A1ntu_.js} +2 -2
  202. package/dist/{sla-engine-BqX-7u-7.js.map → sla-engine-O-A1ntu_.js.map} +1 -1
  203. package/dist/{sop-Vp0UPWFW.js → sop-BV7ICAFR.js} +4 -11
  204. package/dist/sop-BV7ICAFR.js.map +1 -0
  205. package/dist/{sop-DkhVChGy.js → sop-D33qTHUb.js} +1 -1
  206. package/dist/survey-engine-DKctGcLQ.js +2 -0
  207. package/dist/{survey-engine-DBjCYqCv.js → survey-engine-DngXBv47.js} +5 -4
  208. package/dist/survey-engine-DngXBv47.js.map +1 -0
  209. package/dist/{sync-state-CwLSt_1m.js → sync-state-BaA8LbTI.js} +1 -1
  210. package/dist/{sync-state-ChaLbamC.js → sync-state-DMZgzpez.js} +4 -12
  211. package/dist/sync-state-DMZgzpez.js.map +1 -0
  212. package/dist/{ticket-writer-CjqKeIRD.js → ticket-writer-DsfpeLGZ.js} +1 -1
  213. package/dist/{ticket-writer-j2oX_Wal.js → ticket-writer-a9on36Wb.js} +12 -24
  214. package/dist/ticket-writer-a9on36Wb.js.map +1 -0
  215. package/dist/{tone-Bdm5uaht.js → tone-C7bqK69y.js} +5 -12
  216. package/dist/tone-C7bqK69y.js.map +1 -0
  217. package/dist/{tone-DRKlZgPr.cjs → tone-Cmc7O2Fx.cjs} +3 -9
  218. package/dist/tone-Cmc7O2Fx.cjs.map +1 -0
  219. package/dist/{tone-vNb2DAAD.js → tone-mXSftvTn.js} +3 -8
  220. package/dist/tone-mXSftvTn.js.map +1 -0
  221. package/dist/{transcript-watcher-CL2QUygI.js → transcript-watcher-BoClrJAz.js} +18 -11
  222. package/dist/transcript-watcher-BoClrJAz.js.map +1 -0
  223. package/dist/unmatched-transcripts-C92zAoM4.js +2 -0
  224. package/dist/unmatched-transcripts-DC-VQ9YS.js +16 -0
  225. package/dist/unmatched-transcripts-DC-VQ9YS.js.map +1 -0
  226. package/dist/update-deal-CWy1eLJI.js +2 -0
  227. package/dist/{update-deal-DKC79skb.js → update-deal-DSzr_Aau.js} +3 -3
  228. package/dist/{update-deal-DKC79skb.js.map → update-deal-DSzr_Aau.js.map} +1 -1
  229. package/dist/{usage-D0-TYJkw.js → usage-BVlFlKW_.js} +8 -6
  230. package/dist/usage-BVlFlKW_.js.map +1 -0
  231. package/dist/usage-CClTf5e6.cjs.map +1 -1
  232. package/dist/usage-D0u9a-lV.js.map +1 -1
  233. package/dist/{vault-DXCg29W-.js → vault-CfwZdNzC.js} +3 -4
  234. package/dist/vault-CfwZdNzC.js.map +1 -0
  235. package/dist/{vault-C1D3zScD.js → vault-DxKP4_R2.js} +1 -1
  236. package/dist/{webhooks-Xn6zO6kd.cjs → webhooks-CwW-3kvG.cjs} +5 -19
  237. package/dist/webhooks-CwW-3kvG.cjs.map +1 -0
  238. package/dist/{webhooks-7EpA05Qr.js → webhooks-DXr1IoKn.js} +8 -21
  239. package/dist/webhooks-DXr1IoKn.js.map +1 -0
  240. package/dist/{webhooks-BO2UAnmn.js → webhooks-sWZ8CJtR.js} +5 -18
  241. package/dist/webhooks-sWZ8CJtR.js.map +1 -0
  242. package/package.json +22 -2
  243. package/dist/approvals-DpjxGHFp.js.map +0 -1
  244. package/dist/auth-CyFuu9X_.js +0 -2
  245. package/dist/auth-DFWwWcYD.js.map +0 -1
  246. package/dist/backup-CeMk9z86.js.map +0 -1
  247. package/dist/backup-f_hC7rBV.js +0 -2
  248. package/dist/context-builder-BzWAp3Zs.js.map +0 -1
  249. package/dist/context-builder-DlrRcqmJ.js +0 -2
  250. package/dist/custom-fields-Pl2t9xzp.js.map +0 -1
  251. package/dist/custom-objects-BHgn1GEX.js.map +0 -1
  252. package/dist/customer-dir-DIylZ8Q6.js.map +0 -1
  253. package/dist/file-lock-B_zi7NQl.js.map +0 -1
  254. package/dist/gmail-sync-DIaxInDT.js.map +0 -1
  255. package/dist/gmail-sync-hHm9gaWd.cjs.map +0 -1
  256. package/dist/gmail-sync-rQaVqKWd.js.map +0 -1
  257. package/dist/goal-engine-KpBftn4V.js.map +0 -1
  258. package/dist/identity-gyfWdrcX.js +0 -2
  259. package/dist/import-hubspot-BaK71U_K.js.map +0 -1
  260. package/dist/index-V8BFaH-b.d.ts.map +0 -1
  261. package/dist/index-YqwMd6aQ.d.cts.map +0 -1
  262. package/dist/interactions-writer-CrPStUll.cjs.map +0 -1
  263. package/dist/interactions-writer-DO3KcSR3.js.map +0 -1
  264. package/dist/interactions-writer-SLHnoEeE.js.map +0 -1
  265. package/dist/interactions-writer-dSPy1XfO.js +0 -2
  266. package/dist/knowledge-base-D0Fh40kc.js.map +0 -1
  267. package/dist/lancedb-rlvWoPwl.js.map +0 -1
  268. package/dist/lead-model-BCFzyktm.js.map +0 -1
  269. package/dist/memory-Bb6ky3kb.js.map +0 -1
  270. package/dist/notification-dispatcher-0vYNngWe.js.map +0 -1
  271. package/dist/pipeline-writer-BvVquKIe.js.map +0 -1
  272. package/dist/pipeline-writer-N2omexxp.cjs.map +0 -1
  273. package/dist/pipeline-writer-eufx_0o1.js.map +0 -1
  274. package/dist/proactive-worker-BrLHNhjH.js.map +0 -1
  275. package/dist/push-manager-CdqIIkuh.js.map +0 -1
  276. package/dist/quote-generator-BfwENXzg.js.map +0 -1
  277. package/dist/rbac-C7c8tcES.js +0 -2
  278. package/dist/rbac-CTIktZaC.js.map +0 -1
  279. package/dist/relationship-health-odxEoQdJ.js.map +0 -1
  280. package/dist/revenue-simulation-Bqf2DLVB.js.map +0 -1
  281. package/dist/segments-BqcD5HIl.js.map +0 -1
  282. package/dist/sequence-engine-CCTHEBgi.js +0 -2
  283. package/dist/sequence-engine-J1lTW_in.js.map +0 -1
  284. package/dist/sequence-store-DaaWr0Os.js.map +0 -1
  285. package/dist/server-Dyva03K8.js.map +0 -1
  286. package/dist/session-B9AilxOE.js.map +0 -1
  287. package/dist/session-D0qFkBla.cjs.map +0 -1
  288. package/dist/session-D9ub6Wl1.js.map +0 -1
  289. package/dist/session-mWHA71Lw.js +0 -2
  290. package/dist/session-store-B0QZE8Bx.cjs.map +0 -1
  291. package/dist/session-store-C8tEvMPw.js.map +0 -1
  292. package/dist/sop-Vp0UPWFW.js.map +0 -1
  293. package/dist/survey-engine-C06hcQt3.js +0 -2
  294. package/dist/survey-engine-DBjCYqCv.js.map +0 -1
  295. package/dist/sync-state-ChaLbamC.js.map +0 -1
  296. package/dist/ticket-writer-j2oX_Wal.js.map +0 -1
  297. package/dist/tone-Bdm5uaht.js.map +0 -1
  298. package/dist/tone-DRKlZgPr.cjs.map +0 -1
  299. package/dist/tone-vNb2DAAD.js.map +0 -1
  300. package/dist/transcript-watcher-CL2QUygI.js.map +0 -1
  301. package/dist/unmatched-transcripts-BsH5bhkU.js +0 -26
  302. package/dist/unmatched-transcripts-BsH5bhkU.js.map +0 -1
  303. package/dist/unmatched-transcripts-D0PrJ9iz.js +0 -2
  304. package/dist/update-deal-BNwPGaTV.js +0 -2
  305. package/dist/usage-D0-TYJkw.js.map +0 -1
  306. package/dist/vault-DXCg29W-.js.map +0 -1
  307. package/dist/webhooks-7EpA05Qr.js.map +0 -1
  308. package/dist/webhooks-BO2UAnmn.js.map +0 -1
  309. package/dist/webhooks-Xn6zO6kd.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-lock-CcHotQkZ.js","names":[],"sources":["../src/core/file-lock.ts"],"sourcesContent":["import fs from \"fs\";\nimport { withFileQueue } from \"../fs/write-queue.js\";\nimport { writeFileAtomic } from \"../fs/atomic-write.js\";\n\nexport async function withJsonFile<T>(\n filePath: string,\n updater: (current: T | null) => T | Promise<T>\n): Promise<T> {\n return withFileQueue(filePath, async () => {\n // Read current state\n let current: T | null = null;\n if (fs.existsSync(filePath)) {\n try {\n current = JSON.parse(fs.readFileSync(filePath, \"utf-8\") as string) as T;\n } catch {\n current = null;\n }\n }\n\n // Apply updater — may throw, in which case we do NOT write\n const next = await updater(current);\n\n // Serialized by the queue lock AND crash-safe via temp-file + rename.\n writeFileAtomic(filePath, JSON.stringify(next, null, 2));\n\n return next;\n });\n}\n"],"mappings":";;;;AAIA,eAAsB,aACpB,UACA,SACY;CACZ,OAAO,cAAc,UAAU,YAAY;EAEzC,IAAI,UAAoB;EACxB,IAAI,GAAG,WAAW,QAAQ,GACxB,IAAI;GACF,UAAU,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAW;EACnE,QAAQ;GACN,UAAU;EACZ;EAIF,MAAM,OAAO,MAAM,QAAQ,OAAO;EAGlC,gBAAgB,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;EAEvD,OAAO;CACT,CAAC;AACH"}
@@ -1,6 +1,8 @@
1
+ import { i as writeJsonFile } from "./json-store-WWsFzXub.js";
1
2
  import { t as AgentConfigSchema } from "./agent-config-zPvcqu07.js";
2
- import { r as readInteractions, t as appendInteraction } from "./interactions-writer-SLHnoEeE.js";
3
- import { a as summarizeEmail } from "./llm-DvzZqva0.js";
3
+ import { i as readInteractions, n as appendInteraction } from "./interactions-writer-B8XAzdqR.js";
4
+ import { n as logger } from "./logger-Dyl4VcLO.js";
5
+ import { a as summarizeEmail } from "./llm-PZzgPphl.js";
4
6
  import path from "path";
5
7
  import fs from "fs";
6
8
  import https from "https";
@@ -23,11 +25,10 @@ function readAgentConfig(dataDir, slug) {
23
25
  function writeLastWake(dataDir, slug, config) {
24
26
  const p = agentConfigPath(dataDir, slug);
25
27
  try {
26
- const updated = {
28
+ writeJsonFile(p, {
27
29
  ...config,
28
30
  lastWake: (/* @__PURE__ */ new Date()).toISOString()
29
- };
30
- fs.writeFileSync(p, JSON.stringify(updated, null, 2), "utf-8");
31
+ });
31
32
  } catch {}
32
33
  }
33
34
  function sendTelegramMessage(token, chatId, text) {
@@ -100,6 +101,7 @@ async function syncGmail(opts) {
100
101
  auth: opts.auth
101
102
  });
102
103
  const maxPages = opts.maxPages ?? 5;
104
+ const includeAttachments = opts.includeAttachments ?? true;
103
105
  let q = opts.query;
104
106
  if (opts.since) {
105
107
  const after = Math.floor(opts.since.getTime() / 1e3);
@@ -136,15 +138,20 @@ async function syncGmail(opts) {
136
138
  msgData = (await retryWithBackoff(() => gmail$1.users.messages.get({
137
139
  userId: "me",
138
140
  id: msg.id,
139
- format: "metadata",
140
- metadataHeaders: [
141
- "Subject",
142
- "From",
143
- "Date"
144
- ]
141
+ ...includeAttachments ? { format: "full" } : {
142
+ format: "metadata",
143
+ metadataHeaders: [
144
+ "Subject",
145
+ "From",
146
+ "Date"
147
+ ]
148
+ }
145
149
  }))).data;
146
150
  } catch (err) {
147
- process.stderr.write(`[gmail-sync] Skipping message ${msg.id} after retries: ${err.message}\n`);
151
+ logger.warn("gmail-sync", "skipping message after retries", {
152
+ messageId: msg.id,
153
+ error: err.message
154
+ });
148
155
  skipped++;
149
156
  continue;
150
157
  }
@@ -154,8 +161,29 @@ async function syncGmail(opts) {
154
161
  const dateStr = headers.find((h) => h.name === "Date")?.value;
155
162
  const date = dateStr ? new Date(dateStr).toISOString().slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
156
163
  const snippet = msgData.snippet ?? "";
157
- const { summarizeEmail } = await import("./llm-DEjWcqmW.js");
158
- const emailSummary = await summarizeEmail(subject, snippet, from);
164
+ const { extractEmailBodyMarkdown } = await import("./email-body-BOd7U-D2.js");
165
+ const body = await extractEmailBodyMarkdown(msgData.payload ?? void 0) || snippet;
166
+ const { summarizeEmail } = await import("./llm-DSX1-wFu.js");
167
+ const emailSummary = await summarizeEmail(subject, body, from);
168
+ let attachmentLinks = [];
169
+ if (includeAttachments) try {
170
+ const { processMessageAttachments } = await import("./attachments-rLa96rOK.js");
171
+ attachmentLinks = (await processMessageAttachments({
172
+ gmail: gmail$1,
173
+ dataDir: opts.dataDir,
174
+ slug: opts.slug,
175
+ messageId: msg.id,
176
+ source,
177
+ payload: msgData.payload ?? void 0,
178
+ date,
179
+ ...opts.maxAttachmentBytes !== void 0 ? { maxBytes: opts.maxAttachmentBytes } : {}
180
+ })).map((a) => a.markdownName);
181
+ } catch (err) {
182
+ logger.warn("gmail-sync", "attachment processing failed", {
183
+ messageId: msg.id,
184
+ error: err.message
185
+ });
186
+ }
159
187
  await appendInteraction(opts.dataDir, opts.slug, {
160
188
  date,
161
189
  type: "Email",
@@ -164,17 +192,23 @@ async function syncGmail(opts) {
164
192
  subject,
165
193
  summary: emailSummary.summary,
166
194
  nextSteps: emailSummary.nextSteps,
195
+ ...attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {},
167
196
  sourceRef: source,
168
197
  synced: (/* @__PURE__ */ new Date()).toISOString()
169
198
  });
170
199
  existingContent += source;
171
- const { indexInLanceDB } = await import("./lancedb-CCBbpulq.js");
172
- await indexInLanceDB(opts.dataDir, opts.slug, `${subject}\n${snippet}`, source, {
173
- date,
174
- type: "Email"
175
- }).catch((err) => {
176
- process.stderr.write(`[gmail-sync] LanceDB index failed: ${err.message}\n`);
177
- });
200
+ const { indexInLanceDB } = await import("./lancedb-CswQEE5K.js");
201
+ const { chunkText } = await import("./chunk-ChC83jai.js");
202
+ const bodyChunks = chunkText(`${subject}\n${body}`);
203
+ for (let i = 0; i < bodyChunks.length; i++) {
204
+ const ref = i === 0 ? source : `${source}#${i}`;
205
+ await indexInLanceDB(opts.dataDir, opts.slug, bodyChunks[i], ref, {
206
+ date,
207
+ type: "Email"
208
+ }).catch((err) => {
209
+ logger.error("gmail-sync", "LanceDB index failed", { error: err.message });
210
+ });
211
+ }
178
212
  if (agentConfigExists(opts.dataDir, opts.slug)) notifyAgentWake(opts.dataDir, opts.slug, {
179
213
  trigger: "email",
180
214
  subject,
@@ -201,4 +235,4 @@ function sleep(ms) {
201
235
  //#endregion
202
236
  export { syncGmail };
203
237
 
204
- //# sourceMappingURL=gmail-sync-DIaxInDT.js.map
238
+ //# sourceMappingURL=gmail-sync-B4Iu3AQb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail-sync-B4Iu3AQb.js","names":["gmail","gmailApi"],"sources":["../src/core/agent-notifier.ts","../src/sync/gmail-sync.ts"],"sourcesContent":["// src/core/agent-notifier.ts\n// Sends a Telegram wake notification when a new inbound email from a customer\n// domain is detected and an agent config exists for that customer slug.\n// All errors are swallowed — this is a notification feature and must never\n// crash the core loop.\n\nimport fs from \"fs\";\nimport https from \"https\";\nimport path from \"path\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\nimport { AgentConfigSchema, type AgentConfig } from \"../schemas/agent-config.js\";\nimport { summarizeEmail } from \"./llm.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface WakeContext {\n trigger: \"email\" | \"calendar\";\n subject: string;\n from: string;\n snippet: string;\n}\n\n// ─── Agent config helpers ─────────────────────────────────────────────────────\n\nfunction agentConfigPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \".agentic\", \"agents\", `${slug}.agent.json`);\n}\n\nfunction readAgentConfig(dataDir: string, slug: string): AgentConfig | null {\n const p = agentConfigPath(dataDir, slug);\n if (!fs.existsSync(p)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n const result = AgentConfigSchema.safeParse(raw);\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\nfunction writeLastWake(dataDir: string, slug: string, config: AgentConfig): void {\n const p = agentConfigPath(dataDir, slug);\n try {\n const updated: AgentConfig = { ...config, lastWake: new Date().toISOString() };\n writeJsonFile(p, updated);\n } catch {\n // non-fatal — just a housekeeping write\n }\n}\n\n// ─── Telegram transport ───────────────────────────────────────────────────────\n\nfunction sendTelegramMessage(token: string, chatId: string, text: string): Promise<void> {\n const body = JSON.stringify({ chat_id: chatId, text, parse_mode: \"Markdown\" });\n return 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\n// ─── Message builder ──────────────────────────────────────────────────────────\n\nfunction buildWakeMessage(\n slug: string,\n subject: string,\n summary: string,\n nextSteps: string[]\n): string {\n const suggestedAction = nextSteps[0] ?? \"Follow up within 24h\";\n return (\n `📧 New email from **${slug}**: ${subject}\\n` +\n `${summary}\\n\\n` +\n `💡 Suggested action: ${suggestedAction}`\n );\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Fire-and-forget notification: reads the agent config for `slug`, summarises\n * the inbound email with the LLM, and sends a Telegram message.\n *\n * Silently returns (no throw) when:\n * - no agent config exists for the slug\n * - TELEGRAM_BOT_TOKEN env var is not set\n * - no chat id is available (neither in config nor in TELEGRAM_CHAT_ID env var)\n * - any HTTPS / LLM error occurs\n */\nexport async function notifyAgentWake(\n dataDir: string,\n slug: string,\n context: WakeContext\n): Promise<void> {\n try {\n // 1. Read agent config — bail silently if not found\n const config = readAgentConfig(dataDir, slug);\n if (!config) return;\n\n // 2. Check for Telegram token — bail silently if absent\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n if (!token) return;\n\n // 3. Determine chat id — config takes precedence, fallback to env var\n const chatId = config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"];\n if (!chatId) return;\n\n // 4. Summarise the email (LLM, with fallback built into summarizeEmail itself)\n const emailSummary = await summarizeEmail(context.subject, context.snippet, context.from);\n\n // 5. Build and send the Telegram message\n const text = buildWakeMessage(\n slug,\n context.subject,\n emailSummary.summary,\n emailSummary.nextSteps\n );\n await sendTelegramMessage(token, chatId, text);\n\n // 6. Update lastWake on success\n writeLastWake(dataDir, slug, config);\n } catch {\n // Swallow all errors — this is a notification feature, never crashes core loop\n }\n}\n","// src/sync/gmail-sync.ts\nimport fs from \"fs\";\nimport path from \"path\";\nimport { gmail as gmailApi, type gmail_v1 } from \"@googleapis/gmail\";\nimport type { OAuth2Client } from \"google-auth-library\";\nimport { readInteractions, appendInteraction } from \"../fs/interactions-writer.js\";\nimport { notifyAgentWake } from \"../core/agent-notifier.js\";\nimport { logger } from \"../core/logger.js\";\n\ninterface SyncOptions {\n slug: string;\n dataDir: string;\n auth: OAuth2Client;\n query: string;\n since?: Date;\n maxPages?: number;\n /** Download, convert and index email attachments (default true). */\n includeAttachments?: boolean;\n /** Per-attachment size cap in bytes. */\n maxAttachmentBytes?: number;\n}\n\n/**\n * Retry a function with exponential backoff on any error.\n * Delays: 1s, 2s, 4s, 8s … (2^attempt seconds), up to maxRetries retries.\n */\nexport async function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {\n let attempt = 0;\n while (true) {\n try {\n return await fn();\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n const delayMs = 1000 * Math.pow(2, attempt);\n await sleep(delayMs);\n attempt++;\n }\n }\n}\n\nexport async function syncGmail(opts: SyncOptions): Promise<{ synced: number; skipped: number }> {\n const gmail = gmailApi({ version: \"v1\", auth: opts.auth });\n const maxPages = opts.maxPages ?? 5;\n const includeAttachments = opts.includeAttachments ?? true;\n\n let q = opts.query;\n if (opts.since) {\n const after = Math.floor(opts.since.getTime() / 1000);\n q += ` after:${after}`;\n }\n\n // Collect all message stubs across pages (Task A — pagination)\n const allMessages: Array<{ id?: string | null; threadId?: string | null }> = [];\n let pageToken: string | undefined = undefined;\n let pagesFetched = 0;\n\n do {\n const listResp: { data: gmail_v1.Schema$ListMessagesResponse } =\n await gmail.users.messages.list({\n userId: \"me\",\n q,\n maxResults: 200,\n ...(pageToken ? { pageToken } : {}),\n });\n const pageMessages = listResp.data.messages ?? [];\n allMessages.push(...pageMessages);\n pageToken = listResp.data.nextPageToken ?? undefined;\n pagesFetched++;\n } while (pageToken && pagesFetched < maxPages);\n\n // Read existing interactions once before the loop — avoids O(messages) file reads\n let existingContent = await readInteractions(opts.dataDir, opts.slug);\n\n let synced = 0;\n let skipped = 0;\n\n for (const msg of allMessages) {\n if (!msg.id) continue;\n\n const source = `gmail://thread/${msg.threadId ?? msg.id}`;\n\n if (existingContent.includes(source)) {\n skipped++;\n continue;\n }\n\n // Rate limiting ~10 req/s\n await sleep(100);\n\n // Task B — exponential backoff retry on any error\n let msgData: gmail_v1.Schema$Message;\n try {\n const detail = await retryWithBackoff(() =>\n gmail.users.messages.get({\n userId: \"me\",\n id: msg.id!,\n // \"full\" exposes payload.parts so attachments can be downloaded;\n // fall back to lighter \"metadata\" when attachment sync is disabled.\n ...(includeAttachments\n ? { format: \"full\" }\n : { format: \"metadata\", metadataHeaders: [\"Subject\", \"From\", \"Date\"] }),\n })\n );\n msgData = detail.data;\n } catch (err) {\n logger.warn(\"gmail-sync\", \"skipping message after retries\", {\n messageId: msg.id,\n error: (err as Error).message,\n });\n skipped++;\n continue;\n }\n\n const headers = msgData.payload?.headers ?? [];\n const subject = headers.find((h) => h.name === \"Subject\")?.value ?? \"(no subject)\";\n const from = headers.find((h) => h.name === \"From\")?.value ?? \"\";\n const dateStr = headers.find((h) => h.name === \"Date\")?.value;\n const date = dateStr\n ? new Date(dateStr).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10);\n const snippet = msgData.snippet ?? \"\";\n\n // Extract the full inline body (plain preferred, else HTML->Markdown) so\n // summaries and search cover the whole message, not just the snippet.\n const { extractEmailBodyMarkdown } = await import(\"./email-body.js\");\n const body = (await extractEmailBodyMarkdown(msgData.payload ?? undefined)) || snippet;\n\n // LLM summary — non-blocking fallback to raw body/snippet if no API key or error\n const { summarizeEmail } = await import(\"../core/llm.js\");\n const emailSummary = await summarizeEmail(subject, body, from);\n\n // Download, convert and index attachments before logging the interaction so\n // the entry can link to the generated Markdown. Failures here are swallowed.\n let attachmentLinks: string[] = [];\n if (includeAttachments) {\n try {\n const { processMessageAttachments } = await import(\"./attachments.js\");\n const saved = await processMessageAttachments({\n gmail,\n dataDir: opts.dataDir,\n slug: opts.slug,\n messageId: msg.id,\n source,\n payload: msgData.payload ?? undefined,\n date,\n ...(opts.maxAttachmentBytes !== undefined\n ? { maxBytes: opts.maxAttachmentBytes }\n : {}),\n });\n attachmentLinks = saved.map((a) => a.markdownName);\n } catch (err) {\n logger.warn(\"gmail-sync\", \"attachment processing failed\", {\n messageId: msg.id,\n error: (err as Error).message,\n });\n }\n }\n\n await appendInteraction(opts.dataDir, opts.slug, {\n date,\n type: \"Email\",\n direction: detectDirection(from),\n with: from,\n subject,\n summary: emailSummary.summary,\n nextSteps: emailSummary.nextSteps,\n ...(attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {}),\n sourceRef: source,\n synced: new Date().toISOString(),\n });\n\n // Append to in-memory string so within-batch duplicates are detected\n existingContent += source;\n\n // Index the full email (subject + body) into LanceDB for semantic search,\n // chunked so long threads stay searchable (non-blocking).\n const { indexInLanceDB } = await import(\"../core/lancedb.js\");\n const { chunkText } = await import(\"../core/chunk.js\");\n const bodyChunks = chunkText(`${subject}\\n${body}`);\n for (let i = 0; i < bodyChunks.length; i++) {\n const ref = i === 0 ? source : `${source}#${i}`;\n await indexInLanceDB(opts.dataDir, opts.slug, bodyChunks[i]!, ref, {\n date,\n type: \"Email\",\n }).catch((err: unknown) => {\n logger.error(\"gmail-sync\", \"LanceDB index failed\", { error: (err as Error).message });\n });\n }\n\n // Agent wake: notify if an agent config exists for this customer (fire-and-forget)\n if (agentConfigExists(opts.dataDir, opts.slug)) {\n notifyAgentWake(opts.dataDir, opts.slug, {\n trigger: \"email\",\n subject,\n from,\n snippet,\n }).catch(() => {\n // Notification is non-blocking; swallow all errors\n });\n }\n\n synced++;\n }\n\n return { synced, skipped };\n}\n\nfunction agentConfigExists(dataDir: string, slug: string): boolean {\n const configPath = path.join(dataDir, \".agentic\", \"agents\", `${slug}.agent.json`);\n return fs.existsSync(configPath);\n}\n\nfunction detectDirection(_from: string): \"inbound\" | \"outbound\" {\n return \"inbound\";\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
1
  const require_chunk = require("./chunk-DakpK96I.cjs");
2
- const require_interactions_writer = require("./interactions-writer-CrPStUll.cjs");
3
- const require_llm = require("./llm-iijeXmgq.cjs");
2
+ const require_session_store = require("./session-store-yfwnj0OC.cjs");
3
+ const require_interactions_writer = require("./interactions-writer-BRJNrefF.cjs");
4
+ const require_logger = require("./logger-BkInaGoV.cjs");
5
+ const require_llm = require("./llm-CXycmEl9.cjs");
4
6
  let path = require("path");
5
7
  path = require_chunk.__toESM(path, 1);
6
8
  let fs = require("fs");
@@ -37,11 +39,10 @@ function readAgentConfig(dataDir, slug) {
37
39
  function writeLastWake(dataDir, slug, config) {
38
40
  const p = agentConfigPath(dataDir, slug);
39
41
  try {
40
- const updated = {
42
+ require_session_store.writeJsonFile(p, {
41
43
  ...config,
42
44
  lastWake: (/* @__PURE__ */ new Date()).toISOString()
43
- };
44
- fs.default.writeFileSync(p, JSON.stringify(updated, null, 2), "utf-8");
45
+ });
45
46
  } catch {}
46
47
  }
47
48
  function sendTelegramMessage(token, chatId, text) {
@@ -114,6 +115,7 @@ async function syncGmail(opts) {
114
115
  auth: opts.auth
115
116
  });
116
117
  const maxPages = opts.maxPages ?? 5;
118
+ const includeAttachments = opts.includeAttachments ?? true;
117
119
  let q = opts.query;
118
120
  if (opts.since) {
119
121
  const after = Math.floor(opts.since.getTime() / 1e3);
@@ -150,15 +152,20 @@ async function syncGmail(opts) {
150
152
  msgData = (await retryWithBackoff(() => gmail.users.messages.get({
151
153
  userId: "me",
152
154
  id: msg.id,
153
- format: "metadata",
154
- metadataHeaders: [
155
- "Subject",
156
- "From",
157
- "Date"
158
- ]
155
+ ...includeAttachments ? { format: "full" } : {
156
+ format: "metadata",
157
+ metadataHeaders: [
158
+ "Subject",
159
+ "From",
160
+ "Date"
161
+ ]
162
+ }
159
163
  }))).data;
160
164
  } catch (err) {
161
- process.stderr.write(`[gmail-sync] Skipping message ${msg.id} after retries: ${err.message}\n`);
165
+ require_logger.logger.warn("gmail-sync", "skipping message after retries", {
166
+ messageId: msg.id,
167
+ error: err.message
168
+ });
162
169
  skipped++;
163
170
  continue;
164
171
  }
@@ -168,8 +175,29 @@ async function syncGmail(opts) {
168
175
  const dateStr = headers.find((h) => h.name === "Date")?.value;
169
176
  const date = dateStr ? new Date(dateStr).toISOString().slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
170
177
  const snippet = msgData.snippet ?? "";
171
- const { summarizeEmail } = await Promise.resolve().then(() => require("./llm-iijeXmgq.cjs")).then((n) => n.llm_exports);
172
- const emailSummary = await summarizeEmail(subject, snippet, from);
178
+ const { extractEmailBodyMarkdown } = await Promise.resolve().then(() => require("./email-body-BFSRa0AW.cjs"));
179
+ const body = await extractEmailBodyMarkdown(msgData.payload ?? void 0) || snippet;
180
+ const { summarizeEmail } = await Promise.resolve().then(() => require("./llm-CXycmEl9.cjs")).then((n) => n.llm_exports);
181
+ const emailSummary = await summarizeEmail(subject, body, from);
182
+ let attachmentLinks = [];
183
+ if (includeAttachments) try {
184
+ const { processMessageAttachments } = await Promise.resolve().then(() => require("./attachments-CX2GAtsw.cjs"));
185
+ attachmentLinks = (await processMessageAttachments({
186
+ gmail,
187
+ dataDir: opts.dataDir,
188
+ slug: opts.slug,
189
+ messageId: msg.id,
190
+ source,
191
+ payload: msgData.payload ?? void 0,
192
+ date,
193
+ ...opts.maxAttachmentBytes !== void 0 ? { maxBytes: opts.maxAttachmentBytes } : {}
194
+ })).map((a) => a.markdownName);
195
+ } catch (err) {
196
+ require_logger.logger.warn("gmail-sync", "attachment processing failed", {
197
+ messageId: msg.id,
198
+ error: err.message
199
+ });
200
+ }
173
201
  await require_interactions_writer.appendInteraction(opts.dataDir, opts.slug, {
174
202
  date,
175
203
  type: "Email",
@@ -178,17 +206,23 @@ async function syncGmail(opts) {
178
206
  subject,
179
207
  summary: emailSummary.summary,
180
208
  nextSteps: emailSummary.nextSteps,
209
+ ...attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {},
181
210
  sourceRef: source,
182
211
  synced: (/* @__PURE__ */ new Date()).toISOString()
183
212
  });
184
213
  existingContent += source;
185
214
  const { indexInLanceDB } = await Promise.resolve().then(() => require("./mcp.cjs")).then((n) => n.lancedb_exports);
186
- await indexInLanceDB(opts.dataDir, opts.slug, `${subject}\n${snippet}`, source, {
187
- date,
188
- type: "Email"
189
- }).catch((err) => {
190
- process.stderr.write(`[gmail-sync] LanceDB index failed: ${err.message}\n`);
191
- });
215
+ const { chunkText } = await Promise.resolve().then(() => require("./chunk-BfDYWZQ8.cjs"));
216
+ const bodyChunks = chunkText(`${subject}\n${body}`);
217
+ for (let i = 0; i < bodyChunks.length; i++) {
218
+ const ref = i === 0 ? source : `${source}#${i}`;
219
+ await indexInLanceDB(opts.dataDir, opts.slug, bodyChunks[i], ref, {
220
+ date,
221
+ type: "Email"
222
+ }).catch((err) => {
223
+ require_logger.logger.error("gmail-sync", "LanceDB index failed", { error: err.message });
224
+ });
225
+ }
192
226
  if (agentConfigExists(opts.dataDir, opts.slug)) notifyAgentWake(opts.dataDir, opts.slug, {
193
227
  trigger: "email",
194
228
  subject,
@@ -215,4 +249,4 @@ function sleep(ms) {
215
249
  //#endregion
216
250
  exports.syncGmail = syncGmail;
217
251
 
218
- //# sourceMappingURL=gmail-sync-hHm9gaWd.cjs.map
252
+ //# sourceMappingURL=gmail-sync-BpSVESSe.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail-sync-BpSVESSe.cjs","names":["z","summarizeEmail","readInteractions","appendInteraction"],"sources":["../src/schemas/agent-config.ts","../src/core/agent-notifier.ts","../src/sync/gmail-sync.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","// src/core/agent-notifier.ts\n// Sends a Telegram wake notification when a new inbound email from a customer\n// domain is detected and an agent config exists for that customer slug.\n// All errors are swallowed — this is a notification feature and must never\n// crash the core loop.\n\nimport fs from \"fs\";\nimport https from \"https\";\nimport path from \"path\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\nimport { AgentConfigSchema, type AgentConfig } from \"../schemas/agent-config.js\";\nimport { summarizeEmail } from \"./llm.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface WakeContext {\n trigger: \"email\" | \"calendar\";\n subject: string;\n from: string;\n snippet: string;\n}\n\n// ─── Agent config helpers ─────────────────────────────────────────────────────\n\nfunction agentConfigPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \".agentic\", \"agents\", `${slug}.agent.json`);\n}\n\nfunction readAgentConfig(dataDir: string, slug: string): AgentConfig | null {\n const p = agentConfigPath(dataDir, slug);\n if (!fs.existsSync(p)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n const result = AgentConfigSchema.safeParse(raw);\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\nfunction writeLastWake(dataDir: string, slug: string, config: AgentConfig): void {\n const p = agentConfigPath(dataDir, slug);\n try {\n const updated: AgentConfig = { ...config, lastWake: new Date().toISOString() };\n writeJsonFile(p, updated);\n } catch {\n // non-fatal — just a housekeeping write\n }\n}\n\n// ─── Telegram transport ───────────────────────────────────────────────────────\n\nfunction sendTelegramMessage(token: string, chatId: string, text: string): Promise<void> {\n const body = JSON.stringify({ chat_id: chatId, text, parse_mode: \"Markdown\" });\n return 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\n// ─── Message builder ──────────────────────────────────────────────────────────\n\nfunction buildWakeMessage(\n slug: string,\n subject: string,\n summary: string,\n nextSteps: string[]\n): string {\n const suggestedAction = nextSteps[0] ?? \"Follow up within 24h\";\n return (\n `📧 New email from **${slug}**: ${subject}\\n` +\n `${summary}\\n\\n` +\n `💡 Suggested action: ${suggestedAction}`\n );\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Fire-and-forget notification: reads the agent config for `slug`, summarises\n * the inbound email with the LLM, and sends a Telegram message.\n *\n * Silently returns (no throw) when:\n * - no agent config exists for the slug\n * - TELEGRAM_BOT_TOKEN env var is not set\n * - no chat id is available (neither in config nor in TELEGRAM_CHAT_ID env var)\n * - any HTTPS / LLM error occurs\n */\nexport async function notifyAgentWake(\n dataDir: string,\n slug: string,\n context: WakeContext\n): Promise<void> {\n try {\n // 1. Read agent config — bail silently if not found\n const config = readAgentConfig(dataDir, slug);\n if (!config) return;\n\n // 2. Check for Telegram token — bail silently if absent\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n if (!token) return;\n\n // 3. Determine chat id — config takes precedence, fallback to env var\n const chatId = config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"];\n if (!chatId) return;\n\n // 4. Summarise the email (LLM, with fallback built into summarizeEmail itself)\n const emailSummary = await summarizeEmail(context.subject, context.snippet, context.from);\n\n // 5. Build and send the Telegram message\n const text = buildWakeMessage(\n slug,\n context.subject,\n emailSummary.summary,\n emailSummary.nextSteps\n );\n await sendTelegramMessage(token, chatId, text);\n\n // 6. Update lastWake on success\n writeLastWake(dataDir, slug, config);\n } catch {\n // Swallow all errors — this is a notification feature, never crashes core loop\n }\n}\n","// src/sync/gmail-sync.ts\nimport fs from \"fs\";\nimport path from \"path\";\nimport { gmail as gmailApi, type gmail_v1 } from \"@googleapis/gmail\";\nimport type { OAuth2Client } from \"google-auth-library\";\nimport { readInteractions, appendInteraction } from \"../fs/interactions-writer.js\";\nimport { notifyAgentWake } from \"../core/agent-notifier.js\";\nimport { logger } from \"../core/logger.js\";\n\ninterface SyncOptions {\n slug: string;\n dataDir: string;\n auth: OAuth2Client;\n query: string;\n since?: Date;\n maxPages?: number;\n /** Download, convert and index email attachments (default true). */\n includeAttachments?: boolean;\n /** Per-attachment size cap in bytes. */\n maxAttachmentBytes?: number;\n}\n\n/**\n * Retry a function with exponential backoff on any error.\n * Delays: 1s, 2s, 4s, 8s … (2^attempt seconds), up to maxRetries retries.\n */\nexport async function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {\n let attempt = 0;\n while (true) {\n try {\n return await fn();\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n const delayMs = 1000 * Math.pow(2, attempt);\n await sleep(delayMs);\n attempt++;\n }\n }\n}\n\nexport async function syncGmail(opts: SyncOptions): Promise<{ synced: number; skipped: number }> {\n const gmail = gmailApi({ version: \"v1\", auth: opts.auth });\n const maxPages = opts.maxPages ?? 5;\n const includeAttachments = opts.includeAttachments ?? true;\n\n let q = opts.query;\n if (opts.since) {\n const after = Math.floor(opts.since.getTime() / 1000);\n q += ` after:${after}`;\n }\n\n // Collect all message stubs across pages (Task A — pagination)\n const allMessages: Array<{ id?: string | null; threadId?: string | null }> = [];\n let pageToken: string | undefined = undefined;\n let pagesFetched = 0;\n\n do {\n const listResp: { data: gmail_v1.Schema$ListMessagesResponse } =\n await gmail.users.messages.list({\n userId: \"me\",\n q,\n maxResults: 200,\n ...(pageToken ? { pageToken } : {}),\n });\n const pageMessages = listResp.data.messages ?? [];\n allMessages.push(...pageMessages);\n pageToken = listResp.data.nextPageToken ?? undefined;\n pagesFetched++;\n } while (pageToken && pagesFetched < maxPages);\n\n // Read existing interactions once before the loop — avoids O(messages) file reads\n let existingContent = await readInteractions(opts.dataDir, opts.slug);\n\n let synced = 0;\n let skipped = 0;\n\n for (const msg of allMessages) {\n if (!msg.id) continue;\n\n const source = `gmail://thread/${msg.threadId ?? msg.id}`;\n\n if (existingContent.includes(source)) {\n skipped++;\n continue;\n }\n\n // Rate limiting ~10 req/s\n await sleep(100);\n\n // Task B — exponential backoff retry on any error\n let msgData: gmail_v1.Schema$Message;\n try {\n const detail = await retryWithBackoff(() =>\n gmail.users.messages.get({\n userId: \"me\",\n id: msg.id!,\n // \"full\" exposes payload.parts so attachments can be downloaded;\n // fall back to lighter \"metadata\" when attachment sync is disabled.\n ...(includeAttachments\n ? { format: \"full\" }\n : { format: \"metadata\", metadataHeaders: [\"Subject\", \"From\", \"Date\"] }),\n })\n );\n msgData = detail.data;\n } catch (err) {\n logger.warn(\"gmail-sync\", \"skipping message after retries\", {\n messageId: msg.id,\n error: (err as Error).message,\n });\n skipped++;\n continue;\n }\n\n const headers = msgData.payload?.headers ?? [];\n const subject = headers.find((h) => h.name === \"Subject\")?.value ?? \"(no subject)\";\n const from = headers.find((h) => h.name === \"From\")?.value ?? \"\";\n const dateStr = headers.find((h) => h.name === \"Date\")?.value;\n const date = dateStr\n ? new Date(dateStr).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10);\n const snippet = msgData.snippet ?? \"\";\n\n // Extract the full inline body (plain preferred, else HTML->Markdown) so\n // summaries and search cover the whole message, not just the snippet.\n const { extractEmailBodyMarkdown } = await import(\"./email-body.js\");\n const body = (await extractEmailBodyMarkdown(msgData.payload ?? undefined)) || snippet;\n\n // LLM summary — non-blocking fallback to raw body/snippet if no API key or error\n const { summarizeEmail } = await import(\"../core/llm.js\");\n const emailSummary = await summarizeEmail(subject, body, from);\n\n // Download, convert and index attachments before logging the interaction so\n // the entry can link to the generated Markdown. Failures here are swallowed.\n let attachmentLinks: string[] = [];\n if (includeAttachments) {\n try {\n const { processMessageAttachments } = await import(\"./attachments.js\");\n const saved = await processMessageAttachments({\n gmail,\n dataDir: opts.dataDir,\n slug: opts.slug,\n messageId: msg.id,\n source,\n payload: msgData.payload ?? undefined,\n date,\n ...(opts.maxAttachmentBytes !== undefined\n ? { maxBytes: opts.maxAttachmentBytes }\n : {}),\n });\n attachmentLinks = saved.map((a) => a.markdownName);\n } catch (err) {\n logger.warn(\"gmail-sync\", \"attachment processing failed\", {\n messageId: msg.id,\n error: (err as Error).message,\n });\n }\n }\n\n await appendInteraction(opts.dataDir, opts.slug, {\n date,\n type: \"Email\",\n direction: detectDirection(from),\n with: from,\n subject,\n summary: emailSummary.summary,\n nextSteps: emailSummary.nextSteps,\n ...(attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {}),\n sourceRef: source,\n synced: new Date().toISOString(),\n });\n\n // Append to in-memory string so within-batch duplicates are detected\n existingContent += source;\n\n // Index the full email (subject + body) into LanceDB for semantic search,\n // chunked so long threads stay searchable (non-blocking).\n const { indexInLanceDB } = await import(\"../core/lancedb.js\");\n const { chunkText } = await import(\"../core/chunk.js\");\n const bodyChunks = chunkText(`${subject}\\n${body}`);\n for (let i = 0; i < bodyChunks.length; i++) {\n const ref = i === 0 ? source : `${source}#${i}`;\n await indexInLanceDB(opts.dataDir, opts.slug, bodyChunks[i]!, ref, {\n date,\n type: \"Email\",\n }).catch((err: unknown) => {\n logger.error(\"gmail-sync\", \"LanceDB index failed\", { error: (err as Error).message });\n });\n }\n\n // Agent wake: notify if an agent config exists for this customer (fire-and-forget)\n if (agentConfigExists(opts.dataDir, opts.slug)) {\n notifyAgentWake(opts.dataDir, opts.slug, {\n trigger: \"email\",\n subject,\n from,\n snippet,\n }).catch(() => {\n // Notification is non-blocking; swallow all errors\n });\n }\n\n synced++;\n }\n\n return { synced, skipped };\n}\n\nfunction agentConfigExists(dataDir: string, slug: string): boolean {\n const configPath = path.join(dataDir, \".agentic\", \"agents\", `${slug}.agent.json`);\n return fs.existsSync(configPath);\n}\n\nfunction detectDirection(_from: string): \"inbound\" | \"outbound\" {\n return \"inbound\";\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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"}
@@ -1,5 +1,7 @@
1
- import { i as readInteractions, t as appendInteraction } from "./interactions-writer-DO3KcSR3.js";
2
- import { r as summarizeEmail } from "./llm-Z8RIYkpF.js";
1
+ import { w as writeJsonFile } from "./session-store-DWxJ5Pof.js";
2
+ import { i as readInteractions, t as appendInteraction } from "./interactions-writer-ZQcpFOh9.js";
3
+ import { t as logger } from "./logger-UaF5p9d1.js";
4
+ import { r as summarizeEmail } from "./llm-BnSUBisu.js";
3
5
  import path from "path";
4
6
  import fs from "fs";
5
7
  import { z } from "zod";
@@ -33,11 +35,10 @@ function readAgentConfig(dataDir, slug) {
33
35
  function writeLastWake(dataDir, slug, config) {
34
36
  const p = agentConfigPath(dataDir, slug);
35
37
  try {
36
- const updated = {
38
+ writeJsonFile(p, {
37
39
  ...config,
38
40
  lastWake: (/* @__PURE__ */ new Date()).toISOString()
39
- };
40
- fs.writeFileSync(p, JSON.stringify(updated, null, 2), "utf-8");
41
+ });
41
42
  } catch {}
42
43
  }
43
44
  function sendTelegramMessage(token, chatId, text) {
@@ -110,6 +111,7 @@ async function syncGmail(opts) {
110
111
  auth: opts.auth
111
112
  });
112
113
  const maxPages = opts.maxPages ?? 5;
114
+ const includeAttachments = opts.includeAttachments ?? true;
113
115
  let q = opts.query;
114
116
  if (opts.since) {
115
117
  const after = Math.floor(opts.since.getTime() / 1e3);
@@ -146,15 +148,20 @@ async function syncGmail(opts) {
146
148
  msgData = (await retryWithBackoff(() => gmail$1.users.messages.get({
147
149
  userId: "me",
148
150
  id: msg.id,
149
- format: "metadata",
150
- metadataHeaders: [
151
- "Subject",
152
- "From",
153
- "Date"
154
- ]
151
+ ...includeAttachments ? { format: "full" } : {
152
+ format: "metadata",
153
+ metadataHeaders: [
154
+ "Subject",
155
+ "From",
156
+ "Date"
157
+ ]
158
+ }
155
159
  }))).data;
156
160
  } catch (err) {
157
- process.stderr.write(`[gmail-sync] Skipping message ${msg.id} after retries: ${err.message}\n`);
161
+ logger.warn("gmail-sync", "skipping message after retries", {
162
+ messageId: msg.id,
163
+ error: err.message
164
+ });
158
165
  skipped++;
159
166
  continue;
160
167
  }
@@ -164,8 +171,29 @@ async function syncGmail(opts) {
164
171
  const dateStr = headers.find((h) => h.name === "Date")?.value;
165
172
  const date = dateStr ? new Date(dateStr).toISOString().slice(0, 10) : (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
166
173
  const snippet = msgData.snippet ?? "";
167
- const { summarizeEmail } = await import("./llm-Z8RIYkpF.js").then((n) => n.n);
168
- const emailSummary = await summarizeEmail(subject, snippet, from);
174
+ const { extractEmailBodyMarkdown } = await import("./email-body-BOd7U-D2.js");
175
+ const body = await extractEmailBodyMarkdown(msgData.payload ?? void 0) || snippet;
176
+ const { summarizeEmail } = await import("./llm-BnSUBisu.js").then((n) => n.n);
177
+ const emailSummary = await summarizeEmail(subject, body, from);
178
+ let attachmentLinks = [];
179
+ if (includeAttachments) try {
180
+ const { processMessageAttachments } = await import("./attachments-D207gXfN.js");
181
+ attachmentLinks = (await processMessageAttachments({
182
+ gmail: gmail$1,
183
+ dataDir: opts.dataDir,
184
+ slug: opts.slug,
185
+ messageId: msg.id,
186
+ source,
187
+ payload: msgData.payload ?? void 0,
188
+ date,
189
+ ...opts.maxAttachmentBytes !== void 0 ? { maxBytes: opts.maxAttachmentBytes } : {}
190
+ })).map((a) => a.markdownName);
191
+ } catch (err) {
192
+ logger.warn("gmail-sync", "attachment processing failed", {
193
+ messageId: msg.id,
194
+ error: err.message
195
+ });
196
+ }
169
197
  await appendInteraction(opts.dataDir, opts.slug, {
170
198
  date,
171
199
  type: "Email",
@@ -174,17 +202,23 @@ async function syncGmail(opts) {
174
202
  subject,
175
203
  summary: emailSummary.summary,
176
204
  nextSteps: emailSummary.nextSteps,
205
+ ...attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {},
177
206
  sourceRef: source,
178
207
  synced: (/* @__PURE__ */ new Date()).toISOString()
179
208
  });
180
209
  existingContent += source;
181
210
  const { indexInLanceDB } = await import("./mcp.js").then((n) => n.t);
182
- await indexInLanceDB(opts.dataDir, opts.slug, `${subject}\n${snippet}`, source, {
183
- date,
184
- type: "Email"
185
- }).catch((err) => {
186
- process.stderr.write(`[gmail-sync] LanceDB index failed: ${err.message}\n`);
187
- });
211
+ const { chunkText } = await import("./chunk-e_w8qqtP.js");
212
+ const bodyChunks = chunkText(`${subject}\n${body}`);
213
+ for (let i = 0; i < bodyChunks.length; i++) {
214
+ const ref = i === 0 ? source : `${source}#${i}`;
215
+ await indexInLanceDB(opts.dataDir, opts.slug, bodyChunks[i], ref, {
216
+ date,
217
+ type: "Email"
218
+ }).catch((err) => {
219
+ logger.error("gmail-sync", "LanceDB index failed", { error: err.message });
220
+ });
221
+ }
188
222
  if (agentConfigExists(opts.dataDir, opts.slug)) notifyAgentWake(opts.dataDir, opts.slug, {
189
223
  trigger: "email",
190
224
  subject,
@@ -211,4 +245,4 @@ function sleep(ms) {
211
245
  //#endregion
212
246
  export { syncGmail };
213
247
 
214
- //# sourceMappingURL=gmail-sync-rQaVqKWd.js.map
248
+ //# sourceMappingURL=gmail-sync-DIbrPnTK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gmail-sync-DIbrPnTK.js","names":["gmail","gmailApi"],"sources":["../src/schemas/agent-config.ts","../src/core/agent-notifier.ts","../src/sync/gmail-sync.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","// src/core/agent-notifier.ts\n// Sends a Telegram wake notification when a new inbound email from a customer\n// domain is detected and an agent config exists for that customer slug.\n// All errors are swallowed — this is a notification feature and must never\n// crash the core loop.\n\nimport fs from \"fs\";\nimport https from \"https\";\nimport path from \"path\";\nimport { writeJsonFile } from \"../fs/json-store.js\";\nimport { AgentConfigSchema, type AgentConfig } from \"../schemas/agent-config.js\";\nimport { summarizeEmail } from \"./llm.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface WakeContext {\n trigger: \"email\" | \"calendar\";\n subject: string;\n from: string;\n snippet: string;\n}\n\n// ─── Agent config helpers ─────────────────────────────────────────────────────\n\nfunction agentConfigPath(dataDir: string, slug: string): string {\n return path.join(dataDir, \".agentic\", \"agents\", `${slug}.agent.json`);\n}\n\nfunction readAgentConfig(dataDir: string, slug: string): AgentConfig | null {\n const p = agentConfigPath(dataDir, slug);\n if (!fs.existsSync(p)) return null;\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n const result = AgentConfigSchema.safeParse(raw);\n return result.success ? result.data : null;\n } catch {\n return null;\n }\n}\n\nfunction writeLastWake(dataDir: string, slug: string, config: AgentConfig): void {\n const p = agentConfigPath(dataDir, slug);\n try {\n const updated: AgentConfig = { ...config, lastWake: new Date().toISOString() };\n writeJsonFile(p, updated);\n } catch {\n // non-fatal — just a housekeeping write\n }\n}\n\n// ─── Telegram transport ───────────────────────────────────────────────────────\n\nfunction sendTelegramMessage(token: string, chatId: string, text: string): Promise<void> {\n const body = JSON.stringify({ chat_id: chatId, text, parse_mode: \"Markdown\" });\n return 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\n// ─── Message builder ──────────────────────────────────────────────────────────\n\nfunction buildWakeMessage(\n slug: string,\n subject: string,\n summary: string,\n nextSteps: string[]\n): string {\n const suggestedAction = nextSteps[0] ?? \"Follow up within 24h\";\n return (\n `📧 New email from **${slug}**: ${subject}\\n` +\n `${summary}\\n\\n` +\n `💡 Suggested action: ${suggestedAction}`\n );\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Fire-and-forget notification: reads the agent config for `slug`, summarises\n * the inbound email with the LLM, and sends a Telegram message.\n *\n * Silently returns (no throw) when:\n * - no agent config exists for the slug\n * - TELEGRAM_BOT_TOKEN env var is not set\n * - no chat id is available (neither in config nor in TELEGRAM_CHAT_ID env var)\n * - any HTTPS / LLM error occurs\n */\nexport async function notifyAgentWake(\n dataDir: string,\n slug: string,\n context: WakeContext\n): Promise<void> {\n try {\n // 1. Read agent config — bail silently if not found\n const config = readAgentConfig(dataDir, slug);\n if (!config) return;\n\n // 2. Check for Telegram token — bail silently if absent\n const token = process.env[\"TELEGRAM_BOT_TOKEN\"];\n if (!token) return;\n\n // 3. Determine chat id — config takes precedence, fallback to env var\n const chatId = config.telegramChatId ?? process.env[\"TELEGRAM_CHAT_ID\"];\n if (!chatId) return;\n\n // 4. Summarise the email (LLM, with fallback built into summarizeEmail itself)\n const emailSummary = await summarizeEmail(context.subject, context.snippet, context.from);\n\n // 5. Build and send the Telegram message\n const text = buildWakeMessage(\n slug,\n context.subject,\n emailSummary.summary,\n emailSummary.nextSteps\n );\n await sendTelegramMessage(token, chatId, text);\n\n // 6. Update lastWake on success\n writeLastWake(dataDir, slug, config);\n } catch {\n // Swallow all errors — this is a notification feature, never crashes core loop\n }\n}\n","// src/sync/gmail-sync.ts\nimport fs from \"fs\";\nimport path from \"path\";\nimport { gmail as gmailApi, type gmail_v1 } from \"@googleapis/gmail\";\nimport type { OAuth2Client } from \"google-auth-library\";\nimport { readInteractions, appendInteraction } from \"../fs/interactions-writer.js\";\nimport { notifyAgentWake } from \"../core/agent-notifier.js\";\nimport { logger } from \"../core/logger.js\";\n\ninterface SyncOptions {\n slug: string;\n dataDir: string;\n auth: OAuth2Client;\n query: string;\n since?: Date;\n maxPages?: number;\n /** Download, convert and index email attachments (default true). */\n includeAttachments?: boolean;\n /** Per-attachment size cap in bytes. */\n maxAttachmentBytes?: number;\n}\n\n/**\n * Retry a function with exponential backoff on any error.\n * Delays: 1s, 2s, 4s, 8s … (2^attempt seconds), up to maxRetries retries.\n */\nexport async function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {\n let attempt = 0;\n while (true) {\n try {\n return await fn();\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n const delayMs = 1000 * Math.pow(2, attempt);\n await sleep(delayMs);\n attempt++;\n }\n }\n}\n\nexport async function syncGmail(opts: SyncOptions): Promise<{ synced: number; skipped: number }> {\n const gmail = gmailApi({ version: \"v1\", auth: opts.auth });\n const maxPages = opts.maxPages ?? 5;\n const includeAttachments = opts.includeAttachments ?? true;\n\n let q = opts.query;\n if (opts.since) {\n const after = Math.floor(opts.since.getTime() / 1000);\n q += ` after:${after}`;\n }\n\n // Collect all message stubs across pages (Task A — pagination)\n const allMessages: Array<{ id?: string | null; threadId?: string | null }> = [];\n let pageToken: string | undefined = undefined;\n let pagesFetched = 0;\n\n do {\n const listResp: { data: gmail_v1.Schema$ListMessagesResponse } =\n await gmail.users.messages.list({\n userId: \"me\",\n q,\n maxResults: 200,\n ...(pageToken ? { pageToken } : {}),\n });\n const pageMessages = listResp.data.messages ?? [];\n allMessages.push(...pageMessages);\n pageToken = listResp.data.nextPageToken ?? undefined;\n pagesFetched++;\n } while (pageToken && pagesFetched < maxPages);\n\n // Read existing interactions once before the loop — avoids O(messages) file reads\n let existingContent = await readInteractions(opts.dataDir, opts.slug);\n\n let synced = 0;\n let skipped = 0;\n\n for (const msg of allMessages) {\n if (!msg.id) continue;\n\n const source = `gmail://thread/${msg.threadId ?? msg.id}`;\n\n if (existingContent.includes(source)) {\n skipped++;\n continue;\n }\n\n // Rate limiting ~10 req/s\n await sleep(100);\n\n // Task B — exponential backoff retry on any error\n let msgData: gmail_v1.Schema$Message;\n try {\n const detail = await retryWithBackoff(() =>\n gmail.users.messages.get({\n userId: \"me\",\n id: msg.id!,\n // \"full\" exposes payload.parts so attachments can be downloaded;\n // fall back to lighter \"metadata\" when attachment sync is disabled.\n ...(includeAttachments\n ? { format: \"full\" }\n : { format: \"metadata\", metadataHeaders: [\"Subject\", \"From\", \"Date\"] }),\n })\n );\n msgData = detail.data;\n } catch (err) {\n logger.warn(\"gmail-sync\", \"skipping message after retries\", {\n messageId: msg.id,\n error: (err as Error).message,\n });\n skipped++;\n continue;\n }\n\n const headers = msgData.payload?.headers ?? [];\n const subject = headers.find((h) => h.name === \"Subject\")?.value ?? \"(no subject)\";\n const from = headers.find((h) => h.name === \"From\")?.value ?? \"\";\n const dateStr = headers.find((h) => h.name === \"Date\")?.value;\n const date = dateStr\n ? new Date(dateStr).toISOString().slice(0, 10)\n : new Date().toISOString().slice(0, 10);\n const snippet = msgData.snippet ?? \"\";\n\n // Extract the full inline body (plain preferred, else HTML->Markdown) so\n // summaries and search cover the whole message, not just the snippet.\n const { extractEmailBodyMarkdown } = await import(\"./email-body.js\");\n const body = (await extractEmailBodyMarkdown(msgData.payload ?? undefined)) || snippet;\n\n // LLM summary — non-blocking fallback to raw body/snippet if no API key or error\n const { summarizeEmail } = await import(\"../core/llm.js\");\n const emailSummary = await summarizeEmail(subject, body, from);\n\n // Download, convert and index attachments before logging the interaction so\n // the entry can link to the generated Markdown. Failures here are swallowed.\n let attachmentLinks: string[] = [];\n if (includeAttachments) {\n try {\n const { processMessageAttachments } = await import(\"./attachments.js\");\n const saved = await processMessageAttachments({\n gmail,\n dataDir: opts.dataDir,\n slug: opts.slug,\n messageId: msg.id,\n source,\n payload: msgData.payload ?? undefined,\n date,\n ...(opts.maxAttachmentBytes !== undefined\n ? { maxBytes: opts.maxAttachmentBytes }\n : {}),\n });\n attachmentLinks = saved.map((a) => a.markdownName);\n } catch (err) {\n logger.warn(\"gmail-sync\", \"attachment processing failed\", {\n messageId: msg.id,\n error: (err as Error).message,\n });\n }\n }\n\n await appendInteraction(opts.dataDir, opts.slug, {\n date,\n type: \"Email\",\n direction: detectDirection(from),\n with: from,\n subject,\n summary: emailSummary.summary,\n nextSteps: emailSummary.nextSteps,\n ...(attachmentLinks.length > 0 ? { attachments: attachmentLinks } : {}),\n sourceRef: source,\n synced: new Date().toISOString(),\n });\n\n // Append to in-memory string so within-batch duplicates are detected\n existingContent += source;\n\n // Index the full email (subject + body) into LanceDB for semantic search,\n // chunked so long threads stay searchable (non-blocking).\n const { indexInLanceDB } = await import(\"../core/lancedb.js\");\n const { chunkText } = await import(\"../core/chunk.js\");\n const bodyChunks = chunkText(`${subject}\\n${body}`);\n for (let i = 0; i < bodyChunks.length; i++) {\n const ref = i === 0 ? source : `${source}#${i}`;\n await indexInLanceDB(opts.dataDir, opts.slug, bodyChunks[i]!, ref, {\n date,\n type: \"Email\",\n }).catch((err: unknown) => {\n logger.error(\"gmail-sync\", \"LanceDB index failed\", { error: (err as Error).message });\n });\n }\n\n // Agent wake: notify if an agent config exists for this customer (fire-and-forget)\n if (agentConfigExists(opts.dataDir, opts.slug)) {\n notifyAgentWake(opts.dataDir, opts.slug, {\n trigger: \"email\",\n subject,\n from,\n snippet,\n }).catch(() => {\n // Notification is non-blocking; swallow all errors\n });\n }\n\n synced++;\n }\n\n return { synced, skipped };\n}\n\nfunction agentConfigExists(dataDir: string, slug: string): boolean {\n const configPath = path.join(dataDir, \".agentic\", \"agents\", `${slug}.agent.json`);\n return fs.existsSync(configPath);\n}\n\nfunction detectDirection(_from: string): \"inbound\" | \"outbound\" {\n return \"inbound\";\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\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;;;ACeD,SAAS,gBAAgB,SAAiB,MAAsB;CAC9D,OAAO,KAAK,KAAK,SAAS,YAAY,UAAU,GAAG,KAAK,YAAY;AACtE;AAEA,SAAS,gBAAgB,SAAiB,MAAkC;CAC1E,MAAM,IAAI,gBAAgB,SAAS,IAAI;CACvC,IAAI,CAAC,GAAG,WAAW,CAAC,GAAG,OAAO;CAC9B,IAAI;EACF,MAAM,MAAM,KAAK,MAAM,GAAG,aAAa,GAAG,OAAO,CAAW;EAC5D,MAAM,SAAS,kBAAkB,UAAU,GAAG;EAC9C,OAAO,OAAO,UAAU,OAAO,OAAO;CACxC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,SAAiB,MAAc,QAA2B;CAC/E,MAAM,IAAI,gBAAgB,SAAS,IAAI;CACvC,IAAI;EAEF,cAAc,GAAG;GADc,GAAG;GAAQ,2BAAU,IAAI,KAAK,GAAE,YAAY;EACpD,CAAC;CAC1B,QAAQ,CAER;AACF;AAIA,SAAS,oBAAoB,OAAe,QAAgB,MAA6B;CACvF,MAAM,OAAO,KAAK,UAAU;EAAE,SAAS;EAAQ;EAAM,YAAY;CAAW,CAAC;CAC7E,OAAO,IAAI,SAAe,SAAS,WAAW;EAC5C,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;AAIA,SAAS,iBACP,MACA,SACA,SACA,WACQ;CAER,OACE,uBAAuB,KAAK,MAAM,QAAQ,IACvC,QAAQ,2BAHW,UAAU,MAAM;AAM1C;;;;;;;;;;;AAcA,eAAsB,gBACpB,SACA,MACA,SACe;CACf,IAAI;EAEF,MAAM,SAAS,gBAAgB,SAAS,IAAI;EAC5C,IAAI,CAAC,QAAQ;EAGb,MAAM,QAAQ,QAAQ,IAAI;EAC1B,IAAI,CAAC,OAAO;EAGZ,MAAM,SAAS,OAAO,kBAAkB,QAAQ,IAAI;EACpD,IAAI,CAAC,QAAQ;EAGb,MAAM,eAAe,MAAM,eAAe,QAAQ,SAAS,QAAQ,SAAS,QAAQ,IAAI;EASxF,MAAM,oBAAoB,OAAO,QANpB,iBACX,MACA,QAAQ,SACR,aAAa,SACb,aAAa,SAE6B,CAAC;EAG7C,cAAc,SAAS,MAAM,MAAM;CACrC,QAAQ,CAER;AACF;;;;;;;AChHA,eAAsB,iBAAoB,IAAsB,aAAa,GAAe;CAC1F,IAAI,UAAU;CACd,OAAO,MACL,IAAI;EACF,OAAO,MAAM,GAAG;CAClB,SAAS,KAAK;EACZ,IAAI,WAAW,YAAY,MAAM;EAEjC,MAAM,MADU,MAAO,KAAK,IAAI,GAAG,OAAO,CACvB;EACnB;CACF;AAEJ;AAEA,eAAsB,UAAU,MAAiE;CAC/F,MAAMA,UAAQC,MAAS;EAAE,SAAS;EAAM,MAAM,KAAK;CAAK,CAAC;CACzD,MAAM,WAAW,KAAK,YAAY;CAClC,MAAM,qBAAqB,KAAK,sBAAsB;CAEtD,IAAI,IAAI,KAAK;CACb,IAAI,KAAK,OAAO;EACd,MAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,QAAQ,IAAI,GAAI;EACpD,KAAK,UAAU;CACjB;CAGA,MAAM,cAAuE,CAAC;CAC9E,IAAI,YAAgC,KAAA;CACpC,IAAI,eAAe;CAEnB,GAAG;EACD,MAAM,WACJ,MAAMD,QAAM,MAAM,SAAS,KAAK;GAC9B,QAAQ;GACR;GACA,YAAY;GACZ,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACnC,CAAC;EACH,MAAM,eAAe,SAAS,KAAK,YAAY,CAAC;EAChD,YAAY,KAAK,GAAG,YAAY;EAChC,YAAY,SAAS,KAAK,iBAAiB,KAAA;EAC3C;CACF,SAAS,aAAa,eAAe;CAGrC,IAAI,kBAAkB,MAAM,iBAAiB,KAAK,SAAS,KAAK,IAAI;CAEpE,IAAI,SAAS;CACb,IAAI,UAAU;CAEd,KAAK,MAAM,OAAO,aAAa;EAC7B,IAAI,CAAC,IAAI,IAAI;EAEb,MAAM,SAAS,kBAAkB,IAAI,YAAY,IAAI;EAErD,IAAI,gBAAgB,SAAS,MAAM,GAAG;GACpC;GACA;EACF;EAGA,MAAM,MAAM,GAAG;EAGf,IAAI;EACJ,IAAI;GAYF,WAAU,MAXW,uBACnBA,QAAM,MAAM,SAAS,IAAI;IACvB,QAAQ;IACR,IAAI,IAAI;IAGR,GAAI,qBACA,EAAE,QAAQ,OAAO,IACjB;KAAE,QAAQ;KAAY,iBAAiB;MAAC;MAAW;MAAQ;KAAM;IAAE;GACzE,CAAC,CACH,GACiB;EACnB,SAAS,KAAK;GACZ,OAAO,KAAK,cAAc,kCAAkC;IAC1D,WAAW,IAAI;IACf,OAAQ,IAAc;GACxB,CAAC;GACD;GACA;EACF;EAEA,MAAM,UAAU,QAAQ,SAAS,WAAW,CAAC;EAC7C,MAAM,UAAU,QAAQ,MAAM,MAAM,EAAE,SAAS,SAAS,GAAG,SAAS;EACpE,MAAM,OAAO,QAAQ,MAAM,MAAM,EAAE,SAAS,MAAM,GAAG,SAAS;EAC9D,MAAM,UAAU,QAAQ,MAAM,MAAM,EAAE,SAAS,MAAM,GAAG;EACxD,MAAM,OAAO,UACT,IAAI,KAAK,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,qBAC3C,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;EACxC,MAAM,UAAU,QAAQ,WAAW;EAInC,MAAM,EAAE,6BAA6B,MAAM,OAAO;EAClD,MAAM,OAAQ,MAAM,yBAAyB,QAAQ,WAAW,KAAA,CAAS,KAAM;EAG/E,MAAM,EAAE,mBAAmB,MAAM,OAAO,qBAAA,MAAA,MAAA,EAAA,CAAA;EACxC,MAAM,eAAe,MAAM,eAAe,SAAS,MAAM,IAAI;EAI7D,IAAI,kBAA4B,CAAC;EACjC,IAAI,oBACF,IAAI;GACF,MAAM,EAAE,8BAA8B,MAAM,OAAO;GAanD,mBAAkB,MAZE,0BAA0B;IAC5C,OAAA;IACA,SAAS,KAAK;IACd,MAAM,KAAK;IACX,WAAW,IAAI;IACf;IACA,SAAS,QAAQ,WAAW,KAAA;IAC5B;IACA,GAAI,KAAK,uBAAuB,KAAA,IAC5B,EAAE,UAAU,KAAK,mBAAmB,IACpC,CAAC;GACP,CAAC,GACuB,KAAK,MAAM,EAAE,YAAY;EACnD,SAAS,KAAK;GACZ,OAAO,KAAK,cAAc,gCAAgC;IACxD,WAAW,IAAI;IACf,OAAQ,IAAc;GACxB,CAAC;EACH;EAGF,MAAM,kBAAkB,KAAK,SAAS,KAAK,MAAM;GAC/C;GACA,MAAM;GACN,WAAW,gBAAgB,IAAI;GAC/B,MAAM;GACN;GACA,SAAS,aAAa;GACtB,WAAW,aAAa;GACxB,GAAI,gBAAgB,SAAS,IAAI,EAAE,aAAa,gBAAgB,IAAI,CAAC;GACrE,WAAW;GACX,yBAAQ,IAAI,KAAK,GAAE,YAAY;EACjC,CAAC;EAGD,mBAAmB;EAInB,MAAM,EAAE,mBAAmB,MAAM,OAAO,YAAA,MAAA,MAAA,EAAA,CAAA;EACxC,MAAM,EAAE,cAAc,MAAM,OAAO;EACnC,MAAM,aAAa,UAAU,GAAG,QAAQ,IAAI,MAAM;EAClD,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;GAC1C,MAAM,MAAM,MAAM,IAAI,SAAS,GAAG,OAAO,GAAG;GAC5C,MAAM,eAAe,KAAK,SAAS,KAAK,MAAM,WAAW,IAAK,KAAK;IACjE;IACA,MAAM;GACR,CAAC,EAAE,OAAO,QAAiB;IACzB,OAAO,MAAM,cAAc,wBAAwB,EAAE,OAAQ,IAAc,QAAQ,CAAC;GACtF,CAAC;EACH;EAGA,IAAI,kBAAkB,KAAK,SAAS,KAAK,IAAI,GAC3C,gBAAgB,KAAK,SAAS,KAAK,MAAM;GACvC,SAAS;GACT;GACA;GACA;EACF,CAAC,EAAE,YAAY,CAEf,CAAC;EAGH;CACF;CAEA,OAAO;EAAE;EAAQ;CAAQ;AAC3B;AAEA,SAAS,kBAAkB,SAAiB,MAAuB;CACjE,MAAM,aAAa,KAAK,KAAK,SAAS,YAAY,UAAU,GAAG,KAAK,YAAY;CAChF,OAAO,GAAG,WAAW,UAAU;AACjC;AAEA,SAAS,gBAAgB,OAAuC;CAC9D,OAAO;AACT;AAEA,SAAS,MAAM,IAA2B;CACxC,OAAO,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD"}
@@ -1,6 +1,6 @@
1
- import { n as readSyncState, r as updateSlugSyncState } from "./sync-state-ChaLbamC.js";
2
- import { t as appendInteraction } from "./interactions-writer-SLHnoEeE.js";
3
- import { n as readSubscriptions, s as writeSubscriptions } from "./push-manager-CdqIIkuh.js";
1
+ import { n as readSyncState, r as updateSlugSyncState } from "./sync-state-DMZgzpez.js";
2
+ import { n as appendInteraction } from "./interactions-writer-B8XAzdqR.js";
3
+ import { n as readSubscriptions, s as writeSubscriptions } from "./push-manager-C0ECQgva.js";
4
4
  //#region src/sync/gmail-webhook-handler.ts
5
5
  function decodeGmailPubSubPayload(body) {
6
6
  try {
@@ -94,4 +94,4 @@ function buildGmailRenewFn(accessToken, topicName, registerFn) {
94
94
  //#endregion
95
95
  export { verifyGmailPubSubSignature as i, decodeGmailPubSubPayload as n, handleGmailPushEvent as r, buildGmailRenewFn as t };
96
96
 
97
- //# sourceMappingURL=gmail-webhook-handler-e5Od25FX.js.map
97
+ //# sourceMappingURL=gmail-webhook-handler-BzOFbvgh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gmail-webhook-handler-e5Od25FX.js","names":["b"],"sources":["../src/sync/gmail-webhook-handler.ts"],"sourcesContent":["import {\n readSubscriptions,\n writeSubscriptions,\n type PushSubscription,\n type RenewFn,\n} from \"./push-manager.js\";\nimport { updateSlugSyncState, readSyncState } from \"../fs/sync-state.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\nimport type { HistoryMessage, WatchRegistration } from \"./gmail-push-watch.js\";\n\nexport interface GmailPubSubMessage {\n emailAddress: string;\n historyId: string;\n}\n\nexport function decodeGmailPubSubPayload(body: unknown): GmailPubSubMessage | null {\n try {\n const b = body as { message?: { data?: string } };\n const data = b?.message?.data;\n if (!data) return null;\n const decoded = Buffer.from(data, \"base64\").toString(\"utf-8\");\n const parsed = JSON.parse(decoded) as { emailAddress?: string; historyId?: string };\n if (!parsed.emailAddress || !parsed.historyId) return null;\n return { emailAddress: parsed.emailAddress, historyId: parsed.historyId };\n } catch {\n return null;\n }\n}\n\nexport function verifyGmailPubSubSignature(\n authHeader: string | undefined,\n expectedToken: string\n): boolean {\n if (!authHeader) return false;\n const token = authHeader.startsWith(\"Bearer \") ? authHeader.slice(7) : authHeader;\n return token === expectedToken;\n}\n\nfunction findSubscriptionByEmail(\n subs: PushSubscription[],\n emailAddress: string\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"gmail\" &&\n s.status === \"active\" &&\n (s.providerData.gmailEmailAddress === emailAddress || s.slug === emailAddress)\n ) ?? null\n );\n}\n\nexport type FetchHistoryFn = (\n accessToken: string,\n startHistoryId: string\n) => Promise<HistoryMessage[]>;\nexport type FetchMessageFn = (\n accessToken: string,\n messageId: string\n) => Promise<{\n id: string;\n threadId: string;\n subject: string;\n from: string;\n date: string;\n body: string;\n}>;\nexport type AppendInteractionFn = typeof appendInteraction;\n\nexport interface HandleGmailPushOptions {\n fetchHistoryFn?: FetchHistoryFn;\n fetchMessageFn?: FetchMessageFn;\n appendInteractionFn?: AppendInteractionFn;\n accessToken?: string;\n}\n\nexport { readSubscriptions };\n\nexport async function handleGmailPushEvent(\n dataDir: string,\n payload: GmailPubSubMessage,\n subscriptionId: string,\n options: HandleGmailPushOptions = {}\n): Promise<{ processed: number; slug: string | null }> {\n const subs = await readSubscriptions(dataDir);\n const sub = findSubscriptionByEmail(subs, payload.emailAddress);\n if (!sub) return { processed: 0, slug: null };\n\n const slug = sub.slug;\n const syncState = readSyncState(dataDir);\n const lastHistoryId =\n syncState[slug]?.lastGmailPushHistoryId ?? sub.providerData.gmailHistoryId ?? \"0\";\n\n // Skip if already processed\n if (BigInt(payload.historyId) <= BigInt(lastHistoryId)) {\n return { processed: 0, slug };\n }\n\n const startHistoryId = sub.providerData.gmailHistoryId ?? lastHistoryId;\n\n const {\n fetchHistoryFn,\n fetchMessageFn,\n appendInteractionFn = appendInteraction,\n accessToken = \"\",\n } = options;\n\n if (!fetchHistoryFn) return { processed: 0, slug };\n\n const messages = await fetchHistoryFn(accessToken, startHistoryId);\n let processed = 0;\n\n for (const msg of messages) {\n if (!fetchMessageFn) continue;\n try {\n const full = await fetchMessageFn(accessToken, msg.id);\n const sourceRef = `gmail://thread/${full.threadId}`;\n\n await appendInteractionFn(dataDir, slug, {\n date: new Date().toISOString().slice(0, 10),\n type: \"Email\",\n direction: \"inbound\",\n with: full.from,\n subject: full.subject,\n summary: full.body.slice(0, 300) || \"(no body)\",\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n processed++;\n } catch {\n // Skip individual message errors\n }\n }\n\n // Update sync state\n updateSlugSyncState(dataDir, slug, { lastGmailPushHistoryId: payload.historyId });\n\n // Update subscription counters\n const subIdx = subs.findIndex((s) => s.id === sub.id);\n if (subIdx !== -1) {\n subs[subIdx] = {\n ...subs[subIdx]!,\n eventsProcessed: subs[subIdx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed, slug };\n}\n\nexport type RegisterGmailWatchFn = (\n accessToken: string,\n topicName: string\n) => Promise<WatchRegistration>;\n\nexport function buildGmailRenewFn(\n accessToken: string,\n topicName: string,\n registerFn?: RegisterGmailWatchFn\n): RenewFn {\n return async (_sub: PushSubscription) => {\n const doRegister: RegisterGmailWatchFn =\n registerFn ??\n (async (token: string, topic: string) => {\n const { registerGmailWatch } = await import(\"./gmail-push-watch.js\");\n return registerGmailWatch(token, topic);\n });\n\n const registration = await doRegister(accessToken, topicName);\n return {\n expiresAt: new Date(Number(registration.expiration)).toISOString(),\n providerData: { gmailHistoryId: registration.historyId },\n };\n };\n}\n"],"mappings":";;;;AAeA,SAAgB,yBAAyB,MAA0C;CACjF,IAAI;EAEF,MAAM,OAAOA,MAAG,SAAS;EACzB,IAAI,CAAC,MAAM,OAAO;EAClB,MAAM,UAAU,OAAO,KAAK,MAAM,QAAQ,EAAE,SAAS,OAAO;EAC5D,MAAM,SAAS,KAAK,MAAM,OAAO;EACjC,IAAI,CAAC,OAAO,gBAAgB,CAAC,OAAO,WAAW,OAAO;EACtD,OAAO;GAAE,cAAc,OAAO;GAAc,WAAW,OAAO;EAAU;CAC1E,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,2BACd,YACA,eACS;CACT,IAAI,CAAC,YAAY,OAAO;CAExB,QADc,WAAW,WAAW,SAAS,IAAI,WAAW,MAAM,CAAC,IAAI,gBACtD;AACnB;AAEA,SAAS,wBACP,MACA,cACyB;CACzB,OACE,KAAK,MACF,MACC,EAAE,aAAa,WACf,EAAE,WAAW,aACZ,EAAE,aAAa,sBAAsB,gBAAgB,EAAE,SAAS,aACrE,KAAK;AAET;AA4BA,eAAsB,qBACpB,SACA,SACA,gBACA,UAAkC,CAAC,GACkB;CACrD,MAAM,OAAO,MAAM,kBAAkB,OAAO;CAC5C,MAAM,MAAM,wBAAwB,MAAM,QAAQ,YAAY;CAC9D,IAAI,CAAC,KAAK,OAAO;EAAE,WAAW;EAAG,MAAM;CAAK;CAE5C,MAAM,OAAO,IAAI;CAEjB,MAAM,gBADY,cAAc,OAEtB,EAAE,OAAO,0BAA0B,IAAI,aAAa,kBAAkB;CAGhF,IAAI,OAAO,QAAQ,SAAS,KAAK,OAAO,aAAa,GACnD,OAAO;EAAE,WAAW;EAAG;CAAK;CAG9B,MAAM,iBAAiB,IAAI,aAAa,kBAAkB;CAE1D,MAAM,EACJ,gBACA,gBACA,sBAAsB,mBACtB,cAAc,OACZ;CAEJ,IAAI,CAAC,gBAAgB,OAAO;EAAE,WAAW;EAAG;CAAK;CAEjD,MAAM,WAAW,MAAM,eAAe,aAAa,cAAc;CACjE,IAAI,YAAY;CAEhB,KAAK,MAAM,OAAO,UAAU;EAC1B,IAAI,CAAC,gBAAgB;EACrB,IAAI;GACF,MAAM,OAAO,MAAM,eAAe,aAAa,IAAI,EAAE;GACrD,MAAM,YAAY,kBAAkB,KAAK;GAEzC,MAAM,oBAAoB,SAAS,MAAM;IACvC,uBAAM,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;IAC1C,MAAM;IACN,WAAW;IACX,MAAM,KAAK;IACX,SAAS,KAAK;IACd,SAAS,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK;IACpC,WAAW,CAAC;IACZ;IACA,yBAAQ,IAAI,KAAK,GAAE,YAAY;GACjC,CAAC;GACD;EACF,QAAQ,CAER;CACF;CAGA,oBAAoB,SAAS,MAAM,EAAE,wBAAwB,QAAQ,UAAU,CAAC;CAGhF,MAAM,SAAS,KAAK,WAAW,MAAM,EAAE,OAAO,IAAI,EAAE;CACpD,IAAI,WAAW,IAAI;EACjB,KAAK,UAAU;GACb,GAAG,KAAK;GACR,iBAAiB,KAAK,QAAS,kBAAkB;GACjD,8BAAa,IAAI,KAAK,GAAE,YAAY;EACtC;EACA,MAAM,mBAAmB,SAAS,IAAI;CACxC;CAEA,OAAO;EAAE;EAAW;CAAK;AAC3B;AAOA,SAAgB,kBACd,aACA,WACA,YACS;CACT,OAAO,OAAO,SAA2B;EAQvC,MAAM,eAAe,OANnB,eACC,OAAO,OAAe,UAAkB;GACvC,MAAM,EAAE,uBAAuB,MAAM,OAAO;GAC5C,OAAO,mBAAmB,OAAO,KAAK;EACxC,IAEoC,aAAa,SAAS;EAC5D,OAAO;GACL,WAAW,IAAI,KAAK,OAAO,aAAa,UAAU,CAAC,EAAE,YAAY;GACjE,cAAc,EAAE,gBAAgB,aAAa,UAAU;EACzD;CACF;AACF"}
1
+ {"version":3,"file":"gmail-webhook-handler-BzOFbvgh.js","names":["b"],"sources":["../src/sync/gmail-webhook-handler.ts"],"sourcesContent":["import {\n readSubscriptions,\n writeSubscriptions,\n type PushSubscription,\n type RenewFn,\n} from \"./push-manager.js\";\nimport { updateSlugSyncState, readSyncState } from \"../fs/sync-state.js\";\nimport { appendInteraction } from \"../fs/interactions-writer.js\";\nimport type { HistoryMessage, WatchRegistration } from \"./gmail-push-watch.js\";\n\nexport interface GmailPubSubMessage {\n emailAddress: string;\n historyId: string;\n}\n\nexport function decodeGmailPubSubPayload(body: unknown): GmailPubSubMessage | null {\n try {\n const b = body as { message?: { data?: string } };\n const data = b?.message?.data;\n if (!data) return null;\n const decoded = Buffer.from(data, \"base64\").toString(\"utf-8\");\n const parsed = JSON.parse(decoded) as { emailAddress?: string; historyId?: string };\n if (!parsed.emailAddress || !parsed.historyId) return null;\n return { emailAddress: parsed.emailAddress, historyId: parsed.historyId };\n } catch {\n return null;\n }\n}\n\nexport function verifyGmailPubSubSignature(\n authHeader: string | undefined,\n expectedToken: string\n): boolean {\n if (!authHeader) return false;\n const token = authHeader.startsWith(\"Bearer \") ? authHeader.slice(7) : authHeader;\n return token === expectedToken;\n}\n\nfunction findSubscriptionByEmail(\n subs: PushSubscription[],\n emailAddress: string\n): PushSubscription | null {\n return (\n subs.find(\n (s) =>\n s.provider === \"gmail\" &&\n s.status === \"active\" &&\n (s.providerData.gmailEmailAddress === emailAddress || s.slug === emailAddress)\n ) ?? null\n );\n}\n\nexport type FetchHistoryFn = (\n accessToken: string,\n startHistoryId: string\n) => Promise<HistoryMessage[]>;\nexport type FetchMessageFn = (\n accessToken: string,\n messageId: string\n) => Promise<{\n id: string;\n threadId: string;\n subject: string;\n from: string;\n date: string;\n body: string;\n}>;\nexport type AppendInteractionFn = typeof appendInteraction;\n\nexport interface HandleGmailPushOptions {\n fetchHistoryFn?: FetchHistoryFn;\n fetchMessageFn?: FetchMessageFn;\n appendInteractionFn?: AppendInteractionFn;\n accessToken?: string;\n}\n\nexport { readSubscriptions };\n\nexport async function handleGmailPushEvent(\n dataDir: string,\n payload: GmailPubSubMessage,\n subscriptionId: string,\n options: HandleGmailPushOptions = {}\n): Promise<{ processed: number; slug: string | null }> {\n const subs = await readSubscriptions(dataDir);\n const sub = findSubscriptionByEmail(subs, payload.emailAddress);\n if (!sub) return { processed: 0, slug: null };\n\n const slug = sub.slug;\n const syncState = readSyncState(dataDir);\n const lastHistoryId =\n syncState[slug]?.lastGmailPushHistoryId ?? sub.providerData.gmailHistoryId ?? \"0\";\n\n // Skip if already processed\n if (BigInt(payload.historyId) <= BigInt(lastHistoryId)) {\n return { processed: 0, slug };\n }\n\n const startHistoryId = sub.providerData.gmailHistoryId ?? lastHistoryId;\n\n const {\n fetchHistoryFn,\n fetchMessageFn,\n appendInteractionFn = appendInteraction,\n accessToken = \"\",\n } = options;\n\n if (!fetchHistoryFn) return { processed: 0, slug };\n\n const messages = await fetchHistoryFn(accessToken, startHistoryId);\n let processed = 0;\n\n for (const msg of messages) {\n if (!fetchMessageFn) continue;\n try {\n const full = await fetchMessageFn(accessToken, msg.id);\n const sourceRef = `gmail://thread/${full.threadId}`;\n\n await appendInteractionFn(dataDir, slug, {\n date: new Date().toISOString().slice(0, 10),\n type: \"Email\",\n direction: \"inbound\",\n with: full.from,\n subject: full.subject,\n summary: full.body.slice(0, 300) || \"(no body)\",\n nextSteps: [],\n sourceRef,\n synced: new Date().toISOString(),\n });\n processed++;\n } catch {\n // Skip individual message errors\n }\n }\n\n // Update sync state\n updateSlugSyncState(dataDir, slug, { lastGmailPushHistoryId: payload.historyId });\n\n // Update subscription counters\n const subIdx = subs.findIndex((s) => s.id === sub.id);\n if (subIdx !== -1) {\n subs[subIdx] = {\n ...subs[subIdx]!,\n eventsProcessed: subs[subIdx]!.eventsProcessed + 1,\n lastEventAt: new Date().toISOString(),\n };\n await writeSubscriptions(dataDir, subs);\n }\n\n return { processed, slug };\n}\n\nexport type RegisterGmailWatchFn = (\n accessToken: string,\n topicName: string\n) => Promise<WatchRegistration>;\n\nexport function buildGmailRenewFn(\n accessToken: string,\n topicName: string,\n registerFn?: RegisterGmailWatchFn\n): RenewFn {\n return async (_sub: PushSubscription) => {\n const doRegister: RegisterGmailWatchFn =\n registerFn ??\n (async (token: string, topic: string) => {\n const { registerGmailWatch } = await import(\"./gmail-push-watch.js\");\n return registerGmailWatch(token, topic);\n });\n\n const registration = await doRegister(accessToken, topicName);\n return {\n expiresAt: new Date(Number(registration.expiration)).toISOString(),\n providerData: { gmailHistoryId: registration.historyId },\n };\n };\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import "./push-manager-CdqIIkuh.js";
2
- import { t as buildGmailRenewFn } from "./gmail-webhook-handler-e5Od25FX.js";
1
+ import "./push-manager-C0ECQgva.js";
2
+ import { t as buildGmailRenewFn } from "./gmail-webhook-handler-BzOFbvgh.js";
3
3
  export { buildGmailRenewFn };