@memberjunction/server 6.2.0-edge.4 → 6.2.0-edge.6

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 (560) hide show
  1. package/README.md +47 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/SessionJanitor.d.ts +91 -30
  4. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  5. package/dist/agentSessions/SessionJanitor.js +311 -81
  6. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  7. package/dist/agentSessions/SessionManager.d.ts +17 -3
  8. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  9. package/dist/agentSessions/SessionManager.js +44 -10
  10. package/dist/agentSessions/SessionManager.js.map +1 -1
  11. package/dist/apiRoot.d.ts +32 -0
  12. package/dist/apiRoot.d.ts.map +1 -0
  13. package/dist/apiRoot.js +67 -0
  14. package/dist/apiRoot.js.map +1 -0
  15. package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
  16. package/dist/auth/AuthProviderCatalogRouter.js +27 -0
  17. package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
  18. package/dist/auth/LoginScreenEngine.d.ts +22 -0
  19. package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
  20. package/dist/auth/LoginScreenEngine.js +57 -0
  21. package/dist/auth/LoginScreenEngine.js.map +1 -0
  22. package/dist/auth/index.d.ts +1 -0
  23. package/dist/auth/index.d.ts.map +1 -1
  24. package/dist/auth/index.js +52 -10
  25. package/dist/auth/index.js.map +1 -1
  26. package/dist/auth/initializeProviders.d.ts +10 -0
  27. package/dist/auth/initializeProviders.d.ts.map +1 -1
  28. package/dist/auth/initializeProviders.js +37 -1
  29. package/dist/auth/initializeProviders.js.map +1 -1
  30. package/dist/auth/localDevAuth.d.ts +79 -0
  31. package/dist/auth/localDevAuth.d.ts.map +1 -0
  32. package/dist/auth/localDevAuth.js +439 -0
  33. package/dist/auth/localDevAuth.js.map +1 -0
  34. package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
  35. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  36. package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
  37. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  39. package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
  40. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  41. package/dist/auth/magicLink/types.d.ts +12 -0
  42. package/dist/auth/magicLink/types.d.ts.map +1 -1
  43. package/dist/auth/sessionUserClone.d.ts +1 -1
  44. package/dist/auth/sessionUserClone.d.ts.map +1 -1
  45. package/dist/auth/sessionUserClone.js +3 -2
  46. package/dist/auth/sessionUserClone.js.map +1 -1
  47. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
  48. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
  49. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
  50. package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
  51. package/dist/broadcast/index.d.ts +6 -0
  52. package/dist/broadcast/index.d.ts.map +1 -0
  53. package/dist/broadcast/index.js +6 -0
  54. package/dist/broadcast/index.js.map +1 -0
  55. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
  56. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
  57. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
  58. package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
  59. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
  60. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
  61. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
  62. package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
  63. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
  64. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
  65. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
  66. package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
  67. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
  68. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
  69. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
  70. package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
  71. package/dist/cache.d.ts +3 -2
  72. package/dist/cache.d.ts.map +1 -1
  73. package/dist/cache.js.map +1 -1
  74. package/dist/config.d.ts +632 -22
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +104 -4
  77. package/dist/config.js.map +1 -1
  78. package/dist/context.d.ts +3 -1
  79. package/dist/context.d.ts.map +1 -1
  80. package/dist/context.js +29 -8
  81. package/dist/context.js.map +1 -1
  82. package/dist/coreRoutes.d.ts +29 -0
  83. package/dist/coreRoutes.d.ts.map +1 -0
  84. package/dist/coreRoutes.js +156 -0
  85. package/dist/coreRoutes.js.map +1 -0
  86. package/dist/generated/graphql-schemas/__mj.d.ts +1016 -29
  87. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  88. package/dist/generated/graphql-schemas/__mj.js +5125 -434
  89. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  90. package/dist/generic/CacheInvalidationResolver.d.ts +85 -30
  91. package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
  92. package/dist/generic/CacheInvalidationResolver.js +175 -60
  93. package/dist/generic/CacheInvalidationResolver.js.map +1 -1
  94. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  95. package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
  96. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  97. package/dist/generic/PubSubManager.d.ts +10 -0
  98. package/dist/generic/PubSubManager.d.ts.map +1 -1
  99. package/dist/generic/PubSubManager.js +44 -1
  100. package/dist/generic/PubSubManager.js.map +1 -1
  101. package/dist/generic/PushStatusResolver.d.ts +39 -6
  102. package/dist/generic/PushStatusResolver.d.ts.map +1 -1
  103. package/dist/generic/PushStatusResolver.js +94 -12
  104. package/dist/generic/PushStatusResolver.js.map +1 -1
  105. package/dist/generic/ResolverBase.d.ts +168 -69
  106. package/dist/generic/ResolverBase.d.ts.map +1 -1
  107. package/dist/generic/ResolverBase.js +464 -405
  108. package/dist/generic/ResolverBase.js.map +1 -1
  109. package/dist/generic/RunViewResolver.d.ts +11 -3
  110. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  111. package/dist/generic/RunViewResolver.js +91 -13
  112. package/dist/generic/RunViewResolver.js.map +1 -1
  113. package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
  114. package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
  115. package/dist/generic/actionOutputFieldSecurity.js +47 -0
  116. package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
  117. package/dist/generic/fieldSecurityTransport.d.ts +30 -0
  118. package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
  119. package/dist/generic/fieldSecurityTransport.js +30 -0
  120. package/dist/generic/fieldSecurityTransport.js.map +1 -0
  121. package/dist/globalCors.d.ts +33 -0
  122. package/dist/globalCors.d.ts.map +1 -0
  123. package/dist/globalCors.js +58 -0
  124. package/dist/globalCors.js.map +1 -0
  125. package/dist/index.d.ts +14 -0
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +230 -222
  128. package/dist/index.js.map +1 -1
  129. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  130. package/dist/integration/CustomColumnPromoter.js +6 -1
  131. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  132. package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
  133. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  134. package/dist/logging/variablesLoggingMiddleware.js +50 -1
  135. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  136. package/dist/multiTenancy/index.d.ts.map +1 -1
  137. package/dist/multiTenancy/index.js +22 -2
  138. package/dist/multiTenancy/index.js.map +1 -1
  139. package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -1
  140. package/dist/realtimeSessions/RealtimeSessionVerificationService.js +2 -1
  141. package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -1
  142. package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -1
  143. package/dist/realtimeSessions/RealtimeVerifyRouter.js +2 -1
  144. package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -1
  145. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
  146. package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
  147. package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
  148. package/dist/realtimeSessions/RecordingAssemblyScheduler.js.map +1 -0
  149. package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -1
  150. package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
  151. package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
  152. package/dist/realtimeSessions/index.d.ts +2 -0
  153. package/dist/realtimeSessions/index.d.ts.map +1 -1
  154. package/dist/realtimeSessions/index.js +2 -0
  155. package/dist/realtimeSessions/index.js.map +1 -1
  156. package/dist/realtimeSessions/ownedRealtimeSession.d.ts +32 -0
  157. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
  158. package/dist/realtimeSessions/ownedRealtimeSession.js +56 -0
  159. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
  160. package/dist/realtimeSessions/sessionAccess.d.ts +91 -8
  161. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  162. package/dist/realtimeSessions/sessionAccess.js +127 -20
  163. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  164. package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
  165. package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
  166. package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
  167. package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
  168. package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
  169. package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
  170. package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
  171. package/dist/realtimeSessions/verificationWorkflow.js +1 -1
  172. package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
  173. package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
  174. package/dist/realtimeWidget/WidgetRouter.js +19 -3
  175. package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
  176. package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
  177. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  178. package/dist/realtimeWidget/WidgetSessionService.js +201 -19
  179. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  180. package/dist/realtimeWidget/host-identity.d.ts +1 -1
  181. package/dist/realtimeWidget/host-identity.js +1 -1
  182. package/dist/realtimeWidget/widgetCore.d.ts +20 -1
  183. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  184. package/dist/realtimeWidget/widgetCore.js +17 -1
  185. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  186. package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
  187. package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
  188. package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
  189. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  190. package/dist/resolvers/ActionResolver.d.ts +19 -1
  191. package/dist/resolvers/ActionResolver.d.ts.map +1 -1
  192. package/dist/resolvers/ActionResolver.js +63 -12
  193. package/dist/resolvers/ActionResolver.js.map +1 -1
  194. package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
  195. package/dist/resolvers/AdhocQueryResolver.js +1 -0
  196. package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
  197. package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
  198. package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
  199. package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
  200. package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
  201. package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
  202. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  203. package/dist/resolvers/AgentSessionResolver.js +40 -17
  204. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  205. package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
  206. package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
  207. package/dist/resolvers/ArtifactFileResolver.js +9 -7
  208. package/dist/resolvers/ArtifactFileResolver.js.map +1 -1
  209. package/dist/resolvers/ConversationRunEventsResolver.d.ts +8 -4
  210. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  211. package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
  212. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  213. package/dist/resolvers/DatasetResolver.d.ts +24 -3
  214. package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
  215. package/dist/resolvers/DatasetResolver.js +57 -8
  216. package/dist/resolvers/DatasetResolver.js.map +1 -1
  217. package/dist/resolvers/EntityCommunicationsResolver.d.ts +13 -1
  218. package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
  219. package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
  220. package/dist/resolvers/EntityCommunicationsResolver.js.map +1 -1
  221. package/dist/resolvers/EntityRecordNameResolver.d.ts +2 -1
  222. package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
  223. package/dist/resolvers/EntityRecordNameResolver.js +3 -2
  224. package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
  225. package/dist/resolvers/FetchEntityVectorsResolver.d.ts +46 -3
  226. package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
  227. package/dist/resolvers/FetchEntityVectorsResolver.js +211 -50
  228. package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
  229. package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
  230. package/dist/resolvers/FileCategoryResolver.js +1 -0
  231. package/dist/resolvers/FileCategoryResolver.js.map +1 -1
  232. package/dist/resolvers/FileResolver.d.ts +87 -11
  233. package/dist/resolvers/FileResolver.d.ts.map +1 -1
  234. package/dist/resolvers/FileResolver.js +292 -105
  235. package/dist/resolvers/FileResolver.js.map +1 -1
  236. package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
  237. package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
  238. package/dist/resolvers/GetDataContextDataResolver.js +117 -38
  239. package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
  240. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  241. package/dist/resolvers/GetDataResolver.js +6 -3
  242. package/dist/resolvers/GetDataResolver.js.map +1 -1
  243. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  244. package/dist/resolvers/IntegrationDiscoveryResolver.js +49 -9
  245. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  246. package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
  247. package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
  248. package/dist/resolvers/MergeRecordsResolver.js +31 -3
  249. package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
  250. package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
  251. package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
  252. package/dist/resolvers/PipelineProgressResolver.js +5 -3
  253. package/dist/resolvers/PipelineProgressResolver.js.map +1 -1
  254. package/dist/resolvers/QueryResolver.d.ts +2 -0
  255. package/dist/resolvers/QueryResolver.d.ts.map +1 -1
  256. package/dist/resolvers/QueryResolver.js +33 -6
  257. package/dist/resolvers/QueryResolver.js.map +1 -1
  258. package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
  259. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  260. package/dist/resolvers/QuerySystemUserResolver.js +15 -6
  261. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  262. package/dist/resolvers/RSUResolver.d.ts.map +1 -1
  263. package/dist/resolvers/RSUResolver.js +12 -0
  264. package/dist/resolvers/RSUResolver.js.map +1 -1
  265. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +18 -10
  266. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  267. package/dist/resolvers/RealtimeClientSessionResolver.js +92 -54
  268. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  269. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  270. package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
  271. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  272. package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
  273. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  274. package/dist/resolvers/RunAIAgentResolver.js +241 -67
  275. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  276. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  277. package/dist/resolvers/RunTemplateResolver.js +4 -4
  278. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  279. package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
  280. package/dist/resolvers/RunTestResolver.js +3 -1
  281. package/dist/resolvers/RunTestResolver.js.map +1 -1
  282. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts +12 -1
  283. package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
  284. package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
  285. package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
  286. package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
  287. package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
  288. package/dist/resolvers/SqlLoggingConfigResolver.js.map +1 -1
  289. package/dist/resolvers/SyncDataResolver.d.ts.map +1 -1
  290. package/dist/resolvers/SyncDataResolver.js +4 -0
  291. package/dist/resolvers/SyncDataResolver.js.map +1 -1
  292. package/dist/resolvers/TaskGraphFrameResolver.d.ts +2 -1
  293. package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
  294. package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
  295. package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
  296. package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
  297. package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
  298. package/dist/resolvers/TestQuerySQLResolver.js +66 -13
  299. package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
  300. package/dist/resolvers/TransactionGroupResolver.d.ts +34 -2
  301. package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
  302. package/dist/resolvers/TransactionGroupResolver.js +150 -27
  303. package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
  304. package/dist/resolvers/UserViewResolver.d.ts +1 -1
  305. package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
  306. package/dist/resolvers/UserViewResolver.js +2 -1
  307. package/dist/resolvers/UserViewResolver.js.map +1 -1
  308. package/dist/resolvers/agent-run-data-guard.d.ts +31 -0
  309. package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
  310. package/dist/resolvers/agent-run-data-guard.js +72 -0
  311. package/dist/resolvers/agent-run-data-guard.js.map +1 -0
  312. package/dist/rest/EntityCRUDHandler.d.ts +6 -0
  313. package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
  314. package/dist/rest/EntityCRUDHandler.js +19 -5
  315. package/dist/rest/EntityCRUDHandler.js.map +1 -1
  316. package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
  317. package/dist/rest/RESTEndpointHandler.js +8 -5
  318. package/dist/rest/RESTEndpointHandler.js.map +1 -1
  319. package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
  320. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
  321. package/dist/rest/RealtimeRecordingHandler.js +383 -0
  322. package/dist/rest/RealtimeRecordingHandler.js.map +1 -0
  323. package/dist/rest/ViewOperationsHandler.d.ts +9 -1
  324. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  325. package/dist/rest/ViewOperationsHandler.js +43 -17
  326. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  327. package/dist/rest/setupRESTEndpoints.d.ts +6 -0
  328. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  329. package/dist/rest/setupRESTEndpoints.js +6 -0
  330. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  331. package/dist/serverExtensionReservedRoots.d.ts +12 -10
  332. package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
  333. package/dist/serverExtensionReservedRoots.js +14 -17
  334. package/dist/serverExtensionReservedRoots.js.map +1 -1
  335. package/dist/serverRoleConfigUnits.d.ts +29 -0
  336. package/dist/serverRoleConfigUnits.d.ts.map +1 -0
  337. package/dist/serverRoleConfigUnits.js +33 -0
  338. package/dist/serverRoleConfigUnits.js.map +1 -0
  339. package/dist/services/MJStorageBlobStore.d.ts +8 -1
  340. package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
  341. package/dist/services/MJStorageBlobStore.js +25 -19
  342. package/dist/services/MJStorageBlobStore.js.map +1 -1
  343. package/dist/services/StartTaskGraphDispatcher.js +2 -2
  344. package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
  345. package/dist/services/TaskGraphActionRunner.d.ts +5 -3
  346. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  347. package/dist/services/TaskGraphActionRunner.js +27 -5
  348. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  349. package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
  350. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  351. package/dist/services/TaskGraphAgentRunner.js +157 -9
  352. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  353. package/dist/services/TaskGraphContinuationDeliverer.d.ts +59 -2
  354. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  355. package/dist/services/TaskGraphContinuationDeliverer.js +626 -16
  356. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  357. package/dist/sharedCache.d.ts.map +1 -1
  358. package/dist/sharedCache.js +3 -0
  359. package/dist/sharedCache.js.map +1 -1
  360. package/dist/types.d.ts +23 -1
  361. package/dist/types.d.ts.map +1 -1
  362. package/dist/types.js.map +1 -1
  363. package/package.json +99 -98
  364. package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
  365. package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
  366. package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
  367. package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
  368. package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
  369. package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
  370. package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
  371. package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
  372. package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
  373. package/src/__tests__/FetchEntityVectorsResolver.test.ts +369 -18
  374. package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
  375. package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
  376. package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
  377. package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
  378. package/src/__tests__/LoginScreenEngine.test.ts +86 -0
  379. package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
  380. package/src/__tests__/PipelineProgressResolver.filter.test.ts +58 -0
  381. package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
  382. package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
  383. package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
  384. package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
  385. package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
  386. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
  387. package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
  388. package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
  389. package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
  390. package/src/__tests__/ResolverBase.clientOldValues.test.ts +243 -7
  391. package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
  392. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
  393. package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
  394. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
  395. package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
  396. package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
  397. package/src/__tests__/RunAIAgentResolver.branch.test.ts +69 -13
  398. package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
  399. package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
  400. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +20 -28
  401. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +264 -0
  402. package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
  403. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
  404. package/src/__tests__/RunTemplateResolver.test.ts +35 -2
  405. package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
  406. package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
  407. package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
  408. package/src/__tests__/SessionJanitor.test.ts +365 -10
  409. package/src/__tests__/SessionManager.test.ts +122 -0
  410. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
  411. package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
  412. package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +468 -23
  413. package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
  414. package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
  415. package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
  416. package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
  417. package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
  418. package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
  419. package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
  420. package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
  421. package/src/__tests__/WidgetSessionService.test.ts +583 -4
  422. package/src/__tests__/agent-run-data-guard.test.ts +130 -0
  423. package/src/__tests__/apiRootBoot.test.ts +202 -0
  424. package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
  425. package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
  426. package/src/__tests__/cacheInvalidationRecordData.test.ts +109 -0
  427. package/src/__tests__/conversationRunEvents.test.ts +56 -14
  428. package/src/__tests__/fieldSecurityFixtures.ts +91 -0
  429. package/src/__tests__/globalCors.test.ts +217 -0
  430. package/src/__tests__/localDevAuth.test.ts +196 -0
  431. package/src/__tests__/multiTenancy.postgresRewrite.test.ts +181 -0
  432. package/src/__tests__/multiTenancy.security.test.ts +51 -0
  433. package/src/__tests__/multiTenancy.test.ts +1 -1
  434. package/src/__tests__/orphanedConversationDetailReconciler.test.ts +10 -0
  435. package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
  436. package/src/__tests__/pushStatusResolver.filter.test.ts +64 -1
  437. package/src/__tests__/realtimeRecordingHandler.test.ts +669 -0
  438. package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
  439. package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
  440. package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
  441. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
  442. package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
  443. package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
  444. package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
  445. package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
  446. package/src/__tests__/resolverBase.fls.test.ts +146 -97
  447. package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
  448. package/src/__tests__/runParamsTimeZone.test.ts +179 -0
  449. package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
  450. package/src/__tests__/runViewProperties.test.ts +114 -0
  451. package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
  452. package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
  453. package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
  454. package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
  455. package/src/__tests__/sessionUserClone.test.ts +5 -1
  456. package/src/__tests__/sharedCache.test.ts +5 -3
  457. package/src/__tests__/startupSweepOrder.test.ts +40 -0
  458. package/src/__tests__/unifiedAuth.test.ts +28 -0
  459. package/src/__tests__/widgetGuestElevation.test.ts +2 -0
  460. package/src/agentSessions/SessionJanitor.ts +369 -93
  461. package/src/agentSessions/SessionManager.ts +45 -10
  462. package/src/apiRoot.ts +78 -0
  463. package/src/auth/AuthProviderCatalogRouter.ts +29 -0
  464. package/src/auth/LoginScreenEngine.ts +50 -0
  465. package/src/auth/index.ts +57 -12
  466. package/src/auth/initializeProviders.ts +43 -1
  467. package/src/auth/localDevAuth.ts +528 -0
  468. package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
  469. package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
  470. package/src/auth/magicLink/types.ts +10 -0
  471. package/src/auth/sessionUserClone.ts +3 -2
  472. package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
  473. package/src/broadcast/index.ts +5 -0
  474. package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
  475. package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
  476. package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
  477. package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
  478. package/src/cache.ts +2 -1
  479. package/src/config.ts +115 -5
  480. package/src/context.ts +32 -8
  481. package/src/coreRoutes.ts +181 -0
  482. package/src/generated/graphql-schemas/__mj.ts +3684 -284
  483. package/src/generic/CacheInvalidationResolver.ts +172 -60
  484. package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
  485. package/src/generic/PubSubManager.ts +54 -1
  486. package/src/generic/PushStatusResolver.ts +109 -12
  487. package/src/generic/ResolverBase.ts +540 -431
  488. package/src/generic/RunViewResolver.ts +97 -15
  489. package/src/generic/actionOutputFieldSecurity.ts +75 -0
  490. package/src/generic/fieldSecurityTransport.ts +62 -0
  491. package/src/globalCors.ts +71 -0
  492. package/src/index.ts +239 -238
  493. package/src/integration/CustomColumnPromoter.ts +6 -1
  494. package/src/logging/variablesLoggingMiddleware.ts +59 -3
  495. package/src/multiTenancy/index.ts +24 -2
  496. package/src/realtimeSessions/README.md +7 -3
  497. package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
  498. package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
  499. package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
  500. package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
  501. package/src/realtimeSessions/index.ts +2 -0
  502. package/src/realtimeSessions/ownedRealtimeSession.ts +69 -0
  503. package/src/realtimeSessions/sessionAccess.ts +183 -23
  504. package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
  505. package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
  506. package/src/realtimeSessions/verificationWorkflow.ts +3 -1
  507. package/src/realtimeWidget/WidgetRouter.ts +22 -4
  508. package/src/realtimeWidget/WidgetSessionService.ts +252 -17
  509. package/src/realtimeWidget/host-identity.ts +1 -1
  510. package/src/realtimeWidget/widgetCore.ts +30 -1
  511. package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
  512. package/src/resolvers/ActionResolver.ts +78 -15
  513. package/src/resolvers/AdhocQueryResolver.ts +1 -0
  514. package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
  515. package/src/resolvers/AgentSessionResolver.ts +46 -18
  516. package/src/resolvers/ArtifactFileResolver.ts +9 -8
  517. package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
  518. package/src/resolvers/DatasetResolver.ts +70 -10
  519. package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
  520. package/src/resolvers/EntityRecordNameResolver.ts +3 -2
  521. package/src/resolvers/FetchEntityVectorsResolver.ts +248 -55
  522. package/src/resolvers/FileCategoryResolver.ts +1 -0
  523. package/src/resolvers/FileResolver.ts +334 -109
  524. package/src/resolvers/GetDataContextDataResolver.ts +139 -41
  525. package/src/resolvers/GetDataResolver.ts +6 -3
  526. package/src/resolvers/IntegrationDiscoveryResolver.ts +53 -9
  527. package/src/resolvers/MergeRecordsResolver.ts +23 -2
  528. package/src/resolvers/PipelineProgressResolver.ts +12 -3
  529. package/src/resolvers/QueryResolver.ts +31 -6
  530. package/src/resolvers/QuerySystemUserResolver.ts +15 -6
  531. package/src/resolvers/RSUResolver.ts +12 -0
  532. package/src/resolvers/RealtimeClientSessionResolver.ts +114 -66
  533. package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
  534. package/src/resolvers/RunAIAgentResolver.ts +296 -85
  535. package/src/resolvers/RunTemplateResolver.ts +6 -4
  536. package/src/resolvers/RunTestResolver.ts +3 -1
  537. package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
  538. package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
  539. package/src/resolvers/SyncDataResolver.ts +4 -0
  540. package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
  541. package/src/resolvers/TestQuerySQLResolver.ts +69 -13
  542. package/src/resolvers/TransactionGroupResolver.ts +185 -30
  543. package/src/resolvers/UserViewResolver.ts +2 -1
  544. package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
  545. package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
  546. package/src/resolvers/agent-run-data-guard.ts +89 -0
  547. package/src/rest/EntityCRUDHandler.ts +22 -5
  548. package/src/rest/RESTEndpointHandler.ts +8 -5
  549. package/src/rest/RealtimeRecordingHandler.ts +467 -0
  550. package/src/rest/ViewOperationsHandler.ts +52 -18
  551. package/src/rest/setupRESTEndpoints.ts +6 -0
  552. package/src/serverExtensionReservedRoots.ts +14 -17
  553. package/src/serverRoleConfigUnits.ts +48 -0
  554. package/src/services/MJStorageBlobStore.ts +31 -30
  555. package/src/services/StartTaskGraphDispatcher.ts +2 -2
  556. package/src/services/TaskGraphActionRunner.ts +25 -5
  557. package/src/services/TaskGraphAgentRunner.ts +188 -11
  558. package/src/services/TaskGraphContinuationDeliverer.ts +738 -16
  559. package/src/sharedCache.ts +3 -0
  560. package/src/types.ts +23 -1
