@memberjunction/ai-agents 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 (88) hide show
  1. package/README.md +53 -0
  2. package/dist/AgentRunner.d.ts +5 -2
  3. package/dist/AgentRunner.d.ts.map +1 -1
  4. package/dist/AgentRunner.js +14 -4
  5. package/dist/AgentRunner.js.map +1 -1
  6. package/dist/MemoryWriteManager.d.ts +188 -0
  7. package/dist/MemoryWriteManager.d.ts.map +1 -0
  8. package/dist/MemoryWriteManager.js +299 -0
  9. package/dist/MemoryWriteManager.js.map +1 -0
  10. package/dist/agent-context-injector.d.ts +29 -0
  11. package/dist/agent-context-injector.d.ts.map +1 -1
  12. package/dist/agent-context-injector.js +90 -32
  13. package/dist/agent-context-injector.js.map +1 -1
  14. package/dist/agent-memory-context-builder.d.ts +100 -0
  15. package/dist/agent-memory-context-builder.d.ts.map +1 -0
  16. package/dist/agent-memory-context-builder.js +172 -0
  17. package/dist/agent-memory-context-builder.js.map +1 -0
  18. package/dist/agent-types/index.d.ts +1 -0
  19. package/dist/agent-types/index.d.ts.map +1 -1
  20. package/dist/agent-types/index.js +1 -0
  21. package/dist/agent-types/index.js.map +1 -1
  22. package/dist/agent-types/loop-agent-response-type.d.ts +12 -1
  23. package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
  24. package/dist/agent-types/loop-agent-response-type.js.map +1 -1
  25. package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
  26. package/dist/agent-types/loop-agent-type.js +4 -0
  27. package/dist/agent-types/loop-agent-type.js.map +1 -1
  28. package/dist/agent-types/realtime-agent-type.d.ts +146 -0
  29. package/dist/agent-types/realtime-agent-type.d.ts.map +1 -0
  30. package/dist/agent-types/realtime-agent-type.js +176 -0
  31. package/dist/agent-types/realtime-agent-type.js.map +1 -0
  32. package/dist/base-agent.d.ts +386 -39
  33. package/dist/base-agent.d.ts.map +1 -1
  34. package/dist/base-agent.js +1121 -261
  35. package/dist/base-agent.js.map +1 -1
  36. package/dist/index.d.ts +13 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +17 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/memory-manager-agent.d.ts +99 -4
  41. package/dist/memory-manager-agent.d.ts.map +1 -1
  42. package/dist/memory-manager-agent.js +349 -117
  43. package/dist/memory-manager-agent.js.map +1 -1
  44. package/dist/realtime/bridge-realtime-session-factory.d.ts +111 -0
  45. package/dist/realtime/bridge-realtime-session-factory.d.ts.map +1 -0
  46. package/dist/realtime/bridge-realtime-session-factory.js +163 -0
  47. package/dist/realtime/bridge-realtime-session-factory.js.map +1 -0
  48. package/dist/realtime/bridge-room-transcript-sink.d.ts +58 -0
  49. package/dist/realtime/bridge-room-transcript-sink.d.ts.map +1 -0
  50. package/dist/realtime/bridge-room-transcript-sink.js +127 -0
  51. package/dist/realtime/bridge-room-transcript-sink.js.map +1 -0
  52. package/dist/realtime/meeting-controls-channel-server.d.ts +198 -0
  53. package/dist/realtime/meeting-controls-channel-server.d.ts.map +1 -0
  54. package/dist/realtime/meeting-controls-channel-server.js +319 -0
  55. package/dist/realtime/meeting-controls-channel-server.js.map +1 -0
  56. package/dist/realtime/meeting-controls-state.d.ts +191 -0
  57. package/dist/realtime/meeting-controls-state.d.ts.map +1 -0
  58. package/dist/realtime/meeting-controls-state.js +219 -0
  59. package/dist/realtime/meeting-controls-state.js.map +1 -0
  60. package/dist/realtime/realtime-channel-server-host.d.ts +166 -0
  61. package/dist/realtime/realtime-channel-server-host.d.ts.map +1 -0
  62. package/dist/realtime/realtime-channel-server-host.js +378 -0
  63. package/dist/realtime/realtime-channel-server-host.js.map +1 -0
  64. package/dist/realtime/realtime-client-session-service.d.ts +1026 -0
  65. package/dist/realtime/realtime-client-session-service.d.ts.map +1 -0
  66. package/dist/realtime/realtime-client-session-service.js +1607 -0
  67. package/dist/realtime/realtime-client-session-service.js.map +1 -0
  68. package/dist/realtime/realtime-coagent-config.d.ts +258 -0
  69. package/dist/realtime/realtime-coagent-config.d.ts.map +1 -0
  70. package/dist/realtime/realtime-coagent-config.js +408 -0
  71. package/dist/realtime/realtime-coagent-config.js.map +1 -0
  72. package/dist/realtime/realtime-narration.d.ts +67 -0
  73. package/dist/realtime/realtime-narration.d.ts.map +1 -0
  74. package/dist/realtime/realtime-narration.js +127 -0
  75. package/dist/realtime/realtime-narration.js.map +1 -0
  76. package/dist/realtime/realtime-session-runner.d.ts +383 -0
  77. package/dist/realtime/realtime-session-runner.d.ts.map +1 -0
  78. package/dist/realtime/realtime-session-runner.js +532 -0
  79. package/dist/realtime/realtime-session-runner.js.map +1 -0
  80. package/dist/realtime/realtime-tool-broker.d.ts +294 -0
  81. package/dist/realtime/realtime-tool-broker.d.ts.map +1 -0
  82. package/dist/realtime/realtime-tool-broker.js +206 -0
  83. package/dist/realtime/realtime-tool-broker.js.map +1 -0
  84. package/dist/realtime/whiteboard-channel-server.d.ts +50 -0
  85. package/dist/realtime/whiteboard-channel-server.d.ts.map +1 -0
  86. package/dist/realtime/whiteboard-channel-server.js +85 -0
  87. package/dist/realtime/whiteboard-channel-server.js.map +1 -0
  88. package/package.json +17 -17
