@ukiahinsure/a2ui-react-adapter 0.1.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 +44 -0
- package/dist/a2ui-structural.css +205 -0
- package/dist/chunk-AOBRTDAU.js +33 -0
- package/dist/chunk-AOBRTDAU.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +2854 -0
- package/dist/index.js.map +1 -0
- package/dist/providerValidation.d.ts +59 -0
- package/dist/providerValidation.js +249 -0
- package/dist/providerValidation.js.map +1 -0
- package/dist/session-B_2-bK2s.d.ts +348 -0
- package/package.json +53 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
A2UI_CAPABILITIES_TOPIC,
|
|
3
|
+
A2UI_CLIENT_TOPIC,
|
|
4
|
+
A2UI_SERVER_TOPIC
|
|
5
|
+
} from "./chunk-AOBRTDAU.js";
|
|
6
|
+
|
|
7
|
+
// src/providerValidation.ts
|
|
8
|
+
var MAX_EVIDENCE_EVENTS = 128;
|
|
9
|
+
function createA2UIProviderValidationProbe(sourceRoom) {
|
|
10
|
+
const evidence = [];
|
|
11
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
12
|
+
const scenarioByRequest = /* @__PURE__ */ new Map();
|
|
13
|
+
let lastActionText;
|
|
14
|
+
let lastCapability;
|
|
15
|
+
let disposed = false;
|
|
16
|
+
const record = (event) => {
|
|
17
|
+
if (disposed) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
evidence.push(event);
|
|
21
|
+
if (evidence.length > MAX_EVIDENCE_EVENTS) {
|
|
22
|
+
evidence.splice(0, evidence.length - MAX_EVIDENCE_EVENTS);
|
|
23
|
+
}
|
|
24
|
+
for (const listener of listeners) {
|
|
25
|
+
listener();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const inspectOutgoing = (text, topic) => {
|
|
29
|
+
const envelope = decodeObject(text);
|
|
30
|
+
if (envelope === null) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const requestId = safeString(envelope.requestId);
|
|
34
|
+
if (topic === A2UI_CAPABILITIES_TOPIC && envelope.kind === "capabilities") {
|
|
35
|
+
const capability = envelope;
|
|
36
|
+
lastCapability = capability;
|
|
37
|
+
if (requestId !== void 0) {
|
|
38
|
+
scenarioByRequest.set(requestId, "capability");
|
|
39
|
+
}
|
|
40
|
+
record({
|
|
41
|
+
name: "capability_sent",
|
|
42
|
+
exactEnvelopeVersion: envelope.envelopeVersion === "v1",
|
|
43
|
+
exactA2uiVersion: Array.isArray(envelope.supportedA2uiVersions) && envelope.supportedA2uiVersions.length === 1 && envelope.supportedA2uiVersions[0] === "0.9.1",
|
|
44
|
+
exactCatalog: Array.isArray(envelope.supportedCatalogIds) && envelope.supportedCatalogIds.length === 1 && envelope.supportedCatalogIds[0] === "https://a2ui.org/specification/v0_9/basic_catalog.json",
|
|
45
|
+
exactRenderer: typeof envelope.renderer === "object" && envelope.renderer !== null && "name" in envelope.renderer && "version" in envelope.renderer && envelope.renderer.name === "a2ui_react_adapter" && envelope.renderer.version === "0.1.0",
|
|
46
|
+
requestsSnapshot: envelope.requestSnapshot === true
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (topic === A2UI_CLIENT_TOPIC && envelope.kind === "action") {
|
|
51
|
+
const action = safeAction(envelope.action);
|
|
52
|
+
const revision = safeInteger(envelope.revision);
|
|
53
|
+
if (requestId === void 0 || action === void 0 || revision === null) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
lastActionText = text;
|
|
57
|
+
scenarioByRequest.set(requestId, "action");
|
|
58
|
+
record({ name: "action_sent", action, revision });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const inspectIncoming = (text) => {
|
|
62
|
+
const envelope = decodeObject(text);
|
|
63
|
+
if (envelope === null) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const seq = safeInteger(envelope.seq);
|
|
67
|
+
if (seq === null) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (envelope.kind === "snapshot" || envelope.kind === "surfaceUpdate") {
|
|
71
|
+
const revision = safeInteger(envelope.revision);
|
|
72
|
+
if (revision === null) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const inResponseTo2 = safeString(envelope.inResponseTo);
|
|
76
|
+
record({
|
|
77
|
+
name: "server_surface",
|
|
78
|
+
kind: envelope.kind,
|
|
79
|
+
seq,
|
|
80
|
+
revision,
|
|
81
|
+
correlated: inResponseTo2 !== void 0 && scenarioByRequest.has(inResponseTo2)
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (envelope.kind !== "ack") {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const inResponseTo = safeString(envelope.inResponseTo);
|
|
89
|
+
const status = envelope.status;
|
|
90
|
+
if (inResponseTo === void 0 || status !== "accepted" && status !== "rejected") {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const scenario = scenarioByRequest.get(inResponseTo);
|
|
94
|
+
if (scenario === void 0) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const reasonCode = safeReason(envelope.reasonCode);
|
|
98
|
+
record({
|
|
99
|
+
name: "server_ack",
|
|
100
|
+
scenario,
|
|
101
|
+
seq,
|
|
102
|
+
status,
|
|
103
|
+
...reasonCode === void 0 ? {} : { reasonCode },
|
|
104
|
+
correlated: true
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
const room = {
|
|
108
|
+
localParticipant: {
|
|
109
|
+
async sendText(text, options) {
|
|
110
|
+
inspectOutgoing(text, options.topic);
|
|
111
|
+
return sourceRoom.localParticipant.sendText(text, options);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
registerTextStreamHandler(topic, handler) {
|
|
115
|
+
if (topic !== A2UI_SERVER_TOPIC) {
|
|
116
|
+
sourceRoom.registerTextStreamHandler(topic, handler);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
sourceRoom.registerTextStreamHandler(topic, (reader, participant) => {
|
|
120
|
+
let readPromise;
|
|
121
|
+
const inspectedReader = {
|
|
122
|
+
...reader.info === void 0 ? {} : { info: reader.info },
|
|
123
|
+
readAll(options) {
|
|
124
|
+
readPromise ??= reader.readAll(options).then((text) => {
|
|
125
|
+
inspectIncoming(text);
|
|
126
|
+
return text;
|
|
127
|
+
});
|
|
128
|
+
return readPromise;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
handler(inspectedReader, participant);
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
unregisterTextStreamHandler(topic) {
|
|
135
|
+
sourceRoom.unregisterTextStreamHandler(topic);
|
|
136
|
+
},
|
|
137
|
+
on(event, listener) {
|
|
138
|
+
return sourceRoom.on(event, listener);
|
|
139
|
+
},
|
|
140
|
+
off(event, listener) {
|
|
141
|
+
return sourceRoom.off(event, listener);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const sendProbe = async (scenario, envelope) => {
|
|
145
|
+
if (disposed) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
const requestId = safeString(envelope.requestId);
|
|
149
|
+
if (requestId === void 0) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
scenarioByRequest.set(requestId, scenario);
|
|
153
|
+
record({ name: "probe_sent", scenario });
|
|
154
|
+
try {
|
|
155
|
+
await sourceRoom.localParticipant.sendText(JSON.stringify(envelope), {
|
|
156
|
+
topic: scenario === "unsupported" ? A2UI_CAPABILITIES_TOPIC : A2UI_CLIENT_TOPIC
|
|
157
|
+
});
|
|
158
|
+
return true;
|
|
159
|
+
} catch {
|
|
160
|
+
scenarioByRequest.delete(requestId);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
return {
|
|
165
|
+
room,
|
|
166
|
+
getSnapshot() {
|
|
167
|
+
return {
|
|
168
|
+
events: evidence.map((event) => ({ ...event })),
|
|
169
|
+
hasCapability: evidence.some(
|
|
170
|
+
(event) => event.name === "capability_sent"
|
|
171
|
+
),
|
|
172
|
+
hasSurface: evidence.some((event) => event.name === "server_surface"),
|
|
173
|
+
hasAction: evidence.some((event) => event.name === "action_sent")
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
subscribe(listener) {
|
|
177
|
+
if (disposed) {
|
|
178
|
+
return () => void 0;
|
|
179
|
+
}
|
|
180
|
+
listeners.add(listener);
|
|
181
|
+
return () => listeners.delete(listener);
|
|
182
|
+
},
|
|
183
|
+
async replayLastAction() {
|
|
184
|
+
const envelope = decodeObject(lastActionText);
|
|
185
|
+
return envelope === null ? false : sendProbe("duplicate", envelope);
|
|
186
|
+
},
|
|
187
|
+
async sendStaleLastAction() {
|
|
188
|
+
const previous = decodeObject(lastActionText);
|
|
189
|
+
if (previous === null || previous.kind !== "action") {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
const envelope = {
|
|
193
|
+
...previous,
|
|
194
|
+
requestId: createRequestId("req")
|
|
195
|
+
};
|
|
196
|
+
return sendProbe("stale", envelope);
|
|
197
|
+
},
|
|
198
|
+
async sendUnsupportedCapability() {
|
|
199
|
+
if (lastCapability === void 0) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
const envelope = {
|
|
203
|
+
...lastCapability,
|
|
204
|
+
requestId: createRequestId("cap"),
|
|
205
|
+
supportedA2uiVersions: ["1.0.0"],
|
|
206
|
+
requestSnapshot: false
|
|
207
|
+
};
|
|
208
|
+
return sendProbe("unsupported", envelope);
|
|
209
|
+
},
|
|
210
|
+
dispose() {
|
|
211
|
+
disposed = true;
|
|
212
|
+
lastActionText = void 0;
|
|
213
|
+
lastCapability = void 0;
|
|
214
|
+
scenarioByRequest.clear();
|
|
215
|
+
evidence.splice(0);
|
|
216
|
+
listeners.clear();
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function decodeObject(text) {
|
|
221
|
+
if (typeof text !== "string") {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const value = JSON.parse(text);
|
|
226
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
227
|
+
} catch {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function safeString(value) {
|
|
232
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
233
|
+
}
|
|
234
|
+
function safeInteger(value) {
|
|
235
|
+
return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
|
|
236
|
+
}
|
|
237
|
+
function safeReason(value) {
|
|
238
|
+
return typeof value === "string" && /^[a-z0-9_]{1,64}$/.test(value) ? value : void 0;
|
|
239
|
+
}
|
|
240
|
+
function safeAction(value) {
|
|
241
|
+
return value === "flow.submit" || value === "flow.skip" || value === "flow.revise" || value === "flow.list.add" || value === "flow.list.update" || value === "flow.list.delete" ? value : void 0;
|
|
242
|
+
}
|
|
243
|
+
function createRequestId(prefix) {
|
|
244
|
+
return `${prefix}-${globalThis.crypto.randomUUID()}`;
|
|
245
|
+
}
|
|
246
|
+
export {
|
|
247
|
+
createA2UIProviderValidationProbe
|
|
248
|
+
};
|
|
249
|
+
//# sourceMappingURL=providerValidation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providerValidation.ts"],"sourcesContent":["/**\n * Provider-acceptance probe for a real A2UI room.\n *\n * This opt-in entry point keeps protocol-aware fault injection in the adapter\n * package. It never records action payloads, message bodies, participant\n * identities, tokens, URLs, or request identifiers in its public evidence.\n */\n\nimport {\n A2UI_CAPABILITIES_TOPIC,\n A2UI_CLIENT_TOPIC,\n A2UI_SERVER_TOPIC,\n} from \"./envelope.js\";\nimport type { A2UICapabilityEnvelope } from \"./envelope.js\";\nimport type { A2UIRoomLike, A2UIStreamParticipant } from \"./session.js\";\n\nconst MAX_EVIDENCE_EVENTS = 128;\n\nexport type A2UIProviderValidationScenario =\n | \"capability\"\n | \"action\"\n | \"duplicate\"\n | \"stale\"\n | \"unsupported\";\n\nexport type A2UIProviderValidationEvent =\n | {\n readonly name: \"capability_sent\";\n readonly exactEnvelopeVersion: boolean;\n readonly exactA2uiVersion: boolean;\n readonly exactCatalog: boolean;\n readonly exactRenderer: boolean;\n readonly requestsSnapshot: boolean;\n }\n | {\n readonly name: \"action_sent\";\n readonly action: string;\n readonly revision: number;\n }\n | {\n readonly name: \"server_surface\";\n readonly kind: \"snapshot\" | \"surfaceUpdate\";\n readonly seq: number;\n readonly revision: number;\n readonly correlated: boolean;\n }\n | {\n readonly name: \"server_ack\";\n readonly scenario: A2UIProviderValidationScenario;\n readonly seq: number;\n readonly status: \"accepted\" | \"rejected\";\n readonly reasonCode?: string;\n readonly correlated: boolean;\n }\n | {\n readonly name: \"probe_sent\";\n readonly scenario: \"duplicate\" | \"stale\" | \"unsupported\";\n };\n\nexport interface A2UIProviderValidationSnapshot {\n readonly events: readonly A2UIProviderValidationEvent[];\n readonly hasCapability: boolean;\n readonly hasSurface: boolean;\n readonly hasAction: boolean;\n}\n\nexport interface A2UIProviderValidationProbe {\n /** Structural room wrapper passed to A2UISessionController. */\n readonly room: A2UIRoomLike;\n getSnapshot(): A2UIProviderValidationSnapshot;\n subscribe(listener: () => void): () => void;\n replayLastAction(): Promise<boolean>;\n sendStaleLastAction(): Promise<boolean>;\n sendUnsupportedCapability(): Promise<boolean>;\n dispose(): void;\n}\n\ninterface ReaderLike {\n readonly info?: { readonly size?: number };\n readAll(options?: { signal?: AbortSignal }): Promise<string>;\n}\n\ntype StreamHandler = (\n reader: ReaderLike,\n participant: A2UIStreamParticipant,\n) => void;\n\ntype SafeEnvelope = Record<string, unknown>;\n\n/** Wrap one caller-owned room with transient, body-free provider evidence. */\nexport function createA2UIProviderValidationProbe(\n sourceRoom: A2UIRoomLike,\n): A2UIProviderValidationProbe {\n const evidence: A2UIProviderValidationEvent[] = [];\n const listeners = new Set<() => void>();\n const scenarioByRequest = new Map<\n string,\n A2UIProviderValidationScenario\n >();\n let lastActionText: string | undefined;\n let lastCapability: A2UICapabilityEnvelope | undefined;\n let disposed = false;\n\n const record = (event: A2UIProviderValidationEvent): void => {\n if (disposed) {\n return;\n }\n evidence.push(event);\n if (evidence.length > MAX_EVIDENCE_EVENTS) {\n evidence.splice(0, evidence.length - MAX_EVIDENCE_EVENTS);\n }\n for (const listener of listeners) {\n listener();\n }\n };\n\n const inspectOutgoing = (text: string, topic: string): void => {\n const envelope = decodeObject(text);\n if (envelope === null) {\n return;\n }\n const requestId = safeString(envelope.requestId);\n if (\n topic === A2UI_CAPABILITIES_TOPIC &&\n envelope.kind === \"capabilities\"\n ) {\n const capability = envelope as unknown as A2UICapabilityEnvelope;\n lastCapability = capability;\n if (requestId !== undefined) {\n scenarioByRequest.set(requestId, \"capability\");\n }\n record({\n name: \"capability_sent\",\n exactEnvelopeVersion: envelope.envelopeVersion === \"v1\",\n exactA2uiVersion:\n Array.isArray(envelope.supportedA2uiVersions) &&\n envelope.supportedA2uiVersions.length === 1 &&\n envelope.supportedA2uiVersions[0] === \"0.9.1\",\n exactCatalog:\n Array.isArray(envelope.supportedCatalogIds) &&\n envelope.supportedCatalogIds.length === 1 &&\n envelope.supportedCatalogIds[0] ===\n \"https://a2ui.org/specification/v0_9/basic_catalog.json\",\n exactRenderer:\n typeof envelope.renderer === \"object\" &&\n envelope.renderer !== null &&\n \"name\" in envelope.renderer &&\n \"version\" in envelope.renderer &&\n envelope.renderer.name === \"a2ui_react_adapter\" &&\n envelope.renderer.version === \"0.1.0\",\n requestsSnapshot: envelope.requestSnapshot === true,\n });\n return;\n }\n if (topic === A2UI_CLIENT_TOPIC && envelope.kind === \"action\") {\n const action = safeAction(envelope.action);\n const revision = safeInteger(envelope.revision);\n if (requestId === undefined || action === undefined || revision === null) {\n return;\n }\n lastActionText = text;\n scenarioByRequest.set(requestId, \"action\");\n record({ name: \"action_sent\", action, revision });\n }\n };\n\n const inspectIncoming = (text: string): void => {\n const envelope = decodeObject(text);\n if (envelope === null) {\n return;\n }\n const seq = safeInteger(envelope.seq);\n if (seq === null) {\n return;\n }\n if (envelope.kind === \"snapshot\" || envelope.kind === \"surfaceUpdate\") {\n const revision = safeInteger(envelope.revision);\n if (revision === null) {\n return;\n }\n const inResponseTo = safeString(envelope.inResponseTo);\n record({\n name: \"server_surface\",\n kind: envelope.kind,\n seq,\n revision,\n correlated:\n inResponseTo !== undefined && scenarioByRequest.has(inResponseTo),\n });\n return;\n }\n if (envelope.kind !== \"ack\") {\n return;\n }\n const inResponseTo = safeString(envelope.inResponseTo);\n const status = envelope.status;\n if (\n inResponseTo === undefined ||\n (status !== \"accepted\" && status !== \"rejected\")\n ) {\n return;\n }\n const scenario = scenarioByRequest.get(inResponseTo);\n if (scenario === undefined) {\n return;\n }\n const reasonCode = safeReason(envelope.reasonCode);\n record({\n name: \"server_ack\",\n scenario,\n seq,\n status,\n ...(reasonCode === undefined ? {} : { reasonCode }),\n correlated: true,\n });\n };\n\n const room: A2UIRoomLike = {\n localParticipant: {\n async sendText(text, options) {\n inspectOutgoing(text, options.topic);\n return sourceRoom.localParticipant.sendText(text, options);\n },\n },\n registerTextStreamHandler(topic, handler: StreamHandler) {\n if (topic !== A2UI_SERVER_TOPIC) {\n sourceRoom.registerTextStreamHandler(topic, handler);\n return;\n }\n sourceRoom.registerTextStreamHandler(topic, (reader, participant) => {\n let readPromise: Promise<string> | undefined;\n const inspectedReader: ReaderLike = {\n ...(reader.info === undefined ? {} : { info: reader.info }),\n readAll(options) {\n readPromise ??= reader.readAll(options).then((text) => {\n inspectIncoming(text);\n return text;\n });\n return readPromise;\n },\n };\n handler(inspectedReader, participant);\n });\n },\n unregisterTextStreamHandler(topic) {\n sourceRoom.unregisterTextStreamHandler(topic);\n },\n on(event, listener) {\n return sourceRoom.on(event, listener);\n },\n off(event, listener) {\n return sourceRoom.off(event, listener);\n },\n };\n\n const sendProbe = async (\n scenario: \"duplicate\" | \"stale\" | \"unsupported\",\n envelope: SafeEnvelope,\n ): Promise<boolean> => {\n if (disposed) {\n return false;\n }\n const requestId = safeString(envelope.requestId);\n if (requestId === undefined) {\n return false;\n }\n scenarioByRequest.set(requestId, scenario);\n record({ name: \"probe_sent\", scenario });\n try {\n await sourceRoom.localParticipant.sendText(JSON.stringify(envelope), {\n topic:\n scenario === \"unsupported\"\n ? A2UI_CAPABILITIES_TOPIC\n : A2UI_CLIENT_TOPIC,\n });\n return true;\n } catch {\n scenarioByRequest.delete(requestId);\n return false;\n }\n };\n\n return {\n room,\n getSnapshot() {\n return {\n events: evidence.map((event) => ({ ...event })),\n hasCapability: evidence.some(\n (event) => event.name === \"capability_sent\",\n ),\n hasSurface: evidence.some((event) => event.name === \"server_surface\"),\n hasAction: evidence.some((event) => event.name === \"action_sent\"),\n };\n },\n subscribe(listener) {\n if (disposed) {\n return () => undefined;\n }\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n async replayLastAction() {\n const envelope = decodeObject(lastActionText);\n return envelope === null\n ? false\n : sendProbe(\"duplicate\", envelope);\n },\n async sendStaleLastAction() {\n const previous = decodeObject(lastActionText);\n if (previous === null || previous.kind !== \"action\") {\n return false;\n }\n const envelope: SafeEnvelope = {\n ...previous,\n requestId: createRequestId(\"req\"),\n };\n return sendProbe(\"stale\", envelope);\n },\n async sendUnsupportedCapability() {\n if (lastCapability === undefined) {\n return false;\n }\n const envelope: SafeEnvelope = {\n ...lastCapability,\n requestId: createRequestId(\"cap\"),\n supportedA2uiVersions: [\"1.0.0\"],\n requestSnapshot: false,\n };\n return sendProbe(\"unsupported\", envelope);\n },\n dispose() {\n disposed = true;\n lastActionText = undefined;\n lastCapability = undefined;\n scenarioByRequest.clear();\n evidence.splice(0);\n listeners.clear();\n },\n };\n}\n\nfunction decodeObject(text: string | undefined): SafeEnvelope | null {\n if (typeof text !== \"string\") {\n return null;\n }\n try {\n const value: unknown = JSON.parse(text);\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as SafeEnvelope)\n : null;\n } catch {\n return null;\n }\n}\n\nfunction safeString(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction safeInteger(value: unknown): number | null {\n return typeof value === \"number\" && Number.isSafeInteger(value)\n ? value\n : null;\n}\n\nfunction safeReason(value: unknown): string | undefined {\n return typeof value === \"string\" && /^[a-z0-9_]{1,64}$/.test(value)\n ? value\n : undefined;\n}\n\nfunction safeAction(value: unknown): string | undefined {\n return value === \"flow.submit\" ||\n value === \"flow.skip\" ||\n value === \"flow.revise\" ||\n value === \"flow.list.add\" ||\n value === \"flow.list.update\" ||\n value === \"flow.list.delete\"\n ? value\n : undefined;\n}\n\nfunction createRequestId(prefix: \"cap\" | \"req\"): string {\n return `${prefix}-${globalThis.crypto.randomUUID()}`;\n}\n"],"mappings":"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","names":["inResponseTo"]}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MedAid A2UI LiveKit envelope contract v1 — client-side constants and types.
|
|
3
|
+
*
|
|
4
|
+
* These declarations mirror the canonical machine-readable contract owned by
|
|
5
|
+
* `livekitFlowfactory/contracts/a2ui-livekit-*-envelope.v1.schema.json`
|
|
6
|
+
* (human-readable source: MediAid `docs/aide/contracts/a2ui-livekit-envelope-contract.md`).
|
|
7
|
+
* They are a declared copy: Stage 17 acceptance verifies drift against the
|
|
8
|
+
* canonical fixtures. The envelope contract version ("v1", the `.v1` topic
|
|
9
|
+
* suffix) is not the A2UI protocol version; `a2uiVersion` is negotiated
|
|
10
|
+
* separately.
|
|
11
|
+
*/
|
|
12
|
+
declare const ENVELOPE_VERSION: "v1";
|
|
13
|
+
declare const A2UI_PROTOCOL_VERSION: "0.9.1";
|
|
14
|
+
declare const A2UI_SERVER_TOPIC = "medaid.a2ui.server.v1";
|
|
15
|
+
declare const A2UI_CLIENT_TOPIC = "medaid.a2ui.client.v1";
|
|
16
|
+
declare const A2UI_CAPABILITIES_TOPIC = "medaid.a2ui.capabilities.v1";
|
|
17
|
+
declare const MAX_ENVELOPE_BYTES = 262144;
|
|
18
|
+
declare const CLIENT_ACTIONS: readonly ["flow.submit", "flow.skip", "flow.revise", "flow.list.add", "flow.list.update", "flow.list.delete"];
|
|
19
|
+
type ClientAction = (typeof CLIENT_ACTIONS)[number];
|
|
20
|
+
declare const CLIENT_ERROR_CODES: readonly ["render_failed", "catalog_unsupported", "message_malformed", "envelope_unsupported"];
|
|
21
|
+
type ClientErrorCode = (typeof CLIENT_ERROR_CODES)[number];
|
|
22
|
+
type ParticipantRole = "member" | "agent";
|
|
23
|
+
interface SectionNavigation {
|
|
24
|
+
orderedSectionIds: string[];
|
|
25
|
+
activeSectionId: string | null;
|
|
26
|
+
completedSectionIds: string[];
|
|
27
|
+
futureSectionIds: string[];
|
|
28
|
+
}
|
|
29
|
+
interface A2UIFieldInteractionField {
|
|
30
|
+
field: string;
|
|
31
|
+
componentId: string;
|
|
32
|
+
}
|
|
33
|
+
interface A2UIFieldInteractionGroup {
|
|
34
|
+
groupId: string;
|
|
35
|
+
ownerStateId: string;
|
|
36
|
+
sectionId?: string;
|
|
37
|
+
mode: "current" | "revision";
|
|
38
|
+
fields: A2UIFieldInteractionField[];
|
|
39
|
+
}
|
|
40
|
+
interface ServerEnvelopeBase {
|
|
41
|
+
envelopeVersion: typeof ENVELOPE_VERSION;
|
|
42
|
+
a2uiVersion: string;
|
|
43
|
+
seq: number;
|
|
44
|
+
}
|
|
45
|
+
interface SurfaceUpdateEnvelope extends ServerEnvelopeBase {
|
|
46
|
+
kind: "surfaceUpdate";
|
|
47
|
+
surfaceId: string;
|
|
48
|
+
revision: number;
|
|
49
|
+
activeStateId?: string | null;
|
|
50
|
+
sectionNavigation?: SectionNavigation;
|
|
51
|
+
fieldInteractions?: A2UIFieldInteractionGroup[];
|
|
52
|
+
messages: unknown[];
|
|
53
|
+
}
|
|
54
|
+
interface SnapshotEnvelope extends ServerEnvelopeBase {
|
|
55
|
+
kind: "snapshot";
|
|
56
|
+
surfaceId: string;
|
|
57
|
+
revision: number;
|
|
58
|
+
activeStateId?: string | null;
|
|
59
|
+
sectionNavigation?: SectionNavigation;
|
|
60
|
+
fieldInteractions?: A2UIFieldInteractionGroup[];
|
|
61
|
+
inResponseTo?: string;
|
|
62
|
+
messages: unknown[];
|
|
63
|
+
}
|
|
64
|
+
interface AckFieldError {
|
|
65
|
+
field: string;
|
|
66
|
+
reasonCode: string;
|
|
67
|
+
}
|
|
68
|
+
interface AckEnvelope extends ServerEnvelopeBase {
|
|
69
|
+
kind: "ack";
|
|
70
|
+
inResponseTo: string;
|
|
71
|
+
status: "accepted" | "rejected";
|
|
72
|
+
reasonCode?: string;
|
|
73
|
+
fieldErrors?: AckFieldError[];
|
|
74
|
+
noSurface?: boolean;
|
|
75
|
+
}
|
|
76
|
+
type A2UIServerEnvelope = SurfaceUpdateEnvelope | SnapshotEnvelope | AckEnvelope;
|
|
77
|
+
interface A2UIClientActionEnvelope {
|
|
78
|
+
envelopeVersion: typeof ENVELOPE_VERSION;
|
|
79
|
+
a2uiVersion: string;
|
|
80
|
+
kind: "action";
|
|
81
|
+
requestId: string;
|
|
82
|
+
role: ParticipantRole;
|
|
83
|
+
surfaceId: string;
|
|
84
|
+
stateId: string;
|
|
85
|
+
revision: number;
|
|
86
|
+
action: ClientAction;
|
|
87
|
+
payload?: {
|
|
88
|
+
fields?: Record<string, string | number | boolean | null>;
|
|
89
|
+
targetStateId?: string;
|
|
90
|
+
listPath?: string;
|
|
91
|
+
itemId?: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
interface A2UIClientErrorEnvelope {
|
|
95
|
+
envelopeVersion: typeof ENVELOPE_VERSION;
|
|
96
|
+
a2uiVersion: string;
|
|
97
|
+
kind: "clientError";
|
|
98
|
+
requestId: string;
|
|
99
|
+
role: ParticipantRole;
|
|
100
|
+
errorCode: ClientErrorCode;
|
|
101
|
+
surfaceId?: string;
|
|
102
|
+
detail?: string;
|
|
103
|
+
}
|
|
104
|
+
interface A2UICapabilityEnvelope {
|
|
105
|
+
envelopeVersion: typeof ENVELOPE_VERSION;
|
|
106
|
+
kind: "capabilities";
|
|
107
|
+
requestId: string;
|
|
108
|
+
role: ParticipantRole;
|
|
109
|
+
supportedA2uiVersions: string[];
|
|
110
|
+
supportedCatalogIds: string[];
|
|
111
|
+
renderer: {
|
|
112
|
+
name: string;
|
|
113
|
+
version: string;
|
|
114
|
+
};
|
|
115
|
+
requestSnapshot?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* A2UIClientAdapter: the only place protocol messages are applied client-side.
|
|
120
|
+
*
|
|
121
|
+
* Wraps the official A2UI v0.9.1 message processor with the trusted Basic
|
|
122
|
+
* Catalog. Hosts never see protocol SDK types directly; they receive the
|
|
123
|
+
* adapter's surface handles and safe error events using the locked
|
|
124
|
+
* client-error codes. Unsupported catalogs or malformed messages produce an
|
|
125
|
+
* error callback and leave prior committed surfaces untouched — there is no
|
|
126
|
+
* partial render path and no dynamic catalog loading.
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
/** Opaque renderer-owned handle. Hosts may identify it, but never inspect SDK state. */
|
|
130
|
+
interface AdapterSurface {
|
|
131
|
+
readonly id: string;
|
|
132
|
+
}
|
|
133
|
+
interface A2UIClientErrorEvent {
|
|
134
|
+
errorCode: ClientErrorCode;
|
|
135
|
+
surfaceId?: string;
|
|
136
|
+
detail?: string;
|
|
137
|
+
}
|
|
138
|
+
/** Protocol-neutral action emitted by the trusted official renderer. */
|
|
139
|
+
interface A2UIRendererActionEvent {
|
|
140
|
+
actionName: string;
|
|
141
|
+
surfaceId: string;
|
|
142
|
+
sourceComponentId: string;
|
|
143
|
+
context: Readonly<Record<string, unknown>>;
|
|
144
|
+
}
|
|
145
|
+
type A2UIFieldControl = "text" | "date" | "choice";
|
|
146
|
+
type A2UIFieldValue = string | number | boolean | null;
|
|
147
|
+
/** Safe structural metadata used only for labels, validation, and focus. */
|
|
148
|
+
interface A2UIFieldDescriptor {
|
|
149
|
+
readonly componentId: string;
|
|
150
|
+
readonly field: string;
|
|
151
|
+
readonly label: string;
|
|
152
|
+
readonly control: A2UIFieldControl;
|
|
153
|
+
readonly required: boolean;
|
|
154
|
+
}
|
|
155
|
+
/** Safe authored tab title/child binding; section status remains envelope-owned. */
|
|
156
|
+
interface A2UITabDescriptor {
|
|
157
|
+
readonly componentId: string;
|
|
158
|
+
readonly title: string;
|
|
159
|
+
readonly childId: string;
|
|
160
|
+
}
|
|
161
|
+
interface A2UISurfaceStructure {
|
|
162
|
+
readonly fields: readonly A2UIFieldDescriptor[];
|
|
163
|
+
readonly tabs: readonly A2UITabDescriptor[];
|
|
164
|
+
}
|
|
165
|
+
interface A2UIClientAdapterOptions {
|
|
166
|
+
/** Safe client-error events (locked codes); wired to LiveKit in later items. */
|
|
167
|
+
onClientError?: (event: A2UIClientErrorEvent) => void;
|
|
168
|
+
/** Trusted catalog actions normalized without exposing official A2UI types. */
|
|
169
|
+
onAction?: (action: A2UIRendererActionEvent) => void;
|
|
170
|
+
/** Application acknowledgements; consumed by Queue-Item 024-005. */
|
|
171
|
+
onAck?: (envelope: Extract<A2UIServerEnvelope, {
|
|
172
|
+
kind: "ack";
|
|
173
|
+
}>) => void;
|
|
174
|
+
}
|
|
175
|
+
declare class A2UIClientAdapter {
|
|
176
|
+
#private;
|
|
177
|
+
readonly rendererName = "a2ui_react_adapter";
|
|
178
|
+
readonly rendererVersion = "0.1.0";
|
|
179
|
+
constructor(options?: A2UIClientAdapterOptions);
|
|
180
|
+
/** Catalog ids this adapter will announce in capability envelopes. */
|
|
181
|
+
get supportedCatalogIds(): string[];
|
|
182
|
+
get lastSeq(): number;
|
|
183
|
+
/** The most recently created live surface, if any. */
|
|
184
|
+
get currentSurface(): AdapterSurface | undefined;
|
|
185
|
+
getSurface(id: string): AdapterSurface | undefined;
|
|
186
|
+
/** Immutable value-free structure for host accessibility augmentation. */
|
|
187
|
+
getSurfaceStructure(id: string): A2UISurfaceStructure;
|
|
188
|
+
/** Read a complete scalar field set without exposing the SDK data model. */
|
|
189
|
+
getFieldValues(surfaceId: string, fields: readonly string[]): Record<string, A2UIFieldValue> | null;
|
|
190
|
+
/** Restore a private draft after a trusted processor replacement. */
|
|
191
|
+
applyFieldValues(surfaceId: string, values: Readonly<Record<string, A2UIFieldValue>>): boolean;
|
|
192
|
+
/**
|
|
193
|
+
* Apply one validated server envelope. Returns false for duplicates and
|
|
194
|
+
* out-of-order deliveries (the per-session `seq` guard) and for envelopes
|
|
195
|
+
* that fail to apply; committed surfaces are never partially replaced.
|
|
196
|
+
*/
|
|
197
|
+
applyServerEnvelope(envelope: A2UIServerEnvelope): boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Reset the ordering guard; used when a reconnect snapshot restarts the
|
|
200
|
+
* server sequence (Queue-Item 024-004).
|
|
201
|
+
*/
|
|
202
|
+
resetSequence(): void;
|
|
203
|
+
/** Clear every generated surface without disposing the reusable adapter. */
|
|
204
|
+
clearSurfaces(): void;
|
|
205
|
+
/** Emit one of the locked, body-free client error events. */
|
|
206
|
+
reportClientError(errorCode: ClientErrorCode, surfaceId?: string, detail?: string): void;
|
|
207
|
+
/** Report a rendering failure (used by the surface host error boundary). */
|
|
208
|
+
reportRenderFailure(surfaceId: string | undefined, detail: string): void;
|
|
209
|
+
subscribe: (listener: () => void) => (() => void);
|
|
210
|
+
subscribeActions: (listener: (event: A2UIRendererActionEvent) => void) => (() => void);
|
|
211
|
+
subscribeClientErrors: (listener: (event: A2UIClientErrorEvent) => void) => (() => void);
|
|
212
|
+
/** Monotonic change counter for `useSyncExternalStore`. */
|
|
213
|
+
getVersion: () => number;
|
|
214
|
+
dispose(): void;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* MedAid v0.9.1 action/correlation bridge.
|
|
219
|
+
*
|
|
220
|
+
* The official renderer emits protocol actions; this bridge binds them to the
|
|
221
|
+
* authoritative session metadata and the MedAid v1 envelope. It deliberately
|
|
222
|
+
* separates LiveKit delivery from application acknowledgement so a successful
|
|
223
|
+
* `sendText` call can never be mistaken for a workflow transition.
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
type A2UIActionDeliveryStatus = "publishing" | "awaiting_ack" | "retryable";
|
|
227
|
+
interface A2UIPendingAction {
|
|
228
|
+
readonly requestId: string;
|
|
229
|
+
readonly action: ClientAction;
|
|
230
|
+
readonly surfaceId: string;
|
|
231
|
+
readonly stateId: string;
|
|
232
|
+
readonly revision: number;
|
|
233
|
+
readonly deliveryStatus: A2UIActionDeliveryStatus;
|
|
234
|
+
readonly attemptCount: number;
|
|
235
|
+
readonly groupId?: string;
|
|
236
|
+
}
|
|
237
|
+
interface A2UIActionResult {
|
|
238
|
+
readonly requestId: string;
|
|
239
|
+
readonly action: ClientAction;
|
|
240
|
+
readonly surfaceId: string;
|
|
241
|
+
readonly stateId: string;
|
|
242
|
+
readonly revision: number;
|
|
243
|
+
readonly status: "accepted" | "rejected";
|
|
244
|
+
readonly reasonCode?: string;
|
|
245
|
+
readonly fieldErrors: readonly AckFieldError[];
|
|
246
|
+
readonly groupId?: string;
|
|
247
|
+
}
|
|
248
|
+
interface A2UIActionState {
|
|
249
|
+
readonly pendingRequests: readonly A2UIPendingAction[];
|
|
250
|
+
readonly lastResult: A2UIActionResult | null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Protocol-neutral A2UI session lifecycle over a structural LiveKit room.
|
|
255
|
+
*
|
|
256
|
+
* LiveKit SDK imports stay out of this module and its declarations. Hosts
|
|
257
|
+
* retain ownership of shared rooms and must explicitly identify which remote
|
|
258
|
+
* participant is trusted to publish Flow Factory server envelopes.
|
|
259
|
+
*/
|
|
260
|
+
|
|
261
|
+
interface A2UITextStreamReaderLike {
|
|
262
|
+
readonly info?: {
|
|
263
|
+
readonly size?: number;
|
|
264
|
+
};
|
|
265
|
+
readAll(options?: {
|
|
266
|
+
signal?: AbortSignal;
|
|
267
|
+
}): Promise<string>;
|
|
268
|
+
}
|
|
269
|
+
interface A2UIStreamParticipant {
|
|
270
|
+
readonly identity: string;
|
|
271
|
+
}
|
|
272
|
+
type A2UITextStreamHandler = (reader: A2UITextStreamReaderLike, participant: A2UIStreamParticipant) => void;
|
|
273
|
+
type A2UIRoomEvent = "reconnected" | "disconnected";
|
|
274
|
+
/** Minimal room contract used by the adapter; no LiveKit SDK type is exposed. */
|
|
275
|
+
interface A2UIRoomLike {
|
|
276
|
+
readonly localParticipant: {
|
|
277
|
+
sendText(text: string, options: {
|
|
278
|
+
topic: string;
|
|
279
|
+
}): Promise<unknown>;
|
|
280
|
+
};
|
|
281
|
+
registerTextStreamHandler(topic: string, handler: A2UITextStreamHandler): void;
|
|
282
|
+
unregisterTextStreamHandler(topic: string): void;
|
|
283
|
+
on(event: A2UIRoomEvent, listener: () => void): unknown;
|
|
284
|
+
off(event: A2UIRoomEvent, listener: () => void): unknown;
|
|
285
|
+
}
|
|
286
|
+
type A2UITrustedServerParticipant = (participant: A2UIStreamParticipant) => boolean;
|
|
287
|
+
type A2UISessionStatus = "idle" | "connecting" | "connected" | "unsupported" | "closed";
|
|
288
|
+
interface A2UISurfaceMetadata {
|
|
289
|
+
readonly surfaceId: string;
|
|
290
|
+
readonly activeStateId: string | null;
|
|
291
|
+
readonly revision: number;
|
|
292
|
+
readonly sectionNavigation: SectionNavigation | null;
|
|
293
|
+
readonly fieldInteractions: readonly A2UIFieldInteractionGroup[];
|
|
294
|
+
}
|
|
295
|
+
/** Protocol-neutral local edit/navigation state exposed to host applications. */
|
|
296
|
+
interface A2UIEditingSnapshot {
|
|
297
|
+
readonly authoritativeSectionId: string | null;
|
|
298
|
+
readonly viewedSectionId: string | null;
|
|
299
|
+
readonly dirtyGroupIds: readonly string[];
|
|
300
|
+
readonly pendingGroupIds: readonly string[];
|
|
301
|
+
readonly rejectedGroupIds: readonly string[];
|
|
302
|
+
}
|
|
303
|
+
interface A2UISessionSnapshot {
|
|
304
|
+
readonly status: A2UISessionStatus;
|
|
305
|
+
readonly restoring: boolean;
|
|
306
|
+
readonly surface: A2UISurfaceMetadata | null;
|
|
307
|
+
readonly actions: A2UIActionState;
|
|
308
|
+
readonly editing: A2UIEditingSnapshot;
|
|
309
|
+
}
|
|
310
|
+
interface A2UISessionControllerOptions {
|
|
311
|
+
room: A2UIRoomLike;
|
|
312
|
+
adapter: A2UIClientAdapter;
|
|
313
|
+
isTrustedServerParticipant: A2UITrustedServerParticipant;
|
|
314
|
+
participantRole?: ParticipantRole;
|
|
315
|
+
/** Time before a delivered action becomes explicitly retryable. */
|
|
316
|
+
actionAckTimeoutMs?: number;
|
|
317
|
+
/** Best-effort capability retry schedule for servers that attach after join. */
|
|
318
|
+
capabilityRetryScheduleMs?: readonly number[];
|
|
319
|
+
}
|
|
320
|
+
/** Attach one A2UI adapter to an existing, caller-owned room. */
|
|
321
|
+
declare class A2UISessionController {
|
|
322
|
+
#private;
|
|
323
|
+
constructor(options: A2UISessionControllerOptions);
|
|
324
|
+
get status(): A2UISessionStatus;
|
|
325
|
+
get restoring(): boolean;
|
|
326
|
+
get surfaceMetadata(): A2UISurfaceMetadata | null;
|
|
327
|
+
get actionState(): A2UIActionState;
|
|
328
|
+
/** Retry one or every delivery-failed/timed-out request with its original id. */
|
|
329
|
+
retryPendingAction(requestId?: string): boolean;
|
|
330
|
+
/** Select the authoritative server-owned section without choosing workflow state. */
|
|
331
|
+
returnToCurrentSection(): boolean;
|
|
332
|
+
/** View one exact server-issued active/completed section locally. */
|
|
333
|
+
viewSection(sectionId: string): boolean;
|
|
334
|
+
/** Capture a renderer-owned group after a real user input/change event. */
|
|
335
|
+
captureFieldGroupDraft(groupId: string): boolean;
|
|
336
|
+
/** Commit one complete owning group against the latest surface revision. */
|
|
337
|
+
commitFieldGroup(groupId: string): boolean;
|
|
338
|
+
getSnapshot: () => A2UISessionSnapshot;
|
|
339
|
+
subscribe: (listener: () => void) => (() => void);
|
|
340
|
+
/** Register the server stream before announcing exact renderer capability. */
|
|
341
|
+
start(): Promise<void>;
|
|
342
|
+
/**
|
|
343
|
+
* Detach only A2UI handlers/listeners. The shared room remains caller-owned.
|
|
344
|
+
*/
|
|
345
|
+
stop(): void;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export { A2UIClientAdapter as A, type A2UITrustedServerParticipant as B, A2UI_CAPABILITIES_TOPIC as C, A2UI_CLIENT_TOPIC as D, A2UI_PROTOCOL_VERSION as E, A2UI_SERVER_TOPIC as F, type AckEnvelope as G, type AckFieldError as H, type AdapterSurface as I, CLIENT_ACTIONS as J, CLIENT_ERROR_CODES as K, type ClientAction as L, type ClientErrorCode as M, ENVELOPE_VERSION as N, MAX_ENVELOPE_BYTES as O, type ParticipantRole as P, type SnapshotEnvelope as Q, type SurfaceUpdateEnvelope as R, type SectionNavigation as S, type A2UISurfaceMetadata as a, A2UISessionController as b, type A2UIRoomLike as c, type A2UIActionDeliveryStatus as d, type A2UIActionResult as e, type A2UIActionState as f, type A2UICapabilityEnvelope as g, type A2UIClientActionEnvelope as h, type A2UIClientAdapterOptions as i, type A2UIClientErrorEnvelope as j, type A2UIClientErrorEvent as k, type A2UIEditingSnapshot as l, type A2UIFieldControl as m, type A2UIFieldDescriptor as n, type A2UIFieldInteractionField as o, type A2UIFieldInteractionGroup as p, type A2UIFieldValue as q, type A2UIPendingAction as r, type A2UIRendererActionEvent as s, type A2UIServerEnvelope as t, type A2UISessionControllerOptions as u, type A2UISessionSnapshot as v, type A2UISessionStatus as w, type A2UIStreamParticipant as x, type A2UISurfaceStructure as y, type A2UITabDescriptor as z };
|