@memberjunction/ng-conversations 6.1.0-edge.6 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/lib/components/dialogs/input-dialog.component.d.ts.map +1 -1
  2. package/dist/lib/components/dialogs/input-dialog.component.js +3 -3
  3. package/dist/lib/components/dialogs/input-dialog.component.js.map +1 -1
  4. package/dist/lib/components/message/message-input.component.js +4 -4
  5. package/dist/lib/components/message/message-input.component.js.map +1 -1
  6. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +1 -1
  7. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
  8. package/dist/lib/components/realtime/realtime-activity-rail.component.js +2 -2
  9. package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
  10. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +16 -1
  11. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -1
  12. package/dist/lib/components/realtime/realtime-agent-picker.component.js +39 -7
  13. package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -1
  14. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +2 -2
  15. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -1
  16. package/dist/lib/components/realtime/realtime-delegation-card.component.js +159 -107
  17. package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
  18. package/dist/lib/components/realtime/realtime-session-state.d.ts +9 -4
  19. package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
  20. package/dist/lib/components/realtime/realtime-session-state.js +95 -19
  21. package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
  22. package/dist/lib/services/delegation-result-parser.d.ts +5 -0
  23. package/dist/lib/services/delegation-result-parser.d.ts.map +1 -1
  24. package/dist/lib/services/delegation-result-parser.js +12 -0
  25. package/dist/lib/services/delegation-result-parser.js.map +1 -1
  26. package/dist/lib/services/realtime-session-review.service.d.ts +31 -1
  27. package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
  28. package/dist/lib/services/realtime-session-review.service.js +63 -8
  29. package/dist/lib/services/realtime-session-review.service.js.map +1 -1
  30. package/dist/lib/services/realtime-session.service.d.ts +15 -8
  31. package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
  32. package/dist/lib/services/realtime-session.service.js +62 -14
  33. package/dist/lib/services/realtime-session.service.js.map +1 -1
  34. package/package.json +31 -31
@@ -70,7 +70,7 @@ export declare class RealtimeActivityRailComponent implements OnInit, OnDestroy
70
70
  StepLabel(card: RealtimeDelegationCardVM): string;
71
71
  /** Elapsed duration for finished work ("8s", "1m 04s"); null while running (kept cheap). */
72
72
  Elapsed(card: RealtimeDelegationCardVM): string | null;
73
- /** True when the dev "Open run" link should render for this card (gear on + run id known). */
73
+ /** True when the dev "Open run" link should render for this card (gear on + run id known). Suppressed for direct actions. */
74
74
  ShowOpenRun(card: RealtimeDelegationCardVM): boolean;
75
75
  /** Emits the open-run request for the card's delegated run. */
76
76
  OpenRun(card: RealtimeDelegationCardVM): void;
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-activity-rail.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-activity-rail.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAiB,MAAM,EAAE,SAAS,EAA6B,MAAM,eAAe,CAAC;AAGrH,OAAO,EAAsB,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EACL,oBAAoB,EAAE,wBAAwB,EAC/C,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;;AAMnF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAOa,6BAA8B,YAAW,MAAM,EAAE,SAAS;IACrE,+DAA+D;IAC/D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAM;IAE7C,6DAA6D;IAClC,KAAK,EAAG,oBAAoB,CAAC;IAExD,kFAAkF;IACzE,OAAO,UAAS;IAEzB;;;;;OAKG;IACM,QAAQ,UAAS;IAE1B,sEAAsE;IAC7D,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAE7C,6EAA6E;IACpE,aAAa,SAAM;IAE5B;;;;;OAKG;IACM,cAAc,EAAE,wBAAwB,EAAE,CAAM;IAEzD,0FAA0F;IAChF,gBAAgB,uBAA8B;IAExD,8FAA8F;IACpF,qBAAqB,yCAAgD;IAE/E,qFAAqF;IAC9E,SAAS,UAAS;IAEzB,+FAA+F;IACxF,gBAAgB,EAAE,wBAAwB,GAAG,IAAI,CAAQ;IAEhE,0FAA0F;IACnF,oBAAoB,SAAkC;IAE7D,OAAO,CAAC,UAAU,CAAC,CAAe;IAClC,OAAO,CAAC,GAAG,CAA6B;IAExC,qFAAqF;IACrF,SAAgB,oBAAoB,UACyD;IAE7F,QAAQ,IAAI,IAAI;IAKhB,WAAW,IAAI,IAAI;IAInB,qFAAqF;IAC9E,MAAM,IAAI,IAAI;IAIrB,wCAAwC;IACjC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAIvE,gDAAgD;IACzC,SAAS,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAIxD,4FAA4F;IACrF,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM,GAAG,IAAI;IAO7D,8FAA8F;IACvF,WAAW,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO;IAI3D,+DAA+D;IACxD,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,IAAI;IAMpD,6FAA6F;IACtF,aAAa,CAAC,IAAI,EAAE,wBAAwB,GAAG,wBAAwB,EAAE;IAIhF,qDAAqD;IAC9C,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,GAAG,MAAM;IAI/E,8EAA8E;IACvE,cAAc,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO;IAIlE;;;;;OAKG;IACI,YAAY,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI;IAS7D,iEAAiE;IAC1D,aAAa,IAAI,IAAI;IAO5B;;;;;OAKG;IACI,cAAc,CAAC,KAAK,EAAE,2BAA2B,GAAG,IAAI;IAS/D,8CAA8C;IACvC,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAMtD,wFAAwF;IACxF,OAAO,CAAC,aAAa;IAWrB,gFAAgF;IAChF,OAAO,CAAC,gBAAgB;yCA3Kb,6BAA6B;2CAA7B,6BAA6B;CAkLzC"}
1
+ {"version":3,"file":"realtime-activity-rail.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-activity-rail.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAiB,MAAM,EAAE,SAAS,EAA6B,MAAM,eAAe,CAAC;AAGrH,OAAO,EAAsB,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EACL,oBAAoB,EAAE,wBAAwB,EAC/C,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;;AAMnF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAOa,6BAA8B,YAAW,MAAM,EAAE,SAAS;IACrE,+DAA+D;IAC/D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAM;IAE7C,6DAA6D;IAClC,KAAK,EAAG,oBAAoB,CAAC;IAExD,kFAAkF;IACzE,OAAO,UAAS;IAEzB;;;;;OAKG;IACM,QAAQ,UAAS;IAE1B,sEAAsE;IAC7D,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAE7C,6EAA6E;IACpE,aAAa,SAAM;IAE5B;;;;;OAKG;IACM,cAAc,EAAE,wBAAwB,EAAE,CAAM;IAEzD,0FAA0F;IAChF,gBAAgB,uBAA8B;IAExD,8FAA8F;IACpF,qBAAqB,yCAAgD;IAE/E,qFAAqF;IAC9E,SAAS,UAAS;IAEzB,+FAA+F;IACxF,gBAAgB,EAAE,wBAAwB,GAAG,IAAI,CAAQ;IAEhE,0FAA0F;IACnF,oBAAoB,SAAkC;IAE7D,OAAO,CAAC,UAAU,CAAC,CAAe;IAClC,OAAO,CAAC,GAAG,CAA6B;IAExC,qFAAqF;IACrF,SAAgB,oBAAoB,UACyD;IAE7F,QAAQ,IAAI,IAAI;IAKhB,WAAW,IAAI,IAAI;IAInB,qFAAqF;IAC9E,MAAM,IAAI,IAAI;IAIrB,wCAAwC;IACjC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAIvE,gDAAgD;IACzC,SAAS,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAIxD,4FAA4F;IACrF,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM,GAAG,IAAI;IAO7D,6HAA6H;IACtH,WAAW,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO;IAI3D,+DAA+D;IACxD,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,IAAI;IAMpD,6FAA6F;IACtF,aAAa,CAAC,IAAI,EAAE,wBAAwB,GAAG,wBAAwB,EAAE;IAIhF,qDAAqD;IAC9C,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,GAAG,MAAM;IAI/E,8EAA8E;IACvE,cAAc,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO;IAIlE;;;;;OAKG;IACI,YAAY,CAAC,QAAQ,EAAE,wBAAwB,GAAG,IAAI;IAS7D,iEAAiE;IAC1D,aAAa,IAAI,IAAI;IAO5B;;;;;OAKG;IACI,cAAc,CAAC,KAAK,EAAE,2BAA2B,GAAG,IAAI;IAS/D,8CAA8C;IACvC,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,MAAM;IAMtD,wFAAwF;IACxF,OAAO,CAAC,aAAa;IAWrB,gFAAgF;IAChF,OAAO,CAAC,gBAAgB;yCA3Kb,6BAA6B;2CAA7B,6BAA6B;CAkLzC"}
@@ -340,9 +340,9 @@ export class RealtimeActivityRailComponent {
340
340
  }
341
341
  return FormatElapsed(card.FinishedAt - card.StartedAt);
342
342
  }
343
- /** True when the dev "Open run" link should render for this card (gear on + run id known). */
343
+ /** True when the dev "Open run" link should render for this card (gear on + run id known). Suppressed for direct actions. */
344
344
  ShowOpenRun(card) {
345
- return this.DevMode && !!card.RunID;
345
+ return this.DevMode && card.Kind !== 'action' && !!card.RunID;
346
346
  }
347
347
  /** Emits the open-run request for the card's delegated run. */
