@agenticmail/enterprise 0.5.212 → 0.5.214

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.
@@ -0,0 +1,45 @@
1
+ import {
2
+ AgentRuntime,
3
+ EmailChannel,
4
+ FollowUpScheduler,
5
+ SessionManager,
6
+ SubAgentManager,
7
+ ToolRegistry,
8
+ callLLM,
9
+ createAgentRuntime,
10
+ createNoopHooks,
11
+ createRuntimeHooks,
12
+ estimateMessageTokens,
13
+ estimateTokens,
14
+ executeTool,
15
+ runAgentLoop,
16
+ toolsToDefinitions
17
+ } from "./chunk-NJRHVKVY.js";
18
+ import {
19
+ PROVIDER_REGISTRY,
20
+ listAllProviders,
21
+ resolveApiKeyForProvider,
22
+ resolveProvider
23
+ } from "./chunk-UF3ZJMJO.js";
24
+ import "./chunk-KFQGP6VL.js";
25
+ export {
26
+ AgentRuntime,
27
+ EmailChannel,
28
+ FollowUpScheduler,
29
+ PROVIDER_REGISTRY,
30
+ SessionManager,
31
+ SubAgentManager,
32
+ ToolRegistry,
33
+ callLLM,
34
+ createAgentRuntime,
35
+ createNoopHooks,
36
+ createRuntimeHooks,
37
+ estimateMessageTokens,
38
+ estimateTokens,
39
+ executeTool,
40
+ listAllProviders,
41
+ resolveApiKeyForProvider,
42
+ resolveProvider,
43
+ runAgentLoop,
44
+ toolsToDefinitions
45
+ };
@@ -0,0 +1,15 @@
1
+ import {
2
+ createServer
3
+ } from "./chunk-IP5HZWZM.js";
4
+ import "./chunk-OF4MUWWS.js";
5
+ import "./chunk-UF3ZJMJO.js";
6
+ import "./chunk-3OC6RH7W.js";
7
+ import "./chunk-2DDKGTD6.js";
8
+ import "./chunk-YVK6F5OD.js";
9
+ import "./chunk-MKRNEM5A.js";
10
+ import "./chunk-DRXMYYKN.js";
11
+ import "./chunk-6WSX7QXF.js";
12
+ import "./chunk-KFQGP6VL.js";
13
+ export {
14
+ createServer
15
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ promptCompanyInfo,
3
+ promptDatabase,
4
+ promptDeployment,
5
+ promptDomain,
6
+ promptRegistration,
7
+ provision,
8
+ runSetupWizard
9
+ } from "./chunk-QRRW3LES.js";
10
+ import "./chunk-VQQ4SYYQ.js";
11
+ import "./chunk-KFQGP6VL.js";
12
+ export {
13
+ promptCompanyInfo,
14
+ promptDatabase,
15
+ promptDeployment,
16
+ promptDomain,
17
+ promptRegistration,
18
+ provision,
19
+ runSetupWizard
20
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.212",
3
+ "version": "0.5.214",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,8 +14,10 @@ var PAD = 40;
14
14
  var STATUS_COLORS = { created: '#6366f1', assigned: '#f59e0b', in_progress: '#06b6d4', completed: '#22c55e', failed: '#ef4444', cancelled: '#6b7394' };
15
15
  var PRIORITY_COLORS = { urgent: '#ef4444', high: '#f59e0b', normal: '#6366f1', low: '#6b7394' };
16
16
  var DELEGATION_COLORS = { delegation: '#6366f1', review: '#f59e0b', revision: '#f97316', escalation: '#ef4444', return: '#22c55e' };
17
- var BG = '#0a0c14';
18
- var EDGE_COLOR = 'rgba(255,255,255,0.18)';
17
+ // Theme-aware: use CSS variables where possible, detect dark/light
18
+ function isDark() { try { return window.matchMedia('(prefers-color-scheme: dark)').matches || document.documentElement.classList.contains('dark') || document.body.getAttribute('data-theme') === 'dark'; } catch(e) { return true; } }
19
+ var BG = 'var(--bg-canvas, var(--bg-primary, #0a0c14))';
20
+ var EDGE_COLOR = 'var(--tp-edge, rgba(128,128,128,0.25))';
19
21
  var EDGE_HL = 'rgba(99,102,241,0.7)';
20
22
 
21
23
  // ─── CSS Keyframes (injected once) ──────────────────────
@@ -24,13 +26,15 @@ function injectCSS() {
24
26
  if (_injected) return; _injected = true;
25
27
  var style = document.createElement('style');
26
28
  style.textContent = `
29
+ :root { --tp-bg: #0a0c14; --tp-text: #fff; --tp-text-dim: rgba(255,255,255,0.4); --tp-text-faint: rgba(255,255,255,0.15); --tp-border: rgba(255,255,255,0.08); --tp-card: rgba(255,255,255,0.02); --tp-card-hover: rgba(255,255,255,0.06); --tp-edge: rgba(255,255,255,0.18); --tp-toolbar: rgba(0,0,0,0.3); --tp-metrics: rgba(0,0,0,0.12); }
30
+ [data-theme="light"], .light, :root:not(.dark) { --tp-bg: var(--bg-primary, #f8fafc); --tp-text: var(--text-primary, #1e293b); --tp-text-dim: var(--text-muted, #64748b); --tp-text-faint: rgba(0,0,0,0.06); --tp-border: var(--border, rgba(0,0,0,0.08)); --tp-card: rgba(0,0,0,0.02); --tp-card-hover: rgba(0,0,0,0.05); --tp-edge: rgba(0,0,0,0.2); --tp-toolbar: rgba(0,0,0,0.03); --tp-metrics: rgba(0,0,0,0.02); }
31
+ @media (prefers-color-scheme: light) { :root:not(.dark) { --tp-bg: var(--bg-primary, #f8fafc); --tp-text: var(--text-primary, #1e293b); --tp-text-dim: var(--text-muted, #64748b); --tp-text-faint: rgba(0,0,0,0.06); --tp-border: var(--border, rgba(0,0,0,0.08)); --tp-card: rgba(0,0,0,0.02); --tp-card-hover: rgba(0,0,0,0.05); --tp-edge: rgba(0,0,0,0.2); --tp-toolbar: rgba(0,0,0,0.03); --tp-metrics: rgba(0,0,0,0.02); } }
27
32
  @keyframes flowDash { to { stroke-dashoffset: -24; } }
28
33
  @keyframes flowPulse { 0%,100% { opacity: 0.4; } 50% { opacity: 1; } }
29
34
  @keyframes taskPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(6,182,212,0.3); } 50% { box-shadow: 0 0 8px 2px rgba(6,182,212,0.2); } }
30
35
  .tp-flow-active { animation: flowDash 1.2s linear infinite; }
31
36
  .tp-node-active { animation: taskPulse 2s ease-in-out infinite; }
32
37
  .tp-node:hover { transform: scale(1.03); z-index: 10; }
33
- .tp-customer-badge { background: linear-gradient(135deg, #6366f1, #a855f7); color: #fff; font-size: 9px; padding: 1px 6px; border-radius: 8px; font-weight: 600; }
34
38
  .tp-chain-tag { font-size: 9px; padding: 1px 5px; border-radius: 4px; font-weight: 600; letter-spacing: 0.02em; }
35
39
  `;
36
40
  document.head.appendChild(style);
@@ -173,13 +177,13 @@ function formatDuration(ms) {
173
177
  function tag(color, text) { return h('span', { className: 'tp-chain-tag', style: { background: color + '22', color: color } }, text); }
174
178
 
175
179
  var toolbarBtnStyle = {
176
- background: 'rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.12)',
177
- borderRadius: 6, color: '#fff', fontSize: 12, fontWeight: 600, padding: '4px 10px', cursor: 'pointer',
180
+ background: 'var(--tp-border)', border: '1px solid rgba(255,255,255,0.12)',
181
+ borderRadius: 6, color: 'var(--tp-text)', fontSize: 12, fontWeight: 600, padding: '4px 10px', cursor: 'pointer',
178
182
  };
179
183
  function legendDot(color, label) {
180
184
  return h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
181
185
  h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: color } }),
182
- h('span', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 11 } }, label)
186
+ h('span', { style: { color: 'var(--tp-text-dim)', fontSize: 11 } }, label)
183
187
  );
