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