@masons/agent-network 0.4.16 → 0.4.17

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.
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { EventEmitter } from "node:events";
3
3
  import createDebug from "debug";
4
4
  import WebSocket from "ws";
5
- import { CURRENT_PROTOCOL_VERSION, isErrorEvent, isFlushComplete, isMessageReceived, isRegisterAck, isSessionCreated, isSessionEnded, REGISTER_ACK_TIMEOUT_MS, } from "./types.js";
5
+ import { CURRENT_PROTOCOL_VERSION, isAddressedMessage, isDeliveryPending, isRegisterAck, isSendAck, isStructuredError, REGISTER_ACK_TIMEOUT_MS, SEND_ACK_TIMEOUT_MS, } from "./types.js";
6
6
  import { PLUGIN_VERSION } from "./version.js";
7
7
  const dbg = createDebug("agent-network:connector");
8
8
  const dbgMsg = createDebug("agent-network:connector:msg");
@@ -11,6 +11,21 @@ const BACKOFF_INITIAL_MS = 1_000;
11
11
  const BACKOFF_MAX_MS = 30_000;
12
12
  const ALREADY_CONNECTED_RETRY_MS = 2_000;
13
13
  const ALREADY_CONNECTED_MAX_RETRIES = 30;
14
+ // --- Error class for structured errors ---
15
+ /**
16
+ * Error with machine-readable `code` from Connector structured errors.
17
+ * Thrown by `send()` when the Connector responds with ERROR { code }.
18
+ */
19
+ export class ConnectorError extends Error {
20
+ code;
21
+ to;
22
+ constructor(message, code, to) {
23
+ super(message);
24
+ this.name = "ConnectorError";
25
+ this.code = code;
26
+ this.to = to;
27
+ }
28
+ }
14
29
  // --- ConnectorClient ---