184
188
  }
185
189
  var _h4 = { marginTop: 16, marginBottom: 8, fontSize: 14 };
@@ -191,10 +195,10 @@ function CustomerBadge(props) {
191
195
  var c = props.customer;
192
196
  if (!c) return null;
193
197
  return h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, padding: '4px 8px', background: 'rgba(99,102,241,0.08)', border: '1px solid rgba(99,102,241,0.2)', borderRadius: 8, fontSize: 11, marginBottom: 4 } },
194
- h('div', { style: { width: 20, height: 20, borderRadius: '50%', background: 'linear-gradient(135deg, #6366f1, #a855f7)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 9, fontWeight: 700, flexShrink: 0 } }, (c.name || '?').charAt(0).toUpperCase()),
198
+ h('div', { style: { width: 20, height: 20, borderRadius: '50%', background: 'linear-gradient(135deg, #6366f1, #a855f7)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--tp-text)', fontSize: 9, fontWeight: 700, flexShrink: 0 } }, (c.name || '?').charAt(0).toUpperCase()),
195
199
  h('div', { style: { overflow: 'hidden' } },
196
- h('div', { style: { fontWeight: 600, color: '#fff', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, c.name || 'Unknown'),
197
- h('div', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 9 } },
200
+ h('div', { style: { fontWeight: 600, color: 'var(--tp-text)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, c.name || 'Unknown'),
201
+ h('div', { style: { color: 'var(--tp-text-dim)', fontSize: 9 } },
198
202
  c.isNew ? 'New customer' : (c.company || c.email || c.channel || '')
199
203
  )
200
204
  )
@@ -227,7 +231,7 @@ function TaskDetail(props) {
227
231
 
228
232
  // Customer context
229
233
  task.customerContext && h('div', { style: { padding: 12, background: 'rgba(99,102,241,0.06)', border: '1px solid rgba(99,102,241,0.15)', borderRadius: 'var(--radius)', marginBottom: 16 } },
230
- h('div', { style: { fontSize: 11, fontWeight: 600, color: 'rgba(255,255,255,0.5)', marginBottom: 8 } }, 'CUSTOMER'),
234
+ h('div', { style: { fontSize: 11, fontWeight: 600, color: 'var(--tp-text-dim)', marginBottom: 8 } }, 'CUSTOMER'),
231
235
  h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6px 16px', fontSize: 13 } },
232
236
  task.customerContext.name && h(Fragment, null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, 'Name'), h('div', null, task.customerContext.name)),
233
237
  task.customerContext.email && h(Fragment, null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11 } }, 'Email'), h('div', null, task.customerContext.email)),
@@ -267,7 +271,7 @@ function TaskDetail(props) {
267
271
  return h(Fragment, { key: ct.id },
268
272
  i > 0 && h('div', { style: { display: 'flex', alignItems: 'center', flexShrink: 0 } },
269
273
  h('div', { style: { width: 32, height: 2, background: (DELEGATION_COLORS[ct.delegationType] || '#6366f1') + '66' } }),
270
- h('div', { style: { fontSize: 8, color: 'rgba(255,255,255,0.4)', position: 'relative', top: -8 } }, ct.delegationType || '')
274
+ h('div', { style: { fontSize: 8, color: 'var(--tp-text-dim)', position: 'relative', top: -8 } }, ct.delegationType || '')
271
275
  ),
272
276
  h('div', { style: {
273
277
  padding: '6px 10px', borderRadius: 8, fontSize: 11, flexShrink: 0,
@@ -312,123 +316,39 @@ function TaskDetail(props) {
312
316
  function MetricsBar(props) {
313
317
  var s = props.stats;
314
318
  function chip(label, value, color) {
315
- return h('div', { style: { display: 'flex', alignItems: 'center', gap: 4, padding: '3px 8px', background: 'rgba(255,255,255,0.04)', borderRadius: 6 } },
316
- h('span', { style: { fontSize: 10, color: 'rgba(255,255,255,0.35)' } }, label),
319
+ return h('div', { style: { display: 'flex', alignItems: 'center', gap: 4, padding: '3px 8px', background: 'var(--tp-card)', borderRadius: 6 } },
320
+ h('span', { style: { fontSize: 10, color: 'var(--tp-text-dim)' } }, label),
317
321
  h('span', { style: { fontSize: 11, fontWeight: 700, color: color } }, value)
318
322
  );
319
323
  }
320
324
  var hasActivity = s.total > 0;
321
325
 
322
- return h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, padding: '6px 16px', borderBottom: '1px solid rgba(255,255,255,0.06)', background: 'rgba(0,0,0,0.12)', flexShrink: 0, overflowX: 'auto', fontSize: 11 } },
323
- h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.25)', fontWeight: 600, letterSpacing: '0.06em', marginRight: 2 } }, 'TODAY'),
326
+ return h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, padding: '6px 16px', borderBottom: '1px solid var(--tp-border)', background: 'var(--tp-metrics)', flexShrink: 0, overflowX: 'auto', fontSize: 11 } },
327
+ h('span', { style: { fontSize: 9, color: 'var(--tp-text-faint)', fontWeight: 600, letterSpacing: '0.06em', marginRight: 2 } }, 'TODAY'),
324
328
  chip('Done', s.todayCompleted || 0, '#22c55e'),
325
329
  chip('Active', s.inProgress || 0, '#06b6d4'),
326
330
  chip('New', s.todayCreated || 0, '#f59e0b'),
327
331
  s.todayFailed > 0 && chip('Failed', s.todayFailed, '#ef4444'),
328
- hasActivity && h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.08)' } }),
329
- hasActivity && h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.25)', fontWeight: 600, letterSpacing: '0.06em' } }, 'ALL'),
332
+ hasActivity && h('div', { style: { width: 1, height: 14, background: 'var(--tp-border)' } }),
333
+ hasActivity && h('span', { style: { fontSize: 9, color: 'var(--tp-text-faint)', fontWeight: 600, letterSpacing: '0.06em' } }, 'ALL'),
330
334
  hasActivity && chip('Total', s.total, 'rgba(255,255,255,0.6)'),
331
335
  s.avgDurationMs > 0 && chip('Avg', formatDuration(s.avgDurationMs), '#fff'),
332
336
  s.totalTokens > 0 && chip('Tokens', s.totalTokens > 999999 ? (s.totalTokens / 1000000).toFixed(1) + 'M' : s.totalTokens > 999 ? (s.totalTokens / 1000).toFixed(1) + 'K' : s.totalTokens, '#a855f7'),
333
337
  s.totalCost > 0 && chip('Cost', '$' + s.totalCost.toFixed(2), '#22c55e'),
334
338
  s.topAgents && s.topAgents.length > 0 && h(Fragment, null,
335
- h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.08)' } }),
339
+ h('div', { style: { width: 1, height: 14, background: 'var(--tp-border)' } }),
336
340
  s.topAgents.slice(0, 3).map(function(a) {
337
- return h('div', { key: a.agent, style: { display: 'flex', alignItems: 'center', gap: 3, padding: '2px 6px', background: 'rgba(255,255,255,0.04)', borderRadius: 6 } },
341
+ return h('div', { key: a.agent, style: { display: 'flex', alignItems: 'center', gap: 3, padding: '2px 6px', background: 'var(--tp-card)', borderRadius: 6 } },
338
342
  h('div', { style: { width: 12, height: 12, borderRadius: '50%', background: '#6366f133', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 7, fontWeight: 700, color: '#6366f1' } }, (a.name || '?').charAt(0).toUpperCase()),
339
- h('span', { style: { fontSize: 10, color: '#fff', fontWeight: 600 } }, a.name),
340
- h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.3)' } }, a.completed + '/' + a.active)
343
+ h('span', { style: { fontSize: 10, color: 'var(--tp-text)', fontWeight: 600 } }, a.name),
344
+ h('span', { style: { fontSize: 9, color: 'var(--tp-text-dim)' } }, a.completed + '/' + a.active)
341
345
  );
342
346
  })
343
347
  )
344
348
  );
345
349
  }
346
350
 
347
- // ─── Inline Chain Flowchart (appears below canvas when task expanded)
348
- function ChainFlowInline(props) {
349
- var chain = props.chain;
350
- var taskId = props.taskId;
351
- var onClose = props.onClose;
352
- var onClickTask = props.onClickTask;
353
- if (!chain || chain.length === 0) return null;
354
-
355
- var MINI_W = 180;
356
- var MINI_H = 56;
357
- var MINI_GAP = 40;
358
- var totalW = chain.length * MINI_W + (chain.length - 1) * MINI_GAP + 40;
359
-
360
- return h('div', { style: { borderTop: '1px solid rgba(255,255,255,0.08)', background: 'rgba(0,0,0,0.25)', padding: '12px 16px', flexShrink: 0, overflow: 'auto' } },
361
- h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 } },
362
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
363
- h('span', { style: { fontSize: 11, fontWeight: 600, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.06em' } }, 'TASK FLOW'),
364
- h('span', { style: { fontSize: 10, color: 'rgba(255,255,255,0.3)', fontFamily: 'var(--font-mono)' } }, chain[0].chainId ? '#' + chain[0].chainId.slice(0, 8) : ''),
365
- h('span', { style: { fontSize: 10, color: 'rgba(255,255,255,0.3)' } }, chain.length + ' step' + (chain.length > 1 ? 's' : ''))
366
- ),
367
- h('button', { onClick: onClose, style: { background: 'none', border: 'none', color: 'rgba(255,255,255,0.3)', cursor: 'pointer', fontSize: 16, padding: '0 4px' } }, '\u00D7')
368
- ),
369
- h('div', { style: { position: 'relative', height: MINI_H + 20, minWidth: totalW } },
370
- // SVG arrows
371
- h('svg', { width: totalW, height: MINI_H + 20, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none' } },
372
- h('defs', null,
373
- h('marker', { id: 'cf-arr', markerWidth: 6, markerHeight: 4, refX: 6, refY: 2, orient: 'auto' },
374
- h('polygon', { points: '0 0, 6 2, 0 4', fill: 'rgba(99,102,241,0.5)' })
375
- )
376
- ),
377
- chain.map(function(ct, i) {
378
- if (i === 0) return null;
379
- var x1 = 20 + (i - 1) * (MINI_W + MINI_GAP) + MINI_W;
380
- var x2 = 20 + i * (MINI_W + MINI_GAP);
381
- var y = 10 + MINI_H / 2;
382
- var dType = ct.delegationType || 'delegation';
383
- var color = DELEGATION_COLORS[dType] || 'rgba(99,102,241,0.5)';
384
- var isActive = chain[i - 1].status === 'in_progress' || ct.status === 'in_progress';
385
- return h(Fragment, { key: 'e' + i },
386
- h('line', { x1: x1, y1: y, x2: x2, y2: y, stroke: color + '88', strokeWidth: 2, markerEnd: 'url(#cf-arr)' }),
387
- isActive && h('line', { x1: x1, y1: y, x2: x2, y2: y, stroke: STATUS_COLORS.in_progress, strokeWidth: 2, strokeDasharray: '4 12', className: 'tp-flow-active', style: { opacity: 0.7 } }),
388
- dType !== 'delegation' && h('text', { x: (x1 + x2) / 2, y: y - 6, fill: color, fontSize: 8, textAnchor: 'middle', fontWeight: 600 }, dType)
389
- );
390
- })
391
- ),
392
- // Task nodes
393
- chain.map(function(ct, i) {
394
- var x = 20 + i * (MINI_W + MINI_GAP);
395
- var sc = STATUS_COLORS[ct.status] || '#6b7394';
396
- var isMe = ct.id === taskId;
397
- var isActive = ct.status === 'in_progress';
398
- return h('div', {
399
- key: ct.id,
400
- onClick: function() { if (onClickTask) onClickTask(ct); },
401
- style: {
402
- position: 'absolute', left: x, top: 10, width: MINI_W, height: MINI_H,
403
- background: isMe ? sc + '15' : 'rgba(255,255,255,0.02)',
404
- border: '1.5px solid ' + (isMe ? sc : 'rgba(255,255,255,0.1)'),
405
- borderLeft: '3px solid ' + sc,
406
- borderRadius: 8, padding: '6px 10px', cursor: 'pointer',
407
- display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 3,
408
- transition: 'all 0.15s',
409
- },
410
- onMouseEnter: function(e) { e.currentTarget.style.background = 'rgba(255,255,255,0.05)'; },
411
- onMouseLeave: function(e) { e.currentTarget.style.background = isMe ? sc + '15' : 'rgba(255,255,255,0.02)'; },
412
- },
413
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 5 } },
414
- h('div', { style: { width: 16, height: 16, borderRadius: '50%', background: sc + '33', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 7, fontWeight: 700, color: sc, flexShrink: 0 } },
415
- (ct.assignedToName || '?').charAt(0).toUpperCase()
416
- ),
417
- h('span', { style: { fontSize: 10, fontWeight: 600, color: '#fff', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 } }, ct.assignedToName || ct.assignedTo)
418
- ),
419
- h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
420
- tag(sc, ct.status.replace('_', ' ')),
421
- h('span', { style: { fontSize: 8, color: 'rgba(255,255,255,0.3)' } }, timeAgo(ct.createdAt)),
422
- ct.actualDurationMs && h('span', { style: { fontSize: 8, color: 'rgba(255,255,255,0.3)', marginLeft: 'auto' } }, formatDuration(ct.actualDurationMs))
423
- ),
424
- isActive && ct.progress > 0 && h('div', { style: { height: 2, background: 'rgba(255,255,255,0.08)', borderRadius: 1, overflow: 'hidden' } },
425
- h('div', { style: { height: '100%', width: ct.progress + '%', background: sc, borderRadius: 1 } })
426
- )
427
- );
428
- })
429
- )
430
- );
431
- }
351
+ // (ChainFlowInline removed chain flow now renders inline on canvas)
432
352
 