348
348
  OpenRun(card) {
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-activity-rail.component.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-activity-rail.component.ts","../../../../src/lib/components/realtime/realtime-activity-rail.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, OnInit, OnDestroy, ChangeDetectorRef, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Subscription } from 'rxjs';\nimport { AngularSplitModule, SplitGutterInteractionEvent } from 'angular-split';\nimport { UserInfo } from '@memberjunction/core';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { ArtifactsModule } from '@memberjunction/ng-artifacts';\nimport {\n RealtimeSessionState, RealtimeDelegationCardVM, FriendlyStepLabel, FormatElapsed\n} from './realtime-session-state';\nimport { ParsedDelegationArtifact } from '../../services/delegation-result-parser';\nimport {\n ACTIVITY_SPLIT_DEFAULT_PERCENT, ACTIVITY_SPLIT_PREF_KEY,\n ClampActivitySplitPercent, ParseActivitySplitPercent, SerializeActivitySplitPercent\n} from './realtime-surface-tab-style';\n\n/**\n * The \"Session activity\" surface (the Activity tab's content): a compact, newest-first list\n * of EVERY tool/agent call of the session — so parallel/multiple delegations live here and\n * the conversation thread stays clean.\n *\n * Each entry shows agent name, status, the latest friendly step label, elapsed time, and a\n * one-line result preview. When a run produced ARTIFACTS, each artifact renders as an inline\n * PREVIEW card (reusing `mj-artifact-message-card`, the same component the conversation\n * messages use). Clicking a preview SPLITS this pane horizontally — activity list on the\n * LEFT, the full artifact viewer on the RIGHT (`as-split`); clicking another swaps the right\n * pane; a close control collapses back to full-width activity. The split width is persisted\n * per-user via {@link UserInfoEngine} (NOT localStorage). This replaces the old \"one surface\n * tab per artifact\" model.\n *\n * Collapsible via the chevron in its header (standalone presentation only). Presentational\n * over the same {@link RealtimeSessionState} the thread renders.\n */\n@Component({\n standalone: true,\n selector: 'mj-realtime-activity-rail',\n imports: [CommonModule, AngularSplitModule, ArtifactsModule],\n templateUrl: './realtime-activity-rail.component.html',\n styleUrl: './realtime-activity-rail.component.css'\n})\nexport class RealtimeActivityRailComponent implements OnInit, OnDestroy {\n /** Maximum characters of the result preview in a done card. */\n private static readonly PreviewMaxChars = 90;\n\n /** Shared live-session state, owned by the overlay shell. */\n @Input({ required: true }) State!: RealtimeSessionState;\n\n /** Whether developer affordances (\"Open run\" links) are revealed (gear-gated). */\n @Input() DevMode = false;\n\n /**\n * EMBEDDED mode: the rail renders as the content of the surface panel's Activity tab —\n * its own header/collapse chrome is hidden (the panel's tab strip owns collapse) and it\n * stretches to the pane's full width. Default `false` keeps the original standalone\n * right-rail presentation for any other consumer.\n */\n @Input() Embedded = false;\n\n /** The signed-in user, threaded to the split-pane artifact viewer. */\n @Input() CurrentUser: UserInfo | null = null;\n\n /** The active environment id, threaded to the split-pane artifact viewer. */\n @Input() EnvironmentID = '';\n\n /**\n * Extra artifacts NOT tied to a specific activity card — the SESSION-REVIEW chain's\n * carryover artifacts. Rendered as a \"Session artifacts\" group above the activity list so\n * a reviewed session's outputs are reachable inside the Activity tab (the live path gets\n * artifacts from each card's `Artifacts`). Empty for a live session.\n */\n @Input() ExtraArtifacts: ParsedDelegationArtifact[] = [];\n\n /** Emitted with the delegated run's ID when an entry's dev \"Open run\" link is clicked. */\n @Output() OpenRunRequested = new EventEmitter<string>();\n\n /** Emitted when an artifact is opened in the split viewer (lets a host react if it wants). */\n @Output() OpenArtifactRequested = new EventEmitter<ParsedDelegationArtifact>();\n\n /** Whether the rail is collapsed to its slim toggle strip (standalone mode only). */\n public Collapsed = false;\n\n /** The artifact currently shown in the split-pane viewer, or null when the split is closed. */\n public SelectedArtifact: ParsedDelegationArtifact | null = null;\n\n /** The artifact (right) pane's width percentage — persisted per-user, clamped on load. */\n public SplitArtifactPercent = ACTIVITY_SPLIT_DEFAULT_PERCENT;\n\n private changedSub?: Subscription;\n private cdr = inject(ChangeDetectorRef);\n\n /** True while reduced-motion is preferred — disables the split's drag transition. */\n public readonly PrefersReducedMotion =\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n ngOnInit(): void {\n this.loadSplitPref();\n this.changedSub = this.State.Changed$.subscribe(() => this.cdr.markForCheck());\n }\n\n ngOnDestroy(): void {\n this.changedSub?.unsubscribe();\n }\n\n /** Toggle the rail between expanded and slim-collapsed (standalone presentation). */\n public Toggle(): void {\n this.Collapsed = !this.Collapsed;\n }\n\n /** track fn for the @for over cards. */\n public TrackCard(index: number, card: RealtimeDelegationCardVM): string {\n return card.CallID;\n }\n\n /** Friendly label of the card's latest step. */\n public StepLabel(card: RealtimeDelegationCardVM): string {\n return FriendlyStepLabel(card.LatestStep, card.LatestMessage);\n }\n\n /** Elapsed duration for finished work (\"8s\", \"1m 04s\"); null while running (kept cheap). */\n public Elapsed(card: RealtimeDelegationCardVM): string | null {\n if (card.FinishedAt == null) {\n return null;\n }\n return FormatElapsed(card.FinishedAt - card.StartedAt);\n }\n\n /** True when the dev \"Open run\" link should render for this card (gear on + run id known). */\n public ShowOpenRun(card: RealtimeDelegationCardVM): boolean {\n return this.DevMode && !!card.RunID;\n }\n\n /** Emits the open-run request for the card's delegated run. */\n public OpenRun(card: RealtimeDelegationCardVM): void {\n if (card.RunID) {\n this.OpenRunRequested.emit(card.RunID);\n }\n }\n\n /** The artifacts a done card carries (empty array when none — keeps the template simple). */\n public CardArtifacts(card: RealtimeDelegationCardVM): ParsedDelegationArtifact[] {\n return card.Done && card.Artifacts ? card.Artifacts : [];\n }\n\n /** track fn for the @for over a card's artifacts. */\n public TrackArtifact(index: number, artifact: ParsedDelegationArtifact): string {\n return artifact.ArtifactVersionID;\n }\n\n /** Whether the artifact `a` is the one currently open in the split viewer. */\n public IsArtifactOpen(artifact: ParsedDelegationArtifact): boolean {\n return this.SelectedArtifact?.ArtifactVersionID === artifact.ArtifactVersionID;\n }\n\n /**\n * Opens (or swaps to) `artifact` in the split-pane viewer: the activity list keeps the\n * left pane, the full artifact viewer fills the right. Re-opening the same artifact is a\n * no-op; opening a different one swaps the right pane. Also reachable from the surface-tabs\n * \"View →\" affordance.\n */\n public OpenArtifact(artifact: ParsedDelegationArtifact): void {\n if (this.SelectedArtifact?.ArtifactVersionID === artifact.ArtifactVersionID) {\n return;\n }\n this.SelectedArtifact = artifact;\n this.OpenArtifactRequested.emit(artifact);\n this.cdr.markForCheck();\n }\n\n /** Closes the split, returning to a full-width activity list. */\n public CloseArtifact(): void {\n if (this.SelectedArtifact) {\n this.SelectedArtifact = null;\n this.cdr.markForCheck();\n }\n }\n\n /**\n * `as-split` drag finished: adopt + persist the artifact (right) pane's width percentage\n * per-user (debounced, via {@link UserInfoEngine} — never localStorage). The split reports\n * the two area sizes (percent here); index 1 is the artifact pane. A wildcard `'*'` size\n * (non-numeric) is ignored — nothing to persist.\n */\n public OnSplitDragEnd(event: SplitGutterInteractionEvent): void {\n const right = event.sizes[1];\n if (typeof right !== 'number') {\n return;\n }\n this.SplitArtifactPercent = ClampActivitySplitPercent(right);\n this.persistSplitPref(this.SplitArtifactPercent);\n }\n\n /** One-line result preview for done items. */\n public Preview(card: RealtimeDelegationCardVM): string {\n const text = (card.Result || card.LatestMessage || '').replace(/\\s+/g, ' ').trim();\n const max = RealtimeActivityRailComponent.PreviewMaxChars;\n return text.length > max ? `${text.slice(0, max).trimEnd()}…` : text;\n }\n\n /** Reads the persisted split width once (tolerant; default tier applies when unset). */\n private loadSplitPref(): void {\n try {\n const percent = ParseActivitySplitPercent(UserInfoEngine.Instance.GetSetting(ACTIVITY_SPLIT_PREF_KEY));\n if (percent !== null) {\n this.SplitArtifactPercent = percent;\n }\n } catch {\n // UserInfoEngine not configured (plain-node tests / early bootstrap) — default applies.\n }\n }\n\n /** Persists the split width preference server-side (debounced, best-effort). */\n private persistSplitPref(percent: number): void {\n try {\n UserInfoEngine.Instance.SetSettingDebounced(ACTIVITY_SPLIT_PREF_KEY, SerializeActivitySplitPercent(percent));\n } catch {\n // engine unavailable — the width still applies for this session\n }\n }\n}\n","<aside class=\"rail\" [class.rail--collapsed]=\"Collapsed && !Embedded\" [class.rail--embedded]=\"Embedded\" aria-label=\"Session activity\">\n @if (!Embedded) {\n <div class=\"rail__head\">\n <button type=\"button\"\n class=\"rail__toggle\"\n (click)=\"Toggle()\"\n [title]=\"Collapsed ? 'Show session activity' : 'Hide session activity'\"\n [attr.aria-expanded]=\"!Collapsed\">\n <i class=\"fa-solid\" [ngClass]=\"Collapsed ? 'fa-chevron-left' : 'fa-chevron-right'\" aria-hidden=\"true\"></i>\n </button>\n @if (!Collapsed) {\n <span class=\"rail__title\">\n <i class=\"fa-solid fa-wave-square\" aria-hidden=\"true\"></i>\n Session activity\n </span>\n }\n </div>\n }\n\n @if (!Collapsed || Embedded) {\n <as-split class=\"rail__split\"\n direction=\"horizontal\"\n unit=\"percent\"\n [gutterSize]=\"SelectedArtifact ? 8 : 0\"\n [disabled]=\"!SelectedArtifact\"\n [useTransition]=\"!PrefersReducedMotion\"\n (dragEnd)=\"OnSplitDragEnd($event)\">\n\n <!-- LEFT: the activity list (full width until an artifact splits the pane) -->\n <as-split-area [size]=\"SelectedArtifact ? (100 - SplitArtifactPercent) : 100\">\n <div class=\"rail__list\">\n @if (ExtraArtifacts.length > 0) {\n <div class=\"rail__group\">\n <div class=\"rail__group-title\">\n <i class=\"fa-solid fa-folder-open\" aria-hidden=\"true\"></i> Session artifacts\n </div>\n @for (artifact of ExtraArtifacts; track TrackArtifact($index, artifact)) {\n <div class=\"act-card__artifact\"\n [class.act-card__artifact--open]=\"IsArtifactOpen(artifact)\">\n @if (CurrentUser) {\n <mj-artifact-message-card\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser\"\n (actionPerformed)=\"OpenArtifact(artifact)\">\n </mj-artifact-message-card>\n } @else {\n <button type=\"button\" class=\"act-card__artifact-link\" (click)=\"OpenArtifact(artifact)\"\n [title]=\"'View ' + artifact.Name\">\n <i class=\"fa-solid fa-file-lines\" aria-hidden=\"true\"></i>\n <span>{{ artifact.Name }}</span>\n <i class=\"fa-solid fa-up-right-and-down-left-from-center\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n </div>\n }\n @for (card of State.Cards; track TrackCard($index, card)) {\n <div class=\"act-card\"\n [class.act-card--running]=\"!card.Done\"\n [class.act-card--done]=\"card.Done && card.Success\"\n [class.act-card--failed]=\"card.Done && !card.Success\">\n <div class=\"act-card__top\">\n <span class=\"act-card__name\">{{ card.AgentName }}</span>\n @if (!card.Done) {\n <span class=\"act-status act-status--running\">\n <i class=\"fa-solid fa-spinner spin\" aria-hidden=\"true\"></i> Running\n </span>\n } @else if (card.Success) {\n <span class=\"act-status act-status--done\">\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i> Done\n </span>\n } @else {\n <span class=\"act-status act-status--failed\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i> Failed\n </span>\n }\n </div>\n <div class=\"act-card__step\">{{ StepLabel(card) }}</div>\n @if (card.Done) {\n @if (Elapsed(card); as elapsed) {\n <div class=\"act-card__meta\">\n <i class=\"fa-regular fa-clock\" aria-hidden=\"true\"></i> {{ elapsed }}\n </div>\n }\n @if (Preview(card)) {\n <div class=\"act-card__preview\">{{ Preview(card) }}</div>\n }\n }\n @for (artifact of CardArtifacts(card); track TrackArtifact($index, artifact)) {\n <div class=\"act-card__artifact\"\n [class.act-card__artifact--open]=\"IsArtifactOpen(artifact)\">\n @if (CurrentUser) {\n <!-- Inline preview (same component the conversation message cards use);\n clicking expands it into the split-pane viewer on the right. -->\n <mj-artifact-message-card\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser\"\n (actionPerformed)=\"OpenArtifact(artifact)\">\n </mj-artifact-message-card>\n } @else {\n <button type=\"button\" class=\"act-card__artifact-link\" (click)=\"OpenArtifact(artifact)\"\n [title]=\"'View ' + artifact.Name\">\n <i class=\"fa-solid fa-file-lines\" aria-hidden=\"true\"></i>\n <span>{{ artifact.Name }}</span>\n <i class=\"fa-solid fa-up-right-and-down-left-from-center\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n @if (ShowOpenRun(card)) {\n <button type=\"button\" class=\"act-card__dev-link\" (click)=\"OpenRun(card)\"\n title=\"Open the delegated agent run record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\" aria-hidden=\"true\"></i>\n <span>Open run</span>\n </button>\n }\n </div>\n } @empty {\n <div class=\"rail__empty\">Tool and agent calls will appear here as they run.</div>\n }\n </div>\n </as-split-area>\n\n <!-- RIGHT: the full artifact viewer (only present while an artifact is open) -->\n @if (SelectedArtifact; as artifact) {\n <as-split-area [size]=\"SplitArtifactPercent\" [minSize]=\"25\">\n <div class=\"rail__viewer\">\n <div class=\"rail__viewer-head\">\n <span class=\"rail__viewer-title\" [title]=\"artifact.Name\">\n <i class=\"fa-solid fa-file-lines\" aria-hidden=\"true\"></i>\n {{ artifact.Name }}\n </span>\n <button type=\"button\" class=\"rail__viewer-close\" (click)=\"CloseArtifact()\"\n title=\"Close artifact\" aria-label=\"Close artifact\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"rail__viewer-body\">\n <mj-artifact-viewer-panel\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser!\"\n [environmentId]=\"EnvironmentID\"\n [showHeader]=\"false\"\n [showTabs]=\"false\"\n [showSaveToCollection]=\"false\"\n [showCloseButton]=\"false\"\n [showMaximizeButton]=\"false\">\n </mj-artifact-viewer-panel>\n </div>\n </div>\n </as-split-area>\n }\n </as-split>\n }\n</aside>\n"]}
1
+ {"version":3,"file":"realtime-activity-rail.component.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-activity-rail.component.ts","../../../../src/lib/components/realtime/realtime-activity-rail.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, OnInit, OnDestroy, ChangeDetectorRef, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Subscription } from 'rxjs';\nimport { AngularSplitModule, SplitGutterInteractionEvent } from 'angular-split';\nimport { UserInfo } from '@memberjunction/core';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { ArtifactsModule } from '@memberjunction/ng-artifacts';\nimport {\n RealtimeSessionState, RealtimeDelegationCardVM, FriendlyStepLabel, FormatElapsed\n} from './realtime-session-state';\nimport { ParsedDelegationArtifact } from '../../services/delegation-result-parser';\nimport {\n ACTIVITY_SPLIT_DEFAULT_PERCENT, ACTIVITY_SPLIT_PREF_KEY,\n ClampActivitySplitPercent, ParseActivitySplitPercent, SerializeActivitySplitPercent\n} from './realtime-surface-tab-style';\n\n/**\n * The \"Session activity\" surface (the Activity tab's content): a compact, newest-first list\n * of EVERY tool/agent call of the session — so parallel/multiple delegations live here and\n * the conversation thread stays clean.\n *\n * Each entry shows agent name, status, the latest friendly step label, elapsed time, and a\n * one-line result preview. When a run produced ARTIFACTS, each artifact renders as an inline\n * PREVIEW card (reusing `mj-artifact-message-card`, the same component the conversation\n * messages use). Clicking a preview SPLITS this pane horizontally — activity list on the\n * LEFT, the full artifact viewer on the RIGHT (`as-split`); clicking another swaps the right\n * pane; a close control collapses back to full-width activity. The split width is persisted\n * per-user via {@link UserInfoEngine} (NOT localStorage). This replaces the old \"one surface\n * tab per artifact\" model.\n *\n * Collapsible via the chevron in its header (standalone presentation only). Presentational\n * over the same {@link RealtimeSessionState} the thread renders.\n */\n@Component({\n standalone: true,\n selector: 'mj-realtime-activity-rail',\n imports: [CommonModule, AngularSplitModule, ArtifactsModule],\n templateUrl: './realtime-activity-rail.component.html',\n styleUrl: './realtime-activity-rail.component.css'\n})\nexport class RealtimeActivityRailComponent implements OnInit, OnDestroy {\n /** Maximum characters of the result preview in a done card. */\n private static readonly PreviewMaxChars = 90;\n\n /** Shared live-session state, owned by the overlay shell. */\n @Input({ required: true }) State!: RealtimeSessionState;\n\n /** Whether developer affordances (\"Open run\" links) are revealed (gear-gated). */\n @Input() DevMode = false;\n\n /**\n * EMBEDDED mode: the rail renders as the content of the surface panel's Activity tab —\n * its own header/collapse chrome is hidden (the panel's tab strip owns collapse) and it\n * stretches to the pane's full width. Default `false` keeps the original standalone\n * right-rail presentation for any other consumer.\n */\n @Input() Embedded = false;\n\n /** The signed-in user, threaded to the split-pane artifact viewer. */\n @Input() CurrentUser: UserInfo | null = null;\n\n /** The active environment id, threaded to the split-pane artifact viewer. */\n @Input() EnvironmentID = '';\n\n /**\n * Extra artifacts NOT tied to a specific activity card — the SESSION-REVIEW chain's\n * carryover artifacts. Rendered as a \"Session artifacts\" group above the activity list so\n * a reviewed session's outputs are reachable inside the Activity tab (the live path gets\n * artifacts from each card's `Artifacts`). Empty for a live session.\n */\n @Input() ExtraArtifacts: ParsedDelegationArtifact[] = [];\n\n /** Emitted with the delegated run's ID when an entry's dev \"Open run\" link is clicked. */\n @Output() OpenRunRequested = new EventEmitter<string>();\n\n /** Emitted when an artifact is opened in the split viewer (lets a host react if it wants). */\n @Output() OpenArtifactRequested = new EventEmitter<ParsedDelegationArtifact>();\n\n /** Whether the rail is collapsed to its slim toggle strip (standalone mode only). */\n public Collapsed = false;\n\n /** The artifact currently shown in the split-pane viewer, or null when the split is closed. */\n public SelectedArtifact: ParsedDelegationArtifact | null = null;\n\n /** The artifact (right) pane's width percentage — persisted per-user, clamped on load. */\n public SplitArtifactPercent = ACTIVITY_SPLIT_DEFAULT_PERCENT;\n\n private changedSub?: Subscription;\n private cdr = inject(ChangeDetectorRef);\n\n /** True while reduced-motion is preferred — disables the split's drag transition. */\n public readonly PrefersReducedMotion =\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n ngOnInit(): void {\n this.loadSplitPref();\n this.changedSub = this.State.Changed$.subscribe(() => this.cdr.markForCheck());\n }\n\n ngOnDestroy(): void {\n this.changedSub?.unsubscribe();\n }\n\n /** Toggle the rail between expanded and slim-collapsed (standalone presentation). */\n public Toggle(): void {\n this.Collapsed = !this.Collapsed;\n }\n\n /** track fn for the @for over cards. */\n public TrackCard(index: number, card: RealtimeDelegationCardVM): string {\n return card.CallID;\n }\n\n /** Friendly label of the card's latest step. */\n public StepLabel(card: RealtimeDelegationCardVM): string {\n return FriendlyStepLabel(card.LatestStep, card.LatestMessage);\n }\n\n /** Elapsed duration for finished work (\"8s\", \"1m 04s\"); null while running (kept cheap). */\n public Elapsed(card: RealtimeDelegationCardVM): string | null {\n if (card.FinishedAt == null) {\n return null;\n }\n return FormatElapsed(card.FinishedAt - card.StartedAt);\n }\n\n /** True when the dev \"Open run\" link should render for this card (gear on + run id known). Suppressed for direct actions. */\n public ShowOpenRun(card: RealtimeDelegationCardVM): boolean {\n return this.DevMode && card.Kind !== 'action' && !!card.RunID;\n }\n\n /** Emits the open-run request for the card's delegated run. */\n public OpenRun(card: RealtimeDelegationCardVM): void {\n if (card.RunID) {\n this.OpenRunRequested.emit(card.RunID);\n }\n }\n\n /** The artifacts a done card carries (empty array when none — keeps the template simple). */\n public CardArtifacts(card: RealtimeDelegationCardVM): ParsedDelegationArtifact[] {\n return card.Done && card.Artifacts ? card.Artifacts : [];\n }\n\n /** track fn for the @for over a card's artifacts. */\n public TrackArtifact(index: number, artifact: ParsedDelegationArtifact): string {\n return artifact.ArtifactVersionID;\n }\n\n /** Whether the artifact `a` is the one currently open in the split viewer. */\n public IsArtifactOpen(artifact: ParsedDelegationArtifact): boolean {\n return this.SelectedArtifact?.ArtifactVersionID === artifact.ArtifactVersionID;\n }\n\n /**\n * Opens (or swaps to) `artifact` in the split-pane viewer: the activity list keeps the\n * left pane, the full artifact viewer fills the right. Re-opening the same artifact is a\n * no-op; opening a different one swaps the right pane. Also reachable from the surface-tabs\n * \"View →\" affordance.\n */\n public OpenArtifact(artifact: ParsedDelegationArtifact): void {\n if (this.SelectedArtifact?.ArtifactVersionID === artifact.ArtifactVersionID) {\n return;\n }\n this.SelectedArtifact = artifact;\n this.OpenArtifactRequested.emit(artifact);\n this.cdr.markForCheck();\n }\n\n /** Closes the split, returning to a full-width activity list. */\n public CloseArtifact(): void {\n if (this.SelectedArtifact) {\n this.SelectedArtifact = null;\n this.cdr.markForCheck();\n }\n }\n\n /**\n * `as-split` drag finished: adopt + persist the artifact (right) pane's width percentage\n * per-user (debounced, via {@link UserInfoEngine} — never localStorage). The split reports\n * the two area sizes (percent here); index 1 is the artifact pane. A wildcard `'*'` size\n * (non-numeric) is ignored — nothing to persist.\n */\n public OnSplitDragEnd(event: SplitGutterInteractionEvent): void {\n const right = event.sizes[1];\n if (typeof right !== 'number') {\n return;\n }\n this.SplitArtifactPercent = ClampActivitySplitPercent(right);\n this.persistSplitPref(this.SplitArtifactPercent);\n }\n\n /** One-line result preview for done items. */\n public Preview(card: RealtimeDelegationCardVM): string {\n const text = (card.Result || card.LatestMessage || '').replace(/\\s+/g, ' ').trim();\n const max = RealtimeActivityRailComponent.PreviewMaxChars;\n return text.length > max ? `${text.slice(0, max).trimEnd()}…` : text;\n }\n\n /** Reads the persisted split width once (tolerant; default tier applies when unset). */\n private loadSplitPref(): void {\n try {\n const percent = ParseActivitySplitPercent(UserInfoEngine.Instance.GetSetting(ACTIVITY_SPLIT_PREF_KEY));\n if (percent !== null) {\n this.SplitArtifactPercent = percent;\n }\n } catch {\n // UserInfoEngine not configured (plain-node tests / early bootstrap) — default applies.\n }\n }\n\n /** Persists the split width preference server-side (debounced, best-effort). */\n private persistSplitPref(percent: number): void {\n try {\n UserInfoEngine.Instance.SetSettingDebounced(ACTIVITY_SPLIT_PREF_KEY, SerializeActivitySplitPercent(percent));\n } catch {\n // engine unavailable — the width still applies for this session\n }\n }\n}\n","<aside class=\"rail\" [class.rail--collapsed]=\"Collapsed && !Embedded\" [class.rail--embedded]=\"Embedded\" aria-label=\"Session activity\">\n @if (!Embedded) {\n <div class=\"rail__head\">\n <button type=\"button\"\n class=\"rail__toggle\"\n (click)=\"Toggle()\"\n [title]=\"Collapsed ? 'Show session activity' : 'Hide session activity'\"\n [attr.aria-expanded]=\"!Collapsed\">\n <i class=\"fa-solid\" [ngClass]=\"Collapsed ? 'fa-chevron-left' : 'fa-chevron-right'\" aria-hidden=\"true\"></i>\n </button>\n @if (!Collapsed) {\n <span class=\"rail__title\">\n <i class=\"fa-solid fa-wave-square\" aria-hidden=\"true\"></i>\n Session activity\n </span>\n }\n </div>\n }\n\n @if (!Collapsed || Embedded) {\n <as-split class=\"rail__split\"\n direction=\"horizontal\"\n unit=\"percent\"\n [gutterSize]=\"SelectedArtifact ? 8 : 0\"\n [disabled]=\"!SelectedArtifact\"\n [useTransition]=\"!PrefersReducedMotion\"\n (dragEnd)=\"OnSplitDragEnd($event)\">\n\n <!-- LEFT: the activity list (full width until an artifact splits the pane) -->\n <as-split-area [size]=\"SelectedArtifact ? (100 - SplitArtifactPercent) : 100\">\n <div class=\"rail__list\">\n @if (ExtraArtifacts.length > 0) {\n <div class=\"rail__group\">\n <div class=\"rail__group-title\">\n <i class=\"fa-solid fa-folder-open\" aria-hidden=\"true\"></i> Session artifacts\n </div>\n @for (artifact of ExtraArtifacts; track TrackArtifact($index, artifact)) {\n <div class=\"act-card__artifact\"\n [class.act-card__artifact--open]=\"IsArtifactOpen(artifact)\">\n @if (CurrentUser) {\n <mj-artifact-message-card\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser\"\n (actionPerformed)=\"OpenArtifact(artifact)\">\n </mj-artifact-message-card>\n } @else {\n <button type=\"button\" class=\"act-card__artifact-link\" (click)=\"OpenArtifact(artifact)\"\n [title]=\"'View ' + artifact.Name\">\n <i class=\"fa-solid fa-file-lines\" aria-hidden=\"true\"></i>\n <span>{{ artifact.Name }}</span>\n <i class=\"fa-solid fa-up-right-and-down-left-from-center\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n </div>\n }\n @for (card of State.Cards; track TrackCard($index, card)) {\n <div class=\"act-card\"\n [class.act-card--running]=\"!card.Done\"\n [class.act-card--done]=\"card.Done && card.Success\"\n [class.act-card--failed]=\"card.Done && !card.Success\">\n <div class=\"act-card__top\">\n <span class=\"act-card__name\">{{ card.AgentName }}</span>\n @if (!card.Done) {\n <span class=\"act-status act-status--running\">\n <i class=\"fa-solid fa-spinner spin\" aria-hidden=\"true\"></i> Running\n </span>\n } @else if (card.Success) {\n <span class=\"act-status act-status--done\">\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i> Done\n </span>\n } @else {\n <span class=\"act-status act-status--failed\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i> Failed\n </span>\n }\n </div>\n <div class=\"act-card__step\">{{ StepLabel(card) }}</div>\n @if (card.Done) {\n @if (Elapsed(card); as elapsed) {\n <div class=\"act-card__meta\">\n <i class=\"fa-regular fa-clock\" aria-hidden=\"true\"></i> {{ elapsed }}\n </div>\n }\n @if (Preview(card)) {\n <div class=\"act-card__preview\">{{ Preview(card) }}</div>\n }\n }\n @for (artifact of CardArtifacts(card); track TrackArtifact($index, artifact)) {\n <div class=\"act-card__artifact\"\n [class.act-card__artifact--open]=\"IsArtifactOpen(artifact)\">\n @if (CurrentUser) {\n <!-- Inline preview (same component the conversation message cards use);\n clicking expands it into the split-pane viewer on the right. -->\n <mj-artifact-message-card\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser\"\n (actionPerformed)=\"OpenArtifact(artifact)\">\n </mj-artifact-message-card>\n } @else {\n <button type=\"button\" class=\"act-card__artifact-link\" (click)=\"OpenArtifact(artifact)\"\n [title]=\"'View ' + artifact.Name\">\n <i class=\"fa-solid fa-file-lines\" aria-hidden=\"true\"></i>\n <span>{{ artifact.Name }}</span>\n <i class=\"fa-solid fa-up-right-and-down-left-from-center\" aria-hidden=\"true\"></i>\n </button>\n }\n </div>\n }\n @if (ShowOpenRun(card)) {\n <button type=\"button\" class=\"act-card__dev-link\" (click)=\"OpenRun(card)\"\n title=\"Open the delegated agent run record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\" aria-hidden=\"true\"></i>\n <span>Open run</span>\n </button>\n }\n </div>\n } @empty {\n <div class=\"rail__empty\">Tool and agent calls will appear here as they run.</div>\n }\n </div>\n </as-split-area>\n\n <!-- RIGHT: the full artifact viewer (only present while an artifact is open) -->\n @if (SelectedArtifact; as artifact) {\n <as-split-area [size]=\"SplitArtifactPercent\" [minSize]=\"25\">\n <div class=\"rail__viewer\">\n <div class=\"rail__viewer-head\">\n <span class=\"rail__viewer-title\" [title]=\"artifact.Name\">\n <i class=\"fa-solid fa-file-lines\" aria-hidden=\"true\"></i>\n {{ artifact.Name }}\n </span>\n <button type=\"button\" class=\"rail__viewer-close\" (click)=\"CloseArtifact()\"\n title=\"Close artifact\" aria-label=\"Close artifact\">\n <i class=\"fa-solid fa-xmark\" aria-hidden=\"true\"></i>\n </button>\n </div>\n <div class=\"rail__viewer-body\">\n <mj-artifact-viewer-panel\n [artifactId]=\"artifact.ArtifactID\"\n [currentUser]=\"CurrentUser!\"\n [environmentId]=\"EnvironmentID\"\n [showHeader]=\"false\"\n [showTabs]=\"false\"\n [showSaveToCollection]=\"false\"\n [showCloseButton]=\"false\"\n [showMaximizeButton]=\"false\">\n </mj-artifact-viewer-panel>\n </div>\n </div>\n </as-split-area>\n }\n </as-split>\n }\n</aside>\n"]}
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter, OnInit, AfterViewInit } from '@angular/core';
2
2
  import type { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';
3
3
  import { UUIDsEqual } from '@memberjunction/global';
4
+ import { UserInfo } from '@memberjunction/core';
4
5
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
5
6
  import { RealtimeModelOption, RealtimePairedAgentRow } from '../../services/realtime-pairing';
6
7
  import { RealtimeModelVoices, RealtimeVoiceOption } from '@memberjunction/graphql-dataprovider';
@@ -58,6 +59,13 @@ export interface RealtimeAgentPick {
58
59
  * `.message-input-wrapper` is `position: relative`.
59
60
  */
60
61
  export declare class RealtimeAgentPickerComponent extends BaseAngularComponent implements OnInit, AfterViewInit {
62
+ /**
63
+ * The authenticated user context (forwarded from the host composer / chat area).
64
+ * Falls back to `this.ProviderToUse?.CurrentUser` when not provided.
65
+ */
66
+ CurrentUser: UserInfo | null;
67
+ /** Returns the explicit CurrentUser input or falls back to ProviderToUse.CurrentUser. */
68
+ get EffectiveUser(): UserInfo | null;
61
69
  /** Agents the user can call — same cached set the @mention / routing logic uses. */
62
70
  Agents: MJAIAgentEntityExtended[];
63
71
  /** The agent the default resolution would pick — preselected and listed first. */
@@ -117,6 +125,13 @@ export declare class RealtimeAgentPickerComponent extends BaseAngularComponent i
117
125
  /** True while the agent list is constrained to the selected co-agent's paired targets. */
118
126
  get IsConstrainedByPairings(): boolean;
119
127
  ngOnInit(): void;
128
+ /**
129
+ * Checks whether the user holds the advanced-session-controls authorization and, if so,
130
+ * loads the available realtime voice models and voices. If the initially evaluated user
131
+ * lacks the authorization, attempts a one-time RefreshCurrentUser() on the provider in
132
+ * case role assignments were granted after client metadata was cached.
133
+ */
134
+ private checkAuthorizationAndLoadModels;
120
135
  /** Reads the per-user recording-consent preference (defensive: any failure → off). */
121
136
  private readPersistedRecordingConsent;
122
137
  /** Records + persists (cross-device, debounced) the user's recording-consent choice. */
@@ -186,6 +201,6 @@ export declare class RealtimeAgentPickerComponent extends BaseAngularComponent i
186
201
  private isDefault;
187
202
  private findAgent;
188
203
  static ɵfac: i0.ɵɵFactoryDeclaration<RealtimeAgentPickerComponent, never>;
189
- static ɵcmp: i0.ɵɵComponentDeclaration<RealtimeAgentPickerComponent, "mj-realtime-agent-picker", never, { "Agents": { "alias": "Agents"; "required": false; }; "DefaultAgentId": { "alias": "DefaultAgentId"; "required": false; }; "CoAgents": { "alias": "CoAgents"; "required": false; }; "DefaultCoAgentId": { "alias": "DefaultCoAgentId"; "required": false; }; }, { "AgentPicked": "AgentPicked"; "Cancelled": "Cancelled"; }, never, never, true, never>;
204
+ static ɵcmp: i0.ɵɵComponentDeclaration<RealtimeAgentPickerComponent, "mj-realtime-agent-picker", never, { "CurrentUser": { "alias": "CurrentUser"; "required": false; }; "Agents": { "alias": "Agents"; "required": false; }; "DefaultAgentId": { "alias": "DefaultAgentId"; "required": false; }; "CoAgents": { "alias": "CoAgents"; "required": false; }; "DefaultCoAgentId": { "alias": "DefaultCoAgentId"; "required": false; }; }, { "AgentPicked": "AgentPicked"; "Cancelled": "Cancelled"; }, never, never, true, never>;
190
205
  }
191
206
  //# sourceMappingURL=realtime-agent-picker.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-agent-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-agent-picker.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"realtime-agent-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-agent-picker.component.ts"],"names":[],"mappings":"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"}
@@ -184,6 +184,15 @@ const RECORDING_CONSENT_KEY = 'mj.realtimeVoice.recordingConsent.v1';
184
184
  * `.message-input-wrapper` is `position: relative`.
185
185
  */
186
186
  export class RealtimeAgentPickerComponent extends BaseAngularComponent {
187
+ /**
188
+ * The authenticated user context (forwarded from the host composer / chat area).
189
+ * Falls back to `this.ProviderToUse?.CurrentUser` when not provided.
190
+ */
191
+ CurrentUser = null;
192
+ /** Returns the explicit CurrentUser input or falls back to ProviderToUse.CurrentUser. */
193
+ get EffectiveUser() {
194
+ return this.CurrentUser ?? this.ProviderToUse?.CurrentUser ?? null;
195
+ }
187
196
  /** Agents the user can call — same cached set the @mention / routing logic uses. */
188
197
  Agents = [];
189
198
  /** The agent the default resolution would pick — preselected and listed first. */
@@ -254,10 +263,7 @@ export class RealtimeAgentPickerComponent extends BaseAngularComponent {
254
263
  ? this.DefaultAgentId
255
264
  : this.FilteredAgents[0]?.ID ?? null;
256
265
  this.SelectedAgentId = initial;
257
- this.CanOverrideSessionConfig = UserHoldsAuthorization(this.ProviderToUse?.CurrentUser, REALTIME_ADVANCED_SESSION_CONTROLS, this.ProviderToUse);
258
- if (this.CanOverrideSessionConfig) {
259
- void this.loadVoiceModels();
260
- }
266
+ void this.checkAuthorizationAndLoadModels();
261
267
  // Preselect the persisted co-agent preference when it's still a valid candidate.
262
268
  if (this.DefaultCoAgentId && this.CoAgents.some(a => UUIDsEqual(a.ID, this.DefaultCoAgentId))) {
263
269
  this.SelectedCoAgentId = this.DefaultCoAgentId;
@@ -265,6 +271,30 @@ export class RealtimeAgentPickerComponent extends BaseAngularComponent {
265
271
  }
266
272
  this.RecordingConsent = this.readPersistedRecordingConsent();
267
273
  }
274
+ /**
275
+ * Checks whether the user holds the advanced-session-controls authorization and, if so,
276
+ * loads the available realtime voice models and voices. If the initially evaluated user
277
+ * lacks the authorization, attempts a one-time RefreshCurrentUser() on the provider in
278
+ * case role assignments were granted after client metadata was cached.
279
+ */
280
+ async checkAuthorizationAndLoadModels() {
281
+ this.CanOverrideSessionConfig = UserHoldsAuthorization(this.EffectiveUser, REALTIME_ADVANCED_SESSION_CONTROLS, this.ProviderToUse);
282
+ if (!this.CanOverrideSessionConfig && this.ProviderToUse?.RefreshCurrentUser) {
283
+ try {
284
+ const refreshed = await this.ProviderToUse.RefreshCurrentUser();
285
+ if (refreshed) {
286
+ this.CanOverrideSessionConfig = UserHoldsAuthorization(refreshed, REALTIME_ADVANCED_SESSION_CONTROLS, this.ProviderToUse);
287
+ }
288
+ }
289
+ catch (e) {
290
+ console.warn('[RealtimeAgentPicker] Failed to refresh CurrentUser for authorization check:', e);
291
+ }
292
+ }
293
+ if (this.CanOverrideSessionConfig) {
294
+ await this.loadVoiceModels();
295
+ }
296
+ this.cdr.markForCheck();
297
+ }
268
298
  /** Reads the per-user recording-consent preference (defensive: any failure → off). */
269
299
  readPersistedRecordingConsent() {
270
300
  try {
@@ -492,7 +522,7 @@ export class RealtimeAgentPickerComponent extends BaseAngularComponent {
492
522
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.searchInput = _t.first);
493
523
  } }, hostBindings: function RealtimeAgentPickerComponent_HostBindings(rf, ctx) { if (rf & 1) {
494
524
  i0.ɵɵlistener("click", function RealtimeAgentPickerComponent_click_HostBindingHandler($event) { return ctx.OnDocumentClick($event); }, i0.ɵɵresolveDocument)("keydown.escape", function RealtimeAgentPickerComponent_keydown_escape_HostBindingHandler() { return ctx.OnEscape(); }, i0.ɵɵresolveDocument);
495
- } }, inputs: { Agents: "Agents", DefaultAgentId: "DefaultAgentId", CoAgents: "CoAgents", DefaultCoAgentId: "DefaultCoAgentId" }, outputs: { AgentPicked: "AgentPicked", Cancelled: "Cancelled" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 30, vars: 6, consts: [["searchInput", ""], ["coAgentSelect", ""], ["modelSelect", ""], ["voiceSelect", ""], [1, "mj-voice-picker", 3, "click"], [1, "mj-voice-picker__header"], [1, "fa-solid", "fa-phone"], [1, "mj-voice-picker__select-row"], [1, "mj-voice-picker__search"], [1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Search agents", 3, "input", "keydown.enter", "keydown.arrowdown", "keydown.arrowup", "value"], [1, "mj-voice-picker__list"], [1, "mj-voice-picker__empty"], [1, "mj-voice-picker__record-row"], [1, "mj-voice-picker__record-label"], [1, "fa-solid", "fa-microphone"], [1, "mj-voice-picker__record-copy"], [1, "mj-voice-picker__switch"], ["type", "checkbox", "aria-label", "Record this call (audio stored securely)", 3, "change", "checked"], [1, "mj-voice-picker__switch-track"], [1, "mj-voice-picker__footer"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "outline", "size", "sm", 3, "click"], ["for", "mjRealtimeCoAgentSelect", 1, "mj-voice-picker__select-label"], [1, "fa-solid", "fa-headset"], ["id", "mjRealtimeCoAgentSelect", 1, "mj-voice-picker__select", 3, "change", "value"], ["value", ""], [3, "value"], ["type", "button", 1, "mj-voice-picker__item", 3, "mj-voice-picker__item--selected", "title"], ["type", "button", 1, "mj-voice-picker__item", 3, "click", "dblclick", "title"], [1, "mj-voice-picker__item-name"], [1, "mj-voice-picker__default-badge"], [1, "mj-voice-picker__select-row", "mj-voice-picker__select-row--model"], ["for", "mjRealtimeModelSelect", 1, "mj-voice-picker__select-label"], [1, "fa-solid", "fa-microchip"], ["id", "mjRealtimeModelSelect", 1, "mj-voice-picker__select", 3, "change", "value"], ["for", "mjRealtimeVoiceSelect", 1, "mj-voice-picker__select-label"], [1, "fa-solid", "fa-waveform-lines"], ["id", "mjRealtimeVoiceSelect", 1, "mj-voice-picker__select", 3, "change", "value"]], template: function RealtimeAgentPickerComponent_Template(rf, ctx) { if (rf & 1) {
525
+ } }, inputs: { CurrentUser: "CurrentUser", Agents: "Agents", DefaultAgentId: "DefaultAgentId", CoAgents: "CoAgents", DefaultCoAgentId: "DefaultCoAgentId" }, outputs: { AgentPicked: "AgentPicked", Cancelled: "Cancelled" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 30, vars: 6, consts: [["searchInput", ""], ["coAgentSelect", ""], ["modelSelect", ""], ["voiceSelect", ""], [1, "mj-voice-picker", 3, "click"], [1, "mj-voice-picker__header"], [1, "fa-solid", "fa-phone"], [1, "mj-voice-picker__select-row"], [1, "mj-voice-picker__search"], [1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Search agents", 3, "input", "keydown.enter", "keydown.arrowdown", "keydown.arrowup", "value"], [1, "mj-voice-picker__list"], [1, "mj-voice-picker__empty"], [1, "mj-voice-picker__record-row"], [1, "mj-voice-picker__record-label"], [1, "fa-solid", "fa-microphone"], [1, "mj-voice-picker__record-copy"], [1, "mj-voice-picker__switch"], ["type", "checkbox", "aria-label", "Record this call (audio stored securely)", 3, "change", "checked"], [1, "mj-voice-picker__switch-track"], [1, "mj-voice-picker__footer"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], ["mjButton", "", "variant", "outline", "size", "sm", 3, "click"], ["for", "mjRealtimeCoAgentSelect", 1, "mj-voice-picker__select-label"], [1, "fa-solid", "fa-headset"], ["id", "mjRealtimeCoAgentSelect", 1, "mj-voice-picker__select", 3, "change", "value"], ["value", ""], [3, "value"], ["type", "button", 1, "mj-voice-picker__item", 3, "mj-voice-picker__item--selected", "title"], ["type", "button", 1, "mj-voice-picker__item", 3, "click", "dblclick", "title"], [1, "mj-voice-picker__item-name"], [1, "mj-voice-picker__default-badge"], [1, "mj-voice-picker__select-row", "mj-voice-picker__select-row--model"], ["for", "mjRealtimeModelSelect", 1, "mj-voice-picker__select-label"], [1, "fa-solid", "fa-microchip"], ["id", "mjRealtimeModelSelect", 1, "mj-voice-picker__select", 3, "change", "value"], ["for", "mjRealtimeVoiceSelect", 1, "mj-voice-picker__select-label"], [1, "fa-solid", "fa-waveform-lines"], ["id", "mjRealtimeVoiceSelect", 1, "mj-voice-picker__select", 3, "change", "value"]], template: function RealtimeAgentPickerComponent_Template(rf, ctx) { if (rf & 1) {
496
526
  const _r1 = i0.ɵɵgetCurrentView();
497
527
  i0.ɵɵelementStart(0, "div", 4);
498
528
  i0.ɵɵlistener("click", function RealtimeAgentPickerComponent_Template_div_click_0_listener($event) { return $event.stopPropagation(); });
@@ -670,7 +700,9 @@ export class RealtimeAgentPickerComponent extends BaseAngularComponent {
670
700
  </div>
671
701
  </div>
672
702
  `, styles: ["\n :host {\n /* Rendered inside a body-level CDK connected overlay (see message-input) \u2014 CDK\n owns the positioning + z-index, so the host is just a plain block. The height\n cap keeps the popover from growing past the viewport; the agent list inside\n scrolls when content exceeds it. CDK's push keeps it on-screen, and because\n it lives at the body level it can never be clipped by the chat overlay border. */\n display: block;\n max-height: min(440px, calc(100vh - 24px));\n }\n .mj-voice-picker {\n width: 300px;\n max-width: calc(100vw - 24px);\n display: flex; flex-direction: column;\n /* Match the host cap so the inner flex column knows its bound and lets the agent\n list ('mj-voice-picker__list') take the remaining space + scroll. */\n max-height: min(440px, calc(100vh - 120px));\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);\n overflow: hidden;\n }\n .mj-voice-picker__header {\n display: flex; align-items: center; gap: 8px;\n padding: 10px 12px 8px;\n font-size: 13px; font-weight: 600;\n color: var(--mj-text-primary);\n }\n .mj-voice-picker__header i { color: var(--mj-brand-primary); }\n .mj-voice-picker__search {\n display: flex; align-items: center; gap: 8px;\n margin: 0 12px 8px;\n padding: 0 10px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n }\n .mj-voice-picker__search i { font-size: 12px; color: var(--mj-text-muted); }\n .mj-voice-picker__search input {\n flex: 1; min-width: 0;\n padding: 7px 0;\n background: transparent; border: none; outline: none;\n font-size: 13px; color: var(--mj-text-primary);\n }\n .mj-voice-picker__search input::placeholder { color: var(--mj-text-disabled); }\n .mj-voice-picker__search:focus-within { border-color: var(--mj-border-focus); }\n .mj-voice-picker__list {\n /* The agent list is the flexible region: it scrolls internally and gives way\n first when the whole popup is capped to the overlay height. min-height:0 lets\n it actually shrink inside the flex column; the 220px max keeps it tidy when\n there's plenty of room. */\n flex: 1 1 auto;\n min-height: 0;\n max-height: 220px; overflow-y: auto;\n border-top: 1px solid var(--mj-border-subtle);\n border-bottom: 1px solid var(--mj-border-subtle);\n padding: 4px 0;\n }\n /* Keep the chrome around the list from collapsing when the popup is height-capped. */\n .mj-voice-picker__header,\n .mj-voice-picker__search,\n .mj-voice-picker__select-row,\n .mj-voice-picker__record-row,\n .mj-voice-picker__footer { flex-shrink: 0; }\n .mj-voice-picker__item {\n display: flex; align-items: center; gap: 8px; width: 100%;\n padding: 7px 12px;\n background: transparent; border: none; cursor: pointer;\n color: var(--mj-text-primary); font-size: 13px; text-align: left;\n }\n .mj-voice-picker__item i { width: 16px; text-align: center; color: var(--mj-text-secondary); }\n .mj-voice-picker__item:hover { background: var(--mj-bg-surface-hover); }\n .mj-voice-picker__item--selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n .mj-voice-picker__item--selected i { color: var(--mj-brand-primary); }\n .mj-voice-picker__item-name {\n flex: 1; min-width: 0;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n .mj-voice-picker__default-badge {\n flex-shrink: 0;\n padding: 1px 6px;\n border-radius: 999px;\n font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n }\n .mj-voice-picker__empty {\n padding: 12px; font-size: 12px; color: var(--mj-text-muted); text-align: center;\n }\n .mj-voice-picker__select-row {\n display: flex; align-items: center; gap: 8px;\n padding: 0 12px 8px;\n }\n .mj-voice-picker__select-row--model {\n padding: 8px 12px 0;\n }\n .mj-voice-picker__select-label {\n display: flex; align-items: center; gap: 6px;\n flex-shrink: 0;\n font-size: 12px; color: var(--mj-text-secondary);\n }\n .mj-voice-picker__select-label i { font-size: 11px; color: var(--mj-text-muted); }\n .mj-voice-picker__select {\n flex: 1; min-width: 0;\n padding: 5px 8px;\n font-size: 12px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n outline: none;\n }\n .mj-voice-picker__select:focus { border-color: var(--mj-border-focus); }\n .mj-voice-picker__record-row {\n display: flex; align-items: center; justify-content: space-between; gap: 10px;\n margin: 10px 12px 2px;\n padding: 10px 0 2px;\n border-top: 1px solid var(--mj-border-subtle);\n }\n .mj-voice-picker__record-label {\n display: flex; align-items: center; gap: 8px;\n font-size: 12px; color: var(--mj-text-secondary);\n }\n .mj-voice-picker__record-label > i { font-size: 12px; color: var(--mj-text-muted); }\n .mj-voice-picker__record-copy { display: flex; flex-direction: column; line-height: 1.3; }\n .mj-voice-picker__record-copy small { font-size: 10px; color: var(--mj-text-muted); }\n .mj-voice-picker__switch { position: relative; flex-shrink: 0; display: inline-flex; cursor: pointer; }\n .mj-voice-picker__switch input {\n position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer;\n }\n .mj-voice-picker__switch-track {\n position: relative; width: 34px; height: 18px; border-radius: 999px;\n background: var(--mj-border-strong); transition: background 150ms ease;\n }\n .mj-voice-picker__switch-track::after {\n content: ''; position: absolute; top: 2px; left: 2px;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--mj-bg-surface); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);\n transition: transform 150ms ease;\n }\n .mj-voice-picker__switch input:checked + .mj-voice-picker__switch-track { background: var(--mj-brand-primary); }\n .mj-voice-picker__switch input:checked + .mj-voice-picker__switch-track::after { transform: translateX(16px); }\n .mj-voice-picker__switch input:focus-visible + .mj-voice-picker__switch-track {\n outline: 2px solid var(--mj-border-focus); outline-offset: 2px;\n }\n @media (prefers-reduced-motion: reduce) {\n .mj-voice-picker__switch-track, .mj-voice-picker__switch-track::after { transition: none; }\n }\n .mj-voice-picker__footer {\n display: flex; align-items: center; gap: 8px;\n padding: 10px 12px;\n }\n "] }]
673
- }], null, { Agents: [{
703
+ }], null, { CurrentUser: [{
704
+ type: Input
705
+ }], Agents: [{
674
706
  type: Input
675
707
  }], DefaultAgentId: [{
676
708
  type: Input
@@ -692,5 +724,5 @@ export class RealtimeAgentPickerComponent extends BaseAngularComponent {
692
724
  type: HostListener,
693
725
  args: ['document:keydown.escape']
694
726
  }] }); })();
695
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeAgentPickerComponent, { className: "RealtimeAgentPickerComponent", filePath: "src/lib/components/realtime/realtime-agent-picker.component.ts", lineNumber: 371 }); })();
727
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RealtimeAgentPickerComponent, { className: "RealtimeAgentPickerComponent", filePath: "src/lib/components/realtime/realtime-agent-picker.component.ts", lineNumber: 372 }); })();
696
728
  //# sourceMappingURL=realtime-agent-picker.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-agent-picker.component.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-agent-picker.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n HostListener,\n Input,\n OnInit,\n Output,\n ViewChild,\n AfterViewInit,\n inject,\n} from '@angular/core';\nimport type { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { REALTIME_ADVANCED_SESSION_CONTROLS, UserHoldsAuthorization } from '../../services/user-authorization';\nimport {\n BuildRealtimeModelOptions,\n ConstrainTargetsToPairings,\n DefaultPairedTargetId,\n LoadCoAgentPairings,\n RealtimeModelOption,\n RealtimePairedAgentRow,\n} from '../../services/realtime-pairing';\nimport { GraphQLLiveKitClient, GraphQLDataProvider, RealtimeModelVoices, RealtimeVoiceOption } from '@memberjunction/graphql-dataprovider';\n\n/**\n * The user's confirmed choice from the realtime picker: the agent to call, an optional\n * EXPLICIT realtime model (`null` = \"Auto (recommended)\", i.e. let the server pick —\n * always `null` for users without the `Realtime: Advanced Session Controls`\n * authorization, who never see the model selector), and an optional EXPLICIT co-agent\n * (`null` = the server's co-agent resolution chain).\n */\nexport interface RealtimeAgentPick {\n /** The agent the voice call should front. */\n Agent: MJAIAgentEntityExtended;\n /** Explicit realtime model id, or `null` for the server's automatic selection. */\n PreferredModelId: string | null;\n /** Explicit provider-native voice id (e.g. `echo`), or `null` for the configured/default voice. */\n PreferredVoice: string | null;\n /** Explicit co-agent id (`MJ: AI Agents.ID`, Realtime type), or `null` for the server's resolution chain. */\n CoAgentId: string | null;\n /** Whether the user consented to recording this call (mic + agent audio). */\n RecordingConsent: boolean;\n}\n\n/** `MJ: User Settings` key for the per-user \"record this call\" consent toggle (string `'true'`/`'false'`). */\nconst RECORDING_CONSENT_KEY = 'mj.realtimeVoice.recordingConsent.v1';\n\n/**\n * Compact anchored popover that lets the user choose WHICH agent a realtime\n * voice call should front — shown by {@link MessageInputComponent} when the\n * phone button is clicked on a conversation with **no prior agent\n * participation** (new / empty conversation), and on demand via the caret\n * button next to the phone (any conversation), where it doubles as the way\n * to pick a specific **co-agent** (the Realtime-type agent that voices the\n * call) and — for authorized users — a specific **voice model**. The plain\n * phone click on an existing conversation keeps its friction-free \"call the\n * resolved agent immediately\" behavior.\n *\n * Mostly dumb: the host supplies the agent list (the same cached set the\n * @mention autocomplete / routing logic uses), the Realtime-type co-agent\n * candidates (filtered from that same set), and the defaults to preselect;\n * the picker emits the user's choice. The data it loads itself:\n * - the compact \"Voice model\" option list (active Realtime models via a\n * narrow RunView) — loaded and rendered ONLY when the current user holds\n * the `Realtime: Advanced Session Controls` authorization (pure UX\n * disclosure; the server enforces the authorization on the mint), and\n * - the chosen co-agent's pairing rows (`MJ: AI Agent Co Agents`, Active 'CoAgent'-type rows): a\n * co-agent with pairing rows may only front its paired targets, so the\n * agent list constrains to those rows (Sequence order) with the IsDefault\n * row preselected; a co-agent with zero rows is universal and the list is\n * untouched.\n *\n * It does NOT persist anything (the host persists the co-agent preference\n * via `UserInfoEngine`; `mj-conversation-agent-picker` pins\n * `MJConversationEntity.DefaultAgentID` — different jobs, which is why this\n * is a separate component).\n *\n * Anchors itself bottom-right above the composer — the host's\n * `.message-input-wrapper` is `position: relative`.\n */\n@Component({\n standalone: true,\n selector: 'mj-realtime-agent-picker',\n imports: [MJButtonDirective],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"mj-voice-picker\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-voice-picker__header\">\n <i class=\"fa-solid fa-phone\"></i>\n <span>Start a voice call with…</span>\n </div>\n @if (ShowCoAgentSelect) {\n <div class=\"mj-voice-picker__select-row\">\n <label class=\"mj-voice-picker__select-label\" for=\"mjRealtimeCoAgentSelect\">\n <i class=\"fa-solid fa-headset\"></i>\n <span>Co-agent</span>\n </label>\n <select\n #coAgentSelect\n id=\"mjRealtimeCoAgentSelect\"\n class=\"mj-voice-picker__select\"\n [value]=\"SelectedCoAgentId ?? ''\"\n (change)=\"OnCoAgentChange(coAgentSelect.value)\">\n <option value=\"\">Auto (recommended)</option>\n @for (coAgent of CoAgents; track coAgent.ID) {\n <option [value]=\"coAgent.ID\">{{ coAgent.Name }}</option>\n }\n </select>\n </div>\n }\n <div class=\"mj-voice-picker__search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <input\n #searchInput\n type=\"text\"\n placeholder=\"Search agents\"\n [value]=\"SearchText\"\n (input)=\"OnSearchInput(searchInput.value)\"\n (keydown.enter)=\"StartCall()\"\n (keydown.arrowdown)=\"MoveSelection(1); $event.preventDefault()\"\n (keydown.arrowup)=\"MoveSelection(-1); $event.preventDefault()\" />\n </div>\n <div class=\"mj-voice-picker__list\">\n @if (FilteredAgents.length === 0) {\n <div class=\"mj-voice-picker__empty\">{{ IsConstrainedByPairings ? 'No paired agents available' : 'No matching agents' }}</div>\n } @else {\n @for (agent of FilteredAgents; track agent.ID) {\n <button\n type=\"button\"\n class=\"mj-voice-picker__item\"\n [class.mj-voice-picker__item--selected]=\"IsSelected(agent)\"\n [title]=\"agent.Description || agent.Name\"\n (click)=\"Select(agent)\"\n (dblclick)=\"Pick(agent)\">\n <i [class]=\"IconClassFor(agent)\"></i>\n <span class=\"mj-voice-picker__item-name\">{{ agent.Name }}</span>\n @if (IsDefaultChoice(agent)) {\n <span class=\"mj-voice-picker__default-badge\">default</span>\n }\n </button>\n }\n }\n </div>\n @if (CanOverrideSessionConfig && Models.length > 0) {\n <div class=\"mj-voice-picker__select-row mj-voice-picker__select-row--model\">\n <label class=\"mj-voice-picker__select-label\" for=\"mjRealtimeModelSelect\">\n <i class=\"fa-solid fa-microchip\"></i>\n <span>Voice model</span>\n </label>\n <select\n #modelSelect\n id=\"mjRealtimeModelSelect\"\n class=\"mj-voice-picker__select\"\n [value]=\"SelectedModelId ?? ''\"\n (change)=\"OnModelChange(modelSelect.value)\">\n <option value=\"\">Auto (recommended)</option>\n @for (model of Models; track model.ID) {\n <option [value]=\"model.ID\">{{ model.Name }}</option>\n }\n </select>\n </div>\n @if (SelectedModelVoices.length) {\n <div class=\"mj-voice-picker__select-row mj-voice-picker__select-row--model\">\n <label class=\"mj-voice-picker__select-label\" for=\"mjRealtimeVoiceSelect\">\n <i class=\"fa-solid fa-waveform-lines\"></i>\n <span>Voice</span>\n </label>\n <select\n #voiceSelect\n id=\"mjRealtimeVoiceSelect\"\n class=\"mj-voice-picker__select\"\n [value]=\"SelectedVoiceId ?? ''\"\n (change)=\"OnVoiceChange(voiceSelect.value)\">\n <option value=\"\">Default</option>\n @for (v of SelectedModelVoices; track v.ID) {\n <option [value]=\"v.ID\">{{ v.Name }}</option>\n }\n </select>\n </div>\n }\n }\n <div class=\"mj-voice-picker__record-row\">\n <span class=\"mj-voice-picker__record-label\">\n <i class=\"fa-solid fa-microphone\"></i>\n <span class=\"mj-voice-picker__record-copy\">\n Record this call\n <small>Audio stored securely</small>\n </span>\n </span>\n <label class=\"mj-voice-picker__switch\">\n <input\n type=\"checkbox\"\n [checked]=\"RecordingConsent\"\n (change)=\"OnRecordingConsentChange($any($event.target).checked)\"\n aria-label=\"Record this call (audio stored securely)\" />\n <span class=\"mj-voice-picker__switch-track\"></span>\n </label>\n </div>\n <div class=\"mj-voice-picker__footer\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"!SelectedAgent\" (click)=\"StartCall()\">\n <i class=\"fa-solid fa-phone\"></i> Start\n </button>\n <button mjButton variant=\"outline\" size=\"sm\" (click)=\"Cancel()\">Cancel</button>\n </div>\n </div>\n `,\n styles: [`\n :host {\n /* Rendered inside a body-level CDK connected overlay (see message-input) — CDK\n owns the positioning + z-index, so the host is just a plain block. The height\n cap keeps the popover from growing past the viewport; the agent list inside\n scrolls when content exceeds it. CDK's push keeps it on-screen, and because\n it lives at the body level it can never be clipped by the chat overlay border. */\n display: block;\n max-height: min(440px, calc(100vh - 24px));\n }\n .mj-voice-picker {\n width: 300px;\n max-width: calc(100vw - 24px);\n display: flex; flex-direction: column;\n /* Match the host cap so the inner flex column knows its bound and lets the agent\n list ('mj-voice-picker__list') take the remaining space + scroll. */\n max-height: min(440px, calc(100vh - 120px));\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);\n overflow: hidden;\n }\n .mj-voice-picker__header {\n display: flex; align-items: center; gap: 8px;\n padding: 10px 12px 8px;\n font-size: 13px; font-weight: 600;\n color: var(--mj-text-primary);\n }\n .mj-voice-picker__header i { color: var(--mj-brand-primary); }\n .mj-voice-picker__search {\n display: flex; align-items: center; gap: 8px;\n margin: 0 12px 8px;\n padding: 0 10px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n }\n .mj-voice-picker__search i { font-size: 12px; color: var(--mj-text-muted); }\n .mj-voice-picker__search input {\n flex: 1; min-width: 0;\n padding: 7px 0;\n background: transparent; border: none; outline: none;\n font-size: 13px; color: var(--mj-text-primary);\n }\n .mj-voice-picker__search input::placeholder { color: var(--mj-text-disabled); }\n .mj-voice-picker__search:focus-within { border-color: var(--mj-border-focus); }\n .mj-voice-picker__list {\n /* The agent list is the flexible region: it scrolls internally and gives way\n first when the whole popup is capped to the overlay height. min-height:0 lets\n it actually shrink inside the flex column; the 220px max keeps it tidy when\n there's plenty of room. */\n flex: 1 1 auto;\n min-height: 0;\n max-height: 220px; overflow-y: auto;\n border-top: 1px solid var(--mj-border-subtle);\n border-bottom: 1px solid var(--mj-border-subtle);\n padding: 4px 0;\n }\n /* Keep the chrome around the list from collapsing when the popup is height-capped. */\n .mj-voice-picker__header,\n .mj-voice-picker__search,\n .mj-voice-picker__select-row,\n .mj-voice-picker__record-row,\n .mj-voice-picker__footer { flex-shrink: 0; }\n .mj-voice-picker__item {\n display: flex; align-items: center; gap: 8px; width: 100%;\n padding: 7px 12px;\n background: transparent; border: none; cursor: pointer;\n color: var(--mj-text-primary); font-size: 13px; text-align: left;\n }\n .mj-voice-picker__item i { width: 16px; text-align: center; color: var(--mj-text-secondary); }\n .mj-voice-picker__item:hover { background: var(--mj-bg-surface-hover); }\n .mj-voice-picker__item--selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n .mj-voice-picker__item--selected i { color: var(--mj-brand-primary); }\n .mj-voice-picker__item-name {\n flex: 1; min-width: 0;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n .mj-voice-picker__default-badge {\n flex-shrink: 0;\n padding: 1px 6px;\n border-radius: 999px;\n font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n }\n .mj-voice-picker__empty {\n padding: 12px; font-size: 12px; color: var(--mj-text-muted); text-align: center;\n }\n .mj-voice-picker__select-row {\n display: flex; align-items: center; gap: 8px;\n padding: 0 12px 8px;\n }\n .mj-voice-picker__select-row--model {\n padding: 8px 12px 0;\n }\n .mj-voice-picker__select-label {\n display: flex; align-items: center; gap: 6px;\n flex-shrink: 0;\n font-size: 12px; color: var(--mj-text-secondary);\n }\n .mj-voice-picker__select-label i { font-size: 11px; color: var(--mj-text-muted); }\n .mj-voice-picker__select {\n flex: 1; min-width: 0;\n padding: 5px 8px;\n font-size: 12px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n outline: none;\n }\n .mj-voice-picker__select:focus { border-color: var(--mj-border-focus); }\n .mj-voice-picker__record-row {\n display: flex; align-items: center; justify-content: space-between; gap: 10px;\n margin: 10px 12px 2px;\n padding: 10px 0 2px;\n border-top: 1px solid var(--mj-border-subtle);\n }\n .mj-voice-picker__record-label {\n display: flex; align-items: center; gap: 8px;\n font-size: 12px; color: var(--mj-text-secondary);\n }\n .mj-voice-picker__record-label > i { font-size: 12px; color: var(--mj-text-muted); }\n .mj-voice-picker__record-copy { display: flex; flex-direction: column; line-height: 1.3; }\n .mj-voice-picker__record-copy small { font-size: 10px; color: var(--mj-text-muted); }\n .mj-voice-picker__switch { position: relative; flex-shrink: 0; display: inline-flex; cursor: pointer; }\n .mj-voice-picker__switch input {\n position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer;\n }\n .mj-voice-picker__switch-track {\n position: relative; width: 34px; height: 18px; border-radius: 999px;\n background: var(--mj-border-strong); transition: background 150ms ease;\n }\n .mj-voice-picker__switch-track::after {\n content: ''; position: absolute; top: 2px; left: 2px;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--mj-bg-surface); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);\n transition: transform 150ms ease;\n }\n .mj-voice-picker__switch input:checked + .mj-voice-picker__switch-track { background: var(--mj-brand-primary); }\n .mj-voice-picker__switch input:checked + .mj-voice-picker__switch-track::after { transform: translateX(16px); }\n .mj-voice-picker__switch input:focus-visible + .mj-voice-picker__switch-track {\n outline: 2px solid var(--mj-border-focus); outline-offset: 2px;\n }\n @media (prefers-reduced-motion: reduce) {\n .mj-voice-picker__switch-track, .mj-voice-picker__switch-track::after { transition: none; }\n }\n .mj-voice-picker__footer {\n display: flex; align-items: center; gap: 8px;\n padding: 10px 12px;\n }\n `]\n})\nexport class RealtimeAgentPickerComponent extends BaseAngularComponent implements OnInit, AfterViewInit {\n /** Agents the user can call — same cached set the @mention / routing logic uses. */\n @Input() Agents: MJAIAgentEntityExtended[] = [];\n\n /** The agent the default resolution would pick — preselected and listed first. */\n @Input() DefaultAgentId: string | null = null;\n\n /**\n * The ACTIVE Realtime-type co-agent candidates the user can run (host-filtered from\n * the same cached, run-permission-filtered agent set as {@link Agents}). The co-agent\n * selector renders only when MORE THAN ONE candidate exists — with zero or one\n * there's nothing meaningful to choose and the server's resolution chain applies.\n */\n @Input() CoAgents: MJAIAgentEntityExtended[] = [];\n\n /**\n * The user's persisted co-agent preference (host-loaded via `UserInfoEngine`) —\n * preselected in the co-agent selector when it's still a valid candidate.\n */\n @Input() DefaultCoAgentId: string | null = null;\n\n /** Emitted with the chosen agent + optional explicit voice model / co-agent when the user confirms. */\n @Output() AgentPicked = new EventEmitter<RealtimeAgentPick>();\n\n /** Emitted when the user dismisses without starting a call. */\n @Output() Cancelled = new EventEmitter<void>();\n\n @ViewChild('searchInput') private searchInput?: ElementRef<HTMLInputElement>;\n\n public SearchText = '';\n public SelectedAgentId: string | null = null;\n\n /** Active Realtime models the model selector offers (loaded only for authorized users). */\n public Models: RealtimeModelOption[] = [];\n\n /** The explicitly chosen voice model id, or `null` for \"Auto (recommended)\" (the default). */\n public SelectedModelId: string | null = null;\n\n /** Active Realtime models + their voices (for the voice selector); loaded only for authorized users. */\n public VoiceModels: RealtimeModelVoices[] = [];\n\n /** The explicitly chosen voice id, or `null` for the configured/default voice. */\n public SelectedVoiceId: string | null = null;\n\n /** Voices for the currently-selected model (empty when no model picked or it declares none). */\n public get SelectedModelVoices(): RealtimeVoiceOption[] {\n return this.VoiceModels.find((m) => UUIDsEqual(m.ModelID, this.SelectedModelId))?.Voices ?? [];\n }\n\n /** The explicitly chosen co-agent id, or `null` for \"Auto (recommended)\" (the server's chain). */\n public SelectedCoAgentId: string | null = null;\n\n /**\n * Whether the user has opted to record this call (mic + agent audio). Loaded from the\n * per-user persisted preference (`mj.realtimeVoice.recordingConsent.v1`) on init and\n * persisted cross-device on change. Defaults to off.\n */\n public RecordingConsent = false;\n\n /**\n * Whether the current user holds the `Realtime: Advanced Session Controls`\n * authorization. Gates the voice-model selector (and any future config-override\n * controls) — pure disclosure; the server enforces the authorization on the mint.\n */\n public CanOverrideSessionConfig = false;\n\n /** The selected co-agent's pairing rows; empty = universal co-agent (no constraint). */\n public Pairings: RealtimePairedAgentRow[] = [];\n\n // Template helper — expose the shared UUID comparator to the view.\n public readonly UUIDsEqual = UUIDsEqual;\n\n private readonly cdr = inject(ChangeDetectorRef);\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n /** Monotonic guard so a stale pairing load can't clobber a newer co-agent choice. */\n private pairingLoadToken = 0;\n\n /** Co-agent selector visibility: only when there is a real choice to make. */\n public get ShowCoAgentSelect(): boolean {\n return this.CoAgents.length > 1;\n }\n\n /** True while the agent list is constrained to the selected co-agent's paired targets. */\n public get IsConstrainedByPairings(): boolean {\n return this.Pairings.length > 0;\n }\n\n public ngOnInit(): void {\n const initial = this.DefaultAgentId && this.findAgent(this.DefaultAgentId)\n ? this.DefaultAgentId\n : this.FilteredAgents[0]?.ID ?? null;\n this.SelectedAgentId = initial;\n this.CanOverrideSessionConfig = UserHoldsAuthorization(\n this.ProviderToUse?.CurrentUser,\n REALTIME_ADVANCED_SESSION_CONTROLS,\n this.ProviderToUse\n );\n if (this.CanOverrideSessionConfig) {\n void this.loadVoiceModels();\n }\n // Preselect the persisted co-agent preference when it's still a valid candidate.\n if (this.DefaultCoAgentId && this.CoAgents.some(a => UUIDsEqual(a.ID, this.DefaultCoAgentId))) {\n this.SelectedCoAgentId = this.DefaultCoAgentId;\n void this.reloadPairings();\n }\n this.RecordingConsent = this.readPersistedRecordingConsent();\n }\n\n /** Reads the per-user recording-consent preference (defensive: any failure → off). */\n private readPersistedRecordingConsent(): boolean {\n try {\n return UserInfoEngine.Instance.GetSetting(RECORDING_CONSENT_KEY) === 'true';\n } catch {\n return false;\n }\n }\n\n /** Records + persists (cross-device, debounced) the user's recording-consent choice. */\n public OnRecordingConsentChange(checked: boolean): void {\n this.RecordingConsent = checked;\n try {\n UserInfoEngine.Instance.SetSettingDebounced(RECORDING_CONSENT_KEY, String(checked));\n } catch (error) {\n console.warn('[RealtimeAgentPicker] Failed to persist recording consent:', error);\n }\n this.cdr.markForCheck();\n }\n\n /**\n * Loads the \"Voice model\" options: active models whose type is `Realtime`, filtered from\n * {@link AIEngineBase}'s already-cached `Models` (provider-scoped engine instance, lazy\n * `Config` — no RunView round-trip). Called ONLY for users who hold the\n * advanced-session-controls authorization. Tolerant — a failure simply leaves\n * the selector hidden (the server's automatic selection still applies).\n */\n private async loadVoiceModels(): Promise<void> {\n try {\n const engine = AIEngineBase.GetProviderInstance<AIEngineBase>(this.ProviderToUse, AIEngineBase) as AIEngineBase;\n await engine.Config(false, undefined, this.ProviderToUse);\n this.Models = BuildRealtimeModelOptions(engine.Models ?? []);\n } catch (error) {\n console.error('[RealtimeAgentPicker] Failed to load realtime models:', error);\n this.Models = [];\n }\n // Voices come from the driver via a server query (the cached models carry no voice list).\n try {\n const client = new GraphQLLiveKitClient(this.ProviderToUse as unknown as GraphQLDataProvider);\n this.VoiceModels = await client.GetRealtimeModelVoices();\n } catch (error) {\n console.error('[RealtimeAgentPicker] Failed to load realtime voices:', error);\n this.VoiceModels = [];\n }\n this.cdr.markForCheck();\n }\n\n /** Records the voice-model choice (`''` = Auto → `null`); clears the voice so it can't outlive a switch. */\n public OnModelChange(value: string): void {\n this.SelectedModelId = value && value.length > 0 ? value : null;\n this.SelectedVoiceId = null;\n this.cdr.markForCheck();\n }\n\n /** Records the voice choice (`''` = default → `null`). */\n public OnVoiceChange(value: string): void {\n this.SelectedVoiceId = value && value.length > 0 ? value : null;\n this.cdr.markForCheck();\n }\n\n /** Records the co-agent choice (`''` = Auto → `null`) and refreshes the pairing constraint. */\n public OnCoAgentChange(value: string): void {\n this.SelectedCoAgentId = value && value.length > 0 ? value : null;\n void this.reloadPairings();\n }\n\n /**\n * Loads the selected co-agent's pairing rows and re-applies the target constraint:\n * with rows present, the agent list narrows to the paired targets (Sequence order)\n * and the IsDefault row's target is preselected; with zero rows (or Auto co-agent)\n * the list returns to the unconstrained default. Guarded against stale loads when\n * the user changes co-agents quickly.\n */\n private async reloadPairings(): Promise<void> {\n const token = ++this.pairingLoadToken;\n const coAgentId = this.SelectedCoAgentId;\n const pairings = coAgentId ? await LoadCoAgentPairings(this.ProviderToUse, coAgentId) : [];\n if (token !== this.pairingLoadToken) {\n return; // a newer co-agent choice superseded this load\n }\n this.Pairings = pairings;\n this.ensureSelectionVisible();\n this.cdr.markForCheck();\n }\n\n /**\n * Keeps the target selection coherent after the visible list changes: prefers the\n * pairing default (when constrained), then the host default, then keeps the current\n * selection when still visible, then the first visible row.\n */\n private ensureSelectionVisible(): void {\n const visible = this.FilteredAgents;\n if (this.IsConstrainedByPairings) {\n const pairedDefault = DefaultPairedTargetId(this.Pairings);\n if (pairedDefault && visible.some(a => UUIDsEqual(a.ID, pairedDefault))) {\n this.SelectedAgentId = pairedDefault;\n return;\n }\n }\n if (this.SelectedAgentId && visible.some(a => UUIDsEqual(a.ID, this.SelectedAgentId))) {\n return; // current selection still valid\n }\n if (this.DefaultAgentId && visible.some(a => UUIDsEqual(a.ID, this.DefaultAgentId))) {\n this.SelectedAgentId = this.DefaultAgentId;\n return;\n }\n this.SelectedAgentId = visible[0]?.ID ?? null;\n }\n\n public ngAfterViewInit(): void {\n // Microtask so focus lands after the popover renders without\n // triggering ExpressionChangedAfterItHasBeenChecked.\n Promise.resolve().then(() => this.searchInput?.nativeElement.focus());\n }\n\n /**\n * The visible target list: pairing-constrained first (a co-agent WITH pairing rows may\n * only front its paired targets, kept in pairing Sequence order), then search-filtered.\n * Unconstrained lists keep the default-first + alphabetical ordering.\n */\n public get FilteredAgents(): MJAIAgentEntityExtended[] {\n const base = ConstrainTargetsToPairings(this.Agents, this.Pairings);\n const needle = this.SearchText.trim().toLowerCase();\n const filtered = base.filter(a => !needle || (a.Name ?? '').toLowerCase().includes(needle));\n if (this.IsConstrainedByPairings) {\n return filtered; // pairing Sequence order is the canonical order\n }\n return filtered.sort((a, b) => {\n const aDefault = this.isDefault(a) ? 0 : 1;\n const bDefault = this.isDefault(b) ? 0 : 1;\n if (aDefault !== bDefault) return aDefault - bDefault;\n return (a.Name ?? '').localeCompare(b.Name ?? '');\n });\n }\n\n public get SelectedAgent(): MJAIAgentEntityExtended | null {\n return this.SelectedAgentId ? this.findAgent(this.SelectedAgentId) : null;\n }\n\n public IsSelected(agent: MJAIAgentEntityExtended): boolean {\n return !!this.SelectedAgentId && UUIDsEqual(agent.ID, this.SelectedAgentId);\n }\n\n /**\n * Whether `agent` carries the \"default\" badge: under a pairing constraint that's the\n * co-agent's IsDefault target; otherwise the host-resolved default agent.\n */\n public IsDefaultChoice(agent: MJAIAgentEntityExtended): boolean {\n if (this.IsConstrainedByPairings) {\n const pairedDefault = DefaultPairedTargetId(this.Pairings);\n return !!pairedDefault && UUIDsEqual(agent.ID, pairedDefault);\n }\n return this.isDefault(agent);\n }\n\n public IconClassFor(agent: MJAIAgentEntityExtended): string {\n return agent.IconClass?.trim() || 'fa-solid fa-robot';\n }\n\n public OnSearchInput(value: string): void {\n this.SearchText = value;\n // Keep the selection on a visible row.\n const visible = this.FilteredAgents;\n if (!visible.some(a => this.IsSelected(a))) {\n this.SelectedAgentId = visible[0]?.ID ?? null;\n }\n this.cdr.markForCheck();\n }\n\n /** Arrow-key navigation through the filtered list. */\n public MoveSelection(delta: number): void {\n const visible = this.FilteredAgents;\n if (visible.length === 0) return;\n const currentIndex = visible.findIndex(a => this.IsSelected(a));\n const nextIndex = currentIndex < 0\n ? 0\n : Math.min(visible.length - 1, Math.max(0, currentIndex + delta));\n this.SelectedAgentId = visible[nextIndex].ID;\n this.cdr.markForCheck();\n }\n\n public Select(agent: MJAIAgentEntityExtended): void {\n this.SelectedAgentId = agent.ID;\n this.cdr.markForCheck();\n }\n\n /** Confirm a specific agent immediately (double-click shortcut). */\n public Pick(agent: MJAIAgentEntityExtended): void {\n this.AgentPicked.emit(this.buildPick(agent));\n }\n\n public StartCall(): void {\n const agent = this.SelectedAgent;\n if (agent) {\n this.AgentPicked.emit(this.buildPick(agent));\n }\n }\n\n public Cancel(): void {\n this.Cancelled.emit();\n }\n\n /** Dismiss when the user clicks anywhere outside the popover. (The phone\n * button that opens it stops propagation, so the opening click never\n * reaches this handler.) */\n @HostListener('document:click', ['$event'])\n public OnDocumentClick(event: MouseEvent): void {\n if (!this.elementRef.nativeElement.contains(event.target as Node)) {\n this.Cancelled.emit();\n }\n }\n\n @HostListener('document:keydown.escape')\n public OnEscape(): void {\n this.Cancelled.emit();\n }\n\n /**\n * Assembles the emitted pick. The model preference is hard-gated on the\n * authorization here (defense in depth — unauthorized users never see the selector,\n * so `SelectedModelId` should already be null).\n */\n private buildPick(agent: MJAIAgentEntityExtended): RealtimeAgentPick {\n return {\n Agent: agent,\n PreferredModelId: this.CanOverrideSessionConfig ? this.SelectedModelId : null,\n PreferredVoice: this.CanOverrideSessionConfig ? this.SelectedVoiceId : null,\n CoAgentId: this.SelectedCoAgentId,\n RecordingConsent: this.RecordingConsent\n };\n }\n\n private isDefault(agent: MJAIAgentEntityExtended): boolean {\n return !!this.DefaultAgentId && UUIDsEqual(agent.ID, this.DefaultAgentId);\n }\n\n private findAgent(id: string): MJAIAgentEntityExtended | null {\n return this.Agents.find(a => UUIDsEqual(a.ID, id)) ?? null;\n }\n}\n"]}
1
+ {"version":3,"file":"realtime-agent-picker.component.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-agent-picker.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n HostListener,\n Input,\n OnInit,\n Output,\n ViewChild,\n AfterViewInit,\n inject,\n} from '@angular/core';\nimport type { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport { UserInfo } from '@memberjunction/core';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { REALTIME_ADVANCED_SESSION_CONTROLS, UserHoldsAuthorization } from '../../services/user-authorization';\nimport {\n BuildRealtimeModelOptions,\n ConstrainTargetsToPairings,\n DefaultPairedTargetId,\n LoadCoAgentPairings,\n RealtimeModelOption,\n RealtimePairedAgentRow,\n} from '../../services/realtime-pairing';\nimport { GraphQLLiveKitClient, GraphQLDataProvider, RealtimeModelVoices, RealtimeVoiceOption } from '@memberjunction/graphql-dataprovider';\n\n/**\n * The user's confirmed choice from the realtime picker: the agent to call, an optional\n * EXPLICIT realtime model (`null` = \"Auto (recommended)\", i.e. let the server pick —\n * always `null` for users without the `Realtime: Advanced Session Controls`\n * authorization, who never see the model selector), and an optional EXPLICIT co-agent\n * (`null` = the server's co-agent resolution chain).\n */\nexport interface RealtimeAgentPick {\n /** The agent the voice call should front. */\n Agent: MJAIAgentEntityExtended;\n /** Explicit realtime model id, or `null` for the server's automatic selection. */\n PreferredModelId: string | null;\n /** Explicit provider-native voice id (e.g. `echo`), or `null` for the configured/default voice. */\n PreferredVoice: string | null;\n /** Explicit co-agent id (`MJ: AI Agents.ID`, Realtime type), or `null` for the server's resolution chain. */\n CoAgentId: string | null;\n /** Whether the user consented to recording this call (mic + agent audio). */\n RecordingConsent: boolean;\n}\n\n/** `MJ: User Settings` key for the per-user \"record this call\" consent toggle (string `'true'`/`'false'`). */\nconst RECORDING_CONSENT_KEY = 'mj.realtimeVoice.recordingConsent.v1';\n\n/**\n * Compact anchored popover that lets the user choose WHICH agent a realtime\n * voice call should front — shown by {@link MessageInputComponent} when the\n * phone button is clicked on a conversation with **no prior agent\n * participation** (new / empty conversation), and on demand via the caret\n * button next to the phone (any conversation), where it doubles as the way\n * to pick a specific **co-agent** (the Realtime-type agent that voices the\n * call) and — for authorized users — a specific **voice model**. The plain\n * phone click on an existing conversation keeps its friction-free \"call the\n * resolved agent immediately\" behavior.\n *\n * Mostly dumb: the host supplies the agent list (the same cached set the\n * @mention autocomplete / routing logic uses), the Realtime-type co-agent\n * candidates (filtered from that same set), and the defaults to preselect;\n * the picker emits the user's choice. The data it loads itself:\n * - the compact \"Voice model\" option list (active Realtime models via a\n * narrow RunView) — loaded and rendered ONLY when the current user holds\n * the `Realtime: Advanced Session Controls` authorization (pure UX\n * disclosure; the server enforces the authorization on the mint), and\n * - the chosen co-agent's pairing rows (`MJ: AI Agent Co Agents`, Active 'CoAgent'-type rows): a\n * co-agent with pairing rows may only front its paired targets, so the\n * agent list constrains to those rows (Sequence order) with the IsDefault\n * row preselected; a co-agent with zero rows is universal and the list is\n * untouched.\n *\n * It does NOT persist anything (the host persists the co-agent preference\n * via `UserInfoEngine`; `mj-conversation-agent-picker` pins\n * `MJConversationEntity.DefaultAgentID` — different jobs, which is why this\n * is a separate component).\n *\n * Anchors itself bottom-right above the composer — the host's\n * `.message-input-wrapper` is `position: relative`.\n */\n@Component({\n standalone: true,\n selector: 'mj-realtime-agent-picker',\n imports: [MJButtonDirective],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"mj-voice-picker\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-voice-picker__header\">\n <i class=\"fa-solid fa-phone\"></i>\n <span>Start a voice call with…</span>\n </div>\n @if (ShowCoAgentSelect) {\n <div class=\"mj-voice-picker__select-row\">\n <label class=\"mj-voice-picker__select-label\" for=\"mjRealtimeCoAgentSelect\">\n <i class=\"fa-solid fa-headset\"></i>\n <span>Co-agent</span>\n </label>\n <select\n #coAgentSelect\n id=\"mjRealtimeCoAgentSelect\"\n class=\"mj-voice-picker__select\"\n [value]=\"SelectedCoAgentId ?? ''\"\n (change)=\"OnCoAgentChange(coAgentSelect.value)\">\n <option value=\"\">Auto (recommended)</option>\n @for (coAgent of CoAgents; track coAgent.ID) {\n <option [value]=\"coAgent.ID\">{{ coAgent.Name }}</option>\n }\n </select>\n </div>\n }\n <div class=\"mj-voice-picker__search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <input\n #searchInput\n type=\"text\"\n placeholder=\"Search agents\"\n [value]=\"SearchText\"\n (input)=\"OnSearchInput(searchInput.value)\"\n (keydown.enter)=\"StartCall()\"\n (keydown.arrowdown)=\"MoveSelection(1); $event.preventDefault()\"\n (keydown.arrowup)=\"MoveSelection(-1); $event.preventDefault()\" />\n </div>\n <div class=\"mj-voice-picker__list\">\n @if (FilteredAgents.length === 0) {\n <div class=\"mj-voice-picker__empty\">{{ IsConstrainedByPairings ? 'No paired agents available' : 'No matching agents' }}</div>\n } @else {\n @for (agent of FilteredAgents; track agent.ID) {\n <button\n type=\"button\"\n class=\"mj-voice-picker__item\"\n [class.mj-voice-picker__item--selected]=\"IsSelected(agent)\"\n [title]=\"agent.Description || agent.Name\"\n (click)=\"Select(agent)\"\n (dblclick)=\"Pick(agent)\">\n <i [class]=\"IconClassFor(agent)\"></i>\n <span class=\"mj-voice-picker__item-name\">{{ agent.Name }}</span>\n @if (IsDefaultChoice(agent)) {\n <span class=\"mj-voice-picker__default-badge\">default</span>\n }\n </button>\n }\n }\n </div>\n @if (CanOverrideSessionConfig && Models.length > 0) {\n <div class=\"mj-voice-picker__select-row mj-voice-picker__select-row--model\">\n <label class=\"mj-voice-picker__select-label\" for=\"mjRealtimeModelSelect\">\n <i class=\"fa-solid fa-microchip\"></i>\n <span>Voice model</span>\n </label>\n <select\n #modelSelect\n id=\"mjRealtimeModelSelect\"\n class=\"mj-voice-picker__select\"\n [value]=\"SelectedModelId ?? ''\"\n (change)=\"OnModelChange(modelSelect.value)\">\n <option value=\"\">Auto (recommended)</option>\n @for (model of Models; track model.ID) {\n <option [value]=\"model.ID\">{{ model.Name }}</option>\n }\n </select>\n </div>\n @if (SelectedModelVoices.length) {\n <div class=\"mj-voice-picker__select-row mj-voice-picker__select-row--model\">\n <label class=\"mj-voice-picker__select-label\" for=\"mjRealtimeVoiceSelect\">\n <i class=\"fa-solid fa-waveform-lines\"></i>\n <span>Voice</span>\n </label>\n <select\n #voiceSelect\n id=\"mjRealtimeVoiceSelect\"\n class=\"mj-voice-picker__select\"\n [value]=\"SelectedVoiceId ?? ''\"\n (change)=\"OnVoiceChange(voiceSelect.value)\">\n <option value=\"\">Default</option>\n @for (v of SelectedModelVoices; track v.ID) {\n <option [value]=\"v.ID\">{{ v.Name }}</option>\n }\n </select>\n </div>\n }\n }\n <div class=\"mj-voice-picker__record-row\">\n <span class=\"mj-voice-picker__record-label\">\n <i class=\"fa-solid fa-microphone\"></i>\n <span class=\"mj-voice-picker__record-copy\">\n Record this call\n <small>Audio stored securely</small>\n </span>\n </span>\n <label class=\"mj-voice-picker__switch\">\n <input\n type=\"checkbox\"\n [checked]=\"RecordingConsent\"\n (change)=\"OnRecordingConsentChange($any($event.target).checked)\"\n aria-label=\"Record this call (audio stored securely)\" />\n <span class=\"mj-voice-picker__switch-track\"></span>\n </label>\n </div>\n <div class=\"mj-voice-picker__footer\">\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"!SelectedAgent\" (click)=\"StartCall()\">\n <i class=\"fa-solid fa-phone\"></i> Start\n </button>\n <button mjButton variant=\"outline\" size=\"sm\" (click)=\"Cancel()\">Cancel</button>\n </div>\n </div>\n `,\n styles: [`\n :host {\n /* Rendered inside a body-level CDK connected overlay (see message-input) — CDK\n owns the positioning + z-index, so the host is just a plain block. The height\n cap keeps the popover from growing past the viewport; the agent list inside\n scrolls when content exceeds it. CDK's push keeps it on-screen, and because\n it lives at the body level it can never be clipped by the chat overlay border. */\n display: block;\n max-height: min(440px, calc(100vh - 24px));\n }\n .mj-voice-picker {\n width: 300px;\n max-width: calc(100vw - 24px);\n display: flex; flex-direction: column;\n /* Match the host cap so the inner flex column knows its bound and lets the agent\n list ('mj-voice-picker__list') take the remaining space + scroll. */\n max-height: min(440px, calc(100vh - 120px));\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);\n overflow: hidden;\n }\n .mj-voice-picker__header {\n display: flex; align-items: center; gap: 8px;\n padding: 10px 12px 8px;\n font-size: 13px; font-weight: 600;\n color: var(--mj-text-primary);\n }\n .mj-voice-picker__header i { color: var(--mj-brand-primary); }\n .mj-voice-picker__search {\n display: flex; align-items: center; gap: 8px;\n margin: 0 12px 8px;\n padding: 0 10px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n }\n .mj-voice-picker__search i { font-size: 12px; color: var(--mj-text-muted); }\n .mj-voice-picker__search input {\n flex: 1; min-width: 0;\n padding: 7px 0;\n background: transparent; border: none; outline: none;\n font-size: 13px; color: var(--mj-text-primary);\n }\n .mj-voice-picker__search input::placeholder { color: var(--mj-text-disabled); }\n .mj-voice-picker__search:focus-within { border-color: var(--mj-border-focus); }\n .mj-voice-picker__list {\n /* The agent list is the flexible region: it scrolls internally and gives way\n first when the whole popup is capped to the overlay height. min-height:0 lets\n it actually shrink inside the flex column; the 220px max keeps it tidy when\n there's plenty of room. */\n flex: 1 1 auto;\n min-height: 0;\n max-height: 220px; overflow-y: auto;\n border-top: 1px solid var(--mj-border-subtle);\n border-bottom: 1px solid var(--mj-border-subtle);\n padding: 4px 0;\n }\n /* Keep the chrome around the list from collapsing when the popup is height-capped. */\n .mj-voice-picker__header,\n .mj-voice-picker__search,\n .mj-voice-picker__select-row,\n .mj-voice-picker__record-row,\n .mj-voice-picker__footer { flex-shrink: 0; }\n .mj-voice-picker__item {\n display: flex; align-items: center; gap: 8px; width: 100%;\n padding: 7px 12px;\n background: transparent; border: none; cursor: pointer;\n color: var(--mj-text-primary); font-size: 13px; text-align: left;\n }\n .mj-voice-picker__item i { width: 16px; text-align: center; color: var(--mj-text-secondary); }\n .mj-voice-picker__item:hover { background: var(--mj-bg-surface-hover); }\n .mj-voice-picker__item--selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n .mj-voice-picker__item--selected i { color: var(--mj-brand-primary); }\n .mj-voice-picker__item-name {\n flex: 1; min-width: 0;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n .mj-voice-picker__default-badge {\n flex-shrink: 0;\n padding: 1px 6px;\n border-radius: 999px;\n font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n }\n .mj-voice-picker__empty {\n padding: 12px; font-size: 12px; color: var(--mj-text-muted); text-align: center;\n }\n .mj-voice-picker__select-row {\n display: flex; align-items: center; gap: 8px;\n padding: 0 12px 8px;\n }\n .mj-voice-picker__select-row--model {\n padding: 8px 12px 0;\n }\n .mj-voice-picker__select-label {\n display: flex; align-items: center; gap: 6px;\n flex-shrink: 0;\n font-size: 12px; color: var(--mj-text-secondary);\n }\n .mj-voice-picker__select-label i { font-size: 11px; color: var(--mj-text-muted); }\n .mj-voice-picker__select {\n flex: 1; min-width: 0;\n padding: 5px 8px;\n font-size: 12px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n outline: none;\n }\n .mj-voice-picker__select:focus { border-color: var(--mj-border-focus); }\n .mj-voice-picker__record-row {\n display: flex; align-items: center; justify-content: space-between; gap: 10px;\n margin: 10px 12px 2px;\n padding: 10px 0 2px;\n border-top: 1px solid var(--mj-border-subtle);\n }\n .mj-voice-picker__record-label {\n display: flex; align-items: center; gap: 8px;\n font-size: 12px; color: var(--mj-text-secondary);\n }\n .mj-voice-picker__record-label > i { font-size: 12px; color: var(--mj-text-muted); }\n .mj-voice-picker__record-copy { display: flex; flex-direction: column; line-height: 1.3; }\n .mj-voice-picker__record-copy small { font-size: 10px; color: var(--mj-text-muted); }\n .mj-voice-picker__switch { position: relative; flex-shrink: 0; display: inline-flex; cursor: pointer; }\n .mj-voice-picker__switch input {\n position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer;\n }\n .mj-voice-picker__switch-track {\n position: relative; width: 34px; height: 18px; border-radius: 999px;\n background: var(--mj-border-strong); transition: background 150ms ease;\n }\n .mj-voice-picker__switch-track::after {\n content: ''; position: absolute; top: 2px; left: 2px;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--mj-bg-surface); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);\n transition: transform 150ms ease;\n }\n .mj-voice-picker__switch input:checked + .mj-voice-picker__switch-track { background: var(--mj-brand-primary); }\n .mj-voice-picker__switch input:checked + .mj-voice-picker__switch-track::after { transform: translateX(16px); }\n .mj-voice-picker__switch input:focus-visible + .mj-voice-picker__switch-track {\n outline: 2px solid var(--mj-border-focus); outline-offset: 2px;\n }\n @media (prefers-reduced-motion: reduce) {\n .mj-voice-picker__switch-track, .mj-voice-picker__switch-track::after { transition: none; }\n }\n .mj-voice-picker__footer {\n display: flex; align-items: center; gap: 8px;\n padding: 10px 12px;\n }\n `]\n})\nexport class RealtimeAgentPickerComponent extends BaseAngularComponent implements OnInit, AfterViewInit {\n /**\n * The authenticated user context (forwarded from the host composer / chat area).\n * Falls back to `this.ProviderToUse?.CurrentUser` when not provided.\n */\n @Input() CurrentUser: UserInfo | null = null;\n\n /** Returns the explicit CurrentUser input or falls back to ProviderToUse.CurrentUser. */\n public get EffectiveUser(): UserInfo | null {\n return this.CurrentUser ?? this.ProviderToUse?.CurrentUser ?? null;\n }\n\n /** Agents the user can call — same cached set the @mention / routing logic uses. */\n @Input() Agents: MJAIAgentEntityExtended[] = [];\n\n /** The agent the default resolution would pick — preselected and listed first. */\n @Input() DefaultAgentId: string | null = null;\n\n /**\n * The ACTIVE Realtime-type co-agent candidates the user can run (host-filtered from\n * the same cached, run-permission-filtered agent set as {@link Agents}). The co-agent\n * selector renders only when MORE THAN ONE candidate exists — with zero or one\n * there's nothing meaningful to choose and the server's resolution chain applies.\n */\n @Input() CoAgents: MJAIAgentEntityExtended[] = [];\n\n /**\n * The user's persisted co-agent preference (host-loaded via `UserInfoEngine`) —\n * preselected in the co-agent selector when it's still a valid candidate.\n */\n @Input() DefaultCoAgentId: string | null = null;\n\n /** Emitted with the chosen agent + optional explicit voice model / co-agent when the user confirms. */\n @Output() AgentPicked = new EventEmitter<RealtimeAgentPick>();\n\n /** Emitted when the user dismisses without starting a call. */\n @Output() Cancelled = new EventEmitter<void>();\n\n @ViewChild('searchInput') private searchInput?: ElementRef<HTMLInputElement>;\n\n public SearchText = '';\n public SelectedAgentId: string | null = null;\n\n /** Active Realtime models the model selector offers (loaded only for authorized users). */\n public Models: RealtimeModelOption[] = [];\n\n /** The explicitly chosen voice model id, or `null` for \"Auto (recommended)\" (the default). */\n public SelectedModelId: string | null = null;\n\n /** Active Realtime models + their voices (for the voice selector); loaded only for authorized users. */\n public VoiceModels: RealtimeModelVoices[] = [];\n\n /** The explicitly chosen voice id, or `null` for the configured/default voice. */\n public SelectedVoiceId: string | null = null;\n\n /** Voices for the currently-selected model (empty when no model picked or it declares none). */\n public get SelectedModelVoices(): RealtimeVoiceOption[] {\n return this.VoiceModels.find((m) => UUIDsEqual(m.ModelID, this.SelectedModelId))?.Voices ?? [];\n }\n\n /** The explicitly chosen co-agent id, or `null` for \"Auto (recommended)\" (the server's chain). */\n public SelectedCoAgentId: string | null = null;\n\n /**\n * Whether the user has opted to record this call (mic + agent audio). Loaded from the\n * per-user persisted preference (`mj.realtimeVoice.recordingConsent.v1`) on init and\n * persisted cross-device on change. Defaults to off.\n */\n public RecordingConsent = false;\n\n /**\n * Whether the current user holds the `Realtime: Advanced Session Controls`\n * authorization. Gates the voice-model selector (and any future config-override\n * controls) — pure disclosure; the server enforces the authorization on the mint.\n */\n public CanOverrideSessionConfig = false;\n\n /** The selected co-agent's pairing rows; empty = universal co-agent (no constraint). */\n public Pairings: RealtimePairedAgentRow[] = [];\n\n // Template helper — expose the shared UUID comparator to the view.\n public readonly UUIDsEqual = UUIDsEqual;\n\n private readonly cdr = inject(ChangeDetectorRef);\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n /** Monotonic guard so a stale pairing load can't clobber a newer co-agent choice. */\n private pairingLoadToken = 0;\n\n /** Co-agent selector visibility: only when there is a real choice to make. */\n public get ShowCoAgentSelect(): boolean {\n return this.CoAgents.length > 1;\n }\n\n /** True while the agent list is constrained to the selected co-agent's paired targets. */\n public get IsConstrainedByPairings(): boolean {\n return this.Pairings.length > 0;\n }\n\n public ngOnInit(): void {\n const initial = this.DefaultAgentId && this.findAgent(this.DefaultAgentId)\n ? this.DefaultAgentId\n : this.FilteredAgents[0]?.ID ?? null;\n this.SelectedAgentId = initial;\n void this.checkAuthorizationAndLoadModels();\n // Preselect the persisted co-agent preference when it's still a valid candidate.\n if (this.DefaultCoAgentId && this.CoAgents.some(a => UUIDsEqual(a.ID, this.DefaultCoAgentId))) {\n this.SelectedCoAgentId = this.DefaultCoAgentId;\n void this.reloadPairings();\n }\n this.RecordingConsent = this.readPersistedRecordingConsent();\n }\n\n /**\n * Checks whether the user holds the advanced-session-controls authorization and, if so,\n * loads the available realtime voice models and voices. If the initially evaluated user\n * lacks the authorization, attempts a one-time RefreshCurrentUser() on the provider in\n * case role assignments were granted after client metadata was cached.\n */\n private async checkAuthorizationAndLoadModels(): Promise<void> {\n this.CanOverrideSessionConfig = UserHoldsAuthorization(\n this.EffectiveUser,\n REALTIME_ADVANCED_SESSION_CONTROLS,\n this.ProviderToUse\n );\n\n if (!this.CanOverrideSessionConfig && this.ProviderToUse?.RefreshCurrentUser) {\n try {\n const refreshed = await this.ProviderToUse.RefreshCurrentUser();\n if (refreshed) {\n this.CanOverrideSessionConfig = UserHoldsAuthorization(\n refreshed,\n REALTIME_ADVANCED_SESSION_CONTROLS,\n this.ProviderToUse\n );\n }\n } catch (e) {\n console.warn('[RealtimeAgentPicker] Failed to refresh CurrentUser for authorization check:', e);\n }\n }\n\n if (this.CanOverrideSessionConfig) {\n await this.loadVoiceModels();\n }\n this.cdr.markForCheck();\n }\n\n /** Reads the per-user recording-consent preference (defensive: any failure → off). */\n private readPersistedRecordingConsent(): boolean {\n try {\n return UserInfoEngine.Instance.GetSetting(RECORDING_CONSENT_KEY) === 'true';\n } catch {\n return false;\n }\n }\n\n /** Records + persists (cross-device, debounced) the user's recording-consent choice. */\n public OnRecordingConsentChange(checked: boolean): void {\n this.RecordingConsent = checked;\n try {\n UserInfoEngine.Instance.SetSettingDebounced(RECORDING_CONSENT_KEY, String(checked));\n } catch (error) {\n console.warn('[RealtimeAgentPicker] Failed to persist recording consent:', error);\n }\n this.cdr.markForCheck();\n }\n\n /**\n * Loads the \"Voice model\" options: active models whose type is `Realtime`, filtered from\n * {@link AIEngineBase}'s already-cached `Models` (provider-scoped engine instance, lazy\n * `Config` — no RunView round-trip). Called ONLY for users who hold the\n * advanced-session-controls authorization. Tolerant — a failure simply leaves\n * the selector hidden (the server's automatic selection still applies).\n */\n private async loadVoiceModels(): Promise<void> {\n try {\n const engine = AIEngineBase.GetProviderInstance<AIEngineBase>(this.ProviderToUse, AIEngineBase) as AIEngineBase;\n await engine.Config(false, undefined, this.ProviderToUse);\n this.Models = BuildRealtimeModelOptions(engine.Models ?? []);\n } catch (error) {\n console.error('[RealtimeAgentPicker] Failed to load realtime models:', error);\n this.Models = [];\n }\n // Voices come from the driver via a server query (the cached models carry no voice list).\n try {\n const client = new GraphQLLiveKitClient(this.ProviderToUse as unknown as GraphQLDataProvider);\n this.VoiceModels = await client.GetRealtimeModelVoices();\n } catch (error) {\n console.error('[RealtimeAgentPicker] Failed to load realtime voices:', error);\n this.VoiceModels = [];\n }\n this.cdr.markForCheck();\n }\n\n /** Records the voice-model choice (`''` = Auto → `null`); clears the voice so it can't outlive a switch. */\n public OnModelChange(value: string): void {\n this.SelectedModelId = value && value.length > 0 ? value : null;\n this.SelectedVoiceId = null;\n this.cdr.markForCheck();\n }\n\n /** Records the voice choice (`''` = default → `null`). */\n public OnVoiceChange(value: string): void {\n this.SelectedVoiceId = value && value.length > 0 ? value : null;\n this.cdr.markForCheck();\n }\n\n /** Records the co-agent choice (`''` = Auto → `null`) and refreshes the pairing constraint. */\n public OnCoAgentChange(value: string): void {\n this.SelectedCoAgentId = value && value.length > 0 ? value : null;\n void this.reloadPairings();\n }\n\n /**\n * Loads the selected co-agent's pairing rows and re-applies the target constraint:\n * with rows present, the agent list narrows to the paired targets (Sequence order)\n * and the IsDefault row's target is preselected; with zero rows (or Auto co-agent)\n * the list returns to the unconstrained default. Guarded against stale loads when\n * the user changes co-agents quickly.\n */\n private async reloadPairings(): Promise<void> {\n const token = ++this.pairingLoadToken;\n const coAgentId = this.SelectedCoAgentId;\n const pairings = coAgentId ? await LoadCoAgentPairings(this.ProviderToUse, coAgentId) : [];\n if (token !== this.pairingLoadToken) {\n return; // a newer co-agent choice superseded this load\n }\n this.Pairings = pairings;\n this.ensureSelectionVisible();\n this.cdr.markForCheck();\n }\n\n /**\n * Keeps the target selection coherent after the visible list changes: prefers the\n * pairing default (when constrained), then the host default, then keeps the current\n * selection when still visible, then the first visible row.\n */\n private ensureSelectionVisible(): void {\n const visible = this.FilteredAgents;\n if (this.IsConstrainedByPairings) {\n const pairedDefault = DefaultPairedTargetId(this.Pairings);\n if (pairedDefault && visible.some(a => UUIDsEqual(a.ID, pairedDefault))) {\n this.SelectedAgentId = pairedDefault;\n return;\n }\n }\n if (this.SelectedAgentId && visible.some(a => UUIDsEqual(a.ID, this.SelectedAgentId))) {\n return; // current selection still valid\n }\n if (this.DefaultAgentId && visible.some(a => UUIDsEqual(a.ID, this.DefaultAgentId))) {\n this.SelectedAgentId = this.DefaultAgentId;\n return;\n }\n this.SelectedAgentId = visible[0]?.ID ?? null;\n }\n\n public ngAfterViewInit(): void {\n // Microtask so focus lands after the popover renders without\n // triggering ExpressionChangedAfterItHasBeenChecked.\n Promise.resolve().then(() => this.searchInput?.nativeElement.focus());\n }\n\n /**\n * The visible target list: pairing-constrained first (a co-agent WITH pairing rows may\n * only front its paired targets, kept in pairing Sequence order), then search-filtered.\n * Unconstrained lists keep the default-first + alphabetical ordering.\n */\n public get FilteredAgents(): MJAIAgentEntityExtended[] {\n const base = ConstrainTargetsToPairings(this.Agents, this.Pairings);\n const needle = this.SearchText.trim().toLowerCase();\n const filtered = base.filter(a => !needle || (a.Name ?? '').toLowerCase().includes(needle));\n if (this.IsConstrainedByPairings) {\n return filtered; // pairing Sequence order is the canonical order\n }\n return filtered.sort((a, b) => {\n const aDefault = this.isDefault(a) ? 0 : 1;\n const bDefault = this.isDefault(b) ? 0 : 1;\n if (aDefault !== bDefault) return aDefault - bDefault;\n return (a.Name ?? '').localeCompare(b.Name ?? '');\n });\n }\n\n public get SelectedAgent(): MJAIAgentEntityExtended | null {\n return this.SelectedAgentId ? this.findAgent(this.SelectedAgentId) : null;\n }\n\n public IsSelected(agent: MJAIAgentEntityExtended): boolean {\n return !!this.SelectedAgentId && UUIDsEqual(agent.ID, this.SelectedAgentId);\n }\n\n /**\n * Whether `agent` carries the \"default\" badge: under a pairing constraint that's the\n * co-agent's IsDefault target; otherwise the host-resolved default agent.\n */\n public IsDefaultChoice(agent: MJAIAgentEntityExtended): boolean {\n if (this.IsConstrainedByPairings) {\n const pairedDefault = DefaultPairedTargetId(this.Pairings);\n return !!pairedDefault && UUIDsEqual(agent.ID, pairedDefault);\n }\n return this.isDefault(agent);\n }\n\n public IconClassFor(agent: MJAIAgentEntityExtended): string {\n return agent.IconClass?.trim() || 'fa-solid fa-robot';\n }\n\n public OnSearchInput(value: string): void {\n this.SearchText = value;\n // Keep the selection on a visible row.\n const visible = this.FilteredAgents;\n if (!visible.some(a => this.IsSelected(a))) {\n this.SelectedAgentId = visible[0]?.ID ?? null;\n }\n this.cdr.markForCheck();\n }\n\n /** Arrow-key navigation through the filtered list. */\n public MoveSelection(delta: number): void {\n const visible = this.FilteredAgents;\n if (visible.length === 0) return;\n const currentIndex = visible.findIndex(a => this.IsSelected(a));\n const nextIndex = currentIndex < 0\n ? 0\n : Math.min(visible.length - 1, Math.max(0, currentIndex + delta));\n this.SelectedAgentId = visible[nextIndex].ID;\n this.cdr.markForCheck();\n }\n\n public Select(agent: MJAIAgentEntityExtended): void {\n this.SelectedAgentId = agent.ID;\n this.cdr.markForCheck();\n }\n\n /** Confirm a specific agent immediately (double-click shortcut). */\n public Pick(agent: MJAIAgentEntityExtended): void {\n this.AgentPicked.emit(this.buildPick(agent));\n }\n\n public StartCall(): void {\n const agent = this.SelectedAgent;\n if (agent) {\n this.AgentPicked.emit(this.buildPick(agent));\n }\n }\n\n public Cancel(): void {\n this.Cancelled.emit();\n }\n\n /** Dismiss when the user clicks anywhere outside the popover. (The phone\n * button that opens it stops propagation, so the opening click never\n * reaches this handler.) */\n @HostListener('document:click', ['$event'])\n public OnDocumentClick(event: MouseEvent): void {\n if (!this.elementRef.nativeElement.contains(event.target as Node)) {\n this.Cancelled.emit();\n }\n }\n\n @HostListener('document:keydown.escape')\n public OnEscape(): void {\n this.Cancelled.emit();\n }\n\n /**\n * Assembles the emitted pick. The model preference is hard-gated on the\n * authorization here (defense in depth — unauthorized users never see the selector,\n * so `SelectedModelId` should already be null).\n */\n private buildPick(agent: MJAIAgentEntityExtended): RealtimeAgentPick {\n return {\n Agent: agent,\n PreferredModelId: this.CanOverrideSessionConfig ? this.SelectedModelId : null,\n PreferredVoice: this.CanOverrideSessionConfig ? this.SelectedVoiceId : null,\n CoAgentId: this.SelectedCoAgentId,\n RecordingConsent: this.RecordingConsent\n };\n }\n\n private isDefault(agent: MJAIAgentEntityExtended): boolean {\n return !!this.DefaultAgentId && UUIDsEqual(agent.ID, this.DefaultAgentId);\n }\n\n private findAgent(id: string): MJAIAgentEntityExtended | null {\n return this.Agents.find(a => UUIDsEqual(a.ID, id)) ?? null;\n }\n}\n"]}
@@ -40,11 +40,11 @@ export declare class RealtimeDelegationCardComponent {
40
40
  CancelRequested: EventEmitter<string>;
41
41
  /** Whether the done chip is expanded inline to show the full result text. */
42
42
  Expanded: boolean;
43
- /** The artifacts this (done) delegation produced (empty array while running / when none). */
43
+ /** The artifacts this (done) delegation produced (empty array while running / when none). Suppressed for direct actions. */
44
44
  get Artifacts(): ParsedDelegationArtifact[];
45
45
  /** Emits the open-artifact request for one of this card's produced artifacts. */
46
46
  OpenArtifact(event: MouseEvent, artifact: ParsedDelegationArtifact): void;
47
- /** True when the dev "Open run" link should render (gear on + run id known). */
47
+ /** True when the dev "Open run" link should render (gear on + run id known). Suppressed for direct actions. */
48
48
  get ShowOpenRun(): boolean;
49
49
  /** Emits the open-run request for this card's delegated run. */
50
50
  OpenRun(event: MouseEvent): void;