@memberjunction/server 6.2.0-edge.3 → 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 (543) hide show
  1. package/README.md +20 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
  5. package/dist/agentSessions/BridgeHostInstance.js +30 -0
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
  7. package/dist/agentSessions/HostInstance.d.ts +75 -11
  8. package/dist/agentSessions/HostInstance.d.ts.map +1 -1
  9. package/dist/agentSessions/HostInstance.js +99 -28
  10. package/dist/agentSessions/HostInstance.js.map +1 -1
  11. package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
  12. package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
  13. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  14. package/dist/agentSessions/SessionJanitor.d.ts +236 -27
  15. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  16. package/dist/agentSessions/SessionJanitor.js +682 -54
  17. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  18. package/dist/agentSessions/SessionManager.d.ts +48 -3
  19. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  20. package/dist/agentSessions/SessionManager.js +112 -9
  21. package/dist/agentSessions/SessionManager.js.map +1 -1
  22. package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
  23. package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
  24. package/dist/agentSessions/SessionRunIDVerifier.js +129 -0
  25. package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
  26. package/dist/agentSessions/index.d.ts +2 -0
  27. package/dist/agentSessions/index.d.ts.map +1 -1
  28. package/dist/agentSessions/index.js +10 -3
  29. package/dist/agentSessions/index.js.map +1 -1
  30. package/dist/apiRoot.d.ts +32 -0
  31. package/dist/apiRoot.d.ts.map +1 -0
  32. package/dist/apiRoot.js +67 -0
  33. package/dist/apiRoot.js.map +1 -0
  34. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  35. package/dist/auth/AuthProviderCatalogRouter.js +25 -0
  36. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  37. package/dist/auth/LoginScreenEngine.d.ts +22 -0
  38. package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
  39. package/dist/auth/LoginScreenEngine.js +57 -0
  40. package/dist/auth/LoginScreenEngine.js.map +1 -0
  41. package/dist/auth/actionAuthorization.d.ts +40 -0
  42. package/dist/auth/actionAuthorization.d.ts.map +1 -0
  43. package/dist/auth/actionAuthorization.js +94 -0
  44. package/dist/auth/actionAuthorization.js.map +1 -0
  45. package/dist/auth/index.d.ts +1 -0
  46. package/dist/auth/index.d.ts.map +1 -1
  47. package/dist/auth/index.js +4 -10
  48. package/dist/auth/index.js.map +1 -1
  49. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  50. package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
  51. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  52. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  53. package/dist/auth/magicLink/MagicLinkService.js +6 -3
  54. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  55. package/dist/auth/newUsers.d.ts +10 -0
  56. package/dist/auth/newUsers.d.ts.map +1 -1
  57. package/dist/auth/newUsers.js +80 -11
  58. package/dist/auth/newUsers.js.map +1 -1
  59. package/dist/config.d.ts +2514 -22
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +166 -4
  62. package/dist/config.js.map +1 -1
  63. package/dist/context.d.ts +8 -0
  64. package/dist/context.d.ts.map +1 -1
  65. package/dist/context.js +14 -11
  66. package/dist/context.js.map +1 -1
  67. package/dist/coreRoutes.d.ts +29 -0
  68. package/dist/coreRoutes.d.ts.map +1 -0
  69. package/dist/coreRoutes.js +142 -0
  70. package/dist/coreRoutes.js.map +1 -0
  71. package/dist/generated/graphql-schemas/__mj.d.ts +504 -3
  72. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  73. package/dist/generated/graphql-schemas/__mj.js +2456 -7
  74. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  75. package/dist/generic/CacheInvalidationResolver.d.ts +43 -8
  76. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  77. package/dist/generic/CacheInvalidationResolver.js +59 -19
  78. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  79. package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
  80. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  81. package/dist/generic/PushStatusResolver.js +1 -1
  82. package/dist/generic/PushStatusResolver.js.map +1 -1
  83. package/dist/generic/ResolverBase.d.ts +123 -48
  84. package/dist/generic/ResolverBase.d.ts.map +1 -1
  85. package/dist/generic/ResolverBase.js +293 -305
  86. package/dist/generic/ResolverBase.js.map +1 -1
  87. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  88. package/dist/generic/RunViewResolver.js +16 -0
  89. package/dist/generic/RunViewResolver.js.map +1 -1
  90. package/dist/globalCors.d.ts +33 -0
  91. package/dist/globalCors.d.ts.map +1 -0
  92. package/dist/globalCors.js +58 -0
  93. package/dist/globalCors.js.map +1 -0
  94. package/dist/index.d.ts +17 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +219 -278
  97. package/dist/index.js.map +1 -1
  98. package/dist/isolatedProvider.d.ts +20 -0
  99. package/dist/isolatedProvider.d.ts.map +1 -0
  100. package/dist/isolatedProvider.js +22 -0
  101. package/dist/isolatedProvider.js.map +1 -0
  102. package/dist/multiTenancy/index.d.ts.map +1 -1
  103. package/dist/multiTenancy/index.js +22 -2
  104. package/dist/multiTenancy/index.js.map +1 -1
  105. package/dist/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
  106. package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
  107. package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
  108. package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
  109. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
  110. package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
  111. package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
  112. package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
  113. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
  114. package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
  115. package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
  116. package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
  117. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
  118. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
  119. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +139 -0
  120. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
  121. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
  122. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
  123. package/dist/realtimeSessions/RealtimeVerifyRouter.js +99 -0
  124. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
  125. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
  126. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
  127. package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
  128. package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
  129. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
  130. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
  131. package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
  132. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
  133. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
  134. package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
  135. package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
  136. package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
  137. package/dist/realtimeSessions/index.d.ts +19 -0
  138. package/dist/realtimeSessions/index.d.ts.map +1 -0
  139. package/dist/realtimeSessions/index.js +19 -0
  140. package/dist/realtimeSessions/index.js.map +1 -0
  141. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +30 -0
  142. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
  143. package/dist/realtimeSessions/ownedRealtimeSession.js +47 -0
  144. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
  145. package/dist/realtimeSessions/sessionAccess.d.ts +92 -0
  146. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
  147. package/dist/realtimeSessions/sessionAccess.js +94 -0
  148. package/dist/realtimeSessions/sessionAccess.js.map +1 -0
  149. package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
  150. package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
  151. package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
  152. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
  153. package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
  154. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
  155. package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
  156. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
  157. package/dist/realtimeSessions/verificationCore.d.ts +243 -0
  158. package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
  159. package/dist/realtimeSessions/verificationCore.js +356 -0
  160. package/dist/realtimeSessions/verificationCore.js.map +1 -0
  161. package/dist/realtimeSessions/verificationPages.d.ts +66 -0
  162. package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
  163. package/dist/realtimeSessions/verificationPages.js +110 -0
  164. package/dist/realtimeSessions/verificationPages.js.map +1 -0
  165. package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
  166. package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
  167. package/dist/realtimeSessions/verificationPolicy.js +249 -0
  168. package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
  169. package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
  170. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
  171. package/dist/realtimeSessions/verificationWorkflow.js +435 -0
  172. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
  173. package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
  174. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  175. package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
  176. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  177. package/dist/resolvers/ActionResolver.d.ts +31 -5
  178. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  179. package/dist/resolvers/ActionResolver.js +75 -14
  180. package/dist/resolvers/ActionResolver.js.map +1 -1
  181. package/dist/resolvers/AdhocQueryResolver.d.ts +14 -1
  182. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  183. package/dist/resolvers/AdhocQueryResolver.js +21 -5
  184. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  185. package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
  186. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
  187. package/dist/resolvers/ArtifactFileResolver.js +9 -7
  188. package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
  189. package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
  190. package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
  191. package/dist/resolvers/CacheStatsResolver.js +113 -2
  192. package/dist/resolvers/CacheStatsResolver.js.map +1 -1
  193. package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
  194. package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
  195. package/dist/resolvers/ClientToolRequestResolver.js +28 -10
  196. package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
  197. package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
  198. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  199. package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
  200. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  201. package/dist/resolvers/DatasetResolver.d.ts +24 -3
  202. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  203. package/dist/resolvers/DatasetResolver.js +57 -8
  204. package/dist/resolvers/DatasetResolver.js.map +1 -1
  205. package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
  206. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  207. package/dist/resolvers/EntityRecordNameResolver.js +3 -2
  208. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  209. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +27 -1
  210. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  211. package/dist/resolvers/FetchEntityVectorsResolver.js +148 -2
  212. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  213. package/dist/resolvers/FileResolver.d.ts +82 -11
  214. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  215. package/dist/resolvers/FileResolver.js +279 -101
  216. package/dist/resolvers/FileResolver.js.map +1 -1
  217. package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
  218. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  219. package/dist/resolvers/GetDataContextDataResolver.js +117 -38
  220. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  221. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  222. package/dist/resolvers/GetDataResolver.js +6 -3
  223. package/dist/resolvers/GetDataResolver.js.map +1 -1
  224. package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
  225. package/dist/resolvers/HumanHandoffResolver.js +7 -1
  226. package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
  227. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  228. package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
  229. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  230. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  231. package/dist/resolvers/MCPResolver.js +9 -4
  232. package/dist/resolvers/MCPResolver.js.map +1 -1
  233. package/dist/resolvers/QueryResolver.d.ts +2 -0
  234. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  235. package/dist/resolvers/QueryResolver.js +13 -0
  236. package/dist/resolvers/QueryResolver.js.map +1 -1
  237. package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
  238. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  239. package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
  240. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  241. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +157 -33
  242. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  243. package/dist/resolvers/RealtimeClientSessionResolver.js +404 -116
  244. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  245. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
  246. package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
  247. package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
  248. package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
  249. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
  250. package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
  251. package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
  252. package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
  253. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
  254. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  255. package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
  256. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  257. package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
  258. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  259. package/dist/resolvers/RunAIAgentResolver.js +100 -14
  260. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  261. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  262. package/dist/resolvers/RunTemplateResolver.js +12 -11
  263. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  264. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
  265. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
  266. package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
  267. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  268. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  269. package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
  270. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  271. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  272. package/dist/resolvers/SyncDataResolver.js +6 -2
  273. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  274. package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
  275. package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
  276. package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
  277. package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
  278. package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
  279. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  280. package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
  281. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  282. package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
  283. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  284. package/dist/resolvers/TestQuerySQLResolver.js +65 -13
  285. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  286. package/dist/resolvers/TransactionGroupResolver.d.ts +19 -2
  287. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  288. package/dist/resolvers/TransactionGroupResolver.js +102 -24
  289. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  290. package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
  291. package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
  292. package/dist/resolvers/UserAvatarResolver.js +124 -0
  293. package/dist/resolvers/UserAvatarResolver.js.map +1 -0
  294. package/dist/resolvers/UserResolver.d.ts.map +1 -1
  295. package/dist/resolvers/UserResolver.js +2 -17
  296. package/dist/resolvers/UserResolver.js.map +1 -1
  297. package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
  298. package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
  299. package/dist/resolvers/agent-run-data-guard.js +72 -0
  300. package/dist/resolvers/agent-run-data-guard.js.map +1 -0
  301. package/dist/resolvers/agentVisionConsentAudit.d.ts +37 -0
  302. package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
  303. package/dist/resolvers/agentVisionConsentAudit.js +63 -0
  304. package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
  305. package/dist/resolvers/avatarInputValidation.d.ts +40 -0
  306. package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
  307. package/dist/resolvers/avatarInputValidation.js +97 -0
  308. package/dist/resolvers/avatarInputValidation.js.map +1 -0
  309. package/dist/resolvers/currentUserRoles.d.ts +29 -0
  310. package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
  311. package/dist/resolvers/currentUserRoles.js +30 -0
  312. package/dist/resolvers/currentUserRoles.js.map +1 -1
  313. package/dist/resolvers/meetings/meetingMutations.d.ts +6 -0
  314. package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
  315. package/dist/resolvers/meetings/meetingMutations.js +21 -0
  316. package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
  317. package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
  318. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
  319. package/dist/rest/RealtimeRecordingHandler.js +383 -0
  320. package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
  321. package/dist/rest/ViewOperationsHandler.d.ts +3 -1
  322. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  323. package/dist/rest/ViewOperationsHandler.js +21 -12
  324. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  325. package/dist/rest/setupRESTEndpoints.d.ts +6 -0
  326. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  327. package/dist/rest/setupRESTEndpoints.js +6 -0
  328. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  329. package/dist/serverExtensionReservedRoots.d.ts +12 -10
  330. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  331. package/dist/serverExtensionReservedRoots.js +14 -17
  332. package/dist/serverExtensionReservedRoots.js.map +1 -1
  333. package/dist/services/MJStorageBlobStore.d.ts +8 -1
  334. package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
  335. package/dist/services/MJStorageBlobStore.js +25 -19
  336. package/dist/services/MJStorageBlobStore.js.map +1 -1
  337. package/dist/services/StartTaskGraphDispatcher.js +2 -2
  338. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  339. package/dist/services/TaskGraphActionRunner.d.ts +5 -3
  340. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  341. package/dist/services/TaskGraphActionRunner.js +27 -5
  342. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  343. package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
  344. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  345. package/dist/services/TaskGraphAgentRunner.js +157 -9
  346. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  347. package/dist/services/TaskGraphContinuationDeliverer.d.ts +57 -4
  348. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  349. package/dist/services/TaskGraphContinuationDeliverer.js +607 -18
  350. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  351. package/dist/sharedCache.d.ts +105 -0
  352. package/dist/sharedCache.d.ts.map +1 -0
  353. package/dist/sharedCache.js +279 -0
  354. package/dist/sharedCache.js.map +1 -0
  355. package/dist/types.d.ts +6 -0
  356. package/dist/types.d.ts.map +1 -1
  357. package/dist/types.js.map +1 -1
  358. package/package.json +99 -96
  359. package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
  360. package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
  361. package/src/__tests__/BridgeHostInstance.test.ts +232 -0
  362. package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
  363. package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
  364. package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
  365. package/src/__tests__/FetchEntityVectorsResolver.test.ts +433 -0
  366. package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
  367. package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
  368. package/src/__tests__/HostInstance.test.ts +84 -0
  369. package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
  370. package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
  371. package/src/__tests__/LoginScreenEngine.test.ts +86 -0
  372. package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
  373. package/src/__tests__/MeetingResolver.test.ts +75 -1
  374. package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
  375. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
  376. package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
  377. package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
  378. package/src/__tests__/RealtimeRelay.test.ts +876 -0
  379. package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
  380. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
  381. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
  382. package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
  383. package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
  384. package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
  385. package/src/__tests__/ResolverBase.clientOldValues.test.ts +212 -6
  386. package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
  387. package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
  388. package/src/__tests__/RunAIAgentResolver.branch.test.ts +170 -0
  389. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +6 -4
  390. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -0
  391. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
  392. package/src/__tests__/RunTemplateResolver.test.ts +132 -0
  393. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
  394. package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
  395. package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
  396. package/src/__tests__/SessionJanitor.test.ts +1203 -21
  397. package/src/__tests__/SessionManager.test.ts +240 -17
  398. package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
  399. package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
  400. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
  401. package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
  402. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +403 -22
  403. package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
  404. package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
  405. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
  406. package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
  407. package/src/__tests__/UserAvatarResolver.test.ts +255 -0
  408. package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
  409. package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
  410. package/src/__tests__/actionAuthorization.test.ts +130 -0
  411. package/src/__tests__/agent-run-data-guard.test.ts +130 -0
  412. package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
  413. package/src/__tests__/apiRootBoot.test.ts +202 -0
  414. package/src/__tests__/avatarInputValidation.test.ts +160 -0
  415. package/src/__tests__/cacheInvalidationRecordData.test.ts +64 -0
  416. package/src/__tests__/conversationRunEvents.test.ts +56 -14
  417. package/src/__tests__/globalCors.test.ts +217 -0
  418. package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
  419. package/src/__tests__/magicLinkService.consume.test.ts +6 -0
  420. package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
  421. package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
  422. package/src/__tests__/multiTenancy.security.test.ts +51 -0
  423. package/src/__tests__/multiTenancy.test.ts +1 -1
  424. package/src/__tests__/newUsers.template.test.ts +151 -0
  425. package/src/__tests__/newUsers.test.ts +74 -5
  426. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
  427. package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
  428. package/src/__tests__/pushStatusResolver.filter.test.ts +7 -0
  429. package/src/__tests__/realtimeRecordingHandler.test.ts +652 -0
  430. package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
  431. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
  432. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
  433. package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
  434. package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
  435. package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
  436. package/src/__tests__/realtimeSessions/helpers.ts +4 -0
  437. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
  438. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
  439. package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
  440. package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
  441. package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
  442. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
  443. package/src/__tests__/resolverBase.fls.test.ts +87 -4
  444. package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
  445. package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -96
  446. package/src/__tests__/sharedCache.test.ts +312 -0
  447. package/src/__tests__/widgetGuestElevation.test.ts +44 -0
  448. package/src/agentSessions/BridgeHostInstance.ts +32 -0
  449. package/src/agentSessions/HostInstance.ts +124 -28
  450. package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
  451. package/src/agentSessions/SessionJanitor.ts +821 -59
  452. package/src/agentSessions/SessionManager.ts +138 -26
  453. package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
  454. package/src/agentSessions/index.ts +10 -3
  455. package/src/apiRoot.ts +78 -0
  456. package/src/auth/AuthProviderCatalogRouter.ts +26 -0
  457. package/src/auth/LoginScreenEngine.ts +50 -0
  458. package/src/auth/actionAuthorization.ts +116 -0
  459. package/src/auth/index.ts +4 -8
  460. package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
  461. package/src/auth/magicLink/MagicLinkService.ts +6 -3
  462. package/src/auth/newUsers.ts +99 -11
  463. package/src/config.ts +171 -5
  464. package/src/context.ts +15 -10
  465. package/src/coreRoutes.ts +166 -0
  466. package/src/generated/graphql-schemas/__mj.ts +1815 -57
  467. package/src/generic/CacheInvalidationResolver.ts +73 -18
  468. package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
  469. package/src/generic/PushStatusResolver.ts +1 -1
  470. package/src/generic/ResolverBase.ts +335 -326
  471. package/src/generic/RunViewResolver.ts +21 -0
  472. package/src/globalCors.ts +71 -0
  473. package/src/index.ts +231 -300
  474. package/src/isolatedProvider.ts +22 -0
  475. package/src/multiTenancy/index.ts +24 -2
  476. package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
  477. package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
  478. package/src/realtimeSessions/README.md +179 -0
  479. package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
  480. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +168 -0
  481. package/src/realtimeSessions/RealtimeVerifyRouter.ts +109 -0
  482. package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
  483. package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
  484. package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
  485. package/src/realtimeSessions/index.ts +18 -0
  486. package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
  487. package/src/realtimeSessions/sessionAccess.ts +145 -0
  488. package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
  489. package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
  490. package/src/realtimeSessions/verificationCore.ts +502 -0
  491. package/src/realtimeSessions/verificationPages.ts +149 -0
  492. package/src/realtimeSessions/verificationPolicy.ts +321 -0
  493. package/src/realtimeSessions/verificationWorkflow.ts +702 -0
  494. package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
  495. package/src/resolvers/ActionResolver.ts +84 -21
  496. package/src/resolvers/AdhocQueryResolver.ts +24 -6
  497. package/src/resolvers/ArtifactFileResolver.ts +9 -8
  498. package/src/resolvers/CacheStatsResolver.ts +89 -2
  499. package/src/resolvers/ClientToolRequestResolver.ts +25 -7
  500. package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
  501. package/src/resolvers/DatasetResolver.ts +70 -10
  502. package/src/resolvers/EntityRecordNameResolver.ts +3 -2
  503. package/src/resolvers/FetchEntityVectorsResolver.ts +179 -2
  504. package/src/resolvers/FileResolver.ts +321 -106
  505. package/src/resolvers/GetDataContextDataResolver.ts +139 -41
  506. package/src/resolvers/GetDataResolver.ts +6 -3
  507. package/src/resolvers/HumanHandoffResolver.ts +8 -0
  508. package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
  509. package/src/resolvers/MCPResolver.ts +8 -5
  510. package/src/resolvers/QueryResolver.ts +11 -0
  511. package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
  512. package/src/resolvers/RealtimeClientSessionResolver.ts +511 -129
  513. package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
  514. package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
  515. package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
  516. package/src/resolvers/RunAIAgentResolver.ts +116 -14
  517. package/src/resolvers/RunTemplateResolver.ts +13 -12
  518. package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
  519. package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
  520. package/src/resolvers/SyncDataResolver.ts +6 -2
  521. package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
  522. package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
  523. package/src/resolvers/TestQuerySQLResolver.ts +68 -13
  524. package/src/resolvers/TransactionGroupResolver.ts +131 -27
  525. package/src/resolvers/UserAvatarResolver.ts +111 -0
  526. package/src/resolvers/UserResolver.ts +2 -16
  527. package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
  528. package/src/resolvers/agent-run-data-guard.ts +89 -0
  529. package/src/resolvers/agentVisionConsentAudit.ts +82 -0
  530. package/src/resolvers/avatarInputValidation.ts +123 -0
  531. package/src/resolvers/currentUserRoles.ts +32 -1
  532. package/src/resolvers/meetings/meetingMutations.ts +21 -0
  533. package/src/rest/RealtimeRecordingHandler.ts +464 -0
  534. package/src/rest/ViewOperationsHandler.ts +29 -13
  535. package/src/rest/setupRESTEndpoints.ts +6 -0
  536. package/src/serverExtensionReservedRoots.ts +14 -17
  537. package/src/services/MJStorageBlobStore.ts +31 -30
  538. package/src/services/StartTaskGraphDispatcher.ts +2 -2
  539. package/src/services/TaskGraphActionRunner.ts +25 -5
  540. package/src/services/TaskGraphAgentRunner.ts +188 -11
  541. package/src/services/TaskGraphContinuationDeliverer.ts +713 -17
  542. package/src/sharedCache.ts +305 -0
  543. package/src/types.ts +7 -0