@@ -0,0 +1,378 @@
1
+ /**
2
+ * @fileoverview Process-wide host for SERVER-SIDE interactive-channel plugins — the resolution +
3
+ * per-session lifecycle half of the `BaseRealtimeChannelServer` contract (`@memberjunction/ai`).
4
+ *
5
+ * The base class is deliberately MJ-core-free; this host is the DB-aware piece: it reads the ACTIVE
6
+ * `MJ: AI Agent Channels` registry rows at session start, resolves each row's `ServerPluginClass`
7
+ * through the MJ ClassFactory into a fresh per-session plugin instance (the exact mirror of how
8
+ * `VoiceSessionService.loadActiveChannels` resolves `ClientPluginClass` in the browser), and routes
9
+ * the session lifecycle events into those instances:
10
+ *
11
+ * - **session started** — `SessionManager.CreateSession` (MJServer) → {@link RealtimeChannelServerHost.OnSessionStarted}
12
+ * - **channel state saved** — the `SaveSessionChannelState` mutation → {@link RealtimeChannelServerHost.OnChannelStateSave}
13
+ * (pre-persistence; the plugin may normalize the payload)
14
+ * - **session closed** — `SessionManager.CloseSession` (every close path: explicit, janitor,
15
+ * shutdown drain, error teardown) → {@link RealtimeChannelServerHost.OnSessionClosed}
16
+ *
17
+ * **Failure posture:** nothing in here ever throws to a caller. Registry failures degrade to "no
18
+ * server plugins"; a throwing plugin hook is logged and skipped; an unknown session is a no-op.
19
+ * Channel plugins can never break a live call or block persistence.
20
+ *
21
+ * **`BaseSingleton` (per MJ rule #7):** plugin instances are in-memory, per-process state shared
22
+ * between the resolver layer and `SessionManager`/`SessionJanitor` call paths — a plain instance
23
+ * field on either would split the state, so the host is a Global-Object-Store-backed singleton.
24
+ *
25
+ * @module @memberjunction/ai-agents
26
+ * @author MemberJunction.com
27
+ */
28
+ import { BaseRealtimeChannelServer, } from '@memberjunction/ai';
29
+ import { BaseSingleton, MJGlobal } from '@memberjunction/global';
30
+ import { LogError, LogStatus } from '@memberjunction/core';
31
+ import { AIEngineBase } from '@memberjunction/ai-engine-base';
32
+ /** Entity name — kept in sync with the session machinery's `MJ:`-prefix convention. */
33
+ const CHANNEL_ENTITY = 'MJ: AI Agent Channels';
34
+ /**
35
+ * Default post-close linger (ms) before plugin instances are disposed. The client's channel-state
36
+ * pipeline debounces saves (~3s) and flushes once at teardown — that final flush legitimately lands
37
+ * AFTER `CloseAgentSession`, and it is the state of record a future resume restores, so it must
38
+ * still route through the plugin's normalization hook. 15s comfortably covers the flush window.
39
+ */
40
+ const DEFAULT_DISPOSE_LINGER_MS = 15_000;
41
+ /**
42
+ * Singleton host that owns every live `BaseRealtimeChannelServer` instance in this process —
43
+ * one instance per (session × active channel row with a resolvable `ServerPluginClass`).
44
+ *
45
+ * ### Lifecycle routing (who calls what)
46
+ * | Host method | Invoked from | Plugin hook(s) fired |
47
+ * |---|---|---|
48
+ * | {@link OnSessionStarted} | `SessionManager.CreateSession` after the session row persists | `Initialize(ctx)` then `OnSessionStarted()` per resolved plugin |
49
+ * | {@link OnChannelStateSave} | `SaveSessionChannelState` resolver, pre-persistence | the matching channel's `OnChannelStateSave(stateJson)` |
50
+ * | {@link OnSessionClosed} | `SessionManager.CloseSession` (all close provenances) | `OnSessionClosed(reason)` per plugin, then deferred `Dispose()` |
51
+ *
52
+ * ### Janitor / cross-host notes
53
+ * - The janitor's sweeps run on EVERY instance and funnel through `SessionManager.CloseSession`,
54
+ * so a session whose plugins live in this process is eventually cleaned up here even when its
55
+ * client vanished — including when ANOTHER instance won the close race (`CloseSession`'s
56
+ * already-closed path still notifies this host, and an unknown session is a no-op).
57
+ * - A close for a session minted on a different host/boot simply finds no local instances.
58
+ */
59
+ export class RealtimeChannelServerHost extends BaseSingleton {
60
+ constructor() {
61
+ super();
62
+ /** session id (lowercased) → that session's plugin instances + disposal state. */
63
+ this.sessions = new Map();
64
+ /**
65
+ * Post-close linger (ms) before {@link BaseRealtimeChannelServer.Dispose} runs, so the client's
66
+ * legitimate post-close state flush still routes through the plugin. `0` disposes immediately
67
+ * (used by tests). Mutable on purpose — a deployment-level knob, not per-session state.
68
+ */
69
+ this.DisposeLingerMs = DEFAULT_DISPOSE_LINGER_MS;
70
+ }
71
+ /** Process-wide singleton accessor (Global Object Store backed via {@link BaseSingleton}). */
72
+ static get Instance() {
73
+ return super.getInstance();
74
+ }
75
+ /** Number of sessions currently holding live (or close-lingering) plugin instances. */
76
+ get ActiveSessionCount() {
77
+ return this.sessions.size;
78
+ }
79
+ /**
80
+ * Returns the live plugin instance for `(agentSessionID, channelName)`, or `null` when none
81
+ * exists (no plugin resolved for that channel, unknown session, or already disposed).
82
+ * Lookup is case/whitespace-insensitive on both keys.
83
+ */
84
+ GetSessionPlugin(agentSessionID, channelName) {
85
+ const entry = this.sessions.get(this.sessionKey(agentSessionID));
86
+ return entry?.plugins.get(this.channelKey(channelName)) ?? null;
87
+ }
88
+ // ──────────────────────────────────────────────────────────────────────────────
89
+ // Server-executed channel tools — the deferred "channel tool contribution feeding
90
+ // RealtimeSessionRunner.ExtraTools" the realtime guide flagged. The host aggregates every
91
+ // live plugin's GetServerToolDefinitions and routes each prefixed tool call back to the owner.
92
+ // ──────────────────────────────────────────────────────────────────────────────
93
+ /**
94
+ * Aggregates the **server-executed** tool definitions every live plugin of a session contributes
95
+ * (each plugin's {@link BaseRealtimeChannelServer.GetServerToolDefinitions}, possibly
96
+ * runtime-computed) into one flat set — the per-session server-channel tool vocabulary fed into
97
+ * `RealtimeSessionRunner.ServerChannelTools`.
98
+ *
99
+ * Tolerant by contract: a plugin whose `GetServerToolDefinitions` throws is logged and skipped,
100
+ * never breaking the assembly. An unknown session returns `[]`.
101
+ *
102
+ * @param agentSessionID The session whose channels' server tools to collect.
103
+ * @returns The aggregated tool definitions across all of the session's live channel plugins.
104
+ */
105
+ GetSessionServerTools(agentSessionID) {
106
+ const entry = this.sessions.get(this.sessionKey(agentSessionID));
107
+ if (!entry) {
108
+ return [];
109
+ }
110
+ const tools = [];
111
+ for (const [name, plugin] of entry.plugins) {
112
+ try {
113
+ const channelTools = plugin.GetServerToolDefinitions();
114
+ if (Array.isArray(channelTools)) {
115
+ tools.push(...channelTools);
116
+ }
117
+ }
118
+ catch (error) {
119
+ LogError(`[RealtimeChannelServerHost] GetServerToolDefinitions failed for session ${agentSessionID} / ` +
120
+ `channel '${name}' — skipping its tools: ${this.message(error)}`);
121
+ }
122
+ }
123
+ return tools;
124
+ }
125
+ /**
126
+ * Routes ONE server-executed tool call to the session's plugin that owns it, matched by the
127
+ * plugin's {@link BaseRealtimeChannelServer.ToolNamePrefix}, and returns its result. Resolution
128
+ * picks the plugin whose (non-empty) prefix the tool name starts with — the longest matching
129
+ * prefix wins, so overlapping prefixes resolve deterministically.
130
+ *
131
+ * Never throws: an unowned tool (no channel prefix matches), an unknown session, or a plugin that
132
+ * throws all resolve to a structured `{ Success: false }` result so the model always receives a
133
+ * consistent `tool_response`.
134
+ *
135
+ * @param agentSessionID The session the tool call belongs to.
136
+ * @param toolName The full tool name the model invoked.
137
+ * @param argsJson The raw arguments JSON the model emitted.
138
+ * @returns The execution result (or a structured error).
139
+ */
140
+ async ExecuteSessionServerTool(agentSessionID, toolName, argsJson) {
141
+ const plugin = this.resolveToolOwner(agentSessionID, toolName);
142
+ if (!plugin) {
143
+ return { Success: false, Output: `No active channel owns the server tool '${toolName}' for session ${agentSessionID}.` };
144
+ }
145
+ try {
146
+ return await plugin.ExecuteServerTool(toolName, argsJson);
147
+ }
148
+ catch (error) {
149
+ LogError(`[RealtimeChannelServerHost] ExecuteServerTool failed for session ${agentSessionID} / tool '${toolName}': ${this.message(error)}`);
150
+ return { Success: false, Output: `Server tool '${toolName}' failed: ${this.message(error)}` };
151
+ }
152
+ }
153
+ /** Returns `true` when the given tool name is owned by a live channel of the session. */
154
+ OwnsServerTool(agentSessionID, toolName) {
155
+ return this.resolveToolOwner(agentSessionID, toolName) !== null;
156
+ }
157
+ /**
158
+ * Finds the live plugin of a session whose {@link BaseRealtimeChannelServer.ToolNamePrefix} the
159
+ * tool name starts with. The longest matching prefix wins so overlapping prefixes
160
+ * (`'Meeting_'` vs `'MeetingControls_'`) resolve to the most specific channel.
161
+ */
162
+ resolveToolOwner(agentSessionID, toolName) {
163
+ const entry = this.sessions.get(this.sessionKey(agentSessionID));
164
+ if (!entry) {
165
+ return null;
166
+ }
167
+ let best = null;
168
+ let bestPrefixLength = -1;
169
+ for (const plugin of entry.plugins.values()) {
170
+ const prefix = plugin.ToolNamePrefix;
171
+ if (prefix && toolName.startsWith(prefix) && prefix.length > bestPrefixLength) {
172
+ best = plugin;
173
+ bestPrefixLength = prefix.length;
174
+ }
175
+ }
176
+ return best;
177
+ }
178
+ /**
179
+ * Session-started entry point. Loads the ACTIVE channel registry rows, resolves each row's
180
+ * `ServerPluginClass` through the ClassFactory into ONE fresh instance for this session, and
181
+ * brackets each with `Initialize(ctx)` + `OnSessionStarted()`. Rows with no registered plugin
182
+ * are skipped with a log; a plugin whose start bracket throws is dropped (logged) without
183
+ * touching its siblings. Never throws — any registry/host failure degrades to "no plugins".
184
+ *
185
+ * Idempotent per session: a second start notification for a session that already holds
186
+ * instances is ignored (logged) rather than double-initializing.
187
+ */
188
+ async OnSessionStarted(ctx, contextUser, provider) {
189
+ try {
190
+ const key = this.sessionKey(ctx.AgentSessionID);
191
+ if (this.sessions.has(key)) {
192
+ LogStatus(`[RealtimeChannelServerHost] Session ${ctx.AgentSessionID} already has server channel plugins — ignoring duplicate start.`);
193
+ return;
194
+ }
195
+ const rows = await this.fetchChannelDefinitions(contextUser, provider);
196
+ const plugins = await this.instantiateSessionPlugins(rows, ctx);
197
+ if (plugins.size > 0) {
198
+ this.sessions.set(key, { plugins, disposeTimer: null });
199
+ }
200
+ }
201
+ catch (error) {
202
+ LogError(`[RealtimeChannelServerHost] OnSessionStarted failed for session ${ctx.AgentSessionID}: ${this.message(error)}`);
203
+ }
204
+ }
205
+ /**
206
+ * Channel-state-save entry point, invoked PRE-persistence. Routes the payload to the session's
207
+ * matching plugin and returns the string to persist:
208
+ * - no live plugin for the channel (or unknown session) → the original `stateJson`;
209
+ * - the plugin returns a non-empty replacement string → that replacement;
210
+ * - the plugin returns `null`/empty/non-string, or throws (logged) → the original `stateJson`.
211
+ *
212
+ * A plugin can therefore only ever *transform* a save — it can never lose or block one.
213
+ */
214
+ async OnChannelStateSave(agentSessionID, channelName, stateJson) {
215
+ const plugin = this.GetSessionPlugin(agentSessionID, channelName);
216
+ if (!plugin) {
217
+ return stateJson;
218
+ }
219
+ try {
220
+ const replacement = await plugin.OnChannelStateSave(stateJson);
221
+ if (typeof replacement === 'string' && replacement.length > 0) {
222
+ return replacement;
223
+ }
224
+ }
225
+ catch (error) {
226
+ LogError(`[RealtimeChannelServerHost] OnChannelStateSave plugin failure for session ${agentSessionID} / ` +
227
+ `channel '${channelName}' — persisting the original state: ${this.message(error)}`);
228
+ }
229
+ return stateJson;
230
+ }
231
+ /**
232
+ * Session-closed entry point — invoked from EVERY close provenance (explicit, janitor sweeps,
233
+ * shutdown drain, error teardown; `SessionManager.CloseSession` is the single funnel). Fires
234
+ * each plugin's `OnSessionClosed(reason)` (failures logged, siblings unaffected), then defers
235
+ * `Dispose()` by {@link DisposeLingerMs} so the client's post-close state flush still routes
236
+ * through the plugins. Idempotent: an unknown session, or one whose close hooks already fired
237
+ * (disposal pending), is a no-op. Never throws.
238
+ */
239
+ async OnSessionClosed(agentSessionID, closeReason) {
240
+ const key = this.sessionKey(agentSessionID);
241
+ const entry = this.sessions.get(key);
242
+ if (!entry || entry.disposeTimer) {
243
+ return; // unknown session, or close hooks already fired and disposal is pending
244
+ }
245
+ for (const [name, plugin] of entry.plugins) {
246
+ try {
247
+ await plugin.OnSessionClosed(closeReason);
248
+ }
249
+ catch (error) {
250
+ LogError(`[RealtimeChannelServerHost] OnSessionClosed plugin failure for session ${agentSessionID} / ` +
251
+ `channel '${name}': ${this.message(error)}`);
252
+ }
253
+ }
254
+ this.scheduleDisposal(key, entry);
255
+ }
256
+ // ----- internals -------------------------------------------------------------------------
257
+ /** Disposes the session's plugins now, or after the linger window when one is configured. */
258
+ scheduleDisposal(key, entry) {
259
+ if (this.DisposeLingerMs <= 0) {
260
+ this.disposeSession(key);
261
+ return;
262
+ }
263
+ entry.disposeTimer = setTimeout(() => this.disposeSession(key), this.DisposeLingerMs);
264
+ // Never keep the process alive for a disposal sweep.
265
+ entry.disposeTimer.unref?.();
266
+ }
267
+ /** Disposes and forgets every plugin instance of one session (failures logged per plugin). */
268
+ disposeSession(key) {
269
+ const entry = this.sessions.get(key);
270
+ if (!entry) {
271
+ return;
272
+ }
273
+ this.sessions.delete(key);
274
+ for (const [name, plugin] of entry.plugins) {
275
+ try {
276
+ plugin.Dispose();
277
+ }
278
+ catch (error) {
279
+ LogError(`[RealtimeChannelServerHost] Dispose failure for channel '${name}': ${this.message(error)}`);
280
+ }
281
+ }
282
+ }
283
+ /**
284
+ * Reads the ACTIVE `MJ: AI Agent Channels` rows from {@link AIEngineBase}'s cached
285
+ * `AgentChannels` (provider-scoped engine instance, lazy `Config` — no per-session RunView;
286
+ * the engine's BaseEntity-event reactivity keeps the registry fresh) — the server-side mirror
287
+ * of the client's `fetchChannelDefinitions`. Failures are logged and degrade to an empty
288
+ * list; channel availability must never block a session.
289
+ */
290
+ async fetchChannelDefinitions(contextUser, provider) {
291
+ try {
292
+ const engine = AIEngineBase.GetProviderInstance(provider, AIEngineBase);
293
+ await engine.Config(false, contextUser, provider);
294
+ return (engine.AgentChannels ?? [])
295
+ .filter((c) => c.IsActive)
296
+ .map((c) => ({
297
+ ID: c.ID,
298
+ Name: c.Name,
299
+ ServerPluginClass: c.ServerPluginClass ?? null,
300
+ }));
301
+ }
302
+ catch (error) {
303
+ LogError(`[RealtimeChannelServerHost] Failed to load the channel registry from ${CHANNEL_ENTITY}: ${this.message(error)}`);
304
+ return [];
305
+ }
306
+ }
307
+ /** Resolves + start-brackets one fresh plugin instance per resolvable registry row. */
308
+ async instantiateSessionPlugins(rows, ctx) {
309
+ const plugins = new Map();
310
+ for (const row of rows) {
311
+ const plugin = this.resolveChannelPlugin(row);
312
+ if (!plugin) {
313
+ continue;
314
+ }
315
+ try {
316
+ plugin.Initialize(ctx);
317
+ await plugin.OnSessionStarted();
318
+ plugins.set(this.channelKey(row.Name), plugin);
319
+ }
320
+ catch (error) {
321
+ LogError(`[RealtimeChannelServerHost] Server plugin for channel '${row.Name}' failed during session start ` +
322
+ `— dropping it for session ${ctx.AgentSessionID}: ${this.message(error)}`);
323
+ this.safeDispose(plugin, row.Name);
324
+ }
325
+ }
326
+ return plugins;
327
+ }
328
+ /**
329
+ * Resolves one registry row's `ServerPluginClass` via the ClassFactory (registration checked
330
+ * first, exactly like the client half and the realtime model drivers) and instantiates a fresh
331
+ * per-session plugin. Returns `null` (logged) when the key is blank, unregistered (e.g. its
332
+ * `Load...()` function was never called server-side), or fails to instantiate.
333
+ */
334
+ resolveChannelPlugin(row) {
335
+ const key = row.ServerPluginClass?.trim();
336
+ if (!key) {
337
+ LogStatus(`[RealtimeChannelServerHost] Channel '${row.Name}' has no ServerPluginClass — skipping.`);
338
+ return null;
339
+ }
340
+ const registration = MJGlobal.Instance.ClassFactory.GetRegistration(BaseRealtimeChannelServer, key);
341
+ if (!registration) {
342
+ LogStatus(`[RealtimeChannelServerHost] No server plugin registered for channel '${row.Name}' (key '${key}') — skipping.`);
343
+ return null;
344
+ }
345
+ const plugin = MJGlobal.Instance.ClassFactory.CreateInstance(BaseRealtimeChannelServer, key);
346
+ if (!plugin) {
347
+ LogError(`[RealtimeChannelServerHost] Failed to instantiate server plugin for channel '${row.Name}' (key '${key}').`);
348
+ return null;
349
+ }
350
+ if (this.channelKey(plugin.ChannelName) !== this.channelKey(row.Name)) {
351
+ LogError(`[RealtimeChannelServerHost] Server plugin '${key}' reports ChannelName '${plugin.ChannelName}' but is ` +
352
+ `registered on channel row '${row.Name}' — routing by the row name; fix the plugin/registry mismatch.`);
353
+ }
354
+ return plugin;
355
+ }
356
+ /** Disposes a plugin defensively, swallowing (logging) any error. */
357
+ safeDispose(plugin, channelName) {
358
+ try {
359
+ plugin.Dispose();
360
+ }
361
+ catch (error) {
362
+ LogError(`[RealtimeChannelServerHost] Dispose failure for channel '${channelName}': ${this.message(error)}`);
363
+ }
364
+ }
365
+ /** Canonical map key for a session id (UUID case differs across DB platforms). */
366
+ sessionKey(agentSessionID) {
367
+ return agentSessionID.trim().toLowerCase();
368
+ }
369
+ /** Canonical map key for a channel name. */
370
+ channelKey(channelName) {
371
+ return channelName.trim().toLowerCase();
372
+ }
373
+ /** Normalizes an unknown thrown value into a loggable message. */
374
+ message(error) {
375
+ return error instanceof Error ? error.message : String(error);
376
+ }
377
+ }
378
+ //# sourceMappingURL=realtime-channel-server-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-channel-server-host.js","sourceRoot":"","sources":["../../src/realtime/realtime-channel-server-host.ts"],"names":[],"mappings":"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"}