@memberjunction/server 5.40.2 → 5.42.0

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 (129) hide show
  1. package/dist/agentSessions/HostInstance.d.ts +19 -0
  2. package/dist/agentSessions/HostInstance.d.ts.map +1 -0
  3. package/dist/agentSessions/HostInstance.js +48 -0
  4. package/dist/agentSessions/HostInstance.js.map +1 -0
  5. package/dist/agentSessions/SessionJanitor.d.ts +97 -0
  6. package/dist/agentSessions/SessionJanitor.d.ts.map +1 -0
  7. package/dist/agentSessions/SessionJanitor.js +222 -0
  8. package/dist/agentSessions/SessionJanitor.js.map +1 -0
  9. package/dist/agentSessions/SessionManager.d.ts +142 -0
  10. package/dist/agentSessions/SessionManager.d.ts.map +1 -0
  11. package/dist/agentSessions/SessionManager.js +308 -0
  12. package/dist/agentSessions/SessionManager.js.map +1 -0
  13. package/dist/agentSessions/index.d.ts +5 -0
  14. package/dist/agentSessions/index.d.ts.map +1 -0
  15. package/dist/agentSessions/index.js +31 -0
  16. package/dist/agentSessions/index.js.map +1 -0
  17. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +102 -0
  18. package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -0
  19. package/dist/agentSessions/remoteBrowserGoalEngine.js +164 -0
  20. package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -0
  21. package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts +66 -0
  22. package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts.map +1 -0
  23. package/dist/agentSessions/remoteBrowserGoalRegistry.js +86 -0
  24. package/dist/agentSessions/remoteBrowserGoalRegistry.js.map +1 -0
  25. package/dist/auth/initializeProviders.d.ts.map +1 -1
  26. package/dist/auth/initializeProviders.js +4 -2
  27. package/dist/auth/initializeProviders.js.map +1 -1
  28. package/dist/config.d.ts +7 -0
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +12 -2
  31. package/dist/config.js.map +1 -1
  32. package/dist/context.d.ts.map +1 -1
  33. package/dist/context.js +41 -7
  34. package/dist/context.js.map +1 -1
  35. package/dist/generated/generated.d.ts +1296 -49
  36. package/dist/generated/generated.d.ts.map +1 -1
  37. package/dist/generated/generated.js +50343 -43421
  38. package/dist/generated/generated.js.map +1 -1
  39. package/dist/generic/ResolverBase.js +1 -1
  40. package/dist/generic/ResolverBase.js.map +1 -1
  41. package/dist/generic/RunViewResolver.js +9 -10
  42. package/dist/generic/RunViewResolver.js.map +1 -1
  43. package/dist/index.d.ts +4 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +127 -54
  46. package/dist/index.js.map +1 -1
  47. package/dist/integration/CustomColumnPromoter.d.ts +108 -0
  48. package/dist/integration/CustomColumnPromoter.d.ts.map +1 -0
  49. package/dist/integration/CustomColumnPromoter.js +508 -0
  50. package/dist/integration/CustomColumnPromoter.js.map +1 -0
  51. package/dist/logging/StartupLogger.d.ts +177 -0
  52. package/dist/logging/StartupLogger.d.ts.map +1 -0
  53. package/dist/logging/StartupLogger.js +354 -0
  54. package/dist/logging/StartupLogger.js.map +1 -0
  55. package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
  56. package/dist/logging/variablesLoggingMiddleware.js +21 -2
  57. package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
  58. package/dist/resolvers/AgentSessionResolver.d.ts +42 -0
  59. package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -0
  60. package/dist/resolvers/AgentSessionResolver.js +152 -0
  61. package/dist/resolvers/AgentSessionResolver.js.map +1 -0
  62. package/dist/resolvers/EntityPermissionResolver.d.ts +16 -0
  63. package/dist/resolvers/EntityPermissionResolver.d.ts.map +1 -0
  64. package/dist/resolvers/EntityPermissionResolver.js +95 -0
  65. package/dist/resolvers/EntityPermissionResolver.js.map +1 -0
  66. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +27 -0
  67. package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -0
  68. package/dist/resolvers/ExecuteRemoteOperationResolver.js +136 -0
  69. package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -0
  70. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +101 -1
  71. package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
  72. package/dist/resolvers/IntegrationDiscoveryResolver.js +544 -42
  73. package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
  74. package/dist/resolvers/RealtimeBridgeResolver.d.ts +121 -0
  75. package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -0
  76. package/dist/resolvers/RealtimeBridgeResolver.js +533 -0
  77. package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -0
  78. package/dist/resolvers/RealtimeClientSessionResolver.d.ts +688 -0
  79. package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -0
  80. package/dist/resolvers/RealtimeClientSessionResolver.js +1774 -0
  81. package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -0
  82. package/dist/resolvers/RemoteBrowserActionResolver.d.ts +432 -0
  83. package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -0
  84. package/dist/resolvers/RemoteBrowserActionResolver.js +1108 -0
  85. package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -0
  86. package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
  87. package/dist/resolvers/RunAIAgentResolver.js +0 -7
  88. package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
  89. package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
  90. package/dist/rest/setupRESTEndpoints.js +5 -3
  91. package/dist/rest/setupRESTEndpoints.js.map +1 -1
  92. package/dist/services/ScheduledJobsService.d.ts.map +1 -1
  93. package/dist/services/ScheduledJobsService.js +6 -5
  94. package/dist/services/ScheduledJobsService.js.map +1 -1
  95. package/package.json +83 -74
  96. package/src/__tests__/RealtimeBridgeResolver.test.ts +100 -0
  97. package/src/__tests__/RealtimeClientSessionResolver.test.ts +2605 -0
  98. package/src/__tests__/RemoteBrowserAudioStream.test.ts +181 -0
  99. package/src/__tests__/RemoteBrowserGoalEngine.test.ts +144 -0
  100. package/src/__tests__/SessionJanitor.test.ts +234 -0
  101. package/src/__tests__/SessionManager.test.ts +465 -0
  102. package/src/__tests__/remoteBrowserGoalRegistry.test.ts +81 -0
  103. package/src/__tests__/subscriptionRedaction.test.ts +5 -0
  104. package/src/agentSessions/HostInstance.ts +53 -0
  105. package/src/agentSessions/SessionJanitor.ts +267 -0
  106. package/src/agentSessions/SessionManager.ts +446 -0
  107. package/src/agentSessions/index.ts +32 -0
  108. package/src/agentSessions/remoteBrowserGoalEngine.ts +191 -0
  109. package/src/agentSessions/remoteBrowserGoalRegistry.ts +122 -0
  110. package/src/auth/initializeProviders.ts +4 -2
  111. package/src/config.ts +13 -2
  112. package/src/context.ts +42 -7
  113. package/src/generated/generated.ts +33808 -29025
  114. package/src/generic/ResolverBase.ts +1 -1
  115. package/src/generic/RunViewResolver.ts +9 -9
  116. package/src/index.ts +128 -54
  117. package/src/integration/CustomColumnPromoter.ts +597 -0
  118. package/src/logging/StartupLogger.ts +441 -0
  119. package/src/logging/variablesLoggingMiddleware.ts +25 -5
  120. package/src/resolvers/AgentSessionResolver.ts +138 -0
  121. package/src/resolvers/EntityPermissionResolver.ts +73 -0
  122. package/src/resolvers/ExecuteRemoteOperationResolver.ts +108 -0
  123. package/src/resolvers/IntegrationDiscoveryResolver.ts +396 -37
  124. package/src/resolvers/RealtimeBridgeResolver.ts +450 -0
  125. package/src/resolvers/RealtimeClientSessionResolver.ts +2162 -0
  126. package/src/resolvers/RemoteBrowserActionResolver.ts +1122 -0
  127. package/src/resolvers/RunAIAgentResolver.ts +1 -9
  128. package/src/rest/setupRESTEndpoints.ts +5 -3
  129. package/src/services/ScheduledJobsService.ts +6 -5