433
353
  // ─── Main Page ───────────────────────────────────────────
434
354
  export function TaskPipelinePage() {
@@ -612,8 +532,8 @@ export function TaskPipelinePage() {
612
532
  var hoveredNode = hoveredId ? nodes.find(function(n) { return n.id === hoveredId; }) : null;
613
533
 
614
534
  // ─── Toolbar ─────────────────────────────────────────
615
- var toolbar = h('div', { style: { display: 'flex', alignItems: 'center', gap: 10, padding: '10px 16px', borderBottom: '1px solid rgba(255,255,255,0.08)', background: 'rgba(0,0,0,0.3)', flexShrink: 0, flexWrap: 'wrap' } },
616
- h('div', { style: { fontWeight: 700, fontSize: 14, color: '#fff', display: 'flex', alignItems: 'center', gap: 6 } },
535
+ var toolbar = h('div', { style: { display: 'flex', alignItems: 'center', gap: 10, padding: '10px 16px', borderBottom: '1px solid var(--tp-border)', background: 'var(--tp-toolbar)', flexShrink: 0, flexWrap: 'wrap' } },
536
+ h('div', { style: { fontWeight: 700, fontSize: 14, color: 'var(--tp-text)', display: 'flex', alignItems: 'center', gap: 6 } },
617
537
  I.workflow(), 'Task Pipeline',
618
538
  h(HelpButton, { label: 'Task Pipeline' },
619
539
  h('p', null, 'Visual flow of all agent tasks. Tasks flow left-to-right showing delegation chains, multi-agent handoffs, and circular review loops.'),
@@ -638,9 +558,9 @@ export function TaskPipelinePage() {
638
558
  ),
639
559
  h('div', { style: { display: 'flex', alignItems: 'center', gap: 4 } },
640
560
  h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: '#22c55e', animation: 'flowPulse 2s infinite' } }),
641
- h('span', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } }, 'Live')
561
+ h('span', { style: { color: 'var(--tp-text-dim)', fontSize: 11 } }, 'Live')
642
562
  ),
643
- h('div', { style: { color: 'rgba(255,255,255,0.4)', fontSize: 11 } },
563
+ h('div', { style: { color: 'var(--tp-text-dim)', fontSize: 11 } },
644
564
  (stats.inProgress || 0) + ' active \u00B7 ' + (stats.completed || 0) + ' done \u00B7 ' + (stats.total || 0) + ' total'
645
565
  ),
646
566
  h('div', { style: { flex: 1 } }),
@@ -654,7 +574,7 @@ export function TaskPipelinePage() {
654
574
  legendDot(STATUS_COLORS.failed, 'Failed'),
655
575
  h('div', { style: { width: 1, height: 14, background: 'rgba(255,255,255,0.12)' } }),
656
576
  h('button', { onClick: function() { setZoom(function(z) { return Math.min(3, z + 0.2); }); }, style: toolbarBtnStyle }, '+'),
657
- h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 11, minWidth: 36, textAlign: 'center' } }, Math.round(zoom * 100) + '%'),
577
+ h('div', { style: { color: 'var(--tp-text-dim)', fontSize: 11, minWidth: 36, textAlign: 'center' } }, Math.round(zoom * 100) + '%'),
658
578
  h('button', { onClick: function() { setZoom(function(z) { return Math.max(0.15, z - 0.2); }); }, style: toolbarBtnStyle }, '\u2212'),
659
579
  h('button', { onClick: fitToView, style: toolbarBtnStyle }, 'Fit'),
660
580
  h('button', { onClick: loadData, style: toolbarBtnStyle }, 'Refresh'),
@@ -662,13 +582,13 @@ export function TaskPipelinePage() {
662
582
 
663
583
  if (loading) return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading task pipeline...');
664
584
 
665
- if (nodes.length === 0) return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
585
+ if (nodes.length === 0) return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--tp-bg)', borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
666
586
  toolbar,
667
587
  h(MetricsBar, { stats: stats }),
668
588
  h('div', { style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' } },
669
589
  h('div', { style: { width: 48, height: 48, borderRadius: 12, background: 'rgba(99,102,241,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 16, color: '#6366f1' } }, I.workflow()),
670
- h('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 6, color: '#fff' } }, 'No Tasks in Pipeline'),
671
- h('div', { style: { color: 'rgba(255,255,255,0.5)', fontSize: 13 } }, 'Tasks will appear here as agents are assigned work.')
590
+ h('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 6, color: 'var(--tp-text)' } }, 'No Tasks in Pipeline'),
591
+ h('div', { style: { color: 'var(--tp-text-dim)', fontSize: 13 } }, 'Tasks will appear here as agents are assigned work.')
672
592
  )
673
593
  );
674
594
 
@@ -684,7 +604,7 @@ export function TaskPipelinePage() {
684
604
  }
685
605
  }
686
606
 
687
- return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: BG, borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
607
+ return h('div', { style: { height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--tp-bg)', borderRadius: 'var(--radius-lg)', overflow: 'hidden' } },
688
608
  toolbar,
689
609
  // Metrics bar
690
610
  h(MetricsBar, { stats: stats }),
@@ -699,7 +619,7 @@ export function TaskPipelinePage() {
699
619
 
700
620
  // Chain labels (left side)
701
621
  chainInfos.map(function(ci, i) {
702
- return h('div', { key: ci.chainId, style: { position: 'absolute', left: 4, top: ci.y - 2, fontSize: 9, color: 'rgba(255,255,255,0.25)', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', maxWidth: PAD - 8, overflow: 'hidden' } },
622
+ return h('div', { key: ci.chainId, style: { position: 'absolute', left: 4, top: ci.y - 2, fontSize: 9, color: 'var(--tp-text-faint)', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', maxWidth: PAD - 8, overflow: 'hidden' } },
703
623
  ci.customer && h(CustomerBadge, { customer: ci.customer })
704
624
  );
705
625
  }),
@@ -788,21 +708,141 @@ export function TaskPipelinePage() {
788
708
  h('div', { style: { width: 18, height: 18, borderRadius: '50%', background: sc + '33', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 8, fontWeight: 700, color: sc, flexShrink: 0, border: '1px solid ' + sc + '44' } },
789
709
  (t.assignedToName || t.assignedTo || '?').charAt(0).toUpperCase()
790
710
  ),
791
- h('span', { style: { fontSize: 11, fontWeight: 600, color: '#fff', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title)
711
+ h('span', { style: { fontSize: 11, fontWeight: 600, color: 'var(--tp-text)', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, t.title)
792
712
  ),
793
713
  // Status + agent name + time
794
714
  h('div', { style: { display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' } },
795
715
  tag(sc, t.status.replace('_', ' ')),
796
- h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.4)' } }, t.assignedToName || t.assignedTo),
716
+ h('span', { style: { fontSize: 9, color: 'var(--tp-text-dim)' } }, t.assignedToName || t.assignedTo),
797
717
  t.delegationType && tag(DELEGATION_COLORS[t.delegationType] || '#6b7394', t.delegationType),
798
- h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.3)', marginLeft: 'auto' } }, timeAgo(t.createdAt))
718
+ h('span', { style: { fontSize: 9, color: 'var(--tp-text-dim)', marginLeft: 'auto' } }, timeAgo(t.createdAt))
799
719
  ),
800
720
  // Progress bar
801
- isActive && t.progress > 0 && h('div', { style: { height: 2, background: 'rgba(255,255,255,0.08)', borderRadius: 1, overflow: 'hidden', marginTop: 1 } },
721
+ isActive && t.progress > 0 && h('div', { style: { height: 2, background: 'var(--tp-border)', borderRadius: 1, overflow: 'hidden', marginTop: 1 } },
802
722
  h('div', { style: { height: '100%', width: t.progress + '%', background: sc, borderRadius: 1, transition: 'width 0.3s' } })
803
723
  )
804
724
  );
805
- })
725
+ }),
726
+
727
+ // ── Expanded chain flow (rendered ON the canvas below clicked node) ──
728
+ expandedChain && expandedChain.length > 0 && (function() {
729
+ // Find the clicked node position to anchor below it
730
+ var anchor = nodes.find(function(n) { return n.id === expandedTaskId; });
731
+ if (!anchor) return null;
732
+ var flowY = anchor.y + anchor.h + 20;
733
+ var flowX = anchor.x;
734
+ var STEP_W = 120;
735
+ var STEP_H = 44;
736
+ var STEP_GAP = 48;
737
+ var ARROW_W = STEP_GAP;
738
+
739
+ // Build person-centric flow steps: createdBy → assignedTo for each chain task, then final status
740
+ var steps = [];
741
+ expandedChain.forEach(function(ct, i) {
742
+ if (i === 0 && ct.createdBy && ct.createdBy !== 'system') {
743
+ steps.push({ label: ct.createdByName || ct.createdBy, type: 'person', isHuman: ct.createdBy.indexOf('agent') === -1 && ct.createdBy !== 'system', status: null, arrow: ct.delegationType || 'assigned' });
744
+ } else if (i === 0 && ct.createdBy === 'system') {
745
+ steps.push({ label: 'System', type: 'system', isHuman: false, status: null, arrow: 'assigned' });
746
+ }
747
+ steps.push({ label: ct.assignedToName || ct.assignedTo, type: 'agent', isHuman: false, status: ct.status, taskId: ct.id, arrow: i < expandedChain.length - 1 ? (expandedChain[i + 1].delegationType || 'delegation') : null, duration: ct.actualDurationMs, progress: ct.progress });
748
+ });
749
+ // Add final status node
750
+ var lastTask = expandedChain[expandedChain.length - 1];
751
+ var isDone = lastTask.status === 'completed' || lastTask.status === 'failed' || lastTask.status === 'cancelled';
752
+ if (isDone) {
753
+ steps.push({ label: lastTask.status === 'completed' ? 'Completed!' : lastTask.status === 'failed' ? 'Failed' : 'Cancelled', type: 'terminal', isHuman: false, status: lastTask.status, arrow: null });
754
+ }
755
+
756
+ var totalW = steps.length * STEP_W + (steps.length - 1) * STEP_GAP;
757
+
758
+ var maxFlowW = Math.max(totalW + 40, 400);
759
+ var containerW = (containerRef.current ? containerRef.current.getBoundingClientRect().width / zoom : 800) - flowX;
760
+ if (maxFlowW > containerW) maxFlowW = Math.max(containerW, 320);
761
+
762
+ return h('div', { style: { position: 'absolute', left: flowX, top: flowY, pointerEvents: 'auto', maxWidth: maxFlowW, zIndex: 20 } },
763
+ // Background card
764
+ h('div', { style: { background: 'var(--bg-primary, rgba(10,12,20,0.95))', border: '1px solid rgba(99,102,241,0.2)', borderRadius: 12, padding: '14px 16px 12px', backdropFilter: 'blur(8px)', overflowX: 'auto', overflowY: 'hidden' } },
765
+ // Header
766
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 } },
767
+ h('span', { style: { fontSize: 10, fontWeight: 600, color: 'var(--tp-text-dim)', letterSpacing: '0.06em' } }, 'TASK FLOW'),
768
+ expandedChain[0].chainId && h('span', { style: { fontSize: 9, color: 'rgba(255,255,255,0.2)', fontFamily: 'var(--font-mono)' } }, '#' + expandedChain[0].chainId.slice(0, 8)),
769
+ h('div', { style: { flex: 1 } }),
770
+ h('button', { className: 'tp-node', onClick: function() { setExpandedTaskId(null); }, style: { background: 'none', border: 'none', color: 'var(--tp-text-dim)', cursor: 'pointer', fontSize: 14, padding: '0 2px' } }, '\u00D7')
771
+ ),
772
+ // Flow
773
+ h('div', { style: { position: 'relative', height: STEP_H + 8, minWidth: totalW } },
774
+ // SVG arrows
775
+ h('svg', { width: totalW, height: STEP_H + 8, style: { position: 'absolute', top: 0, left: 0, pointerEvents: 'none' } },
776
+ h('defs', null,
777
+ h('marker', { id: 'fc-arr', markerWidth: 6, markerHeight: 4, refX: 6, refY: 2, orient: 'auto' },
778
+ h('polygon', { points: '0 0, 6 2, 0 4', fill: 'rgba(99,102,241,0.5)' })
779
+ )
780
+ ),
781
+ steps.map(function(step, i) {
782
+ if (i === steps.length - 1 || !step.arrow) return null;
783
+ var x1 = i * (STEP_W + STEP_GAP) + STEP_W;
784
+ var x2 = (i + 1) * (STEP_W + STEP_GAP);
785
+ var y = 4 + STEP_H / 2;
786
+ var arrowColor = DELEGATION_COLORS[step.arrow] || 'rgba(99,102,241,0.5)';
787
+ var nextStep = steps[i + 1];
788
+ var isFlowActive = step.status === 'in_progress' || (nextStep && nextStep.status === 'in_progress');
789
+ return h(Fragment, { key: 'a' + i },
790
+ h('line', { x1: x1, y1: y, x2: x2, y2: y, stroke: arrowColor + '88', strokeWidth: 2, markerEnd: 'url(#fc-arr)' }),
791
+ isFlowActive && h('line', { x1: x1, y1: y, x2: x2, y2: y, stroke: STATUS_COLORS.in_progress, strokeWidth: 2, strokeDasharray: '4 12', className: 'tp-flow-active', style: { opacity: 0.7 } }),
792
+ step.arrow !== 'assigned' && step.arrow !== 'delegation' && h('text', { x: (x1 + x2) / 2, y: y - 6, fill: arrowColor, fontSize: 8, textAnchor: 'middle', fontWeight: 600 }, step.arrow)
793
+ );
794
+ })
795
+ ),
796
+ // Step nodes
797
+ steps.map(function(step, i) {
798
+ var x = i * (STEP_W + STEP_GAP);
799
+ var sc = step.type === 'terminal'
800
+ ? (STATUS_COLORS[step.status] || '#22c55e')
801
+ : step.type === 'person' || step.isHuman
802
+ ? '#f59e0b'
803
+ : step.status ? (STATUS_COLORS[step.status] || '#6366f1') : '#6366f1';
804
+ var isTerminal = step.type === 'terminal';
805
+ var isMe = step.taskId === expandedTaskId;
806
+
807
+ return h('div', {
808
+ key: i,
809
+ className: 'tp-node',
810
+ onClick: function(e) { e.stopPropagation(); if (step.taskId) { var ct = expandedChain.find(function(c) { return c.id === step.taskId; }); if (ct) openTaskDetail(ct); } },
811
+ style: {
812
+ position: 'absolute', left: x, top: 4, width: STEP_W, height: STEP_H,
813
+ background: isTerminal ? sc + '15' : isMe ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.02)',
814
+ border: '1.5px solid ' + (isTerminal ? sc + '44' : isMe ? sc : 'rgba(255,255,255,0.1)'),
815
+ borderRadius: isTerminal ? 22 : 10,
816
+ display: 'flex', alignItems: 'center', gap: 8, padding: '0 10px',
817
+ cursor: step.taskId ? 'pointer' : 'default',
818
+ },
819
+ },
820
+ // Avatar
821
+ h('div', { style: {
822
+ width: 26, height: 26, borderRadius: '50%', flexShrink: 0,
823
+ background: isTerminal ? sc + '33' : step.isHuman || step.type === 'person' ? 'linear-gradient(135deg, #f59e0b, #f97316)' : step.type === 'system' ? 'rgba(255,255,255,0.1)' : 'linear-gradient(135deg, #6366f1, #8b5cf6)',
824
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
825
+ fontSize: isTerminal ? 12 : 10, fontWeight: 700,
826
+ color: isTerminal ? sc : '#fff',
827
+ border: '2px solid ' + (isTerminal ? sc + '44' : 'transparent'),
828
+ } },
829
+ isTerminal ? (step.status === 'completed' ? '\u2714' : step.status === 'failed' ? '\u2716' : '\u2716') : step.label.charAt(0).toUpperCase()
830
+ ),
831
+ // Info
832
+ h('div', { style: { overflow: 'hidden', flex: 1, minWidth: 0 } },
833
+ h('div', { style: { fontSize: 11, fontWeight: 600, color: isTerminal ? sc : '#fff', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, step.label),
834
+ !isTerminal && h('div', { style: { fontSize: 9, color: 'var(--tp-text-dim)', marginTop: 1 } },
835
+ step.type === 'person' || step.isHuman ? 'Human' : step.type === 'system' ? 'System' : 'Agent',
836
+ step.duration ? ' \u00B7 ' + formatDuration(step.duration) : '',
837
+ step.status === 'in_progress' && step.progress > 0 ? ' \u00B7 ' + step.progress + '%' : ''
838
+ )
839
+ )
840
+ );
841
+ })
842
+ )
843
+ )
844
+ );
845
+ })()
806
846
  )
807
847
  ),
808
848
 
@@ -814,24 +854,16 @@ export function TaskPipelinePage() {
814
854
  padding: '10px 14px', pointerEvents: 'none', zIndex: 1000, minWidth: 180, maxWidth: 280,
815
855
  }},
816
856
  hoveredNode.task.customerContext && h(CustomerBadge, { customer: hoveredNode.task.customerContext }),
817
- h('div', { style: { fontSize: 12, fontWeight: 600, color: '#fff', marginBottom: 6 } }, hoveredNode.task.title),
857
+ h('div', { style: { fontSize: 12, fontWeight: 600, color: 'var(--tp-text)', marginBottom: 6 } }, hoveredNode.task.title),
818
858
  h('div', { style: { display: 'flex', flexDirection: 'column', gap: 3, fontSize: 11 } },
819
- h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Agent'), h('span', { style: { fontWeight: 600 } }, hoveredNode.task.assignedToName || '-')),
820
- h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Status'), h('span', { style: { color: STATUS_COLORS[hoveredNode.task.status] } }, hoveredNode.task.status.replace('_', ' '))),
821
- hoveredNode.task.chainId && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Chain Step'), h('span', null, '#' + ((hoveredNode.task.chainSeq || 0) + 1))),
822
- hoveredNode.task.delegationType && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Type'), h('span', { style: { color: DELEGATION_COLORS[hoveredNode.task.delegationType] } }, hoveredNode.task.delegationType)),
823
- hoveredNode.task.progress > 0 && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'rgba(255,255,255,0.4)' } }, 'Progress'), h('span', { style: { color: STATUS_COLORS.in_progress } }, hoveredNode.task.progress + '%'))
859
+ h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'var(--tp-text-dim)' } }, 'Agent'), h('span', { style: { fontWeight: 600 } }, hoveredNode.task.assignedToName || '-')),
860
+ h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'var(--tp-text-dim)' } }, 'Status'), h('span', { style: { color: STATUS_COLORS[hoveredNode.task.status] } }, hoveredNode.task.status.replace('_', ' '))),
861
+ hoveredNode.task.chainId && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'var(--tp-text-dim)' } }, 'Chain Step'), h('span', null, '#' + ((hoveredNode.task.chainSeq || 0) + 1))),
862
+ hoveredNode.task.delegationType && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'var(--tp-text-dim)' } }, 'Type'), h('span', { style: { color: DELEGATION_COLORS[hoveredNode.task.delegationType] } }, hoveredNode.task.delegationType)),
863
+ hoveredNode.task.progress > 0 && h('div', { style: { display: 'flex', justifyContent: 'space-between' } }, h('span', { style: { color: 'var(--tp-text-dim)' } }, 'Progress'), h('span', { style: { color: STATUS_COLORS.in_progress } }, hoveredNode.task.progress + '%'))
824
864
  )