@@ -1,6 +1,6 @@
1
1
  import { Arg, Ctx, Field, InputType, Mutation, ObjectType, registerEnumType, Resolver, PubSub, PubSubEngine } from 'type-graphql';
2
2
  import { AppContext } from '../types.js';
3
- import { LogError, UserInfo, CompositeKey, DatabaseProviderBase, LogStatus } from '@memberjunction/core';
3
+ import { AssertClientInputWritableForUser, LogError, UserInfo, CompositeKey, DatabaseProviderBase, LogStatus } from '@memberjunction/core';
4
4
  import { MJQueryCategoryEntity, MJQueryPermissionEntity, MJQueryFieldEntity, MJQueryParameterEntity, MJQueryEntityEntity, QueryEngine } from '@memberjunction/core-entities';
5
5
  import { MJQueryResolver, MJQuery_, MJQueryField_, MJQueryParameter_, MJQueryEntity_, MJQueryPermission_ } from '../generated/generated.js';
6
6
  import { GetReadWriteProvider } from '../util.js';
@@ -301,6 +301,8 @@ export class MJQueryResolverExtended extends MJQueryResolver {
301
301
  CacheMaxSize: input.CacheMaxSize || null
302
302
  };
303
303
 
304
+ // Field-level security: refuse a key the caller may not read, or that names no field
305
+ AssertClientInputWritableForUser(record.EntityInfo, fieldsToSet, context.userPayload.userRecord);
304
306
  record.SetMany(fieldsToSet, true);
