@memberjunction/ng-conversations 6.2.0-edge.4 → 6.2.0-edge.5

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 (95) hide show
  1. package/README.md +29 -0
  2. package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts +1 -1
  3. package/dist/lib/components/conversation/conversation-agent-picker.component.js +3 -3
  4. package/dist/lib/components/conversation/conversation-agent-picker.component.js.map +1 -1
  5. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +75 -14
  6. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  7. package/dist/lib/components/conversation/conversation-chat-area.component.js +711 -359
  8. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  9. package/dist/lib/components/message/message-input.component.js +9 -9
  10. package/dist/lib/components/message/message-input.component.js.map +1 -1
  11. package/dist/lib/components/message/message-item.component.d.ts +137 -16
  12. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  13. package/dist/lib/components/message/message-item.component.js +1361 -296
  14. package/dist/lib/components/message/message-item.component.js.map +1 -1
  15. package/dist/lib/components/message/message-list.component.d.ts +20 -2
  16. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  17. package/dist/lib/components/message/message-list.component.js +28 -4
  18. package/dist/lib/components/message/message-list.component.js.map +1 -1
  19. package/dist/lib/components/sidebar/routines-section.component.js +1 -1
  20. package/dist/lib/components/sidebar/routines-section.component.js.map +1 -1
  21. package/dist/lib/components/task/tasks-full-view.component.d.ts +152 -31
  22. package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -1
  23. package/dist/lib/components/task/tasks-full-view.component.js +2227 -214
  24. package/dist/lib/components/task/tasks-full-view.component.js.map +1 -1
  25. package/dist/lib/components/tasks/tasks-dropdown.component.js +4 -4
  26. package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
  27. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -1
  28. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  29. package/dist/lib/components/workspace/conversation-workspace.component.js +25 -28
  30. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  31. package/dist/lib/services/agent-state.service.d.ts.map +1 -1
  32. package/dist/lib/services/agent-state.service.js +8 -8
  33. package/dist/lib/services/agent-state.service.js.map +1 -1
  34. package/dist/lib/services/browser-realtime-media-host.d.ts +7 -7
  35. package/dist/lib/services/browser-realtime-media-host.js +8 -78
  36. package/dist/lib/services/browser-realtime-media-host.js.map +1 -1
  37. package/dist/lib/services/conversation-agent.service.d.ts +16 -8
  38. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  39. package/dist/lib/services/conversation-agent.service.js +57 -27
  40. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  41. package/dist/lib/services/conversation-naming.d.ts.map +1 -1
  42. package/dist/lib/services/conversation-naming.js +9 -2
  43. package/dist/lib/services/conversation-naming.js.map +1 -1
  44. package/dist/lib/services/conversation-tasks-filter.d.ts +15 -0
  45. package/dist/lib/services/conversation-tasks-filter.d.ts.map +1 -0
  46. package/dist/lib/services/conversation-tasks-filter.js +69 -0
  47. package/dist/lib/services/conversation-tasks-filter.js.map +1 -0
  48. package/dist/lib/services/realtime-audio-recorder.d.ts +94 -86
  49. package/dist/lib/services/realtime-audio-recorder.d.ts.map +1 -1
  50. package/dist/lib/services/realtime-audio-recorder.js +222 -195
  51. package/dist/lib/services/realtime-audio-recorder.js.map +1 -1
  52. package/dist/lib/services/realtime-session.service.d.ts +30 -2
  53. package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
  54. package/dist/lib/services/realtime-session.service.js +33 -6
  55. package/dist/lib/services/realtime-session.service.js.map +1 -1
  56. package/dist/lib/services/recording/libopus-wasm-binary.d.ts +3 -0
  57. package/dist/lib/services/recording/libopus-wasm-binary.d.ts.map +1 -0
  58. package/dist/lib/services/recording/libopus-wasm-binary.js +489 -0
  59. package/dist/lib/services/recording/libopus-wasm-binary.js.map +1 -0
  60. package/dist/lib/services/recording/libopus-worker-source.d.ts +48 -0
  61. package/dist/lib/services/recording/libopus-worker-source.d.ts.map +1 -0
  62. package/dist/lib/services/recording/libopus-worker-source.js +170 -0
  63. package/dist/lib/services/recording/libopus-worker-source.js.map +1 -0
  64. package/dist/lib/services/recording/linear-resampler.d.ts +45 -0
  65. package/dist/lib/services/recording/linear-resampler.d.ts.map +1 -0
  66. package/dist/lib/services/recording/linear-resampler.js +85 -0
  67. package/dist/lib/services/recording/linear-resampler.js.map +1 -0
  68. package/dist/lib/services/recording/opus-packet-encoder.d.ts +50 -0
  69. package/dist/lib/services/recording/opus-packet-encoder.d.ts.map +1 -0
  70. package/dist/lib/services/recording/opus-packet-encoder.js +41 -0
  71. package/dist/lib/services/recording/opus-packet-encoder.js.map +1 -0
  72. package/dist/lib/services/recording/wasm-opus-encoder.d.ts +86 -0
  73. package/dist/lib/services/recording/wasm-opus-encoder.d.ts.map +1 -0
  74. package/dist/lib/services/recording/wasm-opus-encoder.js +232 -0
  75. package/dist/lib/services/recording/wasm-opus-encoder.js.map +1 -0
  76. package/dist/lib/services/recording/webcodecs-opus-encoder.d.ts +39 -0
  77. package/dist/lib/services/recording/webcodecs-opus-encoder.d.ts.map +1 -0
  78. package/dist/lib/services/recording/webcodecs-opus-encoder.js +101 -0
  79. package/dist/lib/services/recording/webcodecs-opus-encoder.js.map +1 -0
  80. package/dist/lib/services/search.service.d.ts.map +1 -1
  81. package/dist/lib/services/search.service.js +3 -16
  82. package/dist/lib/services/search.service.js.map +1 -1
  83. package/dist/lib/utils/distinct-artifacts.d.ts +9 -3
  84. package/dist/lib/utils/distinct-artifacts.d.ts.map +1 -1
  85. package/dist/lib/utils/distinct-artifacts.js +24 -5
  86. package/dist/lib/utils/distinct-artifacts.js.map +1 -1
  87. package/package.json +36 -36
  88. package/dist/lib/services/browser-recording-unload-guard.d.ts +0 -30
  89. package/dist/lib/services/browser-recording-unload-guard.d.ts.map +0 -1
  90. package/dist/lib/services/browser-recording-unload-guard.js +0 -43
  91. package/dist/lib/services/browser-recording-unload-guard.js.map +0 -1
  92. package/dist/lib/services/realtime-pcm-wav.d.ts +0 -79
  93. package/dist/lib/services/realtime-pcm-wav.d.ts.map +0 -1
  94. package/dist/lib/services/realtime-pcm-wav.js +0 -213
  95. package/dist/lib/services/realtime-pcm-wav.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"agent-state.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/agent-state.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-state.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/agent-state.service.ts"],"names":[],"mappings":"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"}