@@ -23,9 +23,9 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
23
23
  * and **executes** every tool call the browser relays back. The three mutations here cover the MVP:
24
24
  *
25
25
  * 1. {@link RealtimeClientSessionResolver.StartRealtimeClientSession} — authorize, create the
26
- * session (storing the target agent id server-side, authoritatively), mint the ephemeral config.
27
- * 2. {@link RealtimeClientSessionResolver.ExecuteRealtimeSessionTool} — execute a relayed tool call,
28
- * reading the target agent id from the session (NOT the client) so the browser can't swap targets.
26
+ * session (storing the target agent id on it), mint the ephemeral config.
27
+ * 2. {@link RealtimeClientSessionResolver.ExecuteRealtimeSessionTool} — execute a relayed tool call
28
+ * for the target agent stored on the session, re-authorized for the caller on every call.
29
29
  * 3. {@link RealtimeClientSessionResolver.RelayRealtimeTranscript} — persist a transcript turn as a
30
30
  * `Conversation Detail`.
31
31
  *
@@ -33,25 +33,45 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
33
33
  * - **Start**: the caller must have `CanRun` on the *target* agent (the Realtime Co-Agent is an internal
34
34
  * orchestration agent, so the meaningful gate is the agent doing the real work).
35
35
  * - **Execute / Relay**: inbound ownership — the session's `UserID` must equal `contextUser.ID`.
