@memberjunction/ai-agents 5.40.2 → 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.
- package/README.md +45 -0
- package/dist/AgentRunner.d.ts +5 -2
- package/dist/AgentRunner.d.ts.map +1 -1
- package/dist/AgentRunner.js +14 -4
- package/dist/AgentRunner.js.map +1 -1
- package/dist/MemoryWriteManager.d.ts +188 -0
- package/dist/MemoryWriteManager.d.ts.map +1 -0
- package/dist/MemoryWriteManager.js +299 -0
- package/dist/MemoryWriteManager.js.map +1 -0
- package/dist/agent-context-injector.d.ts +29 -0
- package/dist/agent-context-injector.d.ts.map +1 -1
- package/dist/agent-context-injector.js +90 -32
- package/dist/agent-context-injector.js.map +1 -1
- package/dist/agent-memory-context-builder.d.ts +100 -0
- package/dist/agent-memory-context-builder.d.ts.map +1 -0
- package/dist/agent-memory-context-builder.js +172 -0
- package/dist/agent-memory-context-builder.js.map +1 -0
- package/dist/agent-types/index.d.ts +1 -0
- package/dist/agent-types/index.d.ts.map +1 -1
- package/dist/agent-types/index.js +1 -0
- package/dist/agent-types/index.js.map +1 -1
- package/dist/agent-types/loop-agent-response-type.d.ts +12 -1
- package/dist/agent-types/loop-agent-response-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-response-type.js.map +1 -1
- package/dist/agent-types/loop-agent-type.d.ts.map +1 -1
- package/dist/agent-types/loop-agent-type.js +4 -0
- package/dist/agent-types/loop-agent-type.js.map +1 -1
- package/dist/agent-types/realtime-agent-type.d.ts +146 -0
- package/dist/agent-types/realtime-agent-type.d.ts.map +1 -0
- package/dist/agent-types/realtime-agent-type.js +176 -0
- package/dist/agent-types/realtime-agent-type.js.map +1 -0
- package/dist/base-agent.d.ts +365 -24
- package/dist/base-agent.d.ts.map +1 -1
- package/dist/base-agent.js +995 -175
- package/dist/base-agent.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/memory-manager-agent.d.ts +55 -2
- package/dist/memory-manager-agent.d.ts.map +1 -1
- package/dist/memory-manager-agent.js +261 -62
- package/dist/memory-manager-agent.js.map +1 -1
- package/dist/realtime/meeting-controls-channel-server.d.ts +198 -0
- package/dist/realtime/meeting-controls-channel-server.d.ts.map +1 -0
- package/dist/realtime/meeting-controls-channel-server.js +319 -0
- package/dist/realtime/meeting-controls-channel-server.js.map +1 -0
- package/dist/realtime/meeting-controls-state.d.ts +191 -0
- package/dist/realtime/meeting-controls-state.d.ts.map +1 -0
- package/dist/realtime/meeting-controls-state.js +219 -0
- package/dist/realtime/meeting-controls-state.js.map +1 -0
- package/dist/realtime/realtime-channel-server-host.d.ts +166 -0
- package/dist/realtime/realtime-channel-server-host.d.ts.map +1 -0
- package/dist/realtime/realtime-channel-server-host.js +378 -0
- package/dist/realtime/realtime-channel-server-host.js.map +1 -0
- package/dist/realtime/realtime-client-session-service.d.ts +884 -0
- package/dist/realtime/realtime-client-session-service.d.ts.map +1 -0
- package/dist/realtime/realtime-client-session-service.js +1401 -0
- package/dist/realtime/realtime-client-session-service.js.map +1 -0
- package/dist/realtime/realtime-coagent-config.d.ts +202 -0
- package/dist/realtime/realtime-coagent-config.d.ts.map +1 -0
- package/dist/realtime/realtime-coagent-config.js +334 -0
- package/dist/realtime/realtime-coagent-config.js.map +1 -0
- package/dist/realtime/realtime-narration.d.ts +67 -0
- package/dist/realtime/realtime-narration.d.ts.map +1 -0
- package/dist/realtime/realtime-narration.js +127 -0
- package/dist/realtime/realtime-narration.js.map +1 -0
- package/dist/realtime/realtime-session-runner.d.ts +383 -0
- package/dist/realtime/realtime-session-runner.d.ts.map +1 -0
- package/dist/realtime/realtime-session-runner.js +532 -0
- package/dist/realtime/realtime-session-runner.js.map +1 -0
- package/dist/realtime/realtime-tool-broker.d.ts +279 -0
- package/dist/realtime/realtime-tool-broker.d.ts.map +1 -0
- package/dist/realtime/realtime-tool-broker.js +184 -0
- package/dist/realtime/realtime-tool-broker.js.map +1 -0
- package/dist/realtime/whiteboard-channel-server.d.ts +50 -0
- package/dist/realtime/whiteboard-channel-server.d.ts.map +1 -0
- package/dist/realtime/whiteboard-channel-server.js +85 -0
- package/dist/realtime/whiteboard-channel-server.js.map +1 -0
- package/package.json +17 -17
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Topology-agnostic broker that executes a single realtime tool call server-side.
|
|
3
|
+
*
|
|
4
|
+
* The {@link RealtimeToolBroker} owns the *tool-execution* half of a realtime session: given a
|
|
5
|
+
* provider {@link RealtimeToolCall}, it routes the call (the stable `invoke-target-agent` tool →
|
|
6
|
+
* delegate to the target agent; every other tool → an injected tool executor), owns the per-call
|
|
7
|
+
* {@link AbortController} so barge-in can cancel an in-flight delegated run, and serializes the
|
|
8
|
+
* success/error result into the exact JSON the model expects as a `tool_response`.
|
|
9
|
+
*
|
|
10
|
+
* **Why factor this out.** Both audio topologies must execute a realtime tool call *identically*:
|
|
11
|
+
* the **server-bridged** path ({@link RealtimeSessionRunner}, where the provider socket lives on the
|
|
12
|
+
* server) and the **client-direct** path (where the browser owns the socket and relays tool calls
|
|
13
|
+
* back to a server resolver). Keeping one broker guarantees the two paths produce byte-for-byte
|
|
14
|
+
* identical tool results, including structured errors for spoken-error-handling. The broker is the
|
|
15
|
+
* single tool-execution path both topologies share.
|
|
16
|
+
*
|
|
17
|
+
* **Dependency injection.** Every collaborator that would otherwise pull in `BaseAgent`, metadata,
|
|
18
|
+
* or the database is injected via {@link RealtimeToolBrokerDeps}, keeping the broker fully
|
|
19
|
+
* unit-testable against mocks.
|
|
20
|
+
*
|
|
21
|
+
* @module @memberjunction/ai-agents
|
|
22
|
+
* @author MemberJunction.com
|
|
23
|
+
*/
|
|
24
|
+
import { RealtimeToolCall } from '@memberjunction/ai';
|
|
25
|
+
import { AgentExecutionProgressCallback } from '@memberjunction/ai-core-plus';
|
|
26
|
+
/**
|
|
27
|
+
* The stable name of the primary tool every Realtime Co-Agent registers with the realtime provider.
|
|
28
|
+
*
|
|
29
|
+
* Per the plan's design rule, the realtime-registered tool set is **target-independent**: the
|
|
30
|
+
* co-agent always exposes this single `invoke-target-agent` tool, and the specific target is a
|
|
31
|
+
* runtime parameter passed *inside* the call — never a different tool per target. This keeps the
|
|
32
|
+
* provider contract identical across targets (and is what lets a pre-provisioned, fixed-tool
|
|
33
|
+
* provider like Eleven Labs fit the same model later).
|
|
34
|
+
*/
|
|
35
|
+
export declare const INVOKE_TARGET_AGENT_TOOL_NAME = "invoke-target-agent";
|
|
36
|
+
/**
|
|
37
|
+
* A request to delegate work to the target agent, derived from an `invoke-target-agent` tool call.
|
|
38
|
+
*/
|
|
39
|
+
export interface DelegateToTargetRequest {
|
|
40
|
+
/** The provider-assigned call ID, used to correlate the eventual tool result. */
|
|
41
|
+
CallID: string;
|
|
42
|
+
/**
|
|
43
|
+
* The raw arguments JSON string the model emitted for the call (e.g. the natural-language
|
|
44
|
+
* request to hand to the target agent). The delegate parses this into the target's expected
|
|
45
|
+
* input shape.
|
|
46
|
+
*/
|
|
47
|
+
Arguments: string;
|
|
48
|
+
/**
|
|
49
|
+
* Abort signal owned by the broker for this specific delegated call. On barge-in
|
|
50
|
+
* ({@link RealtimeToolBroker.AbortInFlight}), the broker aborts the controller behind this
|
|
51
|
+
* signal so a stale delegated result is never narrated into a conversation that has moved on.
|
|
52
|
+
*/
|
|
53
|
+
AbortSignal: AbortSignal;
|
|
54
|
+
/**
|
|
55
|
+
* Optional progress callback the CALLER wants threaded into the delegated agent run's
|
|
56
|
+
* `onProgress` (in addition to any host-level callback the delegate already wires). The
|
|
57
|
+
* server-bridged {@link import('./realtime-session-runner.js').RealtimeSessionRunner} supplies
|
|
58
|
+
* this so it can narrate the delegated run's progress over the live provider socket
|
|
59
|
+
* (`SendContextNote` / `RequestSpokenUpdate`). Delegates that run agents should invoke it
|
|
60
|
+
* alongside their own progress plumbing; delegates without progress support may ignore it.
|
|
61
|
+
*/
|
|
62
|
+
OnProgress?: AgentExecutionProgressCallback;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One artifact a delegated target-agent run produced, surfaced to the transport layer (and into
|
|
66
|
+
* the serialized tool result as `artifacts: [{ artifactId, artifactVersionId, name }]`) so the
|
|
67
|
+
* client call overlay can open it in an artifact tab while the model narrates the outcome.
|
|
68
|
+
*/
|
|
69
|
+
export interface DelegatedRunArtifact {
|
|
70
|
+
/** The `MJ: Artifacts` row id. */
|
|
71
|
+
ArtifactID: string;
|
|
72
|
+
/** The `MJ: Artifact Versions` row id of the version this run produced. */
|
|
73
|
+
ArtifactVersionID: string;
|
|
74
|
+
/** The artifact's display name (tab title in the call overlay). */
|
|
75
|
+
Name: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The result of a delegated target-agent run, fed back to the realtime model as a tool response.
|
|
79
|
+
*/
|
|
80
|
+
export interface DelegatedResult {
|
|
81
|
+
/** The provider call ID this result corresponds to. */
|
|
82
|
+
CallID: string;
|
|
83
|
+
/** Whether the delegated run completed successfully. */
|
|
84
|
+
Success: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* The textual outcome to narrate (on success) or the error to surface (on failure). The
|
|
87
|
+
* driver feeds this back to the model as the `tool_response` so it can speak the outcome.
|
|
88
|
+
*/
|
|
89
|
+
Output: string;
|
|
90
|
+
/**
|
|
91
|
+
* When the delegated run paused awaiting user feedback (an interactive target agent, e.g.
|
|
92
|
+
* Query Builder confirming a task graph), this carries the paused run's id so the transport
|
|
93
|
+
* layer can persist it and resume the SAME run on the user's next answer (rather than starting
|
|
94
|
+
* a fresh run). Absent when the run completed (or failed) without pausing. The {@link Output} in
|
|
95
|
+
* this case is the agent's clarifying QUESTION, and {@link Success} is `true` (a valid
|
|
96
|
+
* intermediate outcome).
|
|
97
|
+
*/
|
|
98
|
+
PausedRunID?: string;
|
|
99
|
+
/**
|
|
100
|
+
* ID of the delegated target-agent run (`MJ: AI Agent Runs`), when one was created. Serialized
|
|
101
|
+
* into the tool result as `runId` so client developer tooling (the call overlay's dev links)
|
|
102
|
+
* can open the underlying run record. Absent when the delegation failed before a run existed.
|
|
103
|
+
*/
|
|
104
|
+
RunID?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Artifacts the delegated run produced (when artifact creation is enabled for the target and
|
|
107
|
+
* the run returned a payload). Serialized into the tool result as `artifacts` so the call
|
|
108
|
+
* overlay can auto-open them in artifact tabs. Absent/empty when the run produced none.
|
|
109
|
+
*/
|
|
110
|
+
Artifacts?: DelegatedRunArtifact[];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* A non-target tool call routed to the injected {@link RealtimeToolBrokerDeps.ExecuteTool} handler.
|
|
114
|
+
* This is any tool other than `invoke-target-agent` (e.g. a UI/control tool such as `ShowChart`, or
|
|
115
|
+
* a fast server/client tool the co-agent was given directly).
|
|
116
|
+
*/
|
|
117
|
+
export interface ToolExecutionResult {
|
|
118
|
+
/** The provider call ID this result corresponds to. */
|
|
119
|
+
CallID: string;
|
|
120
|
+
/** Whether the tool executed successfully. */
|
|
121
|
+
Success: boolean;
|
|
122
|
+
/** The textual result to feed back to the model as the tool response. */
|
|
123
|
+
Output: string;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Verbose-aware status logging callback.
|
|
127
|
+
*/
|
|
128
|
+
export type RealtimeStatusLogger = (message: string, verboseOnly?: boolean) => void;
|
|
129
|
+
/**
|
|
130
|
+
* Error logging callback.
|
|
131
|
+
*/
|
|
132
|
+
export type RealtimeErrorLogger = (error: Error | string) => void;
|
|
133
|
+
/**
|
|
134
|
+
* The serialized outcome of executing a realtime tool call.
|
|
135
|
+
*
|
|
136
|
+
* {@link RealtimeToolBroker.ExecuteToolCall} returns this so the *transport* layer (server-bridged
|
|
137
|
+
* session or client-direct resolver) can feed {@link ExecutedToolCall.ResultJson} back to the model
|
|
138
|
+
* (e.g. via `IRealtimeSession.SendToolResult`) without knowing anything about how the result was
|
|
139
|
+
* produced.
|
|
140
|
+
*/
|
|
141
|
+
export interface ExecutedToolCall {
|
|
142
|
+
/**
|
|
143
|
+
* The JSON-stringified tool result, shaped exactly as the model expects: `{ success, output }`
|
|
144
|
+
* on success or `{ success: false, error }` on failure (consistent structured errors so the
|
|
145
|
+
* model can narrate the failure — spoken-error-handling).
|
|
146
|
+
*/
|
|
147
|
+
ResultJson: string;
|
|
148
|
+
/** Whether the tool/delegation completed successfully. */
|
|
149
|
+
Success: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* When the delegated target-agent run paused awaiting user feedback, this surfaces the paused
|
|
152
|
+
* run's id (propagated from {@link DelegatedResult.PausedRunID}) so the transport layer can
|
|
153
|
+
* persist it and resume that run on the user's next answer. Absent for non-delegation tools and
|
|
154
|
+
* for delegated runs that completed without pausing.
|
|
155
|
+
*/
|
|
156
|
+
PausedRunID?: string;
|
|
157
|
+
/**
|
|
158
|
+
* ID of the delegated target-agent run (propagated from {@link DelegatedResult.RunID}) — also
|
|
159
|
+
* embedded in {@link ResultJson} as `runId`. Absent for non-delegation tools and for
|
|
160
|
+
* delegations that failed before a run was created.
|
|
161
|
+
*/
|
|
162
|
+
RunID?: string;
|
|
163
|
+
/**
|
|
164
|
+
* Artifacts the delegated run produced (propagated from {@link DelegatedResult.Artifacts}) —
|
|
165
|
+
* also embedded in {@link ResultJson} as `artifacts: [{ artifactId, artifactVersionId, name }]`.
|
|
166
|
+
* Absent for non-delegation tools and for delegations that produced no artifacts.
|
|
167
|
+
*/
|
|
168
|
+
Artifacts?: DelegatedRunArtifact[];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The injected collaborators the broker needs to execute a tool call.
|
|
172
|
+
*
|
|
173
|
+
* Each member is a seam that decouples the broker from `BaseAgent`/metadata/DB so it can be
|
|
174
|
+
* exercised with mocks. In production, the {@link RealtimeSessionRunner} (and, in P5b, the
|
|
175
|
+
* client-direct server resolver) constructs these from `BaseAgent`-backed implementations.
|
|
176
|
+
*/
|
|
177
|
+
export interface RealtimeToolBrokerDeps {
|
|
178
|
+
/**
|
|
179
|
+
* Runs the target agent for an `invoke-target-agent` tool call. The broker creates and owns a
|
|
180
|
+
* fresh {@link AbortController} per delegated call and passes its signal in via
|
|
181
|
+
* {@link DelegateToTargetRequest.AbortSignal}; barge-in aborts it. In production this is backed
|
|
182
|
+
* by `BaseAgent.ExecuteSubAgent` (top-level target only — sub-agents are not exposed).
|
|
183
|
+
*/
|
|
184
|
+
DelegateToTarget: (request: DelegateToTargetRequest) => Promise<DelegatedResult>;
|
|
185
|
+
/**
|
|
186
|
+
* Executes a non-target tool call (any tool other than `invoke-target-agent`). In production
|
|
187
|
+
* this routes to the co-agent's server/client/UI tool execution under the session's context
|
|
188
|
+
* user.
|
|
189
|
+
*/
|
|
190
|
+
ExecuteTool: (call: RealtimeToolCall) => Promise<ToolExecutionResult>;
|
|
191
|
+
/** Optional verbose-aware status logger. */
|
|
192
|
+
LogStatus?: RealtimeStatusLogger;
|
|
193
|
+
/** Optional error logger. */
|
|
194
|
+
LogError?: RealtimeErrorLogger;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Executes a single realtime tool call server-side, identically across both audio topologies.
|
|
198
|
+
*
|
|
199
|
+
* Construct with a {@link RealtimeToolBrokerDeps}, then call {@link ExecuteToolCall} per provider
|
|
200
|
+
* tool call. The broker routes the call, owns the abort controller for an in-flight delegated run,
|
|
201
|
+
* and returns the serialized {@link ExecutedToolCall} the transport layer relays back to the model.
|
|
202
|
+
*/
|
|
203
|
+
export declare class RealtimeToolBroker {
|
|
204
|
+
private deps;
|
|
205
|
+
/** The abort controller for the currently in-flight delegated run, if any. */
|
|
206
|
+
private currentDelegationController;
|
|
207
|
+
/**
|
|
208
|
+
* @param deps The injected collaborators that execute tool calls.
|
|
209
|
+
*/
|
|
210
|
+
constructor(deps: RealtimeToolBrokerDeps);
|
|
211
|
+
/**
|
|
212
|
+
* Executes a realtime tool call and returns its serialized result.
|
|
213
|
+
*
|
|
214
|
+
* Routes `invoke-target-agent` → {@link RealtimeToolBrokerDeps.DelegateToTarget} (creating and
|
|
215
|
+
* owning a per-call {@link AbortController} so {@link AbortInFlight} can cancel it on barge-in),
|
|
216
|
+
* and every other tool → {@link RealtimeToolBrokerDeps.ExecuteTool}. Always resolves with an
|
|
217
|
+
* {@link ExecutedToolCall} — failures are serialized as structured errors rather than thrown, so
|
|
218
|
+
* the model always receives a consistent `tool_response`.
|
|
219
|
+
*
|
|
220
|
+
* @param call The tool-call request emitted by the model.
|
|
221
|
+
* @returns The serialized result the transport layer relays back to the model.
|
|
222
|
+
*/
|
|
223
|
+
ExecuteToolCall(call: RealtimeToolCall): Promise<ExecutedToolCall>;
|
|
224
|
+
/**
|
|
225
|
+
* Aborts the in-flight delegated run's controller (if any).
|
|
226
|
+
*
|
|
227
|
+
* Called by the transport layer on barge-in so a stale delegated result is never narrated into a
|
|
228
|
+
* conversation that has moved on. Safe no-op when no delegation is in flight.
|
|
229
|
+
*/
|
|
230
|
+
AbortInFlight(): void;
|
|
231
|
+
/**
|
|
232
|
+
* Delegates an `invoke-target-agent` call to the target agent, owning a fresh
|
|
233
|
+
* {@link AbortController} so {@link AbortInFlight} can cancel the in-flight run on barge-in.
|
|
234
|
+
*
|
|
235
|
+
* @param call The `invoke-target-agent` tool call.
|
|
236
|
+
* @returns The serialized delegation result (or structured error).
|
|
237
|
+
*/
|
|
238
|
+
private runInvokeTarget;
|
|
239
|
+
/**
|
|
240
|
+
* Routes a non-target tool call to the injected tool executor.
|
|
241
|
+
*
|
|
242
|
+
* @param call The tool call (any tool other than `invoke-target-agent`).
|
|
243
|
+
* @returns The serialized tool result (or structured error).
|
|
244
|
+
*/
|
|
245
|
+
private runOtherTool;
|
|
246
|
+
/**
|
|
247
|
+
* Serializes a successful tool/delegation outcome to the model's expected `tool_response` shape.
|
|
248
|
+
*
|
|
249
|
+
* @param success Whether the tool/delegation completed successfully.
|
|
250
|
+
* @param output The textual outcome (narration on success, error text on failure).
|
|
251
|
+
* @param pausedRunID When the delegated run paused awaiting feedback, the paused run's id to
|
|
252
|
+
* surface for resumption. Omitted for completed runs and non-delegation tools.
|
|
253
|
+
* @param runID ID of the delegated agent run, when one was created. Embedded in the JSON as
|
|
254
|
+
* `runId` so the client overlay's developer tooling can link to the run record. Omitted for
|
|
255
|
+
* non-delegation tools.
|
|
256
|
+
* @param artifacts Artifacts the delegated run produced. Embedded in the JSON as
|
|
257
|
+
* `artifacts: [{ artifactId, artifactVersionId, name }]` so the client overlay can open
|
|
258
|
+
* them in artifact tabs. Omitted when absent or empty.
|
|
259
|
+
* @returns The serialized result.
|
|
260
|
+
*/
|
|
261
|
+
private serializeResult;
|
|
262
|
+
/**
|
|
263
|
+
* Serializes a thrown error to a structured error `tool_response` so the model can narrate the
|
|
264
|
+
* failure (consistent with the plan's spoken-error-handling) rather than leaving the call
|
|
265
|
+
* unanswered.
|
|
266
|
+
*
|
|
267
|
+
* @param error The thrown error or message.
|
|
268
|
+
* @returns The serialized error result (always `Success: false`).
|
|
269
|
+
*/
|
|
270
|
+
private serializeError;
|
|
271
|
+
/**
|
|
272
|
+
* Logs an error via the injected logger (if any), prefixed with the failing operation.
|
|
273
|
+
*
|
|
274
|
+
* @param error The thrown error or message.
|
|
275
|
+
* @param operation A short description of what was being attempted.
|
|
276
|
+
*/
|
|
277
|
+
private logError;
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=realtime-tool-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-tool-broker.d.ts","sourceRoot":"","sources":["../../src/realtime/realtime-tool-broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Topology-agnostic broker that executes a single realtime tool call server-side.
|
|
3
|
+
*
|
|
4
|
+
* The {@link RealtimeToolBroker} owns the *tool-execution* half of a realtime session: given a
|
|
5
|
+
* provider {@link RealtimeToolCall}, it routes the call (the stable `invoke-target-agent` tool →
|
|
6
|
+
* delegate to the target agent; every other tool → an injected tool executor), owns the per-call
|
|
7
|
+
* {@link AbortController} so barge-in can cancel an in-flight delegated run, and serializes the
|
|
8
|
+
* success/error result into the exact JSON the model expects as a `tool_response`.
|
|
9
|
+
*
|
|
10
|
+
* **Why factor this out.** Both audio topologies must execute a realtime tool call *identically*:
|
|
11
|
+
* the **server-bridged** path ({@link RealtimeSessionRunner}, where the provider socket lives on the
|
|
12
|
+
* server) and the **client-direct** path (where the browser owns the socket and relays tool calls
|
|
13
|
+
* back to a server resolver). Keeping one broker guarantees the two paths produce byte-for-byte
|
|
14
|
+
* identical tool results, including structured errors for spoken-error-handling. The broker is the
|
|
15
|
+
* single tool-execution path both topologies share.
|
|
16
|
+
*
|
|
17
|
+
* **Dependency injection.** Every collaborator that would otherwise pull in `BaseAgent`, metadata,
|
|
18
|
+
* or the database is injected via {@link RealtimeToolBrokerDeps}, keeping the broker fully
|
|
19
|
+
* unit-testable against mocks.
|
|
20
|
+
*
|
|
21
|
+
* @module @memberjunction/ai-agents
|
|
22
|
+
* @author MemberJunction.com
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* The stable name of the primary tool every Realtime Co-Agent registers with the realtime provider.
|
|
26
|
+
*
|
|
27
|
+
* Per the plan's design rule, the realtime-registered tool set is **target-independent**: the
|
|
28
|
+
* co-agent always exposes this single `invoke-target-agent` tool, and the specific target is a
|
|
29
|
+
* runtime parameter passed *inside* the call — never a different tool per target. This keeps the
|
|
30
|
+
* provider contract identical across targets (and is what lets a pre-provisioned, fixed-tool
|
|
31
|
+
* provider like Eleven Labs fit the same model later).
|
|
32
|
+
*/
|
|
33
|
+
export const INVOKE_TARGET_AGENT_TOOL_NAME = 'invoke-target-agent';
|
|
34
|
+
/**
|
|
35
|
+
* Executes a single realtime tool call server-side, identically across both audio topologies.
|
|
36
|
+
*
|
|
37
|
+
* Construct with a {@link RealtimeToolBrokerDeps}, then call {@link ExecuteToolCall} per provider
|
|
38
|
+
* tool call. The broker routes the call, owns the abort controller for an in-flight delegated run,
|
|
39
|
+
* and returns the serialized {@link ExecutedToolCall} the transport layer relays back to the model.
|
|
40
|
+
*/
|
|
41
|
+
export class RealtimeToolBroker {
|
|
42
|
+
/**
|
|
43
|
+
* @param deps The injected collaborators that execute tool calls.
|
|
44
|
+
*/
|
|
45
|
+
constructor(deps) {
|
|
46
|
+
/** The abort controller for the currently in-flight delegated run, if any. */
|
|
47
|
+
this.currentDelegationController = null;
|
|
48
|
+
this.deps = deps;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Executes a realtime tool call and returns its serialized result.
|
|
52
|
+
*
|
|
53
|
+
* Routes `invoke-target-agent` → {@link RealtimeToolBrokerDeps.DelegateToTarget} (creating and
|
|
54
|
+
* owning a per-call {@link AbortController} so {@link AbortInFlight} can cancel it on barge-in),
|
|
55
|
+
* and every other tool → {@link RealtimeToolBrokerDeps.ExecuteTool}. Always resolves with an
|
|
56
|
+
* {@link ExecutedToolCall} — failures are serialized as structured errors rather than thrown, so
|
|
57
|
+
* the model always receives a consistent `tool_response`.
|
|
58
|
+
*
|
|
59
|
+
* @param call The tool-call request emitted by the model.
|
|
60
|
+
* @returns The serialized result the transport layer relays back to the model.
|
|
61
|
+
*/
|
|
62
|
+
async ExecuteToolCall(call) {
|
|
63
|
+
if (call.ToolName === INVOKE_TARGET_AGENT_TOOL_NAME) {
|
|
64
|
+
return this.runInvokeTarget(call);
|
|
65
|
+
}
|
|
66
|
+
return this.runOtherTool(call);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Aborts the in-flight delegated run's controller (if any).
|
|
70
|
+
*
|
|
71
|
+
* Called by the transport layer on barge-in so a stale delegated result is never narrated into a
|
|
72
|
+
* conversation that has moved on. Safe no-op when no delegation is in flight.
|
|
73
|
+
*/
|
|
74
|
+
AbortInFlight() {
|
|
75
|
+
if (this.currentDelegationController) {
|
|
76
|
+
this.currentDelegationController.abort();
|
|
77
|
+
this.currentDelegationController = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Delegates an `invoke-target-agent` call to the target agent, owning a fresh
|
|
82
|
+
* {@link AbortController} so {@link AbortInFlight} can cancel the in-flight run on barge-in.
|
|
83
|
+
*
|
|
84
|
+
* @param call The `invoke-target-agent` tool call.
|
|
85
|
+
* @returns The serialized delegation result (or structured error).
|
|
86
|
+
*/
|
|
87
|
+
async runInvokeTarget(call) {
|
|
88
|
+
const controller = new AbortController();
|
|
89
|
+
this.currentDelegationController = controller;
|
|
90
|
+
try {
|
|
91
|
+
const result = await this.deps.DelegateToTarget({
|
|
92
|
+
CallID: call.CallID,
|
|
93
|
+
Arguments: call.Arguments,
|
|
94
|
+
AbortSignal: controller.signal
|
|
95
|
+
});
|
|
96
|
+
return this.serializeResult(result.Success, result.Output, result.PausedRunID, result.RunID, result.Artifacts);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
this.logError(error, 'delegating to target agent');
|
|
100
|
+
return this.serializeError(error);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
// Only clear if this is still the active controller (a later delegation may have replaced it).
|
|
104
|
+
if (this.currentDelegationController === controller) {
|
|
105
|
+
this.currentDelegationController = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Routes a non-target tool call to the injected tool executor.
|
|
111
|
+
*
|
|
112
|
+
* @param call The tool call (any tool other than `invoke-target-agent`).
|
|
113
|
+
* @returns The serialized tool result (or structured error).
|
|
114
|
+
*/
|
|
115
|
+
async runOtherTool(call) {
|
|
116
|
+
try {
|
|
117
|
+
const result = await this.deps.ExecuteTool(call);
|
|
118
|
+
return this.serializeResult(result.Success, result.Output);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
this.logError(error, `executing tool '${call.ToolName}'`);
|
|
122
|
+
return this.serializeError(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Serializes a successful tool/delegation outcome to the model's expected `tool_response` shape.
|
|
127
|
+
*
|
|
128
|
+
* @param success Whether the tool/delegation completed successfully.
|
|
129
|
+
* @param output The textual outcome (narration on success, error text on failure).
|
|
130
|
+
* @param pausedRunID When the delegated run paused awaiting feedback, the paused run's id to
|
|
131
|
+
* surface for resumption. Omitted for completed runs and non-delegation tools.
|
|
132
|
+
* @param runID ID of the delegated agent run, when one was created. Embedded in the JSON as
|
|
133
|
+
* `runId` so the client overlay's developer tooling can link to the run record. Omitted for
|
|
134
|
+
* non-delegation tools.
|
|
135
|
+
* @param artifacts Artifacts the delegated run produced. Embedded in the JSON as
|
|
136
|
+
* `artifacts: [{ artifactId, artifactVersionId, name }]` so the client overlay can open
|
|
137
|
+
* them in artifact tabs. Omitted when absent or empty.
|
|
138
|
+
* @returns The serialized result.
|
|
139
|
+
*/
|
|
140
|
+
serializeResult(success, output, pausedRunID, runID, artifacts) {
|
|
141
|
+
const payload = { success, output };
|
|
142
|
+
if (runID) {
|
|
143
|
+
payload.runId = runID;
|
|
144
|
+
}
|
|
145
|
+
const hasArtifacts = artifacts != null && artifacts.length > 0;
|
|
146
|
+
if (hasArtifacts) {
|
|
147
|
+
payload.artifacts = artifacts.map(a => ({
|
|
148
|
+
artifactId: a.ArtifactID,
|
|
149
|
+
artifactVersionId: a.ArtifactVersionID,
|
|
150
|
+
name: a.Name
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
ResultJson: JSON.stringify(payload),
|
|
155
|
+
Success: success,
|
|
156
|
+
PausedRunID: pausedRunID,
|
|
157
|
+
RunID: runID,
|
|
158
|
+
Artifacts: hasArtifacts ? artifacts : undefined
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Serializes a thrown error to a structured error `tool_response` so the model can narrate the
|
|
163
|
+
* failure (consistent with the plan's spoken-error-handling) rather than leaving the call
|
|
164
|
+
* unanswered.
|
|
165
|
+
*
|
|
166
|
+
* @param error The thrown error or message.
|
|
167
|
+
* @returns The serialized error result (always `Success: false`).
|
|
168
|
+
*/
|
|
169
|
+
serializeError(error) {
|
|
170
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
171
|
+
return { ResultJson: JSON.stringify({ success: false, error: message }), Success: false };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Logs an error via the injected logger (if any), prefixed with the failing operation.
|
|
175
|
+
*
|
|
176
|
+
* @param error The thrown error or message.
|
|
177
|
+
* @param operation A short description of what was being attempted.
|
|
178
|
+
*/
|
|
179
|
+
logError(error, operation) {
|
|
180
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
181
|
+
this.deps.LogError?.(`RealtimeToolBroker error while ${operation}: ${message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=realtime-tool-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-tool-broker.js","sourceRoot":"","sources":["../../src/realtime/realtime-tool-broker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-side channel plugin for the live Whiteboard — the reference
|
|
3
|
+
* `BaseRealtimeChannelServer` implementation, paired with the browser's
|
|
4
|
+
* `RealtimeWhiteboardChannel` client plugin through the seeded `MJ: AI Agent Channels` row
|
|
5
|
+
* (`Name: 'Whiteboard'`, `ServerPluginClass: 'WhiteboardChannelServer'`,
|
|
6
|
+
* `ClientPluginClass: 'RealtimeWhiteboardChannel'`).
|
|
7
|
+
*
|
|
8
|
+
* Deliberately small and honest about its job: the whiteboard executes entirely client-side, so
|
|
9
|
+
* the server half's real value is guarding the **persisted state of record** — the
|
|
10
|
+
* `MJ: AI Agent Session Channels.Config` blob a future resume rehydrates through the client
|
|
11
|
+
* plugin's `RestoreState`. This plugin validates that every save the client lands is parseable
|
|
12
|
+
* board JSON (flagging corrupt payloads loudly instead of discovering them at the next resume)
|
|
13
|
+
* and normalizes valid payloads to a canonical compact serialization.
|
|
14
|
+
*
|
|
15
|
+
* @module @memberjunction/ai-agents
|
|
16
|
+
* @author MemberJunction.com
|
|
17
|
+
*/
|
|
18
|
+
import { BaseRealtimeChannelServer } from '@memberjunction/ai';
|
|
19
|
+
/**
|
|
20
|
+
* Server half of the Whiteboard interactive channel. One instance per realtime session (created
|
|
21
|
+
* by `RealtimeChannelServerHost` from the channel registry — never construct directly).
|
|
22
|
+
*
|
|
23
|
+
* Behavior on each landed state save ({@link OnChannelStateSave}):
|
|
24
|
+
* - payload parses to a JSON **object** → persist the canonical compact re-serialization
|
|
25
|
+
* (`JSON.stringify(parsed)`), or the original when already canonical;
|
|
26
|
+
* - payload is malformed JSON or a non-object (array/primitive) → log loudly and keep the
|
|
27
|
+
* original unchanged — persistence is never blocked, and the client's `RestoreState` contract
|
|
28
|
+
* is tolerant of bad payloads, so flag-don't-drop is the safe posture.
|
|
29
|
+
*/
|
|
30
|
+
export declare class WhiteboardChannelServer extends BaseRealtimeChannelServer {
|
|
31
|
+
/** Matches the seeded `MJ: AI Agent Channels` row's `Name`. */
|
|
32
|
+
get ChannelName(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Validates + canonicalizes a landed board-state save (see the class doc for the contract).
|
|
35
|
+
*
|
|
36
|
+
* @param stateJson The serialized board scene the client submitted.
|
|
37
|
+
* @returns The compact canonical JSON when the payload is a valid object and differs from the
|
|
38
|
+
* input; `null` (= keep the original) otherwise.
|
|
39
|
+
*/
|
|
40
|
+
OnChannelStateSave(stateJson: string): Promise<string | null>;
|
|
41
|
+
/** Flags a corrupt board payload — kept loud so it is caught before the next resume, not at it. */
|
|
42
|
+
private logInvalidState;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Tree-shaking prevention for {@link WhiteboardChannelServer}'s `@RegisterClass` registration.
|
|
46
|
+
* Called from a static code path in the server host (MJServer's `agentSessions` module) so the
|
|
47
|
+
* registration always executes — mirroring every other `Load...()` in the realtime stack.
|
|
48
|
+
*/
|
|
49
|
+
export declare function LoadWhiteboardChannelServer(): void;
|
|
50
|
+
//# sourceMappingURL=whiteboard-channel-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whiteboard-channel-server.d.ts","sourceRoot":"","sources":["../../src/realtime/whiteboard-channel-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAI/D;;;;;;;;;;GAUG;AACH,qBACa,uBAAwB,SAAQ,yBAAyB;IAClE,+DAA+D;IAC/D,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED;;;;;;OAMG;IACmB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgBnF,mGAAmG;IACnG,OAAO,CAAC,eAAe;CAO1B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-side channel plugin for the live Whiteboard — the reference
|
|
3
|
+
* `BaseRealtimeChannelServer` implementation, paired with the browser's
|
|
4
|
+
* `RealtimeWhiteboardChannel` client plugin through the seeded `MJ: AI Agent Channels` row
|
|
5
|
+
* (`Name: 'Whiteboard'`, `ServerPluginClass: 'WhiteboardChannelServer'`,
|
|
6
|
+
* `ClientPluginClass: 'RealtimeWhiteboardChannel'`).
|
|
7
|
+
*
|
|
8
|
+
* Deliberately small and honest about its job: the whiteboard executes entirely client-side, so
|
|
9
|
+
* the server half's real value is guarding the **persisted state of record** — the
|
|
10
|
+
* `MJ: AI Agent Session Channels.Config` blob a future resume rehydrates through the client
|
|
11
|
+
* plugin's `RestoreState`. This plugin validates that every save the client lands is parseable
|
|
12
|
+
* board JSON (flagging corrupt payloads loudly instead of discovering them at the next resume)
|
|
13
|
+
* and normalizes valid payloads to a canonical compact serialization.
|
|
14
|
+
*
|
|
15
|
+
* @module @memberjunction/ai-agents
|
|
16
|
+
* @author MemberJunction.com
|
|
17
|
+
*/
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
import { BaseRealtimeChannelServer } from '@memberjunction/ai';
|
|
25
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
26
|
+
import { LogError } from '@memberjunction/core';
|
|
27
|
+
/**
|
|
28
|
+
* Server half of the Whiteboard interactive channel. One instance per realtime session (created
|
|
29
|
+
* by `RealtimeChannelServerHost` from the channel registry — never construct directly).
|
|
30
|
+
*
|
|
31
|
+
* Behavior on each landed state save ({@link OnChannelStateSave}):
|
|
32
|
+
* - payload parses to a JSON **object** → persist the canonical compact re-serialization
|
|
33
|
+
* (`JSON.stringify(parsed)`), or the original when already canonical;
|
|
34
|
+
* - payload is malformed JSON or a non-object (array/primitive) → log loudly and keep the
|
|
35
|
+
* original unchanged — persistence is never blocked, and the client's `RestoreState` contract
|
|
36
|
+
* is tolerant of bad payloads, so flag-don't-drop is the safe posture.
|
|
37
|
+
*/
|
|
38
|
+
let WhiteboardChannelServer = class WhiteboardChannelServer extends BaseRealtimeChannelServer {
|
|
39
|
+
/** Matches the seeded `MJ: AI Agent Channels` row's `Name`. */
|
|
40
|
+
get ChannelName() {
|
|
41
|
+
return 'Whiteboard';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validates + canonicalizes a landed board-state save (see the class doc for the contract).
|
|
45
|
+
*
|
|
46
|
+
* @param stateJson The serialized board scene the client submitted.
|
|
47
|
+
* @returns The compact canonical JSON when the payload is a valid object and differs from the
|
|
48
|
+
* input; `null` (= keep the original) otherwise.
|
|
49
|
+
*/
|
|
50
|
+
async OnChannelStateSave(stateJson) {
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(stateJson);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
this.logInvalidState('is not valid JSON');
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
60
|
+
this.logInvalidState('is not a JSON object');
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const canonical = JSON.stringify(parsed);
|
|
64
|
+
return canonical === stateJson ? null : canonical;
|
|
65
|
+
}
|
|
66
|
+
/** Flags a corrupt board payload — kept loud so it is caught before the next resume, not at it. */
|
|
67
|
+
logInvalidState(problem) {
|
|
68
|
+
const sessionID = this.Context?.AgentSessionID ?? 'unknown';
|
|
69
|
+
LogError(`[WhiteboardChannelServer] Board state save for session ${sessionID} ${problem} — ` +
|
|
70
|
+
'persisting the payload unchanged; a future resume will start with a fresh board.');
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
WhiteboardChannelServer = __decorate([
|
|
74
|
+
RegisterClass(BaseRealtimeChannelServer, 'WhiteboardChannelServer')
|
|
75
|
+
], WhiteboardChannelServer);
|
|
76
|
+
export { WhiteboardChannelServer };
|
|
77
|
+
/**
|
|
78
|
+
* Tree-shaking prevention for {@link WhiteboardChannelServer}'s `@RegisterClass` registration.
|
|
79
|
+
* Called from a static code path in the server host (MJServer's `agentSessions` module) so the
|
|
80
|
+
* registration always executes — mirroring every other `Load...()` in the realtime stack.
|
|
81
|
+
*/
|
|
82
|
+
export function LoadWhiteboardChannelServer() {
|
|
83
|
+
// no-op — the import + call create a static reference bundlers cannot eliminate
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=whiteboard-channel-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whiteboard-channel-server.js","sourceRoot":"","sources":["../../src/realtime/whiteboard-channel-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;;;;;;;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD;;;;;;;;;;GAUG;AAEI,IAAM,uBAAuB,GAA7B,MAAM,uBAAwB,SAAQ,yBAAyB;IAClE,+DAA+D;IAC/D,IAAW,WAAW;QAClB,OAAO,YAAY,CAAC;IACxB,CAAC;IAED;;;;;;OAMG;IACa,KAAK,CAAC,kBAAkB,CAAC,SAAiB;QACtD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACL,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC;YAC1C,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC,eAAe,CAAC,sBAAsB,CAAC,CAAC;YAC7C,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzC,OAAO,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,CAAC;IAED,mGAAmG;IAC3F,eAAe,CAAC,OAAe;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,cAAc,IAAI,SAAS,CAAC;QAC5D,QAAQ,CACJ,0DAA0D,SAAS,IAAI,OAAO,KAAK;YAC/E,kFAAkF,CACzF,CAAC;IACN,CAAC;CACJ,CAAA;AArCY,uBAAuB;IADnC,aAAa,CAAC,yBAAyB,EAAE,yBAAyB,CAAC;GACvD,uBAAuB,CAqCnC;;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B;IACvC,gFAAgF;AACpF,CAAC"}
|