@memberjunction/ai-agents 5.40.1 → 5.41.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 (80) hide show
  1. package/README.md +45 -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 +365 -24
  33. package/dist/base-agent.d.ts.map +1 -1
  34. package/dist/base-agent.js +995 -175
  35. package/dist/base-agent.js.map +1 -1
  36. package/dist/index.d.ts +11 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +15 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/memory-manager-agent.d.ts +55 -2
  41. package/dist/memory-manager-agent.d.ts.map +1 -1
  42. package/dist/memory-manager-agent.js +261 -62
  43. package/dist/memory-manager-agent.js.map +1 -1
  44. package/dist/realtime/meeting-controls-channel-server.d.ts +198 -0
  45. package/dist/realtime/meeting-controls-channel-server.d.ts.map +1 -0
  46. package/dist/realtime/meeting-controls-channel-server.js +319 -0
  47. package/dist/realtime/meeting-controls-channel-server.js.map +1 -0
  48. package/dist/realtime/meeting-controls-state.d.ts +191 -0
  49. package/dist/realtime/meeting-controls-state.d.ts.map +1 -0
  50. package/dist/realtime/meeting-controls-state.js +219 -0
  51. package/dist/realtime/meeting-controls-state.js.map +1 -0
  52. package/dist/realtime/realtime-channel-server-host.d.ts +166 -0
  53. package/dist/realtime/realtime-channel-server-host.d.ts.map +1 -0
  54. package/dist/realtime/realtime-channel-server-host.js +378 -0
  55. package/dist/realtime/realtime-channel-server-host.js.map +1 -0
  56. package/dist/realtime/realtime-client-session-service.d.ts +884 -0
  57. package/dist/realtime/realtime-client-session-service.d.ts.map +1 -0
  58. package/dist/realtime/realtime-client-session-service.js +1401 -0
  59. package/dist/realtime/realtime-client-session-service.js.map +1 -0
  60. package/dist/realtime/realtime-coagent-config.d.ts +202 -0
  61. package/dist/realtime/realtime-coagent-config.d.ts.map +1 -0
  62. package/dist/realtime/realtime-coagent-config.js +334 -0
  63. package/dist/realtime/realtime-coagent-config.js.map +1 -0
  64. package/dist/realtime/realtime-narration.d.ts +67 -0
  65. package/dist/realtime/realtime-narration.d.ts.map +1 -0
  66. package/dist/realtime/realtime-narration.js +127 -0
  67. package/dist/realtime/realtime-narration.js.map +1 -0
  68. package/dist/realtime/realtime-session-runner.d.ts +383 -0
  69. package/dist/realtime/realtime-session-runner.d.ts.map +1 -0
  70. package/dist/realtime/realtime-session-runner.js +532 -0
  71. package/dist/realtime/realtime-session-runner.js.map +1 -0
  72. package/dist/realtime/realtime-tool-broker.d.ts +279 -0
  73. package/dist/realtime/realtime-tool-broker.d.ts.map +1 -0
  74. package/dist/realtime/realtime-tool-broker.js +184 -0
  75. package/dist/realtime/realtime-tool-broker.js.map +1 -0
  76. package/dist/realtime/whiteboard-channel-server.d.ts +50 -0
  77. package/dist/realtime/whiteboard-channel-server.d.ts.map +1 -0
  78. package/dist/realtime/whiteboard-channel-server.js +85 -0
  79. package/dist/realtime/whiteboard-channel-server.js.map +1 -0
  80. package/package.json +17 -17
