@agenticmail/enterprise 0.5.311 → 0.5.313

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 (214) hide show
  1. package/README.md +682 -544
  2. package/dist/agent-autonomy-PSXQ4MNP.js +766 -0
  3. package/dist/agent-heartbeat-6H3YAQ32.js +510 -0
  4. package/dist/agent-heartbeat-7WS3XILF.js +510 -0
  5. package/dist/agent-heartbeat-BFGKYUUK.js +510 -0
  6. package/dist/agent-heartbeat-SSV65YTX.js +510 -0
  7. package/dist/agent-heartbeat-T5IIHVF4.js +510 -0
  8. package/dist/agent-heartbeat-X3C6FIU2.js +510 -0
  9. package/dist/agent-tools-BW6CLQQ7.js +13897 -0
  10. package/dist/agent-tools-KEA7QEWF.js +13897 -0
  11. package/dist/agent-tools-NU7V3S5N.js +13899 -0
  12. package/dist/agent-tools-WINDYRQ2.js +13897 -0
  13. package/dist/chunk-3ELH5CU6.js +4910 -0
  14. package/dist/chunk-4QYRS3MS.js +1519 -0
  15. package/dist/chunk-52REEVDW.js +1519 -0
  16. package/dist/chunk-5RZJ76YI.js +4977 -0
  17. package/dist/chunk-6L7FQI5Q.js +4909 -0
  18. package/dist/chunk-763OMGFI.js +1519 -0
  19. package/dist/chunk-7ILSXGY6.js +1519 -0
  20. package/dist/chunk-7UCKD25B.js +551 -0
  21. package/dist/chunk-A6MSR7DL.js +4977 -0
  22. package/dist/chunk-ASD2YB6O.js +1519 -0
  23. package/dist/chunk-AZOIHLLX.js +4977 -0
  24. package/dist/chunk-BDCFOP7O.js +537 -0
  25. package/dist/chunk-BSVWPG6I.js +106 -0
  26. package/dist/chunk-C46DNDZB.js +1519 -0
  27. package/dist/chunk-CFVTK4FQ.js +4977 -0
  28. package/dist/chunk-CHJAOKCJ.js +4921 -0
  29. package/dist/chunk-CYEWTXYH.js +4977 -0
  30. package/dist/chunk-D3KFSWLK.js +48 -0
  31. package/dist/chunk-DUVGNAIY.js +4977 -0
  32. package/dist/chunk-DX4XEFVE.js +25229 -0
  33. package/dist/chunk-EX6FQSEV.js +167 -0
  34. package/dist/chunk-F5VZ5EUH.js +1519 -0
  35. package/dist/chunk-FVUDSPOD.js +4977 -0
  36. package/dist/chunk-G6FTZKJX.js +4977 -0
  37. package/dist/chunk-GFEAZN6Y.js +1519 -0
  38. package/dist/chunk-HKV4FQFW.js +1519 -0
  39. package/dist/chunk-ICCPULDT.js +25217 -0
  40. package/dist/chunk-IYEM627Q.js +25216 -0
  41. package/dist/chunk-JHRJ4QJ6.js +1519 -0
  42. package/dist/chunk-K2DAUYHV.js +4977 -0
  43. package/dist/chunk-KDQDSZZQ.js +4973 -0
  44. package/dist/chunk-LDUD6AZY.js +1519 -0
  45. package/dist/chunk-LES5TJ5L.js +4909 -0
  46. package/dist/chunk-MJGGW6MC.js +106 -0
  47. package/dist/chunk-MQKIWAHQ.js +106 -0
  48. package/dist/chunk-NGA7BBPF.js +48 -0
  49. package/dist/chunk-OE3TI4IQ.js +1519 -0
  50. package/dist/chunk-OHSBIYDR.js +4977 -0
  51. package/dist/chunk-OPOBUYJT.js +1519 -0
  52. package/dist/chunk-OZEYDEPB.js +1519 -0
  53. package/dist/chunk-P4PODSQH.js +1519 -0
  54. package/dist/chunk-P7UOSFIE.js +636 -0
  55. package/dist/chunk-PFN6DODU.js +4921 -0
  56. package/dist/chunk-PKDVM4IY.js +4917 -0
  57. package/dist/chunk-Q5KG3G7U.js +25115 -0
  58. package/dist/chunk-QMVNW4FJ.js +25229 -0
  59. package/dist/chunk-QZ5UPRBE.js +4977 -0
  60. package/dist/chunk-SPP23N42.js +4977 -0
  61. package/dist/chunk-SRGHNFOY.js +4921 -0
  62. package/dist/chunk-TPLVQFXM.js +2594 -0
  63. package/dist/chunk-U3XYF4QP.js +4977 -0
  64. package/dist/chunk-VRRJH2DY.js +4921 -0
  65. package/dist/chunk-WY42BS3F.js +1519 -0
  66. package/dist/chunk-XAA4VHHZ.js +1519 -0
  67. package/dist/chunk-Y2KIY4BA.js +4969 -0
  68. package/dist/chunk-Z5Y5KTPC.js +4977 -0
  69. package/dist/chunk-ZA4QRACH.js +4977 -0
  70. package/dist/chunk-ZHLGSTXF.js +4909 -0
  71. package/dist/cli-agent-26BUULHZ.js +2169 -0
  72. package/dist/cli-agent-2FLJWXOC.js +2169 -0
  73. package/dist/cli-agent-4NNQFLO6.js +2255 -0
  74. package/dist/cli-agent-5WV3EEPW.js +2252 -0
  75. package/dist/cli-agent-65JUT6DU.js +2193 -0
  76. package/dist/cli-agent-6HLL7A5K.js +2255 -0
  77. package/dist/cli-agent-CZ26QWUZ.js +2210 -0
  78. package/dist/cli-agent-HPVSWDNQ.js +2255 -0
  79. package/dist/cli-agent-K4SBVG5X.js +2210 -0
  80. package/dist/cli-agent-K5D424X2.js +2252 -0
  81. package/dist/cli-agent-U4OL5FGK.js +2210 -0
  82. package/dist/cli-agent-WUMPOIKQ.js +2169 -0
  83. package/dist/cli-agent-WWRGGJ2F.js +2255 -0
  84. package/dist/cli-agent-ZDBBTVGU.js +2193 -0
  85. package/dist/cli-agent-ZIZ5JP4O.js +2252 -0
  86. package/dist/cli-recover-I4KNR2OI.js +487 -0
  87. package/dist/cli-recover-IQTUKWR2.js +487 -0
  88. package/dist/cli-recover-OYJHELOR.js +487 -0
  89. package/dist/cli-recover-PVQC7UXB.js +487 -0
  90. package/dist/cli-recover-T32NABFA.js +487 -0
  91. package/dist/cli-serve-FE4CMMSN.js +143 -0
  92. package/dist/cli-serve-FTQJ3RUK.js +143 -0
  93. package/dist/cli-serve-G4PUCASH.js +143 -0
  94. package/dist/cli-serve-HBZYUUQ3.js +143 -0
  95. package/dist/cli-serve-L3NUROMO.js +143 -0
  96. package/dist/cli-serve-LAA5WIZK.js +143 -0
  97. package/dist/cli-serve-LV4TUSJD.js +143 -0
  98. package/dist/cli-serve-MFCTVA2L.js +140 -0
  99. package/dist/cli-serve-QCRUFI5B.js +143 -0
  100. package/dist/cli-serve-S7OGQN4P.js +143 -0
  101. package/dist/cli-serve-SI4BQRXT.js +140 -0
  102. package/dist/cli-serve-UNB7EHN4.js +143 -0
  103. package/dist/cli-serve-UV3GVDRD.js +143 -0
  104. package/dist/cli-serve-V5QICXR5.js +143 -0
  105. package/dist/cli-serve-VG6Z6GIB.js +143 -0
  106. package/dist/cli-serve-XSYHPGZI.js +143 -0
  107. package/dist/cli-serve-Y534FCRV.js +140 -0
  108. package/dist/cli-verify-CZIITRED.js +149 -0
  109. package/dist/cli-verify-N73GOKEF.js +149 -0
  110. package/dist/cli-verify-QEEBZOUZ.js +149 -0
  111. package/dist/cli-verify-RC5HI6DU.js +149 -0
  112. package/dist/cli-verify-VKBNIEAX.js +149 -0
  113. package/dist/cli.js +5 -5
  114. package/dist/dashboard/app.js +16 -3
  115. package/dist/dashboard/components/org-switcher.js +5 -1
  116. package/dist/dashboard/org-switcher.js +156 -0
  117. package/dist/dashboard/pages/login.js +160 -4
  118. package/dist/dashboard/pages/task-pipeline.js +1 -1
  119. package/dist/factory-3IWXVE37.js +9 -0
  120. package/dist/factory-5M6PTMLC.js +11 -0
  121. package/dist/factory-CSSHN7GE.js +11 -0
  122. package/dist/factory-JFWXTAWK.js +11 -0
  123. package/dist/factory-TBGUYM5X.js +9 -0
  124. package/dist/google-W5AYGNUJ.js +33 -0
  125. package/dist/index.js +6 -6
  126. package/dist/meetings-FJ453ENF.js +12 -0
  127. package/dist/postgres-BCHZWRU3.js +832 -0
  128. package/dist/postgres-BI4QVRM6.js +825 -0
  129. package/dist/postgres-BOTHOPDW.js +875 -0
  130. package/dist/postgres-JBUKR3TA.js +873 -0
  131. package/dist/postgres-Z7QYSU6K.js +861 -0
  132. package/dist/routes-7QYAQTWA.js +90 -0
  133. package/dist/routes-JCBVZU54.js +90 -0
  134. package/dist/routes-KEDEJFRE.js +90 -0
  135. package/dist/routes-WI64ADVH.js +90 -0
  136. package/dist/routes-X36OSCID.js +90 -0
  137. package/dist/runtime-75XR6KEW.js +45 -0
  138. package/dist/runtime-BNM7ZNNL.js +45 -0
  139. package/dist/runtime-ES6WCJ7D.js +45 -0
  140. package/dist/runtime-KYJTML2B.js +45 -0
  141. package/dist/runtime-LO67ZHQA.js +45 -0
  142. package/dist/runtime-VIXKKVSZ.js +45 -0
  143. package/dist/runtime-WHWJPCGK.js +45 -0
  144. package/dist/runtime-Z2Q6GUHH.js +45 -0
  145. package/dist/runtime-ZZ6CALSB.js +45 -0
  146. package/dist/server-27A4WEJC.js +28 -0
  147. package/dist/server-2CBXP4WS.js +28 -0
  148. package/dist/server-4JQAB5R4.js +28 -0
  149. package/dist/server-6BOM5U64.js +28 -0
  150. package/dist/server-CA2I3LJY.js +28 -0
  151. package/dist/server-FLJKNPRD.js +28 -0
  152. package/dist/server-HMIHIQ2N.js +28 -0
  153. package/dist/server-KIXXLR2D.js +28 -0
  154. package/dist/server-KSEIZTXF.js +28 -0
  155. package/dist/server-MPVW7DKZ.js +28 -0
  156. package/dist/server-PRTVRQ2D.js +28 -0
  157. package/dist/server-SYIG6HAX.js +28 -0
  158. package/dist/server-U32KDIXC.js +28 -0
  159. package/dist/server-WFN6CA4T.js +28 -0
  160. package/dist/server-XQUE3FGT.js +28 -0
  161. package/dist/server-XWT2UORK.js +28 -0
  162. package/dist/server-Y3BGNN5Q.js +28 -0
  163. package/dist/setup-352L2TPS.js +20 -0
  164. package/dist/setup-4MM645XK.js +20 -0
  165. package/dist/setup-5JPWW6IP.js +20 -0
  166. package/dist/setup-CUN6LVUV.js +20 -0
  167. package/dist/setup-D3YHPWPY.js +20 -0
  168. package/dist/setup-D4A5I6UM.js +20 -0
  169. package/dist/setup-DOPLXTB3.js +20 -0
  170. package/dist/setup-E3NSIM6B.js +20 -0
  171. package/dist/setup-E3V2D7NL.js +20 -0
  172. package/dist/setup-FSYPGI2C.js +20 -0
  173. package/dist/setup-G3RPKRG3.js +20 -0
  174. package/dist/setup-KJ77HNWK.js +20 -0
  175. package/dist/setup-LPSOY5V5.js +20 -0
  176. package/dist/setup-N3ODDSQE.js +20 -0
  177. package/dist/setup-NLDM3M2P.js +20 -0
  178. package/dist/setup-PT6WGOYB.js +20 -0
  179. package/dist/setup-SWJMNDWF.js +20 -0
  180. package/dist/system-prompts-6OUTAMH6.js +41 -0
  181. package/dist/task-queue-YP2I54IA.js +9 -0
  182. package/dist/telegram-QRNGRT5M.js +17 -0
  183. package/dist/whatsapp-VYVINCGV.js +31 -0
  184. package/god_is_great.html +35 -0
  185. package/package.json +1 -1
  186. package/src/admin/routes.ts +24 -4
  187. package/src/agent-tools/index.ts +4 -1
  188. package/src/agent-tools/tool-resolver.ts +15 -4
  189. package/src/agent-tools/tools/browser.ts +2 -2
  190. package/src/agent-tools/tools/local/dependency-manager.ts +286 -0
  191. package/src/agent-tools/tools/local/index.ts +3 -0
  192. package/src/agent-tools/tools/messaging/telegram.ts +29 -0
  193. package/src/agent-tools/tools/messaging/whatsapp.ts +59 -4
  194. package/src/auth/routes.ts +15 -12
  195. package/src/cli-agent.ts +47 -6
  196. package/src/cli-serve.ts +2 -5
  197. package/src/dashboard/app.js +16 -3
  198. package/src/dashboard/components/org-switcher.js +5 -1
  199. package/src/dashboard/pages/login.js +160 -4
  200. package/src/dashboard/pages/task-pipeline.js +1 -1
  201. package/src/db/adapter.ts +2 -0
  202. package/src/db/factory.ts +78 -0
  203. package/src/db/postgres.ts +57 -12
  204. package/src/engine/agent-autonomy.ts +1 -1
  205. package/src/engine/agent-heartbeat.ts +1 -1
  206. package/src/engine/messaging-poller.ts +146 -11
  207. package/src/engine/oauth-connect-routes.ts +23 -3
  208. package/src/engine/routes.ts +1 -1
  209. package/src/engine/task-poller.ts +54 -3
  210. package/src/engine/task-queue.ts +30 -0
  211. package/src/runtime/index.ts +2 -1
  212. package/src/runtime/types.ts +2 -0
  213. package/src/server.ts +43 -1
  214. package/src/system-prompts/triage.ts +1 -1