36
+ * - **Session `Config`**: the owner can edit it through the generic entity API, so nothing in it is
37
+ * trusted. Execute re-checks `CanRun` on its target, direct actions never come from it, and its
38
+ * run ids are used only when {@link SessionRunIDVerifier} finds them to be records of the session.
39
+ * - **Scope-limited callers** (anonymous and resource-scoped magic-link sessions, including public
40
+ * web-widget guests): no direct actions. Their scope exists only as row-level filters, which an
41
+ * action can read around, and a scoped anonymous caller's relay runs as the system user. They
42
+ * reach the target agent only through `invoke-target-agent`.
36
43
  *
37
44
  * @module @memberjunction/server
38
45
  */
39
46
  import { Resolver, Mutation, Arg, Ctx, Int, Float, ObjectType, Field, PubSub, PubSubEngine } from 'type-graphql';
40
- import { AuthorizationEvaluator, LogError, LogStatus, RunView } from '@memberjunction/core';
47
+ import { AuthorizationEvaluator, LogError, LogErrorEx, LogStatus, RunView } from '@memberjunction/core';
41
48
  import { UUIDsEqual, IsValidUUID } from '@memberjunction/global';
49
+ import { ConversationEngine, } from '@memberjunction/core-entities';
42
50
  import { AIEngine } from '@memberjunction/aiengine';
43
- import { AIAgentPermissionHelper, AIEngineBase } from '@memberjunction/ai-engine-base';
44
- import { RealtimeClientSessionService, RealtimeChannelServerHost, EvaluateRuntimeOverrideAuthorization, FindIgnoredRealtimeConfigKeys, ParseRealtimeTypeConfiguration, ResolveEffectiveRealtimeConfig, ResolveRealtimeCoAgentID, FilterAllowedAgentsByCanRun, REALTIME_ADVANCED_SESSION_CONTROLS_AUTHORIZATION, resolveRecordingStorageAccountID, storeRealtimeRecording, writeRealtimeRecordingSegment, deleteRealtimeRecordingSegments, } from '@memberjunction/ai-agents';
51
+ import { AIAgentPermissionHelper, AIEngineBase, HasRealtimeUsage, ParseRealtimeUsageRecord } from '@memberjunction/ai-engine-base';
52
+ import { RealtimeClientSessionService, RealtimeChannelServerHost, EvaluateRuntimeOverrideAuthorization, FindIgnoredRealtimeConfigKeys, ParseRealtimeTypeConfiguration, ResolveEffectiveRealtimeConfig, ResolveRealtimeCoAgentID, FilterAllowedAgentsByCanRun, INVOKE_TARGET_AGENT_TOOL_NAME, REALTIME_ADVANCED_SESSION_CONTROLS_AUTHORIZATION, resolveRecordingStorageAccountID, storeRealtimeRecording, writeRealtimeRecordingSegment, deleteRealtimeRecordingSegments, } from '@memberjunction/ai-agents';
53
+ import { IsValidRealtimeToolDefinition, NormalizeChannelKey, ParseRealtimeChannelCandidates } from '@memberjunction/ai-core-plus';
45
54
  import { ResolverBase } from '../generic/ResolverBase.js';
46
55
  import { GetReadWriteProvider } from '../util.js';
47
56
  import { SessionManager } from '../agentSessions/index.js';
48
- import { ResolveWidgetGuestRunContext, ResolveScopedAnonymousRunUser } from '../realtimeWidget/widgetGuestElevation.js';
57
+ import { SessionRunIDVerifier } from '../agentSessions/SessionRunIDVerifier.js';
58
+ import { IsScopeLimitedPrincipal } from '../auth/scopeLimitedPrincipal.js';
59
+ import { ResolveWidgetGuestRunContext, ResolveScopedAnonymousRunUser, ResolveRecordingStoreUser } from '../realtimeWidget/widgetGuestElevation.js';
60
+ import { RunWithTrustedSessionConfigWrites } from '@memberjunction/core-entities-server';
61
+ import { BuildMintConfigStamps, LoadInheritedVerifiedIdentity } from '../realtimeSessions/sessionConfigStamps.js';
62
+ import { ParseConfigObject } from '../realtimeSessions/verificationCore.js';
63
+ import { LoadOwnedRealtimeSession } from '../realtimeSessions/ownedRealtimeSession.js';
49
64
  /**
50
65
  * Progress steps worth narrating to the realtime model — mirrors the normal agent-run path's filter
51
66
  * in {@link import('./RunAIAgentResolver.js').RunAIAgentResolver}. Initialization/validation/finalization
52
67
  * noise is dropped so the model only narrates meaningful work.
53
68
  */
54
69
  const SIGNIFICANT_PROGRESS_STEPS = ['prompt_execution', 'action_execution', 'subagent_execution', 'decision_processing'];
70
+ /**
71
+ * The longest `usageDetailsJson` a usage relay may carry. Every field of both detail blocks fits in a few hundred
72
+ * characters, so anything longer is not a relay the browser runtime sends.
73
+ */
74
+ const MAX_USAGE_DETAILS_JSON_LENGTH = 4096;
55
75
  /** Entity name — centralised so the `MJ:`-prefix convention is applied in exactly one place. */
56
76
  const SESSION_ENTITY = 'MJ: AI Agent Sessions';
57
77
  const CO_AGENT_ENTITY = 'MJ: AI Agent Co Agents';
@@ -77,7 +97,8 @@ const WHITEBOARD_ARTIFACT_TYPE_NAME = 'Whiteboard';
77
97
  */
78
98
  const MAX_CLIENT_TOOLS = 64;
79
99
  /** Maximum accepted size (chars) of the serialized client tool declarations. Raised in step with
80
- * {@link MAX_CLIENT_TOOLS} — multi-channel tool sets with verbose descriptions + JSON schemas run large. */
100
+ * {@link MAX_CLIENT_TOOLS} — multi-channel tool sets with verbose descriptions + JSON schemas run large.
101
+ * Also the largest serialized app-context snapshot stored on the session config. */
81
102
  const MAX_CLIENT_TOOLS_JSON_CHARS = 256_000;
82
103
  /** Maximum accepted size (chars) of a persisted channel state blob. */
83
104
  const MAX_CHANNEL_STATE_CHARS = 2_000_000;
@@ -121,6 +142,14 @@ __decorate([
121
142
  Field(() => String),
122
143
  __metadata("design:type", String)
123
144
  ], StartRealtimeClientSessionResult.prototype, "ExpiresAt", void 0);
145
+ __decorate([
146
+ Field(() => String, { nullable: true }),
147
+ __metadata("design:type", String)
148
+ ], StartRealtimeClientSessionResult.prototype, "Transport", void 0);
149
+ __decorate([
150
+ Field(() => String, { nullable: true }),
151
+ __metadata("design:type", String)
152
+ ], StartRealtimeClientSessionResult.prototype, "RelayUrl", void 0);
124
153
  __decorate([
125
154
  Field(() => String),
126
155
  __metadata("design:type", String)
@@ -149,6 +178,14 @@ __decorate([
149
178
  Field(() => String, { nullable: true }),
150
179
  __metadata("design:type", String)
151
180
  ], StartRealtimeClientSessionResult.prototype, "EffectiveConfigJson", void 0);
181
+ __decorate([
182
+ Field(() => String, { nullable: true }),
183
+ __metadata("design:type", String)
184
+ ], StartRealtimeClientSessionResult.prototype, "ClientPolicyJson", void 0);
185
+ __decorate([
186
+ Field(() => String, { nullable: true }),
187
+ __metadata("design:type", String)
188
+ ], StartRealtimeClientSessionResult.prototype, "AvatarStatusJson", void 0);
152
189
  StartRealtimeClientSessionResult = __decorate([
153
190
  ObjectType()
154
191
  ], StartRealtimeClientSessionResult);
@@ -211,7 +248,7 @@ export { CancelRealtimeSessionToolResult };
211
248
  * Result of {@link RealtimeClientSessionResolver.UploadRealtimeRecording} — a structured
212
249
  * success/failure envelope. A recording-storage failure must never throw to the browser (the
213
250
  * session it belongs to has already ended), so problems come back as `Success: false` with a
214
- * human-readable reason; only ownership/authn violations (from `loadOwnedSession`) still throw.
251
+ * human-readable reason; only ownership/authn violations (from `LoadOwnedRealtimeSession`) still throw.
215
252
  */
216
253
  let UploadRealtimeRecordingResult = class UploadRealtimeRecordingResult {
217
254
  };
@@ -246,6 +283,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
246
283
  // state on — otherwise that cleanup silently no-ops on a freshly-constructed instance.
247
284
  this.clientSessionService = new RealtimeClientSessionService();
248
285
  this.sessionManager = new SessionManager(this.clientSessionService);
286
+ this.runIDVerifier = new SessionRunIDVerifier();
249
287
  }
250
288
  /**
251
289
  * The reason a `Save()`/`Delete()` returned false, for a log line. `CompleteMessage` renders
@@ -281,7 +319,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
281
319
  * agent's `DefaultCoAgentID` → type-level `AIAgentCoAgent` default row → global Realtime Co-Agent) —
282
320
  * see {@link ResolveRealtimeCoAgentID} for the full contract.
283
321
  * 3. Create the durable `AIAgentSession` (run by the co-agent), storing `targetAgentID` in its
284
- * config server-side — this is the authoritative target for all later relays.
322
+ * config — the target later relays use, re-authorized for the caller on each relay.
285
323
  * 4. Mint the {@link import('@memberjunction/ai').ClientRealtimeSessionConfig} via the service.
286
324
  * On failure the just-created session is closed so no half-open session leaks.
287
325
  *
@@ -307,10 +345,16 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
307
345
  * `TypeConfiguration` ← this). **Authorization-gated**: requires the
308
346
  * `Realtime: Advanced Session Controls` authorization — unauthorized callers receive a
309
347
  * structured rejection (never a silent ignore). Must be a JSON object.
348
+ * @param showsAgentVideo `false` from an app that shows no agent video (no channel of it shows it: the embeddable
349
+ * widgets, the mobile app): the session asks the model for no avatar, and `AvatarStatusJson` says `host`. Absent or
350
+ * `true`: the app may show it. It only ever turns video off, so it needs no authorization.
351
+ * @param conversationBranchId Optional fork (`MJ: Conversation Branches.ID`) the session starts on;
352
+ * omitted is Main. It must be a fork of `conversationId`, else the start fails and no session is
353
+ * created. The session's history is read from that fork's path and its transcript is written there.
310
354
  *
311
355
  * @returns The ephemeral config + session linkage the browser needs to open its socket.
312
356
  */
313
- async StartRealtimeClientSession(targetAgentId, { userPayload, providers }, conversationId, lastSessionId, preferredModelId, clientToolsJson, coAgentId, configOverridesJson, recordingStartedAt, recordingConsent, mediaCollectionId, applicationId, appContextJson) {
357
+ async StartRealtimeClientSession(targetAgentId, { userPayload, providers }, conversationId, lastSessionId, preferredModelId, clientToolsJson, coAgentId, configOverridesJson, recordingStartedAt, recordingConsent, mediaCollectionId, applicationId, appContextJson, channelCandidatesJson, showsAgentVideo, conversationBranchId) {
314
358
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
315
359
  const coAgentID = await ResolveRealtimeCoAgentID(targetAgentId, coAgentId, contextUser, provider);
316
360
  // PAIRING CONSTRAINTS: a co-agent with pairing rows is restricted to that target list
@@ -344,16 +388,24 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
344
388
  'using the agent default media kit.');
345
389
  }
346
390
  }