@@ -0,0 +1,308 @@
1
+ import { LogError, RunView } from '@memberjunction/core';
2
+ import { AIAgentPermissionHelper } from '@memberjunction/ai-engine-base';
3
+ import { RealtimeClientSessionService, RealtimeChannelServerHost } from '@memberjunction/ai-agents';
4
+ import { GetHostInstanceID } from './HostInstance.js';
5
+ /** Entity names — centralised so the `MJ:`-prefix convention is applied in exactly one place. */
6
+ const SESSION_ENTITY = 'MJ: AI Agent Sessions';
7
+ const SESSION_CHANNEL_ENTITY = 'MJ: AI Agent Session Channels';
8
+ const CONVERSATION_ENTITY = 'MJ: Conversations';
9
+ /**
10
+ * Thrown by {@link SessionManager.CreateSession} when the caller lacks `CanRun` on the target
11
+ * agent. No session row is created — denial happens before any write. Distinct error type so the
12
+ * resolver layer can map it to an authorization-shaped GraphQL error rather than a generic failure.
13
+ */
14
+ export class SessionAuthorizationError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = 'SessionAuthorizationError';
18
+ }
19
+ }
20
+ /**
21
+ * Minimum interval between persisted `LastActiveAt` writes for a single session. A chatty channel
22
+ * (e.g. an audio heartbeat every ~200ms) must not turn into a DB write storm — heartbeats inside
23
+ * this window are coalesced in-memory and dropped. Mirrors the `SetSettingDebounced` philosophy.
24
+ */
25
+ const HEARTBEAT_MIN_WRITE_INTERVAL_MS = 3_000;
26
+ /**
27
+ * Server-side lifecycle manager for `AIAgentSession` **records** (not the media/socket transport —
28
+ * that arrives in P5). Stateless with respect to the caller: every method takes the request's
29
+ * `contextUser` and request-scoped `IMetadataProvider` so all entity operations run under the
30
+ * correct identity/provider and never pin the process-global provider.
31
+ *
32
+ * The only in-memory state it holds is a small per-session last-heartbeat-write timestamp map used
33
+ * purely to coalesce {@link Heartbeat} writes; it carries no user or provider state across calls.
34
+ *
35
+ * Responsibilities:
36
+ * - **Create** — authorize (`CanRun`), optionally mint a Conversation, persist an `Active` session.
37
+ * - **Close** — terminal transition; disconnect channels; idempotent.
38
+ * - **Heartbeat** — coalesced `LastActiveAt` bump; reactivates `Idle → Active`.
39
+ * - **MarkIdle** — `Active → Idle` transition when the last channel goes quiet.
40
+ *
41
+ * @remarks
42
+ * P5 will extend {@link CloseSession} to also abort any in-flight `AIAgentRun` (via its cancellation
43
+ * token) and call `ClientToolRequestManager.ClearSession(...)`; those resources don't exist yet at
44
+ * this layer, so close here only reconciles the durable record + channel rows.
45
+ */
46
+ export class SessionManager {
47
+ constructor() {
48
+ /** session ID (lowercased) → epoch ms of its last persisted `LastActiveAt` write. */
49
+ this.heartbeatLastWrite = new Map();
50
+ }
51
+ /**
52
+ * Authorize and create a new session. Flow:
53
+ * 1. `CanRun` check on the agent — denied → {@link SessionAuthorizationError}, no row written.
54
+ * 2. Resolve the conversation (use the supplied one, or create+link a fresh Conversation).
55
+ * 3. Persist the `AIAgentSession` as `Active`, stamped with this host and `LastActiveAt = now`.
56
+ *
57
+ * @returns the saved {@link MJAIAgentSessionEntity}.
58
+ * @throws {SessionAuthorizationError} when the user may not run the agent.
59
+ * @throws {Error} when conversation creation or session save fails.
60
+ */
61
+ async CreateSession(input, contextUser, provider) {
62
+ await this.assertCanRun(input.agentID, contextUser);
63
+ const conversationID = input.conversationID
64
+ ?? await this.createConversation(input, contextUser, provider);
65
+ const session = await this.persistNewSession(input, conversationID, contextUser, provider);
66
+ await this.notifyChannelPluginsSessionStarted(session, contextUser, provider);
67
+ return session;
68
+ }
69
+ /**
70
+ * Close a session (terminal). Sets `Status = 'Closed'` + `ClosedAt = now` + `CloseReason`, then
71
+ * disconnects all of its channel rows. Idempotent: closing an already-`Closed` session is a
72
+ * no-op that returns `true` (and never overwrites the original `CloseReason`). A
73
+ * missing/unloadable session returns `false`.
74
+ *
75
+ * @param closeReason Why the session is being closed. **Defaults to `'Explicit'`** so existing
76
+ * call sites (the user-initiated close mutations) stamp the common case without modification;
77
+ * background callers (janitor sweeps, shutdown drain, error teardowns) must pass their reason
78
+ * explicitly. `NULL` rows remain only for legacy data closed before the column existed.
79
+ *
80
+ * @remarks P5 adds in-flight-run abort + `ClientToolRequestManager.ClearSession` here.
81
+ */
82
+ async CloseSession(agentSessionID, contextUser, provider, closeReason = 'Explicit') {
83
+ const session = await this.loadSession(agentSessionID, contextUser, provider);
84
+ if (!session) {
85
+ return false;
86
+ }
87
+ this.heartbeatLastWrite.delete(agentSessionID.toLowerCase());
88
+ if (session.Status === 'Closed') {
89
+ // Idempotent — already terminal. Still notify the channel-plugin host so a session
90
+ // whose plugin instances live in THIS process is cleaned up even when another host
91
+ // instance won the close race (the host no-ops for sessions it doesn't hold).
92
+ await this.notifyChannelPluginsSessionClosed(agentSessionID, session.CloseReason ?? null);
93
+ return true;
94
+ }
95
+ const closed = await this.markSessionClosed(session, closeReason);
96
+ if (!closed) {
97
+ return false;
98
+ }
99
+ await this.disconnectChannels(agentSessionID, contextUser, provider);
100
+ await this.finalizeObservabilityRuns(session, contextUser, provider);
101
+ await this.notifyChannelPluginsSessionClosed(agentSessionID, closeReason);
102
+ return true;
103
+ }
104
+ /**
105
+ * Notifies the server-side channel-plugin host that a session started, so it can resolve the
106
+ * ACTIVE `MJ: AI Agent Channels` rows' `ServerPluginClass` plugins (one fresh instance per
107
+ * channel, per session) and fire their start hooks. Strictly best-effort: the host itself
108
+ * never throws, and this guard makes session creation immune to any plugin-layer surprise.
109
+ */
110
+ async notifyChannelPluginsSessionStarted(session, contextUser, provider) {
111
+ try {
112
+ await RealtimeChannelServerHost.Instance.OnSessionStarted({
113
+ AgentSessionID: session.ID,
114
+ AgentID: session.AgentID,
115
+ UserID: session.UserID,
116
+ ConversationID: session.ConversationID ?? null,
117
+ }, contextUser, provider);
118
+ }
119
+ catch (e) {
120
+ LogError(`SessionManager.notifyChannelPluginsSessionStarted failed: ${e instanceof Error ? e.message : String(e)}`);
121
+ }
122
+ }
123
+ /**
124
+ * Notifies the server-side channel-plugin host that a session closed (fires each plugin's
125
+ * `OnSessionClosed` hook and schedules disposal). Invoked from EVERY close provenance —
126
+ * explicit hang-up, janitor sweeps, shutdown drain, and error teardowns all funnel through
127
+ * {@link CloseSession}. Strictly best-effort, same posture as the start notification.
128
+ */
129
+ async notifyChannelPluginsSessionClosed(agentSessionID, closeReason) {
130
+ try {
131
+ await RealtimeChannelServerHost.Instance.OnSessionClosed(agentSessionID, closeReason);
132
+ }
133
+ catch (e) {
134
+ LogError(`SessionManager.notifyChannelPluginsSessionClosed failed: ${e instanceof Error ? e.message : String(e)}`);
135
+ }
136
+ }
137
+ /**
138
+ * Finalizes the co-agent observability runs a realtime voice session stored in its `Config_`
139
+ * (see `RealtimeClientSessionResolver`). No-op when the config carries no run ids. Tolerant: a
140
+ * malformed config or a finalize failure is swallowed so it can never break session close.
141
+ */
142
+ async finalizeObservabilityRuns(session, contextUser, provider) {
143
+ const config = this.parseSessionRunConfig(session.Config_);
144
+ if (!config.coAgentRunID && !config.promptRunID && !config.coAgentRunStepID) {
145
+ return;
146
+ }
147
+ try {
148
+ await new RealtimeClientSessionService().FinalizeCoAgentRun(config.coAgentRunID ?? null, config.promptRunID ?? null, contextUser, provider, true, config.coAgentRunStepID ?? null);
149
+ }
150
+ catch (e) {
151
+ LogError(`SessionManager.finalizeObservabilityRuns failed: ${e instanceof Error ? e.message : String(e)}`);
152
+ }
153
+ }
154
+ /** Parses the session's `Config_` for observability run ids; returns an empty config on any error. */
155
+ parseSessionRunConfig(raw) {
156
+ if (!raw) {
157
+ return {};
158
+ }
159
+ try {
160
+ const parsed = JSON.parse(raw);
161
+ return {
162
+ coAgentRunID: typeof parsed.coAgentRunID === 'string' ? parsed.coAgentRunID : undefined,
163
+ promptRunID: typeof parsed.promptRunID === 'string' ? parsed.promptRunID : undefined,
164
+ coAgentRunStepID: typeof parsed.coAgentRunStepID === 'string' ? parsed.coAgentRunStepID : undefined,
165
+ };
166
+ }
167
+ catch {
168
+ return {};
169
+ }
170
+ }
171
+ /**
172
+ * Record activity on a session. Coalesces persisted writes so at most one `LastActiveAt` update
173
+ * lands per session per {@link HEARTBEAT_MIN_WRITE_INTERVAL_MS}; intervening calls return `true`
174
+ * without touching the DB. Reactivates an `Idle` session to `Active`. A `Closed` session is not
175
+ * reactivated (returns `false`).
176
+ */
177
+ async Heartbeat(agentSessionID, contextUser, provider) {
178
+ const key = agentSessionID.toLowerCase();
179
+ const reactivating = this.shouldForceWrite(key, provider);
180
+ if (!reactivating && !this.heartbeatWriteDue(key)) {
181
+ return true; // coalesced — skip the DB round-trip
182
+ }
183
+ return this.persistHeartbeat(agentSessionID, contextUser, provider);
184
+ }
185
+ /**
186
+ * Transition an `Active` session to `Idle` (e.g. when its last channel disconnects). No-op when
187
+ * the session is already `Idle` or `Closed`. Returns `true` on success / no-op-needed.
188
+ */
189
+ async MarkIdle(agentSessionID, contextUser, provider) {
190
+ const session = await this.loadSession(agentSessionID, contextUser, provider);
191
+ if (!session) {
192
+ return false;
193
+ }
194
+ if (session.Status !== 'Active') {
195
+ return true; // only Active → Idle is meaningful
196
+ }
197
+ session.Status = 'Idle';
198
+ return this.saveOrLog(session, 'MarkIdle');
199
+ }
200
+ // ----- internals -------------------------------------------------------------------------
201
+ /** Throws {@link SessionAuthorizationError} unless the user has `CanRun` on the agent. */
202
+ async assertCanRun(agentID, contextUser) {
203
+ const canRun = await AIAgentPermissionHelper.HasPermission(agentID, contextUser, 'run');
204
+ if (!canRun) {
205
+ throw new SessionAuthorizationError(`User ${contextUser.Email} is not permitted to open a session for agent ${agentID}`);
206
+ }
207
+ }
208
+ /** Creates and links a new Conversation for the session, returning its ID. */
209
+ async createConversation(input, contextUser, provider) {
210
+ const conversation = await provider.GetEntityObject(CONVERSATION_ENTITY, contextUser);
211
+ conversation.NewRecord();
212
+ conversation.UserID = input.userID;
213
+ conversation.Name = 'Agent Session';
214
+ const saved = await conversation.Save();
215
+ if (!saved) {
216
+ throw new Error(`Failed to create Conversation for agent session: ${conversation.LatestResult?.CompleteMessage ?? 'unknown error'}`);
217
+ }
218
+ return conversation.ID;
219
+ }
220
+ /** Persists the new session row as `Active` and returns the saved entity. */
221
+ async persistNewSession(input, conversationID, contextUser, provider) {
222
+ const session = await provider.GetEntityObject(SESSION_ENTITY, contextUser);
223
+ session.NewRecord();
224
+ session.AgentID = input.agentID;
225
+ session.UserID = input.userID;
226
+ session.Status = 'Active';
227
+ session.ConversationID = conversationID;
228
+ session.LastSessionID = input.lastSessionID ?? null;
229
+ session.HostInstanceID = GetHostInstanceID();
230
+ session.Config_ = input.config ?? null;
231
+ session.LastActiveAt = new Date();
232
+ const saved = await session.Save();
233
+ if (!saved) {
234
+ throw new Error(`Failed to create agent session: ${session.LatestResult?.CompleteMessage ?? 'unknown error'}`);
235
+ }
236
+ return session;
237
+ }
238
+ /** Loads a session entity by ID, returning null when it cannot be loaded. */
239
+ async loadSession(agentSessionID, contextUser, provider) {
240
+ const session = await provider.GetEntityObject(SESSION_ENTITY, contextUser);
241
+ const loaded = await session.Load(agentSessionID);
242
+ return loaded ? session : null;
243
+ }
244
+ /** Sets `Closed` + `ClosedAt` + `CloseReason` and saves. Returns the save result. */
245
+ async markSessionClosed(session, closeReason) {
246
+ session.Status = 'Closed';
247
+ session.ClosedAt = new Date();
248
+ session.CloseReason = closeReason;
249
+ return this.saveOrLog(session, 'CloseSession');
250
+ }
251
+ /** Marks every channel row of the session `Disconnected` (with `DisconnectedAt`), if not already. */
252
+ async disconnectChannels(agentSessionID, contextUser, provider) {
253
+ const rv = RunView.FromMetadataProvider(provider);
254
+ const result = await rv.RunView({
255
+ EntityName: SESSION_CHANNEL_ENTITY,
256
+ ExtraFilter: `AgentSessionID='${agentSessionID}' AND Status <> 'Disconnected'`,
257
+ ResultType: 'entity_object',
258
+ }, contextUser);
259
+ if (!result.Success) {
260
+ LogError(`SessionManager.disconnectChannels failed to load channels: ${result.ErrorMessage}`);
261
+ return;
262
+ }
263
+ const now = new Date();
264
+ for (const channel of result.Results) {
265
+ channel.Status = 'Disconnected';
266
+ channel.DisconnectedAt = now;
267
+ await this.saveOrLog(channel, 'disconnectChannels');
268
+ }
269
+ }
270
+ /** True once the per-session coalescing window has elapsed since the last persisted write. */
271
+ heartbeatWriteDue(key) {
272
+ const last = this.heartbeatLastWrite.get(key);
273
+ return last == null || (Date.now() - last) >= HEARTBEAT_MIN_WRITE_INTERVAL_MS;
274
+ }
275
+ /**
276
+ * Whether this heartbeat must write regardless of the coalescing window. We force a write the
277
+ * first time we see a session (no cached timestamp) so an `Idle → Active` reactivation is never
278
+ * swallowed by coalescing.
279
+ */
280
+ shouldForceWrite(key, _provider) {
281
+ return !this.heartbeatLastWrite.has(key);
282
+ }
283
+ /** Loads, bumps `LastActiveAt`, reactivates `Idle → Active`, saves, and records the write time. */
284
+ async persistHeartbeat(agentSessionID, contextUser, provider) {
285
+ const session = await this.loadSession(agentSessionID, contextUser, provider);
286
+ if (!session || session.Status === 'Closed') {
287
+ return false;
288
+ }
289
+ session.LastActiveAt = new Date();
290
+ if (session.Status === 'Idle') {
291
+ session.Status = 'Active';
292
+ }
293
+ const saved = await this.saveOrLog(session, 'Heartbeat');
294
+ if (saved) {
295
+ this.heartbeatLastWrite.set(agentSessionID.toLowerCase(), Date.now());
296
+ }
297
+ return saved;
298
+ }
299
+ /** Saves an entity, logging `CompleteMessage` on failure. Returns the boolean save result. */
300
+ async saveOrLog(entity, op) {
301
+ const saved = await entity.Save();
302
+ if (!saved) {
303
+ LogError(`SessionManager.${op} save failed: ${entity.LatestResult?.CompleteMessage ?? 'unknown error'}`);
304
+ }
305
+ return saved;
306
+ }
307
+ }
308
+ //# sourceMappingURL=SessionManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.js","sourceRoot":"","sources":["../../src/agentSessions/SessionManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './remoteBrowserGoalEngine.js';
2
+ export * from './HostInstance.js';
3
+ export * from './SessionManager.js';
4
+ export * from './SessionJanitor.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agentSessions/index.ts"],"names":[],"mappings":"AA4BA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @fileoverview AI Agent Session lifecycle substrate (server-side, record + janitor layer).
3
+ *
4
+ * Exposes the session-record lifecycle manager, the orphan-reconciliation janitor, and the
5
+ * per-process host identity helpers. The audio/media (WebRTC) transport and the long-lived
6
+ * realtime agent run are deliberately **not** part of this layer — they arrive in P5.
7
+ *
8
+ * @module @memberjunction/server
9
+ */
10
+ import { LoadWhiteboardChannelServer, LoadMeetingControlsChannelServer } from '@memberjunction/ai-agents';
11
+ import { LoadRemoteBrowserChannel } from '@memberjunction/remote-browser-server';
12
+ import { LoadSelfHostRemoteBrowser } from '@memberjunction/remote-browser-selfhost';
13
+ import { BindRemoteBrowserGoalEngine } from './remoteBrowserGoalEngine.js';
14
+ // Tree-shaking prevention: force the server-side channel plugin registrations
15
+ // (`@RegisterClass(BaseRealtimeChannelServer, ...)`) to execute on any static path that touches
16
+ // the session lifecycle — `SessionManager.CreateSession` resolves them via the ClassFactory.
17
+ LoadWhiteboardChannelServer();
18
+ LoadMeetingControlsChannelServer();
19
+ // Remote Browser native channel (client-direct): the lifecycle-only server channel plugin + the
20
+ // Self-Hosted Chrome backend driver (whose default runner launches a local headless Chromium via
21
+ // Playwright — pulled in transitively through the SelfHost package, documented and acceptable).
22
+ LoadRemoteBrowserChannel();
23
+ LoadSelfHostRemoteBrowser();
24
+ // Bind goal-driven browser control to MJ's computer-use engine (vision-model auto-selection, prompt-run
25
+ // logging, credential resolution). Without this, RunComputerUseGoal falls back to the bare base engine.
26
+ BindRemoteBrowserGoalEngine();
27
+ export * from './remoteBrowserGoalEngine.js';
28
+ export * from './HostInstance.js';
29
+ export * from './SessionManager.js';
30
+ export * from './SessionJanitor.js';
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agentSessions/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,2BAA2B,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC;AAC1G,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAE3E,8EAA8E;AAC9E,gGAAgG;AAChG,6FAA6F;AAC7F,2BAA2B,EAAE,CAAC;AAC9B,gCAAgC,EAAE,CAAC;AACnC,gGAAgG;AAChG,iGAAiG;AACjG,gGAAgG;AAChG,wBAAwB,EAAE,CAAC;AAC3B,yBAAyB,EAAE,CAAC;AAC5B,wGAAwG;AACxG,wGAAwG;AACxG,2BAA2B,EAAE,CAAC;AAE9B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @fileoverview **Production binding** of the Remote Browser goal-engine seam to MJ's computer-use engine.
3
+ *
4
+ * The CDP remote-browser session ({@link CdpRemoteBrowserSession}) runs goal-driven browser control through
5
+ * an injectable {@link ComputerUseGoalRun} factory. By default that factory yields a bare
6
+ * `ProgressComputerUseEngine` (base computer-use), which has no model auto-selection — so it can't run a goal
7
+ * unsupervised. This module binds the factory to {@link MJProgressComputerUseEngine}: an MJ-aware engine that
8
+ * routes controller/judge prompts through `AIPromptRunner`, auto-selects a vision-capable model from MJ
9
+ * metadata, persists step media, AND forwards per-step progress for live narration.
10
+ *
11
+ * Called once at server startup from `agentSessions/index.ts` (the realtime composition point), so every
12
+ * goal-driven run on this process uses the MJ engine. Kept here (an application package), not in any library,
13
+ * because it is the seam where the transport-neutral CDP layer is wired to MJ's AI stack.
14
+ *
15
+ * @module @memberjunction/server
16
+ */
17
+ import { UserInfo, IMetadataProvider } from '@memberjunction/core';
18
+ import { MJAIAgentRunStepEntity } from '@memberjunction/core-entities';
19
+ import type { ComputerUseResult, RunComputerUseParams, StepRecord } from '@memberjunction/computer-use';
20
+ import { MJComputerUseEngine, MJRunComputerUseParams } from '@memberjunction/computer-use-engine';
21
+ import type { RemoteBrowserGoalResult } from '@memberjunction/remote-browser-base';
22
+ import { type ComputerUseGoalProgress, type ComputerUseGoalRun } from '@memberjunction/remote-browser-cdp';
23
+ /**
24
+ * Adapts the transport-neutral base computer-use params into {@link MJRunComputerUseParams}, injecting the
25
+ * acting user so the MJ engine can run its controller/judge prompts as that user. Pure + exported for unit
26
+ * testing; {@link MJProgressComputerUseEngine.Run} is a one-liner over it.
27
+ *
28
+ * @param params The base computer-use params the CDP session built (goal + step cap + start url).
29
+ * @param contextUser The acting MJ user (may be undefined; the MJ engine handles the unset case).
30
+ * @param agentRunID Optional parent agent-run id (links the goal's prompt runs to the realtime agent run).
31
+ * @param agentRunStepID Optional parent agent-run-step id (nests a child prompt step per prompt under it).
32
+ * @returns The MJ-aware params to hand to `MJComputerUseEngine.Run`.
33
+ */
34
+ export declare function buildMJGoalParams(params: RunComputerUseParams, contextUser?: UserInfo, agentRunID?: string, agentRunStepID?: string): MJRunComputerUseParams;
35
+ /**
36
+ * The MJ-aware goal engine bound in production. Extends {@link MJComputerUseEngine} (prompt-runner routing,
37
+ * vision-model auto-selection, credential resolution, media persistence) and satisfies the
38
+ * {@link ComputerUseGoalRun} seam:
39
+ *
40
+ * - {@link SetBrowserAdapter} / {@link Stop} are inherited from the base computer-use engine.
41
+ * - {@link OnProgress} + {@link ContextUser} are set by the CDP session before {@link Run}.
42
+ * - {@link Run} adapts the transport-neutral base params into {@link MJRunComputerUseParams} (injecting the
43
+ * acting user) so the MJ engine can run its prompts.
44
+ * - {@link onStepComplete} chains the MJ media-persistence behavior, then forwards a model-safe progress note.
45
+ */
46
+ export declare class MJProgressComputerUseEngine extends MJComputerUseEngine implements ComputerUseGoalRun {
47
+ /** Per-step progress hook the CDP session sets before {@link Run} (drives live narration). */
48
+ OnProgress?: (progress: ComputerUseGoalProgress) => void;
49
+ /** The MJ user the goal run executes as; the CDP session sets it from the session's context user. */
50
+ ContextUser?: UserInfo;
51
+ /** Parent agent-run id the CDP session sets before {@link Run} (links the goal's prompt runs to it). */
52
+ AgentRunID?: string;
53
+ /** Parent agent-run-step id the CDP session sets before {@link Run} (nests child prompt steps under it). */
54
+ AgentRunStepID?: string;
55
+ /**
56
+ * Adapts the transport-neutral base params into {@link MJRunComputerUseParams} (carrying the acting user
57
+ * + the parent run/step linkage) and delegates to the MJ engine, which auto-selects a vision-capable
58
+ * controller model from MJ metadata and nests a child step per prompt when the linkage is present.
59
+ *
60
+ * @param params The base computer-use params the CDP session built (goal + step cap + start url).
61
+ * @returns The computer-use run result.
62
+ */
63
+ Run(params: RunComputerUseParams): Promise<ComputerUseResult>;
64
+ protected onStepComplete(step: StepRecord, params: MJRunComputerUseParams): void;
65
+ }
66
+ /**
67
+ * Extracts the realtime co-agent observability run id from an `AIAgentSession.Config_` JSON blob (the
68
+ * `coAgentRunID` a voice session persists). Returns `undefined` for a missing/non-voice/un-parseable
69
+ * config — goal-run observability is best-effort and degrades to "no parent run" cleanly.
70
+ *
71
+ * @param configJson The session's `Config_` string.
72
+ * @returns The co-agent run id, or `undefined`.
73
+ */
74
+ export declare function extractCoAgentRunID(configJson: string | null | undefined): string | undefined;
75
+ /**
76
+ * Creates the parent `Tool` step ("Browser goal: …") on the realtime co-agent run for a goal-driven browser
77
+ * run — a single top-level timeline node the computer-use prompt steps then nest under. Best-effort: returns
78
+ * `null` (so the goal still runs, just unlinked) when there's no co-agent run to attach to, or on any error.
79
+ * Uses the shared {@link initAgentRunStep} helper so the field shape matches every other agent-run step.
80
+ *
81
+ * @param provider The metadata provider.
82
+ * @param contextUser The acting user.
83
+ * @param coAgentRunID The realtime co-agent run id (the parent run), or `undefined`.
84
+ * @param goal The natural-language goal (becomes the step name).
85
+ * @returns The saved parent step (to finalize + nest under), or `null`.
86
+ */
87
+ export declare function beginBrowserGoalStep(provider: IMetadataProvider, contextUser: UserInfo | undefined, coAgentRunID: string | undefined, goal: string): Promise<MJAIAgentRunStepEntity | null>;
88
+ /**
89
+ * Finalizes the parent browser-goal step from the goal outcome — `Completed`/`Failed` with an `OutputData`
90
+ * envelope of strategy/status/step-count/url. A no-op when `step` is `null`. Best-effort (logged, never thrown).
91
+ *
92
+ * @param step The parent step from {@link beginBrowserGoalStep} (or `null`).
93
+ * @param result The goal outcome.
94
+ */
95
+ export declare function finalizeBrowserGoalStep(step: MJAIAgentRunStepEntity | null, result: RemoteBrowserGoalResult): Promise<void>;
96
+ /**
97
+ * Binds the CDP goal-engine factory to {@link MJProgressComputerUseEngine}. Idempotent and safe to call at
98
+ * startup; failures are logged (never thrown) so a binding hiccup can't abort server boot — goal runs would
99
+ * simply fall back to the base engine.
100
+ */
101
+ export declare function BindRemoteBrowserGoalEngine(): void;
102
+ //# sourceMappingURL=remoteBrowserGoalEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remoteBrowserGoalEngine.d.ts","sourceRoot":"","sources":["../../src/agentSessions/remoteBrowserGoalEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAY,iBAAiB,EAAW,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AACxG,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AACnF,OAAO,EAA8C,KAAK,uBAAuB,EAAE,KAAK,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAEvJ;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,oBAAoB,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,sBAAsB,CAM5J;AAED;;;;;;;;;;GAUG;AACH,qBAAa,2BAA4B,SAAQ,mBAAoB,YAAW,kBAAkB;IAChG,8FAA8F;IACvF,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAEhE,qGAAqG;IAC9F,WAAW,CAAC,EAAE,QAAQ,CAAC;IAE9B,wGAAwG;IACjG,UAAU,CAAC,EAAE,MAAM,CAAC;IAE3B,4GAA4G;IACrG,cAAc,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;OAOG;IACmB,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,iBAAiB,CAAC;cAIhE,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI;CAI1F;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAU7F;AAQD;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,QAAQ,GAAG,SAAS,EACjC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAuBxC;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBjI;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @fileoverview **Production binding** of the Remote Browser goal-engine seam to MJ's computer-use engine.
3
+ *
4
+ * The CDP remote-browser session ({@link CdpRemoteBrowserSession}) runs goal-driven browser control through
5
+ * an injectable {@link ComputerUseGoalRun} factory. By default that factory yields a bare
6
+ * `ProgressComputerUseEngine` (base computer-use), which has no model auto-selection — so it can't run a goal
7
+ * unsupervised. This module binds the factory to {@link MJProgressComputerUseEngine}: an MJ-aware engine that
8
+ * routes controller/judge prompts through `AIPromptRunner`, auto-selects a vision-capable model from MJ
9
+ * metadata, persists step media, AND forwards per-step progress for live narration.
10
+ *
11
+ * Called once at server startup from `agentSessions/index.ts` (the realtime composition point), so every
12
+ * goal-driven run on this process uses the MJ engine. Kept here (an application package), not in any library,
13
+ * because it is the seam where the transport-neutral CDP layer is wired to MJ's AI stack.
14
+ *
15
+ * @module @memberjunction/server
16
+ */
17
+ import { LogError, RunView } from '@memberjunction/core';
18
+ import { initAgentRunStep, finalizeAgentRunStep } from '@memberjunction/ai-core-plus';
19
+ import { MJComputerUseEngine, MJRunComputerUseParams } from '@memberjunction/computer-use-engine';
20
+ import { buildProgressNote, CdpRemoteBrowserSession } from '@memberjunction/remote-browser-cdp';
21
+ /**
22
+ * Adapts the transport-neutral base computer-use params into {@link MJRunComputerUseParams}, injecting the
23
+ * acting user so the MJ engine can run its controller/judge prompts as that user. Pure + exported for unit
24
+ * testing; {@link MJProgressComputerUseEngine.Run} is a one-liner over it.
25
+ *
26
+ * @param params The base computer-use params the CDP session built (goal + step cap + start url).
27
+ * @param contextUser The acting MJ user (may be undefined; the MJ engine handles the unset case).
28
+ * @param agentRunID Optional parent agent-run id (links the goal's prompt runs to the realtime agent run).
29
+ * @param agentRunStepID Optional parent agent-run-step id (nests a child prompt step per prompt under it).
30
+ * @returns The MJ-aware params to hand to `MJComputerUseEngine.Run`.
31
+ */
32
+ export function buildMJGoalParams(params, contextUser, agentRunID, agentRunStepID) {
33
+ const mjParams = Object.assign(new MJRunComputerUseParams(), params);
34
+ mjParams.ContextUser = contextUser;
35
+ mjParams.AgentRunId = agentRunID;
36
+ mjParams.AgentRunStepID = agentRunStepID;
37
+ return mjParams;
38
+ }
39
+ /**
40
+ * The MJ-aware goal engine bound in production. Extends {@link MJComputerUseEngine} (prompt-runner routing,
41
+ * vision-model auto-selection, credential resolution, media persistence) and satisfies the
42
+ * {@link ComputerUseGoalRun} seam:
43
+ *
44
+ * - {@link SetBrowserAdapter} / {@link Stop} are inherited from the base computer-use engine.
45
+ * - {@link OnProgress} + {@link ContextUser} are set by the CDP session before {@link Run}.
46
+ * - {@link Run} adapts the transport-neutral base params into {@link MJRunComputerUseParams} (injecting the
47
+ * acting user) so the MJ engine can run its prompts.
48
+ * - {@link onStepComplete} chains the MJ media-persistence behavior, then forwards a model-safe progress note.
49
+ */
50
+ export class MJProgressComputerUseEngine extends MJComputerUseEngine {
51
+ /**
52
+ * Adapts the transport-neutral base params into {@link MJRunComputerUseParams} (carrying the acting user
53
+ * + the parent run/step linkage) and delegates to the MJ engine, which auto-selects a vision-capable
54
+ * controller model from MJ metadata and nests a child step per prompt when the linkage is present.
55
+ *
56
+ * @param params The base computer-use params the CDP session built (goal + step cap + start url).
57
+ * @returns The computer-use run result.
58
+ */
59
+ async Run(params) {
60
+ return super.Run(buildMJGoalParams(params, this.ContextUser, this.AgentRunID, this.AgentRunStepID));
61
+ }
62
+ onStepComplete(step, params) {
63
+ super.onStepComplete(step, params); // keep MJ media persistence
64
+ this.OnProgress?.(buildProgressNote(step)); // shared note shape with the base ProgressComputerUseEngine
65
+ }
66
+ }
67
+ /**
68
+ * Extracts the realtime co-agent observability run id from an `AIAgentSession.Config_` JSON blob (the
69
+ * `coAgentRunID` a voice session persists). Returns `undefined` for a missing/non-voice/un-parseable
70
+ * config — goal-run observability is best-effort and degrades to "no parent run" cleanly.
71
+ *
72
+ * @param configJson The session's `Config_` string.
73
+ * @returns The co-agent run id, or `undefined`.
74
+ */
75
+ export function extractCoAgentRunID(configJson) {
76
+ if (!configJson) {
77
+ return undefined;
78
+ }
79
+ try {
80
+ const parsed = JSON.parse(configJson);
81
+ return typeof parsed.coAgentRunID === 'string' ? parsed.coAgentRunID : undefined;
82
+ }
83
+ catch {
84
+ return undefined;
85
+ }
86
+ }
87
+ /** Truncates a goal to a step-name-friendly length. */
88
+ function goalStepName(goal) {
89
+ const trimmed = goal.trim();
90
+ return `Browser goal: ${trimmed.length > 120 ? `${trimmed.slice(0, 120)}…` : trimmed}`;
91
+ }
92
+ /**
93
+ * Creates the parent `Tool` step ("Browser goal: …") on the realtime co-agent run for a goal-driven browser
94
+ * run — a single top-level timeline node the computer-use prompt steps then nest under. Best-effort: returns
95
+ * `null` (so the goal still runs, just unlinked) when there's no co-agent run to attach to, or on any error.
96
+ * Uses the shared {@link initAgentRunStep} helper so the field shape matches every other agent-run step.
97
+ *
98
+ * @param provider The metadata provider.
99
+ * @param contextUser The acting user.
100
+ * @param coAgentRunID The realtime co-agent run id (the parent run), or `undefined`.
101
+ * @param goal The natural-language goal (becomes the step name).
102
+ * @returns The saved parent step (to finalize + nest under), or `null`.
103
+ */
104
+ export async function beginBrowserGoalStep(provider, contextUser, coAgentRunID, goal) {
105
+ if (!coAgentRunID) {
106
+ return null;
107
+ }
108
+ try {
109
+ const rv = new RunView();
110
+ const countResult = await rv.RunView({ EntityName: 'MJ: AI Agent Run Steps', ExtraFilter: `AgentRunID='${coAgentRunID.replace(/'/g, "''")}'`, Fields: ['ID'], ResultType: 'simple' }, contextUser);
111
+ const stepNumber = (countResult.Success ? (countResult.Results?.length ?? 0) : 0) + 1;
112
+ const step = await provider.GetEntityObject('MJ: AI Agent Run Steps', contextUser);
113
+ step.NewRecord();
114
+ initAgentRunStep(step, { AgentRunID: coAgentRunID, StepNumber: stepNumber, StepType: 'Tool', StepName: goalStepName(goal) });
115
+ if (await step.Save()) {
116
+ return step;
117
+ }
118
+ LogError(`[RemoteBrowserGoalEngine] beginBrowserGoalStep save failed: ${step.LatestResult?.CompleteMessage ?? 'unknown error'}`);
119
+ return null;
120
+ }
121
+ catch (err) {
122
+ LogError(`[RemoteBrowserGoalEngine] beginBrowserGoalStep threw: ${err instanceof Error ? err.message : String(err)}`);
123
+ return null;
124
+ }
125
+ }
126
+ /**
127
+ * Finalizes the parent browser-goal step from the goal outcome — `Completed`/`Failed` with an `OutputData`
128
+ * envelope of strategy/status/step-count/url. A no-op when `step` is `null`. Best-effort (logged, never thrown).
129
+ *
130
+ * @param step The parent step from {@link beginBrowserGoalStep} (or `null`).
131
+ * @param result The goal outcome.
132
+ */
133
+ export async function finalizeBrowserGoalStep(step, result) {
134
+ if (!step) {
135
+ return;
136
+ }
137
+ try {
138
+ finalizeAgentRunStep(step, {
139
+ success: result.Success,
140
+ errorMessage: result.Success ? undefined : result.Detail,
141
+ outputData: { strategy: result.Strategy, status: result.Status, stepCount: result.StepCount, currentUrl: result.CurrentUrl },
142
+ });
143
+ if (!(await step.Save())) {
144
+ LogError(`[RemoteBrowserGoalEngine] finalizeBrowserGoalStep save failed: ${step.LatestResult?.CompleteMessage ?? 'unknown error'}`);
145
+ }
146
+ }
147
+ catch (err) {
148
+ LogError(`[RemoteBrowserGoalEngine] finalizeBrowserGoalStep threw: ${err instanceof Error ? err.message : String(err)}`);
149
+ }
150
+ }
151
+ /**
152
+ * Binds the CDP goal-engine factory to {@link MJProgressComputerUseEngine}. Idempotent and safe to call at
153
+ * startup; failures are logged (never thrown) so a binding hiccup can't abort server boot — goal runs would
154
+ * simply fall back to the base engine.
155
+ */
156
+ export function BindRemoteBrowserGoalEngine() {
157
+ try {
158
+ CdpRemoteBrowserSession.SetGoalEngineFactory(() => new MJProgressComputerUseEngine());
159
+ }
160
+ catch (err) {
161
+ LogError(`[RemoteBrowserGoalEngine] Failed to bind MJ computer-use goal engine: ${err instanceof Error ? err.message : String(err)}`);
162
+ }
163
+ }
164
+ //# sourceMappingURL=remoteBrowserGoalEngine.js.map