@@ -193,9 +193,8 @@ export class AgentStateService {
193
193
  if (seconds < 5) {
194
194
  return 'acknowledging';
195
195
  }
196
- // Check if nearing completion (has results or output)
197
- if (run.Result || run.Status === 'Completed') {
198
- // If there's output, likely completing
196
+ // A run that is no longer Running has reached its final block
197
+ if (run.Status !== 'Running') {
199
198
  return 'completing';
200
199
  }
201
200
  // Otherwise, working
@@ -208,13 +207,14 @@ export class AgentStateService {
208
207
  * Extracts confidence score from agent run if available
209
208
  */
210
209
  extractConfidence(run) {
211
- // Try to parse confidence from Result or other fields
210
+ // Try to parse confidence from the run payload (FinalPayload, or Result on older rows)
212
211
  // This is a placeholder - adjust based on actual data structure
213
212
  try {
214
- if (run.Result) {
215
- const result = typeof run.Result === 'string' ? JSON.parse(run.Result) : run.Result;
216
- if (result.confidence != null) {
217
- return parseFloat(result.confidence);
213
+ const payload = run.FinalPayload ?? run.Result;
214
+ if (payload) {
215
+ const result = JSON.parse(payload);
216
+ if (result?.confidence != null) {
217
+ return parseFloat(String(result.confidence));
218
218
  }
219
219
  }
220
220
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agent-state.service.js","sourceRoot":"","sources":["../../../src/lib/services/agent-state.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable, interval, Subscription } from 'rxjs';\nimport { map, shareReplay, switchMap } from 'rxjs/operators';\nimport { LogStatusEx, RunView, UserInfo, Metadata, IMetadataProvider } from '@memberjunction/core';\nimport { EscapeSQLString, UUIDsEqual } from '@memberjunction/global';\nimport { ConversationEngine, MJAIAgentRunEntity, type ConversationScope } from '@memberjunction/core-entities';\n\nexport type AgentStatus = 'acknowledging' | 'working' | 'completing' | 'completed' | 'error';\n\nexport interface AgentWithStatus {\n run: MJAIAgentRunEntity;\n status: AgentStatus;\n confidence: number | null;\n}\n\n/**\n * Manages agent state and provides real-time updates for active agents\n * Polls for agent status changes and provides reactive streams\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class AgentStateService implements OnDestroy {\n private _activeAgents$ = new BehaviorSubject<AgentWithStatus[]>([]);\n private pollSubscription?: Subscription;\n private currentUser?: UserInfo;\n /** The conversation and scope polling was started with, so a restart (SetPollInterval) keeps them. */\n private pollConversationId?: string;\n private pollScope: ConversationScope | null = null;\n private pollInterval: number = 30000; // Poll every 30 seconds (reduced from 3s to minimize DB load)\n private pollCycleCount = 0;\n /** Minimum poll cycles before allowing auto-stop. Prevents premature shutdown\n * when fire-and-forget ACK returns before the server creates the agent run record. */\n private readonly minimumPollCycles = 3;\n\n // Public observable streams\n public readonly ActiveAgents$ = this._activeAgents$.asObservable();\n\n /** @deprecated Use {@link ActiveAgents$}. */\n public get activeAgents$() {\n return this.ActiveAgents$;\n }\n\n private _provider: IMetadataProvider | null = null;\n\n constructor() {}\n\n /**\n * Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.\n */\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n\n ngOnDestroy(): void {\n this.StopPolling();\n }\n\n /**\n * Starts polling for active agents\n * @param currentUser The current user context\n * @param conversationId Optional conversation ID to filter by\n * @param scope The conversation scope whose runs are read; the trunk of `conversationId` when null\n */\n StartPolling(currentUser: UserInfo, conversationId?: string, scope: ConversationScope | null = null): void {\n this.currentUser = currentUser;\n this.StopPolling();\n this.pollConversationId = conversationId;\n this.pollScope = scope;\n this.pollCycleCount = 0;\n\n // Initial load\n this.loadActiveAgents(conversationId, scope);\n\n // Start polling\n this.pollSubscription = interval(this.pollInterval)\n .pipe(switchMap(() => this.loadActiveAgents(conversationId, scope)))\n .subscribe();\n }\n\n /** @deprecated Use {@link StartPolling}. */\n startPolling(currentUser: UserInfo, conversationId?: string, scope: ConversationScope | null = null): void {\n return this.StartPolling(currentUser, conversationId, scope);\n }\n\n /**\n * Stops polling for active agents\n */\n StopPolling(): void {\n if (this.pollSubscription) {\n this.pollSubscription.unsubscribe();\n this.pollSubscription = undefined;\n }\n }\n\n /** @deprecated Use {@link StopPolling}. */\n stopPolling(): void {\n return this.StopPolling();\n }\n\n /**\n * Gets active agents as an observable\n * @param conversationId Optional conversation ID to filter by\n */\n GetActiveAgents(conversationId?: string): Observable<AgentWithStatus[]> {\n if (conversationId) {\n return this.ActiveAgents$.pipe(\n map(agents => agents.filter(a => UUIDsEqual(a.run.ConversationID, conversationId))),\n shareReplay(1)\n );\n }\n return this.ActiveAgents$;\n }\n\n /** @deprecated Use {@link GetActiveAgents}. */\n getActiveAgents(conversationId?: string): Observable<AgentWithStatus[]> {\n return this.GetActiveAgents(conversationId);\n }\n\n /**\n * Gets a specific agent by ID\n * @param agentRunId The agent run ID\n */\n GetAgent(agentRunId: string): AgentWithStatus | undefined {\n return this._activeAgents$.value.find(a => UUIDsEqual(a.run.ID, agentRunId));\n }\n\n /** @deprecated Use {@link GetAgent}. */\n getAgent(agentRunId: string): AgentWithStatus | undefined {\n return this.GetAgent(agentRunId);\n }\n\n /**\n * Manually refreshes active agents\n * @param conversationId Optional conversation ID to filter by\n * @param scope The conversation scope whose runs are read; the trunk of `conversationId` when null\n */\n async Refresh(conversationId?: string, scope: ConversationScope | null = null): Promise<void> {\n await this.loadActiveAgents(conversationId, scope);\n }\n\n /** @deprecated Use {@link Refresh}. */\n async refresh(conversationId?: string, scope: ConversationScope | null = null): Promise<void> {\n return this.Refresh(conversationId, scope);\n }\n\n /**\n * Loads the running and paused agent runs from the database. With a conversation, only runs\n * whose conversation detail is in `scope` (the trunk of `conversationId` when null), plus the\n * conversation's runs that have no conversation detail, which belong to every path.\n */\n private async loadActiveAgents(conversationId?: string, scope: ConversationScope | null = null): Promise<void> {\n if (!this.currentUser) {\n return;\n }\n\n this.pollCycleCount++;\n const timestamp = new Date().toISOString();\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService.loadActiveAgents - Polling for active agents (conversation: ${conversationId || 'ALL'}, cycle: ${this.pollCycleCount})`, verboseOnly: true});\n\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n // Valid statuses: Running, Completed, Paused, Failed, Cancelled\n let filter = `Status IN ('Running', 'Paused')`;\n\n if (conversationId) {\n const inScope = ConversationEngine.ScopeSubquery(scope ?? ConversationEngine.TrunkScope(conversationId));\n filter += ` AND (${inScope} OR ([ConversationID]='${EscapeSQLString(conversationId)}' AND [ConversationDetailID] IS NULL))`;\n }\n\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService - Executing RunView for AI Agent Runs`, verboseOnly: true});\n const result = await rv.RunView<MJAIAgentRunEntity>(\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: filter,\n OrderBy: 'StartedAt DESC',\n MaxRows: 50,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n if (result.Success) {\n const runs = result.Results || [];\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService - Found ${runs.length} active agent(s)`, verboseOnly: true});\n const agentsWithStatus = runs.map(run => this.mapRunToAgentWithStatus(run));\n this._activeAgents$.next(agentsWithStatus);\n\n // Stop polling if no active agents AND minimum cycles met (optimization to reduce DB load).\n // The minimum cycle guard prevents premature shutdown when fire-and-forget returns\n // before the server has created the agent run record.\n if (runs.length === 0 && this.pollSubscription && this.pollCycleCount >= this.minimumPollCycles) {\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService - No active agents after ${this.pollCycleCount} cycles, stopping polling`, verboseOnly: true});\n this.StopPolling();\n }\n }\n } catch (error) {\n console.error('Failed to load active agents:', error);\n }\n }\n\n /**\n * Maps an agent run to include status and confidence information\n */\n private mapRunToAgentWithStatus(run: MJAIAgentRunEntity): AgentWithStatus {\n const status = this.determineAgentStatus(run);\n const confidence = this.extractConfidence(run);\n\n return {\n run,\n status,\n confidence\n };\n }\n\n /**\n * Determines the agent status based on the run entity\n */\n private determineAgentStatus(run: MJAIAgentRunEntity): AgentStatus {\n const status = run.Status?.toLowerCase() || 'running';\n\n // Check for errors\n if (status === 'error' || status === 'failed') {\n return 'error';\n }\n\n // Check for completion\n if (status === 'completed' || status === 'complete' || status === 'success') {\n return 'completed';\n }\n\n // Determine stage based on progress or elapsed time\n if (run.StartedAt) {\n const elapsed = Date.now() - new Date(run.StartedAt).getTime();\n const seconds = elapsed / 1000;\n\n // First 5 seconds: acknowledging\n if (seconds < 5) {\n return 'acknowledging';\n }\n\n // Check if nearing completion (has results or output)\n if (run.Result || run.Status === 'Completed') {\n // If there's output, likely completing\n return 'completing';\n }\n\n // Otherwise, working\n return 'working';\n }\n\n // Default to acknowledging if just started\n return 'acknowledging';\n }\n\n /**\n * Extracts confidence score from agent run if available\n */\n private extractConfidence(run: MJAIAgentRunEntity): number | null {\n // Try to parse confidence from Result or other fields\n // This is a placeholder - adjust based on actual data structure\n try {\n if (run.Result) {\n const result = typeof run.Result === 'string' ? JSON.parse(run.Result) : run.Result;\n if (result.confidence != null) {\n return parseFloat(result.confidence);\n }\n }\n } catch {\n // Ignore parse errors\n }\n\n return null;\n }\n\n /**\n * Stops an agent run that is still in flight.\n *\n * The run's row is the control channel: this marks it `Cancelled` with reason `User Request`,\n * and the server process that owns the run notices within a few seconds (the agent-run\n * watchdog's cancellation poll), aborts the in-flight step through the run's cancellation\n * token, and finalizes the run — keeping the payload as it stood — and its reply as stopped.\n * No request reaches the running process directly, so this works whichever replica owns it.\n *\n * The row is loaded fresh rather than taken from the polled active-agent list, which refreshes\n * every 30 seconds and may not hold a run that started a moment ago.\n *\n * @returns true when the row was marked; false when the run was not found, had already\n * finished (nothing to stop), or the save was refused.\n */\n async CancelAgent(agentRunId: string): Promise<boolean> {\n try {\n const run = await this.Provider.GetEntityObject<MJAIAgentRunEntity>('MJ: AI Agent Runs', this.currentUser);\n if (!await run.Load(agentRunId)) {\n return false;\n }\n if (run.Status !== 'Running' && run.Status !== 'Paused') {\n return false;\n }\n\n run.Status = 'Cancelled';\n run.CancellationReason = 'User Request';\n const saved = await run.Save();\n if (!saved) {\n console.error(`Failed to stop agent run ${agentRunId}: ${run.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n return false;\n }\n\n await this.Refresh();\n return true;\n } catch (error) {\n console.error('Failed to cancel agent:', error);\n return false;\n }\n }\n\n /**\n * Stops the in-flight run behind an agent's reply message, for callers that know the\n * conversation detail but not yet the run (the run row is created a moment after the reply\n * placeholder). Finds the newest Running or Paused run for that detail and stops it.\n *\n * @returns true when a run was found and marked; false otherwise.\n */\n async CancelAgentForDetail(conversationDetailId: string): Promise<boolean> {\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView<{ ID: string }>(\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `ConversationDetailID='${conversationDetailId}' AND Status IN ('Running', 'Paused')`,\n OrderBy: 'StartedAt DESC',\n MaxRows: 1,\n Fields: ['ID'],\n ResultType: 'simple'\n },\n this.currentUser\n );\n const runId = result.Success ? result.Results?.[0]?.ID : undefined;\n if (!runId) {\n return false;\n }\n return await this.CancelAgent(runId);\n } catch (error) {\n console.error('Failed to find the agent run to cancel:', error);\n return false;\n }\n }\n\n /** @deprecated Use {@link CancelAgent}. */\n async cancelAgent(agentRunId: string): Promise<boolean> {\n return this.CancelAgent(agentRunId);\n }\n\n /**\n * Updates the poll interval\n * @param milliseconds The new poll interval in milliseconds\n */\n SetPollInterval(milliseconds: number): void {\n this.pollInterval = milliseconds;\n\n // Restart polling if currently active, on the same conversation and scope\n if (this.pollSubscription && this.currentUser) {\n this.StartPolling(this.currentUser, this.pollConversationId, this.pollScope);\n }\n }\n\n /** @deprecated Use {@link SetPollInterval}. */\n setPollInterval(milliseconds: number): void {\n return this.SetPollInterval(milliseconds);\n }\n}\n"]}
1
+ {"version":3,"file":"agent-state.service.js","sourceRoot":"","sources":["../../../src/lib/services/agent-state.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable, interval, Subscription } from 'rxjs';\nimport { map, shareReplay, switchMap } from 'rxjs/operators';\nimport { LogStatusEx, RunView, UserInfo, Metadata, IMetadataProvider } from '@memberjunction/core';\nimport { EscapeSQLString, UUIDsEqual } from '@memberjunction/global';\nimport { ConversationEngine, MJAIAgentRunEntity, type ConversationScope } from '@memberjunction/core-entities';\n\nexport type AgentStatus = 'acknowledging' | 'working' | 'completing' | 'completed' | 'error';\n\nexport interface AgentWithStatus {\n run: MJAIAgentRunEntity;\n status: AgentStatus;\n confidence: number | null;\n}\n\n/**\n * Manages agent state and provides real-time updates for active agents\n * Polls for agent status changes and provides reactive streams\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class AgentStateService implements OnDestroy {\n private _activeAgents$ = new BehaviorSubject<AgentWithStatus[]>([]);\n private pollSubscription?: Subscription;\n private currentUser?: UserInfo;\n /** The conversation and scope polling was started with, so a restart (SetPollInterval) keeps them. */\n private pollConversationId?: string;\n private pollScope: ConversationScope | null = null;\n private pollInterval: number = 30000; // Poll every 30 seconds (reduced from 3s to minimize DB load)\n private pollCycleCount = 0;\n /** Minimum poll cycles before allowing auto-stop. Prevents premature shutdown\n * when fire-and-forget ACK returns before the server creates the agent run record. */\n private readonly minimumPollCycles = 3;\n\n // Public observable streams\n public readonly ActiveAgents$ = this._activeAgents$.asObservable();\n\n /** @deprecated Use {@link ActiveAgents$}. */\n public get activeAgents$() {\n return this.ActiveAgents$;\n }\n\n private _provider: IMetadataProvider | null = null;\n\n constructor() {}\n\n /**\n * Set the metadata provider this service should use. When unset, falls back to Metadata.Provider.\n */\n public set Provider(value: IMetadataProvider | null) {\n this._provider = value;\n }\n\n public get Provider(): IMetadataProvider {\n return this._provider ?? Metadata.Provider;\n }\n\n ngOnDestroy(): void {\n this.StopPolling();\n }\n\n /**\n * Starts polling for active agents\n * @param currentUser The current user context\n * @param conversationId Optional conversation ID to filter by\n * @param scope The conversation scope whose runs are read; the trunk of `conversationId` when null\n */\n StartPolling(currentUser: UserInfo, conversationId?: string, scope: ConversationScope | null = null): void {\n this.currentUser = currentUser;\n this.StopPolling();\n this.pollConversationId = conversationId;\n this.pollScope = scope;\n this.pollCycleCount = 0;\n\n // Initial load\n this.loadActiveAgents(conversationId, scope);\n\n // Start polling\n this.pollSubscription = interval(this.pollInterval)\n .pipe(switchMap(() => this.loadActiveAgents(conversationId, scope)))\n .subscribe();\n }\n\n /** @deprecated Use {@link StartPolling}. */\n startPolling(currentUser: UserInfo, conversationId?: string, scope: ConversationScope | null = null): void {\n return this.StartPolling(currentUser, conversationId, scope);\n }\n\n /**\n * Stops polling for active agents\n */\n StopPolling(): void {\n if (this.pollSubscription) {\n this.pollSubscription.unsubscribe();\n this.pollSubscription = undefined;\n }\n }\n\n /** @deprecated Use {@link StopPolling}. */\n stopPolling(): void {\n return this.StopPolling();\n }\n\n /**\n * Gets active agents as an observable\n * @param conversationId Optional conversation ID to filter by\n */\n GetActiveAgents(conversationId?: string): Observable<AgentWithStatus[]> {\n if (conversationId) {\n return this.ActiveAgents$.pipe(\n map(agents => agents.filter(a => UUIDsEqual(a.run.ConversationID, conversationId))),\n shareReplay(1)\n );\n }\n return this.ActiveAgents$;\n }\n\n /** @deprecated Use {@link GetActiveAgents}. */\n getActiveAgents(conversationId?: string): Observable<AgentWithStatus[]> {\n return this.GetActiveAgents(conversationId);\n }\n\n /**\n * Gets a specific agent by ID\n * @param agentRunId The agent run ID\n */\n GetAgent(agentRunId: string): AgentWithStatus | undefined {\n return this._activeAgents$.value.find(a => UUIDsEqual(a.run.ID, agentRunId));\n }\n\n /** @deprecated Use {@link GetAgent}. */\n getAgent(agentRunId: string): AgentWithStatus | undefined {\n return this.GetAgent(agentRunId);\n }\n\n /**\n * Manually refreshes active agents\n * @param conversationId Optional conversation ID to filter by\n * @param scope The conversation scope whose runs are read; the trunk of `conversationId` when null\n */\n async Refresh(conversationId?: string, scope: ConversationScope | null = null): Promise<void> {\n await this.loadActiveAgents(conversationId, scope);\n }\n\n /** @deprecated Use {@link Refresh}. */\n async refresh(conversationId?: string, scope: ConversationScope | null = null): Promise<void> {\n return this.Refresh(conversationId, scope);\n }\n\n /**\n * Loads the running and paused agent runs from the database. With a conversation, only runs\n * whose conversation detail is in `scope` (the trunk of `conversationId` when null), plus the\n * conversation's runs that have no conversation detail, which belong to every path.\n */\n private async loadActiveAgents(conversationId?: string, scope: ConversationScope | null = null): Promise<void> {\n if (!this.currentUser) {\n return;\n }\n\n this.pollCycleCount++;\n const timestamp = new Date().toISOString();\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService.loadActiveAgents - Polling for active agents (conversation: ${conversationId || 'ALL'}, cycle: ${this.pollCycleCount})`, verboseOnly: true});\n\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n // Valid statuses: Running, Completed, Paused, Failed, Cancelled\n let filter = `Status IN ('Running', 'Paused')`;\n\n if (conversationId) {\n const inScope = ConversationEngine.ScopeSubquery(scope ?? ConversationEngine.TrunkScope(conversationId));\n filter += ` AND (${inScope} OR ([ConversationID]='${EscapeSQLString(conversationId)}' AND [ConversationDetailID] IS NULL))`;\n }\n\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService - Executing RunView for AI Agent Runs`, verboseOnly: true});\n const result = await rv.RunView<MJAIAgentRunEntity>(\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: filter,\n OrderBy: 'StartedAt DESC',\n MaxRows: 50,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n if (result.Success) {\n const runs = result.Results || [];\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService - Found ${runs.length} active agent(s)`, verboseOnly: true});\n const agentsWithStatus = runs.map(run => this.mapRunToAgentWithStatus(run));\n this._activeAgents$.next(agentsWithStatus);\n\n // Stop polling if no active agents AND minimum cycles met (optimization to reduce DB load).\n // The minimum cycle guard prevents premature shutdown when fire-and-forget returns\n // before the server has created the agent run record.\n if (runs.length === 0 && this.pollSubscription && this.pollCycleCount >= this.minimumPollCycles) {\n LogStatusEx({message: `[${timestamp}] 🤖 AgentStateService - No active agents after ${this.pollCycleCount} cycles, stopping polling`, verboseOnly: true});\n this.StopPolling();\n }\n }\n } catch (error) {\n console.error('Failed to load active agents:', error);\n }\n }\n\n /**\n * Maps an agent run to include status and confidence information\n */\n private mapRunToAgentWithStatus(run: MJAIAgentRunEntity): AgentWithStatus {\n const status = this.determineAgentStatus(run);\n const confidence = this.extractConfidence(run);\n\n return {\n run,\n status,\n confidence\n };\n }\n\n /**\n * Determines the agent status based on the run entity\n */\n private determineAgentStatus(run: MJAIAgentRunEntity): AgentStatus {\n const status = run.Status?.toLowerCase() || 'running';\n\n // Check for errors\n if (status === 'error' || status === 'failed') {\n return 'error';\n }\n\n // Check for completion\n if (status === 'completed' || status === 'complete' || status === 'success') {\n return 'completed';\n }\n\n // Determine stage based on progress or elapsed time\n if (run.StartedAt) {\n const elapsed = Date.now() - new Date(run.StartedAt).getTime();\n const seconds = elapsed / 1000;\n\n // First 5 seconds: acknowledging\n if (seconds < 5) {\n return 'acknowledging';\n }\n\n // A run that is no longer Running has reached its final block\n if (run.Status !== 'Running') {\n return 'completing';\n }\n\n // Otherwise, working\n return 'working';\n }\n\n // Default to acknowledging if just started\n return 'acknowledging';\n }\n\n /**\n * Extracts confidence score from agent run if available\n */\n private extractConfidence(run: MJAIAgentRunEntity): number | null {\n // Try to parse confidence from the run payload (FinalPayload, or Result on older rows)\n // This is a placeholder - adjust based on actual data structure\n try {\n const payload = run.FinalPayload ?? run.Result;\n if (payload) {\n const result: { confidence?: number | string } | null = JSON.parse(payload);\n if (result?.confidence != null) {\n return parseFloat(String(result.confidence));\n }\n }\n } catch {\n // Ignore parse errors\n }\n\n return null;\n }\n\n /**\n * Stops an agent run that is still in flight.\n *\n * The run's row is the control channel: this marks it `Cancelled` with reason `User Request`,\n * and the server process that owns the run notices within a few seconds (the agent-run\n * watchdog's cancellation poll), aborts the in-flight step through the run's cancellation\n * token, and finalizes the run — keeping the payload as it stood — and its reply as stopped.\n * No request reaches the running process directly, so this works whichever replica owns it.\n *\n * The row is loaded fresh rather than taken from the polled active-agent list, which refreshes\n * every 30 seconds and may not hold a run that started a moment ago.\n *\n * @returns true when the row was marked; false when the run was not found, had already\n * finished (nothing to stop), or the save was refused.\n */\n async CancelAgent(agentRunId: string): Promise<boolean> {\n try {\n const run = await this.Provider.GetEntityObject<MJAIAgentRunEntity>('MJ: AI Agent Runs', this.currentUser);\n if (!await run.Load(agentRunId)) {\n return false;\n }\n if (run.Status !== 'Running' && run.Status !== 'Paused') {\n return false;\n }\n\n run.Status = 'Cancelled';\n run.CancellationReason = 'User Request';\n const saved = await run.Save();\n if (!saved) {\n console.error(`Failed to stop agent run ${agentRunId}: ${run.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n return false;\n }\n\n await this.Refresh();\n return true;\n } catch (error) {\n console.error('Failed to cancel agent:', error);\n return false;\n }\n }\n\n /**\n * Stops the in-flight run behind an agent's reply message, for callers that know the\n * conversation detail but not yet the run (the run row is created a moment after the reply\n * placeholder). Finds the newest Running or Paused run for that detail and stops it.\n *\n * @returns true when a run was found and marked; false otherwise.\n */\n async CancelAgentForDetail(conversationDetailId: string): Promise<boolean> {\n try {\n const rv = RunView.FromMetadataProvider(this.Provider);\n const result = await rv.RunView<{ ID: string }>(\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `ConversationDetailID='${conversationDetailId}' AND Status IN ('Running', 'Paused')`,\n OrderBy: 'StartedAt DESC',\n MaxRows: 1,\n Fields: ['ID'],\n ResultType: 'simple'\n },\n this.currentUser\n );\n const runId = result.Success ? result.Results?.[0]?.ID : undefined;\n if (!runId) {\n return false;\n }\n return await this.CancelAgent(runId);\n } catch (error) {\n console.error('Failed to find the agent run to cancel:', error);\n return false;\n }\n }\n\n /** @deprecated Use {@link CancelAgent}. */\n async cancelAgent(agentRunId: string): Promise<boolean> {\n return this.CancelAgent(agentRunId);\n }\n\n /**\n * Updates the poll interval\n * @param milliseconds The new poll interval in milliseconds\n */\n SetPollInterval(milliseconds: number): void {\n this.pollInterval = milliseconds;\n\n // Restart polling if currently active, on the same conversation and scope\n if (this.pollSubscription && this.currentUser) {\n this.StartPolling(this.currentUser, this.pollConversationId, this.pollScope);\n }\n }\n\n /** @deprecated Use {@link SetPollInterval}. */\n setPollInterval(milliseconds: number): void {\n return this.SetPollInterval(milliseconds);\n }\n}\n"]}
@@ -4,9 +4,9 @@ import { type DisplayCaptureOptions, type DisplayCaptureResult, type ILocalMedia
4
4
  * The browser's implementation of the realtime runtime's media seam.
5
5
  *
6
6
  * Everything here is the half of a realtime session that genuinely *is* platform-specific:
7
- * how the microphone is requested, and how captured audio becomes bytes we can upload. The
8
- * orchestration around it — mint, connect, transcripts, tools, narration, channels, teardown —
9
- * lives in `@memberjunction/realtime-runtime` and is identical on every host.
7
+ * how the microphone is requested, and how captured audio becomes Opus packets. The
8
+ * orchestration around it — mint, connect, transcripts, tools, narration, channels, shard
9
+ * upload, teardown — lives in `@memberjunction/realtime-runtime` and is identical on every host.
10
10
  */
11
11
  export declare class BrowserRealtimeMediaHost implements IRealtimeMediaHost {
12
12
  /**
@@ -26,10 +26,10 @@ export declare class BrowserRealtimeMediaHost implements IRealtimeMediaHost {
26
26
  */
27
27
  RequestDisplayCapture(options?: DisplayCaptureOptions): Promise<DisplayCaptureResult>;
28
28
  /**
29
- * Wraps the browser recorder so the runtime never sees a `Blob`.
30
- *
31
- * Returns `null` when the platform cannot record, which the runtime treats as "recording is
32
- * simply off" rather than an error — recording is consent-gated and optional by design.
29
+ * A fresh, unstarted 48 kHz Opus recorder for this call. {@link RealtimeAudioRecorder}
30
+ * implements the runtime's recorder contract itself (packets out, sample count back), so no
31
+ * adapter sits in between. When the browser cannot record, the recorder disables itself on
32
+ * Start and the runtime sees `IsRecording === false`, which it treats as "recording is off".
33
33
  */
34
34
  CreateRecorder(): IRealtimeSessionRecorder | null;
35
35
  }
@@ -4,9 +4,9 @@ import { RealtimeAudioRecorder } from './realtime-audio-recorder';
4
4
  * The browser's implementation of the realtime runtime's media seam.
5
5
  *
6
6
  * Everything here is the half of a realtime session that genuinely *is* platform-specific:
7
- * how the microphone is requested, and how captured audio becomes bytes we can upload. The
8
- * orchestration around it — mint, connect, transcripts, tools, narration, channels, teardown —
9
- * lives in `@memberjunction/realtime-runtime` and is identical on every host.
7
+ * how the microphone is requested, and how captured audio becomes Opus packets. The
8
+ * orchestration around it — mint, connect, transcripts, tools, narration, channels, shard
9
+ * upload, teardown — lives in `@memberjunction/realtime-runtime` and is identical on every host.
10
10
  */
11
11
  export class BrowserRealtimeMediaHost {
12
12
  /**
@@ -32,83 +32,13 @@ export class BrowserRealtimeMediaHost {
32
32
  return RequestDisplayCapture(options);
33
33
  }
34
34
  /**
35
- * Wraps the browser recorder so the runtime never sees a `Blob`.
36
- *
37
- * Returns `null` when the platform cannot record, which the runtime treats as "recording is
38
- * simply off" rather than an error — recording is consent-gated and optional by design.
35
+ * A fresh, unstarted 48 kHz Opus recorder for this call. {@link RealtimeAudioRecorder}
36
+ * implements the runtime's recorder contract itself (packets out, sample count back), so no
37
+ * adapter sits in between. When the browser cannot record, the recorder disables itself on
38
+ * Start and the runtime sees `IsRecording === false`, which it treats as "recording is off".
39
39
  */
40
40
  CreateRecorder() {
41
- return new BrowserRealtimeSessionRecorder(new RealtimeAudioRecorder());
42
- }
43
- }
44
- /**
45
- * Adapts {@link RealtimeAudioRecorder} (which speaks `Blob`, because that is what `MediaRecorder`
46
- * and the Web Audio API hand you) to the runtime's byte-agnostic recorder contract.
47
- *
48
- * The base64 encoding that used to sit in the session service lives here now. That is the correct
49
- * home for it: a browser reaches for `FileReader`, React Native reads a file off disk, and a test
50
- * harness holds bytes in memory — three different answers to a question the orchestration layer
51
- * should never have been asking.
52
- */
53
- class BrowserRealtimeSessionRecorder {
54
- recorder;
55
- constructor(recorder) {
56
- this.recorder = recorder;
57
- }
58
- get IsRecording() {
59
- return this.recorder.IsRecording;
60
- }
61
- get SampleRate() {
62
- return this.recorder.SampleRate;
63
- }
64
- get MimeType() {
65
- return this.recorder.MimeType;
66
- }
67
- Start(micStream, remoteStream) {
68
- this.recorder.Start(micStream, remoteStream);
69
- }
70
- AttachRemoteStream(stream) {
71
- this.recorder.AttachRemoteStream(stream);
72
- }
73
- ReplaceMicrophone(micStream) {
74
- this.recorder.ReplaceMicrophone(micStream);
75
- }
76
- NowOffsetMs() {
77
- return this.recorder.NowOffsetMs();
78
- }
79
- GetPeaks() {
80
- return this.recorder.GetPeaks();
81
- }
82
- async SnapshotNewSegmentBase64() {
83
- const segment = this.recorder.SnapshotNewSegment();
84
- if (!segment || segment.size === 0) {
85
- return null;
86
- }
87
- return this.blobToBase64(segment);
88
- }
89
- async StopAndEncode() {
90
- const blob = await this.recorder.Stop();
91
- if (!blob || blob.size === 0) {
92
- return null;
93
- }
94
- return this.blobToBase64(blob);
95
- }
96
- /**
97
- * Encodes a {@link Blob} as base64 with the `data:` prefix stripped, via {@link FileReader}.
98
- * Resolves to `null` rather than rejecting — a failed encode costs us the recording, never
99
- * the live call.
100
- */
101
- blobToBase64(blob) {
102
- return new Promise((resolve) => {
103
- const reader = new FileReader();
104
- reader.onloadend = () => {
105
- const result = typeof reader.result === 'string' ? reader.result : '';
106
- const comma = result.indexOf(',');
107
- resolve(comma >= 0 ? result.slice(comma + 1) : null);
108
- };
109
- reader.onerror = () => resolve(null);
110
- reader.readAsDataURL(blob);
111
- });
41
+ return new RealtimeAudioRecorder();
112
42
  }
113
43
  }
114
44
  //# sourceMappingURL=browser-realtime-media-host.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser-realtime-media-host.js","sourceRoot":"","sources":["../../../src/lib/services/browser-realtime-media-host.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n IRealtimeMediaHost,\n IRealtimeSessionRecorder,\n} from '@memberjunction/realtime-runtime';\nimport {\n type DisplayCaptureOptions,\n type DisplayCaptureResult,\n type ILocalMediaController,\n LocalMediaController,\n RequestDisplayCapture,\n} from '@memberjunction/ai-realtime-client/media';\nimport { RealtimeAudioRecorder } from './realtime-audio-recorder';\n\n/**\n * The browser's implementation of the realtime runtime's media seam.\n *\n * Everything here is the half of a realtime session that genuinely *is* platform-specific:\n * how the microphone is requested, and how captured audio becomes bytes we can upload. The\n * orchestration around it — mint, connect, transcripts, tools, narration, channels, teardown —\n * lives in `@memberjunction/realtime-runtime` and is identical on every host.\n */\nexport class BrowserRealtimeMediaHost implements IRealtimeMediaHost {\n /**\n * Requests the microphone through the standard Web API, which surfaces the browser's own\n * permission prompt. A denial rejects, and the runtime treats that as a failed session start.\n * The runtime uses {@link CreateLocalMediaController} instead when it is present, as it is here.\n */\n public async AcquireMicrophone(): Promise<MediaStream> {\n return navigator.mediaDevices.getUserMedia({ audio: true });\n }\n\n /**\n * The browser's camera-and-microphone controller. The runtime starts the microphone through it,\n * so a call follows a device switch and keeps going when a headset is unplugged.\n */\n public CreateLocalMediaController(): ILocalMediaController {\n return new LocalMediaController();\n }\n\n /**\n * Opens the browser's share picker for the session's screen share: a screen, window or tab, or one panel of\n * this page where the browser can narrow a capture to it.\n */\n public RequestDisplayCapture(options?: DisplayCaptureOptions): Promise<DisplayCaptureResult> {\n return RequestDisplayCapture(options);\n }\n\n /**\n * Wraps the browser recorder so the runtime never sees a `Blob`.\n *\n * Returns `null` when the platform cannot record, which the runtime treats as \"recording is\n * simply off\" rather than an error — recording is consent-gated and optional by design.\n */\n public CreateRecorder(): IRealtimeSessionRecorder | null {\n return new BrowserRealtimeSessionRecorder(new RealtimeAudioRecorder());\n }\n}\n\n/**\n * Adapts {@link RealtimeAudioRecorder} (which speaks `Blob`, because that is what `MediaRecorder`\n * and the Web Audio API hand you) to the runtime's byte-agnostic recorder contract.\n *\n * The base64 encoding that used to sit in the session service lives here now. That is the correct\n * home for it: a browser reaches for `FileReader`, React Native reads a file off disk, and a test\n * harness holds bytes in memory — three different answers to a question the orchestration layer\n * should never have been asking.\n */\nclass BrowserRealtimeSessionRecorder implements IRealtimeSessionRecorder {\n constructor(private readonly recorder: RealtimeAudioRecorder) {}\n\n public get IsRecording(): boolean {\n return this.recorder.IsRecording;\n }\n\n public get SampleRate(): number {\n return this.recorder.SampleRate;\n }\n\n public get MimeType(): string {\n return this.recorder.MimeType;\n }\n\n public Start(micStream: MediaStream, remoteStream: MediaStream | null): void {\n this.recorder.Start(micStream, remoteStream);\n }\n\n public AttachRemoteStream(stream: MediaStream): void {\n this.recorder.AttachRemoteStream(stream);\n }\n\n public ReplaceMicrophone(micStream: MediaStream): void {\n this.recorder.ReplaceMicrophone(micStream);\n }\n\n public NowOffsetMs(): number {\n return this.recorder.NowOffsetMs();\n }\n\n public GetPeaks(): number[] {\n return this.recorder.GetPeaks();\n }\n\n public async SnapshotNewSegmentBase64(): Promise<string | null> {\n const segment = this.recorder.SnapshotNewSegment();\n if (!segment || segment.size === 0) {\n return null;\n }\n return this.blobToBase64(segment);\n }\n\n public async StopAndEncode(): Promise<string | null> {\n const blob = await this.recorder.Stop();\n if (!blob || blob.size === 0) {\n return null;\n }\n return this.blobToBase64(blob);\n }\n\n /**\n * Encodes a {@link Blob} as base64 with the `data:` prefix stripped, via {@link FileReader}.\n * Resolves to `null` rather than rejecting — a failed encode costs us the recording, never\n * the live call.\n */\n private blobToBase64(blob: Blob): Promise<string | null> {\n return new Promise<string | null>((resolve) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const result = typeof reader.result === 'string' ? reader.result : '';\n const comma = result.indexOf(',');\n resolve(comma >= 0 ? result.slice(comma + 1) : null);\n };\n reader.onerror = () => resolve(null);\n reader.readAsDataURL(blob);\n });\n }\n}\n"]}
1
+ {"version":3,"file":"browser-realtime-media-host.js","sourceRoot":"","sources":["../../../src/lib/services/browser-realtime-media-host.ts"],"names":[],"mappings":"AAIA,OAAO,EAIH,oBAAoB,EACpB,qBAAqB,GACxB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,OAAO,wBAAwB;IACjC;;;;OAIG;IACI,KAAK,CAAC,iBAAiB;QAC1B,OAAO,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IAED;;;OAGG;IACI,0BAA0B;QAC7B,OAAO,IAAI,oBAAoB,EAAE,CAAC;IACtC,CAAC;IAED;;;OAGG;IACI,qBAAqB,CAAC,OAA+B;QACxD,OAAO,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACI,cAAc;QACjB,OAAO,IAAI,qBAAqB,EAAE,CAAC;IACvC,CAAC;CACJ","sourcesContent":["import {\n IRealtimeMediaHost,\n IRealtimeSessionRecorder,\n} from '@memberjunction/realtime-runtime';\nimport {\n type DisplayCaptureOptions,\n type DisplayCaptureResult,\n type ILocalMediaController,\n LocalMediaController,\n RequestDisplayCapture,\n} from '@memberjunction/ai-realtime-client/media';\nimport { RealtimeAudioRecorder } from './realtime-audio-recorder';\n\n/**\n * The browser's implementation of the realtime runtime's media seam.\n *\n * Everything here is the half of a realtime session that genuinely *is* platform-specific:\n * how the microphone is requested, and how captured audio becomes Opus packets. The\n * orchestration around it — mint, connect, transcripts, tools, narration, channels, shard\n * upload, teardown — lives in `@memberjunction/realtime-runtime` and is identical on every host.\n */\nexport class BrowserRealtimeMediaHost implements IRealtimeMediaHost {\n /**\n * Requests the microphone through the standard Web API, which surfaces the browser's own\n * permission prompt. A denial rejects, and the runtime treats that as a failed session start.\n * The runtime uses {@link CreateLocalMediaController} instead when it is present, as it is here.\n */\n public async AcquireMicrophone(): Promise<MediaStream> {\n return navigator.mediaDevices.getUserMedia({ audio: true });\n }\n\n /**\n * The browser's camera-and-microphone controller. The runtime starts the microphone through it,\n * so a call follows a device switch and keeps going when a headset is unplugged.\n */\n public CreateLocalMediaController(): ILocalMediaController {\n return new LocalMediaController();\n }\n\n /**\n * Opens the browser's share picker for the session's screen share: a screen, window or tab, or one panel of\n * this page where the browser can narrow a capture to it.\n */\n public RequestDisplayCapture(options?: DisplayCaptureOptions): Promise<DisplayCaptureResult> {\n return RequestDisplayCapture(options);\n }\n\n /**\n * A fresh, unstarted 48 kHz Opus recorder for this call. {@link RealtimeAudioRecorder}\n * implements the runtime's recorder contract itself (packets out, sample count back), so no\n * adapter sits in between. When the browser cannot record, the recorder disables itself on\n * Start and the runtime sees `IsRecording === false`, which it treats as \"recording is off\".\n */\n public CreateRecorder(): IRealtimeSessionRecorder | null {\n return new RealtimeAudioRecorder();\n }\n}\n"]}
@@ -230,7 +230,7 @@ export declare class ConversationAgentService {
230
230
  * plausible, and wrong: the same failure class as {@link FindLatestAgentOutputVersion},
231
231
  * closed the same way.
232
232
  *
233
- * Two reads, not the four the old shape would have needed — see
233
+ * Three reads: the agent's replies, their Output versions, and the artifacts' names. See
234
234
  * {@link AgentArtifactSummary} for why dropping `runId` removes the join back to agent runs.
235
235
  *
236
236
  * @param historyFrom The chat's history floor, when set (see {@link FindAgentArtifacts}).
@@ -249,7 +249,7 @@ export declare class ConversationAgentService {
249
249
  * again" exchanges where payload continuity matters most. A null payload is a LEGAL agent
250
250
  * input, so the failure is silent: the agent regenerates from scratch instead of modifying.
251
251
  *
252
- * One round trip. The subquery walks details -> Output junctions, and versions carry
252
+ * Two reads: the agent's replies, then their newest Output version. Versions carry
253
253
  * `__mj_CreatedAt`, so "newest" is expressible without joining back for `Sequence`.
254
254
  *
255
255
  * @param historyFrom The chat's history floor. When set, only artifacts on replies written at
@@ -261,18 +261,26 @@ export declare class ConversationAgentService {
261
261
  FindLatestAgentOutputVersion(conversationId: string, agentId: string, historyFrom?: Date | null, branchId?: string | null): Promise<AgentPayloadSource | null>;
262
262
  /**
263
263
  * `ArtifactVersion.ID IN (...)` for every OUTPUT artifact an agent produced on one
264
- * conversation path, in messages no rerun replaced. Shared by {@link FindLatestAgentOutputVersion} and
265
- * {@link findAllAgentArtifacts} so the two can never disagree about what "this agent's
266
- * artifacts" means.
264
+ * conversation path, in messages no rerun replaced, or null when the agent has no such reply
265
+ * there. Shared by {@link FindLatestAgentOutputVersion} and {@link findAllAgentArtifacts} so
266
+ * the two can never disagree about what "this agent's artifacts" means.
267
+ *
268
+ * The replies are read with their own RunView ({@link agentReplyIds}), so the row-level
269
+ * security on conversation messages applies. A subquery over their view would skip it, and the
270
+ * server refuses one for a user whose read of messages is row-filtered.
271
+ */
272
+ private agentOutputVersionFilter;
273
+ /**
274
+ * IDs of the agent's replies on one conversation path, in messages no rerun replaced, written
275
+ * at or after the history floor when one is set. Empty when the path cannot be built
276
+ * ({@link branchPathFilter}) or the read fails.
267
277
  *
268
278
  * Filters on the DETAIL's `Status <> 'Error'` rather than the agent run's
269
279
  * `Status = 'Completed'` (which is what the array scan this replaced used). Doing it on the
270
280
  * run would mean joining back to `MJ: AI Agent Runs` purely to restate a condition the
271
281
  * detail already carries.
272
- *
273
- * @param pathFilter The conversation-and-branch predicate from {@link branchPathFilter}.
274
282
  */
275
- private agentOutputVersionFilter;
283
+ private agentReplyIds;
276
284
  /**
277
285
  * The SQL predicate for one branch's path in a conversation, from
278
286
  * `ConversationEngine.BranchPathFilterFresh`. A null or omitted branch is the trunk and costs
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-agent.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/conversation-agent.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversation-agent.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/conversation-agent.service.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ import { AIEngineBase } from '@memberjunction/ai-engine-base';
6
6
  import { ConversationEngine } from '@memberjunction/core-entities';
7
7
  import { MJNotificationService } from '@memberjunction/ng-notifications';
8
8
  import { ConversationsRuntime } from '@memberjunction/conversations-runtime';
9
- import { UUIDsEqual } from '@memberjunction/global';
9
+ import { EscapeSQLString, UUIDsEqual } from '@memberjunction/global';
10
10
  import { GroupVersionsByArtifact } from '../utils/agent-artifact-summary';
11
11
  import * as i0 from "@angular/core";
12
12
  import * as i1 from "./conversations-runtime-bootstrap.service";
@@ -115,6 +115,11 @@ export class ConversationAgentService {
115
115
  if (this._conversationManagerAgent) {
116
116
  return this._conversationManagerAgent;
117
117
  }
118
+ // A user who may not read the agents has no manager agent to resolve: say nothing, rather than an error for a state
119
+ // that is theirs by design (MJ#5240)
120
+ if (AIEngineBase.Instance.IsPermissionConstrained && AIEngineBase.Instance.ReadableAgents.length === 0) {
121
+ return null;
122
+ }
118
123
  try {
119
124
  const provider = this.Provider;
120
125
  const agent = await ConversationsRuntime.Instance.DefaultAgent.resolve({
@@ -243,7 +248,7 @@ export class ConversationAgentService {
243
248
  if (!message) {
244
249
  return null;
245
250
  }
246
- const parsed = this.mentionParser.parseMentions(message, AIEngineBase.Instance.Agents, []);
251
+ const parsed = this.mentionParser.parseMentions(message, AIEngineBase.Instance.ReadableAgents, []);
247
252
  const mention = parsed.agentMention;
248
253
  return mention?.id === agentId && mention.configurationId ? mention.configurationId : null;
249
254
  }
@@ -288,7 +293,7 @@ export class ConversationAgentService {
288
293
  // Ensure AIEngineBase is configured
289
294
  await AIEngineBase.Instance.Config(false);
290
295
  // Find the agent by name
291
- const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
296
+ const agent = AIEngineBase.Instance.ReadableAgents.find(a => a.Name === agentName);
292
297
  if (!agent || !agent.ID) {
293
298
  const errorMsg = `Sub-agent "${agentName}" not found`;
294
299
  console.warn(`${errorMsg}`);
@@ -383,7 +388,7 @@ export class ConversationAgentService {
383
388
  console.warn('⚠️ Check Sage Intent prompt not found, defaulting to UNSURE');
384
389
  return { decision: 'UNSURE', reasoning: 'Check Sage Intent prompt not found' };
385
390
  }
386
- const agent = AIEngineBase.Instance.Agents.find(a => UUIDsEqual(a.ID, agentId));
391
+ const agent = AIEngineBase.Instance.ReadableAgents.find(a => UUIDsEqual(a.ID, agentId));
387
392
  if (!agent) {
388
393
  console.warn('⚠️ Previous agent not found, defaulting to UNSURE');
389
394
  return { decision: 'UNSURE', reasoning: 'Previous agent not found' };
@@ -510,21 +515,21 @@ ${compactHistory}${artifactContext}
510
515
  * plausible, and wrong: the same failure class as {@link FindLatestAgentOutputVersion},
511
516
  * closed the same way.
512
517
  *
513
- * Two reads, not the four the old shape would have needed — see
518
+ * Three reads: the agent's replies, their Output versions, and the artifacts' names. See
514
519
  * {@link AgentArtifactSummary} for why dropping `runId` removes the join back to agent runs.
515
520
  *
516
521
  * @param historyFrom The chat's history floor, when set (see {@link FindAgentArtifacts}).
517
522
  * @param branchId The branch whose path to read; null or omitted reads the trunk.
518
523
  */
519
524
  async findAllAgentArtifacts(conversationId, agentId, historyFrom, branchId) {
520
- const pathFilter = await this.branchPathFilter(conversationId, branchId);
521
- if (pathFilter == null) {
525
+ const versionFilter = await this.agentOutputVersionFilter(conversationId, agentId, historyFrom, branchId);
526
+ if (!versionFilter) {
522
527
  return []; // the classifier degrades to no artifact context
523
528
  }
524
529
  const rv = RunView.FromMetadataProvider(this.Provider);
525
530
  const versionResult = await rv.RunView({
526
531
  EntityName: 'MJ: Artifact Versions',
527
- ExtraFilter: this.agentOutputVersionFilter(pathFilter, agentId, historyFrom),
532
+ ExtraFilter: versionFilter,
528
533
  OrderBy: '__mj_CreatedAt DESC',
529
534
  MaxRows: MAX_AGENT_ARTIFACT_VERSIONS,
530
535
  Fields: ['ID', 'ArtifactID', 'VersionNumber', 'Name'],
@@ -566,7 +571,7 @@ ${compactHistory}${artifactContext}
566
571
  * again" exchanges where payload continuity matters most. A null payload is a LEGAL agent
567
572
  * input, so the failure is silent: the agent regenerates from scratch instead of modifying.
568
573
  *
569
- * One round trip. The subquery walks details -> Output junctions, and versions carry
574
+ * Two reads: the agent's replies, then their newest Output version. Versions carry
570
575
  * `__mj_CreatedAt`, so "newest" is expressible without joining back for `Sequence`.
571
576
  *
572
577
  * @param historyFrom The chat's history floor. When set, only artifacts on replies written at
@@ -576,35 +581,60 @@ ${compactHistory}${artifactContext}
576
581
  * another branch is never the latest here; null or omitted reads the trunk.
577
582
  */
578
583
  async FindLatestAgentOutputVersion(conversationId, agentId, historyFrom, branchId) {
579
- const pathFilter = await this.branchPathFilter(conversationId, branchId);
580
- if (pathFilter == null) {
584
+ const versionFilter = await this.agentOutputVersionFilter(conversationId, agentId, historyFrom, branchId);
585
+ return versionFilter ? this.runPayloadSourceQuery(versionFilter, '__mj_CreatedAt DESC') : null;
586
+ }
587
+ /**
588
+ * `ArtifactVersion.ID IN (...)` for every OUTPUT artifact an agent produced on one
589
+ * conversation path, in messages no rerun replaced, or null when the agent has no such reply
590
+ * there. Shared by {@link FindLatestAgentOutputVersion} and {@link findAllAgentArtifacts} so
591
+ * the two can never disagree about what "this agent's artifacts" means.
592
+ *
593
+ * The replies are read with their own RunView ({@link agentReplyIds}), so the row-level
594
+ * security on conversation messages applies. A subquery over their view would skip it, and the
595
+ * server refuses one for a user whose read of messages is row-filtered.
596
+ */
597
+ async agentOutputVersionFilter(conversationId, agentId, historyFrom, branchId) {
598
+ const replyIds = await this.agentReplyIds(conversationId, agentId, historyFrom, branchId);
599
+ if (replyIds.length === 0) {
581
600
  return null;
582
601
  }
583
- return this.runPayloadSourceQuery(this.agentOutputVersionFilter(pathFilter, agentId, historyFrom), '__mj_CreatedAt DESC');
602
+ const inReplies = replyIds.map(id => `'${EscapeSQLString(id)}'`).join(',');
603
+ return `ID IN (
604
+ SELECT ArtifactVersionID FROM [__mj].[vwConversationDetailArtifacts]
605
+ WHERE Direction='Output' AND ConversationDetailID IN (${inReplies})
606
+ )`;
584
607
  }
585
608
  /**
586
- * `ArtifactVersion.ID IN (...)` for every OUTPUT artifact an agent produced on one
587
- * conversation path, in messages no rerun replaced. Shared by {@link FindLatestAgentOutputVersion} and
588
- * {@link findAllAgentArtifacts} so the two can never disagree about what "this agent's
589
- * artifacts" means.
609
+ * IDs of the agent's replies on one conversation path, in messages no rerun replaced, written
610
+ * at or after the history floor when one is set. Empty when the path cannot be built
611
+ * ({@link branchPathFilter}) or the read fails.
590
612
  *
591
613
  * Filters on the DETAIL's `Status <> 'Error'` rather than the agent run's
592
614
  * `Status = 'Completed'` (which is what the array scan this replaced used). Doing it on the
593
615
  * run would mean joining back to `MJ: AI Agent Runs` purely to restate a condition the
594
616
  * detail already carries.
595
- *
596
- * @param pathFilter The conversation-and-branch predicate from {@link branchPathFilter}.
597
617
  */
598
- agentOutputVersionFilter(pathFilter, agentId, historyFrom) {
618
+ async agentReplyIds(conversationId, agentId, historyFrom, branchId) {
619
+ const pathFilter = await this.branchPathFilter(conversationId, branchId);
620
+ if (pathFilter == null) {
621
+ return [];
622
+ }
599
623
  const floor = historyFrom ? ` AND ${ConversationEngine.HistoryFromFilter(historyFrom)}` : '';
600
- return `ID IN (
601
- SELECT ArtifactVersionID FROM [__mj].[vwConversationDetailArtifacts]
602
- WHERE Direction='Output' AND ConversationDetailID IN (
603
- SELECT ID FROM [__mj].[vwConversationDetails]
604
- WHERE ${ConversationEngine.LiveRowsFilter(pathFilter)} AND AgentID='${agentId}'
605
- AND Role='AI' AND Status <> 'Error'${floor}
606
- )
607
- )`;
624
+ const rv = RunView.FromMetadataProvider(this.Provider);
625
+ const result = await rv.RunView({
626
+ EntityName: 'MJ: Conversation Details',
627
+ ExtraFilter: `${ConversationEngine.LiveRowsFilter(pathFilter)} AND AgentID='${EscapeSQLString(agentId)}'` +
628
+ ` AND Role='AI' AND Status <> 'Error'${floor}`,
629
+ Fields: ['ID'],
630
+ ResultType: 'simple',
631
+ IgnoreMaxRows: true
632
+ }, this.Provider.CurrentUser ?? undefined);
633
+ if (!result.Success) {
634
+ console.error('Failed to load agent replies:', result.ErrorMessage);
635
+ return [];
636
+ }
637
+ return (result.Results || []).map(reply => reply.ID);
608
638
  }
609
639
  /**
610
640
  * The SQL predicate for one branch's path in a conversation, from