347
- const session = await this.sessionManager.CreateSession({
391
+ const sessionBranchId = await this.resolveSessionBranch(conversationId, conversationBranchId, contextUser, provider);
392
+ if (sessionBranchId) {
393
+ config.conversationBranchID = sessionBranchId;
394
+ }
395
+ // The initial Config carries `maxSessionDeadlineIso` for capped sessions — a server-authoritative key
396
+ // that `MJAIAgentSessionEntityServer` refuses to let the session OWNER write, so this write (and only
397
+ // this one, built entirely from server-decided values) runs in the trusted scope.
398
+ const session = await RunWithTrustedSessionConfigWrites(() => this.sessionManager.CreateSession({
348
399
  agentID: coAgentID,
349
400
  userID: contextUser.ID,
350
401
  conversationID: conversationId,
351
402
  lastSessionID: lastSessionId,
352
403
  config: JSON.stringify(config),
353
- }, contextUser, provider);
404
+ }, contextUser, provider));
354
405
  // Best-effort: stamp recording-start metadata when the browser captured (with consent) at start.
355
406
  await this.stampRecordingStart(session, recordingConsent, recordingStartedAt);
356
407
  const clientTools = this.parseClientTools(clientToolsJson);
408
+ const channelCandidates = this.parseChannelCandidates(channelCandidatesJson);
357
409
  // Best-effort model-context hydration: the PRIOR session chain's transcript (ownership-
358
410
  // checked, capped) is framed into the system prompt so the model REMEMBERS the last leg.
359
411
  // Strictly tolerant — any problem yields no hydration, never a failed start.
@@ -362,19 +414,83 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
362
414
  // mid-thread knows what was already typed. The legs above are excluded: they are already
363
415
  // framed as the resumed transcript, and injecting the same turns twice puts two accounts of
364
416
  // the same exchange in one prompt.
365
- const conversationMessages = await this.loadConversationHistory(conversationId, prior?.LegIDs ?? [], contextUser, provider);
366
- const result = await this.prepareClientSessionOrClose(session, coAgentID, effectiveTargetId, contextUser, provider, preferredModelId, clientTools, prior?.Text, configOverridesJson, maxSessionSeconds, applicationId, this.parseAppContext(appContextJson), conversationMessages);
417
+ const conversationMessages = await this.loadConversationHistory(conversationId, prior?.LegIDs ?? [], contextUser, provider, sessionBranchId);
418
+ const result = await this.prepareClientSessionOrClose(session, coAgentID, effectiveTargetId, contextUser, provider, preferredModelId, clientTools, prior?.Text, configOverridesJson, maxSessionSeconds, applicationId, this.parseAppContext(appContextJson), conversationMessages, channelCandidates, showsAgentVideo);
419
+ await this.stampVerificationPolicy(session, result.EffectiveConfigJson, lastSessionId, contextUser, provider);
367
420
  // Best-effort restore of the PRIOR session's persisted channel states (e.g. the whiteboard
368
421
  // board). Strictly tolerant — any problem yields a null field, never a failed start.
369
422
  result.PriorChannelStatesJson = (await this.loadPriorChannelStatesJson(lastSessionId, contextUser, provider)) ?? undefined;
370
423
  return result;
371
424
  }
425
+ /**
426
+ * The fork a session starts on: null (Main) when none is named; else the named fork, which must
427
+ * belong to the session's conversation. Returns the fork's stored id.
428
+ * @throws when a fork is named without a conversation, or is not a fork of it, or the fork rows cannot be read
429
+ */
430
+ async resolveSessionBranch(conversationId, branchId, contextUser, provider) {
431
+ const named = branchId?.trim();
432
+ if (!named) {
433
+ return null;
434
+ }
435
+ if (!conversationId) {
436
+ throw new Error(`StartRealtimeClientSession: fork ${named} needs a conversation`);
437
+ }
438
+ try {
439
+ const scope = await ConversationEngine.LoadScope(conversationId, named, contextUser, provider);
440
+ return scope.BranchID;
441
+ }
442
+ catch (error) {
443
+ throw new Error(`StartRealtimeClientSession: fork ${named} is not a readable fork of conversation ${conversationId}: ${error.message}`);
444
+ }
445
+ }
446
+ /**
447
+ * Stamps the identity-verification policy snapshot, the configured unverified duration cap and any
448
+ * verified identity inherited from the resumed session onto the new session's `Config`.
449
+ *
450
+ * The effective config (config cascade) is only known after prepare, so this is a second, narrowly
451
+ * scoped write: it runs only when there is something to stamp. It **fails closed** — a cap that could
452
+ * not be applied would leave the session uncapped, so on a save failure the session is closed and the
453
+ * start fails rather than proceeding without its limits.
454
+ */
455
+ async stampVerificationPolicy(session, effectiveConfigJson, lastSessionId, contextUser, provider) {
456
+ const plan = BuildMintConfigStamps({
457
+ EffectiveRealtime: this.readEffectiveRealtime(effectiveConfigJson),
458
+ ExistingConfigRaw: session.Config_,
459
+ InheritedVerified: await LoadInheritedVerifiedIdentity(lastSessionId, contextUser, provider),
460
+ NowMs: Date.now(),
461
+ });
462
+ if (!plan) {
463
+ return;
464
+ }
465
+ session.Config_ = plan.NextConfigRaw;
466
+ const saved = await RunWithTrustedSessionConfigWrites(() => session.Save());
467
+ if (!saved) {
468
+ LogError(`RealtimeClientSessionResolver.stampVerificationPolicy save failed for session ${session.ID}: ${this.describeSaveFailure(session)}`);
469
+ await this.sessionManager.CloseSession(session.ID, contextUser, provider, 'Error');
470
+ throw new Error('Failed to apply the session limits; the realtime session was not started.');
471
+ }
472
+ }
473
+ /** The `realtime` section of the resolved effective config JSON, or undefined when absent/unparseable. */
474
+ readEffectiveRealtime(effectiveConfigJson) {
475
+ if (!effectiveConfigJson) {
476
+ return undefined;
477
+ }
478
+ try {
479
+ return JSON.parse(effectiveConfigJson).realtime;
480
+ }
481
+ catch (error) {
482
+ LogError(`RealtimeClientSessionResolver: effective config JSON was unparseable while stamping verification policy: ${error.message}`);
483
+ return undefined;
484
+ }
485
+ }
372
486
  /**
373
487
  * Execute a single tool call the browser relayed from its provider socket and return the
374
488
  * serialized result for the browser to relay back to the model.
375
489
  *
376
- * Ownership-gated; the target agent id is read from the **session config** (authoritative), never
377
- * from the client. Heartbeats the session on success.
490
+ * Ownership-gated. The target agent id is read from the session config and `CanRun` on it is
491
+ * checked for the caller on every call, because the owner can edit the config. A direct-action
492
+ * call from a scope-limited caller is refused before any elevation. Heartbeats the session on
493
+ * success.
378
494
  *
379
495
  * @returns The serialized tool result JSON.
380
496
  */
@@ -382,12 +498,17 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
382
498
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
383
499
  const session = await this.loadOwnedActiveSession(agentSessionId, contextUser, provider);
384
500
  const config = this.readSessionConfig(session);
501
+ await this.assertCanRunTarget(config.targetAgentID, contextUser, provider);
502
+ if (toolName !== INVOKE_TARGET_AGENT_TOOL_NAME && IsScopeLimitedPrincipal(contextUser)) {
503
+ return this.refuseDirectAction(agentSessionId, toolName);
504
+ }
385
505
  // SCOPED-ANONYMOUS ELEVATION (issue #3371): once ownership is proven above, the delegated
386
506
  // run + its AI-run-entity writes execute as the system user for a scoped anonymous caller
387
507
  // (the caller's role deliberately holds no grants on the run entities). The lead
388
- // targetAgentID comes from the session config and was CanRun-gated at start; the colleague
389
- // union is gated just below, against the CALLER, so elevation never widens agent authority.
508
+ // targetAgentID is CanRun-gated against the CALLER just above, and the colleague union
509
+ // just below, so elevation never widens agent authority.
390
510
  const runUser = ResolveScopedAnonymousRunUser(contextUser);
