Skip to main content

Custom Agent UI

The web widget accepts uiRenderers?: Record<string, ToolRenderer> in its initialization options. Register each renderer under the tool name copied from the dashboard, not the display label. The same option works with Standard, Bubble, and Popup widgets.

type ToolUIInteractionState = 'waiting' | 'ready' | 'submitting' | 'resolved';

type ToolRendererHandle = {
cleanup?: () => void;
getValues?: () => unknown;
validate?: () => boolean | Promise<boolean>;
onInteractionStateChange?: (state: ToolUIInteractionState) => void;
};

type ToolRenderer = (
container: HTMLDivElement,
ui: unknown,
context: {
initialValues?: unknown;
requestSubmit?: () => Promise<void>;
requestCancel?: () => Promise<void>;
}
) => void | (() => void) | ToolRendererHandle;

Renderer Arguments​

ArgumentDescription
containerWidget-owned element into which the renderer mounts its UI.
uiTool output for a completed output tool, or tool arguments (which may be empty) for pending input. The renderer does not receive the tool's lifecycle source.
context.initialValuesPreviously submitted values, when available for restored input UI.
context.requestSubmit()Requests submission of pending input. Available only for input UI.
context.requestCancel()Cancels pending input. Available only for input UI.

Return Value​

Return nothing, a cleanup function, or a ToolRendererHandle. A returned function is equivalent to { cleanup: function }.

Handle memberDescription
cleanupReleases DOM, framework roots, and listeners when this rendering instance is removed or replaced.
getValuesSupplies the visitor's answer for a pending input tool. Required for input submission.
validateOptionally blocks submission by returning false, synchronously or asynchronously.
onInteractionStateChangeNotifies custom controls of waiting, ready, submitting, or resolved. Enable submission only in ready.

requestSubmit()​

context.requestSubmit?: () => Promise<void> is available only while using the "Request user input" tool. Call it from your form's submit handler or another explicit visitor action to submit the data to the LLM.

On submission, the widget awaits optional validate(). If it returns false, nothing is sent. Otherwise the widget reads getValues() and submits those values.

requestCancel()​

context.requestCancel?: () => Promise<void> is available only while using the "Request user input" tool. Call it from your Cancel control. It skips calling the validate() and getValues() and cancels the request for input.

Both methods return Promise<void>.

getValues()​

Return getValues: () => unknown so the widget can read the visitor's answer. It is required for submission, called only after validation succeeds, and is not called on Cancel. Return the values in the shape your tool expects, such as { email: 'ada@example.com' }.

validate()​

Return validate: () => boolean | Promise<boolean> when the answer needs checking before submission. The widget calls it only for requestSubmit(); false prevents submission without reading getValues().

For examples and lifecycle guidance, see Render Tool UI in the Conversation.