@openuidev/a2ui 0.3.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/dist/index.mjs ADDED
@@ -0,0 +1,767 @@
1
+ import { i as toJsonObject, n as dataModelToOpenUIState, r as mergeOpenUIStateIntoDataModel, t as applyDataModelUpdate } from "./json-pointer-DCdSrMT3.mjs";
2
+ import { createParser } from "@openuidev/lang-core";
3
+ import { z } from "zod/v4";
4
+ //#region src/protocol-schema.ts
5
+ const jsonValueSchema = z.json();
6
+ const jsonObjectSchema = z.record(z.string(), jsonValueSchema);
7
+ const versionSchema = z.literal("v1.0");
8
+ const surfaceIdSchema = z.string();
9
+ const extensionKeySchema = z.string().regex(/^[\p{XID_Start}_][\p{XID_Continue}]*$/u);
10
+ const extensionsSchema = z.record(extensionKeySchema, jsonValueSchema);
11
+ const messageMetadataSchema = z.strictObject({ extensions: extensionsSchema.optional() });
12
+ const langComponentsSchema = z.array(z.string().min(1)).min(1).describe("Complete OpenUI Lang statements or statement blocks, merged by statement ID in array order.");
13
+ const a2uiFunctionCallSchema = z.strictObject({
14
+ call: z.string(),
15
+ catalogId: z.string().optional(),
16
+ args: jsonObjectSchema.optional()
17
+ });
18
+ const a2uiFunctionResponseSchema = z.union([z.strictObject({
19
+ functionCallId: z.string(),
20
+ value: jsonValueSchema
21
+ }), z.strictObject({
22
+ functionCallId: z.string(),
23
+ error: z.strictObject({
24
+ code: z.string(),
25
+ message: z.string()
26
+ })
27
+ })]);
28
+ const createSurfaceMessageSchema = z.strictObject({
29
+ version: versionSchema,
30
+ createSurface: z.strictObject({
31
+ surfaceId: surfaceIdSchema,
32
+ catalogId: z.string().optional(),
33
+ sendDataModel: z.boolean().optional().meta({ default: false }),
34
+ components: langComponentsSchema.optional().describe("Optional initial OpenUI Lang statements for single-message surface creation."),
35
+ dataModel: jsonObjectSchema.optional(),
36
+ metadata: messageMetadataSchema.optional()
37
+ })
38
+ });
39
+ const updateComponentsMessageSchema = z.strictObject({
40
+ version: versionSchema,
41
+ updateComponents: z.strictObject({
42
+ surfaceId: surfaceIdSchema,
43
+ components: langComponentsSchema
44
+ })
45
+ });
46
+ const updateDataModelMessageSchema = z.strictObject({
47
+ version: versionSchema,
48
+ updateDataModel: z.strictObject({
49
+ surfaceId: surfaceIdSchema,
50
+ path: z.string().optional(),
51
+ value: jsonValueSchema
52
+ })
53
+ });
54
+ const deleteSurfaceMessageSchema = z.strictObject({
55
+ version: versionSchema,
56
+ deleteSurface: z.strictObject({ surfaceId: surfaceIdSchema })
57
+ });
58
+ const callRendererFunctionMessageSchema = z.strictObject({
59
+ version: versionSchema,
60
+ callRendererFunction: z.strictObject({
61
+ functionCallId: z.string(),
62
+ callFunction: a2uiFunctionCallSchema.extend({ catalogId: z.string() })
63
+ })
64
+ });
65
+ const agentFunctionResponseMessageSchema = z.strictObject({
66
+ version: versionSchema,
67
+ agentFunctionResponse: a2uiFunctionResponseSchema
68
+ });
69
+ const agentToRendererMessageSchema = z.union([
70
+ createSurfaceMessageSchema,
71
+ updateComponentsMessageSchema,
72
+ updateDataModelMessageSchema,
73
+ deleteSurfaceMessageSchema,
74
+ callRendererFunctionMessageSchema,
75
+ agentFunctionResponseMessageSchema
76
+ ]);
77
+ const actionMessageSchema = z.strictObject({
78
+ version: versionSchema,
79
+ action: z.strictObject({
80
+ name: z.string(),
81
+ userMessage: z.string().optional(),
82
+ surfaceId: surfaceIdSchema,
83
+ sourceComponentId: z.string(),
84
+ timestamp: z.iso.datetime({ offset: true }),
85
+ context: jsonObjectSchema,
86
+ metadata: messageMetadataSchema.optional()
87
+ })
88
+ });
89
+ const callAgentFunctionMessageSchema = z.strictObject({
90
+ version: versionSchema,
91
+ callAgentFunction: z.strictObject({
92
+ surfaceId: surfaceIdSchema,
93
+ functionCallId: z.string(),
94
+ callFunction: a2uiFunctionCallSchema
95
+ })
96
+ });
97
+ const rendererFunctionResponseMessageSchema = z.strictObject({
98
+ version: versionSchema,
99
+ rendererFunctionResponse: a2uiFunctionResponseSchema
100
+ });
101
+ const validationErrorCodeSchema = z.enum([
102
+ "VALIDATION_FAILED",
103
+ "UNALLOWED_PARENT",
104
+ "UNALLOWED_CHILD"
105
+ ]);
106
+ const validationFailedErrorMessageSchema = z.strictObject({
107
+ version: versionSchema,
108
+ error: z.strictObject({
109
+ code: validationErrorCodeSchema,
110
+ surfaceId: surfaceIdSchema,
111
+ path: z.string(),
112
+ message: z.string()
113
+ })
114
+ });
115
+ const genericErrorCodeSchema = z.string().refine((code) => !validationErrorCodeSchema.options.includes(code));
116
+ const genericErrorMessageSchema = z.union([z.strictObject({
117
+ version: versionSchema,
118
+ error: z.strictObject({
119
+ code: genericErrorCodeSchema,
120
+ message: z.string(),
121
+ surfaceId: surfaceIdSchema
122
+ })
123
+ }), z.strictObject({
124
+ version: versionSchema,
125
+ error: z.strictObject({
126
+ code: genericErrorCodeSchema,
127
+ message: z.string(),
128
+ functionCallId: z.string()
129
+ })
130
+ })]);
131
+ const rendererToAgentMessageSchema = z.union([
132
+ actionMessageSchema,
133
+ callAgentFunctionMessageSchema,
134
+ rendererFunctionResponseMessageSchema,
135
+ validationFailedErrorMessageSchema,
136
+ genericErrorMessageSchema
137
+ ]);
138
+ const rendererCapabilitiesSchema = z.strictObject({ "v1.0": z.strictObject({
139
+ supportedCatalogIds: z.array(z.string()),
140
+ inlineCatalogs: z.array(jsonObjectSchema).optional()
141
+ }) });
142
+ const agentCapabilitiesSchema = z.strictObject({ "v1.0": z.strictObject({
143
+ supportedCatalogIds: z.array(z.string()).optional(),
144
+ acceptsInlineCatalogs: z.boolean().optional().meta({ default: false })
145
+ }) });
146
+ const rendererDataModelSchema = z.strictObject({
147
+ version: versionSchema,
148
+ surfaces: z.record(z.string(), jsonObjectSchema)
149
+ });
150
+ //#endregion
151
+ //#region src/runtime-schema.ts
152
+ function escapePointerToken(token) {
153
+ return String(token).replace(/~/g, "~0").replace(/\//g, "~1");
154
+ }
155
+ function issuePath(path) {
156
+ return path.length === 0 ? "/" : `/${path.map(escapePointerToken).join("/")}`;
157
+ }
158
+ function validateAgentToRendererMessage(input) {
159
+ const object = input != null && typeof input === "object" && !Array.isArray(input) ? input : void 0;
160
+ const presentKeys = object ? [
161
+ "createSurface",
162
+ "updateComponents",
163
+ "updateDataModel",
164
+ "deleteSurface",
165
+ "callRendererFunction",
166
+ "agentFunctionResponse"
167
+ ].filter((key) => Object.prototype.hasOwnProperty.call(object, key)) : [];
168
+ const result = (presentKeys.length === 1 ? {
169
+ createSurface: createSurfaceMessageSchema,
170
+ updateComponents: updateComponentsMessageSchema,
171
+ updateDataModel: updateDataModelMessageSchema,
172
+ deleteSurface: deleteSurfaceMessageSchema,
173
+ callRendererFunction: callRendererFunctionMessageSchema,
174
+ agentFunctionResponse: agentFunctionResponseMessageSchema
175
+ }[presentKeys[0]] : agentToRendererMessageSchema).safeParse(input);
176
+ if (result.success) return {
177
+ success: true,
178
+ message: result.data
179
+ };
180
+ return {
181
+ success: false,
182
+ issues: result.error.issues.map((issue) => ({
183
+ path: issuePath(issue.path),
184
+ message: issue.message
185
+ }))
186
+ };
187
+ }
188
+ //#endregion
189
+ //#region src/statement-patch.ts
190
+ const STATEMENT_PATTERN = /^(\$?[A-Za-z_][A-Za-z0-9_]*)\s*=\s*([\s\S]*)$/u;
191
+ function stripFences(source) {
192
+ const trimmed = source.trim();
193
+ if (!trimmed.startsWith("```")) return trimmed;
194
+ const firstLineEnd = trimmed.indexOf("\n");
195
+ const lastFence = trimmed.lastIndexOf("```");
196
+ if (firstLineEnd === -1 || lastFence <= firstLineEnd) return trimmed;
197
+ return trimmed.slice(firstLineEnd + 1, lastFence).trim();
198
+ }
199
+ function splitStatements(source) {
200
+ const statements = [];
201
+ let depth = 0;
202
+ let ternaryDepth = 0;
203
+ let quote = false;
204
+ let escaped = false;
205
+ let start = 0;
206
+ for (let index = 0; index < source.length; index += 1) {
207
+ const character = source[index];
208
+ if (escaped) {
209
+ escaped = false;
210
+ continue;
211
+ }
212
+ if (character === "\\" && quote) {
213
+ escaped = true;
214
+ continue;
215
+ }
216
+ if (quote) {
217
+ if (character === quote) quote = false;
218
+ continue;
219
+ }
220
+ if (character === "\"" || character === "'") {
221
+ quote = character;
222
+ continue;
223
+ }
224
+ if (character === "(" || character === "[" || character === "{") depth += 1;
225
+ else if (character === ")" || character === "]" || character === "}") depth = Math.max(0, depth - 1);
226
+ else if (character === "?" && depth === 0) ternaryDepth += 1;
227
+ else if (character === ":" && depth === 0 && ternaryDepth > 0) ternaryDepth -= 1;
228
+ else if (character === "\n" && depth === 0 && ternaryDepth === 0) {
229
+ let next = index + 1;
230
+ while (next < source.length && /\s/u.test(source[next])) next += 1;
231
+ if (source[next] === "?") continue;
232
+ const statement = source.slice(start, index).trim();
233
+ if (statement) statements.push(statement);
234
+ start = index + 1;
235
+ }
236
+ }
237
+ const finalStatement = source.slice(start).trim();
238
+ if (finalStatement) statements.push(finalStatement);
239
+ return statements;
240
+ }
241
+ function parseStatements(source) {
242
+ return splitStatements(stripFences(source)).map((raw) => {
243
+ const match = STATEMENT_PATTERN.exec(raw);
244
+ if (!match) throw new Error(`Invalid OpenUI Lang statement: ${raw}`);
245
+ return {
246
+ id: match[1],
247
+ expression: match[2].trim(),
248
+ raw
249
+ };
250
+ });
251
+ }
252
+ /**
253
+ * Applies statement-level A2UI component patches without removing temporarily
254
+ * unreachable statements. A later update may attach those statements to root.
255
+ */
256
+ function mergeComponentStatements(existing, components) {
257
+ const statements = /* @__PURE__ */ new Map();
258
+ const order = [];
259
+ const upsert = (source, allowDeletion) => {
260
+ for (const statement of parseStatements(source)) {
261
+ if (allowDeletion && statement.expression === "null") {
262
+ statements.delete(statement.id);
263
+ const index = order.indexOf(statement.id);
264
+ if (index !== -1) order.splice(index, 1);
265
+ continue;
266
+ }
267
+ if (!statements.has(statement.id)) order.push(statement.id);
268
+ statements.set(statement.id, statement.raw);
269
+ }
270
+ };
271
+ if (existing.trim()) upsert(existing, false);
272
+ for (const component of components) upsert(component, true);
273
+ return order.map((id) => statements.get(id)).join("\n");
274
+ }
275
+ //#endregion
276
+ //#region src/client.ts
277
+ var A2UIFunctionError = class extends Error {
278
+ code;
279
+ constructor(code, message) {
280
+ super(message);
281
+ this.name = "A2UIFunctionError";
282
+ this.code = code;
283
+ }
284
+ };
285
+ function defaultId() {
286
+ return globalThis.crypto?.randomUUID?.() ?? `a2ui-${Date.now()}-${Math.random().toString(16).slice(2)}`;
287
+ }
288
+ function parseError(error) {
289
+ return {
290
+ source: "parser",
291
+ code: error.code,
292
+ message: error.message,
293
+ statementId: error.statementId,
294
+ component: error.component,
295
+ path: error.path
296
+ };
297
+ }
298
+ function isCreateSurface(message) {
299
+ return "createSurface" in message;
300
+ }
301
+ function isUpdateComponents(message) {
302
+ return "updateComponents" in message;
303
+ }
304
+ function isUpdateDataModel(message) {
305
+ return "updateDataModel" in message;
306
+ }
307
+ function isDeleteSurface(message) {
308
+ return "deleteSurface" in message;
309
+ }
310
+ function isCallRendererFunction(message) {
311
+ return "callRendererFunction" in message;
312
+ }
313
+ function record(value) {
314
+ return value != null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
315
+ }
316
+ function validationTarget(input) {
317
+ const message = record(input);
318
+ if (!message) return {};
319
+ for (const key of [
320
+ "createSurface",
321
+ "updateComponents",
322
+ "updateDataModel",
323
+ "deleteSurface"
324
+ ]) {
325
+ const payload = record(message[key]);
326
+ if (typeof payload?.surfaceId === "string") return { surfaceId: payload.surfaceId };
327
+ }
328
+ for (const key of ["callRendererFunction", "agentFunctionResponse"]) {
329
+ const payload = record(message[key]);
330
+ if (typeof payload?.functionCallId === "string") return { functionCallId: payload.functionCallId };
331
+ }
332
+ return {};
333
+ }
334
+ var A2UIClient = class {
335
+ #parser;
336
+ #functions;
337
+ #onMessage;
338
+ #rendererCapabilities;
339
+ #now;
340
+ #createId;
341
+ #surfaces = /* @__PURE__ */ new Map();
342
+ #surfaceListeners = /* @__PURE__ */ new Set();
343
+ #messageListeners = /* @__PURE__ */ new Set();
344
+ #pendingAgentFunctions = /* @__PURE__ */ new Map();
345
+ #revision = 0;
346
+ constructor(options) {
347
+ this.#parser = createParser(options.schema, options.rootName);
348
+ this.#functions = options.functions;
349
+ this.#onMessage = options.onMessage;
350
+ this.#rendererCapabilities = options.rendererCapabilities;
351
+ this.#now = options.now ?? (() => /* @__PURE__ */ new Date());
352
+ this.#createId = options.createId ?? defaultId;
353
+ }
354
+ subscribe(listener) {
355
+ this.#surfaceListeners.add(listener);
356
+ return () => this.#surfaceListeners.delete(listener);
357
+ }
358
+ subscribeMessages(listener) {
359
+ this.#messageListeners.add(listener);
360
+ return () => this.#messageListeners.delete(listener);
361
+ }
362
+ getSurface(surfaceId) {
363
+ return this.#surfaces.get(surfaceId);
364
+ }
365
+ getSurfaces() {
366
+ return [...this.#surfaces.values()];
367
+ }
368
+ getRendererDataModel() {
369
+ const surfaces = Object.fromEntries([...this.#surfaces].filter(([, surface]) => surface.sendDataModel).map(([surfaceId, surface]) => [surfaceId, structuredClone(surface.dataModel)]));
370
+ if (Object.keys(surfaces).length === 0) return void 0;
371
+ return {
372
+ version: "v1.0",
373
+ surfaces
374
+ };
375
+ }
376
+ getRendererMetadata() {
377
+ const dataModel = this.getRendererDataModel();
378
+ return {
379
+ ...this.#rendererCapabilities ? { a2uiRendererCapabilities: structuredClone(this.#rendererCapabilities) } : {},
380
+ ...dataModel ? { a2uiRendererDataModel: dataModel } : {}
381
+ };
382
+ }
383
+ async process(input) {
384
+ const outbound = [];
385
+ const capture = (next) => outbound.push(next);
386
+ this.#messageListeners.add(capture);
387
+ try {
388
+ const validated = validateAgentToRendererMessage(input);
389
+ if (!validated.success) return this.#invalidMessage(input, validated.issues, outbound);
390
+ const message = validated.message;
391
+ if (isCreateSurface(message)) return this.#createSurface(message, outbound);
392
+ if (isUpdateComponents(message)) return this.#updateComponents(message, outbound);
393
+ if (isUpdateDataModel(message)) return this.#updateDataModel(message, outbound);
394
+ if (isDeleteSurface(message)) return this.#deleteSurface(message, outbound);
395
+ if (isCallRendererFunction(message)) return await this.#callRendererFunction(message, outbound);
396
+ return this.#agentFunctionResponse(message, outbound);
397
+ } finally {
398
+ this.#messageListeners.delete(capture);
399
+ }
400
+ }
401
+ updateSurfaceFromOpenUIState(surfaceId, state) {
402
+ const surface = this.#surfaces.get(surfaceId);
403
+ if (!surface) return false;
404
+ const dataModel = mergeOpenUIStateIntoDataModel(surface.dataModel, state);
405
+ if (JSON.stringify(dataModel) === JSON.stringify(surface.dataModel)) return false;
406
+ this.#replaceSurface({
407
+ ...surface,
408
+ dataModel
409
+ });
410
+ return true;
411
+ }
412
+ dispatchOpenUIAction(surfaceId, event, options = {}) {
413
+ const eventSourceComponentId = event.sourceComponentId;
414
+ const context = {
415
+ ...toJsonObject(event.params),
416
+ ...event.formState ? { formState: toJsonObject(event.formState) } : {},
417
+ ...options.context
418
+ };
419
+ this.dispatchAction({
420
+ surfaceId,
421
+ sourceComponentId: options.sourceComponentId ?? (typeof eventSourceComponentId === "string" ? eventSourceComponentId : "root"),
422
+ name: options.name ?? event.type,
423
+ userMessage: (options.userMessage ?? event.humanFriendlyMessage) || void 0,
424
+ context,
425
+ metadata: options.metadata
426
+ });
427
+ }
428
+ dispatchAction(input) {
429
+ if (!this.#surfaces.has(input.surfaceId)) {
430
+ this.#emitGenericError("SURFACE_NOT_FOUND", `Unknown surface: ${input.surfaceId}`, input.surfaceId);
431
+ return;
432
+ }
433
+ const message = {
434
+ version: "v1.0",
435
+ action: {
436
+ name: input.name,
437
+ ...input.userMessage ? { userMessage: input.userMessage } : {},
438
+ surfaceId: input.surfaceId,
439
+ sourceComponentId: input.sourceComponentId,
440
+ timestamp: this.#now().toISOString(),
441
+ context: input.context ?? {},
442
+ ...input.metadata ? { metadata: input.metadata } : {}
443
+ }
444
+ };
445
+ this.#emit(message);
446
+ }
447
+ callAgentFunction(input) {
448
+ if (!this.#surfaces.has(input.surfaceId)) {
449
+ const message = `Unknown surface: ${input.surfaceId}`;
450
+ this.#emitGenericError("SURFACE_NOT_FOUND", message, input.surfaceId);
451
+ return Promise.reject(new A2UIFunctionError("SURFACE_NOT_FOUND", message));
452
+ }
453
+ const functionCallId = this.#createId();
454
+ return new Promise((resolve, reject) => {
455
+ this.#pendingAgentFunctions.set(functionCallId, {
456
+ surfaceId: input.surfaceId,
457
+ resolve,
458
+ reject
459
+ });
460
+ this.#emit({
461
+ version: "v1.0",
462
+ callAgentFunction: {
463
+ surfaceId: input.surfaceId,
464
+ functionCallId,
465
+ callFunction: {
466
+ call: input.call,
467
+ ...input.catalogId ? { catalogId: input.catalogId } : {},
468
+ ...input.args ? { args: input.args } : {}
469
+ }
470
+ }
471
+ });
472
+ });
473
+ }
474
+ dispose() {
475
+ for (const pending of this.#pendingAgentFunctions.values()) pending.reject(new A2UIFunctionError("CLIENT_DISPOSED", "A2UI client was disposed"));
476
+ this.#pendingAgentFunctions.clear();
477
+ this.#surfaces.clear();
478
+ this.#surfaceListeners.clear();
479
+ this.#messageListeners.clear();
480
+ }
481
+ #createSurface(message, outbound) {
482
+ const input = message.createSurface;
483
+ if (this.#surfaces.has(input.surfaceId)) {
484
+ this.#emitGenericError("SURFACE_ALREADY_EXISTS", `Surface already exists: ${input.surfaceId}`, input.surfaceId);
485
+ return {
486
+ ok: false,
487
+ outbound
488
+ };
489
+ }
490
+ const supportedCatalogIds = this.#rendererCapabilities?.["v1.0"].supportedCatalogIds ?? [];
491
+ if (input.catalogId && supportedCatalogIds.length > 0 && !supportedCatalogIds.includes(input.catalogId)) {
492
+ this.#emitGenericError("UNSUPPORTED_CATALOG", `Renderer does not support catalog: ${input.catalogId}`, input.surfaceId);
493
+ return {
494
+ ok: false,
495
+ outbound
496
+ };
497
+ }
498
+ let source = "";
499
+ let parseResult = null;
500
+ let errors = [];
501
+ if (input.components) try {
502
+ ({source, parseResult, errors} = this.#mergeComponents("", input.components));
503
+ } catch (error) {
504
+ this.#emitValidationError(input.surfaceId, "/createSurface/components", error instanceof Error ? error.message : String(error));
505
+ return {
506
+ ok: false,
507
+ outbound
508
+ };
509
+ }
510
+ this.#replaceSurface({
511
+ surfaceId: input.surfaceId,
512
+ catalogId: input.catalogId,
513
+ metadata: input.metadata,
514
+ sendDataModel: input.sendDataModel ?? false,
515
+ source,
516
+ dataModel: structuredClone(input.dataModel ?? {}),
517
+ parseResult,
518
+ errors,
519
+ revision: 0
520
+ });
521
+ for (const error of errors) this.#emitValidationError(input.surfaceId, "/createSurface/components", error.statementId ? `${error.statementId}: ${error.message}` : error.message);
522
+ return {
523
+ ok: errors.length === 0,
524
+ outbound
525
+ };
526
+ }
527
+ #updateComponents(message, outbound) {
528
+ const input = message.updateComponents;
529
+ const surface = this.#requireSurface(input.surfaceId);
530
+ if (!surface) return {
531
+ ok: false,
532
+ outbound
533
+ };
534
+ try {
535
+ const { source, parseResult, errors } = this.#mergeComponents(surface.source, input.components);
536
+ this.#replaceSurface({
537
+ ...surface,
538
+ source,
539
+ parseResult,
540
+ errors
541
+ });
542
+ for (const error of errors) this.#emitValidationError(input.surfaceId, "/updateComponents/components", error.statementId ? `${error.statementId}: ${error.message}` : error.message);
543
+ return {
544
+ ok: errors.length === 0,
545
+ outbound
546
+ };
547
+ } catch (error) {
548
+ this.#emitValidationError(input.surfaceId, "/updateComponents/components", error instanceof Error ? error.message : String(error));
549
+ return {
550
+ ok: false,
551
+ outbound
552
+ };
553
+ }
554
+ }
555
+ #updateDataModel(message, outbound) {
556
+ const input = message.updateDataModel;
557
+ const surface = this.#requireSurface(input.surfaceId);
558
+ if (!surface) return {
559
+ ok: false,
560
+ outbound
561
+ };
562
+ try {
563
+ const dataModel = applyDataModelUpdate(surface.dataModel, input.path, input.value);
564
+ this.#replaceSurface({
565
+ ...surface,
566
+ dataModel
567
+ });
568
+ return {
569
+ ok: true,
570
+ outbound
571
+ };
572
+ } catch (error) {
573
+ this.#emitValidationError(input.surfaceId, "/updateDataModel/path", error instanceof Error ? error.message : String(error));
574
+ return {
575
+ ok: false,
576
+ outbound
577
+ };
578
+ }
579
+ }
580
+ #deleteSurface(message, outbound) {
581
+ const surfaceId = message.deleteSurface.surfaceId;
582
+ if (!this.#requireSurface(surfaceId)) return {
583
+ ok: false,
584
+ outbound
585
+ };
586
+ this.#surfaces.delete(surfaceId);
587
+ for (const [functionCallId, pending] of this.#pendingAgentFunctions) if (pending.surfaceId === surfaceId) {
588
+ pending.reject(new A2UIFunctionError("SURFACE_DELETED", `Surface was deleted: ${surfaceId}`));
589
+ this.#pendingAgentFunctions.delete(functionCallId);
590
+ }
591
+ this.#notify();
592
+ return {
593
+ ok: true,
594
+ outbound
595
+ };
596
+ }
597
+ async #callRendererFunction(message, outbound) {
598
+ const { functionCallId, callFunction } = message.callRendererFunction;
599
+ const { call, catalogId, args = {} } = callFunction;
600
+ const registration = this.#functions?.[call];
601
+ if (!registration) {
602
+ this.#emitGenericError("INVALID_FUNCTION_CALL", `Renderer function is not registered: ${call}`, void 0, functionCallId);
603
+ return {
604
+ ok: false,
605
+ outbound
606
+ };
607
+ }
608
+ const fn = typeof registration === "function" ? registration : registration.handler;
609
+ if (typeof registration === "function" || (registration.allowedCallers ?? "rendererOnly") === "rendererOnly" || registration.catalogId !== catalogId) {
610
+ this.#emitGenericError("INVALID_FUNCTION_CALL", `Renderer function is not callable from catalog ${catalogId}: ${call}`, void 0, functionCallId);
611
+ return {
612
+ ok: false,
613
+ outbound
614
+ };
615
+ }
616
+ try {
617
+ const value = await fn(args);
618
+ this.#emit({
619
+ version: "v1.0",
620
+ rendererFunctionResponse: {
621
+ functionCallId,
622
+ value
623
+ }
624
+ });
625
+ return {
626
+ ok: true,
627
+ outbound
628
+ };
629
+ } catch (error) {
630
+ this.#emit({
631
+ version: "v1.0",
632
+ rendererFunctionResponse: {
633
+ functionCallId,
634
+ error: {
635
+ code: "EXECUTION_FAILED",
636
+ message: error instanceof Error ? error.message : String(error)
637
+ }
638
+ }
639
+ });
640
+ return {
641
+ ok: false,
642
+ outbound
643
+ };
644
+ }
645
+ }
646
+ #agentFunctionResponse(message, outbound) {
647
+ const response = message.agentFunctionResponse;
648
+ const pending = this.#pendingAgentFunctions.get(response.functionCallId);
649
+ if (!pending) return {
650
+ ok: false,
651
+ outbound,
652
+ issues: [{
653
+ path: "/agentFunctionResponse/functionCallId",
654
+ message: `Unknown functionCallId: ${response.functionCallId}`
655
+ }]
656
+ };
657
+ this.#pendingAgentFunctions.delete(response.functionCallId);
658
+ if ("error" in response) {
659
+ pending.reject(new A2UIFunctionError(response.error.code, response.error.message));
660
+ return {
661
+ ok: true,
662
+ outbound
663
+ };
664
+ }
665
+ pending.resolve(response.value);
666
+ return {
667
+ ok: true,
668
+ outbound
669
+ };
670
+ }
671
+ #replaceSurface(surface) {
672
+ this.#revision += 1;
673
+ this.#surfaces.set(surface.surfaceId, {
674
+ ...surface,
675
+ revision: this.#revision
676
+ });
677
+ this.#notify();
678
+ }
679
+ #mergeComponents(existing, components) {
680
+ const source = mergeComponentStatements(existing, components);
681
+ const parseResult = this.#parser.parse(source);
682
+ return {
683
+ source,
684
+ parseResult,
685
+ errors: parseResult.meta.errors.map(parseError)
686
+ };
687
+ }
688
+ #invalidMessage(input, issues, outbound) {
689
+ const target = validationTarget(input);
690
+ const issue = issues[0] ?? {
691
+ path: "/",
692
+ message: "Invalid A2UI message"
693
+ };
694
+ if (target.surfaceId !== void 0) {
695
+ this.#emitValidationError(target.surfaceId, issue.path, issue.message);
696
+ return {
697
+ ok: false,
698
+ outbound,
699
+ issues
700
+ };
701
+ }
702
+ if (target.functionCallId !== void 0) {
703
+ this.#emitGenericError("INVALID_MESSAGE", `${issue.path}: ${issue.message}`, void 0, target.functionCallId);
704
+ return {
705
+ ok: false,
706
+ outbound,
707
+ issues
708
+ };
709
+ }
710
+ return {
711
+ ok: false,
712
+ outbound,
713
+ issues
714
+ };
715
+ }
716
+ #requireSurface(surfaceId) {
717
+ const surface = this.#surfaces.get(surfaceId);
718
+ if (!surface) this.#emitGenericError("SURFACE_NOT_FOUND", `Unknown surface: ${surfaceId}`, surfaceId);
719
+ return surface;
720
+ }
721
+ #emitValidationError(surfaceId, path, message) {
722
+ const error = {
723
+ version: "v1.0",
724
+ error: {
725
+ code: "VALIDATION_FAILED",
726
+ surfaceId,
727
+ path,
728
+ message
729
+ }
730
+ };
731
+ this.#emit(error);
732
+ }
733
+ #emitGenericError(code, message, surfaceId, functionCallId) {
734
+ if (surfaceId === void 0 && functionCallId === void 0) return;
735
+ const error = surfaceId !== void 0 ? {
736
+ version: "v1.0",
737
+ error: {
738
+ code,
739
+ message,
740
+ surfaceId
741
+ }
742
+ } : {
743
+ version: "v1.0",
744
+ error: {
745
+ code,
746
+ message,
747
+ functionCallId
748
+ }
749
+ };
750
+ this.#emit(error);
751
+ }
752
+ #emit(message) {
753
+ const metadata = this.getRendererMetadata();
754
+ this.#onMessage?.(message, metadata);
755
+ for (const listener of this.#messageListeners) listener(message, metadata);
756
+ }
757
+ #notify() {
758
+ for (const listener of this.#surfaceListeners) listener();
759
+ }
760
+ };
761
+ function createA2UIClient(options) {
762
+ return new A2UIClient(options);
763
+ }
764
+ //#endregion
765
+ export { A2UIClient, A2UIFunctionError, a2uiFunctionCallSchema, a2uiFunctionResponseSchema, actionMessageSchema, agentCapabilitiesSchema, agentFunctionResponseMessageSchema, agentToRendererMessageSchema, applyDataModelUpdate, callAgentFunctionMessageSchema, callRendererFunctionMessageSchema, createA2UIClient, createSurfaceMessageSchema, dataModelToOpenUIState, deleteSurfaceMessageSchema, genericErrorMessageSchema, jsonObjectSchema, jsonValueSchema, mergeOpenUIStateIntoDataModel, messageMetadataSchema, rendererCapabilitiesSchema, rendererDataModelSchema, rendererFunctionResponseMessageSchema, rendererToAgentMessageSchema, updateComponentsMessageSchema, updateDataModelMessageSchema, validateAgentToRendererMessage, validationFailedErrorMessageSchema };
766
+
767
+ //# sourceMappingURL=index.mjs.map