@memberjunction/ng-dashboards 5.40.1 → 5.41.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 (48) hide show
  1. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts +38 -4
  2. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts.map +1 -1
  3. package/dist/AI/components/analytics/ai-analytics-resource.component.js +148 -20
  4. package/dist/AI/components/analytics/ai-analytics-resource.component.js.map +1 -1
  5. package/dist/AI/components/analytics/realtime/realtime-management-data.d.ts +216 -0
  6. package/dist/AI/components/analytics/realtime/realtime-management-data.d.ts.map +1 -0
  7. package/dist/AI/components/analytics/realtime/realtime-management-data.js +313 -0
  8. package/dist/AI/components/analytics/realtime/realtime-management-data.js.map +1 -0
  9. package/dist/AI/components/analytics/realtime/realtime-management.component.d.ts +103 -0
  10. package/dist/AI/components/analytics/realtime/realtime-management.component.d.ts.map +1 -0
  11. package/dist/AI/components/analytics/realtime/realtime-management.component.js +1060 -0
  12. package/dist/AI/components/analytics/realtime/realtime-management.component.js.map +1 -0
  13. package/dist/AI/components/analytics/realtime/realtime-overview.component.d.ts +87 -0
  14. package/dist/AI/components/analytics/realtime/realtime-overview.component.d.ts.map +1 -0
  15. package/dist/AI/components/analytics/realtime/realtime-overview.component.js +710 -0
  16. package/dist/AI/components/analytics/realtime/realtime-overview.component.js.map +1 -0
  17. package/dist/AI/components/analytics/realtime/realtime-session-data.d.ts +127 -0
  18. package/dist/AI/components/analytics/realtime/realtime-session-data.d.ts.map +1 -0
  19. package/dist/AI/components/analytics/realtime/realtime-session-data.js +233 -0
  20. package/dist/AI/components/analytics/realtime/realtime-session-data.js.map +1 -0
  21. package/dist/AI/components/analytics/realtime/realtime-sessions.component.d.ts +94 -0
  22. package/dist/AI/components/analytics/realtime/realtime-sessions.component.d.ts.map +1 -0
  23. package/dist/AI/components/analytics/realtime/realtime-sessions.component.js +728 -0
  24. package/dist/AI/components/analytics/realtime/realtime-sessions.component.js.map +1 -0
  25. package/dist/AI/components/autotagging/tabs/sources-tab.component.js +2 -2
  26. package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
  27. package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
  28. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +4 -1
  29. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
  30. package/dist/AI/components/tags/tags-resource.component.js +2 -2
  31. package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
  32. package/dist/AI/components/vectors/vector-management-resource.component.js +2 -2
  33. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  34. package/dist/AI/index.d.ts +5 -0
  35. package/dist/AI/index.d.ts.map +1 -1
  36. package/dist/AI/index.js +7 -0
  37. package/dist/AI/index.js.map +1 -1
  38. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +5 -5
  39. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
  40. package/dist/MCP/mcp-dashboard.component.d.ts +16 -2
  41. package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
  42. package/dist/MCP/mcp-dashboard.component.js +33 -4
  43. package/dist/MCP/mcp-dashboard.component.js.map +1 -1
  44. package/dist/ai-dashboards.module.d.ts +22 -19
  45. package/dist/ai-dashboards.module.d.ts.map +1 -1
  46. package/dist/ai-dashboards.module.js +18 -0
  47. package/dist/ai-dashboards.module.js.map +1 -1
  48. package/package.json +55 -55
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-overview.component.js","sourceRoot":"","sources":["../../../../../src/AI/components/analytics/realtime/realtime-overview.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Realtime Voice — analytics overview section.\n *\n * Operational analytics for voice-agent sessions, built from\n * `MJ: AI Agent Sessions`, their `MJ: AI Agent Session Channels` rows, and the\n * delegated `MJ: AI Agent Runs` linked via `AgentSessionID`. Renders the KPI\n * row (active sessions, sessions in window, avg duration, delegated runs,\n * janitor closes, cost), a sessions-over-time bar chart, the channel-usage\n * donut, the top delegated target agents, and a recent-sessions list with a\n * \"View all\" jump to the full sessions grid.\n */\n\nimport {\n Component, Input, Output, EventEmitter,\n OnInit, ChangeDetectorRef, inject\n} from '@angular/core';\nimport { CompositeKey } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { KPICardData } from '../../widgets/kpi-card.component';\nimport {\n RealtimeSessionRollup, RealtimeSessionsDataset,\n LoadRealtimeSessionsDataset, FormatSessionDuration, FormatTokenCount,\n FormatSessionCost, ChannelIconClass, TimeRangeToMs,\n BuildSessionStatusDisplay\n} from './realtime-session-data';\n\n// ── View models ──\n\ninterface TimeBucket {\n Label: string;\n Count: number;\n /** 0–100, relative to the busiest bucket. */\n HeightPercent: number;\n /** Flagged when the bucket is delegated-heavy (≥2 delegated runs/session avg). */\n DelegatedHeavy: boolean;\n}\n\ninterface ChannelShare {\n Name: string;\n Count: number;\n Percent: number;\n Color: string;\n}\n\ninterface TargetAgentBar {\n Name: string;\n Count: number;\n WidthPercent: number;\n}\n\ninterface RecentSessionRow {\n ID: string;\n Target: string;\n ViaAgent: string;\n User: string;\n Status: string;\n StatusClass: string;\n StatusLabel: string;\n StatusTitle: string;\n /** Pill icon (close cause for Closed rows); empty for Active, which renders the live dot. */\n StatusIcon: string;\n ChannelIcons: string[];\n Runs: number;\n Cost: string;\n Duration: string;\n}\n\nconst DONUT_COLORS = [\n 'var(--mj-brand-primary)',\n 'var(--mj-status-success)',\n 'var(--mj-brand-accent)',\n 'var(--mj-status-warning)',\n 'var(--mj-text-disabled)'\n];\n\n@Component({\n standalone: false,\n selector: 'app-analytics-realtime-overview',\n template: `\n @if (IsLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading realtime voice analytics...\"></mj-loading>\n </div>\n } @else {\n <!-- KPI Row -->\n <div class=\"kpi-grid\">\n @for (kpi of KPICards; track kpi.title) {\n <app-kpi-card [data]=\"kpi\"></app-kpi-card>\n }\n </div>\n\n <!-- Charts row 1: sessions over time + channel usage -->\n <div class=\"charts-2\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <div class=\"panel-header__title\">\n <i class=\"fa-solid fa-chart-column panel-header__icon\"></i>\n Sessions over time\n </div>\n <span class=\"panel-header__subtitle\">Accented bars = delegated-heavy</span>\n </div>\n <div class=\"panel-body\">\n @if (TimeBuckets.length === 0) {\n <div class=\"panel-empty\">No sessions in the selected period</div>\n } @else {\n <div class=\"bars\">\n @for (bucket of TimeBuckets; track bucket.Label) {\n <div class=\"bcol\" [title]=\"bucket.Label + ': ' + bucket.Count + ' session(s)'\">\n <div class=\"bar\"\n [class.bar--accent]=\"bucket.DelegatedHeavy\"\n [style.height.%]=\"bucket.HeightPercent\"></div>\n <div class=\"blabel\">{{ bucket.Label }}</div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n <div class=\"panel\">\n <div class=\"panel-header\">\n <div class=\"panel-header__title\">\n <i class=\"fa-solid fa-chart-pie panel-header__icon\"></i>\n Channel usage\n </div>\n <span class=\"panel-header__subtitle\">Share of attached session channels</span>\n </div>\n <div class=\"panel-body\">\n @if (ChannelShares.length === 0) {\n <div class=\"panel-empty\">No channels attached in the selected period</div>\n } @else {\n <div class=\"donut-row\">\n <div class=\"donut\" [style.background]=\"DonutGradient\"></div>\n <div class=\"legend\">\n @for (share of ChannelShares; track share.Name) {\n <div class=\"legend-item\">\n <span class=\"legend-dot\" [style.background]=\"share.Color\"></span>\n {{ share.Name }}\n <span class=\"legend-pct\">{{ share.Percent | number:'1.0-0' }}%</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Charts row 2: top delegated target agents -->\n <div class=\"panel\">\n <div class=\"panel-header\">\n <div class=\"panel-header__title\">\n <i class=\"fa-solid fa-diagram-project panel-header__icon\"></i>\n Top delegated target agents\n </div>\n <span class=\"panel-header__subtitle\">Delegated agent runs by target · selected period</span>\n </div>\n <div class=\"panel-body\">\n @if (TopTargetAgents.length === 0) {\n <div class=\"panel-empty\">No delegated runs in the selected period</div>\n }\n @for (bar of TopTargetAgents; track bar.Name) {\n <div class=\"hbar-row\">\n <span class=\"hbar-name\" [title]=\"bar.Name\">{{ bar.Name }}</span>\n <span class=\"hbar-track\">\n <span class=\"hbar-fill\" [style.width.%]=\"bar.WidthPercent\"></span>\n </span>\n <span class=\"hbar-val\">{{ bar.Count }}</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Recent sessions -->\n <div class=\"panel\">\n <div class=\"panel-header\">\n <div class=\"panel-header__title\">\n <i class=\"fa-solid fa-tower-broadcast panel-header__icon\"></i>\n Recent sessions\n </div>\n <button mjButton variant=\"secondary\" size=\"sm\" (click)=\"SectionNavigate.emit('realtime-sessions')\">\n View all <i class=\"fa-solid fa-arrow-right\"></i>\n </button>\n </div>\n <div class=\"table-wrapper\">\n <table class=\"data-table\">\n <thead>\n <tr>\n <th>Target</th><th>User</th><th>Status</th><th>Channels</th>\n <th class=\"th-numeric\">Runs</th><th class=\"th-numeric\">Cost</th>\n <th class=\"th-numeric\">Duration</th><th></th>\n </tr>\n </thead>\n <tbody>\n @if (RecentSessions.length === 0) {\n <tr><td colspan=\"8\" class=\"empty-row\">No sessions in the selected period</td></tr>\n }\n @for (row of RecentSessions; track row.ID) {\n <tr class=\"session-row\" (click)=\"OpenSession(row.ID)\">\n <td>\n <div class=\"cell-strong\">{{ row.Target }}</div>\n <div class=\"cell-sub\">via {{ row.ViaAgent }}</div>\n </td>\n <td>{{ row.User }}</td>\n <td>\n <span class=\"status-pill\" [class]=\"row.StatusClass\"\n [title]=\"row.StatusTitle\">\n @if (row.Status === 'Active') { <span class=\"dot dot--live\"></span> }\n @else if (row.StatusIcon) { <i [class]=\"row.StatusIcon\"></i> }\n {{ row.StatusLabel }}\n </span>\n </td>\n <td>\n <span class=\"chan-icons\">\n @for (icon of row.ChannelIcons; track $index) {\n <i [class]=\"icon\"></i>\n }\n </span>\n </td>\n <td class=\"cell-numeric\">{{ row.Runs }}</td>\n <td class=\"cell-numeric mono\">{{ row.Cost }}</td>\n <td class=\"cell-numeric mono\">{{ row.Duration }}</td>\n <td class=\"cell-action\">\n <button mjButton variant=\"icon\" size=\"sm\" title=\"Open session record\"\n (click)=\"OpenSession(row.ID); $event.stopPropagation()\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n </button>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n `,\n styles: [`\n :host { display: block; }\n\n .loading-container {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 300px;\n }\n\n /* ── KPI Grid ── */\n .kpi-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n margin-bottom: 16px;\n }\n\n /* ── Layout ── */\n .charts-2 {\n display: grid;\n grid-template-columns: 1.4fr 1fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n @media (max-width: 1000px) {\n .charts-2 { grid-template-columns: 1fr; }\n }\n\n /* ── Panel (matches sibling analytics sections) ── */\n .panel {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n margin-bottom: 16px;\n overflow: hidden;\n }\n\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .panel-header__title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .panel-header__icon {\n font-size: 13px;\n color: var(--mj-brand-primary);\n }\n\n .panel-header__subtitle {\n font-size: 12px;\n color: var(--mj-text-muted);\n }\n\n .panel-body { padding: 16px 18px; }\n\n .panel-empty {\n text-align: center;\n padding: 24px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n /* ── Vertical bars ── */\n .bars {\n display: flex;\n align-items: flex-end;\n gap: 8px;\n height: 160px;\n }\n\n .bcol {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-end;\n height: 100%;\n min-width: 0;\n }\n\n .bar {\n width: 100%;\n max-width: 42px;\n min-height: 2px;\n border-radius: 6px 6px 2px 2px;\n background: linear-gradient(180deg,\n color-mix(in srgb, var(--mj-brand-primary) 85%, transparent),\n color-mix(in srgb, var(--mj-brand-primary) 55%, transparent));\n transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .bar--accent {\n background: linear-gradient(180deg,\n color-mix(in srgb, var(--mj-status-success) 85%, transparent),\n color-mix(in srgb, var(--mj-status-success) 55%, transparent));\n }\n\n .blabel {\n margin-top: 6px;\n font-size: 10px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 100%;\n }\n\n /* ── Donut ── */\n .donut-row {\n display: flex;\n align-items: center;\n gap: 22px;\n flex-wrap: wrap;\n }\n\n .donut {\n width: 140px;\n height: 140px;\n border-radius: 50%;\n flex-shrink: 0;\n -webkit-mask: radial-gradient(circle at center, transparent 42px, #000 43px);\n mask: radial-gradient(circle at center, transparent 42px, #000 43px);\n }\n\n .legend {\n display: flex;\n flex-direction: column;\n gap: 8px;\n min-width: 160px;\n flex: 1;\n }\n\n .legend-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12.5px;\n color: var(--mj-text-secondary);\n }\n\n .legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .legend-pct {\n margin-left: auto;\n color: var(--mj-text-muted);\n font-variant-numeric: tabular-nums;\n }\n\n /* ── Horizontal bars ── */\n .hbar-row {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 10px;\n }\n\n .hbar-row:last-child { margin-bottom: 0; }\n\n .hbar-name {\n width: 140px;\n min-width: 140px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .hbar-track {\n flex: 1;\n height: 18px;\n border-radius: 6px;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n }\n\n .hbar-fill {\n display: block;\n height: 100%;\n border-radius: 6px;\n background: linear-gradient(90deg,\n var(--mj-brand-primary),\n color-mix(in srgb, var(--mj-brand-primary) 65%, var(--mj-bg-surface)));\n transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .hbar-val {\n width: 48px;\n min-width: 48px;\n text-align: right;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n font-variant-numeric: tabular-nums;\n }\n\n /* ── Table ── */\n .table-wrapper { overflow-x: auto; }\n\n .data-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n\n .data-table th,\n .data-table td {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .data-table th {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n background: var(--mj-bg-surface-card);\n }\n\n .th-numeric { text-align: right; }\n\n .session-row { cursor: pointer; transition: background 0.15s; }\n .session-row:hover { background: var(--mj-bg-surface-hover); }\n\n .cell-strong {\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .cell-sub {\n font-size: 11px;\n color: var(--mj-text-muted);\n }\n\n .cell-numeric {\n text-align: right;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-secondary);\n }\n\n .cell-action { text-align: right; white-space: nowrap; }\n\n .mono {\n font-family: var(--mj-font-mono, monospace);\n font-size: 12px;\n }\n\n .empty-row {\n text-align: center;\n color: var(--mj-text-disabled);\n padding: 24px;\n }\n\n /* ── Status pills ── */\n .status-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 10px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.3px;\n white-space: nowrap;\n }\n\n .status-active {\n background: color-mix(in srgb, var(--mj-status-success) 12%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .status-idle {\n background: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n }\n\n .status-closed {\n background: color-mix(in srgb, var(--mj-text-disabled) 12%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n }\n\n .dot--live {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--mj-status-success);\n animation: pulse-live 1.6s ease-in-out infinite;\n }\n\n @keyframes pulse-live {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.35; }\n }\n\n .chan-icons {\n display: inline-flex;\n gap: 8px;\n color: var(--mj-text-secondary);\n font-size: 13px;\n }\n `]\n})\nexport class AnalyticsRealtimeOverviewComponent extends BaseAngularComponent implements OnInit {\n private _timeRange = '7d';\n @Input()\n set TimeRange(value: string) {\n const prev = this._timeRange;\n this._timeRange = value;\n if (prev !== value && this.initialized) this.LoadData();\n }\n get TimeRange(): string { return this._timeRange; }\n\n /** Lets the shell jump to the full sessions grid (mirrors Executive Summary's pattern). */\n @Output() SectionNavigate = new EventEmitter<string>();\n\n private initialized = false;\n private cdr = inject(ChangeDetectorRef);\n private dataset: RealtimeSessionsDataset | null = null;\n\n public IsLoading = false;\n public KPICards: KPICardData[] = [];\n public TimeBuckets: TimeBucket[] = [];\n public ChannelShares: ChannelShare[] = [];\n public DonutGradient = '';\n public TopTargetAgents: TargetAgentBar[] = [];\n public RecentSessions: RecentSessionRow[] = [];\n\n ngOnInit(): void {\n this.initialized = true;\n this.LoadData();\n }\n\n /** Reloads everything for the current time range. */\n public async LoadData(): Promise<void> {\n this.IsLoading = true;\n this.cdr.detectChanges();\n try {\n this.dataset = await LoadRealtimeSessionsDataset(this.ProviderToUse, this.TimeRange);\n this.buildKPICards();\n this.buildTimeBuckets();\n this.buildChannelShares();\n this.buildTopTargetAgents();\n this.buildRecentSessions();\n } catch (e) {\n console.error('Realtime Voice overview load error:', e);\n } finally {\n this.IsLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Opens the AIAgentSession record using the shared Explorer record-open path. */\n public OpenSession(sessionId: string): void {\n const key = new CompositeKey();\n key.LoadFromSingleKeyValuePair('ID', sessionId);\n SharedService.Instance.OpenEntityRecord('MJ: AI Agent Sessions', key);\n }\n\n // ── Builders ──\n\n private get windowSessions(): RealtimeSessionRollup[] {\n return this.dataset?.Sessions ?? [];\n }\n\n private buildKPICards(): void {\n const ds = this.dataset;\n const sessions = this.windowSessions;\n const activeCount = sessions.filter(s => s.Record.Status === 'Active').length;\n const startedInWindow = sessions.filter(s => ds != null && s.StartedAt >= ds.WindowStart);\n const priorCount = ds?.PriorWindowSessionCount ?? 0;\n\n const durations = sessions.map(s => s.DurationMs).filter(d => d > 0);\n const avgDuration = durations.length > 0\n ? durations.reduce((a, b) => a + b, 0) / durations.length\n : 0;\n\n const delegatedTotal = sessions.reduce((a, s) => a + s.DelegatedRunCount, 0);\n const delegatedPerSession = sessions.length > 0 ? delegatedTotal / sessions.length : 0;\n const janitorCloses = sessions.filter(s => s.IsJanitorClosed).length;\n const totalCost = sessions.reduce((a, s) => a + s.TotalCost, 0);\n\n const trendPct = priorCount > 0\n ? Math.round(((startedInWindow.length - priorCount) / priorCount) * 100)\n : 0;\n\n this.KPICards = [\n {\n title: 'Active sessions',\n value: activeCount,\n subtitle: 'live right now',\n icon: 'fa-tower-broadcast',\n color: 'success'\n },\n {\n title: `Sessions (${this.TimeRange})`,\n value: startedInWindow.length,\n icon: 'fa-calendar-day',\n color: 'primary',\n trend: priorCount > 0 ? {\n direction: trendPct > 0 ? 'up' : trendPct < 0 ? 'down' : 'stable',\n percentage: Math.abs(trendPct),\n period: `vs prior ${this.TimeRange}`\n } : undefined,\n subtitle: priorCount > 0 ? undefined : 'no prior-period data'\n },\n {\n title: 'Avg duration',\n value: FormatSessionDuration(avgDuration),\n subtitle: 'min:sec per session',\n icon: 'fa-stopwatch',\n color: 'info'\n },\n {\n title: 'Delegated runs',\n value: delegatedTotal,\n subtitle: `${delegatedPerSession.toFixed(1)} per session avg`,\n icon: 'fa-diagram-project',\n color: 'info'\n },\n {\n title: 'Janitor closes',\n value: janitorCloses,\n subtitle: `orphans reconciled (${this.TimeRange})`,\n icon: 'fa-broom',\n color: 'warning'\n },\n {\n title: `Cost (${this.TimeRange})`,\n value: FormatSessionCost(totalCost),\n subtitle: 'co-agent + delegated',\n icon: 'fa-coins',\n color: 'danger'\n }\n ];\n }\n\n private buildTimeBuckets(): void {\n const ds = this.dataset;\n if (!ds) { this.TimeBuckets = []; return; }\n\n const hourly = this.TimeRange === '24h';\n const bucketMs = hourly ? 3_600_000 : 86_400_000;\n const bucketCount = Math.max(1, Math.round(ds.WindowMs / bucketMs));\n const now = Date.now();\n const buckets: { start: number; count: number; delegated: number }[] = [];\n for (let i = bucketCount - 1; i >= 0; i--) {\n buckets.push({ start: now - (i + 1) * bucketMs, count: 0, delegated: 0 });\n }\n\n for (const session of this.windowSessions) {\n const t = session.StartedAt.getTime();\n const idx = buckets.findIndex(b => t >= b.start && t < b.start + bucketMs);\n if (idx >= 0) {\n buckets[idx].count++;\n buckets[idx].delegated += session.DelegatedRunCount;\n }\n }\n\n const max = Math.max(1, ...buckets.map(b => b.count));\n this.TimeBuckets = buckets.map((b, i) => {\n const date = new Date(b.start + bucketMs);\n const isLast = i === buckets.length - 1;\n const label = hourly\n ? date.toLocaleTimeString([], { hour: 'numeric' }).toLowerCase()\n : isLast ? 'Today' : date.toLocaleDateString([], { weekday: this.TimeRange === '7d' ? 'short' : undefined, month: this.TimeRange === '7d' ? undefined : 'numeric', day: this.TimeRange === '7d' ? undefined : 'numeric' });\n return {\n Label: label,\n Count: b.count,\n HeightPercent: Math.round((b.count / max) * 100),\n DelegatedHeavy: b.count > 0 && b.delegated / b.count >= 2\n };\n });\n }\n\n private buildChannelShares(): void {\n const counts = new Map<string, number>();\n for (const session of this.windowSessions) {\n for (const name of session.ChannelNames) {\n counts.set(name, (counts.get(name) ?? 0) + 1);\n }\n }\n const total = Array.from(counts.values()).reduce((a, b) => a + b, 0);\n if (total === 0) {\n this.ChannelShares = [];\n this.DonutGradient = '';\n return;\n }\n const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1]);\n this.ChannelShares = sorted.map(([name, count], i) => ({\n Name: name,\n Count: count,\n Percent: (count / total) * 100,\n Color: DONUT_COLORS[i % DONUT_COLORS.length]\n }));\n\n let cursor = 0;\n const stops: string[] = [];\n for (const share of this.ChannelShares) {\n const end = cursor + share.Percent;\n stops.push(`${share.Color} ${cursor.toFixed(2)}% ${end.toFixed(2)}%`);\n cursor = end;\n }\n this.DonutGradient = `conic-gradient(${stops.join(', ')})`;\n }\n\n private buildTopTargetAgents(): void {\n const counts = new Map<string, number>();\n for (const session of this.windowSessions) {\n if (session.DelegatedRunCount === 0) continue;\n const name = session.TargetAgentName ?? session.TargetAgentID ?? 'Unknown';\n counts.set(name, (counts.get(name) ?? 0) + session.DelegatedRunCount);\n }\n const sorted = Array.from(counts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 5);\n const max = Math.max(1, ...sorted.map(([, c]) => c));\n this.TopTargetAgents = sorted.map(([name, count]) => ({\n Name: name,\n Count: count,\n WidthPercent: Math.round((count / max) * 100)\n }));\n }\n\n private buildRecentSessions(): void {\n this.RecentSessions = [...this.windowSessions]\n .sort((a, b) => b.StartedAt.getTime() - a.StartedAt.getTime())\n .slice(0, 8)\n .map(s => {\n const statusDisplay = BuildSessionStatusDisplay(s.Record.Status, s.CloseReason);\n return {\n ID: s.Record.ID,\n Target: s.TargetAgentName ?? s.Record.Agent ?? 'Unknown',\n ViaAgent: s.Record.Agent ?? 'Unknown',\n User: s.Record.User,\n Status: s.Record.Status,\n StatusClass: 'status-pill status-' + s.Record.Status.toLowerCase(),\n StatusLabel: statusDisplay.Label,\n StatusTitle: statusDisplay.Title,\n StatusIcon: statusDisplay.Icon,\n ChannelIcons: s.ChannelNames.map(ChannelIconClass),\n Runs: s.DelegatedRunCount,\n Cost: FormatSessionCost(s.TotalCost),\n Duration: FormatSessionDuration(s.DurationMs)\n };\n });\n }\n\n /** Exposed for testability / template type narrowing. */\n public FormatTokens(value: number): string {\n return FormatTokenCount(value);\n }\n\n /** Window length in ms for the active range (exposed for tests). */\n public get WindowMs(): number {\n return TimeRangeToMs(this.TimeRange);\n }\n}\n\nexport function LoadAnalyticsRealtimeOverview() { /* tree-shaking prevention */ }\n"]}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @fileoverview Shared data layer for the Realtime Voice analytics sections.
3
+ *
4
+ * Loads `MJ: AI Agent Sessions`, their `MJ: AI Agent Session Channels` rows,
5
+ * and the delegated `MJ: AI Agent Runs` (linked via `AgentSessionID`) in a
6
+ * single batched `RunViews` call, then aggregates everything client-side into
7
+ * per-session rollups (target agent, channel set, delegated-run count, token /
8
+ * cost totals, duration, close cause from the persisted `CloseReason` column).
9
+ *
10
+ * Used by both `AnalyticsRealtimeOverviewComponent` (KPIs + charts) and
11
+ * `AnalyticsRealtimeSessionsComponent` (management grid) so the two sections
12
+ * share one query shape and one aggregation pass.
13
+ */
14
+ import { IMetadataProvider } from '@memberjunction/core';
15
+ /**
16
+ * Why a session was closed, as stamped server-side by `SessionManager.CloseSession`
17
+ * (mirrors `AIAgentSessionEntity.CloseReason`). `null` means a legacy row closed
18
+ * before the column existed — rendered as "unknown".
19
+ */
20
+ export type RealtimeSessionCloseReason = 'Error' | 'Explicit' | 'Janitor' | 'Shutdown';
21
+ export interface RealtimeSessionRecord {
22
+ ID: string;
23
+ AgentID: string;
24
+ Agent: string | null;
25
+ UserID: string;
26
+ User: string;
27
+ Status: 'Active' | 'Idle' | 'Closed';
28
+ ConversationID: string | null;
29
+ LastSessionID: string | null;
30
+ HostInstanceID: string | null;
31
+ Config: string | null;
32
+ LastActiveAt: string;
33
+ ClosedAt: string | null;
34
+ CloseReason: RealtimeSessionCloseReason | null;
35
+ __mj_CreatedAt: string;
36
+ }
37
+ export interface RealtimeSessionChannelRecord {
38
+ ID: string;
39
+ AgentSessionID: string;
40
+ Channel: string;
41
+ Status: string;
42
+ __mj_CreatedAt: string;
43
+ }
44
+ export interface RealtimeSessionRunRecord {
45
+ ID: string;
46
+ AgentSessionID: string | null;
47
+ AgentID: string;
48
+ Agent: string | null;
49
+ TotalCost: number | null;
50
+ TotalTokensUsed: number | null;
51
+ StartedAt: string;
52
+ }
53
+ /** One session with everything the admin pages render, pre-aggregated. */
54
+ export interface RealtimeSessionRollup {
55
+ Record: RealtimeSessionRecord;
56
+ /** Parsed from Config JSON; null when the session carries no target. */
57
+ TargetAgentID: string | null;
58
+ /** Resolved client-side against AIEngineBase's cached agents. */
59
+ TargetAgentName: string | null;
60
+ /** Distinct channel names attached to the session. */
61
+ ChannelNames: string[];
62
+ /** Delegated AIAgentRuns (linked runs excluding the co-agent observability run). */
63
+ DelegatedRunCount: number;
64
+ /** Token total across all linked agent runs (co-agent + delegated). */
65
+ TotalTokens: number;
66
+ /** Cost total across all linked agent runs (co-agent + delegated). */
67
+ TotalCost: number;
68
+ StartedAt: Date;
69
+ /** __mj_CreatedAt → (ClosedAt ?? LastActiveAt). 0 when not derivable. */
70
+ DurationMs: number;
71
+ /** Session resumed from a prior one (LastSessionID set). */
72
+ IsResumed: boolean;
73
+ /**
74
+ * Close cause persisted by the server (`AIAgentSession.CloseReason`).
75
+ * `null` either means the session is still open, or it's a legacy row
76
+ * closed before the column existed ("unknown" in the UI).
77
+ */
78
+ CloseReason: RealtimeSessionCloseReason | null;
79
+ /**
80
+ * `CloseReason === 'Janitor'` — the session was force-closed by the
81
+ * lifecycle janitor (startup orphan recovery or staleness sweep) rather
82
+ * than ended explicitly. Derived from the real column; the old
83
+ * ClosedAt−LastActiveAt timing heuristic has been retired, so legacy
84
+ * NULL-reason rows report `false` here and "unknown" in the UI.
85
+ */
86
+ IsJanitorClosed: boolean;
87
+ }
88
+ /** Result of one load pass — current-window rollups plus trend context. */
89
+ export interface RealtimeSessionsDataset {
90
+ /** Sessions started in the current window (or still open), newest first. */
91
+ Sessions: RealtimeSessionRollup[];
92
+ /** Every loaded session rollup (covers 2× the window, for trend math). */
93
+ AllSessions: RealtimeSessionRollup[];
94
+ /** Count of sessions started in the prior (equal-length) window. */
95
+ PriorWindowSessionCount: number;
96
+ WindowStart: Date;
97
+ WindowMs: number;
98
+ }
99
+ export declare function TimeRangeToMs(range: string): number;
100
+ /**
101
+ * Loads sessions (2× window for trend), their channels, and linked agent runs
102
+ * via one batched RunViews call, then folds them into per-session rollups.
103
+ */
104
+ export declare function LoadRealtimeSessionsDataset(provider: IMetadataProvider, timeRange: string): Promise<RealtimeSessionsDataset>;
105
+ /** How a session's status pill should render — label, hover title, and Font Awesome icon. */
106
+ export interface SessionStatusDisplay {
107
+ Label: string;
108
+ Title: string;
109
+ /** Icon class for the pill; empty for `Active` (the template renders the pulsing live dot instead). */
110
+ Icon: string;
111
+ }
112
+ /**
113
+ * Builds the status-pill presentation for a session from its `Status` and persisted
114
+ * `CloseReason`. `Closed` sessions surface the real close cause; a `null` reason
115
+ * (legacy rows written before the column existed) renders as "unknown".
116
+ */
117
+ export declare function BuildSessionStatusDisplay(status: RealtimeSessionRecord['Status'], closeReason: RealtimeSessionCloseReason | null): SessionStatusDisplay;
118
+ /** Formats a duration as m:ss (or h:mm:ss past the hour) like the mockups' 4:51 / 11:18. */
119
+ export declare function FormatSessionDuration(ms: number): string;
120
+ /** 4200 → "4.2K", 1_250_000 → "1.3M". */
121
+ export declare function FormatTokenCount(value: number): string;
122
+ export declare function FormatSessionCost(value: number, decimals?: number): string;
123
+ /** Today → "3:40 pm", yesterday → "Yesterday", else "M/d". */
124
+ export declare function FormatSessionStart(date: Date): string;
125
+ /** Font Awesome icon for a channel name (keyword-mapped, with a generic fallback). */
126
+ export declare function ChannelIconClass(channelName: string): string;
127
+ //# sourceMappingURL=realtime-session-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-session-data.d.ts","sourceRoot":"","sources":["../../../../../src/AI/components/analytics/realtime/realtime-session-data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,233 @@
1
+ /**
2
+ * @fileoverview Shared data layer for the Realtime Voice analytics sections.
3
+ *
4
+ * Loads `MJ: AI Agent Sessions`, their `MJ: AI Agent Session Channels` rows,
5
+ * and the delegated `MJ: AI Agent Runs` (linked via `AgentSessionID`) in a
6
+ * single batched `RunViews` call, then aggregates everything client-side into
7
+ * per-session rollups (target agent, channel set, delegated-run count, token /
8
+ * cost totals, duration, close cause from the persisted `CloseReason` column).
9
+ *
10
+ * Used by both `AnalyticsRealtimeOverviewComponent` (KPIs + charts) and
11
+ * `AnalyticsRealtimeSessionsComponent` (management grid) so the two sections
12
+ * share one query shape and one aggregation pass.
13
+ */
14
+ import { RunView } from '@memberjunction/core';
15
+ import { NormalizeUUID } from '@memberjunction/global';
16
+ import { AIEngineBase } from '@memberjunction/ai-engine-base';
17
+ // ── Constants ──
18
+ const SESSION_FIELDS = [
19
+ 'ID', 'AgentID', 'Agent', 'UserID', 'User', 'Status', 'ConversationID',
20
+ 'LastSessionID', 'HostInstanceID', 'Config', 'LastActiveAt', 'ClosedAt', 'CloseReason', '__mj_CreatedAt'
21
+ ];
22
+ const SESSION_CHANNEL_FIELDS = ['ID', 'AgentSessionID', 'Channel', 'Status', '__mj_CreatedAt'];
23
+ const SESSION_RUN_FIELDS = ['ID', 'AgentSessionID', 'AgentID', 'Agent', 'TotalCost', 'TotalTokensUsed', 'StartedAt'];
24
+ /** Supported time-range keys → milliseconds. */
25
+ const TIME_RANGE_MS = {
26
+ '24h': 86_400_000,
27
+ '7d': 604_800_000,
28
+ '30d': 2_592_000_000
29
+ };
30
+ export function TimeRangeToMs(range) {
31
+ return TIME_RANGE_MS[range] ?? TIME_RANGE_MS['7d'];
32
+ }
33
+ // ── Loading + aggregation ──
34
+ /**
35
+ * Loads sessions (2× window for trend), their channels, and linked agent runs
36
+ * via one batched RunViews call, then folds them into per-session rollups.
37
+ */
38
+ export async function LoadRealtimeSessionsDataset(provider, timeRange) {
39
+ const windowMs = TimeRangeToMs(timeRange);
40
+ const now = Date.now();
41
+ const windowStart = new Date(now - windowMs);
42
+ const doubleWindowStart = new Date(now - windowMs * 2);
43
+ const rv = RunView.FromMetadataProvider(provider);
44
+ const [sessionResult, channelResult, runResult] = await rv.RunViews([
45
+ {
46
+ EntityName: 'MJ: AI Agent Sessions',
47
+ ExtraFilter: `__mj_CreatedAt >= '${doubleWindowStart.toISOString()}' OR Status IN ('Active','Idle')`,
48
+ Fields: SESSION_FIELDS,
49
+ OrderBy: '__mj_CreatedAt DESC',
50
+ ResultType: 'simple'
51
+ },
52
+ {
53
+ EntityName: 'MJ: AI Agent Session Channels',
54
+ ExtraFilter: `__mj_CreatedAt >= '${doubleWindowStart.toISOString()}' OR Status = 'Connected'`,
55
+ Fields: SESSION_CHANNEL_FIELDS,
56
+ ResultType: 'simple'
57
+ },
58
+ {
59
+ EntityName: 'MJ: AI Agent Runs',
60
+ ExtraFilter: `AgentSessionID IS NOT NULL AND StartedAt >= '${doubleWindowStart.toISOString()}'`,
61
+ Fields: SESSION_RUN_FIELDS,
62
+ OrderBy: 'StartedAt DESC',
63
+ ResultType: 'simple'
64
+ }
65
+ ]);
66
+ const sessions = (sessionResult?.Success ? sessionResult.Results : []);
67
+ const channels = (channelResult?.Success ? channelResult.Results : []);
68
+ const runs = (runResult?.Success ? runResult.Results : []);
69
+ const agentNameMap = await buildAgentNameMap();
70
+ const channelMap = groupBySessionId(channels, c => c.AgentSessionID);
71
+ const runMap = groupBySessionId(runs, r => r.AgentSessionID);
72
+ const allRollups = sessions.map(s => buildRollup(s, channelMap, runMap, agentNameMap));
73
+ const currentWindow = allRollups.filter(r => r.StartedAt >= windowStart || r.Record.Status !== 'Closed');
74
+ const priorCount = allRollups.filter(r => r.StartedAt >= doubleWindowStart && r.StartedAt < windowStart).length;
75
+ return {
76
+ Sessions: currentWindow,
77
+ AllSessions: allRollups,
78
+ PriorWindowSessionCount: priorCount,
79
+ WindowStart: windowStart,
80
+ WindowMs: windowMs
81
+ };
82
+ }
83
+ /** Loads AIEngineBase (no-op when cached) and maps normalized agent ID → name. */
84
+ async function buildAgentNameMap() {
85
+ const map = new Map();
86
+ try {
87
+ await AIEngineBase.Instance.Config(false);
88
+ for (const agent of AIEngineBase.Instance.Agents ?? []) {
89
+ map.set(NormalizeUUID(agent.ID), agent.Name ?? '');
90
+ }
91
+ }
92
+ catch {
93
+ // Tolerate engine load failures — target names degrade to null.
94
+ }
95
+ return map;
96
+ }
97
+ function groupBySessionId(rows, keyOf) {
98
+ const map = new Map();
99
+ for (const row of rows) {
100
+ const raw = keyOf(row);
101
+ if (!raw)
102
+ continue;
103
+ const key = NormalizeUUID(raw);
104
+ const list = map.get(key);
105
+ if (list) {
106
+ list.push(row);
107
+ }
108
+ else {
109
+ map.set(key, [row]);
110
+ }
111
+ }
112
+ return map;
113
+ }
114
+ function buildRollup(session, channelMap, runMap, agentNameMap) {
115
+ const key = NormalizeUUID(session.ID);
116
+ const config = parseSessionConfig(session.Config);
117
+ const sessionRuns = runMap.get(key) ?? [];
118
+ const coAgentRunKey = config.coAgentRunID ? NormalizeUUID(config.coAgentRunID) : null;
119
+ const delegatedRuns = sessionRuns.filter(r => !coAgentRunKey || NormalizeUUID(r.ID) !== coAgentRunKey);
120
+ const totalTokens = sessionRuns.reduce((s, r) => s + (r.TotalTokensUsed ?? 0), 0);
121
+ const totalCost = sessionRuns.reduce((s, r) => s + (r.TotalCost ?? 0), 0);
122
+ const channelNames = Array.from(new Set((channelMap.get(key) ?? []).map(c => c.Channel).filter(Boolean)));
123
+ const startedAt = new Date(session.__mj_CreatedAt);
124
+ const endAt = session.ClosedAt ? new Date(session.ClosedAt) : new Date(session.LastActiveAt);
125
+ const durationMs = Math.max(0, endAt.getTime() - startedAt.getTime());
126
+ const closeReason = session.Status === 'Closed' ? session.CloseReason ?? null : null;
127
+ const targetAgentID = config.targetAgentID ?? null;
128
+ return {
129
+ Record: session,
130
+ TargetAgentID: targetAgentID,
131
+ TargetAgentName: targetAgentID ? agentNameMap.get(NormalizeUUID(targetAgentID)) ?? null : null,
132
+ ChannelNames: channelNames,
133
+ DelegatedRunCount: delegatedRuns.length,
134
+ TotalTokens: totalTokens,
135
+ TotalCost: totalCost,
136
+ StartedAt: startedAt,
137
+ DurationMs: durationMs,
138
+ IsResumed: session.LastSessionID != null,
139
+ CloseReason: closeReason,
140
+ IsJanitorClosed: closeReason === 'Janitor'
141
+ };
142
+ }
143
+ function parseSessionConfig(raw) {
144
+ if (!raw)
145
+ return {};
146
+ try {
147
+ const parsed = JSON.parse(raw);
148
+ return parsed && typeof parsed === 'object' ? parsed : {};
149
+ }
150
+ catch {
151
+ return {};
152
+ }
153
+ }
154
+ /**
155
+ * Builds the status-pill presentation for a session from its `Status` and persisted
156
+ * `CloseReason`. `Closed` sessions surface the real close cause; a `null` reason
157
+ * (legacy rows written before the column existed) renders as "unknown".
158
+ */
159
+ export function BuildSessionStatusDisplay(status, closeReason) {
160
+ if (status === 'Active') {
161
+ return { Label: 'Active', Title: 'Live duplex session', Icon: '' };
162
+ }
163
+ if (status === 'Idle') {
164
+ return { Label: 'Idle', Title: 'Holding — past the idle threshold', Icon: 'fa-solid fa-pause' };
165
+ }
166
+ switch (closeReason) {
167
+ case 'Explicit':
168
+ return { Label: 'Closed', Title: 'Closed explicitly (user hang-up / close mutation)', Icon: 'fa-solid fa-circle-stop' };
169
+ case 'Janitor':
170
+ return { Label: 'Closed · janitor', Title: 'Force-closed by the lifecycle janitor (staleness sweep / orphan reconciliation)', Icon: 'fa-solid fa-broom' };
171
+ case 'Shutdown':
172
+ return { Label: 'Closed · shutdown', Title: 'Closed by the host’s graceful shutdown drain', Icon: 'fa-solid fa-power-off' };
173
+ case 'Error':
174
+ return { Label: 'Closed · error', Title: 'Closed by a failure-path teardown', Icon: 'fa-solid fa-triangle-exclamation' };
175
+ default:
176
+ return { Label: 'Closed · unknown', Title: 'Closed before close-cause tracking existed (legacy row)', Icon: 'fa-solid fa-circle-question' };
177
+ }
178
+ }
179
+ /** Formats a duration as m:ss (or h:mm:ss past the hour) like the mockups' 4:51 / 11:18. */
180
+ export function FormatSessionDuration(ms) {
181
+ if (ms <= 0)
182
+ return '0:00';
183
+ const totalSeconds = Math.floor(ms / 1000);
184
+ const seconds = totalSeconds % 60;
185
+ const minutes = Math.floor(totalSeconds / 60) % 60;
186
+ const hours = Math.floor(totalSeconds / 3600);
187
+ const pad = (n) => n.toString().padStart(2, '0');
188
+ return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${minutes}:${pad(seconds)}`;
189
+ }
190
+ /** 4200 → "4.2K", 1_250_000 → "1.3M". */
191
+ export function FormatTokenCount(value) {
192
+ if (value >= 1_000_000)
193
+ return (value / 1_000_000).toFixed(1) + 'M';
194
+ if (value >= 1_000)
195
+ return (value / 1_000).toFixed(1) + 'K';
196
+ return value.toString();
197
+ }
198
+ export function FormatSessionCost(value, decimals = 2) {
199
+ if (value === 0)
200
+ return '$0.00';
201
+ if (value < 0.01 && decimals < 4)
202
+ decimals = 4;
203
+ return '$' + value.toFixed(decimals);
204
+ }
205
+ /** Today → "3:40 pm", yesterday → "Yesterday", else "M/d". */
206
+ export function FormatSessionStart(date) {
207
+ const now = new Date();
208
+ const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
209
+ const startOfYesterday = new Date(startOfToday.getTime() - 86_400_000);
210
+ if (date >= startOfToday) {
211
+ return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }).toLowerCase();
212
+ }
213
+ if (date >= startOfYesterday) {
214
+ return 'Yesterday';
215
+ }
216
+ return date.toLocaleDateString([], { month: 'numeric', day: 'numeric' });
217
+ }
218
+ /** Font Awesome icon for a channel name (keyword-mapped, with a generic fallback). */
219
+ export function ChannelIconClass(channelName) {
220
+ const name = channelName.toLowerCase();
221
+ if (name.includes('voice') || name.includes('audio'))
222
+ return 'fa-solid fa-microphone-lines';
223
+ if (name.includes('whiteboard') || name.includes('board'))
224
+ return 'fa-solid fa-chalkboard';
225
+ if (name.includes('text') || name.includes('chat'))
226
+ return 'fa-solid fa-comment';
227
+ if (name.includes('control'))
228
+ return 'fa-solid fa-sliders';
229
+ if (name.includes('video'))
230
+ return 'fa-solid fa-video';
231
+ return 'fa-solid fa-plug';
232
+ }
233
+ //# sourceMappingURL=realtime-session-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-session-data.js","sourceRoot":"","sources":["../../../../../src/AI/components/analytics/realtime/realtime-session-data.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Shared data layer for the Realtime Voice analytics sections.\n *\n * Loads `MJ: AI Agent Sessions`, their `MJ: AI Agent Session Channels` rows,\n * and the delegated `MJ: AI Agent Runs` (linked via `AgentSessionID`) in a\n * single batched `RunViews` call, then aggregates everything client-side into\n * per-session rollups (target agent, channel set, delegated-run count, token /\n * cost totals, duration, close cause from the persisted `CloseReason` column).\n *\n * Used by both `AnalyticsRealtimeOverviewComponent` (KPIs + charts) and\n * `AnalyticsRealtimeSessionsComponent` (management grid) so the two sections\n * share one query shape and one aggregation pass.\n */\n\nimport { IMetadataProvider, RunView } from '@memberjunction/core';\nimport { NormalizeUUID } from '@memberjunction/global';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\n\n// ── Raw row shapes (ResultType: 'simple', narrowed Fields) ──\n\n/**\n * Why a session was closed, as stamped server-side by `SessionManager.CloseSession`\n * (mirrors `AIAgentSessionEntity.CloseReason`). `null` means a legacy row closed\n * before the column existed — rendered as \"unknown\".\n */\nexport type RealtimeSessionCloseReason = 'Error' | 'Explicit' | 'Janitor' | 'Shutdown';\n\nexport interface RealtimeSessionRecord {\n ID: string;\n AgentID: string;\n Agent: string | null;\n UserID: string;\n User: string;\n Status: 'Active' | 'Idle' | 'Closed';\n ConversationID: string | null;\n LastSessionID: string | null;\n HostInstanceID: string | null;\n Config: string | null;\n LastActiveAt: string;\n ClosedAt: string | null;\n CloseReason: RealtimeSessionCloseReason | null;\n __mj_CreatedAt: string;\n}\n\nexport interface RealtimeSessionChannelRecord {\n ID: string;\n AgentSessionID: string;\n Channel: string;\n Status: string;\n __mj_CreatedAt: string;\n}\n\nexport interface RealtimeSessionRunRecord {\n ID: string;\n AgentSessionID: string | null;\n AgentID: string;\n Agent: string | null;\n TotalCost: number | null;\n TotalTokensUsed: number | null;\n StartedAt: string;\n}\n\n/** Shape of the run ids the realtime voice stack persists in `AIAgentSession.Config`. */\ninterface RealtimeSessionConfigJson {\n targetAgentID?: string;\n coAgentRunID?: string;\n promptRunID?: string;\n pendingFeedbackRunID?: string;\n}\n\n// ── Aggregated shapes ──\n\n/** One session with everything the admin pages render, pre-aggregated. */\nexport interface RealtimeSessionRollup {\n Record: RealtimeSessionRecord;\n /** Parsed from Config JSON; null when the session carries no target. */\n TargetAgentID: string | null;\n /** Resolved client-side against AIEngineBase's cached agents. */\n TargetAgentName: string | null;\n /** Distinct channel names attached to the session. */\n ChannelNames: string[];\n /** Delegated AIAgentRuns (linked runs excluding the co-agent observability run). */\n DelegatedRunCount: number;\n /** Token total across all linked agent runs (co-agent + delegated). */\n TotalTokens: number;\n /** Cost total across all linked agent runs (co-agent + delegated). */\n TotalCost: number;\n StartedAt: Date;\n /** __mj_CreatedAt → (ClosedAt ?? LastActiveAt). 0 when not derivable. */\n DurationMs: number;\n /** Session resumed from a prior one (LastSessionID set). */\n IsResumed: boolean;\n /**\n * Close cause persisted by the server (`AIAgentSession.CloseReason`).\n * `null` either means the session is still open, or it's a legacy row\n * closed before the column existed (\"unknown\" in the UI).\n */\n CloseReason: RealtimeSessionCloseReason | null;\n /**\n * `CloseReason === 'Janitor'` — the session was force-closed by the\n * lifecycle janitor (startup orphan recovery or staleness sweep) rather\n * than ended explicitly. Derived from the real column; the old\n * ClosedAt−LastActiveAt timing heuristic has been retired, so legacy\n * NULL-reason rows report `false` here and \"unknown\" in the UI.\n */\n IsJanitorClosed: boolean;\n}\n\n/** Result of one load pass — current-window rollups plus trend context. */\nexport interface RealtimeSessionsDataset {\n /** Sessions started in the current window (or still open), newest first. */\n Sessions: RealtimeSessionRollup[];\n /** Every loaded session rollup (covers 2× the window, for trend math). */\n AllSessions: RealtimeSessionRollup[];\n /** Count of sessions started in the prior (equal-length) window. */\n PriorWindowSessionCount: number;\n WindowStart: Date;\n WindowMs: number;\n}\n\n// ── Constants ──\n\nconst SESSION_FIELDS = [\n 'ID', 'AgentID', 'Agent', 'UserID', 'User', 'Status', 'ConversationID',\n 'LastSessionID', 'HostInstanceID', 'Config', 'LastActiveAt', 'ClosedAt', 'CloseReason', '__mj_CreatedAt'\n];\n\nconst SESSION_CHANNEL_FIELDS = ['ID', 'AgentSessionID', 'Channel', 'Status', '__mj_CreatedAt'];\n\nconst SESSION_RUN_FIELDS = ['ID', 'AgentSessionID', 'AgentID', 'Agent', 'TotalCost', 'TotalTokensUsed', 'StartedAt'];\n\n/** Supported time-range keys → milliseconds. */\nconst TIME_RANGE_MS: Record<string, number> = {\n '24h': 86_400_000,\n '7d': 604_800_000,\n '30d': 2_592_000_000\n};\n\nexport function TimeRangeToMs(range: string): number {\n return TIME_RANGE_MS[range] ?? TIME_RANGE_MS['7d'];\n}\n\n// ── Loading + aggregation ──\n\n/**\n * Loads sessions (2× window for trend), their channels, and linked agent runs\n * via one batched RunViews call, then folds them into per-session rollups.\n */\nexport async function LoadRealtimeSessionsDataset(\n provider: IMetadataProvider,\n timeRange: string\n): Promise<RealtimeSessionsDataset> {\n const windowMs = TimeRangeToMs(timeRange);\n const now = Date.now();\n const windowStart = new Date(now - windowMs);\n const doubleWindowStart = new Date(now - windowMs * 2);\n\n const rv = RunView.FromMetadataProvider(provider);\n const [sessionResult, channelResult, runResult] = await rv.RunViews([\n {\n EntityName: 'MJ: AI Agent Sessions',\n ExtraFilter: `__mj_CreatedAt >= '${doubleWindowStart.toISOString()}' OR Status IN ('Active','Idle')`,\n Fields: SESSION_FIELDS,\n OrderBy: '__mj_CreatedAt DESC',\n ResultType: 'simple'\n },\n {\n EntityName: 'MJ: AI Agent Session Channels',\n ExtraFilter: `__mj_CreatedAt >= '${doubleWindowStart.toISOString()}' OR Status = 'Connected'`,\n Fields: SESSION_CHANNEL_FIELDS,\n ResultType: 'simple'\n },\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `AgentSessionID IS NOT NULL AND StartedAt >= '${doubleWindowStart.toISOString()}'`,\n Fields: SESSION_RUN_FIELDS,\n OrderBy: 'StartedAt DESC',\n ResultType: 'simple'\n }\n ]);\n\n const sessions = (sessionResult?.Success ? sessionResult.Results : []) as RealtimeSessionRecord[];\n const channels = (channelResult?.Success ? channelResult.Results : []) as RealtimeSessionChannelRecord[];\n const runs = (runResult?.Success ? runResult.Results : []) as RealtimeSessionRunRecord[];\n\n const agentNameMap = await buildAgentNameMap();\n const channelMap = groupBySessionId(channels, c => c.AgentSessionID);\n const runMap = groupBySessionId(runs, r => r.AgentSessionID);\n\n const allRollups = sessions.map(s => buildRollup(s, channelMap, runMap, agentNameMap));\n\n const currentWindow = allRollups.filter(\n r => r.StartedAt >= windowStart || r.Record.Status !== 'Closed'\n );\n const priorCount = allRollups.filter(\n r => r.StartedAt >= doubleWindowStart && r.StartedAt < windowStart\n ).length;\n\n return {\n Sessions: currentWindow,\n AllSessions: allRollups,\n PriorWindowSessionCount: priorCount,\n WindowStart: windowStart,\n WindowMs: windowMs\n };\n}\n\n/** Loads AIEngineBase (no-op when cached) and maps normalized agent ID → name. */\nasync function buildAgentNameMap(): Promise<Map<string, string>> {\n const map = new Map<string, string>();\n try {\n await AIEngineBase.Instance.Config(false);\n for (const agent of AIEngineBase.Instance.Agents ?? []) {\n map.set(NormalizeUUID(agent.ID), agent.Name ?? '');\n }\n } catch {\n // Tolerate engine load failures — target names degrade to null.\n }\n return map;\n}\n\nfunction groupBySessionId<T>(rows: T[], keyOf: (row: T) => string | null): Map<string, T[]> {\n const map = new Map<string, T[]>();\n for (const row of rows) {\n const raw = keyOf(row);\n if (!raw) continue;\n const key = NormalizeUUID(raw);\n const list = map.get(key);\n if (list) {\n list.push(row);\n } else {\n map.set(key, [row]);\n }\n }\n return map;\n}\n\nfunction buildRollup(\n session: RealtimeSessionRecord,\n channelMap: Map<string, RealtimeSessionChannelRecord[]>,\n runMap: Map<string, RealtimeSessionRunRecord[]>,\n agentNameMap: Map<string, string>\n): RealtimeSessionRollup {\n const key = NormalizeUUID(session.ID);\n const config = parseSessionConfig(session.Config);\n const sessionRuns = runMap.get(key) ?? [];\n const coAgentRunKey = config.coAgentRunID ? NormalizeUUID(config.coAgentRunID) : null;\n\n const delegatedRuns = sessionRuns.filter(r => !coAgentRunKey || NormalizeUUID(r.ID) !== coAgentRunKey);\n const totalTokens = sessionRuns.reduce((s, r) => s + (r.TotalTokensUsed ?? 0), 0);\n const totalCost = sessionRuns.reduce((s, r) => s + (r.TotalCost ?? 0), 0);\n\n const channelNames = Array.from(\n new Set((channelMap.get(key) ?? []).map(c => c.Channel).filter(Boolean))\n );\n\n const startedAt = new Date(session.__mj_CreatedAt);\n const endAt = session.ClosedAt ? new Date(session.ClosedAt) : new Date(session.LastActiveAt);\n const durationMs = Math.max(0, endAt.getTime() - startedAt.getTime());\n\n const closeReason = session.Status === 'Closed' ? session.CloseReason ?? null : null;\n\n const targetAgentID = config.targetAgentID ?? null;\n return {\n Record: session,\n TargetAgentID: targetAgentID,\n TargetAgentName: targetAgentID ? agentNameMap.get(NormalizeUUID(targetAgentID)) ?? null : null,\n ChannelNames: channelNames,\n DelegatedRunCount: delegatedRuns.length,\n TotalTokens: totalTokens,\n TotalCost: totalCost,\n StartedAt: startedAt,\n DurationMs: durationMs,\n IsResumed: session.LastSessionID != null,\n CloseReason: closeReason,\n IsJanitorClosed: closeReason === 'Janitor'\n };\n}\n\nfunction parseSessionConfig(raw: string | null): RealtimeSessionConfigJson {\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw) as RealtimeSessionConfigJson;\n return parsed && typeof parsed === 'object' ? parsed : {};\n } catch {\n return {};\n }\n}\n\n// ── Formatting helpers (shared by both sections) ──\n\n/** How a session's status pill should render — label, hover title, and Font Awesome icon. */\nexport interface SessionStatusDisplay {\n Label: string;\n Title: string;\n /** Icon class for the pill; empty for `Active` (the template renders the pulsing live dot instead). */\n Icon: string;\n}\n\n/**\n * Builds the status-pill presentation for a session from its `Status` and persisted\n * `CloseReason`. `Closed` sessions surface the real close cause; a `null` reason\n * (legacy rows written before the column existed) renders as \"unknown\".\n */\nexport function BuildSessionStatusDisplay(\n status: RealtimeSessionRecord['Status'],\n closeReason: RealtimeSessionCloseReason | null\n): SessionStatusDisplay {\n if (status === 'Active') {\n return { Label: 'Active', Title: 'Live duplex session', Icon: '' };\n }\n if (status === 'Idle') {\n return { Label: 'Idle', Title: 'Holding — past the idle threshold', Icon: 'fa-solid fa-pause' };\n }\n switch (closeReason) {\n case 'Explicit':\n return { Label: 'Closed', Title: 'Closed explicitly (user hang-up / close mutation)', Icon: 'fa-solid fa-circle-stop' };\n case 'Janitor':\n return { Label: 'Closed · janitor', Title: 'Force-closed by the lifecycle janitor (staleness sweep / orphan reconciliation)', Icon: 'fa-solid fa-broom' };\n case 'Shutdown':\n return { Label: 'Closed · shutdown', Title: 'Closed by the host’s graceful shutdown drain', Icon: 'fa-solid fa-power-off' };\n case 'Error':\n return { Label: 'Closed · error', Title: 'Closed by a failure-path teardown', Icon: 'fa-solid fa-triangle-exclamation' };\n default:\n return { Label: 'Closed · unknown', Title: 'Closed before close-cause tracking existed (legacy row)', Icon: 'fa-solid fa-circle-question' };\n }\n}\n\n/** Formats a duration as m:ss (or h:mm:ss past the hour) like the mockups' 4:51 / 11:18. */\nexport function FormatSessionDuration(ms: number): string {\n if (ms <= 0) return '0:00';\n const totalSeconds = Math.floor(ms / 1000);\n const seconds = totalSeconds % 60;\n const minutes = Math.floor(totalSeconds / 60) % 60;\n const hours = Math.floor(totalSeconds / 3600);\n const pad = (n: number) => n.toString().padStart(2, '0');\n return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${minutes}:${pad(seconds)}`;\n}\n\n/** 4200 → \"4.2K\", 1_250_000 → \"1.3M\". */\nexport function FormatTokenCount(value: number): string {\n if (value >= 1_000_000) return (value / 1_000_000).toFixed(1) + 'M';\n if (value >= 1_000) return (value / 1_000).toFixed(1) + 'K';\n return value.toString();\n}\n\nexport function FormatSessionCost(value: number, decimals = 2): string {\n if (value === 0) return '$0.00';\n if (value < 0.01 && decimals < 4) decimals = 4;\n return '$' + value.toFixed(decimals);\n}\n\n/** Today → \"3:40 pm\", yesterday → \"Yesterday\", else \"M/d\". */\nexport function FormatSessionStart(date: Date): string {\n const now = new Date();\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const startOfYesterday = new Date(startOfToday.getTime() - 86_400_000);\n if (date >= startOfToday) {\n return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }).toLowerCase();\n }\n if (date >= startOfYesterday) {\n return 'Yesterday';\n }\n return date.toLocaleDateString([], { month: 'numeric', day: 'numeric' });\n}\n\n/** Font Awesome icon for a channel name (keyword-mapped, with a generic fallback). */\nexport function ChannelIconClass(channelName: string): string {\n const name = channelName.toLowerCase();\n if (name.includes('voice') || name.includes('audio')) return 'fa-solid fa-microphone-lines';\n if (name.includes('whiteboard') || name.includes('board')) return 'fa-solid fa-chalkboard';\n if (name.includes('text') || name.includes('chat')) return 'fa-solid fa-comment';\n if (name.includes('control')) return 'fa-solid fa-sliders';\n if (name.includes('video')) return 'fa-solid fa-video';\n return 'fa-solid fa-plug';\n}\n"]}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @fileoverview Realtime Voice — sessions management grid.
3
+ *
4
+ * Every long-lived `MJ: AI Agent Sessions` row across the platform — live
5
+ * calls, idle holds, and closed history — with the channels each session
6
+ * opened and the agent runs it delegated. KPI strip up top, a filter bar
7
+ * (search · status · target agent · user · host), the data grid (Agent →
8
+ * Target, status pills incl. the persisted close cause — explicit / janitor /
9
+ * shutdown / error / unknown-legacy, channel icons, run count, tokens / cost,
10
+ * started, duration, host instance), and row drill-in to the session record.
11
+ */
12
+ import { OnInit } from '@angular/core';
13
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
14
+ import { KPICardData } from '../../widgets/kpi-card.component';
15
+ import * as i0 from "@angular/core";
16
+ interface SessionGridRow {
17
+ ID: string;
18
+ Agent: string;
19
+ Target: string;
20
+ User: string;
21
+ Status: string;
22
+ StatusLabel: string;
23
+ StatusClass: string;
24
+ StatusTitle: string;
25
+ /** Pill icon (close cause for Closed rows); empty for Active, which renders the live dot. */
26
+ StatusIcon: string;
27
+ IsResumed: boolean;
28
+ ChannelIcons: {
29
+ Icon: string;
30
+ Name: string;
31
+ }[];
32
+ ChannelCount: number;
33
+ Runs: number;
34
+ Tokens: string;
35
+ Cost: string;
36
+ Started: string;
37
+ StartedAt: Date;
38
+ Duration: string;
39
+ Host: string;
40
+ SearchText: string;
41
+ }
42
+ interface SelectOption {
43
+ Value: string;
44
+ Label: string;
45
+ }
46
+ export declare class AnalyticsRealtimeSessionsComponent extends BaseAngularComponent implements OnInit {
47
+ private _timeRange;
48
+ set TimeRange(value: string);
49
+ get TimeRange(): string;
50
+ private initialized;
51
+ private cdr;
52
+ private dataset;
53
+ private allRows;
54
+ IsLoading: boolean;
55
+ KPICards: KPICardData[];
56
+ FilteredRows: SessionGridRow[];
57
+ PagedRows: SessionGridRow[];
58
+ TargetOptions: SelectOption[];
59
+ UserOptions: SelectOption[];
60
+ HostOptions: SelectOption[];
61
+ SearchTerm: string;
62
+ StatusFilter: string;
63
+ TargetFilter: string;
64
+ UserFilter: string;
65
+ HostFilter: string;
66
+ PageIndex: number;
67
+ get LiveCount(): number;
68
+ get IdleCount(): number;
69
+ get ClosedCount(): number;
70
+ get PageCount(): number;
71
+ ngOnInit(): void;
72
+ /** Reloads everything for the current time range. */
73
+ LoadData(): Promise<void>;
74
+ /** Opens the AIAgentSession record via the shared Explorer record-open path. */
75
+ OpenSession(sessionId: string): void;
76
+ OnSearchChanged(event: Event): void;
77
+ OnStatusFilterChanged(event: Event): void;
78
+ OnTargetFilterChanged(event: Event): void;
79
+ OnUserFilterChanged(event: Event): void;
80
+ OnHostFilterChanged(event: Event): void;
81
+ PrevPage(): void;
82
+ NextPage(): void;
83
+ private buildRows;
84
+ private toGridRow;
85
+ private buildKPICards;
86
+ private buildFilterOptions;
87
+ private applyFilters;
88
+ private applyPaging;
89
+ static ɵfac: i0.ɵɵFactoryDeclaration<AnalyticsRealtimeSessionsComponent, never>;
90
+ static ɵcmp: i0.ɵɵComponentDeclaration<AnalyticsRealtimeSessionsComponent, "app-analytics-realtime-sessions", never, { "TimeRange": { "alias": "TimeRange"; "required": false; }; }, {}, never, never, false, never>;
91
+ }
92
+ export declare function LoadAnalyticsRealtimeSessions(): void;
93
+ export {};
94
+ //# sourceMappingURL=realtime-sessions.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-sessions.component.d.ts","sourceRoot":"","sources":["../../../../../src/AI/components/analytics/realtime/realtime-sessions.component.ts"],"names":[],"mappings":"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"}