@@ -163,6 +163,12 @@ function App() {
163
163
  if (document.cookie.match(/em_session|em_csrf/)) {
164
164
  authCall('/me').then(async d => {
165
165
  setUser(d.user || d);
166
+ // If user is org-bound, lock org context immediately
167
+ var u = d.user || d;
168
+ if (u.clientOrgId) {
169
+ setSelectedOrgId(u.clientOrgId);
170
+ localStorage.setItem('em_client_org_id', u.clientOrgId);
171
+ }
166
172
  // Init transport encryption BEFORE setting authed — ensures all subsequent
167
173
  // API calls from child components are encrypted
168
174
  try {
@@ -202,9 +208,10 @@ function App() {
202
208
  apiCall('/settings').then(d => { const s = d.settings || d || {}; if (s.primaryColor) applyBrandColor(s.primaryColor); if (s.orgId) setOrgId(s.orgId); }).catch(() => {});
203
209
  apiCall('/me/permissions').then(d => {
204
210
  if (d && d.permissions) setPermissions(d.permissions);
205
- // If user is assigned to a client org, auto-set org context
211
+ // If user is assigned to a client org, auto-set org context and lock switcher
206
212
  if (d && d.clientOrgId) {
207
213
  localStorage.setItem('em_client_org_id', d.clientOrgId);
214
+ setSelectedOrgId(d.clientOrgId);
208
215
  } else {
209
216
  localStorage.removeItem('em_client_org_id');
210
217
  }
@@ -230,10 +237,15 @@ function App() {
230
237
  if (d.user.permissions) setPermissions(d.user.permissions);
231
238
  if (d.user.clientOrgId) {
232
239
  localStorage.setItem('em_client_org_id', d.user.clientOrgId);
240
+ // Auto-select the client org so all pages filter by it
241
+ onOrgChange(d.user.clientOrgId, null);
233
242
  apiCall('/organizations/' + d.user.clientOrgId).then(function(o) {
234
- if (o && o.name) setImpersonating(function(prev) { return prev ? Object.assign({}, prev, { user: Object.assign({}, prev.user, { clientOrgName: o.name }) }) : prev; });
243
+ if (o && o.name) {
244
+ setImpersonating(function(prev) { return prev ? Object.assign({}, prev, { user: Object.assign({}, prev.user, { clientOrgName: o.name }) }) : prev; });
245
+ onOrgChange(d.user.clientOrgId, o);
246
+ }
235
247
  }).catch(function() {});
236
- } else localStorage.removeItem('em_client_org_id');
248
+ } else { localStorage.removeItem('em_client_org_id'); onOrgChange('', null); }
237
249
  toast('Now viewing as ' + d.user.name, 'info');
238
250
  setPage('dashboard');
239
251
  }
@@ -248,6 +260,7 @@ function App() {
248
260
  return null;
249
261
  });
250
262
  localStorage.removeItem('em_client_org_id');
263
+ onOrgChange('', null); // Reset org selection back to platform org
251
264
  authCall('/me').then(d => { setUser(d.user || d); }).catch(() => {});
252
265
  apiCall('/me/permissions').then(d => { if (d && d.permissions) setPermissions(d.permissions); }).catch(() => {});
253
266
  toast('Stopped impersonation', 'success');
@@ -136,7 +136,11 @@ export function OrgContextSwitcher(props) {
136
136
  */
137
137
  export function useOrgContext() {
138
138
  var app = useApp();
139
- var selectedOrgId = app.selectedOrgId || '';
139
+ var user = app.user || {};
140
+ var userClientOrgId = user.clientOrgId || null;
141
+ var isLocked = !!userClientOrgId && user.role !== 'owner' && user.role !== 'admin';
142
+ // If user is org-bound (locked), always use their clientOrgId regardless of selectedOrgId
143
+ var selectedOrgId = isLocked ? userClientOrgId : (app.selectedOrgId || '');
140
144
  var selectedOrg = app.selectedOrg || null;
141
145
  var onOrgChange = app.onOrgChange || function() {};
142
146
 
@@ -382,6 +382,118 @@ export function OnboardingWizard({ onComplete }) {
382
382
 
383
383
  var set = function(k, v) { setForm(function(f) { return Object.assign({}, f, { [k]: v }); }); };
384
384
 
385
+ // ─── Smart DB URL Analysis & Auto-Optimization ─────
386
+
387
+ var analyzeDbUrl = function(url) {
388
+ if (!url) return null;
389
+ try {
390
+ var u = new URL(url);
391
+ var port = u.port || '5432';
392
+ var host = u.hostname || '';
393
+ var info = {
394
+ host: host, port: port, provider: null, isPooler: false, poolerMode: null,
395
+ directUrl: null, optimizedUrl: null, warnings: [], tips: [], autoFixed: []
396
+ };
397
+
398
+ // ── Supabase Detection ──────────────────────────
399
+ if (host.includes('.supabase.co') || host.includes('pooler.supabase.com')) {
400
+ info.provider = 'supabase';
401
+ var projectRef = u.username.replace('postgres.', '');
402
+
403
+ if (host.includes('pooler.supabase.com')) {
404
+ info.isPooler = true;
405
+ info.poolerMode = port === '6543' ? 'transaction' : port === '5432' ? 'session' : 'unknown';
406
+
407
+ // Build direct URL: db.{ref}.supabase.co:5432
408
+ var directU = new URL(url);
409
+ directU.hostname = 'db.' + projectRef + '.supabase.co';
410
+ directU.port = '5432';
411
+ directU.searchParams.delete('pgbouncer');
412
+ info.directUrl = directU.toString();
413
+
414
+ if (port === '5432') {
415
+ // Auto-fix: switch from session mode (5432) to transaction mode (6543)
416
+ var fixedU = new URL(url);
417
+ fixedU.port = '6543';
418
+ fixedU.searchParams.set('pgbouncer', 'true');
419
+ info.optimizedUrl = fixedU.toString();
420
+ info.autoFixed.push('Switched from session mode (port 5432) to transaction mode (port 6543) — higher connection limits and better for multi-process setups.');
421
+ info.autoFixed.push('Added ?pgbouncer=true for proper connection pooling.');
422
+ } else if (port === '6543') {
423
+ // Already on transaction mode — ensure pgbouncer param is set
424
+ if (!u.searchParams.get('pgbouncer')) {
425
+ var optU = new URL(url);
426
+ optU.searchParams.set('pgbouncer', 'true');
427
+ info.optimizedUrl = optU.toString();
428
+ info.autoFixed.push('Added ?pgbouncer=true parameter for proper PgBouncer transaction mode handling.');
429
+ }
430
+ info.tips.push('Transaction mode pooler detected — optimal for production.');
431
+ }
432
+ info.tips.push('Direct URL auto-generated for migrations (bypasses pooler for DDL operations).');
433
+
434
+ } else if (host.startsWith('db.') || host.includes('.supabase.co')) {
435
+ // Direct connection — build pooler URL for them
436
+ info.directUrl = url;
437
+ var region = host.match(/db\.([^.]+)\.supabase\.co/)?.[1] || projectRef;
438
+ // Try to detect region from hostname pattern
439
+ var poolerU = new URL(url);
440
+ // Supabase pooler format: aws-0-{region}.pooler.supabase.com
441
+ poolerU.hostname = 'aws-0-us-east-1.pooler.supabase.com'; // default, user may need to adjust
442
+ poolerU.port = '6543';
443
+ poolerU.username = 'postgres.' + region;
444
+ poolerU.searchParams.set('pgbouncer', 'true');
445
+ info.warnings.push('Direct connection detected. For production with multiple agents, use the Supabase connection pooler.');
446
+ info.tips.push('Go to Supabase Dashboard > Settings > Database > Connection string > URI, and select "Transaction mode" to get the correct pooler URL.');
447
+ }
448
+ }
449
+ // ── Neon Detection ──────────────────────────────
450
+ else if (host.includes('.neon.tech')) {
451
+ info.provider = 'neon';
452
+ info.isPooler = host.includes('-pooler');
453
+ if (!info.isPooler) {
454
+ // Auto-fix: add -pooler to hostname
455
+ var neonFixedU = new URL(url);
456
+ var parts = neonFixedU.hostname.split('.');
457
+ if (parts[0] && !parts[0].endsWith('-pooler')) {
458
+ parts[0] = parts[0] + '-pooler';
459
+ neonFixedU.hostname = parts.join('.');
460
+ info.optimizedUrl = neonFixedU.toString();
461
+ info.autoFixed.push('Added connection pooler endpoint (-pooler) for better connection handling.');
462
+ }
463
+ // Direct URL is the original
464
+ info.directUrl = url;
465
+ info.tips.push('Direct URL saved for migrations.');
466
+ } else {
467
+ // Already pooled — generate direct URL
468
+ var neonDirectU = new URL(url);
469
+ var neonParts = neonDirectU.hostname.split('.');
470
+ if (neonParts[0]) {
471
+ neonParts[0] = neonParts[0].replace(/-pooler$/, '');
472
+ neonDirectU.hostname = neonParts.join('.');
473
+ }
474
+ info.directUrl = neonDirectU.toString();
475
+ info.tips.push('Neon pooler detected — optimal for production. Direct URL auto-generated for migrations.');
476
+ }
477
+ }
478
+ // ── Generic Postgres ────────────────────────────
479
+ else {
480
+ info.provider = 'postgres';
481
+ // Check for common PgBouncer indicators
482
+ if (port === '6432' || port === '6543' || u.searchParams.get('pgbouncer') === 'true') {
483
+ info.isPooler = true;
484
+ info.poolerMode = 'transaction';
485
+ info.tips.push('PgBouncer detected. Connection pooling will be configured automatically.');
486
+ }
487
+ }
488
+
489
+ return info;
490
+ } catch { return null; }
491
+ };
492
+
493
+ var dbUrlInfo = useMemo(function() {
494
+ return analyzeDbUrl(form.dbConnectionString);
495
+ }, [form.dbConnectionString]);
496
+
385
497
  // ─── DB Config Builder ──────────────────────────────
386
498
 
387
499
  var buildDbConfig = function() {
@@ -389,7 +501,17 @@ export function OnboardingWizard({ onComplete }) {
389
501
  if (t === 'sqlite') return { type: 'sqlite' };
390
502
  if (t === 'turso') return { type: 'turso', connectionString: form.dbConnectionString, authToken: form.dbAuthToken };
391
503
  if (t === 'dynamodb') return { type: 'dynamodb', region: form.dbRegion, accessKeyId: form.dbAccessKey, secretAccessKey: form.dbSecretKey };
392
- return { type: t, connectionString: form.dbConnectionString };
504
+ // Use optimized URL if available (auto-fixed pooler mode, pgbouncer param, etc.)
505
+ var connStr = (dbUrlInfo && dbUrlInfo.optimizedUrl) ? dbUrlInfo.optimizedUrl : form.dbConnectionString;
506
+ var config = { type: t, connectionString: connStr };
507
+ // Auto-attach smart DB metadata for Postgres-family databases
508
+ if (dbUrlInfo) {
509
+ config.poolerDetected = dbUrlInfo.isPooler || !!dbUrlInfo.optimizedUrl;
510
+ config.poolerMode = dbUrlInfo.poolerMode || (dbUrlInfo.optimizedUrl ? 'transaction' : null);
511
+ config.directUrl = dbUrlInfo.directUrl;
512
+ config.provider = dbUrlInfo.provider;
513
+ }
514
+ return config;
393
515
  };
394
516
 
395
517
  // ─── Actions ────────────────────────────────────────
@@ -528,9 +650,43 @@ export function OnboardingWizard({ onComplete }) {
528
650
  );
529
651
  }
530
652
  // Connection string types: postgres, mysql, mongodb, supabase, neon, planetscale, cockroachdb
531
- return h('div', { className: 'form-group' },
532
- h('label', { className: 'form-label' }, 'Connection String'),
533
- h('input', { className: 'input', value: form.dbConnectionString, onChange: function(e) { set('dbConnectionString', e.target.value); }, placeholder: currentDbType ? currentDbType.placeholder : '' })
653
+ var urlHints = dbUrlInfo && form.dbConnectionString.length > 10;
654
+ return h(Fragment, null,
655
+ h('div', { className: 'form-group' },
656
+ h('label', { className: 'form-label' }, 'Connection String'),
657
+ h('input', { className: 'input', value: form.dbConnectionString, onChange: function(e) { set('dbConnectionString', e.target.value); }, placeholder: currentDbType ? currentDbType.placeholder : '' })
658
+ ),
659
+ // Smart URL analysis hints
660
+ urlHints && h('div', { style: { margin: '-8px 0 12px', fontSize: 12, lineHeight: 1.6 } },
661
+ dbUrlInfo.provider && dbUrlInfo.provider !== 'postgres' && h('div', { style: { color: 'var(--accent)', fontWeight: 500, marginBottom: 4 } },
662
+ dbUrlInfo.provider === 'supabase' ? '\uD83D\uDFE2 Supabase' : dbUrlInfo.provider === 'neon' ? '\uD83D\uDFE2 Neon' : dbUrlInfo.provider,
663
+ ' detected',
664
+ dbUrlInfo.isPooler || dbUrlInfo.optimizedUrl ? ' \u2014 connection will be auto-optimized' : ' (direct connection)'
665
+ ),
666
+ // Auto-fix notifications (green — things we fixed for them)
667
+ dbUrlInfo.autoFixed && dbUrlInfo.autoFixed.map(function(f, i) {
668
+ return h('div', { key: 'f' + i, style: { color: '#10b981', padding: '4px 8px', background: 'rgba(16,185,129,0.08)', borderRadius: 6, marginBottom: 4 } },
669
+ '\u2728 Auto-configured: ', f
670
+ );
671
+ }),
672
+ // Warnings (yellow — things they need to act on)
673
+ dbUrlInfo.warnings.map(function(w, i) {
674
+ return h('div', { key: 'w' + i, style: { color: 'var(--warning, #f59e0b)', padding: '4px 8px', background: 'rgba(245,158,11,0.1)', borderRadius: 6, marginBottom: 4 } },
675
+ '\u26A0\uFE0F ', w
676
+ );
677
+ }),
678
+ // Tips (informational)
679
+ dbUrlInfo.tips.map(function(t, i) {
680
+ return h('div', { key: 't' + i, style: { color: 'var(--text-secondary)', padding: '2px 0' } },
681
+ '\u2714\uFE0F ', t
682
+ );
683
+ }),
684
+ // Summary of what will be sent
685
+ (dbUrlInfo.directUrl || dbUrlInfo.optimizedUrl) && h('div', { style: { color: 'var(--text-secondary)', padding: '6px 8px', background: 'var(--bg-secondary)', borderRadius: 6, marginTop: 4, fontSize: 11 } },
686
+ dbUrlInfo.optimizedUrl && h('div', null, '\uD83D\uDD17 Pooler URL: ', h('code', { style: { fontSize: 10 } }, dbUrlInfo.optimizedUrl.substring(0, 60) + '...')),
687
+ dbUrlInfo.directUrl && h('div', null, '\uD83D\uDD17 Direct URL (migrations): ', h('code', { style: { fontSize: 10 } }, dbUrlInfo.directUrl.substring(0, 60) + '...'))
688
+ )
689
+ )
534
690
  );
535
691
  };
536
692
 
@@ -231,6 +231,7 @@ var ACTIVITY_TYPE_COLORS = {
231
231
  created: '#6366f1', assigned: '#991b1b', started: '#06b6d4', in_progress: '#06b6d4',
232
232
  completed: '#15803d', failed: '#ef4444', cancelled: '#6b7394', delegated: '#a855f7',
233
233
  compaction: '#8b5cf6', error: '#ef4444',
234
+ crash: '#dc2626', recovery: '#f59e0b', note: '#3b82f6',
234
235
  };
235
236
 
236
237
  function ActivityLog(props) {
@@ -1084,7 +1085,6 @@ export function AgentTaskPipeline(props) {
1084
1085
  }
1085
1086
 
1086
1087
  return h(Fragment, null,
1087
- h(orgCtx.Switcher),
1088
1088
  active.length > 0 && h('div', { style: { marginBottom: 12 } },
1089
1089
  h('div', { style: { fontSize: 11, fontWeight: 600, color: STATUS_COLORS.in_progress, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 4 } },
1090
1090
  h('div', { style: { width: 6, height: 6, borderRadius: '50%', background: STATUS_COLORS.in_progress, animation: 'flowPulse 2s infinite' } }),
package/src/db/adapter.ts CHANGED
@@ -28,6 +28,8 @@ export interface DatabaseConfig {
28
28
  secretAccessKey?: string;
29
29
  /** Turso-specific */
30
30
  authToken?: string;
31
+ /** Direct connection URL (bypasses PgBouncer) — used for migrations */
32
+ directUrl?: string;
31
33
  /** Extra driver options */
32
34
  options?: Record<string, unknown>;
33
35
  }
package/src/db/factory.ts CHANGED
@@ -35,6 +35,84 @@ export async function createAdapter(config: DatabaseConfig): Promise<DatabaseAda
35
35
  return adapter;
36
36
  }
37
37
 
38
+ /**
39
+ * Smart config builder: auto-detects Supabase/Neon pooler URLs,
40
+ * sets optimal connection params, and derives a directUrl for migrations.
41
+ * Call this when building config from a raw DATABASE_URL env var.
42
+ */
43
+ export function smartDbConfig(connectionString: string, typeHint?: DatabaseType): DatabaseConfig {
44
+ const type: DatabaseType = typeHint || (connectionString.startsWith('postgres') ? 'postgres' : 'sqlite');
45
+ const config: DatabaseConfig = { type, connectionString };
46
+
47
+ if (type === 'sqlite') return config;
48
+
49
+ try {
50
+ const u = new URL(connectionString);
51
+ const host = u.hostname || '';
52
+ const port = u.port || '5432';
53
+
54
+ // ── Supabase detection ──────────────────────────
55
+ if (host.includes('pooler.supabase.com') || host.includes('.supabase.co')) {
56
+ const projectRef = u.username.replace('postgres.', '');
57
+
58
+ if (host.includes('pooler.supabase.com')) {
59
+ // Generate direct URL for migrations
60
+ const directU = new URL(connectionString);
61
+ directU.hostname = 'db.' + projectRef + '.supabase.co';
62
+ directU.port = '5432';
63
+ directU.searchParams.delete('pgbouncer');
64
+ config.directUrl = directU.toString();
65
+
66
+ // Auto-switch to transaction mode if on session mode
67
+ if (port === '5432') {
68
+ const fixedU = new URL(connectionString);
69
+ fixedU.port = '6543';
70
+ fixedU.searchParams.set('pgbouncer', 'true');
71
+ config.connectionString = fixedU.toString();
72
+ console.log('[db] Auto-optimized: Supabase session mode (5432) → transaction mode (6543)');
73
+ } else if (!u.searchParams.get('pgbouncer')) {
74
+ const fixedU = new URL(connectionString);
75
+ fixedU.searchParams.set('pgbouncer', 'true');
76
+ config.connectionString = fixedU.toString();
77
+ console.log('[db] Auto-configured: Added ?pgbouncer=true for Supabase transaction mode');
78
+ }
79
+ } else if (host.startsWith('db.')) {
80
+ // Direct connection — save as directUrl, but warn
81
+ config.directUrl = connectionString;
82
+ console.log('[db] Supabase direct connection detected. For production, use the pooler URL (port 6543).');
83
+ }
84
+ }
85
+ // ── Neon detection ──────────────────────────────
86
+ else if (host.includes('.neon.tech')) {
87
+ if (!host.includes('-pooler')) {
88
+ // Direct endpoint — save as directUrl, generate pooler
89
+ config.directUrl = connectionString;
90
+ const poolerU = new URL(connectionString);
91
+ const parts = poolerU.hostname.split('.');
92
+ if (parts[0] && !parts[0].endsWith('-pooler')) {
93
+ parts[0] = parts[0] + '-pooler';
94
+ poolerU.hostname = parts.join('.');
95
+ config.connectionString = poolerU.toString();
96
+ console.log('[db] Auto-optimized: Neon direct → pooler endpoint');
97
+ }
98
+ } else {
99
+ // Pooler endpoint — derive direct URL
100
+ const directU = new URL(connectionString);
101
+ const parts = directU.hostname.split('.');
102
+ if (parts[0]) {
103
+ parts[0] = parts[0].replace(/-pooler$/, '');
104
+ directU.hostname = parts.join('.');
105
+ }
106
+ config.directUrl = directU.toString();
107
+ }
108
+ }
109
+ } catch {
110
+ // Invalid URL — just pass through as-is
111
+ }
112
+
113
+ return config;
114
+ }
115
+
38
116
  export function getSupportedDatabases(): { type: DatabaseType; label: string; group: string }[] {
39
117
  return [
40
118
  { type: 'postgres', label: 'PostgreSQL', group: 'SQL' },
@@ -31,16 +31,27 @@ export class PostgresAdapter extends DatabaseAdapter {
31
31
  readonly type = 'postgres' as const;
32
32
  private pool: any = null;
33
33
  private ended = false;
34
+ private _directUrl?: string;
35
+ private _connectionString?: string;
36
+ private _isPgBouncer = false;
34
37
 
35
38
  async connect(config: DatabaseConfig): Promise<void> {
36
39
  this.ended = false;
37
40
  const { Pool } = await getPg();
38
41
 
39
42
  // ── Smart pool sizing ──────────────────────────────
40
- // Detect if using PgBouncer/Supabase pooler (port 6543) vs direct (5432)
41
- // and auto-configure pool size accordingly.
42
- const port = config.port || (config.connectionString ? new URL(config.connectionString).port : '5432');
43
- const isPgBouncer = String(port) === '6543' || String(port) === '5433';
43
+ // Detect if using PgBouncer/Supabase pooler from port, hostname, or query params
44
+ const connUrl = config.connectionString ? new URL(config.connectionString) : null;
45
+ const port = config.port || (connUrl ? connUrl.port : '5432');
46
+ const hostname = connUrl?.hostname || config.host || '';
47
+ const isPgBouncer = String(port) === '6543' || String(port) === '5433'
48
+ || hostname.includes('.pooler.supabase.') || hostname.includes('.pooler.')
49
+ || connUrl?.searchParams.get('pgbouncer') === 'true';
50
+
51
+ // Store for migrations and reconnection
52
+ this._directUrl = config.directUrl || undefined;
53
+ this._connectionString = config.connectionString;
54
+ this._isPgBouncer = isPgBouncer;
44
55
 
45
56
  // Pool size logic:
46
57
  // - PgBouncer (Supabase pooler): conservative — shared across processes
@@ -49,11 +60,11 @@ export class PostgresAdapter extends DatabaseAdapter {
49
60
  // - Direct connection: more generous
50
61
  // - Override via DB_POOL_MAX env var
51
62
  const envMax = process.env.DB_POOL_MAX ? parseInt(process.env.DB_POOL_MAX, 10) : 0;
52
- const defaultMax = isPgBouncer ? 4 : 10;
63
+ const defaultMax = isPgBouncer ? 3 : 10;
53
64
  const poolMax = envMax || defaultMax;
54
65
 
55
66
  // PgBouncer transaction mode: short idle timeout to release connections fast
56
- const idleTimeout = isPgBouncer ? 5000 : 30000;
67
+ const idleTimeout = isPgBouncer ? 2000 : 30000;
57
68
 
58
69
  this.pool = new Pool({
59
70
  connectionString: config.connectionString,
@@ -105,16 +116,21 @@ export class PostgresAdapter extends DatabaseAdapter {
105
116
  * admin shutdown (57P01), connection refused, client already released.
106
117
  */
107
118
  private async _query(sql: string, params?: any[]): Promise<any> {
108
- const RETRYABLE = new Set(['XX000', '53300', '57P01', 'ECONNREFUSED', 'ECONNRESET', '57P03']);
109
- for (let attempt = 0; attempt < 2; attempt++) {
119
+ const RETRYABLE = new Set(['XX000', '53300', '57P01', 'ECONNREFUSED', 'ECONNRESET', '57P03', '25P02']);
120
+ for (let attempt = 0; attempt < 3; attempt++) {
110
121
  try {
111
122
  return await this.pool.query(sql, params);
112
123
  } catch (err: any) {
113
124
  const code = err.code || '';
114
125
  const msg = err.message || '';
115
- const isRetryable = RETRYABLE.has(code) || msg.includes('remaining connection') || msg.includes('terminating connection') || msg.includes('Connection terminated');
116
- if (isRetryable && attempt === 0) {
117
- await new Promise(r => setTimeout(r, 500 + Math.random() * 500));
126
+ const isAbortedTx = code === '25P02' || msg.includes('current transaction is aborted');
127
+ const isRetryable = RETRYABLE.has(code) || msg.includes('remaining connection') || msg.includes('terminating connection') || msg.includes('Connection terminated') || msg.includes('MaxClientsInSessionMode');
128
+ if ((isRetryable || isAbortedTx) && attempt < 2) {
129
+ // For aborted transaction state, try to reset the connection by issuing ROLLBACK
130
+ if (isAbortedTx) {
131
+ try { await this.pool.query('ROLLBACK'); } catch {}
132
+ }
133
+ await new Promise(r => setTimeout(r, 500 + Math.random() * 1000));
118
134
  continue;
119
135
  }
120
136
  throw err;
@@ -154,7 +170,33 @@ export class PostgresAdapter extends DatabaseAdapter {
154
170
 
155
171
  async migrate(): Promise<void> {
156
172
  const stmts = getAllCreateStatements();
157
- const client = await this.pool.connect();
173
+
174
+ // For PgBouncer setups, try direct URL for migrations (DDL needs real transactions)
175
+ // Falls back to pooler connection if direct URL is unreachable
176
+ let directPool: any = null;
177
+ let client: any;
178
+ if (this._isPgBouncer && this._directUrl) {
179
+ try {
180
+ const { Pool } = await getPg();
181
+ directPool = new Pool({
182
+ connectionString: this._directUrl,
183
+ ssl: { rejectUnauthorized: false },
184
+ max: 1,
185
+ idleTimeoutMillis: 5000,
186
+ connectionTimeoutMillis: 8000,
187
+ });
188
+ directPool.on('error', () => {}); // Suppress stderr noise during fallback
189
+ client = await directPool.connect();
190
+ console.log('[postgres] Using direct connection for migrations (bypassing PgBouncer)');
191
+ } catch (err: any) {
192
+ console.warn(`[postgres] Direct connection unavailable (${err.message?.slice(0, 80)}), using pooler for migrations`);
193
+ if (directPool) { try { await directPool.end(); } catch {} }
194
+ directPool = null;
195
+ client = await this.pool.connect();
196
+ }
197
+ } else {
198
+ client = await this.pool.connect();
199
+ }
158
200
  try {
159
201
  await client.query('BEGIN');
160
202
  for (const stmt of stmts) {
@@ -247,6 +289,9 @@ export class PostgresAdapter extends DatabaseAdapter {
247
289
  throw err;
248
290
  } finally {
249
291
  client.release();
292
+ if (directPool) {
293
+ try { await directPool.end(); } catch {}
294
+ }
250
295
  }
251
296
  }
252
297
 
@@ -360,7 +360,7 @@ export class AgentAutonomyManager {
360
360
  - ${failedChats} chat message(s) may be unanswered
361
361
 
362
362
  Your morning routine:
363
- 1. Check your inbox with gmail_list (unread only) — scan subjects and senders
363
+ 1. Check your inbox with gmail_search (unread only) — scan subjects and senders
364
364
  2. For each important email, create a Google Task: google_tasks_create with title, notes, and priority
365
365
  3. Check Google Chat for any unanswered messages: google_chat_list_messages
366
366
  4. For any failed sessions that look important, add them as tasks too
@@ -693,7 +693,7 @@ export class AgentHeartbeatManager {
693
693
  ${summaries}
694
694
 
695
695
  For each item, take the appropriate action:
696
- - For unread emails: Check your inbox with gmail_list and respond to any urgent ones.
696
+ - For unread emails: Check your inbox with gmail_search and respond to any urgent ones.
697
697
  - For upcoming events: Check google_calendar_list for the next 2 hours and prepare.
698
698
  - For stale sessions: Review and close any stuck sessions.
699
699
  - For unanswered chats: Check Google Chat and respond.