@@ -0,0 +1,191 @@
1
+ /**
2
+ * @fileoverview Pure, platform-free state machine for the Meeting Controls channel — the
3
+ * facilitator intel an agent acts on: the ordered hand-raise QUEUE, who is speaking, the roster,
4
+ * and a meeting/agenda timer. Deliberately free of any realtime/bridge/MJ dependency so it is
5
+ * exhaustively unit-testable with an injected clock and no platform.
6
+ *
7
+ * The {@link import('./meeting-controls-channel-server.js').MeetingControlsChannelServer} owns one of
8
+ * these per session, mutates it from the injected bridge event stream + the agent's tool calls, and
9
+ * serializes {@link MeetingControlsState.BuildPerception} snapshots back to the model.
10
+ *
11
+ * @module @memberjunction/ai-agents
12
+ * @author MemberJunction.com
13
+ */
14
+ /**
15
+ * The role a participant plays in the meeting, from the facilitator's point of view. A union (not an
16
+ * enum) so it exports cleanly and stays additive. `Agent` is the facilitating bot itself.
17
+ */
18
+ export type MeetingParticipantRole = 'Host' | 'CoHost' | 'Participant' | 'Agent';
19
+ /**
20
+ * One participant on the meeting roster, as the Meeting Controls channel tracks them. Minimal and
21
+ * platform-agnostic — a bridge driver maps its native roster (`BridgeParticipantInfo`, a Zoom
22
+ * participant, a Twilio call leg, …) onto this shape when feeding the channel's event source.
23
+ */
24
+ export interface MeetingParticipant {
25
+ /** Stable platform-native participant id (the key the queue / speaking / mute state is keyed on). */
26
+ ParticipantId: string;
27
+ /** Human-readable display name, when the platform reports one. */
28
+ DisplayName?: string;
29
+ /** The participant's role in the meeting. */
30
+ Role: MeetingParticipantRole;
31
+ /** Whether this participant is an agent bot (the facilitator excludes agents from hand-raise / call-on). */
32
+ IsAgent: boolean;
33
+ }
34
+ /**
35
+ * One entry in the hand-raise queue: who raised, and WHEN (so the queue preserves raise order and the
36
+ * perception feed can report how long each person has been waiting).
37
+ */
38
+ export interface HandRaiseQueueEntry {
39
+ /** The participant who raised their hand. */
40
+ ParticipantId: string;
41
+ /** Their display name at raise time (for a human-readable queue in the perception feed). */
42
+ DisplayName?: string;
43
+ /** Epoch-ms when the hand was raised — drives queue ordering and wait-time reporting. */
44
+ RaisedAtMs: number;
45
+ }
46
+ /**
47
+ * A monotonic clock, injected so the queue/timer logic is deterministic in tests. Defaults to
48
+ * `Date.now` in production.
49
+ */
50
+ export type MeetingControlsClock = () => number;
51
+ /**
52
+ * A snapshot of the timer the agent set (via the `SetTimer` tool), reported in the perception feed so
53
+ * the agent can pace the meeting ("two minutes left on this agenda item").
54
+ */
55
+ export interface MeetingTimerSnapshot {
56
+ /** The total duration the timer was set for, in seconds. */
57
+ DurationSeconds: number;
58
+ /** Whole seconds elapsed since the timer was set (clamped to `[0, DurationSeconds]`). */
59
+ ElapsedSeconds: number;
60
+ /** Whole seconds remaining (clamped to `[0, DurationSeconds]`); `0` once the timer has expired. */
61
+ RemainingSeconds: number;
62
+ /** Whether the timer has reached or passed its duration. */
63
+ Expired: boolean;
64
+ }
65
+ /**
66
+ * The full perception payload the channel serializes back to the model — the facilitator's situational
67
+ * awareness in one object: the roster, the ordered hand-raise queue, who is speaking, and the timer.
68
+ */
69
+ export interface MeetingControlsPerception {
70
+ /** The current roster (every known participant, agents included). */
71
+ Roster: MeetingParticipant[];
72
+ /** The hand-raise queue in RAISE ORDER (oldest first) — who is waiting and for how long. */
73
+ HandRaiseQueue: Array<HandRaiseQueueEntry & {
74
+ WaitingSeconds: number;
75
+ }>;
76
+ /** The participant ids currently speaking (diarized), in no particular order. */
77
+ SpeakingParticipantIds: string[];
78
+ /** The participant ids the agent has muted via the channel (best-effort mirror of mute state). */
79
+ MutedParticipantIds: string[];
80
+ /** The active agenda/meeting timer, or `null` when none is set. */
81
+ Timer: MeetingTimerSnapshot | null;
82
+ }
83
+ /**
84
+ * Pure facilitator state — the ordered hand-raise queue, who is speaking, the roster, mute state, and
85
+ * a single agenda timer. No I/O, no realtime/bridge coupling; mutated by the channel server from the
86
+ * bridge event stream and the agent's tool calls, then snapshotted via {@link BuildPerception}.
87
+ *
88
+ * All time-based behavior reads the injected {@link MeetingControlsClock}, so a test can advance time
89
+ * deterministically and assert exact wait / elapsed / remaining values.
90
+ */
91
+ export declare class MeetingControlsState {
92
+ /** Roster keyed by participant id (insertion order preserved for stable perception output). */
93
+ private roster;
94
+ /** Hand-raise queue in raise order (oldest first). A participant appears at most once. */
95
+ private handQueue;
96
+ /** Currently-speaking participant ids (diarized). */
97
+ private speaking;
98
+ /** Participant ids the agent muted via the channel. */
99
+ private muted;
100
+ /** The active timer's total duration (seconds) and start time (epoch-ms), or `null` when unset. */
101
+ private timer;
102
+ /** Injected clock for deterministic time math. */
103
+ private readonly clock;
104
+ /**
105
+ * @param clock The monotonic clock to read for queue wait-times and the timer. Defaults to `Date.now`.
106
+ */
107
+ constructor(clock?: MeetingControlsClock);
108
+ /**
109
+ * Replaces the roster wholesale with a fresh snapshot from the bridge. Participants who left the
110
+ * meeting (no longer in the snapshot) are pruned from the queue, the speaking set, and the mute
111
+ * set so stale ids never linger in the perception feed.
112
+ *
113
+ * @param participants The current roster snapshot.
114
+ */
115
+ SetRoster(participants: MeetingParticipant[]): void;
116
+ /** The current roster (every known participant), in insertion order. */
117
+ GetRoster(): MeetingParticipant[];
118
+ /** Looks up a participant by id, or `undefined` when not on the roster. */
119
+ GetParticipant(participantId: string): MeetingParticipant | undefined;
120
+ /**
121
+ * Adds a participant to the hand-raise queue (idempotent — a second raise by someone already in
122
+ * the queue keeps their ORIGINAL position and raise time, never jumping them forward). A raise by
123
+ * someone not on the roster is ignored (the bridge should roster them first).
124
+ *
125
+ * @param participantId The participant raising their hand.
126
+ * @returns `true` when the queue changed (a new raise landed), `false` otherwise.
127
+ */
128
+ RaiseHand(participantId: string): boolean;
129
+ /**
130
+ * Removes a participant from the hand-raise queue (the explicit `LowerHand` tool, and the
131
+ * implicit lower when they are called on). Preserves the order of everyone else.
132
+ *
133
+ * @param participantId The participant whose hand to lower.
134
+ * @returns `true` when they were in the queue and were removed, `false` otherwise.
135
+ */
136
+ LowerHand(participantId: string): boolean;
137
+ /** Whether a participant is currently in the hand-raise queue. */
138
+ isQueued(participantId: string): boolean;
139
+ /** The next participant in the queue (front of the line), or `undefined` when the queue is empty. */
140
+ PeekNextHand(): HandRaiseQueueEntry | undefined;
141
+ /**
142
+ * "Calls on" a participant: removes them from the hand-raise queue (lowering their hand) and
143
+ * returns them. When `participantId` is omitted, calls on the FRONT of the queue (next in line).
144
+ * Returns `undefined` when there is no one to call on (empty queue, or the named id isn't queued).
145
+ *
146
+ * @param participantId Optional specific participant to call on; defaults to the queue front.
147
+ * @returns The participant called on, or `undefined`.
148
+ */
149
+ CallOn(participantId?: string): HandRaiseQueueEntry | undefined;
150
+ /** The hand-raise queue in raise order (oldest first), each stamped with current wait seconds. */
151
+ GetHandRaiseQueue(): Array<HandRaiseQueueEntry & {
152
+ WaitingSeconds: number;
153
+ }>;
154
+ /**
155
+ * Replaces the set of currently-speaking participants (from the bridge's diarization stream).
156
+ *
157
+ * @param participantIds The ids now speaking.
158
+ */
159
+ SetSpeaking(participantIds: string[]): void;
160
+ /** The participant ids currently speaking. */
161
+ GetSpeaking(): string[];
162
+ /**
163
+ * Marks a participant muted by the channel (the agent's `MuteParticipant` tool). The actual
164
+ * platform mute is the driver's job; this mirrors the intent so the perception feed reflects it.
165
+ *
166
+ * @param participantId The participant to mark muted.
167
+ * @returns `true` when the participant is on the roster (and was marked), `false` otherwise.
168
+ */
169
+ MarkMuted(participantId: string): boolean;
170
+ /** The participant ids the agent has muted via the channel. */
171
+ GetMuted(): string[];
172
+ /**
173
+ * Sets (or resets) the agenda timer to `durationSeconds`, starting now. A non-positive duration
174
+ * clears the timer. Setting a new timer replaces any existing one.
175
+ *
176
+ * @param durationSeconds The timer duration in seconds; `<= 0` clears it.
177
+ */
178
+ SetTimer(durationSeconds: number): void;
179
+ /** Clears the agenda timer. */
180
+ ClearTimer(): void;
181
+ /** The current timer snapshot (elapsed / remaining / expired), or `null` when none is set. */
182
+ GetTimer(): MeetingTimerSnapshot | null;
183
+ /**
184
+ * Builds the full perception snapshot the channel serializes back to the model — the facilitator's
185
+ * complete situational awareness in one object.
186
+ *
187
+ * @returns The perception payload.
188
+ */
189
+ BuildPerception(): MeetingControlsPerception;
190
+ }
191
+ //# sourceMappingURL=meeting-controls-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meeting-controls-state.d.ts","sourceRoot":"","sources":["../../src/realtime/meeting-controls-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * @fileoverview Pure, platform-free state machine for the Meeting Controls channel — the
3
+ * facilitator intel an agent acts on: the ordered hand-raise QUEUE, who is speaking, the roster,
4
+ * and a meeting/agenda timer. Deliberately free of any realtime/bridge/MJ dependency so it is
5
+ * exhaustively unit-testable with an injected clock and no platform.
6
+ *
7
+ * The {@link import('./meeting-controls-channel-server.js').MeetingControlsChannelServer} owns one of
8
+ * these per session, mutates it from the injected bridge event stream + the agent's tool calls, and
9
+ * serializes {@link MeetingControlsState.BuildPerception} snapshots back to the model.
10
+ *
11
+ * @module @memberjunction/ai-agents
12
+ * @author MemberJunction.com
13
+ */
14
+ /**
15
+ * Pure facilitator state — the ordered hand-raise queue, who is speaking, the roster, mute state, and
16
+ * a single agenda timer. No I/O, no realtime/bridge coupling; mutated by the channel server from the
17
+ * bridge event stream and the agent's tool calls, then snapshotted via {@link BuildPerception}.
18
+ *
19
+ * All time-based behavior reads the injected {@link MeetingControlsClock}, so a test can advance time
20
+ * deterministically and assert exact wait / elapsed / remaining values.
21
+ */
22
+ export class MeetingControlsState {
23
+ /**
24
+ * @param clock The monotonic clock to read for queue wait-times and the timer. Defaults to `Date.now`.
25
+ */
26
+ constructor(clock = () => Date.now()) {
27
+ /** Roster keyed by participant id (insertion order preserved for stable perception output). */
28
+ this.roster = new Map();
29
+ /** Hand-raise queue in raise order (oldest first). A participant appears at most once. */
30
+ this.handQueue = [];
31
+ /** Currently-speaking participant ids (diarized). */
32
+ this.speaking = new Set();
33
+ /** Participant ids the agent muted via the channel. */
34
+ this.muted = new Set();
35
+ /** The active timer's total duration (seconds) and start time (epoch-ms), or `null` when unset. */
36
+ this.timer = null;
37
+ this.clock = clock;
38
+ }
39
+ // ── Roster ────────────────────────────────────────────────────────────────────
40
+ /**
41
+ * Replaces the roster wholesale with a fresh snapshot from the bridge. Participants who left the
42
+ * meeting (no longer in the snapshot) are pruned from the queue, the speaking set, and the mute
43
+ * set so stale ids never linger in the perception feed.
44
+ *
45
+ * @param participants The current roster snapshot.
46
+ */
47
+ SetRoster(participants) {
48
+ this.roster = new Map(participants.map((p) => [p.ParticipantId, p]));
49
+ const present = new Set(participants.map((p) => p.ParticipantId));
50
+ this.handQueue = this.handQueue.filter((e) => present.has(e.ParticipantId));
51
+ for (const id of [...this.speaking]) {
52
+ if (!present.has(id)) {
53
+ this.speaking.delete(id);
54
+ }
55
+ }
56
+ for (const id of [...this.muted]) {
57
+ if (!present.has(id)) {
58
+ this.muted.delete(id);
59
+ }
60
+ }
61
+ }
62
+ /** The current roster (every known participant), in insertion order. */
63
+ GetRoster() {
64
+ return [...this.roster.values()];
65
+ }
66
+ /** Looks up a participant by id, or `undefined` when not on the roster. */
67
+ GetParticipant(participantId) {
68
+ return this.roster.get(participantId);
69
+ }
70
+ // ── Hand-raise queue ───────────────────────────────────────────────────────────
71
+ /**
72
+ * Adds a participant to the hand-raise queue (idempotent — a second raise by someone already in
73
+ * the queue keeps their ORIGINAL position and raise time, never jumping them forward). A raise by
74
+ * someone not on the roster is ignored (the bridge should roster them first).
75
+ *
76
+ * @param participantId The participant raising their hand.
77
+ * @returns `true` when the queue changed (a new raise landed), `false` otherwise.
78
+ */
79
+ RaiseHand(participantId) {
80
+ if (!this.roster.has(participantId) || this.isQueued(participantId)) {
81
+ return false;
82
+ }
83
+ const participant = this.roster.get(participantId);
84
+ this.handQueue.push({
85
+ ParticipantId: participantId,
86
+ DisplayName: participant?.DisplayName,
87
+ RaisedAtMs: this.clock(),
88
+ });
89
+ return true;
90
+ }
91
+ /**
92
+ * Removes a participant from the hand-raise queue (the explicit `LowerHand` tool, and the
93
+ * implicit lower when they are called on). Preserves the order of everyone else.
94
+ *
95
+ * @param participantId The participant whose hand to lower.
96
+ * @returns `true` when they were in the queue and were removed, `false` otherwise.
97
+ */
98
+ LowerHand(participantId) {
99
+ const before = this.handQueue.length;
100
+ this.handQueue = this.handQueue.filter((e) => e.ParticipantId !== participantId);
101
+ return this.handQueue.length !== before;
102
+ }
103
+ /** Whether a participant is currently in the hand-raise queue. */
104
+ isQueued(participantId) {
105
+ return this.handQueue.some((e) => e.ParticipantId === participantId);
106
+ }
107
+ /** The next participant in the queue (front of the line), or `undefined` when the queue is empty. */
108
+ PeekNextHand() {
109
+ return this.handQueue[0];
110
+ }
111
+ /**
112
+ * "Calls on" a participant: removes them from the hand-raise queue (lowering their hand) and
113
+ * returns them. When `participantId` is omitted, calls on the FRONT of the queue (next in line).
114
+ * Returns `undefined` when there is no one to call on (empty queue, or the named id isn't queued).
115
+ *
116
+ * @param participantId Optional specific participant to call on; defaults to the queue front.
117
+ * @returns The participant called on, or `undefined`.
118
+ */
119
+ CallOn(participantId) {
120
+ const target = participantId
121
+ ? this.handQueue.find((e) => e.ParticipantId === participantId)
122
+ : this.handQueue[0];
123
+ if (!target) {
124
+ return undefined;
125
+ }
126
+ this.LowerHand(target.ParticipantId);
127
+ return target;
128
+ }
129
+ /** The hand-raise queue in raise order (oldest first), each stamped with current wait seconds. */
130
+ GetHandRaiseQueue() {
131
+ const now = this.clock();
132
+ return this.handQueue.map((e) => ({
133
+ ...e,
134
+ WaitingSeconds: Math.max(0, Math.floor((now - e.RaisedAtMs) / 1000)),
135
+ }));
136
+ }
137
+ // ── Speaking ──────────────────────────────────────────────────────────────────
138
+ /**
139
+ * Replaces the set of currently-speaking participants (from the bridge's diarization stream).
140
+ *
141
+ * @param participantIds The ids now speaking.
142
+ */
143
+ SetSpeaking(participantIds) {
144
+ this.speaking = new Set(participantIds);
145
+ }
146
+ /** The participant ids currently speaking. */
147
+ GetSpeaking() {
148
+ return [...this.speaking];
149
+ }
150
+ // ── Mute ──────────────────────────────────────────────────────────────────────
151
+ /**
152
+ * Marks a participant muted by the channel (the agent's `MuteParticipant` tool). The actual
153
+ * platform mute is the driver's job; this mirrors the intent so the perception feed reflects it.
154
+ *
155
+ * @param participantId The participant to mark muted.
156
+ * @returns `true` when the participant is on the roster (and was marked), `false` otherwise.
157
+ */
158
+ MarkMuted(participantId) {
159
+ if (!this.roster.has(participantId)) {
160
+ return false;
161
+ }
162
+ this.muted.add(participantId);
163
+ return true;
164
+ }
165
+ /** The participant ids the agent has muted via the channel. */
166
+ GetMuted() {
167
+ return [...this.muted];
168
+ }
169
+ // ── Timer ─────────────────────────────────────────────────────────────────────
170
+ /**
171
+ * Sets (or resets) the agenda timer to `durationSeconds`, starting now. A non-positive duration
172
+ * clears the timer. Setting a new timer replaces any existing one.
173
+ *
174
+ * @param durationSeconds The timer duration in seconds; `<= 0` clears it.
175
+ */
176
+ SetTimer(durationSeconds) {
177
+ if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
178
+ this.timer = null;
179
+ return;
180
+ }
181
+ this.timer = { durationSeconds: Math.floor(durationSeconds), startedAtMs: this.clock() };
182
+ }
183
+ /** Clears the agenda timer. */
184
+ ClearTimer() {
185
+ this.timer = null;
186
+ }
187
+ /** The current timer snapshot (elapsed / remaining / expired), or `null` when none is set. */
188
+ GetTimer() {
189
+ if (!this.timer) {
190
+ return null;
191
+ }
192
+ const elapsedRaw = Math.floor((this.clock() - this.timer.startedAtMs) / 1000);
193
+ const elapsed = Math.min(this.timer.durationSeconds, Math.max(0, elapsedRaw));
194
+ const remaining = Math.max(0, this.timer.durationSeconds - elapsed);
195
+ return {
196
+ DurationSeconds: this.timer.durationSeconds,
197
+ ElapsedSeconds: elapsed,
198
+ RemainingSeconds: remaining,
199
+ Expired: remaining === 0,
200
+ };
201
+ }
202
+ // ── Perception ────────────────────────────────────────────────────────────────
203
+ /**
204
+ * Builds the full perception snapshot the channel serializes back to the model — the facilitator's
205
+ * complete situational awareness in one object.
206
+ *
207
+ * @returns The perception payload.
208
+ */
209
+ BuildPerception() {
210
+ return {
211
+ Roster: this.GetRoster(),
212
+ HandRaiseQueue: this.GetHandRaiseQueue(),
213
+ SpeakingParticipantIds: this.GetSpeaking(),
214
+ MutedParticipantIds: this.GetMuted(),
215
+ Timer: this.GetTimer(),
216
+ };
217
+ }
218
+ }
219
+ //# sourceMappingURL=meeting-controls-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meeting-controls-state.js","sourceRoot":"","sources":["../../src/realtime/meeting-controls-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
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, RealtimeChannelServerContext, RealtimeChannelCloseReason, RealtimeToolDefinition, ServerChannelToolResult } from '@memberjunction/ai';
29
+ import { BaseSingleton } from '@memberjunction/global';
30
+ import { IMetadataProvider, UserInfo } from '@memberjunction/core';
31
+ /**
32
+ * Singleton host that owns every live `BaseRealtimeChannelServer` instance in this process —
33
+ * one instance per (session × active channel row with a resolvable `ServerPluginClass`).
34
+ *
35
+ * ### Lifecycle routing (who calls what)
36
+ * | Host method | Invoked from | Plugin hook(s) fired |
37
+ * |---|---|---|
38
+ * | {@link OnSessionStarted} | `SessionManager.CreateSession` after the session row persists | `Initialize(ctx)` then `OnSessionStarted()` per resolved plugin |
39
+ * | {@link OnChannelStateSave} | `SaveSessionChannelState` resolver, pre-persistence | the matching channel's `OnChannelStateSave(stateJson)` |
40
+ * | {@link OnSessionClosed} | `SessionManager.CloseSession` (all close provenances) | `OnSessionClosed(reason)` per plugin, then deferred `Dispose()` |
41
+ *
42
+ * ### Janitor / cross-host notes
43
+ * - The janitor's sweeps run on EVERY instance and funnel through `SessionManager.CloseSession`,
44
+ * so a session whose plugins live in this process is eventually cleaned up here even when its
45
+ * client vanished — including when ANOTHER instance won the close race (`CloseSession`'s
46
+ * already-closed path still notifies this host, and an unknown session is a no-op).
47
+ * - A close for a session minted on a different host/boot simply finds no local instances.
48
+ */
49
+ export declare class RealtimeChannelServerHost extends BaseSingleton<RealtimeChannelServerHost> {
50
+ /** session id (lowercased) → that session's plugin instances + disposal state. */
51
+ private sessions;
52
+ /**
53
+ * Post-close linger (ms) before {@link BaseRealtimeChannelServer.Dispose} runs, so the client's
54
+ * legitimate post-close state flush still routes through the plugin. `0` disposes immediately
55
+ * (used by tests). Mutable on purpose — a deployment-level knob, not per-session state.
56
+ */
57
+ DisposeLingerMs: number;
58
+ protected constructor();
59
+ /** Process-wide singleton accessor (Global Object Store backed via {@link BaseSingleton}). */
60
+ static get Instance(): RealtimeChannelServerHost;
61
+ /** Number of sessions currently holding live (or close-lingering) plugin instances. */
62
+ get ActiveSessionCount(): number;
63
+ /**
64
+ * Returns the live plugin instance for `(agentSessionID, channelName)`, or `null` when none
65
+ * exists (no plugin resolved for that channel, unknown session, or already disposed).
66
+ * Lookup is case/whitespace-insensitive on both keys.
67
+ */
68
+ GetSessionPlugin(agentSessionID: string, channelName: string): BaseRealtimeChannelServer | null;
69
+ /**
70
+ * Aggregates the **server-executed** tool definitions every live plugin of a session contributes
71
+ * (each plugin's {@link BaseRealtimeChannelServer.GetServerToolDefinitions}, possibly
72
+ * runtime-computed) into one flat set — the per-session server-channel tool vocabulary fed into
73
+ * `RealtimeSessionRunner.ServerChannelTools`.
74
+ *
75
+ * Tolerant by contract: a plugin whose `GetServerToolDefinitions` throws is logged and skipped,
76
+ * never breaking the assembly. An unknown session returns `[]`.
77
+ *
78
+ * @param agentSessionID The session whose channels' server tools to collect.
79
+ * @returns The aggregated tool definitions across all of the session's live channel plugins.
80
+ */
81
+ GetSessionServerTools(agentSessionID: string): RealtimeToolDefinition[];
82
+ /**
83
+ * Routes ONE server-executed tool call to the session's plugin that owns it, matched by the
84
+ * plugin's {@link BaseRealtimeChannelServer.ToolNamePrefix}, and returns its result. Resolution
85
+ * picks the plugin whose (non-empty) prefix the tool name starts with — the longest matching
86
+ * prefix wins, so overlapping prefixes resolve deterministically.
87
+ *
88
+ * Never throws: an unowned tool (no channel prefix matches), an unknown session, or a plugin that
89
+ * throws all resolve to a structured `{ Success: false }` result so the model always receives a
90
+ * consistent `tool_response`.
91
+ *
92
+ * @param agentSessionID The session the tool call belongs to.
93
+ * @param toolName The full tool name the model invoked.
94
+ * @param argsJson The raw arguments JSON the model emitted.
95
+ * @returns The execution result (or a structured error).
96
+ */
97
+ ExecuteSessionServerTool(agentSessionID: string, toolName: string, argsJson: string): Promise<ServerChannelToolResult>;
98
+ /** Returns `true` when the given tool name is owned by a live channel of the session. */
99
+ OwnsServerTool(agentSessionID: string, toolName: string): boolean;
100
+ /**
101
+ * Finds the live plugin of a session whose {@link BaseRealtimeChannelServer.ToolNamePrefix} the
102
+ * tool name starts with. The longest matching prefix wins so overlapping prefixes
103
+ * (`'Meeting_'` vs `'MeetingControls_'`) resolve to the most specific channel.
104
+ */
105
+ private resolveToolOwner;
106
+ /**
107
+ * Session-started entry point. Loads the ACTIVE channel registry rows, resolves each row's
108
+ * `ServerPluginClass` through the ClassFactory into ONE fresh instance for this session, and
109
+ * brackets each with `Initialize(ctx)` + `OnSessionStarted()`. Rows with no registered plugin
110
+ * are skipped with a log; a plugin whose start bracket throws is dropped (logged) without
111
+ * touching its siblings. Never throws — any registry/host failure degrades to "no plugins".
112
+ *
113
+ * Idempotent per session: a second start notification for a session that already holds
114
+ * instances is ignored (logged) rather than double-initializing.
115
+ */
116
+ OnSessionStarted(ctx: RealtimeChannelServerContext, contextUser: UserInfo, provider: IMetadataProvider): Promise<void>;
117
+ /**
118
+ * Channel-state-save entry point, invoked PRE-persistence. Routes the payload to the session's
119
+ * matching plugin and returns the string to persist:
120
+ * - no live plugin for the channel (or unknown session) → the original `stateJson`;
121
+ * - the plugin returns a non-empty replacement string → that replacement;
122
+ * - the plugin returns `null`/empty/non-string, or throws (logged) → the original `stateJson`.
123
+ *
124
+ * A plugin can therefore only ever *transform* a save — it can never lose or block one.
125
+ */
126
+ OnChannelStateSave(agentSessionID: string, channelName: string, stateJson: string): Promise<string>;
127
+ /**
128
+ * Session-closed entry point — invoked from EVERY close provenance (explicit, janitor sweeps,
129
+ * shutdown drain, error teardown; `SessionManager.CloseSession` is the single funnel). Fires
130
+ * each plugin's `OnSessionClosed(reason)` (failures logged, siblings unaffected), then defers
131
+ * `Dispose()` by {@link DisposeLingerMs} so the client's post-close state flush still routes
132
+ * through the plugins. Idempotent: an unknown session, or one whose close hooks already fired
133
+ * (disposal pending), is a no-op. Never throws.
134
+ */
135
+ OnSessionClosed(agentSessionID: string, closeReason: RealtimeChannelCloseReason | null): Promise<void>;
136
+ /** Disposes the session's plugins now, or after the linger window when one is configured. */
137
+ private scheduleDisposal;
138
+ /** Disposes and forgets every plugin instance of one session (failures logged per plugin). */
139
+ private disposeSession;
140
+ /**
141
+ * Reads the ACTIVE `MJ: AI Agent Channels` rows from {@link AIEngineBase}'s cached
142
+ * `AgentChannels` (provider-scoped engine instance, lazy `Config` — no per-session RunView;
143
+ * the engine's BaseEntity-event reactivity keeps the registry fresh) — the server-side mirror
144
+ * of the client's `fetchChannelDefinitions`. Failures are logged and degrade to an empty
145
+ * list; channel availability must never block a session.
146
+ */
147
+ private fetchChannelDefinitions;
148
+ /** Resolves + start-brackets one fresh plugin instance per resolvable registry row. */
149
+ private instantiateSessionPlugins;
150
+ /**
151
+ * Resolves one registry row's `ServerPluginClass` via the ClassFactory (registration checked
152
+ * first, exactly like the client half and the realtime model drivers) and instantiates a fresh
153
+ * per-session plugin. Returns `null` (logged) when the key is blank, unregistered (e.g. its
154
+ * `Load...()` function was never called server-side), or fails to instantiate.
155
+ */
156
+ private resolveChannelPlugin;
157
+ /** Disposes a plugin defensively, swallowing (logging) any error. */
158
+ private safeDispose;
159
+ /** Canonical map key for a session id (UUID case differs across DB platforms). */
160
+ private sessionKey;
161
+ /** Canonical map key for a channel name. */
162
+ private channelKey;
163
+ /** Normalizes an unknown thrown value into a loggable message. */
164
+ private message;
165
+ }
166
+ //# sourceMappingURL=realtime-channel-server-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-channel-server-host.d.ts","sourceRoot":"","sources":["../../src/realtime/realtime-channel-server-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EACH,yBAAyB,EACzB,4BAA4B,EAC5B,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EAC1B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAY,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAuB,MAAM,sBAAsB,CAAC;AA6BxF;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,yBAA0B,SAAQ,aAAa,CAAC,yBAAyB,CAAC;IACnF,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAyC;IAEzD;;;;OAIG;IACI,eAAe,SAA6B;IAEnD,SAAS;IAIT,8FAA8F;IAC9F,WAAkB,QAAQ,IAAI,yBAAyB,CAEtD;IAED,uFAAuF;IACvF,IAAW,kBAAkB,IAAI,MAAM,CAEtC;IAED;;;;OAIG;IACI,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,yBAAyB,GAAG,IAAI;IAWtG;;;;;;;;;;;OAWG;IACI,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,sBAAsB,EAAE;IAsB9E;;;;;;;;;;;;;;OAcG;IACU,wBAAwB,CACjC,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,uBAAuB,CAAC;IAenC,yFAAyF;IAClF,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIxE;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;;;;;;;;OASG;IACU,gBAAgB,CACzB,GAAG,EAAE,4BAA4B,EACjC,WAAW,EAAE,QAAQ,EACrB,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAiBhB;;;;;;;;OAQG;IACU,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBhH;;;;;;;OAOG;IACU,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,0BAA0B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnH,6FAA6F;IAC7F,OAAO,CAAC,gBAAgB;IAUxB,8FAA8F;IAC9F,OAAO,CAAC,cAAc;IAetB;;;;;;OAMG;YACW,uBAAuB;IAoBrC,uFAAuF;YACzE,yBAAyB;IAyBvC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAyB5B,qEAAqE;IACrE,OAAO,CAAC,WAAW;IAQnB,kFAAkF;IAClF,OAAO,CAAC,UAAU;IAIlB,4CAA4C;IAC5C,OAAO,CAAC,UAAU;IAIlB,kEAAkE;IAClE,OAAO,CAAC,OAAO;CAGlB"}