511
+ const runIDs = await this.runIDVerifier.Verify(session.ID, { CoAgentRunID: config.coAgentRunID, PendingFeedbackRunID: config.pendingFeedbackRunID }, runUser, provider);
391
512
  if (runUser !== contextUser) {
392
513
  LogStatus(`ExecuteRealtimeSessionTool: dispatching relayed tool '${toolName}' for session ${agentSessionId} ` +
393
514
  'under the system user (scoped-anonymous caller).');
@@ -400,20 +521,27 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
400
521
  // Multi-target (Move 4): the session's persisted allowed-agent union — a model-named
401
522
  // colleague in the call is validated against this; absent ⇒ single-target behavior.
402
523
  AllowedAgents: await FilterAllowedAgentsByCanRun(config.allowedAgents, contextUser),
403
- DirectActions: config.directActions,
524
+ // No DirectActions: the service takes them from the session's start-time projection
525
+ // or the target agent's metadata, never from the owner-editable config.
404
526
  // Attribution follows the VISITOR even when `runUser` is elevated: the delegated run
405
527
  // row and its context-memory scope must stay the person's, not the system user's.
406
528
  AttributionUserID: contextUser.ID,
407
529
  // Nest the delegated target-agent run under the co-agent observability run (when present).
408
- ParentRunID: config.coAgentRunID,
530
+ ParentRunID: runIDs.CoAgentRunID,
531
+ // The client-tool channel is keyed on the browser session id, so the delegated run can call
532
+ // the surface's tools; the session-start app context gives its prompt the surface context.
533
+ BrowserSessionID: userPayload.sessionId,
534
+ // The app context is a snapshot the browser sent, so a run elevated to the system user
535
+ // does not get it in its prompt.
536
+ AppContext: runUser === contextUser ? config.appContext : undefined,
409
537
  Call: { CallID: callId, ToolName: toolName, Arguments: argsJson },
410
538
  OnProgress: this.buildDelegationProgressCallback(pubSub, userPayload, agentSessionId, callId),
411
539
  // Resume a previously-paused delegated run (if any) with the user's answer.
412
- ResumeRunID: config.pendingFeedbackRunID,
540
+ ResumeRunID: runIDs.PendingFeedbackRunID,
413
541
  }, runUser, provider);
414
542
  const durationMs = Date.now() - startTime;
415
543
  LogStatus(`ExecuteRealtimeSessionTool: completed relayed tool '${toolName}' (callId: ${callId}) in ${durationMs}ms`);
416
- if (toolName !== 'invoke-target-agent') {
544
+ if (toolName !== INVOKE_TARGET_AGENT_TOOL_NAME) {
417
545
  await this.persistDirectActionTurn(session, callId, toolName, argsJson, ResultJson, durationMs, contextUser, provider);
418
546
  }
419
547
  // Roll the paused-run id forward in the session config: clear the one we just consumed, and
@@ -451,6 +579,18 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
451
579
  }), userPayload);
452
580
  };
453
581
  }
582
+ /**
583
+ * The tool result for a direct-action call from a scope-limited caller (see the authorization
584
+ * model above). It has the broker's result shape, so the model hears that the tool is not
585
+ * available and can ask the target agent through `invoke-target-agent` instead.
586
+ */
587
+ refuseDirectAction(agentSessionId, toolName) {
588
+ LogStatus(`ExecuteRealtimeSessionTool: refused direct action '${toolName}' for a scope-limited caller on session ${agentSessionId}.`);
589
+ return JSON.stringify({
590
+ success: false,
591
+ output: `The tool '${toolName}' is not available in this session. Ask the agent to do it with ${INVOKE_TARGET_AGENT_TOOL_NAME}.`,
592
+ });
593
+ }
454
594
  /**
455
595
  * Rolls the session's `pendingFeedbackRunID` forward after a relayed tool call. The id we just
456
596
  * consumed (resumed or none) is dropped; a new paused run id is stored only when the run paused
@@ -462,17 +602,10 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
462
602
  if (!previous && !pausedRunID) {
463
603
  return;
464
604
  }
465
- const next = {
466
- targetAgentID: config.targetAgentID,
467
- coAgentRunID: config.coAgentRunID,
468
- promptRunID: config.promptRunID,
469
- coAgentRunStepID: config.coAgentRunStepID,
470
- pendingFeedbackRunID: pausedRunID,
471
- // Preserve the server-authoritative voice deadline across config rewrites.
472
- maxSessionDeadlineIso: config.maxSessionDeadlineIso,
473
- applicationID: config.applicationID,
474
- allowedAgents: config.allowedAgents,
475
- };
605
+ // Start from the RAW stored config so every key survives the rewrite — including the
606
+ // server-authoritative `maxSessionDeadlineIso` and `identityVerification` (dropping either would be
607
+ // refused by `MJAIAgentSessionEntityServer`, and silently losing a verification would be worse).
608
+ const next = { ...ParseConfigObject(session.Config_), targetAgentID: config.targetAgentID, pendingFeedbackRunID: pausedRunID };
476
609
  session.Config_ = JSON.stringify(next);
477
610
  if (!(await session.Save())) {
478
611
  LogError(`RealtimeClientSessionResolver.updatePendingFeedbackRunID save failed for session ${session.ID}: ${this.describeSaveFailure(session)}`);
@@ -504,9 +637,10 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
504
637
  // conversation (run-viewer observability parity). Best-effort — never fails the transcript relay.
505
638
  // The prompt-run write runs as the scoped-anonymous elevated user (issue #3371) — the visible
506
639
  // Conversation Detail above deliberately stays on the caller.
507
- const promptRunID = this.readPromptRunID(session);
640
+ const runUser = ResolveScopedAnonymousRunUser(contextUser);
641
+ const promptRunID = await this.readPromptRunID(session, runUser, provider);
508
642
  if (promptRunID) {
509
- await this.clientSessionService.AppendPromptRunMessage(promptRunID, this.mapTranscriptRoleToChatRole(role), text, replacesPrevious ?? false, ResolveScopedAnonymousRunUser(contextUser), provider);
643
+ await this.clientSessionService.AppendPromptRunMessage(promptRunID, this.mapTranscriptRoleToChatRole(role), text, replacesPrevious ?? false, runUser, provider);
510
644
  }
511
645
  await this.sessionManager.Heartbeat(agentSessionId, contextUser, provider);
512
646
  return true;
@@ -525,7 +659,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
525
659
  * the call and uploads the assembled blob (base64) once the session ends.
526
660
  *
527
661
  * Hard gates (in order):
528
- * 1. Ownership — `loadOwnedSession` enforces `UserID === contextUser.ID` (throws on violation).
662
+ * 1. Ownership — `LoadOwnedRealtimeSession` enforces `UserID === contextUser.ID` (throws on violation).
529
663
  * 2. Consent — `consent !== true` is a hard refusal: no audio is ever stored without it.
530
664
  * 3. Storage configuration — the session's agent must resolve a recording storage account (the
531
665
  * agent's `RecordingStorageProviderID`, else `AttachmentStorageProviderID`).
@@ -545,16 +679,16 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
545
679
  */