305
307
 
306
308
  // Pass caller-provided parameter sample values to override LLM guesses
@@ -324,7 +326,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
324
326
  }
325
327
 
326
328
 
327
- return await this.buildSuccessResult(record);
329
+ return await this.buildSuccessResult(record, context.userPayload.userRecord, provider);
328
330
  }
329
331
  else {
330
332
  // Save failed - check if another request created the same query (race condition)
@@ -337,7 +339,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
337
339
  LogStatus(`[CreateQuery] Unique constraint detected for query '${input.Name}'. Using existing query (ID: ${existingQuery.ID}) created by concurrent request.`);
338
340
  const existingEntity = await provider.GetEntityObject<MJQueryEntityServer>('MJ: Queries', context.userPayload.userRecord);
339
341
  if (await existingEntity.Load(existingQuery.ID)) {
340
- return await this.buildSuccessResult(existingEntity);
342
+ return await this.buildSuccessResult(existingEntity, context.userPayload.userRecord, provider);
341
343
  }
342
344
  // Entity load failed after confirming the row exists — extremely rare
343
345
  return {
@@ -367,11 +369,16 @@ export class MJQueryResolverExtended extends MJQueryResolver {
367
369
  * Maps an MJQueryEntityServer (with loaded related metadata) to a QueryMutationResultType.
368
370
  * Uses entity.GetAll() for scalar fields so that new CodeGen-generated fields are included
369
371
  * automatically without manual updates. Related entity arrays are mapped explicitly.
372
+ *
373
+ * @param contextUser The caller the result is for. Field security projects the record for them.
370
374
  */
371
- private async buildSuccessResult(entity: MJQueryEntityServer): Promise<QueryMutationResultType> {
375
+ private async buildSuccessResult(entity: MJQueryEntityServer, contextUser: UserInfo, provider: DatabaseProviderBase): Promise<QueryMutationResultType> {
372
376
  return {
373
377
  Success: true,
374
- Query: await this.MapFieldNamesToCodeNames('MJ: Queries', entity.GetAll()) as MJQuery_,
378
+ // Mapped for the caller, so the field-security projection applies: the record was just
379
+ // saved, and a save re-reads the whole row.
380
+ // fls-guard: projected for contextUser by MapFieldNamesToCodeNames
381
+ Query: await this.MapFieldNamesToCodeNames('MJ: Queries', entity.GetAll(), contextUser, provider) as MJQuery_,
375
382
  Fields: this.mapFields(entity.QueryFields),
376
383
  Parameters: this.mapParameters(entity.QueryParameters),
377
384
  Entities: this.mapEntities(entity.QueryEntities),
@@ -523,6 +530,8 @@ export class MJQueryResolverExtended extends MJQueryResolver {
523
530
  if (input.CacheTTLMinutes !== undefined) updateFields.CacheTTLMinutes = input.CacheTTLMinutes;
524
531
  if (input.CacheMaxSize !== undefined) updateFields.CacheMaxSize = input.CacheMaxSize;
525
532
 
533
+ // Field-level security: refuse a key the caller may not read
534
+ AssertClientInputWritableForUser(queryEntity.EntityInfo, updateFields, context.userPayload.userRecord);
526
535
  // Use SetMany to update all fields at once
527
536
  queryEntity.SetMany(updateFields);
528
537
 
@@ -554,7 +563,7 @@ export class MJQueryResolverExtended extends MJQueryResolver {
554
563
  await this.createPermissions(provider, input.Permissions, queryID, context.userPayload.userRecord);
555
564
  }
556
565
 
557
- return await this.buildSuccessResult(queryEntity);
566
+ return await this.buildSuccessResult(queryEntity, context.userPayload.userRecord, provider);
558
567
 
559
568
  } catch (err) {
560
569
  LogError(err);
@@ -318,6 +318,18 @@ export class RSUResolver {
318
318
  SkipRestart: input.SkipRestart ?? undefined,
319
319
  }));
320
320
 
321
+ // MJ-APPLY-6 — deliberately the BARE call, not RunPipelineBatchWithRetry. Everywhere else
322
+ // the MigrationSQL was generated by SchemaBuilder, so the manager knows what a replay would
323
+ // re-execute. Here it is arbitrary SQL handed in by the caller, and `executeMigration` is
324
+ // NOT transactional: it splits on GO/statement boundaries and issues one ExecuteSQL per
325
+ // chunk, so a migration that fails on chunk 3 leaves chunks 1-2 committed while the step is
326
+ // recorded `failed` — which is exactly the state `canSafelyRetry` reads as "nothing was
327
+ // applied, safe to replay". The replay then re-runs the committed chunks and reports THEIR
328
+ // "already exists" error in place of the real one. For generated DDL that risk is accepted
329
+ // upstream (SchemaBuilder.RunSchemaPipeline); for caller-authored DDL it is not ours to
330
+ // take. This is also an attended operator mutation with no in-repo caller — the human on
331
+ // the other end sees the failure and decides. Keep it in step with the singular
332
+ // RunRuntimeSchemaUpdate above, which uses bare RunPipeline for the same reason.
321
333
  const batchResult = await rsm.RunPipelineBatch(pipelineInputs);
322
334
 
323
335
  return {
@@ -33,7 +33,7 @@
33
33
  */
34
34
  import { Resolver, Mutation, Arg, Ctx, Int, Float, ObjectType, Field, PubSub, PubSubEngine } from 'type-graphql';
35
35
  import { AppContext, UserPayload } from '../types.js';
36
- import { AuthorizationEvaluator, UserInfo, IMetadataProvider, LogError, LogErrorEx, LogStatus, RunView, BaseEntity } from '@memberjunction/core';
36
+ import { AuthorizationEvaluator, UserInfo, IMetadataProvider, DatabaseProviderBase, LogError, LogErrorEx, LogStatus, RunView, BaseEntity } from '@memberjunction/core';
37
37
  import { UUIDsEqual, IsValidUUID } from '@memberjunction/global';
38
38
  import {
39
39
  MJAIAgentEntity,
@@ -44,6 +44,8 @@ import {
44
44
  MJConversationDetailArtifactEntity,
45
45
  MJConversationDetailEntity,
46
46
  ConversationEngine,
47
+ MJConversationDetailEntityExtended,
48
+ MJConversationEntity,
47
49
  } from '@memberjunction/core-entities';
48
50
  import { AIEngine } from '@memberjunction/aiengine';
49
51
  import { AIAgentPermissionHelper, AIEngineBase, HasRealtimeUsage, ParseRealtimeUsageRecord, type RealtimeUsageRecord } from '@memberjunction/ai-engine-base';
@@ -62,6 +64,7 @@ import {
62
64
  RealtimeAllowedAgent,
63
65
  RealtimeDirectActionsConfig,
64
66
  REALTIME_ADVANCED_SESSION_CONTROLS_AUTHORIZATION,
67
+ SqlRealtimeRecordingLedger,
65
68
  resolveRecordingStorageAccountID,
66
69
  storeRealtimeRecording,
67
70
  writeRealtimeRecordingSegment,
@@ -87,6 +90,8 @@ import { ResolveWidgetGuestRunContext, ResolveScopedAnonymousRunUser, ResolveRec
87
90
  import { RunWithTrustedSessionConfigWrites } from '@memberjunction/core-entities-server';
88
91
  import { BuildMintConfigStamps, LoadInheritedVerifiedIdentity } from '../realtimeSessions/sessionConfigStamps.js';
89
92
  import { ParseConfigObject } from '../realtimeSessions/verificationCore.js';
93
+ import { LoadOwnedRealtimeSession } from '../realtimeSessions/ownedRealtimeSession.js';
94
+ import { AuthorizeSessionAccess, LoadSessionForAccess } from '../realtimeSessions/sessionAccess.js';
90
95
 
91
96
  /**
92
97
  * Progress steps worth narrating to the realtime model — mirrors the normal agent-run path's filter
@@ -102,7 +107,6 @@ const SIGNIFICANT_PROGRESS_STEPS = ['prompt_execution', 'action_execution', 'sub
102
107
  const MAX_USAGE_DETAILS_JSON_LENGTH = 4096;
103
108
 
104
109
  /** Entity name — centralised so the `MJ:`-prefix convention is applied in exactly one place. */
105
- const SESSION_ENTITY = 'MJ: AI Agent Sessions';
106
110
  const CO_AGENT_ENTITY = 'MJ: AI Agent Co Agents';
107
111
  const CONVERSATION_DETAIL_ENTITY = 'MJ: Conversation Details';
108
112
  const CHANNEL_ENTITY = 'MJ: AI Agent Channels';
@@ -415,7 +419,7 @@ export class CancelRealtimeSessionToolResult {
415
419
  * Result of {@link RealtimeClientSessionResolver.UploadRealtimeRecording} — a structured
416
420
  * success/failure envelope. A recording-storage failure must never throw to the browser (the
417
421
  * session it belongs to has already ended), so problems come back as `Success: false` with a
418
- * human-readable reason; only ownership/authn violations (from `loadOwnedSession`) still throw.
422
+ * human-readable reason; only ownership/authn violations (from `LoadOwnedRealtimeSession`) still throw.
419
423
  */
420
424
  @ObjectType()
421
425
  export class UploadRealtimeRecordingResult {
@@ -595,7 +599,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
595
599
  );
596
600
 
597
601
  // Best-effort: stamp recording-start metadata when the browser captured (with consent) at start.
598
- await this.stampRecordingStart(session, recordingConsent, recordingStartedAt);
602
+ await this.stampRecordingStart(session, recordingConsent, recordingStartedAt, contextUser, provider);
599
603
 
600
604
  const clientTools = this.parseClientTools(clientToolsJson);
601
605
  const channelCandidates = this.parseChannelCandidates(channelCandidatesJson);
@@ -664,10 +668,18 @@ export class RealtimeClientSessionResolver extends ResolverBase {
664
668
  contextUser: UserInfo,
665
669
  provider: IMetadataProvider,
666
670
  ): Promise<void> {
671
+ let inheritedVerified: Awaited<ReturnType<typeof LoadInheritedVerifiedIdentity>> = undefined;
672
+ try {
673
+ inheritedVerified = await LoadInheritedVerifiedIdentity(lastSessionId, contextUser, provider);
674
+ } catch (error) {
675
+ LogError(
676
+ `StartRealtimeClientSession: failed to load inherited verified identity for prior session ${lastSessionId}: ${(error as Error).message}`,
677
+ );
678
+ }
667
679
  const plan = BuildMintConfigStamps({
668
680
  EffectiveRealtime: this.readEffectiveRealtime(effectiveConfigJson),
669
681
  ExistingConfigRaw: session.Config_,
670
- InheritedVerified: await LoadInheritedVerifiedIdentity(lastSessionId, contextUser, provider),
682
+ InheritedVerified: inheritedVerified,
671
683
  NowMs: Date.now(),
672
684
  });
673
685
  if (!plan) {
@@ -902,12 +914,15 @@ export class RealtimeClientSessionResolver extends ResolverBase {
902
914
  ): Promise<boolean> {
903
915
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
904
916
  const session = await this.loadOwnedActiveSession(agentSessionId, contextUser, provider);
905
-
906
- const saved = replacesPrevious
907
- ? await this.replacePreviousTranscriptTurn(session, role, text, contextUser, provider, utteranceStartMs, utteranceEndMs)
908
- : await this.persistTranscriptTurn(session, role, text, contextUser, provider, utteranceStartMs, utteranceEndMs);
909
- if (!saved) {
910
- return false;
917
+ const persistDetail =
918
+ this.mapTranscriptRole(role) === 'User' || (await this.callerOwnsConversation(session, contextUser, provider));
919
+ if (persistDetail) {
920
+ const saved = replacesPrevious
921
+ ? await this.replacePreviousTranscriptTurn(session, role, text, contextUser, provider, utteranceStartMs, utteranceEndMs)
922
+ : await this.persistTranscriptTurn(session, role, text, contextUser, provider, utteranceStartMs, utteranceEndMs);
923
+ if (!saved) {
924
+ return false;
925
+ }
911
926
  }
912
927
  // Mirror the turn onto the co-agent's long-lived prompt run so its Messages capture the full
913
928
  // conversation (run-viewer observability parity). Best-effort — never fails the transcript relay.
@@ -926,7 +941,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
926
941
  );
927
942
  }
928
943
  await this.sessionManager.Heartbeat(agentSessionId, contextUser, provider);
929
- return true;
944
+ return persistDetail;
930
945
  }
931
946
 
932
947
  /**
@@ -944,7 +959,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
944
959
  * the call and uploads the assembled blob (base64) once the session ends.
945
960
  *
946
961
  * Hard gates (in order):
947
- * 1. Ownership — `loadOwnedSession` enforces `UserID === contextUser.ID` (throws on violation).
962
+ * 1. Ownership — `LoadOwnedRealtimeSession` enforces `UserID === contextUser.ID` (throws on violation).
948
963
  * 2. Consent — `consent !== true` is a hard refusal: no audio is ever stored without it.
949
964
  * 3. Storage configuration — the session's agent must resolve a recording storage account (the
950
965
  * agent's `RecordingStorageProviderID`, else `AttachmentStorageProviderID`).
@@ -973,7 +988,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
973
988
  @Arg('peaks', () => [Float], { nullable: true }) peaks?: number[],
974
989
  ): Promise<UploadRealtimeRecordingResult> {
975
990
  const { contextUser, provider } = this.requireUserAndProvider(ctx.userPayload, ctx.providers);
976
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
991
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
977
992
 
978
993
  // HARD consent gate — no audio is ever stored without an explicit grant.
979
994
  if (consent !== true) {
@@ -1015,16 +1030,21 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1015
1030
  });
1016
1031
 
1017
1032
  // Canonical consolidated file written — drop the crash-recovery shards (best-effort).
1018
- if (stored.FileID) {
1033
+ // A FileID with an ErrorMessage is an uploaded file the session was not stamped with: the
1034
+ // shards are then the only recording the janitor could still recover, so keep them.
1035
+ const storedAndStamped = !!stored.FileID && !stored.ErrorMessage;
1036
+ if (storedAndStamped) {
1019
1037
  await deleteRealtimeRecordingSegments(agentSessionId, accountID, runUser);
1038
+ } else if (stored.FileID) {
1039
+ LogError(`RealtimeClientSessionResolver.UploadRealtimeRecording: session ${agentSessionId} has file ${stored.FileID} but it is not stamped (${stored.ErrorMessage}); shards kept`);
1020
1040
  }
1021
1041
 
1022
1042
  return {
1023
- Success: !!stored.FileID,
1043
+ Success: storedAndStamped,
1024
1044
  FileID: stored.FileID ?? undefined,
1025
1045
  // Report the reason the storage layer knew (e.g. Drive's "Service Accounts do not have
1026
1046
  // storage quota"); the generic sentence is only for when no layer supplied one.
1027
- ErrorMessage: stored.FileID ? undefined : (stored.ErrorMessage ?? 'Storage upload failed.'),
1047
+ ErrorMessage: storedAndStamped ? undefined : (stored.ErrorMessage ?? 'Storage upload failed.'),
1028
1048
  };
1029
1049
  } catch (error) {
1030
1050
  const message = error instanceof Error ? error.message : String(error);
@@ -1060,7 +1080,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1060
1080
  ): Promise<boolean> {
1061
1081
  try {
1062
1082
  const { contextUser, provider } = this.requireUserAndProvider(ctx.userPayload, ctx.providers);
1063
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
1083
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1064
1084
  // The consolidated recording already landed and its upload deleted the shards; a shard
1065
1085
  // arriving now (one still on the wire at hang-up) would be an orphan nothing deletes.
1066
1086
  if (session.RecordingFileID) {
@@ -1183,7 +1203,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1183
1203
  @Arg('callId', () => String, { nullable: true }) callId?: string,
1184
1204
  ): Promise<CancelRealtimeSessionToolResult> {
1185
1205
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
1186
- await this.loadOwnedSession(agentSessionId, contextUser, provider);
1206
+ await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1187
1207
  try {
1188
1208
  const aborted = this.clientSessionService.CancelInFlightDelegations(agentSessionId, callId);
1189
1209
  return { AbortedCount: aborted, Success: true };
@@ -1230,7 +1250,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1230
1250
  @Arg('usageDetailsJson', () => String, { nullable: true }) usageDetailsJson?: string,
1231
1251
  ): Promise<boolean> {
1232
1252
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
1233
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
1253
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1234
1254
 
1235
1255
  const inputDelta = this.clampTokenDelta(inputTokens);
1236
1256
  const outputDelta = this.clampTokenDelta(outputTokens);
@@ -1358,7 +1378,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1358
1378
  @Ctx() { userPayload, providers }: AppContext,
1359
1379
  ): Promise<boolean> {
1360
1380
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
1361
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
1381
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1362
1382
 
1363
1383
  if (!this.isChannelInSessionScope(session, channelName)) {
1364
1384
  LogError(`SaveSessionChannelState: channel '${channelName}' is not in session ${agentSessionId}'s resolved scope — state not saved.`);
@@ -1441,7 +1461,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1441
1461
  @Ctx() { userPayload, providers }: AppContext,
1442
1462
  ): Promise<SaveSessionChannelArtifactResult> {
1443
1463
  const { contextUser, provider } = this.requireUserAndProvider(userPayload, providers);
1444
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
1464
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1445
1465
 
1446
1466
  if (!this.isChannelInSessionScope(session, channelName)) {
1447
1467
  const message = `SaveSessionChannelArtifact: channel '${channelName}' is not in session ${agentSessionId}'s resolved scope — nothing saved.`;
@@ -1476,7 +1496,7 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1476
1496
  private requireUserAndProvider(
1477
1497
  userPayload: AppContext['userPayload'],
1478
1498
  providers: AppContext['providers'],
1479
- ): { contextUser: UserInfo; provider: IMetadataProvider } {
1499
+ ): { contextUser: UserInfo; provider: DatabaseProviderBase } {
1480
1500
  const contextUser = this.GetUserFromPayload(userPayload);
1481
1501
  if (!contextUser) {
1482
1502
  throw new Error('Not authenticated: no user context for realtime session operation');
@@ -1954,37 +1974,13 @@ export class RealtimeClientSessionResolver extends ResolverBase {
1954
1974
  contextUser: UserInfo,
1955
1975
  provider: IMetadataProvider,
1956
1976
  ): Promise<MJAIAgentSessionEntity> {
1957
- const session = await this.loadOwnedSession(agentSessionId, contextUser, provider);
1977
+ const session = await LoadOwnedRealtimeSession(agentSessionId, contextUser, provider);
1958
1978
  if (session.Status === 'Closed') {
1959
1979
  throw new Error(`Realtime session ${agentSessionId} is closed`);
1960
1980
  }
1961
1981
  return session;
1962
1982
  }
1963
1983
 
1964
- /**
1965
- * Loads a session and enforces inbound ownership (`UserID === contextUser.ID`) WITHOUT
1966
- * rejecting `Closed` sessions — {@link SaveSessionChannelState}'s final on-end flush
1967
- * legitimately arrives after the session closed. Throws when not found / not owned.
1968
- */
1969
- private async loadOwnedSession(
1970
- agentSessionId: string,
1971
- contextUser: UserInfo,
1972
- provider: IMetadataProvider,
1973
- ): Promise<MJAIAgentSessionEntity> {
1974
- const session = await provider.GetEntityObject<MJAIAgentSessionEntity>(SESSION_ENTITY, contextUser);
1975
- const loaded = await session.Load(agentSessionId);
1976
- if (!loaded) {
1977
- throw new Error(`Realtime session ${agentSessionId} not found`);
1978
- }
1979
- if (!UUIDsEqual(session.UserID, contextUser.ID)) {
1980
- LogError(
1981
- `RealtimeClientSessionResolver: ownership check failed — user ${contextUser.ID} attempted to operate on session ${agentSessionId} owned by ${session.UserID}`,
1982
- );
1983
- throw new Error('Not authorized: you do not own this realtime session');
1984
- }
1985
- return session;
1986
- }
1987
-
1988
1984
  /**
1989
1985
  * Resolves an ACTIVE channel definition (`MJ: AI Agent Channels`) by name from
1990
1986
  * {@link AIEngineBase}'s cached `AgentChannels` (provider-scoped engine instance, lazy
@@ -2087,14 +2083,18 @@ export class RealtimeClientSessionResolver extends ResolverBase {
2087
2083
  return null;
2088
2084
  }
2089
2085
  try {
2090
- const prior = await provider.GetEntityObject<MJAIAgentSessionEntity>(SESSION_ENTITY, contextUser);
2091
- if (!(await prior.Load(lastSessionId))) {
2086
+ const loaded = await LoadSessionForAccess(lastSessionId, contextUser, provider);
2087
+ if (!loaded) {
2092
2088
  LogError(`StartRealtimeClientSession: prior session ${lastSessionId} not found — skipping channel-state restore.`);
2093
2089
  return null;
2094
2090
  }
2095
- if (!UUIDsEqual(prior.UserID, contextUser.ID)) {
2091
+ const access = AuthorizeSessionAccess(
2092
+ { SessionUserID: loaded.Session.UserID, ConversationExternalID: loaded.ConversationExternalID, ConversationReadable: loaded.ConversationReadable },
2093
+ contextUser,
2094
+ );
2095
+ if (!access.Allowed) {
2096
2096
  LogError(
2097
- `StartRealtimeClientSession: prior session ${lastSessionId} is not owned by user ${contextUser.ID} — ` +
2097
+ `StartRealtimeClientSession: prior session ${lastSessionId} is not owned by user ${contextUser.ID} (${access.Reason}) — ` +
2098
2098
  'skipping channel-state restore.',
2099
2099
  );
2100
2100
  return null;
@@ -2301,24 +2301,28 @@ export class RealtimeClientSessionResolver extends ResolverBase {
2301
2301
  break;
2302
2302
  }
2303
2303
  visited.add(key);
2304
- const leg = await provider.GetEntityObject<MJAIAgentSessionEntity>(SESSION_ENTITY, contextUser);
2305
- if (!(await leg.Load(cursor))) {
2304
+ const loaded = await LoadSessionForAccess(cursor, contextUser, provider);
2305
+ if (!loaded) {
2306
2306
  if (legIDs.length === 0) {
2307
2307
  LogError(`StartRealtimeClientSession: prior session ${cursor} not found — skipping transcript hydration.`);
2308
2308
  }
2309
2309
  break;
2310
2310
  }
2311
- if (!UUIDsEqual(leg.UserID, contextUser.ID)) {
2311
+ const access = AuthorizeSessionAccess(
2312
+ { SessionUserID: loaded.Session.UserID, ConversationExternalID: loaded.ConversationExternalID, ConversationReadable: loaded.ConversationReadable },
2313
+ contextUser,
2314
+ );
2315
+ if (!access.Allowed) {
2312
2316
  if (legIDs.length === 0) {
2313
2317
  LogError(
2314
- `StartRealtimeClientSession: prior session ${cursor} is not owned by user ${contextUser.ID} — ` +
2318
+ `StartRealtimeClientSession: prior session ${cursor} is not owned by user ${contextUser.ID} (${access.Reason}) — ` +
2315
2319
  'skipping transcript hydration.',
2316
2320
  );
2317
2321
  }
2318
2322
  break;
2319
2323
  }
2320
- legIDs.push(leg.ID);
2321
- cursor = leg.LastSessionID ?? null;
2324
+ legIDs.push(loaded.Session.ID);
2325
+ cursor = loaded.Session.LastSessionID ?? null;
2322
2326
  }
2323
2327
  return legIDs;
2324
2328
  }
@@ -2599,8 +2603,9 @@ export class RealtimeClientSessionResolver extends ResolverBase {
2599
2603
  callerUser: UserInfo,
2600
2604
  provider: IMetadataProvider,
2601
2605
  ): Promise<string | null> {
2602
- const detail = await provider.GetEntityObject<MJConversationDetailEntity>(CONVERSATION_DETAIL_ENTITY, callerUser);
2606
+ const detail = await provider.GetEntityObject<MJConversationDetailEntityExtended>(CONVERSATION_DETAIL_ENTITY, callerUser);
2603
2607
  detail.NewRecord();
2608
+ detail.MarkServerAuthoredReply(); // a server-written system anchor, not the caller's words
2604
2609
  detail.ConversationID = session.ConversationID;
2605
2610
  detail.BranchID = this.sessionBranchIdOf(session);
2606
2611
  detail.Role = 'AI';
@@ -2868,17 +2873,26 @@ export class RealtimeClientSessionResolver extends ResolverBase {
2868
2873
 
2869
2874
  /**
2870
2875
  * Stamps `RecordingStartedAt` (the recording `t0` alignment origin) + `RecordingMedia` on a
2871
- * just-started session when the browser captured WITH consent. Best-effort: a parse/save failure
2872
- * is logged and swallowed — a recording-metadata problem must never fail the session start.
2876
+ * just-started session when the browser captured WITH consent. `RecordingStartedAt` goes through
2877
+ * the entity save (and stays set on the in-memory session, so a later save of it cannot clear it);
2878
+ * `RecordingMedia` is a recording-ledger column that an entity save cannot write (#6010), so it goes
2879
+ * through the ledger's `spStampAIAgentSessionRecordingFile` proc. The proc stamps `t0` as well, so it
2880
+ * lands even when the entity save fails: the janitor recovers only sessions that carry a
2881
+ * `RecordingStartedAt`. Best-effort: a parse/save/stamp failure is logged and swallowed — a
2882
+ * recording-metadata problem must never fail the session start.
2873
2883
  *
2874
2884
  * @param session The freshly created/loaded session.
2875
2885
  * @param recordingConsent Whether the user consented to recording — only `true` stamps anything.
2876
2886
  * @param recordingStartedAt ISO-8601 recording start timestamp from the browser.
2887
+ * @param contextUser The caller, on whose behalf the proc runs.
2888
+ * @param provider The server-side database provider the proc runs through.
2877
2889
  */
2878
2890
  private async stampRecordingStart(
2879
2891
  session: MJAIAgentSessionEntity,
2880
2892
  recordingConsent: boolean | undefined,
2881
2893
  recordingStartedAt: string | undefined,
2894
+ contextUser: UserInfo,
2895
+ provider: DatabaseProviderBase,
2882
2896
  ): Promise<void> {
2883
2897
  if (recordingConsent !== true || !recordingStartedAt) {
2884
2898
  return;
@@ -2889,12 +2903,19 @@ export class RealtimeClientSessionResolver extends ResolverBase {
2889
2903
  return;
2890
2904
  }
2891
2905
  session.RecordingStartedAt = startedAt;
2892
- session.RecordingMedia = 'Audio';
2893
2906
  if (!(await session.Save())) {
2894
2907
  LogError(
2895
2908
  `RealtimeClientSessionResolver.stampRecordingStart save failed for session ${session.ID}: ${this.describeSaveFailure(session)}`,
2896
2909
  );
2897
2910
  }
2911
+ try {
2912
+ await new SqlRealtimeRecordingLedger(provider, contextUser).StampFile(session.ID, { media: 'Audio', startedAt });
2913
+ } catch (error) {
2914
+ LogError(
2915
+ `RealtimeClientSessionResolver.stampRecordingStart media stamp failed for session ${session.ID}: ` +
2916
+ `${error instanceof Error ? error.message : String(error)}`,
2917
+ );
2918
+ }
2898
2919
  }
2899
2920
 
2900
2921
  /**
@@ -2913,11 +2934,14 @@ export class RealtimeClientSessionResolver extends ResolverBase {
2913
2934
  utteranceStartMs?: number,
2914
2935
  utteranceEndMs?: number,
2915
2936
  ): Promise<boolean> {
2916
- const detail = await provider.GetEntityObject<MJConversationDetailEntity>(
2937
+ const detail = await provider.GetEntityObject<MJConversationDetailEntityExtended>(
2917
2938
  CONVERSATION_DETAIL_ENTITY,
2918
2939
  contextUser,
2919
2940
  );
2920
2941
  detail.NewRecord();
2942
+ // The agent's side of the session's transcript is written here, on the server, for the session's owner
2943
+ // (the user's side is theirs and the marker does not touch it).
2944
+ detail.MarkServerAuthoredReply();
2921
2945
  detail.ConversationID = session.ConversationID;
2922
2946
  detail.BranchID = this.sessionBranchIdOf(session);
2923
2947
  detail.Role = this.mapTranscriptRole(role);
@@ -3006,10 +3030,11 @@ export class RealtimeClientSessionResolver extends ResolverBase {
3006
3030
  // failure here looked like before the re-load. The insert path beside this one has
3007
3031
  // always used `GetEntityObject`; this now matches it, which is also why they now succeed
3008
3032
  // and fail for the same reasons.
3009
- const previous = await provider.GetEntityObject<MJConversationDetailEntity>(
3033
+ const previous = await provider.GetEntityObject<MJConversationDetailEntityExtended>(
3010
3034
  CONVERSATION_DETAIL_ENTITY,
3011
3035
  writeUser,
3012
3036
  );
3037
+ previous.MarkServerAuthoredReply(); // as persistTranscriptTurn: the session's own transcript turn
3013
3038
  if (!(await previous.Load(found.ID))) {
3014
3039
  LogError(
3015
3040
  `RealtimeClientSessionResolver.replacePreviousTranscriptTurn could not re-load turn `
@@ -3050,6 +3075,28 @@ export class RealtimeClientSessionResolver extends ResolverBase {
3050
3075
  return role.trim().toLowerCase() === 'user' ? 'User' : 'AI';
3051
3076
  }
3052
3077
 
3078
+ /**
3079
+ * True when the session's conversation belongs to the caller. In client-direct mode the agent's
3080
+ * words and tool facts reach the server only from the browser, so the server can't vouch for them:
3081
+ * they are stored as the agent's reply only in the caller's own conversation (A19). In a
3082
+ * conversation shared with the caller, only the caller's own side of the session is kept.
3083
+ */
3084
+ private async callerOwnsConversation(
3085
+ session: MJAIAgentSessionEntity,
3086
+ contextUser: UserInfo,
3087
+ provider: IMetadataProvider,
3088
+ ): Promise<boolean> {
3089
+ if (!session.ConversationID) {
3090
+ return false;
3091
+ }
3092
+ const conversation = await provider.GetEntityObject<MJConversationEntity>('MJ: Conversations', contextUser);
3093
+ const owned = (await conversation.Load(session.ConversationID)) && UUIDsEqual(conversation.UserID, contextUser.ID);
3094
+ if (!owned) {
3095
+ LogStatus(`RealtimeClientSessionResolver: session ${session.ID} relays the agent's side only into the caller's own conversation; conversation ${session.ConversationID} is not the caller's, so the agent's turn is not stored.`);
3096
+ }
3097
+ return owned;
3098
+ }
3099
+
3053
3100
  /**
3054
3101
  * Persists a direct action or channel tool execution as a hidden ConversationDetail turn.
3055
3102
  * Stored as stable facts (Role: 'AI', HiddenToUser: true, ExternalID: callId, CompletionTime: durationMs,
@@ -3083,15 +3130,16 @@ export class RealtimeClientSessionResolver extends ResolverBase {
3083
3130
  contextUser: UserInfo,
3084
3131
  provider: IMetadataProvider,
3085
3132
  ): Promise<boolean> {
3086
- if (!session.ConversationID) {
3133
+ if (!(await this.callerOwnsConversation(session, contextUser, provider))) {
3087
3134
  return false;
3088
3135
  }
3089
3136
  try {
3090
- const detail = await provider.GetEntityObject<MJConversationDetailEntity>(
3137
+ const detail = await provider.GetEntityObject<MJConversationDetailEntityExtended>(
3091
3138
  CONVERSATION_DETAIL_ENTITY,
3092
3139
  contextUser,
3093
3140
  );
3094
3141
  detail.NewRecord();
3142
+ detail.MarkServerAuthoredReply(); // the tool's facts are recorded by the server, as the session's owner
3095
3143
  detail.ConversationID = session.ConversationID;
3096
3144
  detail.BranchID = this.sessionBranchIdOf(session);
3097
3145
  detail.Role = 'AI';
@@ -19,7 +19,6 @@
19
19
  import { Resolver, Mutation, Query, Arg, Ctx, Float, Int, ObjectType, Field, PubSub, PubSubEngine } from 'type-graphql';
20
20
  import { AppContext, UserPayload } from '../types.js';
21
21
  import { UserInfo, IMetadataProvider, LogError } from '@memberjunction/core';
22
- import { UUIDsEqual } from '@memberjunction/global';
23
22
  import { MJAIAgentSessionEntity, MJAIAgentEntity } from '@memberjunction/core-entities';
24
23
  import { NormalizeInstanceKey, RemoteBrowserEngine } from '@memberjunction/remote-browser-server';
25
24
  import { BeginBrowserGoalStep, FinalizeBrowserGoalStep, ExtractCoAgentRunID } from '../agentSessions/remoteBrowserGoalEngine.js';
@@ -35,13 +34,16 @@ import {
35
34
  } from '@memberjunction/remote-browser-base';
36
35
  import { ResolverBase } from '../generic/ResolverBase.js';
37
36
  import { GetReadWriteProvider } from '../util.js';
37
+ import {
38
+ LoadOwnedRealtimeSession,
39
+ RealtimeSessionNotFoundError,
40
+ RealtimeSessionOwnershipError,
41
+ } from '../realtimeSessions/ownedRealtimeSession.js';
38
42
  import { PUSH_STATUS_UPDATES_TOPIC } from '../generic/PushStatusResolver.js';
39
43
  import { AIEngine } from '@memberjunction/aiengine';
40
44
  import { AIPromptRunner } from '@memberjunction/ai-prompts';
41
45
  import { AIPromptParams } from '@memberjunction/ai-core-plus';
42
46
 
43
- /** Entity name — centralised so the `MJ:`-prefix convention is applied in exactly one place. */
44
- const SESSION_ENTITY = 'MJ: AI Agent Sessions';
45
47
  /** Entity name for the agent whose `TypeConfiguration` carries the remote-browser provider preference. */
46
48
  const AGENT_ENTITY = 'MJ: AI Agents';
47
49
  /**
@@ -1084,14 +1086,17 @@ export class RemoteBrowserActionResolver extends ResolverBase {
1084
1086
  * @returns The loaded, owned session entity.
1085
1087
  */
1086
1088
  protected async loadOwnedSession(agentSessionID: string, contextUser: UserInfo, provider: IMetadataProvider): Promise<MJAIAgentSessionEntity> {
1087
- const session = await provider.GetEntityObject<MJAIAgentSessionEntity>(SESSION_ENTITY, contextUser);
1088
- if (!(await session.Load(agentSessionID))) {
1089
- throw new Error(`Remote-browser session ${agentSessionID} not found.`);
1090
- }
1091
- if (!UUIDsEqual(session.UserID, contextUser.ID)) {
1092
- throw new Error(`Not authorized: remote-browser session ${agentSessionID} is not owned by you.`);
1089
+ try {
1090
+ return await LoadOwnedRealtimeSession(agentSessionID, contextUser, provider);
1091
+ } catch (err) {
1092
+ if (err instanceof RealtimeSessionNotFoundError) {
1093
+ throw new Error(`Remote-browser session ${agentSessionID} not found.`);
1094
+ }
1095
+ if (err instanceof RealtimeSessionOwnershipError) {
1096
+ throw new Error(`Not authorized: remote-browser session ${agentSessionID} is not owned by you.`);
1097
+ }
1098
+ throw err;
1093
1099
  }
1094
- return session;
1095
1100
  }
1096
1101
 
1097
1102
  /**