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

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 (407) hide show
  1. package/README.md +69 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/agentSessions/BridgeHostInstance.d.ts +8 -3
  4. package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -1
  5. package/dist/agentSessions/BridgeHostInstance.js +14 -4
  6. package/dist/agentSessions/BridgeHostInstance.js.map +1 -1
  7. package/dist/agentSessions/HandoffOfferExpiry.d.ts +10 -0
  8. package/dist/agentSessions/HandoffOfferExpiry.d.ts.map +1 -0
  9. package/dist/agentSessions/HandoffOfferExpiry.js +18 -0
  10. package/dist/agentSessions/HandoffOfferExpiry.js.map +1 -0
  11. package/dist/agentSessions/ReturningVisitorRecap.js +1 -0
  12. package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
  13. package/dist/agentSessions/SessionJanitor.d.ts +31 -8
  14. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
  15. package/dist/agentSessions/SessionJanitor.js +85 -41
  16. package/dist/agentSessions/SessionJanitor.js.map +1 -1
  17. package/dist/agentSessions/SessionManager.d.ts +14 -4
  18. package/dist/agentSessions/SessionManager.d.ts.map +1 -1
  19. package/dist/agentSessions/SessionManager.js +28 -4
  20. package/dist/agentSessions/SessionManager.js.map +1 -1
  21. package/dist/agentSessions/conversationBrowsers.d.ts +61 -0
  22. package/dist/agentSessions/conversationBrowsers.d.ts.map +1 -0
  23. package/dist/agentSessions/conversationBrowsers.js +118 -0
  24. package/dist/agentSessions/conversationBrowsers.js.map +1 -0
  25. package/dist/agentSessions/sessionDeadline.d.ts +43 -0
  26. package/dist/agentSessions/sessionDeadline.d.ts.map +1 -0
  27. package/dist/agentSessions/sessionDeadline.js +68 -0
  28. package/dist/agentSessions/sessionDeadline.js.map +1 -0
  29. package/dist/auth/APIKeyScopeAuth.d.ts.map +1 -1
  30. package/dist/auth/APIKeyScopeAuth.js +5 -4
  31. package/dist/auth/APIKeyScopeAuth.js.map +1 -1
  32. package/dist/auth/loginEmailResolution.d.ts +41 -0
  33. package/dist/auth/loginEmailResolution.d.ts.map +1 -0
  34. package/dist/auth/loginEmailResolution.js +47 -0
  35. package/dist/auth/loginEmailResolution.js.map +1 -0
  36. package/dist/auth/magicLink/MagicLinkKeys.d.ts +50 -2
  37. package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
  38. package/dist/auth/magicLink/MagicLinkKeys.js +129 -12
  39. package/dist/auth/magicLink/MagicLinkKeys.js.map +1 -1
  40. package/dist/auth/magicLink/MagicLinkRouter.d.ts +3 -0
  41. package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
  42. package/dist/auth/magicLink/MagicLinkRouter.js +23 -5
  43. package/dist/auth/magicLink/MagicLinkRouter.js.map +1 -1
  44. package/dist/auth/magicLink/MagicLinkService.d.ts +39 -5
  45. package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
  46. package/dist/auth/magicLink/MagicLinkService.js +221 -9
  47. package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
  48. package/dist/auth/magicLink/index.d.ts +1 -1
  49. package/dist/auth/magicLink/index.d.ts.map +1 -1
  50. package/dist/auth/magicLink/index.js +1 -1
  51. package/dist/auth/magicLink/index.js.map +1 -1
  52. package/dist/auth/magicLink/redeemLanding.d.ts +2 -2
  53. package/dist/auth/magicLink/redeemLanding.d.ts.map +1 -1
  54. package/dist/auth/magicLink/redeemLanding.js +7 -4
  55. package/dist/auth/magicLink/redeemLanding.js.map +1 -1
  56. package/dist/auth/magicLink/types.d.ts +27 -3
  57. package/dist/auth/magicLink/types.d.ts.map +1 -1
  58. package/dist/auth/magicLink/types.js +0 -4
  59. package/dist/auth/magicLink/types.js.map +1 -1
  60. package/dist/auth/newUsers.d.ts.map +1 -1
  61. package/dist/auth/newUsers.js +1 -0
  62. package/dist/auth/newUsers.js.map +1 -1
  63. package/dist/backgroundSweepConfigUnits.d.ts +231 -0
  64. package/dist/backgroundSweepConfigUnits.d.ts.map +1 -0
  65. package/dist/backgroundSweepConfigUnits.js +180 -0
  66. package/dist/backgroundSweepConfigUnits.js.map +1 -0
  67. package/dist/config.d.ts +906 -8
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +59 -0
  70. package/dist/config.js.map +1 -1
  71. package/dist/context.d.ts +2 -0
  72. package/dist/context.d.ts.map +1 -1
  73. package/dist/context.js +35 -4
  74. package/dist/context.js.map +1 -1
  75. package/dist/coreRoutes.d.ts +3 -1
  76. package/dist/coreRoutes.d.ts.map +1 -1
  77. package/dist/coreRoutes.js +6 -4
  78. package/dist/coreRoutes.js.map +1 -1
  79. package/dist/generated/graphql-schemas/__mj.d.ts +228 -0
  80. package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
  81. package/dist/generated/graphql-schemas/__mj.js +1163 -6
  82. package/dist/generated/graphql-schemas/__mj.js.map +1 -1
  83. package/dist/generic/OrphanedConversationDetailReconciler.d.ts +11 -0
  84. package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
  85. package/dist/generic/OrphanedConversationDetailReconciler.js +27 -1
  86. package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
  87. package/dist/generic/ResolverBase.d.ts +50 -5
  88. package/dist/generic/ResolverBase.d.ts.map +1 -1
  89. package/dist/generic/ResolverBase.js +321 -132
  90. package/dist/generic/ResolverBase.js.map +1 -1
  91. package/dist/generic/RunViewResolver.d.ts +4 -0
  92. package/dist/generic/RunViewResolver.d.ts.map +1 -1
  93. package/dist/generic/RunViewResolver.js +47 -7
  94. package/dist/generic/RunViewResolver.js.map +1 -1
  95. package/dist/index.d.ts +1 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +157 -35
  98. package/dist/index.js.map +1 -1
  99. package/dist/integration/ConnectionPause.d.ts +121 -0
  100. package/dist/integration/ConnectionPause.d.ts.map +1 -0
  101. package/dist/integration/ConnectionPause.js +183 -0
  102. package/dist/integration/ConnectionPause.js.map +1 -0
  103. package/dist/integration/CustomColumnPromoter.d.ts +72 -6
  104. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
  105. package/dist/integration/CustomColumnPromoter.js +87 -46
  106. package/dist/integration/CustomColumnPromoter.js.map +1 -1
  107. package/dist/integration/EntityMapLifecycle.d.ts +78 -4
  108. package/dist/integration/EntityMapLifecycle.d.ts.map +1 -1
  109. package/dist/integration/EntityMapLifecycle.js +199 -5
  110. package/dist/integration/EntityMapLifecycle.js.map +1 -1
  111. package/dist/integration/InactiveRowWarnings.d.ts +31 -0
  112. package/dist/integration/InactiveRowWarnings.d.ts.map +1 -1
  113. package/dist/integration/InactiveRowWarnings.js +29 -0
  114. package/dist/integration/InactiveRowWarnings.js.map +1 -1
  115. package/dist/integration/ObjectNamesByID.d.ts +28 -0
  116. package/dist/integration/ObjectNamesByID.d.ts.map +1 -0
  117. package/dist/integration/ObjectNamesByID.js +87 -0
  118. package/dist/integration/ObjectNamesByID.js.map +1 -0
  119. package/dist/integration/PostRestartIntrospect.d.ts +26 -0
  120. package/dist/integration/PostRestartIntrospect.d.ts.map +1 -0
  121. package/dist/integration/PostRestartIntrospect.js +27 -0
  122. package/dist/integration/PostRestartIntrospect.js.map +1 -0
  123. package/dist/integration/RSUPostRestartProgress.d.ts +176 -0
  124. package/dist/integration/RSUPostRestartProgress.d.ts.map +1 -0
  125. package/dist/integration/RSUPostRestartProgress.js +323 -0
  126. package/dist/integration/RSUPostRestartProgress.js.map +1 -0
  127. package/dist/integration/RSUProgressBridge.d.ts +41 -2
  128. package/dist/integration/RSUProgressBridge.d.ts.map +1 -1
  129. package/dist/integration/RSUProgressBridge.js +84 -1
  130. package/dist/integration/RSUProgressBridge.js.map +1 -1
  131. package/dist/integration/RunArtifactAuthorization.d.ts +39 -0
  132. package/dist/integration/RunArtifactAuthorization.d.ts.map +1 -0
  133. package/dist/integration/RunArtifactAuthorization.js +45 -0
  134. package/dist/integration/RunArtifactAuthorization.js.map +1 -0
  135. package/dist/integration/SchemaRefreshLaunch.d.ts +9 -0
  136. package/dist/integration/SchemaRefreshLaunch.d.ts.map +1 -1
  137. package/dist/integration/SchemaRefreshLaunch.js +3 -2
  138. package/dist/integration/SchemaRefreshLaunch.js.map +1 -1
  139. package/dist/integration/ScopedFieldsForBuild.d.ts +46 -0
  140. package/dist/integration/ScopedFieldsForBuild.d.ts.map +1 -0
  141. package/dist/integration/ScopedFieldsForBuild.js +91 -0
  142. package/dist/integration/ScopedFieldsForBuild.js.map +1 -0
  143. package/dist/integration/TargetConfigGuard.d.ts +40 -0
  144. package/dist/integration/TargetConfigGuard.d.ts.map +1 -0
  145. package/dist/integration/TargetConfigGuard.js +40 -0
  146. package/dist/integration/TargetConfigGuard.js.map +1 -0
  147. package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -1
  148. package/dist/realtimeSessions/ownedRealtimeSession.js +2 -3
  149. package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -1
  150. package/dist/realtimeSessions/sessionAccess.d.ts +15 -0
  151. package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
  152. package/dist/realtimeSessions/sessionAccess.js +22 -0
  153. package/dist/realtimeSessions/sessionAccess.js.map +1 -1
  154. package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
  155. package/dist/realtimeWidget/WidgetSessionService.js +2 -0
  156. package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
  157. package/dist/realtimeWidget/visitorIdentity.d.ts.map +1 -1
  158. package/dist/realtimeWidget/visitorIdentity.js +1 -0
  159. package/dist/realtimeWidget/visitorIdentity.js.map +1 -1
  160. package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
  161. package/dist/realtimeWidget/widgetCore.js +4 -7
  162. package/dist/realtimeWidget/widgetCore.js.map +1 -1
  163. package/dist/realtimeWidget/widgetGuestElevation.js +1 -0
  164. package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
  165. package/dist/requestOrigin.d.ts +22 -0
  166. package/dist/requestOrigin.d.ts.map +1 -0
  167. package/dist/requestOrigin.js +60 -0
  168. package/dist/requestOrigin.js.map +1 -0
  169. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
  170. package/dist/resolvers/AgentSessionResolver.js +6 -2
  171. package/dist/resolvers/AgentSessionResolver.js.map +1 -1
  172. package/dist/resolvers/AutotagPipelineResolver.d.ts.map +1 -1
  173. package/dist/resolvers/AutotagPipelineResolver.js +2 -1
  174. package/dist/resolvers/AutotagPipelineResolver.js.map +1 -1
  175. package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
  176. package/dist/resolvers/ConversationRunEventsResolver.js +2 -0
  177. package/dist/resolvers/ConversationRunEventsResolver.js.map +1 -1
  178. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +6 -0
  179. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -1
  180. package/dist/resolvers/ExecuteRemoteOperationResolver.js +30 -7
  181. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -1
  182. package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
  183. package/dist/resolvers/GetDataResolver.js +34 -13
  184. package/dist/resolvers/GetDataResolver.js.map +1 -1
  185. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +401 -11
  186. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  187. package/dist/resolvers/IntegrationDiscoveryResolver.js +2690 -1305
  188. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  189. package/dist/resolvers/MCPResolver.d.ts.map +1 -1
  190. package/dist/resolvers/MCPResolver.js +13 -2
  191. package/dist/resolvers/MCPResolver.js.map +1 -1
  192. package/dist/resolvers/QuerySystemUserResolver.d.ts +36 -0
  193. package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
  194. package/dist/resolvers/QuerySystemUserResolver.js +174 -87
  195. package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
  196. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
  197. package/dist/resolvers/RealtimeBridgeResolver.js +1 -0
  198. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
  199. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +6 -0
  200. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
  201. package/dist/resolvers/RealtimeClientSessionResolver.js +36 -5
  202. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
  203. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +62 -10
  204. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
  205. package/dist/resolvers/RemoteBrowserActionResolver.js +217 -34
  206. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
  207. package/dist/resolvers/RunAIAgentResolver.d.ts +24 -8
  208. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  209. package/dist/resolvers/RunAIAgentResolver.js +185 -67
  210. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  211. package/dist/resolvers/RunAIPromptResolver.d.ts +5 -3
  212. package/dist/resolvers/RunAIPromptResolver.d.ts.map +1 -1
  213. package/dist/resolvers/RunAIPromptResolver.js +19 -18
  214. package/dist/resolvers/RunAIPromptResolver.js.map +1 -1
  215. package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
  216. package/dist/resolvers/RunTemplateResolver.js +3 -1
  217. package/dist/resolvers/RunTemplateResolver.js.map +1 -1
  218. package/dist/resolvers/meetingRecordingRegistration.d.ts.map +1 -1
  219. package/dist/resolvers/meetingRecordingRegistration.js +2 -0
  220. package/dist/resolvers/meetingRecordingRegistration.js.map +1 -1
  221. package/dist/rest/MediaAccessKeys.d.ts.map +1 -1
  222. package/dist/rest/MediaAccessKeys.js +6 -2
  223. package/dist/rest/MediaAccessKeys.js.map +1 -1
  224. package/dist/rest/OAuthCallbackHandler.d.ts.map +1 -1
  225. package/dist/rest/OAuthCallbackHandler.js +11 -5
  226. package/dist/rest/OAuthCallbackHandler.js.map +1 -1
  227. package/dist/rest/RealtimeRecordingHandler.d.ts +3 -3
  228. package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -1
  229. package/dist/rest/RealtimeRecordingHandler.js +2 -4
  230. package/dist/rest/RealtimeRecordingHandler.js.map +1 -1
  231. package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
  232. package/dist/rest/ViewOperationsHandler.js +2 -1
  233. package/dist/rest/ViewOperationsHandler.js.map +1 -1
  234. package/dist/rest/setupRESTEndpoints.d.ts +3 -1
  235. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  236. package/dist/rest/setupRESTEndpoints.js +11 -2
  237. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  238. package/dist/services/DurableEntityActionTaskSubmitter.d.ts.map +1 -1
  239. package/dist/services/DurableEntityActionTaskSubmitter.js +6 -1
  240. package/dist/services/DurableEntityActionTaskSubmitter.js.map +1 -1
  241. package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
  242. package/dist/services/TaskGraphActionRunner.js +2 -26
  243. package/dist/services/TaskGraphActionRunner.js.map +1 -1
  244. package/dist/services/TaskGraphAgentRunner.d.ts +6 -0
  245. package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
  246. package/dist/services/TaskGraphAgentRunner.js +38 -0
  247. package/dist/services/TaskGraphAgentRunner.js.map +1 -1
  248. package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
  249. package/dist/services/TaskGraphContinuationDeliverer.js +1 -0
  250. package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
  251. package/dist/services/WorkQueueHostService.d.ts +2 -1
  252. package/dist/services/WorkQueueHostService.d.ts.map +1 -1
  253. package/dist/services/WorkQueueHostService.js +19 -4
  254. package/dist/services/WorkQueueHostService.js.map +1 -1
  255. package/dist/services/workQueueConfig.d.ts +2 -2
  256. package/dist/services/workQueueConfig.js +5 -5
  257. package/dist/services/workQueueConfig.js.map +1 -1
  258. package/dist/sharedCache.js +1 -1
  259. package/dist/sharedCache.js.map +1 -1
  260. package/dist/types.d.ts +17 -0
  261. package/dist/types.d.ts.map +1 -1
  262. package/dist/types.js.map +1 -1
  263. package/package.json +98 -98
  264. package/src/__tests__/APIKeyScopeAuth.test.ts +4 -8
  265. package/src/__tests__/ActiveOperations.test.ts +68 -0
  266. package/src/__tests__/AgentSessionResolver.ownership.test.ts +13 -0
  267. package/src/__tests__/AssertTargetConfigs.test.ts +75 -0
  268. package/src/__tests__/BatchApplyParity.test.ts +109 -0
  269. package/src/__tests__/BridgeHostInstance.test.ts +42 -0
  270. package/src/__tests__/CollectInactiveRowWarnings.test.ts +115 -0
  271. package/src/__tests__/ConnectionPause.test.ts +192 -0
  272. package/src/__tests__/ConnectionPauseResolver.test.ts +166 -0
  273. package/src/__tests__/ConnectionPauseSave.test.ts +143 -0
  274. package/src/__tests__/CustomColumnPromoterCompletion.test.ts +142 -0
  275. package/src/__tests__/DeleteConnectionRemovesPerConnectionCatalog.test.ts +51 -0
  276. package/src/__tests__/DetachedDiscoveryStart.test.ts +65 -0
  277. package/src/__tests__/DurableEntityActionTaskSubmitter.test.ts +73 -0
  278. package/src/__tests__/EntityMapLifecycle.rekey.test.ts +252 -0
  279. package/src/__tests__/ExecuteRemoteOperationResolver.test.ts +213 -0
  280. package/src/__tests__/HandoffOfferExpiry.test.ts +40 -0
  281. package/src/__tests__/IntegrationRunArtifactAuthorization.test.ts +126 -0
  282. package/src/__tests__/MagicLinkKeys.test.ts +102 -0
  283. package/src/__tests__/MediaAccessKeys.test.ts +57 -10
  284. package/src/__tests__/NewArrivalsDefaultDisabled.test.ts +50 -0
  285. package/src/__tests__/ObjectNamesByID.test.ts +176 -0
  286. package/src/__tests__/PostRestartMapsInCatalogScope.test.ts +146 -0
  287. package/src/__tests__/QuerySystemUserResolver.childRows.test.ts +80 -0
  288. package/src/__tests__/QuerySystemUserResolver.collision.test.ts +200 -0
  289. package/src/__tests__/QuerySystemUserResolver.permissions.test.ts +132 -0
  290. package/src/__tests__/RSUPostRestartProgress.test.ts +343 -0
  291. package/src/__tests__/RSUProgressBridge.test.ts +188 -5
  292. package/src/__tests__/RSUStartupSweepWiring.test.ts +30 -0
  293. package/src/__tests__/ReactivateRefreshMessage.test.ts +10 -3
  294. package/src/__tests__/RealtimeClientSessionResolver.test.ts +22 -0
  295. package/src/__tests__/{RefreshAdoptsNewColumns.test.ts → RefreshReportsNewColumns.test.ts} +25 -16
  296. package/src/__tests__/RemoteBrowserActionRecovery.test.ts +287 -0
  297. package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +3 -0
  298. package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +6 -4
  299. package/src/__tests__/RemoteBrowserAudioStream.test.ts +3 -0
  300. package/src/__tests__/RemoteBrowserSnapshot.test.ts +3 -0
  301. package/src/__tests__/ResolverBase.clientOldValues.test.ts +323 -26
  302. package/src/__tests__/ResolverBase.encryptedApiZeroReencrypt.test.ts +307 -0
  303. package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +53 -2
  304. package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +60 -0
  305. package/src/__tests__/RunAIAgentResolver.branch.test.ts +4 -0
  306. package/src/__tests__/RunAIAgentResolver.consultPredecessor.test.ts +289 -0
  307. package/src/__tests__/RunAIAgentResolver.handoff.test.ts +649 -0
  308. package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +2 -0
  309. package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +2 -0
  310. package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +87 -12
  311. package/src/__tests__/SchemaEvolutionCustomColumns.test.ts +119 -0
  312. package/src/__tests__/SchemaRefreshLaunch.test.ts +24 -2
  313. package/src/__tests__/ScopedFieldsForBuild.test.ts +192 -0
  314. package/src/__tests__/SessionJanitor.test.ts +195 -4
  315. package/src/__tests__/SessionManager.test.ts +69 -0
  316. package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +2 -2
  317. package/src/__tests__/TaskGraphAgentRunner.test.ts +272 -16
  318. package/src/__tests__/ViewOperationsHandler.test.ts +2 -5
  319. package/src/__tests__/WorkQueueHostService.test.ts +25 -7
  320. package/src/__tests__/backgroundSweepConfigUnits.test.ts +153 -0
  321. package/src/__tests__/contextRequestOrigin.test.ts +105 -0
  322. package/src/__tests__/conversationBrowsers.test.ts +62 -0
  323. package/src/__tests__/magicLinkExtensionCapabilities.test.ts +413 -0
  324. package/src/__tests__/orphanedConversationDetailReconciler.start.test.ts +82 -0
  325. package/src/__tests__/ownedRealtimeSession.test.ts +14 -0
  326. package/src/__tests__/realtimeRecordingHandler.test.ts +7 -2
  327. package/src/__tests__/realtimeSessions/sessionAccess.test.ts +79 -1
  328. package/src/__tests__/requestOrigin.test.ts +104 -0
  329. package/src/__tests__/securityHardeningPr6034.test.ts +120 -0
  330. package/src/__tests__/sessionDeadline.test.ts +51 -0
  331. package/src/__tests__/sharedCache.test.ts +11 -0
  332. package/src/__tests__/unifiedAuth.test.ts +73 -1
  333. package/src/agentSessions/BridgeHostInstance.ts +20 -5
  334. package/src/agentSessions/HandoffOfferExpiry.ts +18 -0
  335. package/src/agentSessions/ReturningVisitorRecap.ts +1 -0
  336. package/src/agentSessions/SessionJanitor.ts +92 -42
  337. package/src/agentSessions/SessionManager.ts +38 -8
  338. package/src/agentSessions/conversationBrowsers.ts +143 -0
  339. package/src/agentSessions/sessionDeadline.ts +81 -0
  340. package/src/auth/APIKeyScopeAuth.ts +5 -4
  341. package/src/auth/__tests__/loginEmailResolution.test.ts +67 -0
  342. package/src/auth/loginEmailResolution.ts +67 -0
  343. package/src/auth/magicLink/MagicLinkKeys.ts +146 -14
  344. package/src/auth/magicLink/MagicLinkRouter.ts +33 -6
  345. package/src/auth/magicLink/MagicLinkService.ts +250 -10
  346. package/src/auth/magicLink/index.ts +1 -1
  347. package/src/auth/magicLink/redeemLanding.ts +7 -4
  348. package/src/auth/magicLink/types.ts +28 -3
  349. package/src/auth/newUsers.ts +1 -0
  350. package/src/backgroundSweepConfigUnits.ts +213 -0
  351. package/src/config.ts +69 -0
  352. package/src/context.ts +39 -6
  353. package/src/coreRoutes.ts +14 -7
  354. package/src/generated/graphql-schemas/__mj.ts +822 -7
  355. package/src/generic/OrphanedConversationDetailReconciler.ts +32 -1
  356. package/src/generic/ResolverBase.ts +414 -143
  357. package/src/generic/RunViewResolver.ts +96 -49
  358. package/src/index.ts +201 -35
  359. package/src/integration/ConnectionPause.ts +222 -0
  360. package/src/integration/CustomColumnPromoter.ts +123 -53
  361. package/src/integration/EntityMapLifecycle.ts +229 -6
  362. package/src/integration/InactiveRowWarnings.ts +50 -0
  363. package/src/integration/ObjectNamesByID.ts +97 -0
  364. package/src/integration/PostRestartIntrospect.ts +49 -0
  365. package/src/integration/RSUPostRestartProgress.ts +359 -0
  366. package/src/integration/RSUProgressBridge.ts +85 -2
  367. package/src/integration/RunArtifactAuthorization.ts +65 -0
  368. package/src/integration/SchemaRefreshLaunch.ts +12 -2
  369. package/src/integration/ScopedFieldsForBuild.ts +113 -0
  370. package/src/integration/TargetConfigGuard.ts +71 -0
  371. package/src/realtimeSessions/ownedRealtimeSession.ts +2 -6
  372. package/src/realtimeSessions/sessionAccess.ts +30 -0
  373. package/src/realtimeWidget/WidgetSessionService.ts +2 -0
  374. package/src/realtimeWidget/visitorIdentity.ts +1 -0
  375. package/src/realtimeWidget/widgetCore.ts +4 -8
  376. package/src/realtimeWidget/widgetGuestElevation.ts +1 -0
  377. package/src/requestOrigin.ts +64 -0
  378. package/src/resolvers/AgentSessionResolver.ts +6 -2
  379. package/src/resolvers/AutotagPipelineResolver.ts +2 -1
  380. package/src/resolvers/ConversationRunEventsResolver.ts +2 -0
  381. package/src/resolvers/ExecuteRemoteOperationResolver.ts +41 -9
  382. package/src/resolvers/GetDataResolver.ts +35 -13
  383. package/src/resolvers/IntegrationDiscoveryResolver.ts +1522 -203
  384. package/src/resolvers/MCPResolver.ts +15 -2
  385. package/src/resolvers/QuerySystemUserResolver.ts +197 -98
  386. package/src/resolvers/RealtimeBridgeResolver.ts +1 -0
  387. package/src/resolvers/RealtimeClientSessionResolver.ts +40 -7
  388. package/src/resolvers/RemoteBrowserActionResolver.ts +242 -32
  389. package/src/resolvers/RunAIAgentResolver.ts +265 -86
  390. package/src/resolvers/RunAIPromptResolver.ts +18 -18
  391. package/src/resolvers/RunTemplateResolver.ts +3 -1
  392. package/src/resolvers/__tests__/GetDataResolver.tokens.test.ts +52 -0
  393. package/src/resolvers/meetingRecordingRegistration.ts +2 -0
  394. package/src/rest/MediaAccessKeys.ts +6 -2
  395. package/src/rest/OAuthCallbackHandler.ts +13 -5
  396. package/src/rest/RealtimeRecordingHandler.ts +5 -9
  397. package/src/rest/ViewOperationsHandler.ts +2 -1
  398. package/src/rest/setupRESTEndpoints.ts +11 -2
  399. package/src/services/DurableEntityActionTaskSubmitter.ts +10 -2
  400. package/src/services/TaskGraphActionRunner.ts +2 -23
  401. package/src/services/TaskGraphAgentRunner.ts +43 -0
  402. package/src/services/TaskGraphContinuationDeliverer.ts +1 -0
  403. package/src/services/WorkQueueHostService.ts +26 -4
  404. package/src/services/workQueueConfig.ts +5 -5
  405. package/src/sharedCache.ts +1 -1
  406. package/src/types.ts +18 -0
  407. package/src/services/TaskOrchestration-Integration.md +0 -188