546
680
  async UploadRealtimeRecording(agentSessionId, audioBase64, mimeType, ctx, durationMs, consent, peaks) {
547
681
  const { contextUser, provider } = this.requireUserAndProvider(ctx.userPayload, ctx.providers);
548
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
682
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
549
683
  // HARD consent gate — no audio is ever stored without an explicit grant.
550
684
  if (consent !== true) {
551
685
  return { Success: false, ErrorMessage: 'Recording consent was not granted.' };
552
686
  }
553
- // SCOPED-ANONYMOUS ELEVATION (issue #3371): past the ownership + consent gates, the store is
554
- // server-side plumbing over entities (MJ: AI Agents read, MJ: Files, the file-session link)
555
- // the caller's narrow relay role deliberately does not hold. Attribution flows through the
556
- // session link, so nothing here depends on the caller's identity.
557
- const runUser = ResolveScopedAnonymousRunUser(contextUser);
687
+ // ELEVATION (issue #3371, #5195): past the ownership + consent gates, the store is server-side
688
+ // plumbing over entities (MJ: AI Agents read, MJ: Files, the file-session link, the session's
689
+ // recording stamp) that neither a scoped-anonymous relay role nor the stock UI role holds.
690
+ // Attribution flows through the session link, so nothing here depends on the caller's identity.
691
+ const runUser = ResolveRecordingStoreUser(contextUser);
558
692
  try {
559
693
  const agent = await provider.GetEntityObject('MJ: AI Agents', runUser);
560
694
  if (!(await agent.Load(session.AgentID))) {
@@ -600,11 +734,14 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
600
734
  }
601
735
  /**
602
736
  * Uploads ONE crash-recovery audio shard (~15s) for an IN-PROGRESS recording into the session's
603
- * folder (`realtime-recordings/<sessionId>/seg-NNNN.<ext>`) as a raw storage object. Durability
604
- * insurance during a live call so a browser/tab death loses at most the last window; the shards are
605
- * deleted once the canonical consolidated file lands via {@link UploadRealtimeRecording}. Ownership-
606
- * gated; consent was already established at session start. Best-effort — returns `false` (never
607
- * throws) on any problem so a failed shard never disrupts the live call.
737
+ * folder (`realtime-recordings/<sessionId>/seg-NNNN.r<rate>.pcm` for PCM with a rate in its MIME type,
738
+ * else the legacy `seg-NNNN.<ext>`) as a raw storage object. Durability insurance during a live call
739
+ * so a browser/tab death loses at most the last window; the shards are deleted once the canonical
740
+ * consolidated file lands via {@link UploadRealtimeRecording}, or by the session janitor once it has
741
+ * recovered a recording from them when that upload never arrives. Ownership-
742
+ * gated; consent was already established at session start. Refused (`false`) once the session has
743
+ * its consolidated recording, since nothing would delete a shard written after that. Best-effort —
744
+ * returns `false` (never throws) on any problem so a failed shard never disrupts the live call.
608
745
  *
609
746
  * @param agentSessionId The in-progress session (ownership-gated).
610
747
  * @param segmentIndex 0-based shard index within the session.
@@ -615,7 +752,13 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
615
752
  async UploadRealtimeRecordingSegment(agentSessionId, segmentIndex, audioBase64, mimeType, ctx) {
616
753
  try {
617
754
  const { contextUser, provider } = this.requireUserAndProvider(ctx.userPayload, ctx.providers);
618
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
755
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
756
+ // The consolidated recording already landed and its upload deleted the shards; a shard
757
+ // arriving now (one still on the wire at hang-up) would be an orphan nothing deletes.
758
+ if (session.RecordingFileID) {
759
+ LogStatus(`RealtimeClientSessionResolver.UploadRealtimeRecordingSegment: shard ${segmentIndex} for session ${agentSessionId} refused — the session already has its consolidated recording`);
760
+ return false;
761
+ }
619
762
  // Scoped-anonymous elevation (issue #3371) — same rationale as UploadRealtimeRecording.
620
763
  const runUser = ResolveScopedAnonymousRunUser(contextUser);
621
764
  const agent = await provider.GetEntityObject('MJ: AI Agents', runUser);
@@ -659,11 +802,12 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
659
802
  // Persist the tool turn facts into a hidden ConversationDetail turn so session review
660
803
  // can reconstruct the tool card dynamically on load (Option 1). Best-effort.
661
804
  await this.persistDirectActionTurn(session, undefined, toolName, argsJson, resultJson, undefined, contextUser, provider);
662
- const promptRunID = this.readPromptRunID(session);
805
+ const runUser = ResolveScopedAnonymousRunUser(contextUser);
806
+ const promptRunID = await this.readPromptRunID(session, runUser, provider);
663
807
  if (!promptRunID) {
664
808
  return false;
665
809
  }
666
- return this.clientSessionService.AppendPromptRunMessage(promptRunID, 'assistant', this.formatToolTurn(toolName, argsJson, resultJson), false, ResolveScopedAnonymousRunUser(contextUser), provider);
810
+ return this.clientSessionService.AppendPromptRunMessage(promptRunID, 'assistant', this.formatToolTurn(toolName, argsJson, resultJson), false, runUser, provider);
667
811
  }
668
812
  /**
669
813
  * Formats a co-agent tool call into a compact, human-readable run line: `🔧 <tool> <args> → <result>`,
@@ -699,7 +843,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
699
843
  */
700
844
  async CancelRealtimeSessionTool(agentSessionId, { userPayload, providers }, callId) {
701
845
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
702
- await this.loadOwnedSession(agentSessionId, contextUser, provider);
846
+ await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
703
847
  try {
704
848
  const aborted = this.clientSessionService.CancelInFlightDelegations(agentSessionId, callId);
705
849
  return { AbortedCount: aborted, Success: true };
@@ -726,31 +870,56 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
726
870
  * (observability creation was skipped), a failed load, or a failed save all log and return
727
871
  * `false` — usage relay must never break a live call.
728
872
  *
873
+ * The optional details are the per-modality usage since the last relay: input and output
874
+ * blocks shaped like Core's `RealtimeUsageModalityDetail`, avatar video seconds included. They
875
+ * add into the usage record on the run's `ModelSpecificResponseDetails`, which prices the avatar
876
+ * video at finalize. Only the fields a block defines, with values that are finite numbers of at
877
+ * least 0, are kept; a relay that carries only details (avatar seconds, no tokens) is stored.
878
+ *
729
879
  * @param inputTokens Input-token DELTA to add (negative/non-finite values are clamped to 0).
730
880
  * @param outputTokens Output-token DELTA to add (negative/non-finite values are clamped to 0).
881
+ * @param usageDetailsJson The per-modality usage since the last relay, as JSON (`{ Input, Output }`).
731
882
  * @returns `true` when the accumulated usage was persisted; `false` on any tolerated failure.
732
883
  */
733
- async RelayRealtimeUsage(agentSessionId, inputTokens, outputTokens, { userPayload, providers }) {
884
+ async RelayRealtimeUsage(agentSessionId, inputTokens, outputTokens, { userPayload, providers }, usageDetailsJson) {
734
885
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
735
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
886
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
736
887
  const inputDelta = this.clampTokenDelta(inputTokens);
737
888
  const outputDelta = this.clampTokenDelta(outputTokens);
738
- if (inputDelta === 0 && outputDelta === 0) {
889
+ const details = this.readUsageDetails(usageDetailsJson);
890
+ if (inputDelta === 0 && outputDelta === 0 && !details) {
739
891
  return true; // nothing to add — a no-op flush is a success, not a failure
740
892
  }
741
- const promptRunID = this.readPromptRunID(session);
893
+ // Runs as the scoped-anonymous elevated user (issue #3371) — the caller's role holds no prompt-run grants.
894
+ const runUser = ResolveScopedAnonymousRunUser(contextUser);
895
+ const promptRunID = await this.readPromptRunID(session, runUser, provider);
742
896
  if (!promptRunID) {
743
897
  return false; // no observability prompt run for this session — logged in the helper
744
898
  }
745
899
  // Delegate to the service so usage writes share the per-run serialization with transcript-message
746
900
  // appends — otherwise the frequent usage save clobbers freshly-appended Messages (and vice-versa).
747
- // Runs as the scoped-anonymous elevated user (issue #3371) — the caller's role holds no prompt-run grants.
748
- return this.clientSessionService.AccumulatePromptRunUsage(promptRunID, inputDelta, outputDelta, ResolveScopedAnonymousRunUser(contextUser), provider);
901
+ return this.clientSessionService.AccumulatePromptRunUsage(promptRunID, inputDelta, outputDelta, runUser, provider, details ?? undefined);
749
902
  }
750
903
  /** Clamps a relayed token delta: negative / non-finite values become 0. */
751
904
  clampTokenDelta(value) {
752
905
  return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
753
906
  }
907
+ /**
908
+ * Reads the relayed per-modality usage, or `null` when there is none worth storing: absent,
909
+ * malformed, longer than a relay ever needs (a client cannot make the server parse an
910
+ * arbitrarily large string), or holding no amount above 0.
911
+ */
912
+ readUsageDetails(usageDetailsJson) {
913
+ if (typeof usageDetailsJson !== 'string') {
914
+ return null;
915
+ }
916
+ if (usageDetailsJson.length > MAX_USAGE_DETAILS_JSON_LENGTH) {
917
+ LogError(`RelayRealtimeUsage: usage details of ${usageDetailsJson.length} characters exceed ${MAX_USAGE_DETAILS_JSON_LENGTH} — details dropped.`);
918
+ return null;
919
+ }
920
+ const details = ParseRealtimeUsageRecord(usageDetailsJson);
921
+ return HasRealtimeUsage(details) ? details : null;
922
+ }
754
923
  /**
755
924
  * Sanitizes client-supplied waveform peaks before they're persisted: drops anything that isn't a
756
925
  * finite number, clamps each to `[0, 1]`, and caps the array length so a hostile client can't bloat
@@ -769,23 +938,29 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
769
938
  }
770
939
  /**
771
940
  * Reads the co-agent `AIPromptRun` id from the session config WITHOUT the relay path's
772
- * throw-on-missing-target semantics — usage relay is best-effort, so a missing/malformed
773
- * config or absent id just logs and returns `null`.
941
+ * throw-on-missing-target semantics, and keeps it only when it is the prompt run of the
942
+ * session's co-agent run (the owner can edit the config). Usage relay is best-effort, so a
943
+ * missing/malformed config, an absent id, or an id of another session just logs and returns `null`.
944
+ *
945
+ * @param runUser The user the prompt-run write runs as; the check runs as the same user.
774
946
  */
775
- readPromptRunID(session) {
776
- try {
777
- const config = this.readSessionConfig(session);
778
- if (config.promptRunID) {
779
- return config.promptRunID;
780
- }
947
+ async readPromptRunID(session, runUser, provider) {
948
+ const config = this.tryReadSessionConfig(session);
949
+ if (!config) {
950
+ LogError(`RelayRealtimeUsage: session ${session.ID} has no parseable config — usage delta dropped.`);
951
+ return null;
952
+ }
953
+ if (!config.promptRunID) {
781
954
  LogStatus(`RelayRealtimeUsage: session ${session.ID} has no co-agent promptRunID (observability run ` +
782
955
  'creation was skipped or failed) — usage delta dropped.');
783
956
  return null;
784
957
  }
785
- catch {
786
- LogError(`RelayRealtimeUsage: session ${session.ID} has no parseable config — usage delta dropped.`);
958
+ const verified = await this.runIDVerifier.Verify(session.ID, { CoAgentRunID: config.coAgentRunID, PromptRunID: config.promptRunID }, runUser, provider);
959
+ if (!verified.PromptRunID) {
960
+ LogError(`RelayRealtimeUsage: session ${session.ID} names a promptRunID that is not its co-agent prompt run — ignored.`);
787
961
  return null;
788
962
  }
963
+ return verified.PromptRunID;
789
964
  }
790
965
  /**
791
966
  * Persist an interactive channel's state of record (e.g. the live whiteboard's serialized
@@ -810,7 +985,11 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
810
985
  */
811
986
  async SaveSessionChannelState(agentSessionId, channelName, stateJson, { userPayload, providers }) {
812
987
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
813
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
988
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
989
+ if (!this.isChannelInSessionScope(session, channelName)) {
990
+ LogError(`SaveSessionChannelState: channel '${channelName}' is not in session ${agentSessionId}'s resolved scope — state not saved.`);
991
+ return false;
992
+ }
814
993
  if (stateJson.length > MAX_CHANNEL_STATE_CHARS) {
815
994
  LogError(`SaveSessionChannelState: rejected oversized state for session ${agentSessionId} / channel '${channelName}' ` +
816
995
  `(${stateJson.length} chars > ${MAX_CHANNEL_STATE_CHARS}).`);
@@ -872,7 +1051,12 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
872
1051
  */
873
1052
  async SaveSessionChannelArtifact(agentSessionId, channelName, name, contentJson, { userPayload, providers }) {
874
1053
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
875
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
1054
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1055
+ if (!this.isChannelInSessionScope(session, channelName)) {
1056
+ const message = `SaveSessionChannelArtifact: channel '${channelName}' is not in session ${agentSessionId}'s resolved scope — nothing saved.`;
1057
+ LogError(message);
1058
+ return { Success: false, ErrorMessage: message, ConversationDetailLinked: false };
1059
+ }
876
1060
  if (contentJson.length > MAX_CHANNEL_STATE_CHARS) {
877
1061
  const message = `SaveSessionChannelArtifact: rejected oversized content for session ${agentSessionId} / channel '${channelName}' ` +
878
1062
  `(${contentJson.length} chars > ${MAX_CHANNEL_STATE_CHARS}).`;
@@ -900,8 +1084,10 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
900
1084
  return { contextUser, provider: GetReadWriteProvider(providers) };
901
1085
  }
902
1086
  /**
903
- * Authorization gate for {@link RealtimeClientSessionResolver.StartRealtimeClientSession}: the
904
- * caller must be able to run the **target** agent. Denial throws (no session is created).
1087
+ * Authorization gate for the **target** agent: the caller must be able to run it. Denial throws.
1088
+ * {@link RealtimeClientSessionResolver.StartRealtimeClientSession} runs it before any session is
1089
+ * created, and {@link RealtimeClientSessionResolver.ExecuteRealtimeSessionTool} runs it on every
1090
+ * relayed call, because the owner can edit the target in the session config.
905
1091
  */
906
1092
  async assertCanRunTarget(targetAgentId, contextUser, _provider) {
907
1093
  const canRun = await AIAgentPermissionHelper.HasPermission(targetAgentId, contextUser, 'run');
@@ -1123,8 +1309,9 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1123
1309
  * resumed session remembers the previous leg(s).
1124
1310
  * @param conversationMessages Optional capped history of the TEXT conversation this session is
1125
1311
  * starting from (from {@link loadConversationHistory}) — framed as "Conversation so far".
1312
+ * @param showsAgentVideo `false` when the app shows no agent video: the session asks for no avatar (`host`).
1126
1313
  */
1127
- async prepareClientSessionOrClose(session, coAgentID, targetAgentId, contextUser, provider, preferredModelId, clientTools, priorTranscript, configOverridesJson, maxSessionSeconds, applicationId, appContext, conversationMessages) {
1314
+ async prepareClientSessionOrClose(session, coAgentID, targetAgentId, contextUser, provider, preferredModelId, clientTools, priorTranscript, configOverridesJson, maxSessionSeconds, applicationId, appContext, conversationMessages, channelCandidates, showsAgentVideo) {
1128
1315
  const prep = await this.clientSessionService.PrepareClientSession({
1129
1316
  CoAgentID: coAgentID,
1130
1317
  TargetAgentID: targetAgentId,
@@ -1150,6 +1337,14 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1150
1337
  // (allowed-agent union); the snapshot is injected into the system prompt at mint.
1151
1338
  ApplicationID: applicationId,
1152
1339
  AppContext: appContext,
1340
+ // Scope-limited callers get no direct actions (see the authorization model above), so
1341
+ // the model is not given tools that the relay refuses.
1342
+ ExcludeDirectActions: IsScopeLimitedPrincipal(contextUser),
1343
+ // The channels the browser could mount: scoped here (registry + cascade) and handed back
1344
+ // as the resolved policy; the declared tools are narrowed to match.
1345
+ ChannelCandidates: channelCandidates,
1346
+ // An app with no channel that shows the agent's video: no avatar is asked for (reason `host`).
1347
+ ShowsAgentVideo: showsAgentVideo ?? undefined,
1153
1348
  },
1154
1349
  // SCOPED-ANONYMOUS ELEVATION (issue #3371): the prepare creates the co-agent
1155
1350
  // observability AIAgentRun/AIPromptRun/run-step, which a scoped anonymous caller's role
@@ -1162,7 +1357,8 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1162
1357
  await this.sessionManager.CloseSession(session.ID, contextUser, provider, 'Error');
1163
1358
  throw new Error(prep.ErrorMessage ?? 'Failed to prepare the client realtime session.');
1164
1359
  }
1165
- await this.persistObservabilityRunIDs(session, targetAgentId, prep.CoAgentRunID, prep.PromptRunID, prep.CoAgentRunStepID, applicationId, prep.EffectiveConfig?.realtime?.allowedAgents, prep.EffectiveConfig?.realtime?.directActions);
1360
+ await this.persistObservabilityRunIDs(session, targetAgentId, prep.CoAgentRunID, prep.PromptRunID, prep.CoAgentRunStepID, applicationId, prep.EffectiveConfig?.realtime?.allowedAgents, prep.EffectiveConfig?.realtime?.directActions, prep.ClientPolicy?.Channels.map((c) => c.Key), appContext);
1361
+ this.pruneExcludedServerChannels(session.ID, prep.ClientPolicy);
1166
1362
  const cfg = prep.ClientConfig;
1167
1363
  return {
1168
1364
  AgentSessionId: session.ID,
@@ -1171,68 +1367,102 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1171
1367
  Model: cfg.Model,
1172
1368
  EphemeralToken: cfg.EphemeralToken,
1173
1369
  ExpiresAt: cfg.ExpiresAt,
1370
+ Transport: cfg.Transport,
1371
+ RelayUrl: cfg.RelayUrl,
1174
1372
  SessionConfigJson: JSON.stringify(cfg.SessionConfig),
1175
1373
  ModelName: prep.ModelName,
1176
1374
  DriverClass: prep.DriverClass,
1177
1375
  NarrationInstructionsTemplate: prep.NarrationInstructionsTemplate,
1178
1376
  NarrationPaceMs: prep.NarrationPaceMs,
1179
1377
  EffectiveConfigJson: prep.EffectiveConfig ? JSON.stringify(prep.EffectiveConfig) : undefined,
1378
+ ClientPolicyJson: prep.ClientPolicy ? JSON.stringify(prep.ClientPolicy) : undefined,
1379
+ AvatarStatusJson: prep.AvatarStatus ? JSON.stringify(prep.AvatarStatus) : undefined,
1180
1380
  };
1181
1381
  }
1382
+ /**
1383
+ * Drops the server-side channel plugins of channels the resolved scope left out. Server plugins
1384
+ * start when the session row is created — before the scope is known — so an excluded channel's
1385
+ * server half would otherwise keep running. A no-op when the caller reported no candidates.
1386
+ */
1387
+ pruneExcludedServerChannels(agentSessionID, policy) {
1388
+ const excluded = (policy?.ExcludedChannels ?? []).map((e) => e.Key);
1389
+ if (excluded.length === 0) {
1390
+ return;
1391
+ }
1392
+ const dropped = RealtimeChannelServerHost.Instance.PruneSessionChannels(agentSessionID, excluded);
1393
+ if (dropped.length > 0) {
1394
+ LogStatus(`StartRealtimeClientSession: dropped the server plugins of out-of-scope channel(s) [${dropped.join(', ')}] for session ${agentSessionID}.`);
1395
+ }
1396
+ }
1182
1397
  /**
1183
1398
  * Writes the co-agent observability run ids into the session's `Config_` alongside the
1184
- * authoritative `targetAgentID`, then saves the session. These ids are read back on close to
1185
- * finalize the runs (and on relay to nest delegated runs). Best-effort: a save failure is logged,
1186
- * not thrown — the voice session still proceeds, it just won't carry the run ids.
1187
- */
1188
- async persistObservabilityRunIDs(session, targetAgentID, coAgentRunID, promptRunID, coAgentRunStepID, applicationID, allowedAgents, directActions) {
1189
- // Preserve any server-authoritative voice deadline stamped at session start (this rebuilds the
1190
- // full config, so read the existing value forward rather than dropping it).
1191
- const existing = this.tryReadSessionConfig(session);
1399
+ * `targetAgentID`, then saves the session. These ids are read back on close to
1400
+ * finalize the runs (and on relay to nest delegated runs). The app awareness (application id,
1401
+ * allowed agents, direct actions, app-context snapshot) is written with them; relays read the
1402
+ * allowed agents and the app context, never the direct actions.
1403
+ * Best-effort: a save failure is logged, not thrown — the voice session still proceeds, it just
1404
+ * won't carry the run ids.
1405
+ */
1406
+ async persistObservabilityRunIDs(session, targetAgentID, coAgentRunID, promptRunID, coAgentRunStepID, applicationID, allowedAgents, directActions, channels, appContext) {
1407
+ // Start from the RAW stored config so every key set at session start survives — the
1408
+ // server-authoritative voice deadline (`maxSessionDeadlineIso`) and the per-session media-kit
1409
+ // override among them — rather than rebuilding from a whitelist that silently drops the rest.
1192
1410
  const config = {
1411
+ ...ParseConfigObject(session.Config_),
1193
1412
  targetAgentID,
1194
1413
  coAgentRunID,
1195
1414
  promptRunID,
1196
1415
  coAgentRunStepID,
1197
- maxSessionDeadlineIso: existing?.maxSessionDeadlineIso,
1198
1416
  applicationID,
1417
+ appContext: this.appContextToStore(appContext, session.ID),
1199
1418
  allowedAgents: allowedAgents && allowedAgents.length > 0 ? allowedAgents : undefined,
1200
1419
  directActions,
1420
+ channels,
1201
1421
  };
1202
1422
  session.Config_ = JSON.stringify(config);
1203
- const saved = await session.Save();
1423
+ // Every value written here is server-decided (run ids, the resolved target, the in-scope channel
1424
+ // set), and `channels` is a server-authoritative key the owner may not write — so this save runs in
1425
+ // the trusted scope. Protected keys already in the stored config are carried forward unchanged.
1426
+ const saved = await RunWithTrustedSessionConfigWrites(() => session.Save());
1204
1427
  if (!saved) {
1205
1428
  LogError(`RealtimeClientSessionResolver.persistObservabilityRunIDs save failed for session ${session.ID}: ${this.describeSaveFailure(session)}`);
1206
1429
  }
1207
1430
  }
1431
+ /**
1432
+ * Returns the app-context snapshot to store on the session config. A snapshot whose JSON is longer
1433
+ * than {@link MAX_CLIENT_TOOLS_JSON_CHARS} is not stored: a warning is logged and `undefined` is
1434
+ * returned, so the session still starts and its delegated runs get no app context.
1435
+ *
1436
+ * @param appContext The snapshot the browser sent at session start, or undefined.
1437
+ * @param sessionID The session the snapshot belongs to (for the warning).
1438
+ * @returns The snapshot to store, or `undefined`.
1439
+ */
1440
+ appContextToStore(appContext, sessionID) {
1441
+ if (!appContext) {
1442
+ return undefined;
1443
+ }
1444
+ const chars = JSON.stringify(appContext).length;
1445
+ if (chars > MAX_CLIENT_TOOLS_JSON_CHARS) {
1446
+ LogErrorEx({
1447
+ message: `RealtimeClientSessionResolver: app context for session ${sessionID} not stored — ${chars} chars ` +
1448
+ `exceeds the ${MAX_CLIENT_TOOLS_JSON_CHARS} cap; delegated runs get no app context.`,
1449
+ severity: 'warning',
1450
+ });
1451
+ return undefined;
1452
+ }
1453
+ return appContext;
1454
+ }
1208
1455
  /**
1209
1456
  * Loads a session, enforcing inbound ownership (`UserID === contextUser.ID`) and that it is not
1210
1457
  * `Closed`. Throws on any violation. Returns the loaded session for the caller to read from.
1211
1458
  */
1212
1459
  async loadOwnedActiveSession(agentSessionId, contextUser, provider) {
1213
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
1460
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1214
1461
  if (session.Status === 'Closed') {
1215
1462
  throw new Error(`Realtime session ${agentSessionId} is closed`);
1216
1463
  }
1217
1464
  return session;
1218
1465
  }
1219
- /**
1220
- * Loads a session and enforces inbound ownership (`UserID === contextUser.ID`) WITHOUT
1221
- * rejecting `Closed` sessions — {@link SaveSessionChannelState}'s final on-end flush
1222
- * legitimately arrives after the session closed. Throws when not found / not owned.
1223
- */
1224
- async loadOwnedSession(agentSessionId, contextUser, provider) {
1225
- const session = await provider.GetEntityObject(SESSION_ENTITY, contextUser);
1226
- const loaded = await session.Load(agentSessionId);
1227
- if (!loaded) {
1228
- throw new Error(`Realtime session ${agentSessionId} not found`);
1229
- }
1230
- if (!UUIDsEqual(session.UserID, contextUser.ID)) {
1231
- LogError(`RealtimeClientSessionResolver: ownership check failed — user ${contextUser.ID} attempted to operate on session ${agentSessionId} owned by ${session.UserID}`);
1232
- throw new Error('Not authorized: you do not own this realtime session');
1233
- }
1234
- return session;
1235
- }
1236
1466
  /**
1237
1467
  * Resolves an ACTIVE channel definition (`MJ: AI Agent Channels`) by name from
1238
1468
  * {@link AIEngineBase}'s cached `AgentChannels` (provider-scoped engine instance, lazy
@@ -1399,6 +1629,10 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1399
1629
  * as the resumed transcript are removed, so the prompt never carries two accounts of one
1400
1630
  * exchange.
1401
1631
  *
1632
+ * Only the rows on the session's path that no rerun replaced are read: the fork it started on,
1633
+ * or Main, in `Sequence` order. When that path cannot be built — its branch rows are missing or invalid — the session
1634
+ * starts with no history, never with every branch's rows.
1635
+ *
1402
1636
  * Strictly tolerant — no conversation, a failed read, or any throw yields `[]`, never a failed
1403
1637
  * session start. The call still works; it just starts cold.
1404
1638
  *
@@ -1406,19 +1640,28 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1406
1640
  * @param coveredSessionIDs Session ids already framed via `PriorTranscript`.
1407
1641
  * @param contextUser The calling user (RunView scope — the read is row-level-secured).
1408
1642
  * @param provider The request-scoped metadata provider.
1643
+ * @param branchId The fork the session started on; null is Main.
1409
1644
  * @returns The capped history, oldest first.
1410
1645
  */
1411
- async loadConversationHistory(conversationId, coveredSessionIDs, contextUser, provider) {
1646
+ async loadConversationHistory(conversationId, coveredSessionIDs, contextUser, provider, branchId) {
1412
1647
  if (!conversationId) {
1413
1648
  return [];
1414
1649
  }
1650
+ const rv = RunView.FromMetadataProvider(provider);
1651
+ let pathFilter;
1652
+ try {
1653
+ pathFilter = await ConversationEngine.BranchPathFilterFresh(conversationId, branchId, contextUser, provider);
1654
+ }
1655
+ catch (error) {
1656
+ LogError(`StartRealtimeClientSession: could not build the path of the session's view for conversation ${conversationId}: ${error.message}`);
1657
+ return [];
1658
+ }
1415
1659
  try {
1416
- const rv = RunView.FromMetadataProvider(provider);
1417
1660
  const result = await rv.RunView({
1418
1661
  EntityName: CONVERSATION_DETAIL_ENTITY,
1419
- ExtraFilter: `ConversationID='${conversationId.replace(/'/g, "''")}'`,
1662
+ ExtraFilter: ConversationEngine.LiveRowsFilter(pathFilter),
1420
1663
  Fields: ['ID', 'Role', 'Message', 'HiddenToUser', 'AgentSessionID', '__mj_CreatedAt'],
1421
- OrderBy: '__mj_CreatedAt ASC',
1664
+ OrderBy: 'Sequence ASC',
1422
1665
  ResultType: 'simple',
1423
1666
  }, contextUser);
1424
1667
  if (!result.Success) {
@@ -1707,6 +1950,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1707
1950
  const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, callerUser);
1708
1951
  detail.NewRecord();
1709
1952
  detail.ConversationID = session.ConversationID;
1953
+ detail.BranchID = this.sessionBranchIdOf(session);
1710
1954
  detail.Role = 'AI';
1711
1955
  detail.HiddenToUser = true;
1712
1956
  detail.Message = 'Artifacts produced during a realtime session (system anchor).';
@@ -1835,18 +2079,50 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1835
2079
  }
1836
2080
  /** Shape check for one client tool declaration: Name/Description non-empty strings, ParametersSchema a plain object. */
1837
2081
  isValidClientTool(candidate) {
1838
- if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) {
2082
+ return IsValidRealtimeToolDefinition(candidate);
2083
+ }
2084
+ /**
2085
+ * Tolerantly parses the channel candidates the browser reported at mint (the same caps and
2086
+ * tolerance as {@link parseClientTools}: capped, never throws, anything wrong is dropped and
2087
+ * logged). A caller that sent none yields `undefined` — an unscoped session, exactly as before.
2088
+ */
2089
+ parseChannelCandidates(channelCandidatesJson) {
2090
+ const parsed = ParseRealtimeChannelCandidates(channelCandidatesJson);
2091
+ for (const reason of parsed.Rejected) {
2092
+ LogError(`StartRealtimeClientSession: ${reason}.`);
2093
+ }
2094
+ return parsed.Candidates.length > 0 ? parsed.Candidates : undefined;
2095
+ }
2096
+ /**
2097
+ * Whether a session's persisted scope allows a server-side call to name `channelName`. A session
2098
+ * with no persisted scope (it reported no candidates) is unscoped and allows everything; a scoped
2099
+ * one allows only the channels its policy put in the session.
2100
+ */
2101
+ isChannelInSessionScope(session, channelName) {
2102
+ // Read the RAW config: an absent `channels` key means an unscoped session, but anything present
2103
+ // and malformed fails CLOSED — a scope that cannot be read must not widen to "everything".
2104
+ // (`channels` is a server-authoritative key, so the owner cannot delete it to unscope a session.)
2105
+ const channels = ParseConfigObject(session.Config_)['channels'];
2106
+ if (channels === undefined) {
2107
+ return true;
2108
+ }
2109
+ if (!Array.isArray(channels)) {
2110
+ LogError(`RealtimeClientSessionResolver: session ${session.ID} has a malformed channel scope; refusing channel '${channelName}'.`);
1839
2111
  return false;
1840
2112
  }
1841
- const tool = candidate;
1842
- return (typeof tool.Name === 'string' && tool.Name.trim().length > 0 && tool.Name.length <= 128 &&
1843
- typeof tool.Description === 'string' && tool.Description.trim().length > 0 &&
1844
- tool.ParametersSchema !== null && typeof tool.ParametersSchema === 'object' && !Array.isArray(tool.ParametersSchema));
2113
+ const wanted = NormalizeChannelKey(channelName);
2114
+ return channels.some((key) => typeof key === 'string' && NormalizeChannelKey(key) === wanted);
2115
+ }
2116
+ /** The fork the session was started on (its config's `conversationBranchID`); null is Main. */
2117
+ sessionBranchIdOf(session) {
2118
+ const branchId = ParseConfigObject(session.Config_)['conversationBranchID'];
2119
+ return typeof branchId === 'string' && branchId.length > 0 ? branchId : null;
1845
2120
  }
1846
2121
  /**
1847
- * Parses the session's persisted config, returning the authoritative `targetAgentID` plus any
1848
- * observability run ids (`coAgentRunID`/`promptRunID`). Throws when the config is missing/malformed
1849
- * or carries no target — a relay cannot proceed without a target.
2122
+ * Parses the session's persisted config, returning its `targetAgentID` plus any observability run
2123
+ * ids (`coAgentRunID`/`promptRunID`). Throws when the config is missing/malformed or carries no
2124
+ * target — a relay cannot proceed without a target. The owner can edit the config, so callers
2125
+ * re-check what they act on.
1850
2126
  */
1851
2127
  readSessionConfig(session) {
1852
2128
  const raw = session.Config_;
@@ -1862,10 +2138,16 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1862
2138
  pendingFeedbackRunID: typeof parsed.pendingFeedbackRunID === 'string' ? parsed.pendingFeedbackRunID : undefined,
1863
2139
  maxSessionDeadlineIso: typeof parsed.maxSessionDeadlineIso === 'string' ? parsed.maxSessionDeadlineIso : undefined,
1864
2140
  applicationID: typeof parsed.applicationID === 'string' ? parsed.applicationID : undefined,
2141
+ appContext: parsed.appContext && typeof parsed.appContext === 'object' && !Array.isArray(parsed.appContext)
2142
+ ? parsed.appContext
2143
+ : undefined,
1865
2144
  allowedAgents: Array.isArray(parsed.allowedAgents) ? parsed.allowedAgents : undefined,
1866
2145
  directActions: parsed.directActions && typeof parsed.directActions === 'object' && typeof parsed.directActions.enabled === 'boolean'
1867
2146
  ? parsed.directActions
1868
2147
  : undefined,
2148
+ conversationBranchID: typeof parsed.conversationBranchID === 'string' ? parsed.conversationBranchID : undefined,
2149
+ mediaCollectionID: typeof parsed.mediaCollectionID === 'string' ? parsed.mediaCollectionID : undefined,
2150
+ channels: Array.isArray(parsed.channels) ? parsed.channels.filter((k) => typeof k === 'string') : undefined,
1869
2151
  };
1870
2152
  }
1871
2153
  }
@@ -1931,6 +2213,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
1931
2213
  const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, contextUser);
1932
2214
  detail.NewRecord();
1933
2215
  detail.ConversationID = session.ConversationID;
2216
+ detail.BranchID = this.sessionBranchIdOf(session);
1934
2217
  detail.Role = this.mapTranscriptRole(role);
1935
2218
  detail.Message = text;
1936
2219
  detail.AgentSessionID = session.ID;
@@ -2068,6 +2351,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
2068
2351
  const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, contextUser);
2069
2352
  detail.NewRecord();
2070
2353
  detail.ConversationID = session.ConversationID;
2354
+ detail.BranchID = this.sessionBranchIdOf(session);
2071
2355
  detail.Role = 'AI';
2072
2356
  detail.HiddenToUser = true;
2073
2357
  detail.AgentSessionID = session.ID;
@@ -2129,8 +2413,11 @@ __decorate([
2129
2413
  __param(10, Arg('mediaCollectionId', () => String, { nullable: true })),
2130
2414
  __param(11, Arg('applicationId', () => String, { nullable: true })),
2131
2415
  __param(12, Arg('appContextJson', () => String, { nullable: true })),
2416
+ __param(13, Arg('channelCandidatesJson', () => String, { nullable: true })),
2417
+ __param(14, Arg('showsAgentVideo', () => Boolean, { nullable: true })),
2418
+ __param(15, Arg('conversationBranchId', () => String, { nullable: true })),
2132
2419
  __metadata("design:type", Function),
2133
- __metadata("design:paramtypes", [String, Object, String, String, String, String, String, String, String, Boolean, String, String, String]),
2420
+ __metadata("design:paramtypes", [String, Object, String, String, String, String, String, String, String, Boolean, String, String, String, String, Boolean, String]),
2134
2421
  __metadata("design:returntype", Promise)
2135
2422
  ], RealtimeClientSessionResolver.prototype, "StartRealtimeClientSession", null);
2136
2423
  __decorate([
@@ -2208,8 +2495,9 @@ __decorate([
2208
2495
  __param(1, Arg('inputTokens', () => Int)),
2209
2496
  __param(2, Arg('outputTokens', () => Int)),
2210
2497
  __param(3, Ctx()),
2498
+ __param(4, Arg('usageDetailsJson', () => String, { nullable: true })),
2211
2499
  __metadata("design:type", Function),
2212
- __metadata("design:paramtypes", [String, Number, Number, Object]),
2500
+ __metadata("design:paramtypes", [String, Number, Number, Object, String]),
2213
2501
  __metadata("design:returntype", Promise)
2214
2502
  ], RealtimeClientSessionResolver.prototype, "RelayRealtimeUsage", null);
2215
2503
  __decorate([