15
30
  export class ConnectorClient extends EventEmitter {
16
31
  url;
@@ -23,6 +38,8 @@ export class ConnectorClient extends EventEmitter {
23
38
  registerResolve = null;
24
39
  registerReject = null;
25
40
  registerTimer = null;
41
+ /** Pending SEND_ACK correlation map for address-based sends. */
42
+ pendingSends = new Map();
26
43
  constructor(url, token) {
27
44
  super();
28
45
  this.url = url;
@@ -49,58 +66,54 @@ export class ConnectorClient extends EventEmitter {
49
66
  wsToClose.close(1000, "Client disconnect");
50
67
  }
51
68
  }
52
- createSession(target, opts) {
53
- const requestId = randomUUID();
54
- const event = {
55
- event: "CREATE_SESSION",
56
- requestId,
57
- target,
58
- secret: opts?.secret,
59
- metadata: opts?.metadata,
60
- };
61
- const sent = this.send(event);
62
- dbg("createSession target=%s requestId=%s sent=%s", target, requestId, sent);
63
- return { requestId, sent };
64
- }
65
- sendMessage(sessionId, content, opts) {
69
+ // --- Messaging ---
70
+ /**
71
+ * Send a message to a routable address. Returns a Promise that resolves
72
+ * with the SEND_ACK from the Connector, or rejects with a ConnectorError
73
+ * (structured error code) or a plain Error (timeout / disconnect).
74
+ *
75
+ * The messageId is generated internally and used for ACK correlation.
76
+ */
77
+ send(to, content, contentType = "text", metadata) {
78
+ const messageId = randomUUID();
66
79
  const event = {
67
80
  event: "SEND_MESSAGE",
68
- sessionId,
81
+ messageId,
82
+ to,
69
83
  content,
70
- contentType: opts?.contentType,
71
- metadata: opts?.metadata,
72
- };
73
- const sent = this.send(event);
74
- dbgMsg("sendMessage sessionId=%s contentLength=%d sent=%s", sessionId, content.length, sent);
75
- return sent;
76
- }
77
- endSession(sessionId, reason) {
78
- const event = {
79
- event: "END_SESSION",
80
- sessionId,
81
- reason,
84
+ contentType,
85
+ metadata,
82
86
  };
83
- const sent = this.send(event);
84
- dbg("endSession sessionId=%s sent=%s", sessionId, sent);
85
- return sent;
86
- }
87
- sendDeliveryAck(mailboxSessionId) {
88
- const event = {
89
- event: "DELIVERY_ACK",
90
- mailboxSessionId,
91
- };
92
- const sent = this.send(event);
93
- dbg("sendDeliveryAck mailboxSessionId=%s sent=%s", mailboxSessionId, sent);
94
- return sent;
87
+ return new Promise((resolve, reject) => {
88
+ const timer = setTimeout(() => {
89
+ this.pendingSends.delete(messageId);
90
+ reject(new Error("SEND_ACK timeout"));
91
+ }, SEND_ACK_TIMEOUT_MS);
92
+ this.pendingSends.set(messageId, { resolve, reject, timer });
93
+ const sent = this.sendEvent(event);
94
+ if (!sent) {
95
+ clearTimeout(timer);
96
+ this.pendingSends.delete(messageId);
97
+ reject(new Error("WebSocket not connected"));
98
+ }
99
+ });
95
100
  }
96
- sendTypingStart(sessionId) {
97
- const sent = this.send({ event: "TYPING_START", sessionId });
98
- dbg("sendTypingStart sessionId=%s sent=%s", sessionId, sent);
101
+ /**
102
+ * Send a typing indicator to a routable address.
103
+ */
104
+ sendTyping(to, isTyping) {
105
+ const event = { event: "TYPING", to, isTyping };
106
+ const sent = this.sendEvent(event);
107
+ dbg("sendTyping to=%s isTyping=%s sent=%s", to, isTyping, sent);
99
108
  return sent;
100
109
  }
101
- sendTypingStop(sessionId) {
102
- const sent = this.send({ event: "TYPING_STOP", sessionId });
103
- dbg("sendTypingStop sessionId=%s sent=%s", sessionId, sent);
110
+ /**
111
+ * Acknowledge delivery of stored messages up to a timestamp.
112
+ */
113
+ ackDelivery(upTo) {
114
+ const event = { event: "DELIVERY_ACK", upTo };
115
+ const sent = this.sendEvent(event);
116
+ dbg("ackDelivery upTo=%s sent=%s", upTo, sent);
104
117
  return sent;
105
118
  }
106
119
  on(event, listener) {
@@ -138,7 +151,7 @@ export class ConnectorClient extends EventEmitter {
138
151
  protocolVersion: CURRENT_PROTOCOL_VERSION,
139
152
  clientVersion: PLUGIN_VERSION,
140
153
  };
141
- this.send(event);
154
+ this.sendEvent(event);
142
155
  }
143
156
  startRegisterTimeout() {
144
157
  this.registerTimer = setTimeout(() => {
@@ -178,40 +191,46 @@ export class ConnectorClient extends EventEmitter {
178
191
  this.emit("error", new Error("Received non-JSON message from Connector"));
179
192
  return;
180
193
  }
194
+ // REGISTER_ACK is version-agnostic — always handled first
181
195
  if (isRegisterAck(parsed)) {
182
196
  this.handleRegisterAck(parsed);
183
197
  return;
184
198
  }
185
- if (isSessionCreated(parsed)) {
186
- dbg("SESSION_CREATED sessionId=%s direction=%s", parsed.sessionId, parsed.direction);
187
- this.handleSessionCreated(parsed);
199
+ this.dispatchAddressed(parsed);
200
+ };
201
+ /** Dispatch inbound address-based events. */
202
+ dispatchAddressed(parsed) {
203
+ if (isSendAck(parsed)) {
204
+ dbg("SEND_ACK messageId=%s status=%s", parsed.messageId, parsed.status);
205
+ this.handleSendAck(parsed);
188
206
  return;
189
207
  }
190
- if (isMessageReceived(parsed)) {
191
- dbgMsg("MESSAGE_RECEIVED sessionId=%s contentLength=%d", parsed.sessionId, parsed.content.length);
208
+ if (isAddressedMessage(parsed)) {
209
+ dbgMsg("MESSAGE_RECEIVED from=%s contentLength=%d", parsed.from, parsed.content.length);
192
210
  this.emit("message_received", parsed);
193
211
  return;
194
212
  }
195
- if (isSessionEnded(parsed)) {
196
- dbg("SESSION_ENDED sessionId=%s", parsed.sessionId);
197
- this.emit("session_ended", parsed);
213
+ if (isDeliveryPending(parsed)) {
214
+ dbg("DELIVERY_PENDING count=%d upTo=%s", parsed.count, parsed.upTo);
215
+ this.emit("delivery_pending", parsed);
198
216
  return;
199
217
  }
200
- if (isFlushComplete(parsed)) {
201
- dbg("FLUSH_COMPLETE mailboxSessionId=%s messageCount=%d", parsed.mailboxSessionId, parsed.messageCount);
202
- this.emit("flush_complete", parsed);
203
- return;
204
- }
205
- if (isErrorEvent(parsed)) {
206
- dbg("ERROR message=%s", parsed.message);
207
- this.handleErrorEvent(parsed);
218
+ if (isStructuredError(parsed)) {
219
+ dbg("ERROR code=%s message=%s", parsed.code, parsed.message);
220
+ this.handleStructuredError(parsed);
208
221
  return;
209
222
  }
210
223
  // Unknown event type — silently ignore (forward compatibility)
211
- };
224
+ }
212
225
  handleRegisterAck(ack) {
213
- dbg("REGISTER_ACK status=%s", ack.status);
226
+ dbg("REGISTER_ACK status=%s protocolVersion=%d", ack.status, ack.protocolVersion);
214
227
  if (ack.status === "ok") {
228
+ // Defensive: verify the negotiated version matches what we requested.
229
+ // Current Connector rejects unsupported versions outright (no downgrade),
230
+ // so this check is future-proofing against silent protocol desync.
231
+ if (ack.protocolVersion !== CURRENT_PROTOCOL_VERSION) {
232
+ dbg("WARN: server negotiated protocolVersion=%d, expected=%d", ack.protocolVersion, CURRENT_PROTOCOL_VERSION);
233
+ }
215
234
  this.backoffMs = BACKOFF_INITIAL_MS;
216
235
  this.alreadyConnectedRetries = 0;
217
236
  this.resolveRegister();
@@ -235,17 +254,38 @@ export class ConnectorClient extends EventEmitter {
235
254
  this.ws?.close(4001, reason);
236
255
  }
237
256
  }
238
- handleSessionCreated(event) {
239
- this.emit("session_created", event);
257
+ // --- Address-based handlers ---
258
+ /**
259
+ * Resolve the pending send promise matching this SEND_ACK.
260
+ * Also emits `send_ack` for listeners that want to observe all ACKs.
261
+ */
262
+ handleSendAck(ack) {
263
+ this.emit("send_ack", ack);
264
+ const pending = this.pendingSends.get(ack.messageId);
265
+ if (pending) {
266
+ clearTimeout(pending.timer);
267
+ this.pendingSends.delete(ack.messageId);
268
+ pending.resolve(ack);
269
+ }
240
270
  }
241
- handleErrorEvent(event) {
242
- // (1) Route by sessionId session-scoped error
243
- if (event.sessionId) {
244
- this.emit("session_error", event.sessionId, event.message);
245
- return;
271
+ /**
272
+ * Structured ERROR: message-scoped errors reject pending sends,
273
+ * connection-scoped errors emit generic event.
274
+ */
275
+ handleStructuredError(event) {
276
+ this.emit("structured_error", event);
277
+ // Message-scoped: reject the pending send for this messageId
278
+ if (event.messageId) {
279
+ const pending = this.pendingSends.get(event.messageId);
280
+ if (pending) {
281
+ clearTimeout(pending.timer);
282
+ this.pendingSends.delete(event.messageId);
283
+ pending.reject(new ConnectorError(event.message, event.code, event.to));
284
+ return;
285
+ }
246
286
  }
247
- // (2) Global error (includes requestId-only errors from failed CREATE_SESSION)
248
- this.emit("error", new Error(event.message));
287
+ // Connection-scoped: emit generic error
288
+ this.emit("error", new Error(`[${event.code}] ${event.message}`));
249
289
  }
250
290
  // --- Connection close ---
251
291
  handleClose = () => {
@@ -320,9 +360,16 @@ export class ConnectorClient extends EventEmitter {
320
360
  clearTimeout(this.registerTimer);
321
361
  this.registerTimer = null;
322
362
  }
363
+ // Reject all pending address-based sends on disconnect
364
+ for (const [, pending] of this.pendingSends) {
365
+ clearTimeout(pending.timer);
366
+ pending.reject(new Error("Connection closed"));
367
+ }
368
+ this.pendingSends.clear();
323
369
  }
324
- // --- Helpers ---
325
- send(event) {
370
+ // --- Wire helpers ---
371
+ /** Send an event over the WebSocket. */
372
+ sendEvent(event) {
326
373
  if (this.ws?.readyState === WebSocket.OPEN) {
327
374
  this.ws.send(JSON.stringify(event));
328
375
  return true;
@@ -2,20 +2,21 @@
2
2
  * Conversation Manager — maps contacts to conversations.
3
3
  *
4
4
  * Owns the identity-based API that the Tool Interface and Inbound Dispatcher
5
- * call. Internally delegates session management to SessionLifecycle.
5
+ * call. Sends messages directly to routable addresses via ConnectorClient.send().
6
6
  *
7
- * Contact resolution: handle -> `mstps://${connectorHost}/${handle}`
8
- * Internal key: MSTP address (same as SessionLifecycle).
9
- * One session per contact, last-write-wins (D2).
10
- * In-memory only, no persistence (D3).
7
+ * Contact resolution: handle `mstps://${connectorHost}/${handle}`
8
+ * Address schemes: MSTP (`mstps://`), handle (bare), passport (`passport:@`)
9
+ * Internal key: routable address.
10
+ * One conversation per contact, in-memory only (D3).
11
11
  *
12
- * @see docs/openclaw/session-abstraction-system-design.md §4.1
12
+ * @see docs/connector/gateway-v2-consumer-adaptation-plan.md PR 2
13
13
  */
14
- import type { ConnectorClient } from "./connector-client.js";
15
- import { SessionLifecycle } from "./session-lifecycle.js";
14
+ import { type ConnectorClient } from "./connector-client.js";
16
15
  export interface SendResult {
17
16
  status: "sent" | "failed";
18
17
  error?: string;
18
+ /** Machine-readable error code from Connector (when status === "failed"). */
19
+ errorCode?: string;
19
20
  }
20
21
  export interface ConversationEntry {
21
22
  contact: string;
@@ -31,37 +32,37 @@ export interface ConversationSummary {
31
32
  initiatedBy: "local" | "remote";
32
33
  }
33
34
  export declare class ConversationManager {
34
- readonly sessionLifecycle: SessionLifecycle;
35
35
  private readonly client;
36
36
  private readonly connectorHost;
37
- /** Conversations keyed by MSTP address */
37
+ /** Conversations keyed by routable address */
38
38
  private readonly conversations;
39
39
  constructor(client: ConnectorClient, connectorHost: string);
40
40
  /**
41
- * Send a message to a contact. Session management is transparent.
42
- * Creates a session automatically if none exists (D1 — sync-but-transparent).
41
+ * Send a message to a contact. Sends directly to the resolved address —
42
+ * no session management needed.
43
43
  *
44
- * @param contact - Handle or MSTP address of the target agent.
44
+ * Returns a structured result with status and optional error.
45
+ * ConnectorError codes (ADDRESS_NOT_FOUND, ACCESS_DENIED, etc.) are
46
+ * mapped to human-readable messages for LLM tool results.
47
+ *
48
+ * @param contact - Handle, MSTP address, or passport address of the target.
45
49
  * @param content - Message content to send.
46
50
  */
47
51
  send(contact: string, content: string): Promise<SendResult>;
48
52
  /**
49
- * Register an inbound session (remote agent initiated).
50
- * Called by the channel adapter on SESSION_CREATED with direction=inbound.
51
- */
52
- registerInbound(sessionId: string, contact: string, address: string): void;
53
- /**
54
- * Resolve a sessionId to a contact handle.
55
- * Used by the inbound dispatcher to derive sender identity from a message event.
53
+ * Register an inbound conversation (remote agent or visitor initiated).
54
+ * Called by the channel adapter when a message arrives from a new address.
56
55
  */
57
- getContactBySessionId(sessionId: string): string | undefined;
56
+ registerInbound(contact: string, address: string): void;
58
57
  /**
59
- * Resolve a sessionId to an MSTP address.
60
- * Used by the channel adapter for reverse lookup.
58
+ * Resolve a routable address to a contact handle.
59
+ * Used by the inbound dispatcher to derive sender identity from MESSAGE_RECEIVED.from.
61
60
  */
62
- getAddressBySessionId(sessionId: string): string | undefined;
61
+ getContactByAddress(address: string): string | undefined;
63
62
  /**
64
63
  * List all conversations with metadata.
64
+ * `active` is always true for address-based protocol — there is no session
65
+ * lifecycle. Conversations persist until explicitly ended or Plugin restarts.
65
66
  */
66
67
  listConversations(): ConversationSummary[];
67
68
  /**
@@ -74,25 +75,31 @@ export declare class ConversationManager {
74
75
  hasConversation(contact: string): boolean;
75
76
  /**
76
77
  * End the conversation with a contact.
77
- * Closes the active session if one exists.
78
+ * Removes the conversation entry. No END_SESSION needed — the Connector
79
+ * manages connection lifecycle (idle timeout).
78
80
  */
79
81
  endConversation(contact: string): void;
80
82
  /**
81
- * Resolve a contact (handle or MSTP address) to an MSTP address.
83
+ * Resolve a contact (handle, MSTP address, or passport address) to a
84
+ * routable address.
82
85
  *
83
86
  * Resolution order:
84
- * 1. If `contact` is already an MSTP address (starts with mstps:// or mstp://),
85
- * return it as-is.
86
- * 2. If `contact` is already a key in the conversations map (e.g., a sessionId
87
- * used as fallback address for direct MSTP clients), return it as-is.
88
- * 3. Otherwise construct: `mstps://${connectorHost}/${handle}`.
87
+ * 1. MSTP address (starts with mstps:// or mstp://) → return as-is.
88
+ * 2. Passport address (starts with passport:) → return as-is (already routable).
89
+ * 3. Already a key in the conversations map return as-is.
90
+ * 4. Reverse lookup: if any existing conversation has this handle as its
91
+ * contact, use that conversation's address. This ensures replies to a
92
+ * cross-Connector sender route to the sender's original address, not to
93
+ * a locally-constructed address.
94
+ * 5. Otherwise treat as handle → construct `mstps://${connectorHost}/${handle}`.
89
95
  */
90
96
  resolveAddress(contact: string): string;
91
97
  /**
92
- * Extract handle from an MSTP address.
93
- * `mstps://preview.masons.ai/alice` -> `alice`
98
+ * Extract a display-friendly handle from any address scheme.
99
+ * Delegates to shared utility — single source of truth.
94
100
  */
95
101
  extractHandle(address: string): string;
102
+ private ensureConversationEntry;
96
103
  /** @internal Reset for test isolation. */
97
104
  _resetForTesting(): void;
98
105
  }
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-manager.d.ts","sourceRoot":"","sources":["../src/conversation-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAQ1D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAMD,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;gBAE1D,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM;IAU1D;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAgDjE;;;OAGG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAyB1E;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ5D;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5D;;OAEG;IACH,iBAAiB,IAAI,mBAAmB,EAAE;IAe1C;;;;;;OAMG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKzC;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAkBtC;;;;;;;;;OASG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAYvC;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAUtC,0CAA0C;IAC1C,gBAAgB,IAAI,IAAI;CAIzB"}
1
+ {"version":3,"file":"conversation-manager.d.ts","sourceRoot":"","sources":["../src/conversation-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,KAAK,eAAe,EAAkB,MAAM,uBAAuB,CAAC;AAS7E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACjC;AAwBD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;gBAE1D,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM;IAS1D;;;;;;;;;;OAUG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IA0CjE;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAKvD;;;OAGG;IACH,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAaxD;;;;OAIG;IACH,iBAAiB,IAAI,mBAAmB,EAAE;IAc1C;;;;;;OAMG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKzC;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAUtC;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAoBvC;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAQtC,OAAO,CAAC,uBAAuB;IAwB/B,0CAA0C;IAC1C,gBAAgB,IAAI,IAAI;CAGzB"}