@@ -81,6 +81,8 @@ interface FakeSession {
81
81
  LatestResult?: { CompleteMessage?: string };
82
82
  ClosedAt?: Date | null;
83
83
  CloseReason?: string | null;
84
+ Config_?: string | null;
85
+ LastActiveAt?: Date | string | null;
84
86
  }
85
87
 
86
88
  function makeUser(): UserInfo {
@@ -111,6 +113,10 @@ function makeProvider(store: Map<string, 'Active' | 'Idle' | 'Closed'>): {
111
113
  if (status == null) return false;
112
114
  entity.ID = id;
113
115
  entity.Status = status;
116
+ // CloseSession verifies a TimeLimit against the loaded row's stored deadline, so the
117
+ // fake row carries the swept row's Config_ like the real table row does.
118
+ entity.Config_ = sweptRows.get(id)?.Config_ ?? null;
119
+ entity.LastActiveAt = sweptRows.get(id)?.LastActiveAt ?? null;
114
120
  return true;
115
121
  }),
116
122
  Save: vi.fn(async () => {
@@ -130,6 +136,9 @@ function makeProvider(store: Map<string, 'Active' | 'Idle' | 'Closed'>): {
130
136
  return { provider, closedIds, closedReasons };
131
137
  }
132
138
 
139
+ /** Config_/LastActiveAt of rows a test swept, by session ID: sessionPages is consumed by the sweep before CloseSession loads the row. */
140
+ const sweptRows = new Map<string, { Config_?: string | null; LastActiveAt?: Date | string | null }>();
141
+
133
142
  /**
134
143
  * Queue of result-sets for the *session* sweep pages (entity 'MJ: AI Agent Sessions'). Channel
135
144
  * sweeps (entity 'MJ: AI Agent Session Channels', issued by CloseSession) always resolve empty so
@@ -145,6 +154,7 @@ function sessionSweepCalls(): Array<[{ EntityName: string; ExtraFilter: string;
145
154
  }
146
155
 
147
156
  beforeEach(() => {
157
+ sweptRows.clear();
148
158
  runViewMock.mockReset();
149
159
  logErrorMock.mockReset();
150
160
  logStatusMock.mockReset();
@@ -292,9 +302,114 @@ describe('SessionJanitor.RunStartupRecovery with two MJAPIs on one host (#5309)'
292
302
  });
293
303
  });
294
304
 
305
+ /** An ISO moment `minutes` before now, for a row's `LastActiveAt` / deadline. */
306
+ function minutesAgo(minutes: number): string {
307
+ return new Date(Date.now() - minutes * 60_000).toISOString();
308
+ }
309
+
310
+ /** Config_ JSON carrying an authored hard deadline `minutes` from now (negative = already passed). */
311
+ function deadlineConfig(minutesFromNow: number): string {
312
+ return JSON.stringify({ maxSessionDeadlineIso: new Date(Date.now() + minutesFromNow * 60_000).toISOString() });
313
+ }
314
+
315
+ describe('SessionJanitor close reasons (#6182): sweeps stamp TimeLimit / Idle via ExpiryCloseReason', () => {
316
+ // Idle threshold is 15 min (the janitor default); each row below is placed relative to it.
317
+ beforeEach(() => {
318
+ SessionJanitor.Instance.Configure({ closeThresholdMinutes: 15 });
319
+ });
320
+
321
+ async function runSweep(
322
+ sweep: 'RunStalenessSweep' | 'RunMaxDurationSweep',
323
+ row: Record<string, unknown>,
324
+ ): Promise<{ count: number; reason: string | null | undefined; closed: boolean }> {
325
+ const id = row.ID as string;
326
+ sweptRows.set(id, { Config_: row.Config_ as string | null, LastActiveAt: row.LastActiveAt as Date | string | null });
327
+ sessionPages = [{ Success: true, Results: [row] }];
328
+ const { provider, closedIds, closedReasons } = makeProvider(new Map([[id, 'Active']]));
329
+ const count = await SessionJanitor.Instance[sweep](provider, makeUser());
330
+ return { count, reason: closedReasons.get(id), closed: closedIds.includes(id) };
331
+ }
332
+
333
+ it('the staleness sweep stamps Idle for a quiet row without a deadline', async () => {
334
+ const r = await runSweep('RunStalenessSweep', { ID: 'q1', Status: 'Active', LastActiveAt: minutesAgo(60), Config_: null });
335
+ expect(r.reason).toBe('Idle');
336
+ });
337
+
338
+ it('the max-duration sweep stamps TimeLimit for an expired row', async () => {
339
+ const r = await runSweep('RunMaxDurationSweep', {
340
+ ID: 'd1', Status: 'Active', LastActiveAt: minutesAgo(1), Config_: deadlineConfig(-5),
341
+ });
342
+ expect(r.reason).toBe('TimeLimit');
343
+ });
344
+
345
+ it('the idle sweep stamps TimeLimit when the deadline came first', async () => {
346
+ // Idle moment = 60-15 = 45 min ago; deadline 50 min ago is earlier.
347
+ const r = await runSweep('RunStalenessSweep', {
348
+ ID: 'both1', Status: 'Active', LastActiveAt: minutesAgo(60), Config_: deadlineConfig(-50),
349
+ });
350
+ expect(r.reason).toBe('TimeLimit');
351
+ });
352
+
353
+ it('the deadline sweep stamps Idle when the idle moment came first', async () => {
354
+ // Idle moment = 60-15 = 45 min ago; deadline 5 min ago is later.
355
+ const r = await runSweep('RunMaxDurationSweep', {
356
+ ID: 'both2', Status: 'Active', LastActiveAt: minutesAgo(60), Config_: deadlineConfig(-5),
357
+ });
358
+ expect(r.reason).toBe('Idle');
359
+ });
360
+
361
+ /*
362
+ * The SQL already judged this row stale (LastActiveAt < cutoff), so an unparseable LastActiveAt
363
+ * must not make it immortal: it closes as Idle, and the fallback is logged with the session ID.
364
+ */
365
+ it('the staleness sweep closes a stale row with an unparseable LastActiveAt as Idle and LogErrors the fallback', async () => {
366
+ logErrorMock.mockClear();
367
+ const r = await runSweep('RunStalenessSweep', { ID: 'garbled1', Status: 'Active', LastActiveAt: 'not-a-date', Config_: null });
368
+ expect(r.closed).toBe(true);
369
+ expect(r.reason).toBe('Idle');
370
+ const messages = logErrorMock.mock.calls.map((c) => String(c[0]));
371
+ expect(messages.some((m) => m.includes('garbled1') && m.includes('LastActiveAt unparseable; closing as Idle'))).toBe(true);
372
+ });
373
+
374
+ it('the staleness sweep does not LogError for a row ExpiryCloseReason decides', async () => {
375
+ logErrorMock.mockClear();
376
+ await runSweep('RunStalenessSweep', { ID: 'q2', Status: 'Active', LastActiveAt: minutesAgo(60), Config_: null });
377
+ expect(logErrorMock).not.toHaveBeenCalled();
378
+ });
379
+
380
+ it('skips rows the callback returns null for, without loading their channels', async () => {
381
+ const row = { ID: 'future1', Status: 'Active', LastActiveAt: minutesAgo(1), Config_: deadlineConfig(30) };
382
+ sessionPages = [{ Success: true, Results: [row] }];
383
+ const { provider, closedIds } = makeProvider(new Map([['future1', 'Active']]));
384
+
385
+ const count = await SessionJanitor.Instance.RunMaxDurationSweep(provider, makeUser());
386
+
387
+ expect(count).toBe(0);
388
+ expect(closedIds).toEqual([]);
389
+ const channelReads = runViewMock.mock.calls.filter(
390
+ (c) => (c[0] as { EntityName: string }).EntityName === 'MJ: AI Agent Session Channels',
391
+ );
392
+ expect(channelReads).toHaveLength(0);
393
+ });
394
+
395
+ it('startup recovery still stamps Janitor and the shutdown drain Shutdown', async () => {
396
+ const ours = GetCurrentHostInstance();
397
+ const orphan = { ID: 'orphan1', Status: 'Active', HostInstanceID: `${ours.GetInstancePrefix()}31337:boot-before` };
398
+ sessionPages = [{ Success: true, Results: [orphan] }];
399
+ const recovery = makeProvider(new Map([['orphan1', 'Active']]));
400
+ await SessionJanitor.Instance.RunStartupRecovery(recovery.provider, makeUser());
401
+ expect(recovery.closedReasons.get('orphan1')).toBe('Janitor');
402
+
403
+ sessionPages = [{ Success: true, Results: [{ ID: 'live1', Status: 'Active' }] }];
404
+ const drain = makeProvider(new Map([['live1', 'Active']]));
405
+ await SessionJanitor.Instance.RunShutdownDrain(drain.provider, makeUser());
406
+ expect(drain.closedReasons.get('live1')).toBe('Shutdown');
407
+ });
408
+ });
409
+
295
410
  describe('SessionJanitor.RunStalenessSweep', () => {
296
411
  it('closes only past-threshold sessions (filter is staleness-scoped)', async () => {
297
- const stale = { ID: 'stale-1', Status: 'Idle' };
412
+ const stale = { ID: 'stale-1', Status: 'Idle', LastActiveAt: minutesAgo(60) };
298
413
  sessionPages = [{ Success: true, Results: [stale] }];
299
414
 
300
415
  const store = new Map<string, 'Active' | 'Idle' | 'Closed'>([['stale-1', 'Idle']]);
@@ -304,8 +419,8 @@ describe('SessionJanitor.RunStalenessSweep', () => {
304
419
 
305
420
  expect(count).toBe(1);
306
421
  expect(closedIds).toContain('stale-1');
307
- // Staleness sweep stamps the janitor close cause.
308
- expect(closedReasons.get('stale-1')).toBe('Janitor');
422
+ // A quiet row without a deadline closes as Idle.
423
+ expect(closedReasons.get('stale-1')).toBe('Idle');
309
424
 
310
425
  const filter = sessionSweepCalls()[0][0].ExtraFilter as string;
311
426
  expect(filter).toContain("Status IN ('Active','Idle')");
@@ -313,7 +428,7 @@ describe('SessionJanitor.RunStalenessSweep', () => {
313
428
  });
314
429
 
315
430
  it('is idempotent — re-closing already-Closed rows is a harmless no-op', async () => {
316
- const alreadyClosed = { ID: 'done-1', Status: 'Closed' };
431
+ const alreadyClosed = { ID: 'done-1', Status: 'Closed', LastActiveAt: minutesAgo(60) };
317
432
  sessionPages = [{ Success: true, Results: [alreadyClosed] }];
318
433
 
319
434
  const store = new Map<string, 'Active' | 'Idle' | 'Closed'>([['done-1', 'Closed']]);
@@ -1414,3 +1529,79 @@ describe('SessionJanitor.Configure', () => {
1414
1529
  expect(sessionSweepCalls()).toHaveLength(0);
1415
1530
  });
1416
1531
  });
1532
+
1533
+ describe('SessionJanitor.StartWithSettings (agentSessions.janitor)', () => {
1534
+ const HOUR = 60 * 60_000;
1535
+ const DEFAULTS = { enabled: true, sweepIntervalMs: 60_000, closeThresholdMinutes: 15 };
1536
+
1537
+ beforeEach(() => {
1538
+ vi.useFakeTimers();
1539
+ });
1540
+ afterEach(() => {
1541
+ SessionJanitor.Instance.Stop();
1542
+ SessionJanitor.Instance.Configure({ sweepIntervalMs: DEFAULTS.sweepIntervalMs, closeThresholdMinutes: DEFAULTS.closeThresholdMinutes });
1543
+ vi.clearAllTimers();
1544
+ vi.useRealTimers();
1545
+ });
1546
+
1547
+ it('enabled: runs startup recovery, then ticks on the configured interval with the configured threshold', async () => {
1548
+ const { provider } = makeProvider(new Map());
1549
+ await SessionJanitor.Instance.StartWithSettings({ ...DEFAULTS, sweepIntervalMs: 120_000, closeThresholdMinutes: 30 }, provider, makeUser());
1550
+ expect(sessionSweepCalls()).toHaveLength(1); // startup recovery
1551
+ expect(SessionJanitor.Instance.IsSweepScheduled).toBe(true);
1552
+
1553
+ vi.setSystemTime(new Date('2026-10-29T12:00:00.000Z'));
1554
+ await vi.advanceTimersByTimeAsync(120_000);
1555
+
1556
+ const staleness = sessionSweepCalls().map(c => c[0].ExtraFilter).find(f => f.includes('LastActiveAt <'));
1557
+ expect(staleness).toBeTruthy();
1558
+ // 30 minutes before the tick, not the default 15.
1559
+ expect(staleness).toContain('2026-10-29T11:32:00.000Z');
1560
+ });
1561
+
1562
+ it('disabled: runs startup recovery once and then issues no queries at all', async () => {
1563
+ const { provider } = makeProvider(new Map());
1564
+ await SessionJanitor.Instance.StartWithSettings({ ...DEFAULTS, enabled: false }, provider, makeUser());
1565
+ expect(sessionSweepCalls()).toHaveLength(1);
1566
+ runViewMock.mockClear();
1567
+
1568
+ await vi.advanceTimersByTimeAsync(HOUR);
1569
+
1570
+ expect(runViewMock).not.toHaveBeenCalled();
1571
+ expect(ledgerMock.PurgeClosedBefore).not.toHaveBeenCalled();
1572
+ expect(SessionJanitor.Instance.IsSweepScheduled).toBe(false);
1573
+ });
1574
+
1575
+ it('disabled: still drains this host\'s live sessions on graceful shutdown', async () => {
1576
+ sessionPages = [
1577
+ { Success: true, Results: [] }, // startup recovery
1578
+ { Success: true, Results: [{ ID: 'mine-3', Status: 'Active' }] }, // shutdown drain
1579
+ ];
1580
+ const store = new Map<string, 'Active' | 'Idle' | 'Closed'>([['mine-3', 'Active']]);
1581
+ const { provider, closedIds, closedReasons } = makeProvider(store);
1582
+
1583
+ await SessionJanitor.Instance.StartWithSettings({ ...DEFAULTS, enabled: false }, provider, makeUser());
1584
+ await SessionJanitor.Instance.Shutdown();
1585
+
1586
+ expect(closedIds).toContain('mine-3');
1587
+ expect(closedReasons.get('mine-3')).toBe('Shutdown');
1588
+ });
1589
+
1590
+ it('disabled after an earlier Start stops the tick that Start scheduled', async () => {
1591
+ const { provider } = makeProvider(new Map());
1592
+ await SessionJanitor.Instance.Start(provider, makeUser());
1593
+ expect(SessionJanitor.Instance.IsSweepScheduled).toBe(true);
1594
+
1595
+ await SessionJanitor.Instance.StartWithSettings({ ...DEFAULTS, enabled: false }, provider, makeUser());
1596
+
1597
+ expect(SessionJanitor.Instance.IsSweepScheduled).toBe(false);
1598
+ });
1599
+
1600
+ it('rejects an invalid interval before doing any work', async () => {
1601
+ const { provider } = makeProvider(new Map());
1602
+ await expect(SessionJanitor.Instance.StartWithSettings({ ...DEFAULTS, sweepIntervalMs: 0 }, provider, makeUser()))
1603
+ .rejects.toThrow(/sweepIntervalMs must be a finite number greater than 0/);
1604
+ expect(sessionSweepCalls()).toHaveLength(0);
1605
+ expect(SessionJanitor.Instance.IsSweepScheduled).toBe(false);
1606
+ });
1607
+ });
@@ -42,10 +42,12 @@ vi.mock('../realtimeSessions/RecordingAssemblyScheduler.js', () => ({
42
42
  // --- Mock RunView.FromMetadataProvider so channel sweeps and run-id lookups are controllable,
43
43
  // while leaving the rest of @memberjunction/core intact. RunViews runs each view through runViewMock. ---
44
44
  const runViewMock = vi.fn();
45
+ const logStatusMock = vi.fn();
45
46
  vi.mock('@memberjunction/core', async (importOriginal) => {
46
47
  const actual = await importOriginal<typeof import('@memberjunction/core')>();
47
48
  return {
48
49
  ...actual,
50
+ LogStatus: (...args: unknown[]) => logStatusMock(...args),
49
51
  RunView: {
50
52
  FromMetadataProvider: () => ({
51
53
  RunView: runViewMock,
@@ -71,6 +73,7 @@ vi.mock('@memberjunction/generic-database-provider', async (importOriginal) => {
71
73
  });
72
74
 
73
75
  import { SessionManager, SessionAuthorizationError } from '../agentSessions/SessionManager.js';
76
+ import { IsTrustedSessionConfigWriteActive } from '@memberjunction/core-entities-server';
74
77
  import type { UserInfo, IMetadataProvider } from '@memberjunction/core';
75
78
 
76
79
  /** Minimal fake BaseEntity-shaped record with the fields/methods SessionManager touches. */
@@ -336,6 +339,24 @@ describe('SessionManager.CreateSession', () => {
336
339
  });
337
340
 
338
341
  describe('SessionManager.CloseSession', () => {
342
+ // The session entity refuses a server-only CloseReason (TimeLimit, Idle, Janitor, Shutdown) from an untrusted save,
343
+ // so the one server path that stamps it must save inside the trusted session-write scope.
344
+ it('stamps the close reason inside the trusted session-write scope', async () => {
345
+ runViewMock.mockResolvedValue({ Success: true, Results: [] });
346
+ let trustedAtSave: boolean | null = null;
347
+ const session = makeSessionEntity({ ID: 'session-1', Status: 'Active' });
348
+ session.Save = vi.fn(async () => {
349
+ trustedAtSave = IsTrustedSessionConfigWriteActive();
350
+ return true;
351
+ });
352
+ const { provider } = makeProvider(() => session);
353
+
354
+ expect(await new SessionManager().CloseSession('session-1', makeUser(), provider, 'Janitor')).toBe(true);
355
+
356
+ expect(session.CloseReason).toBe('Janitor');
357
+ expect(trustedAtSave).toBe(true);
358
+ });
359
+
339
360
  it('sets Closed + ClosedAt, disconnects channels', async () => {
340
361
  const channel = makeSessionEntity({ ID: 'chan-1', Status: 'Connected' });
341
362
  runViewMock.mockResolvedValue({ Success: true, Results: [channel] });
@@ -529,6 +550,54 @@ describe('SessionManager.CloseSession', () => {
529
550
  },
530
551
  );
531
552
 
553
+ describe('a TimeLimit close is verified against the stored deadline', () => {
554
+ beforeEach(() => logStatusMock.mockClear());
555
+
556
+ it('is downgraded to Explicit and logged when the stored deadline is far away', async () => {
557
+ const deadline = new Date(Date.now() + 10 * 60_000).toISOString();
558
+ const session = makeSessionEntity({ ID: 'session-far', Status: 'Active', Config_: JSON.stringify({ maxSessionDeadlineIso: deadline }) });
559
+ const { provider } = makeProvider(() => session);
560
+
561
+ const ok = await new SessionManager().CloseSession('session-far', makeUser(), provider, 'TimeLimit');
562
+
563
+ expect(ok).toBe(true);
564
+ expect(session.CloseReason).toBe('Explicit');
565
+ const logged = logStatusMock.mock.calls.map((c) => String(c[0])).join('\n');
566
+ expect(logged).toContain('session-far');
567
+ expect(logged).toContain(deadline);
568
+ });
569
+
570
+ it('is stamped TimeLimit when the stored deadline has passed', async () => {
571
+ const deadline = new Date(Date.now() - 1_000).toISOString();
572
+ const session = makeSessionEntity({ ID: 'session-due', Status: 'Active', Config_: JSON.stringify({ maxSessionDeadlineIso: deadline }) });
573
+ const { provider } = makeProvider(() => session);
574
+
575
+ await new SessionManager().CloseSession('session-due', makeUser(), provider, 'TimeLimit');
576
+
577
+ expect(session.CloseReason).toBe('TimeLimit');
578
+ expect(logStatusMock).not.toHaveBeenCalled();
579
+ });
580
+
581
+ it('tells channel plugins the verified reason: Explicit for a downgraded claim, TimeLimit for a backed one', async () => {
582
+ const far = makeSessionEntity({ ID: 'session-far2', Status: 'Active', Config_: JSON.stringify({ maxSessionDeadlineIso: new Date(Date.now() + 10 * 60_000).toISOString() }) });
583
+ await new SessionManager().CloseSession('session-far2', makeUser(), makeProvider(() => far).provider, 'TimeLimit');
584
+ expect(hostSessionClosedMock).toHaveBeenLastCalledWith('session-far2', 'Explicit');
585
+
586
+ const due = makeSessionEntity({ ID: 'session-due2', Status: 'Active', Config_: JSON.stringify({ maxSessionDeadlineIso: new Date(Date.now() - 1_000).toISOString() }) });
587
+ await new SessionManager().CloseSession('session-due2', makeUser(), makeProvider(() => due).provider, 'TimeLimit');
588
+ expect(hostSessionClosedMock).toHaveBeenLastCalledWith('session-due2', 'TimeLimit');
589
+ });
590
+
591
+ it('is downgraded to Explicit when the session stores no deadline', async () => {
592
+ const session = makeSessionEntity({ ID: 'session-none', Status: 'Active', Config_: null });
593
+ const { provider } = makeProvider(() => session);
594
+
595
+ await new SessionManager().CloseSession('session-none', makeUser(), provider, 'TimeLimit');
596
+
597
+ expect(session.CloseReason).toBe('Explicit');
598
+ });
599
+ });
600
+
532
601
  it('does not overwrite the original CloseReason on an idempotent re-close', async () => {
533
602
  const session = makeSessionEntity({ ID: 'session-1', Status: 'Closed', CloseReason: 'Janitor' });
534
603
  const { provider } = makeProvider(() => session);
@@ -170,7 +170,7 @@ describe('TaskGraphActionRunner: outputs must be storable', () => {
170
170
  expect(result).toMatchObject({ Success: true, Output: { AgentRunID: 'RUN-1' }, ActionLogID: 'LOG-2' });
171
171
  });
172
172
 
173
- it('merges structured JSON Message properties into Output and aliases handle/Handle', async () => {
173
+ it('merges structured JSON Message properties into Output without synthesizing handle/Handle aliases', async () => {
174
174
  vi.mocked(ActionEngineServer.Instance.RunAction).mockResolvedValue({
175
175
  Success: true,
176
176
  Message: JSON.stringify({
@@ -188,10 +188,10 @@ describe('TaskGraphActionRunner: outputs must be storable', () => {
188
188
  expect(result.Success).toBe(true);
189
189
  expect(result.Output).toMatchObject({
190
190
  handle: 'doc-handle-99',
191
- Handle: 'doc-handle-99',
192
191
  documentType: 'pdf',
193
192
  fileName: 'Sales_Demo_Report.pdf',
194
193
  });
194
+ expect(result.Output).not.toHaveProperty('Handle');
195
195
  });
196
196
  });
197
197