@memberjunction/ng-conversations 6.1.1 → 6.1.2

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 (29) hide show
  1. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +36 -1
  2. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -1
  3. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +23 -0
  4. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -1
  5. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +1 -1
  6. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
  7. package/dist/lib/components/realtime/realtime-activity-rail.component.js +2 -2
  8. package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
  9. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +2 -2
  10. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -1
  11. package/dist/lib/components/realtime/realtime-delegation-card.component.js +104 -72
  12. package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
  13. package/dist/lib/components/realtime/realtime-session-state.d.ts +11 -4
  14. package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
  15. package/dist/lib/components/realtime/realtime-session-state.js +43 -1
  16. package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
  17. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +58 -2
  18. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
  19. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +154 -4
  20. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
  21. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +51 -2
  22. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -1
  23. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +285 -5
  24. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -1
  25. package/dist/lib/services/realtime-session.service.d.ts +39 -4
  26. package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
  27. package/dist/lib/services/realtime-session.service.js +128 -8
  28. package/dist/lib/services/realtime-session.service.js.map +1 -1
  29. package/package.json +31 -31
@@ -1,8 +1,8 @@
1
1
  import { Observable, Subject } from 'rxjs';
2
- import { RealtimeCaption, RealtimeDelegationProgress, RealtimeDelegationResult, RealtimeDelegationNarration } from '../../services/realtime-session.service';
2
+ import { RealtimeCaption, RealtimeDelegationProgress, RealtimeDelegationResult, RealtimeDelegationNarration, RealtimeThoughtNarration } from '../../services/realtime-session.service';
3
3
  import { ParsedDelegationArtifact } from '../../services/delegation-result-parser';
4
4
  /**
5
- * The four reactive session streams {@link RealtimeSessionState} merges — structurally
5
+ * The reactive session streams {@link RealtimeSessionState} merges — structurally
6
6
  * satisfied by `RealtimeSessionService`. Narrowed to an interface so the state (and its unit
7
7
  * tests) depend only on the streams, not on the full service / GraphQL import chain.
8
8
  */
@@ -15,6 +15,8 @@ export interface RealtimeSessionStreams {
15
15
  DelegationResult$: Observable<RealtimeDelegationResult>;
16
16
  /** Ephemeral spoken progress narrations. */
17
17
  DelegationNarration$: Observable<RealtimeDelegationNarration>;
18
+ /** Model-authored thought / reasoning narrations. */
19
+ ThoughtNarration$?: Observable<RealtimeThoughtNarration>;
18
20
  }
19
21
  /**
20
22
  * The view-model for one delegated tool/agent call in the live session. Built by
@@ -27,8 +29,8 @@ export interface RealtimeSessionStreams {
27
29
  * references and in-place updates reliably re-render.
28
30
  */