825
865
  ),
826
866
 
827
- // Inline chain flowchart (shown when a task is clicked)
828
- expandedChain && expandedChain.length > 0 && h(ChainFlowInline, {
829
- chain: expandedChain,
830
- taskId: expandedTaskId,
831
- onClose: function() { setExpandedTaskId(null); },
832
- onClickTask: function(t) { openTaskDetail(t); }
833
- }),
834
-
835
867
  // Detail modal (double-click)
836
868
  selectedTask && h(TaskDetail, { task: selectedTask, chain: selectedChain, onClose: function() { setSelectedTask(null); setSelectedChain(null); }, onCancel: cancelTask })
837
869
  );
@@ -28,9 +28,9 @@ export function createTaskQueueRoutes(taskQueue: TaskQueueManager) {
28
28
  });
29
29
 
30
30
  // GET /task-pipeline/stats — pipeline statistics
31
- router.get('/stats', (c) => {
31
+ router.get('/stats', async (c) => {
32
32
  const orgId = c.req.query('orgId') || '';
33
- const stats = taskQueue.getPipelineStats(orgId);
33
+ const stats = await taskQueue.getPipelineStats(orgId);
34
34
  return c.json(stats);
35
35
  });
36
36
 
@@ -385,11 +385,11 @@ export class TaskQueueManager {
385
385
  return this.getAllTasks(orgId, limit);
386
386
  }
387
387
 
388
- getPipelineStats(orgId?: string): {
388
+ async getPipelineStats(orgId?: string): Promise<{
389
389
  created: number; assigned: number; inProgress: number; completed: number; failed: number; cancelled: number; total: number;
390
390
  todayCompleted: number; todayFailed: number; todayCreated: number; avgDurationMs: number; totalCost: number; totalTokens: number;
391
391
  topAgents: Array<{ agent: string; name: string; completed: number; active: number }>;
392
- } {
392
+ }> {
393
393
  const stats = {
394
394
  created: 0, assigned: 0, inProgress: 0, completed: 0, failed: 0, cancelled: 0, total: 0,
395
395
  todayCompleted: 0, todayFailed: 0, todayCreated: 0, avgDurationMs: 0, totalCost: 0, totalTokens: 0,
@@ -434,6 +434,34 @@ export class TaskQueueManager {
434
434
  .map(([agent, d]) => ({ agent, ...d }))
435
435
  .sort((a, b) => (b.completed + b.active) - (a.completed + a.active))
436
436
  .slice(0, 5);
437
+
438
+ // If DB available, also query today's stats from DB to catch anything not in memory
439
+ if (this.db) {
440
+ try {
441
+ const todayISO = todayStart.toISOString();
442
+ const dbToday = await this.db.get(
443
+ `SELECT COUNT(*) FILTER (WHERE status='completed' AND completed_at >= ?) as dc,
444
+ COUNT(*) FILTER (WHERE status='failed' AND completed_at >= ?) as df,
445
+ COUNT(*) FILTER (WHERE created_at >= ?) as dn
446
+ FROM task_pipeline` + (orgId ? ` WHERE org_id = ?` : ''),
447
+ orgId ? [todayISO, todayISO, todayISO, orgId] : [todayISO, todayISO, todayISO]
448
+ ).catch(() => null);
449
+ // Postgres FILTER may not work on all DBs, fallback to separate counts
450
+ if (!dbToday || dbToday.dc === undefined) {
451
+ const r1 = await this.db.get(`SELECT COUNT(*) as c FROM task_pipeline WHERE status='completed' AND completed_at >= ?`, [todayISO]).catch(() => null);
452
+ const r2 = await this.db.get(`SELECT COUNT(*) as c FROM task_pipeline WHERE status='failed' AND completed_at >= ?`, [todayISO]).catch(() => null);
453
+ const r3 = await this.db.get(`SELECT COUNT(*) as c FROM task_pipeline WHERE created_at >= ?`, [todayISO]).catch(() => null);
454
+ if (r1?.c > stats.todayCompleted) stats.todayCompleted = r1.c;
455
+ if (r2?.c > stats.todayFailed) stats.todayFailed = r2.c;
456
+ if (r3?.c > stats.todayCreated) stats.todayCreated = r3.c;
457
+ } else {
458
+ if (dbToday.dc > stats.todayCompleted) stats.todayCompleted = dbToday.dc;
459
+ if (dbToday.df > stats.todayFailed) stats.todayFailed = dbToday.df;
460
+ if (dbToday.dn > stats.todayCreated) stats.todayCreated = dbToday.dn;
461
+ }
462
+ } catch { /* ignore — in-memory stats are fine */ }
463
+ }
464
+
437
465
  return stats;
438
466
  }
439
467