29
31
  export interface RealtimeDelegationCardVM {
30
- /** What this card represents: a delegated agent run, or a direct action invoked by the co-agent. */
31
- Kind: 'agent' | 'action';
32
+ /** What this card represents: a delegated agent run, a direct action, or model-authored narration. */
33
+ Kind: 'agent' | 'action' | 'narration';
32
34
  /** The `invoke-target-agent` or direct-action call this card represents. */
33
35
  CallID: string;
34
36
  /** Display name of the delegated agent (e.g. "Sage") or formatted direct action title (e.g. "Get Weather"). */
@@ -179,6 +181,11 @@ export declare class RealtimeSessionState {
179
181
  private onResult;
180
182
  /** Latest narration REPLACES the previous one (never accumulated). */
181
183
  private onNarration;
184
+ /**
185
+ * Creates or updates a narration delegation card representing model-authored thought / reasoning.
186
+ * Cards are immutable; narration cards are excluded from anchoring the live narration note (Reviewer Items 21 & 22).
187
+ */
188
+ private onThought;
182
189
  /**
183
190
  * Immutable replacement of a card: new card object, new thread-item wrapper, new
184
191
  * Items array, rebuilt Cards array — so every binding sees a fresh reference.
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-session-state.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-session-state.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"realtime-session-state.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-session-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAgB,MAAM,MAAM,CAAC;AACzD,OAAO,EACL,eAAe,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,wBAAwB,EAC7H,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAkB,MAAM,yCAAyC,CAAC;AAEnG;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,2CAA2C;IAC3C,SAAS,EAAE,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;IACzC,kDAAkD;IAClD,mBAAmB,EAAE,UAAU,CAAC,0BAA0B,CAAC,CAAC;IAC5D,mCAAmC;IACnC,iBAAiB,EAAE,UAAU,CAAC,wBAAwB,CAAC,CAAC;IACxD,4CAA4C;IAC5C,oBAAoB,EAAE,UAAU,CAAC,2BAA2B,CAAC,CAAC;IAC9D,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,UAAU,CAAC,wBAAwB,CAAC,CAAC;CAC1D;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,wBAAwB;IACvC,sGAAsG;IACtG,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;IACvC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,+GAA+G;IAC/G,SAAS,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;IACtB,4FAA4F;IAC5F,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,IAAI,EAAE,OAAO,CAAC;IACd,mFAAmF;IACnF,OAAO,EAAE,OAAO,CAAC;IACjB,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACvC,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gEAAgE;AAChE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qEAAqE;AACrE,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,wBAAwB,CAAC;CAChC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,SAAS,CAAC;IAChB,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC;IAChB,oFAAoF;IACpF,IAAI,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,oGAAoG;AACpG,MAAM,MAAM,kBAAkB,GAAG,yBAAyB,GAAG,4BAA4B,GAAG,yBAAyB,CAAC;AAEtH,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CASvE;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,oBAAoB;IAC/B,yFAAyF;IAClF,SAAS,SAAU;IAE1B,oFAAoF;IAC7E,KAAK,EAAE,kBAAkB,EAAE,CAAM;IAExC,oFAAoF;IAC7E,KAAK,EAAE,wBAAwB,EAAE,CAAM;IAE9C,0EAA0E;IACnE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEvC,6FAA6F;IACtF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE1C,6EAA6E;IAC7E,SAAgB,QAAQ,gBAAuB;IAE/C,sFAAsF;IACtF,OAAO,CAAC,kBAAkB,CAAK;IAC/B,kEAAkE;IAClE,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,IAAI,CAAsB;IAElC,2DAA2D;IAC3D,IAAW,oBAAoB,IAAI,OAAO,CAEzC;IAED,kFAAkF;IAC3E,MAAM,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI;IAiBlD,uFAAuF;IAChF,MAAM,IAAI,IAAI;IAOrB;;;;;;OAMG;IACI,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,IAAI;IAiB7D,sFAAsF;IAC/E,KAAK,IAAI,IAAI;IAKpB;;;;;;;;;;OAUG;IACI,qBAAqB,CAAC,EAAE,GAAE,IAAiB,GAAG,IAAI;IAczD,+HAA+H;IAC/H,OAAO,CAAC,UAAU;IAuDlB,0FAA0F;IAC1F,OAAO,CAAC,UAAU;IAgBlB,wFAAwF;IACxF,OAAO,CAAC,UAAU;IAqBlB,oFAAoF;IACpF,OAAO,CAAC,QAAQ;IAsDhB,sEAAsE;IACtE,OAAO,CAAC,WAAW;IAKnB;;;OAGG;IACH,OAAO,CAAC,SAAS;IAmCjB;;;OAGG;IACH,OAAO,CAAC,WAAW;IAYnB,+EAA+E;IAC/E,OAAO,CAAC,YAAY;IAIpB,iFAAiF;IACjF,OAAO,CAAC,eAAe;IAKvB,6EAA6E;IAC7E,OAAO,CAAC,WAAW;IAKnB,+CAA+C;IAC/C,OAAO,CAAC,KAAK;CAQd"}
@@ -71,6 +71,9 @@ export class RealtimeSessionState {
71
71
  return; // already attached
72
72
  }
73
73
  this.subs.push(voice.Captions$.subscribe(c => this.onCaptions(c)), voice.DelegationProgress$.subscribe(p => this.onProgress(p)), voice.DelegationResult$.subscribe(r => this.onResult(r)), voice.DelegationNarration$.subscribe(n => this.onNarration(n)));
74
+ if (voice.ThoughtNarration$) {
75
+ this.subs.push(voice.ThoughtNarration$.subscribe(t => this.onThought(t)));
76
+ }
74
77
  }
75
78
  /** Unsubscribes from all session streams. Call from the owning shell's ngOnDestroy. */
76
79
  Detach() {
@@ -278,6 +281,45 @@ export class RealtimeSessionState {
278
281
  this.Narration = narration.Text;
279
282
  this.Changed$.next();
280
283
  }
284
+ /**
285
+ * Creates or updates a narration delegation card representing model-authored thought / reasoning.
286
+ * Cards are immutable; narration cards are excluded from anchoring the live narration note (Reviewer Items 21 & 22).
287
+ */
288
+ onThought(thought) {
289
+ const callId = thought.CallID ?? 'model-thought';
290
+ const isDone = thought.IsFinal ?? true;
291
+ const existing = this.cardsByCallId.get(callId);
292
+ if (existing) {
293
+ this.replaceCard({
294
+ ...existing,
295
+ LatestMessage: thought.Text,
296
+ Done: isDone,
297
+ Success: true,
298
+ Result: isDone ? thought.Text : existing.Result,
299
+ FinishedAt: isDone ? Date.now() : existing.FinishedAt,
300
+ });
301
+ }
302
+ else {
303
+ const card = {
304
+ CallID: callId,
305
+ Kind: 'narration',
306
+ AgentName: this.AgentName,
307
+ LatestStep: 'reasoning',
308
+ LatestMessage: thought.Text,
309
+ Done: isDone,
310
+ Success: true,
311
+ Result: isDone ? thought.Text : null,
312
+ RunRef: this.shortRunRef(callId),
313
+ StartedAt: Date.now(),
314
+ FinishedAt: isDone ? Date.now() : undefined,
315
+ };
316
+ this.cardsByCallId.set(callId, card);
317
+ this.Items = [...this.Items, { Kind: 'delegation', Card: card }];
318
+ this.rebuildCards();
319
+ this.recomputeActive();
320
+ }
321
+ this.Changed$.next();
322
+ }
281
323
  /**
282
324
  * Immutable replacement of a card: new card object, new thread-item wrapper, new
283
325
  * Items array, rebuilt Cards array — so every binding sees a fresh reference.
@@ -299,7 +341,7 @@ export class RealtimeSessionState {
299
341
  }
300
342
  /** Recomputes which still-running delegation anchors the live narration note. */
301
343
  recomputeActive() {
302
- const running = this.Cards.find(c => !c.Done);
344
+ const running = this.Cards.find(c => !c.Done && c.Kind !== 'narration');
303
345
  this.ActiveCallId = running ? running.CallID : null;
304
346
  }
305
347
  /** Derives a short, stable run reference (e.g. "#a3f1") from the call id. */
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-session-state.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-session-state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Observable, Subject, Subscription } from 'rxjs';\nimport {\n RealtimeCaption, RealtimeDelegationProgress, RealtimeDelegationResult, RealtimeDelegationNarration\n} from '../../services/realtime-session.service';\nimport { ParsedDelegationArtifact, FormatToolName } from '../../services/delegation-result-parser';\n\n/**\n * The four reactive session streams {@link RealtimeSessionState} merges — structurally\n * satisfied by `RealtimeSessionService`. Narrowed to an interface so the state (and its unit\n * tests) depend only on the streams, not on the full service / GraphQL import chain.\n */\nexport interface RealtimeSessionStreams {\n /** Growing user/assistant caption list. */\n Captions$: Observable<RealtimeCaption[]>;\n /** Progress updates from delegated agent runs. */\n DelegationProgress$: Observable<RealtimeDelegationProgress>;\n /** Terminal delegation results. */\n DelegationResult$: Observable<RealtimeDelegationResult>;\n /** Ephemeral spoken progress narrations. */\n DelegationNarration$: Observable<RealtimeDelegationNarration>;\n}\n\n/**\n * The view-model for one delegated tool/agent call in the live session. Built by\n * {@link RealtimeSessionState} from the `DelegationProgress$` / `DelegationResult$`\n * streams (correlated by `CallID`) and rendered both inline in the session thread\n * and as a compact card in the activity rail.\n *\n * Card objects are treated as IMMUTABLE — every progress/result event REPLACES the\n * card object (and the arrays containing it) so Angular bindings always see fresh\n * references and in-place updates reliably re-render.\n */\nexport interface RealtimeDelegationCardVM {\n /** What this card represents: a delegated agent run, or a direct action invoked by the co-agent. */\n Kind: 'agent' | 'action';\n /** The `invoke-target-agent` or direct-action call this card represents. */\n CallID: string;\n /** Display name of the delegated agent (e.g. \"Sage\") or formatted direct action title (e.g. \"Get Weather\"). */\n AgentName: string;\n /** The raw tool name when Kind === 'action' (e.g. \"File_Storage_List_Objects\"). */\n ToolName?: string;\n /** Latest human-readable progress message from the stream. */\n LatestMessage: string;\n /** The delegation phase (`prompt_execution` | `action_execution` | `direct_action` | …). */\n LatestStep: string;\n /** Optional completion percentage (0–100) when the server supplies it. */\n Percentage?: number;\n /** `true` once the delegation reached a terminal result. */\n Done: boolean;\n /** Whether the delegated work succeeded (meaningful once {@link Done} is true). */\n Success: boolean;\n /** Short run identifier (e.g. \"#a3f1\") if known; shown in expanded provenance detail. */\n RunRef?: string;\n /**\n * ID of the delegated agent run (`MJ: AI Agent Runs`) once the result reports it.\n * Powers the gear-gated \"Open run\" developer link on the card / rail entry.\n */\n RunID?: string;\n /** The real result text once the delegation completes. */\n Result?: string | null;\n /**\n * Artifacts the delegated run produced (set with the terminal result). Powers the\n * \"View\" affordance on done cards / rail entries and the surface panel's artifact tabs.\n */\n Artifacts?: ParsedDelegationArtifact[];\n /** Epoch ms when the first progress event for this call arrived. */\n StartedAt: number;\n /** Epoch ms when the terminal result arrived. */\n FinishedAt?: number;\n}\n\n/** A caption turn (user or assistant) in the unified thread. */\nexport interface RealtimeThreadCaptionItem {\n Kind: 'caption';\n Role: 'User' | 'Assistant';\n Text: string;\n}\n\n/** A delegation card in the unified thread (working → done chip). */\nexport interface RealtimeThreadDelegationItem {\n Kind: 'delegation';\n Card: RealtimeDelegationCardVM;\n}\n\n/**\n * A SESSION-BOUNDARY divider in the unified thread — a subtle, token-styled rule marking\n * where one session leg ends and another begins. Two producers:\n * - review→live continuation ({@link RealtimeSessionState.StartLiveContinuation}):\n * \"Resumed live session · <time>\" before the new live items;\n * - multi-leg session review (`BuildReviewThreadItems` over a `lastSessionId` chain):\n * \"Session leg · started <time>\" between legs, carrying the PREVIOUS leg's CloseReason\n * as a small chip so the reader sees why that leg ended.\n */\nexport interface RealtimeThreadDividerItem {\n Kind: 'divider';\n /** The divider's label (e.g. \"Resumed live session\", \"Session leg\"). */\n Label: string;\n /** Timestamp rendered after the label (\"· <time>\"), or null when unknown. */\n At: Date | null;\n /** Font Awesome icon class rendered before the label (e.g. `fa-solid fa-phone`). */\n Icon: string;\n /** Why the PRECEDING leg closed (review chains only) — rendered as a small chip. */\n CloseReason?: string | null;\n}\n\n/** One entry in the chronological thread: a caption bubble, a delegation card, or a leg divider. */\nexport type RealtimeThreadItem = RealtimeThreadCaptionItem | RealtimeThreadDelegationItem | RealtimeThreadDividerItem;\n\nexport { FormatToolName } from '../../services/delegation-result-parser';\n\n/**\n * Maps a raw delegation step id to a human-friendly phrase. Unknown steps fall back to\n * the raw progress message (per product direction) so the UI never shows snake_case ids.\n */\nexport function FriendlyStepLabel(step: string, message: string): string {\n switch (step) {\n case 'direct_action': return 'Looking that up';\n case 'prompt_execution': return 'Thinking it through';\n case 'action_execution': return 'Running actions';\n case 'subagent_execution': return 'Working with another agent';\n case 'decision_processing': return 'Deciding next steps';\n default: return message || 'Working';\n }\n}\n\n/** Formats an elapsed duration in ms as a compact human string (\"8s\", \"1m 04s\"). */\nexport function FormatElapsed(ms: number): string {\n const totalSec = Math.max(0, Math.round(ms / 1000));\n if (totalSec < 60) {\n return `${totalSec}s`;\n }\n const m = Math.floor(totalSec / 60);\n const s = totalSec % 60;\n return `${m}m ${s.toString().padStart(2, '0')}s`;\n}\n\n/**\n * SINGLE SOURCE of merged live-session state, owned by the overlay shell\n * (`RealtimeSessionOverlayComponent`) and passed via `@Input()` to BOTH the session\n * thread and the activity rail — so the per-CallID card bookkeeping and subscription\n * logic exists exactly once.\n *\n * Merges FOUR reactive streams from {@link RealtimeSessionService}:\n * - `Captions$` — growing user/assistant caption list → appended to {@link Items}.\n * - `DelegationProgress$` — first event per `CallID` inserts a working card at the thread\n * tail; later events REPLACE the card immutably (new object +\n * new arrays) so change detection always fires.\n * - `DelegationResult$` — flips the card to Done/Failed with the real result text.\n * - `DelegationNarration$` — the EPHEMERAL spoken progress note (latest only, never\n * accumulated), cleared when no delegation is running anymore.\n *\n * Consumers subscribe to {@link Changed$} and `markForCheck()`; all exposed collections\n * are replaced (never mutated) on change.\n */\nexport class RealtimeSessionState {\n /** Display name used for new delegation cards (set by the overlay's AgentName input). */\n public AgentName = 'Sage';\n\n /** The merged, chronological thread (captions + delegation cards), oldest first. */\n public Items: RealtimeThreadItem[] = [];\n\n /** All delegation cards of the session, NEWEST first — the activity rail's list. */\n public Cards: RealtimeDelegationCardVM[] = [];\n\n /** Latest ephemeral narration text, or null when none should be shown. */\n public Narration: string | null = null;\n\n /** CallID of the most recently started, still-running delegation (anchors the live note). */\n public ActiveCallId: string | null = null;\n\n /** Emits after every state change so views can mark themselves for check. */\n public readonly Changed$ = new Subject<void>();\n\n /** Number of captions already merged into {@link Items} (append-only bookkeeping). */\n private placedCaptionCount = 0;\n /** Live card lookup by CallID (insertion order = start order). */\n private cardsByCallId = new Map<string, RealtimeDelegationCardVM>();\n private subs: Subscription[] = [];\n\n /** True while at least one delegation is still running. */\n public get HasRunningDelegation(): boolean {\n return this.ActiveCallId !== null;\n }\n\n /** Subscribes to the session streams. Call once from the owning overlay shell. */\n public Attach(voice: RealtimeSessionStreams): void {\n if (this.subs.length > 0) {\n return; // already attached\n }\n this.subs.push(\n voice.Captions$.subscribe(c => this.onCaptions(c)),\n voice.DelegationProgress$.subscribe(p => this.onProgress(p)),\n voice.DelegationResult$.subscribe(r => this.onResult(r)),\n voice.DelegationNarration$.subscribe(n => this.onNarration(n))\n );\n }\n\n /** Unsubscribes from all session streams. Call from the owning shell's ngOnDestroy. */\n public Detach(): void {\n for (const s of this.subs) {\n s.unsubscribe();\n }\n this.subs = [];\n }\n\n /**\n * SESSION REVIEW population path: replaces ALL merged state with a pre-built\n * HISTORICAL thread (caption turns + done delegation cards, oldest first — see\n * `BuildReviewThreadItems`). Resets the live-stream bookkeeping first, so a later\n * live session starts from a clean merge baseline; delegation cards are indexed by\n * CallID so the rail's {@link Cards} list and {@link ActiveCallId} derive normally.\n */\n public LoadHistoricalItems(items: RealtimeThreadItem[]): void {\n this.reset();\n this.Items = items.map(item => {\n if (item.Kind === 'delegation') {\n const card: RealtimeDelegationCardVM = item.Card.Kind\n ? item.Card\n : { ...item.Card, Kind: 'agent' };\n this.cardsByCallId.set(card.CallID, card);\n return { Kind: 'delegation', Card: card };\n }\n return item;\n });\n this.rebuildCards();\n this.recomputeActive();\n this.Changed$.next();\n }\n\n /** Clears all merged state and notifies subscribers (e.g. leaving session review). */\n public Clear(): void {\n this.reset();\n this.Changed$.next();\n }\n\n /**\n * REVIEW→LIVE continuation path: KEEPS the historical thread (and its delegation cards\n * in the rail) and appends a \"Resumed live session\" {@link RealtimeThreadDividerItem},\n * then resets only the LIVE-merge bookkeeping so the new session's captions and cards\n * append cleanly AFTER the divider:\n * - `placedCaptionCount` → 0 (the fresh session's caption stream starts empty, so the\n * start-of-session `[]` emission is a no-op rather than a reset);\n * - the ephemeral narration is dropped (nothing is running yet);\n * - historical cards stay indexed by CallID — new live CallIDs are unique, so live\n * cards insert alongside them and `ActiveCallId` recomputes normally.\n */\n public StartLiveContinuation(at: Date = new Date()): void {\n const divider: RealtimeThreadDividerItem = {\n Kind: 'divider',\n Label: 'Resumed live session',\n At: at,\n Icon: 'fa-solid fa-phone'\n };\n this.Items = [...this.Items, divider];\n this.Narration = null;\n this.placedCaptionCount = 0;\n this.recomputeActive();\n this.Changed$.next();\n }\n\n /** Appends any newly-arrived captions, keeping order relative to delegation cards, and updates streaming captions in place. */\n private onCaptions(captions: RealtimeCaption[]): void {\n if (captions.length < this.placedCaptionCount) {\n // Captions are cleared on a fresh session — reset all merge state.\n this.reset();\n this.Changed$.next();\n return;\n }\n\n // Find the indices in this.Items of the captions already placed in the current session.\n // They are the last `placedCaptionCount` caption items in this.Items.\n const liveCaptionIndices: number[] = [];\n for (let idx = this.Items.length - 1; idx >= 0 && liveCaptionIndices.length < this.placedCaptionCount; idx--) {\n if (this.Items[idx].Kind === 'caption') {\n liveCaptionIndices.unshift(idx);\n }\n }\n\n let nextItems = this.Items;\n let hasChanges = false;\n\n // 1. Update any already-placed captions in place if their text or role changed (e.g. streaming deltas or corrections)\n const updateCount = Math.min(this.placedCaptionCount, liveCaptionIndices.length);\n for (let i = 0; i < updateCount; i++) {\n const itemIdx = liveCaptionIndices[i];\n const current = nextItems[itemIdx] as RealtimeThreadCaptionItem;\n const incoming = captions[i];\n if (current.Role !== incoming.Role || current.Text !== incoming.Text) {\n if (nextItems === this.Items) {\n nextItems = [...this.Items];\n }\n nextItems[itemIdx] = { Kind: 'caption', Role: incoming.Role, Text: incoming.Text };\n hasChanges = true;\n }\n }\n\n // 2. Append newly-arrived captions\n if (captions.length > this.placedCaptionCount) {\n const appended: RealtimeThreadItem[] = [];\n for (let i = this.placedCaptionCount; i < captions.length; i++) {\n appended.push({ Kind: 'caption', Role: captions[i].Role, Text: captions[i].Text });\n }\n if (nextItems === this.Items) {\n nextItems = [...this.Items];\n }\n nextItems = [...nextItems, ...appended];\n this.placedCaptionCount = captions.length;\n hasChanges = true;\n }\n\n if (hasChanges) {\n this.Items = nextItems;\n this.Changed$.next();\n }\n }\n\n /** Inserts a new working card, or immutably replaces the existing one for this CallID. */\n private onProgress(progress: RealtimeDelegationProgress): void {\n const existing = this.cardsByCallId.get(progress.CallID);\n if (existing) {\n this.replaceCard({\n ...existing,\n ToolName: progress.ToolName ?? existing.ToolName,\n LatestStep: progress.Step,\n LatestMessage: progress.Message,\n Percentage: progress.Percentage\n });\n } else {\n this.insertCard(progress);\n }\n this.Changed$.next();\n }\n\n /** Creates a working card for a first-seen CallID and appends it to the thread tail. */\n private insertCard(progress: RealtimeDelegationProgress): void {\n const isAction = !!progress.ToolName && progress.ToolName !== 'invoke-target-agent';\n const card: RealtimeDelegationCardVM = {\n CallID: progress.CallID,\n Kind: isAction ? 'action' : 'agent',\n ToolName: progress.ToolName,\n AgentName: isAction ? FormatToolName(progress.ToolName!) : this.AgentName,\n LatestStep: progress.Step,\n LatestMessage: progress.Message,\n Percentage: progress.Percentage,\n Done: false,\n Success: false,\n RunRef: this.shortRunRef(progress.CallID),\n StartedAt: Date.now()\n };\n this.cardsByCallId.set(progress.CallID, card);\n this.Items = [...this.Items, { Kind: 'delegation', Card: card }];\n this.rebuildCards();\n this.recomputeActive();\n }\n\n /** Flips the card for a finished delegation to Done/Failed with its real result. */\n private onResult(result: RealtimeDelegationResult): void {\n const existing = this.cardsByCallId.get(result.CallID);\n if (!existing) {\n if (!result.ToolName) {\n return; // untracked result without tool name — ignore\n }\n // Create-or-update safety net: if a terminal result arrives without prior progress\n // (e.g. fast direct action or missed stream packet), construct and insert the done card\n // rather than dropping the result.\n const isAction = result.ToolName !== 'invoke-target-agent';\n const toolName = result.ToolName;\n const card: RealtimeDelegationCardVM = {\n CallID: result.CallID,\n Kind: isAction ? 'action' : 'agent',\n ToolName: toolName,\n AgentName: isAction ? FormatToolName(toolName) : this.AgentName,\n LatestStep: isAction ? 'direct_action' : 'decision_processing',\n LatestMessage: isAction ? `Executed ${FormatToolName(toolName)}` : 'Completed',\n Done: true,\n Success: result.Success,\n Result: result.Output,\n RunRef: this.shortRunRef(result.CallID),\n RunID: result.RunID,\n Artifacts: isAction ? undefined : result.Artifacts,\n StartedAt: Date.now(),\n FinishedAt: Date.now()\n };\n this.cardsByCallId.set(result.CallID, card);\n this.Items = [...this.Items, { Kind: 'delegation', Card: card }];\n this.rebuildCards();\n this.recomputeActive();\n if (!this.HasRunningDelegation) {\n this.Narration = null;\n }\n this.Changed$.next();\n return;\n }\n this.replaceCard({\n ...existing,\n ToolName: result.ToolName ?? existing.ToolName,\n Done: true,\n Success: result.Success,\n Result: result.Output,\n RunID: result.RunID ?? existing.RunID,\n Artifacts: existing.Kind === 'action' ? undefined : (result.Artifacts ?? existing.Artifacts),\n FinishedAt: Date.now()\n });\n if (!this.HasRunningDelegation) {\n // All delegations finished — the live narration note fades away.\n this.Narration = null;\n }\n this.Changed$.next();\n }\n\n /** Latest narration REPLACES the previous one (never accumulated). */\n private onNarration(narration: RealtimeDelegationNarration): void {\n this.Narration = narration.Text;\n this.Changed$.next();\n }\n\n /**\n * Immutable replacement of a card: new card object, new thread-item wrapper, new\n * Items array, rebuilt Cards array — so every binding sees a fresh reference.\n */\n private replaceCard(card: RealtimeDelegationCardVM): void {\n this.cardsByCallId.set(card.CallID, card);\n const idx = this.Items.findIndex(i => i.Kind === 'delegation' && i.Card.CallID === card.CallID);\n if (idx >= 0) {\n const next = [...this.Items];\n next[idx] = { Kind: 'delegation', Card: card };\n this.Items = next;\n }\n this.rebuildCards();\n this.recomputeActive();\n }\n\n /** Rebuilds the rail's newest-first card list from the (start-ordered) map. */\n private rebuildCards(): void {\n this.Cards = Array.from(this.cardsByCallId.values()).reverse();\n }\n\n /** Recomputes which still-running delegation anchors the live narration note. */\n private recomputeActive(): void {\n const running = this.Cards.find(c => !c.Done);\n this.ActiveCallId = running ? running.CallID : null;\n }\n\n /** Derives a short, stable run reference (e.g. \"#a3f1\") from the call id. */\n private shortRunRef(callId: string): string {\n const compact = callId.replace(/[^a-z0-9]/gi, '');\n return compact.length >= 4 ? `#${compact.slice(-4).toLowerCase()}` : `#${compact.toLowerCase()}`;\n }\n\n /** Clears all merged state (fresh session). */\n private reset(): void {\n this.Items = [];\n this.Cards = [];\n this.Narration = null;\n this.ActiveCallId = null;\n this.placedCaptionCount = 0;\n this.cardsByCallId.clear();\n }\n}\n"]}
1
+ {"version":3,"file":"realtime-session-state.js","sourceRoot":"","sources":["../../../../src/lib/components/realtime/realtime-session-state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Observable, Subject, Subscription } from 'rxjs';\nimport {\n RealtimeCaption, RealtimeDelegationProgress, RealtimeDelegationResult, RealtimeDelegationNarration, RealtimeThoughtNarration\n} from '../../services/realtime-session.service';\nimport { ParsedDelegationArtifact, FormatToolName } from '../../services/delegation-result-parser';\n\n/**\n * The reactive session streams {@link RealtimeSessionState} merges — structurally\n * satisfied by `RealtimeSessionService`. Narrowed to an interface so the state (and its unit\n * tests) depend only on the streams, not on the full service / GraphQL import chain.\n */\nexport interface RealtimeSessionStreams {\n /** Growing user/assistant caption list. */\n Captions$: Observable<RealtimeCaption[]>;\n /** Progress updates from delegated agent runs. */\n DelegationProgress$: Observable<RealtimeDelegationProgress>;\n /** Terminal delegation results. */\n DelegationResult$: Observable<RealtimeDelegationResult>;\n /** Ephemeral spoken progress narrations. */\n DelegationNarration$: Observable<RealtimeDelegationNarration>;\n /** Model-authored thought / reasoning narrations. */\n ThoughtNarration$?: Observable<RealtimeThoughtNarration>;\n}\n\n/**\n * The view-model for one delegated tool/agent call in the live session. Built by\n * {@link RealtimeSessionState} from the `DelegationProgress$` / `DelegationResult$`\n * streams (correlated by `CallID`) and rendered both inline in the session thread\n * and as a compact card in the activity rail.\n *\n * Card objects are treated as IMMUTABLE — every progress/result event REPLACES the\n * card object (and the arrays containing it) so Angular bindings always see fresh\n * references and in-place updates reliably re-render.\n */\nexport interface RealtimeDelegationCardVM {\n /** What this card represents: a delegated agent run, a direct action, or model-authored narration. */\n Kind: 'agent' | 'action' | 'narration';\n /** The `invoke-target-agent` or direct-action call this card represents. */\n CallID: string;\n /** Display name of the delegated agent (e.g. \"Sage\") or formatted direct action title (e.g. \"Get Weather\"). */\n AgentName: string;\n /** The raw tool name when Kind === 'action' (e.g. \"File_Storage_List_Objects\"). */\n ToolName?: string;\n /** Latest human-readable progress message from the stream. */\n LatestMessage: string;\n /** The delegation phase (`prompt_execution` | `action_execution` | `direct_action` | …). */\n LatestStep: string;\n /** Optional completion percentage (0–100) when the server supplies it. */\n Percentage?: number;\n /** `true` once the delegation reached a terminal result. */\n Done: boolean;\n /** Whether the delegated work succeeded (meaningful once {@link Done} is true). */\n Success: boolean;\n /** Short run identifier (e.g. \"#a3f1\") if known; shown in expanded provenance detail. */\n RunRef?: string;\n /**\n * ID of the delegated agent run (`MJ: AI Agent Runs`) once the result reports it.\n * Powers the gear-gated \"Open run\" developer link on the card / rail entry.\n */\n RunID?: string;\n /** The real result text once the delegation completes. */\n Result?: string | null;\n /**\n * Artifacts the delegated run produced (set with the terminal result). Powers the\n * \"View\" affordance on done cards / rail entries and the surface panel's artifact tabs.\n */\n Artifacts?: ParsedDelegationArtifact[];\n /** Epoch ms when the first progress event for this call arrived. */\n StartedAt: number;\n /** Epoch ms when the terminal result arrived. */\n FinishedAt?: number;\n}\n\n/** A caption turn (user or assistant) in the unified thread. */\nexport interface RealtimeThreadCaptionItem {\n Kind: 'caption';\n Role: 'User' | 'Assistant';\n Text: string;\n}\n\n/** A delegation card in the unified thread (working → done chip). */\nexport interface RealtimeThreadDelegationItem {\n Kind: 'delegation';\n Card: RealtimeDelegationCardVM;\n}\n\n/**\n * A SESSION-BOUNDARY divider in the unified thread — a subtle, token-styled rule marking\n * where one session leg ends and another begins. Two producers:\n * - review→live continuation ({@link RealtimeSessionState.StartLiveContinuation}):\n * \"Resumed live session · <time>\" before the new live items;\n * - multi-leg session review (`BuildReviewThreadItems` over a `lastSessionId` chain):\n * \"Session leg · started <time>\" between legs, carrying the PREVIOUS leg's CloseReason\n * as a small chip so the reader sees why that leg ended.\n */\nexport interface RealtimeThreadDividerItem {\n Kind: 'divider';\n /** The divider's label (e.g. \"Resumed live session\", \"Session leg\"). */\n Label: string;\n /** Timestamp rendered after the label (\"· <time>\"), or null when unknown. */\n At: Date | null;\n /** Font Awesome icon class rendered before the label (e.g. `fa-solid fa-phone`). */\n Icon: string;\n /** Why the PRECEDING leg closed (review chains only) — rendered as a small chip. */\n CloseReason?: string | null;\n}\n\n/** One entry in the chronological thread: a caption bubble, a delegation card, or a leg divider. */\nexport type RealtimeThreadItem = RealtimeThreadCaptionItem | RealtimeThreadDelegationItem | RealtimeThreadDividerItem;\n\nexport { FormatToolName } from '../../services/delegation-result-parser';\n\n/**\n * Maps a raw delegation step id to a human-friendly phrase. Unknown steps fall back to\n * the raw progress message (per product direction) so the UI never shows snake_case ids.\n */\nexport function FriendlyStepLabel(step: string, message: string): string {\n switch (step) {\n case 'direct_action': return 'Looking that up';\n case 'prompt_execution': return 'Thinking it through';\n case 'action_execution': return 'Running actions';\n case 'subagent_execution': return 'Working with another agent';\n case 'decision_processing': return 'Deciding next steps';\n default: return message || 'Working';\n }\n}\n\n/** Formats an elapsed duration in ms as a compact human string (\"8s\", \"1m 04s\"). */\nexport function FormatElapsed(ms: number): string {\n const totalSec = Math.max(0, Math.round(ms / 1000));\n if (totalSec < 60) {\n return `${totalSec}s`;\n }\n const m = Math.floor(totalSec / 60);\n const s = totalSec % 60;\n return `${m}m ${s.toString().padStart(2, '0')}s`;\n}\n\n/**\n * SINGLE SOURCE of merged live-session state, owned by the overlay shell\n * (`RealtimeSessionOverlayComponent`) and passed via `@Input()` to BOTH the session\n * thread and the activity rail — so the per-CallID card bookkeeping and subscription\n * logic exists exactly once.\n *\n * Merges FOUR reactive streams from {@link RealtimeSessionService}:\n * - `Captions$` — growing user/assistant caption list → appended to {@link Items}.\n * - `DelegationProgress$` — first event per `CallID` inserts a working card at the thread\n * tail; later events REPLACE the card immutably (new object +\n * new arrays) so change detection always fires.\n * - `DelegationResult$` — flips the card to Done/Failed with the real result text.\n * - `DelegationNarration$` — the EPHEMERAL spoken progress note (latest only, never\n * accumulated), cleared when no delegation is running anymore.\n *\n * Consumers subscribe to {@link Changed$} and `markForCheck()`; all exposed collections\n * are replaced (never mutated) on change.\n */\nexport class RealtimeSessionState {\n /** Display name used for new delegation cards (set by the overlay's AgentName input). */\n public AgentName = 'Sage';\n\n /** The merged, chronological thread (captions + delegation cards), oldest first. */\n public Items: RealtimeThreadItem[] = [];\n\n /** All delegation cards of the session, NEWEST first — the activity rail's list. */\n public Cards: RealtimeDelegationCardVM[] = [];\n\n /** Latest ephemeral narration text, or null when none should be shown. */\n public Narration: string | null = null;\n\n /** CallID of the most recently started, still-running delegation (anchors the live note). */\n public ActiveCallId: string | null = null;\n\n /** Emits after every state change so views can mark themselves for check. */\n public readonly Changed$ = new Subject<void>();\n\n /** Number of captions already merged into {@link Items} (append-only bookkeeping). */\n private placedCaptionCount = 0;\n /** Live card lookup by CallID (insertion order = start order). */\n private cardsByCallId = new Map<string, RealtimeDelegationCardVM>();\n private subs: Subscription[] = [];\n\n /** True while at least one delegation is still running. */\n public get HasRunningDelegation(): boolean {\n return this.ActiveCallId !== null;\n }\n\n /** Subscribes to the session streams. Call once from the owning overlay shell. */\n public Attach(voice: RealtimeSessionStreams): void {\n if (this.subs.length > 0) {\n return; // already attached\n }\n this.subs.push(\n voice.Captions$.subscribe(c => this.onCaptions(c)),\n voice.DelegationProgress$.subscribe(p => this.onProgress(p)),\n voice.DelegationResult$.subscribe(r => this.onResult(r)),\n voice.DelegationNarration$.subscribe(n => this.onNarration(n))\n );\n if (voice.ThoughtNarration$) {\n this.subs.push(\n voice.ThoughtNarration$.subscribe(t => this.onThought(t))\n );\n }\n }\n\n /** Unsubscribes from all session streams. Call from the owning shell's ngOnDestroy. */\n public Detach(): void {\n for (const s of this.subs) {\n s.unsubscribe();\n }\n this.subs = [];\n }\n\n /**\n * SESSION REVIEW population path: replaces ALL merged state with a pre-built\n * HISTORICAL thread (caption turns + done delegation cards, oldest first — see\n * `BuildReviewThreadItems`). Resets the live-stream bookkeeping first, so a later\n * live session starts from a clean merge baseline; delegation cards are indexed by\n * CallID so the rail's {@link Cards} list and {@link ActiveCallId} derive normally.\n */\n public LoadHistoricalItems(items: RealtimeThreadItem[]): void {\n this.reset();\n this.Items = items.map(item => {\n if (item.Kind === 'delegation') {\n const card: RealtimeDelegationCardVM = item.Card.Kind\n ? item.Card\n : { ...item.Card, Kind: 'agent' };\n this.cardsByCallId.set(card.CallID, card);\n return { Kind: 'delegation', Card: card };\n }\n return item;\n });\n this.rebuildCards();\n this.recomputeActive();\n this.Changed$.next();\n }\n\n /** Clears all merged state and notifies subscribers (e.g. leaving session review). */\n public Clear(): void {\n this.reset();\n this.Changed$.next();\n }\n\n /**\n * REVIEW→LIVE continuation path: KEEPS the historical thread (and its delegation cards\n * in the rail) and appends a \"Resumed live session\" {@link RealtimeThreadDividerItem},\n * then resets only the LIVE-merge bookkeeping so the new session's captions and cards\n * append cleanly AFTER the divider:\n * - `placedCaptionCount` → 0 (the fresh session's caption stream starts empty, so the\n * start-of-session `[]` emission is a no-op rather than a reset);\n * - the ephemeral narration is dropped (nothing is running yet);\n * - historical cards stay indexed by CallID — new live CallIDs are unique, so live\n * cards insert alongside them and `ActiveCallId` recomputes normally.\n */\n public StartLiveContinuation(at: Date = new Date()): void {\n const divider: RealtimeThreadDividerItem = {\n Kind: 'divider',\n Label: 'Resumed live session',\n At: at,\n Icon: 'fa-solid fa-phone'\n };\n this.Items = [...this.Items, divider];\n this.Narration = null;\n this.placedCaptionCount = 0;\n this.recomputeActive();\n this.Changed$.next();\n }\n\n /** Appends any newly-arrived captions, keeping order relative to delegation cards, and updates streaming captions in place. */\n private onCaptions(captions: RealtimeCaption[]): void {\n if (captions.length < this.placedCaptionCount) {\n // Captions are cleared on a fresh session — reset all merge state.\n this.reset();\n this.Changed$.next();\n return;\n }\n\n // Find the indices in this.Items of the captions already placed in the current session.\n // They are the last `placedCaptionCount` caption items in this.Items.\n const liveCaptionIndices: number[] = [];\n for (let idx = this.Items.length - 1; idx >= 0 && liveCaptionIndices.length < this.placedCaptionCount; idx--) {\n if (this.Items[idx].Kind === 'caption') {\n liveCaptionIndices.unshift(idx);\n }\n }\n\n let nextItems = this.Items;\n let hasChanges = false;\n\n // 1. Update any already-placed captions in place if their text or role changed (e.g. streaming deltas or corrections)\n const updateCount = Math.min(this.placedCaptionCount, liveCaptionIndices.length);\n for (let i = 0; i < updateCount; i++) {\n const itemIdx = liveCaptionIndices[i];\n const current = nextItems[itemIdx] as RealtimeThreadCaptionItem;\n const incoming = captions[i];\n if (current.Role !== incoming.Role || current.Text !== incoming.Text) {\n if (nextItems === this.Items) {\n nextItems = [...this.Items];\n }\n nextItems[itemIdx] = { Kind: 'caption', Role: incoming.Role, Text: incoming.Text };\n hasChanges = true;\n }\n }\n\n // 2. Append newly-arrived captions\n if (captions.length > this.placedCaptionCount) {\n const appended: RealtimeThreadItem[] = [];\n for (let i = this.placedCaptionCount; i < captions.length; i++) {\n appended.push({ Kind: 'caption', Role: captions[i].Role, Text: captions[i].Text });\n }\n if (nextItems === this.Items) {\n nextItems = [...this.Items];\n }\n nextItems = [...nextItems, ...appended];\n this.placedCaptionCount = captions.length;\n hasChanges = true;\n }\n\n if (hasChanges) {\n this.Items = nextItems;\n this.Changed$.next();\n }\n }\n\n /** Inserts a new working card, or immutably replaces the existing one for this CallID. */\n private onProgress(progress: RealtimeDelegationProgress): void {\n const existing = this.cardsByCallId.get(progress.CallID);\n if (existing) {\n this.replaceCard({\n ...existing,\n ToolName: progress.ToolName ?? existing.ToolName,\n LatestStep: progress.Step,\n LatestMessage: progress.Message,\n Percentage: progress.Percentage\n });\n } else {\n this.insertCard(progress);\n }\n this.Changed$.next();\n }\n\n /** Creates a working card for a first-seen CallID and appends it to the thread tail. */\n private insertCard(progress: RealtimeDelegationProgress): void {\n const isAction = !!progress.ToolName && progress.ToolName !== 'invoke-target-agent';\n const card: RealtimeDelegationCardVM = {\n CallID: progress.CallID,\n Kind: isAction ? 'action' : 'agent',\n ToolName: progress.ToolName,\n AgentName: isAction ? FormatToolName(progress.ToolName!) : this.AgentName,\n LatestStep: progress.Step,\n LatestMessage: progress.Message,\n Percentage: progress.Percentage,\n Done: false,\n Success: false,\n RunRef: this.shortRunRef(progress.CallID),\n StartedAt: Date.now()\n };\n this.cardsByCallId.set(progress.CallID, card);\n this.Items = [...this.Items, { Kind: 'delegation', Card: card }];\n this.rebuildCards();\n this.recomputeActive();\n }\n\n /** Flips the card for a finished delegation to Done/Failed with its real result. */\n private onResult(result: RealtimeDelegationResult): void {\n const existing = this.cardsByCallId.get(result.CallID);\n if (!existing) {\n if (!result.ToolName) {\n return; // untracked result without tool name — ignore\n }\n // Create-or-update safety net: if a terminal result arrives without prior progress\n // (e.g. fast direct action or missed stream packet), construct and insert the done card\n // rather than dropping the result.\n const isAction = result.ToolName !== 'invoke-target-agent';\n const toolName = result.ToolName;\n const card: RealtimeDelegationCardVM = {\n CallID: result.CallID,\n Kind: isAction ? 'action' : 'agent',\n ToolName: toolName,\n AgentName: isAction ? FormatToolName(toolName) : this.AgentName,\n LatestStep: isAction ? 'direct_action' : 'decision_processing',\n LatestMessage: isAction ? `Executed ${FormatToolName(toolName)}` : 'Completed',\n Done: true,\n Success: result.Success,\n Result: result.Output,\n RunRef: this.shortRunRef(result.CallID),\n RunID: result.RunID,\n Artifacts: isAction ? undefined : result.Artifacts,\n StartedAt: Date.now(),\n FinishedAt: Date.now()\n };\n this.cardsByCallId.set(result.CallID, card);\n this.Items = [...this.Items, { Kind: 'delegation', Card: card }];\n this.rebuildCards();\n this.recomputeActive();\n if (!this.HasRunningDelegation) {\n this.Narration = null;\n }\n this.Changed$.next();\n return;\n }\n this.replaceCard({\n ...existing,\n ToolName: result.ToolName ?? existing.ToolName,\n Done: true,\n Success: result.Success,\n Result: result.Output,\n RunID: result.RunID ?? existing.RunID,\n Artifacts: existing.Kind === 'action' ? undefined : (result.Artifacts ?? existing.Artifacts),\n FinishedAt: Date.now()\n });\n if (!this.HasRunningDelegation) {\n // All delegations finished — the live narration note fades away.\n this.Narration = null;\n }\n this.Changed$.next();\n }\n\n /** Latest narration REPLACES the previous one (never accumulated). */\n private onNarration(narration: RealtimeDelegationNarration): void {\n this.Narration = narration.Text;\n this.Changed$.next();\n }\n\n /**\n * Creates or updates a narration delegation card representing model-authored thought / reasoning.\n * Cards are immutable; narration cards are excluded from anchoring the live narration note (Reviewer Items 21 & 22).\n */\n private onThought(thought: RealtimeThoughtNarration): void {\n const callId = thought.CallID ?? 'model-thought';\n const isDone = thought.IsFinal ?? true;\n const existing = this.cardsByCallId.get(callId);\n if (existing) {\n this.replaceCard({\n ...existing,\n LatestMessage: thought.Text,\n Done: isDone,\n Success: true,\n Result: isDone ? thought.Text : existing.Result,\n FinishedAt: isDone ? Date.now() : existing.FinishedAt,\n });\n } else {\n const card: RealtimeDelegationCardVM = {\n CallID: callId,\n Kind: 'narration',\n AgentName: this.AgentName,\n LatestStep: 'reasoning',\n LatestMessage: thought.Text,\n Done: isDone,\n Success: true,\n Result: isDone ? thought.Text : null,\n RunRef: this.shortRunRef(callId),\n StartedAt: Date.now(),\n FinishedAt: isDone ? Date.now() : undefined,\n };\n this.cardsByCallId.set(callId, card);\n this.Items = [...this.Items, { Kind: 'delegation', Card: card }];\n this.rebuildCards();\n this.recomputeActive();\n }\n this.Changed$.next();\n }\n\n /**\n * Immutable replacement of a card: new card object, new thread-item wrapper, new\n * Items array, rebuilt Cards array — so every binding sees a fresh reference.\n */\n private replaceCard(card: RealtimeDelegationCardVM): void {\n this.cardsByCallId.set(card.CallID, card);\n const idx = this.Items.findIndex(i => i.Kind === 'delegation' && i.Card.CallID === card.CallID);\n if (idx >= 0) {\n const next = [...this.Items];\n next[idx] = { Kind: 'delegation', Card: card };\n this.Items = next;\n }\n this.rebuildCards();\n this.recomputeActive();\n }\n\n /** Rebuilds the rail's newest-first card list from the (start-ordered) map. */\n private rebuildCards(): void {\n this.Cards = Array.from(this.cardsByCallId.values()).reverse();\n }\n\n /** Recomputes which still-running delegation anchors the live narration note. */\n private recomputeActive(): void {\n const running = this.Cards.find(c => !c.Done && c.Kind !== 'narration');\n this.ActiveCallId = running ? running.CallID : null;\n }\n\n /** Derives a short, stable run reference (e.g. \"#a3f1\") from the call id. */\n private shortRunRef(callId: string): string {\n const compact = callId.replace(/[^a-z0-9]/gi, '');\n return compact.length >= 4 ? `#${compact.slice(-4).toLowerCase()}` : `#${compact.toLowerCase()}`;\n }\n\n /** Clears all merged state (fresh session). */\n private reset(): void {\n this.Items = [];\n this.Cards = [];\n this.Narration = null;\n this.ActiveCallId = null;\n this.placedCaptionCount = 0;\n this.cardsByCallId.clear();\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { Type } from '@angular/core';
2
- import { RealtimeToolDefinition } from '@memberjunction/ai';
2
+ import { RealtimeToolDefinition, RealtimeTrackDescriptor } from '@memberjunction/ai';
3
+ import { IChannelFrameProvider } from '@memberjunction/ai-realtime-client';
3
4
  import { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '../channels/base-realtime-channel-client';
4
5
  import { RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';
5
6
  import { RemoteBrowserAudioChunkInput } from './remote-browser-audio-player';
@@ -29,9 +30,21 @@ import { RemoteBrowserAudioChunkInput } from './remote-browser-audio-player';
29
30
  * The channel keeps NO client-side state of record — the browser's state lives entirely on
30
31
  * the server — so {@link SerializeState} / {@link RestoreState} use the base no-op behavior.
31
32
  */
32
- export declare class RemoteBrowserChannel extends BaseRealtimeChannelClient<RemoteBrowserSurfaceComponent> {
33
+ export declare class RemoteBrowserChannel extends BaseRealtimeChannelClient<RemoteBrowserSurfaceComponent> implements IChannelFrameProvider {
33
34
  /** The live bound surface, when the channel tab's pane is instantiated. */
34
35
  private surface;
36
+ /** Shared video bridge streaming browser frames to the model when the model supports inbound video. */
37
+ private videoBridge;
38
+ /**
39
+ * Sourced tracks: Remote Browser can source inbound video to the model when the model supports it.
40
+ */
41
+ GetSourcedTracks(): readonly RealtimeTrackDescriptor[];
42
+ /**
43
+ * Produces the latest browser screenshot as a base64-encoded frame for the video bridge.
44
+ * Under active screencast streaming, returns null to avoid redundant server snapshot round-trips
45
+ * and duplicate frame sourcing into the video bridge.
46
+ */
47
+ GetLatestFrame(): Promise<string | null>;
35
48
  /** Subscription to the bound surface's `HumanInput` output, torn down on unbind/dispose. */
36
49
  private humanInputSub;
37
50
  /**
@@ -89,6 +102,10 @@ export declare class RemoteBrowserChannel extends BaseRealtimeChannelClient<Remo
89
102
  GetSurfaceComponent(): Type<RemoteBrowserSurfaceComponent>;
90
103
  /** First-run intro shown the first time the user opens the Browser tab (once per user). */
91
104
  GetOnboardingDetails(): ChannelOnboardingDetails;
105
+ /** Starts the video bridge streaming browser frames to the model when inbound video is supported. */
106
+ protected OnInitialize(): void;
107
+ OnSessionStarted(): void;
108
+ private ensureVideoBridge;
92
109
  /**
93
110
  * Wires the dynamically-created surface: hands it a snapshot fetcher closing over the
94
111
  * channel context (session id + provider live there), so the surface stays transport-
@@ -110,6 +127,34 @@ export declare class RemoteBrowserChannel extends BaseRealtimeChannelClient<Remo
110
127
  BindSurface(instance: RemoteBrowserSurfaceComponent): void;
111
128
  UnbindSurface(): void;
112
129
  Dispose(): void;
130
+ /** Timestamp of the last screencast frame pushed to the video bridge (paces pushes to negotiated cadence). */
131
+ private lastScreencastPushTime;
132
+ /** Base64 content of the last frame pushed to the video bridge (for change-driven deduplication). */
133
+ private lastPushedScreencastFrame;
134
+ /** URL associated with the last frame pushed to the video bridge. */
135
+ private lastPushedUrl;
136
+ /** Minimum allowable screencast push cadence (250ms = 4 fps debounce limit during active bursts). */
137
+ private static readonly SCREENCAST_MIN_CADENCE_MS;
138
+ /** Default screencast push cadence when not otherwise negotiated (1000ms = 1 fps ceiling for Gemini Live). */
139
+ private static readonly SCREENCAST_DEFAULT_CADENCE_MS;
140
+ /** Maximum interval (ms) between visual frame pushes on a static page (heartbeat to maintain track liveness). */
141
+ private static readonly SCREENCAST_HEARTBEAT_MS;
142
+ /** Trailing timer to deliver the settled resting frame after a burst of rapid user activity. */
143
+ private screencastTrailingTimer;
144
+ /** Pending frame buffered during an active cooldown interval, waiting to settle. */
145
+ private pendingScreencastFrame;
146
+ /** Cancels any active trailing-edge settle timer and clears the handle. */
147
+ private clearScreencastTrailingTimer;
148
+ /**
149
+ * Pushes a frame to the video bridge, recording timestamp, frame content, and URL.
150
+ */
151
+ private pushScreencastFrame;
152
+ /**
153
+ * Resolves the effective push cadence in milliseconds based on the negotiated inbound video track.
154
+ * Defaults to 1000ms (1 fps ceiling for Gemini Live), but clamps down to a minimum of 250ms (4 fps)
155
+ * if the negotiated track specifies a higher `Rate`.
156
+ */
157
+ private getNegotiatedVideoCadenceMs;
113
158
  /**
114
159
  * Forwards one PUSHED screencast frame to the bound surface's canvas. Called by the session service
115
160
  * when a `RemoteBrowserScreencastFrame` arrives on the push-status stream for THIS session. No-op when
@@ -120,6 +165,17 @@ export declare class RemoteBrowserChannel extends BaseRealtimeChannelClient<Remo
120
165
  * it, a user navigating during a screencast changed the picture and nothing else: the agent kept
121
166
  * describing the page it last opened, and the pixels proving otherwise were right there on screen.
122
167
  *
168
+ * Pushes to the video bridge are CHANGE-DRIVEN with a TRAILING-EDGE SETTLE DEBOUNCE:
169
+ * 1. The human user surface renders immediately at full 60fps responsiveness.
170
+ * 2. When the page is static (same image and URL), pushes are deduplicated to avoid burning
171
+ * ~15k tokens/min of identical visual context.
172
+ * 3. A frame is pushed immediately on the leading edge when visual content or URL changes after
173
+ * the negotiated cadence (default 1000ms, clamped to >= 250ms).
174
+ * 4. During rapid bursts (scrolling, typing, clicking) within the cooldown window, subsequent frames
175
+ * are buffered; when the cooldown expires, a trailing timer delivers the settled resting frame
176
+ * so the model never misses the final user state.
177
+ * 5. A 15-second heartbeat ensures visual track liveness even during extended idle periods.
178
+ *
123
179
  * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).
124
180
  * @param currentUrl The browser's URL when the frame was captured; absent from older servers.
125
181
  */
@@ -1 +1 @@
1
- {"version":3,"file":"remote-browser-channel.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAAa,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAC/G,OAAO,EAA2D,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAS5I,OAAO,EAAwB,4BAA4B,EAA4B,MAAM,+BAA+B,CAAC;AA2P7H;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBACa,oBAAqB,SAAQ,yBAAyB,CAAC,6BAA6B,CAAC;IAChG,2EAA2E;IAC3E,OAAO,CAAC,OAAO,CAA8C;IAE7D,4FAA4F;IAC5F,OAAO,CAAC,aAAa,CAA6B;IAElD;;;;OAIG;IACH,OAAO,CAAC,SAAS,CAAS;IAE1B;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAS;IAE/B;;;;OAIG;IACH,OAAO,CAAC,YAAY,CAAuB;IAE3C;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,iBAAiB,CAAK;IAE9B,yGAAyG;IACzG,OAAO,CAAC,WAAW,CAAyC;IAE5D,+GAA+G;IAC/G,OAAO,CAAC,aAAa,CAA6B;IAElD;;;;OAIG;IACH,SAAS,CAAC,kBAAkB,SAAQ;IACpC,SAAS,CAAC,iBAAiB,SAAiB;IAE5C,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,IAAW,cAAc,IAAI,MAAM,CAElC;IAED,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,IAAW,OAAO,IAAI,MAAM,CAE3B;IAEM,kBAAkB,IAAI,sBAAsB,EAAE;IAIrC,mBAAmB,IAAI,IAAI,CAAC,6BAA6B,CAAC;IAI1E,2FAA2F;IAC3E,oBAAoB,IAAI,wBAAwB;IAgBhE;;;;;;;;;;;;;;;;;OAiBG;IACI,WAAW,CAAC,QAAQ,EAAE,6BAA6B,GAAG,IAAI;IAYjD,aAAa,IAAI,IAAI;IAUrB,OAAO,IAAI,IAAI;IAU/B;;;;;;;;;;;;OAYG;IACI,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAO9E;;;;;;OAMG;IACI,YAAY,CAAC,KAAK,EAAE,4BAA4B,GAAG,IAAI;IAM9D;;;;OAIG;YACW,eAAe;IAY7B;;;;;;;OAOG;YACW,gBAAgB;IAe9B,yGAAyG;YAC3F,eAAe;IAY7B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAqBvB;;;;;OAKG;YACW,cAAc;IAS5B,0FAA0F;YAC5E,cAAc;IAU5B;;;;;;;OAOG;YACW,aAAa;IAW3B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,cAAc;IAuBtB;;;;;;OAMG;YACW,gBAAgB;IAS9B;;;;;;OAMG;IACU,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA2DhF,iGAAiG;IACjG,OAAO,CAAC,cAAc;IAgBtB;;;;;;;OAOG;YACW,WAAW;IA0CzB;;;;;;;;OAQG;YACW,cAAc;IAqB5B;;;;;;;;;OASG;YACW,aAAa;IAoC3B,iGAAiG;IACjG,OAAO,CAAC,WAAW;IAInB,6FAA6F;IAC7F,OAAO,CAAC,SAAS;IAajB,iEAAiE;IACjE,OAAO,CAAC,EAAE;IAWV,6DAA6D;IAC7D,OAAO,CAAC,IAAI;CAIb;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD"}
1
+ {"version":3,"file":"remote-browser-channel.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-channel.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
+ var RemoteBrowserChannel_1;
7
8
  import { RegisterClass } from '@memberjunction/global';
9
+ import { CHANNEL_INBOUND_VIDEO_TRACK } from '@memberjunction/ai';
10
+ import { ChannelInboundVideoBridge } from '@memberjunction/ai-realtime-client';
8
11
  import { BaseRealtimeChannelClient } from '../channels/base-realtime-channel-client';
9
12
  import { RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';
10
13
  import { MapToolToAction, REMOTE_BROWSER_TOOL_DEFINITIONS, REMOTE_BROWSER_TOOL_NAMES, REMOTE_BROWSER_TOOL_PREFIX, RemoteBrowserToolArgError, } from './remote-browser-tools';
@@ -190,8 +193,30 @@ const INTERPRET_PAGE_MUTATION = `
190
193
  * the server — so {@link SerializeState} / {@link RestoreState} use the base no-op behavior.
191
194
  */
192
195
  let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChannelClient {
196
+ static { RemoteBrowserChannel_1 = this; }
193
197
  /** The live bound surface, when the channel tab's pane is instantiated. */
194
198
  surface = null;
199
+ /** Shared video bridge streaming browser frames to the model when the model supports inbound video. */
200
+ videoBridge = null;
201
+ /**
202
+ * Sourced tracks: Remote Browser can source inbound video to the model when the model supports it.
203
+ */
204
+ GetSourcedTracks() {
205
+ return [CHANNEL_INBOUND_VIDEO_TRACK];
206
+ }
207
+ /**
208
+ * Produces the latest browser screenshot as a base64-encoded frame for the video bridge.
209
+ * Under active screencast streaming, returns null to avoid redundant server snapshot round-trips
210
+ * and duplicate frame sourcing into the video bridge.
211
+ */
212
+ async GetLatestFrame() {
213
+ this.ensureVideoBridge();
214
+ if (this.streaming) {
215
+ return null;
216
+ }
217
+ const snapshot = await this.fetchSnapshot();
218
+ return snapshot?.ScreenshotBase64 ?? null;
219
+ }
195
220
  /** Subscription to the bound surface's `HumanInput` output, torn down on unbind/dispose. */
196
221
  humanInputSub = null;
197
222
  /**
@@ -274,6 +299,22 @@ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChanne
274
299
  IconClass: 'fa-solid fa-globe',
275
300
  };
276
301
  }
302
+ /** Starts the video bridge streaming browser frames to the model when inbound video is supported. */
303
+ OnInitialize() {
304
+ this.ensureVideoBridge();
305
+ }
306
+ OnSessionStarted() {
307
+ this.ensureVideoBridge();
308
+ }
309
+ ensureVideoBridge() {
310
+ if (!this.videoBridge && this.Context) {
311
+ this.videoBridge = new ChannelInboundVideoBridge(() => this.Context?.Client, this);
312
+ }
313
+ if (this.videoBridge && !this.videoBridge.IsActive && this.Context?.Client?.IsTrackEstablished('video', 'inbound')) {
314
+ this.videoBridge.Start?.();
315
+ }
316
+ return this.videoBridge;
317
+ }
277
318
  /**
278
319
  * Wires the dynamically-created surface: hands it a snapshot fetcher closing over the
279
320
  * channel context (session id + provider live there), so the surface stays transport-
@@ -298,6 +339,7 @@ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChanne
298
339
  // Copy-out: the surface reads the remote selection through this on a local `copy` and writes it locally.
299
340
  instance.FetchSelection = () => this.fetchSelection();
300
341
  instance.Interactive = true;
342
+ this.ensureVideoBridge();
301
343
  this.humanInputSub = instance.HumanInput.subscribe((input) => this.relayHumanInput(input));
302
344
  this.audioMutedSub = instance.AudioMutedChange.subscribe((muted) => this.audioPlayer?.SetMuted(muted));
303
345
  void this.startScreencast(instance);
@@ -313,6 +355,10 @@ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChanne
313
355
  this.surface = null;
314
356
  }
315
357
  Dispose() {
358
+ this.clearScreencastTrailingTimer();
359
+ this.pendingScreencastFrame = null;
360
+ this.videoBridge?.Stop();
361
+ this.videoBridge = null;
316
362
  this.humanInputSub?.unsubscribe();
317
363
  this.humanInputSub = null;
318
364
  this.audioMutedSub?.unsubscribe();
@@ -321,6 +367,57 @@ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChanne
321
367
  void this.stopAudioStream();
322
368
  super.Dispose();
323
369
  }
370
+ /** Timestamp of the last screencast frame pushed to the video bridge (paces pushes to negotiated cadence). */
371
+ lastScreencastPushTime = 0;
372
+ /** Base64 content of the last frame pushed to the video bridge (for change-driven deduplication). */
373
+ lastPushedScreencastFrame = null;
374
+ /** URL associated with the last frame pushed to the video bridge. */
375
+ lastPushedUrl = null;
376
+ /** Minimum allowable screencast push cadence (250ms = 4 fps debounce limit during active bursts). */
377
+ static SCREENCAST_MIN_CADENCE_MS = 250;
378
+ /** Default screencast push cadence when not otherwise negotiated (1000ms = 1 fps ceiling for Gemini Live). */
379
+ static SCREENCAST_DEFAULT_CADENCE_MS = 1000;
380
+ /** Maximum interval (ms) between visual frame pushes on a static page (heartbeat to maintain track liveness). */
381
+ static SCREENCAST_HEARTBEAT_MS = 15_000;
382
+ /** Trailing timer to deliver the settled resting frame after a burst of rapid user activity. */
383
+ screencastTrailingTimer = null;
384
+ /** Pending frame buffered during an active cooldown interval, waiting to settle. */
385
+ pendingScreencastFrame = null;
386
+ /** Cancels any active trailing-edge settle timer and clears the handle. */
387
+ clearScreencastTrailingTimer() {
388
+ if (this.screencastTrailingTimer != null) {
389
+ clearTimeout(this.screencastTrailingTimer);
390
+ this.screencastTrailingTimer = null;
391
+ }
392
+ }
393
+ /**
394
+ * Pushes a frame to the video bridge, recording timestamp, frame content, and URL.
395
+ */
396
+ pushScreencastFrame(dataBase64, currentUrl) {
397
+ this.lastScreencastPushTime = Date.now();
398
+ this.lastPushedScreencastFrame = dataBase64;
399
+ this.lastPushedUrl = currentUrl ?? null;
400
+ this.ensureVideoBridge()?.PushFrame(dataBase64);
401
+ }
402
+ /**
403
+ * Resolves the effective push cadence in milliseconds based on the negotiated inbound video track.
404
+ * Defaults to 1000ms (1 fps ceiling for Gemini Live), but clamps down to a minimum of 250ms (4 fps)
405
+ * if the negotiated track specifies a higher `Rate`.
406
+ */
407
+ getNegotiatedVideoCadenceMs() {
408
+ const client = this.Context?.Client;
409
+ if (!client) {
410
+ return RemoteBrowserChannel_1.SCREENCAST_DEFAULT_CADENCE_MS;
411
+ }
412
+ const tracks = client.EstablishedTracks;
413
+ const videoTrack = tracks.find((t) => t.Descriptor.Modality === 'video' &&
414
+ t.Descriptor.Direction === 'inbound');
415
+ const rate = videoTrack?.Descriptor.Rate;
416
+ if (typeof rate === 'number' && rate > 0) {
417
+ return Math.max(RemoteBrowserChannel_1.SCREENCAST_MIN_CADENCE_MS, Math.floor(1000 / rate));
418
+ }
419
+ return RemoteBrowserChannel_1.SCREENCAST_DEFAULT_CADENCE_MS;
420
+ }
324
421
  /**
325
422
  * Forwards one PUSHED screencast frame to the bound surface's canvas. Called by the session service
326
423
  * when a `RemoteBrowserScreencastFrame` arrives on the push-status stream for THIS session. No-op when
@@ -331,14 +428,62 @@ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChanne
331
428
  * it, a user navigating during a screencast changed the picture and nothing else: the agent kept
332
429
  * describing the page it last opened, and the pixels proving otherwise were right there on screen.
333
430
  *
431
+ * Pushes to the video bridge are CHANGE-DRIVEN with a TRAILING-EDGE SETTLE DEBOUNCE:
432
+ * 1. The human user surface renders immediately at full 60fps responsiveness.
433
+ * 2. When the page is static (same image and URL), pushes are deduplicated to avoid burning
434
+ * ~15k tokens/min of identical visual context.
435
+ * 3. A frame is pushed immediately on the leading edge when visual content or URL changes after
436
+ * the negotiated cadence (default 1000ms, clamped to >= 250ms).
437
+ * 4. During rapid bursts (scrolling, typing, clicking) within the cooldown window, subsequent frames
438
+ * are buffered; when the cooldown expires, a trailing timer delivers the settled resting frame
439
+ * so the model never misses the final user state.
440
+ * 5. A 15-second heartbeat ensures visual track liveness even during extended idle periods.
441
+ *
334
442
  * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).
335
443
  * @param currentUrl The browser's URL when the frame was captured; absent from older servers.
336
444
  */
337
445
  OnScreencastFrame(dataBase64, currentUrl) {
338
- if (this.streaming) {
339
- this.surface?.RenderFrame(dataBase64);
340
- this.notePageChange(currentUrl, 'observed');
446
+ if (!this.streaming) {
447
+ return;
448
+ }
449
+ this.surface?.RenderFrame(dataBase64);
450
+ const now = Date.now();
451
+ const cadenceMs = this.getNegotiatedVideoCadenceMs();
452
+ const elapsed = now - this.lastScreencastPushTime;
453
+ if (elapsed >= cadenceMs) {
454
+ // Cooldown has expired: leading edge push.
455
+ this.clearScreencastTrailingTimer();
456
+ this.pendingScreencastFrame = null;
457
+ const frameChanged = dataBase64 !== this.lastPushedScreencastFrame;
458
+ const urlChanged = currentUrl != null && currentUrl !== this.lastPushedUrl;
459
+ const heartbeatElapsed = elapsed >= RemoteBrowserChannel_1.SCREENCAST_HEARTBEAT_MS;
460
+ if (frameChanged || urlChanged || heartbeatElapsed) {
461
+ this.pushScreencastFrame(dataBase64, currentUrl);
462
+ }
463
+ }
464
+ else {
465
+ // Within cooldown window: buffer latest frame and schedule trailing settle timer.
466
+ this.pendingScreencastFrame = { dataBase64, currentUrl };
467
+ if (!this.screencastTrailingTimer) {
468
+ const delay = Math.max(0, cadenceMs - elapsed);
469
+ this.screencastTrailingTimer = setTimeout(() => {
470
+ this.screencastTrailingTimer = null;
471
+ if (!this.streaming || !this.pendingScreencastFrame) {
472
+ return;
473
+ }
474
+ const { dataBase64: pendingData, currentUrl: pendingUrl } = this.pendingScreencastFrame;
475
+ this.pendingScreencastFrame = null;
476
+ const frameChanged = pendingData !== this.lastPushedScreencastFrame;
477
+ const urlChanged = pendingUrl != null && pendingUrl !== this.lastPushedUrl;
478
+ const heartbeat = (Date.now() - this.lastScreencastPushTime) >= RemoteBrowserChannel_1.SCREENCAST_HEARTBEAT_MS;
479
+ if (frameChanged || urlChanged || heartbeat) {
480
+ this.pushScreencastFrame(pendingData, pendingUrl);
481
+ }
482
+ }, delay);
483
+ }
341
484
  }
485
+ // #3496: URL perception note runs AFTER the push so the visual image lands before/with the note.
486
+ this.notePageChange(currentUrl, 'observed');
342
487
  }
343
488
  /**
344
489
  * Feeds one PUSHED tab-audio chunk to the client-side audio player. Called by the session service when a
@@ -447,6 +592,11 @@ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChanne
447
592
  async stopScreencast() {
448
593
  const wasStreaming = this.streaming;
449
594
  this.streaming = false;
595
+ this.clearScreencastTrailingTimer();
596
+ this.pendingScreencastFrame = null;
597
+ this.lastPushedScreencastFrame = null;
598
+ this.lastPushedUrl = null;
599
+ this.lastScreencastPushTime = 0;
450
600
  const sessionId = this.Context?.AgentSessionID;
451
601
  if (!wasStreaming || !sessionId) {
452
602
  return;
@@ -758,7 +908,7 @@ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChanne
758
908
  return JSON.stringify(result);
759
909
  }
760
910
  };
761
- RemoteBrowserChannel = __decorate([
911
+ RemoteBrowserChannel = RemoteBrowserChannel_1 = __decorate([
762
912
  RegisterClass(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel')
763
913
  ], RemoteBrowserChannel);
764
914
  export { RemoteBrowserChannel };