Agent conversations and actions
An assistant interface has two kinds of state: saved conversation messages and the current streamed response. Use the manager to keep those together, and render browser actions separately from backend tool results.
Start a conversation
Choose an implemented agent by its registered resource name. Preview mode selects its committed definition and committed API tool versions; deployed execution uses deployed dependencies. Ensure its declared tables have published data and its APIs have ready runtimes.
Conversations and streaming
agentManager.loadConversations(agentName, page?) -> Promise<void>
agentManager.selectConversation(id) -> Promise<void>
agentManager.loadMoreMessages() -> Promise<void>
agentManager.newConversation({name?, visibility?}?) -> void
agentManager.updateConversation({name?, visibility?}) -> Promise<void>
agentManager.consume(client.runAgent(agentName, options)) -> Promise<void>
agentManager.cancel() -> voidrunAgent options: required message; optional conversationId, creation-only name/ visibility, data, actions, cancelSignal, timeoutMs. runAgent returns a lazy, single-use AgentRun; no POST occurs until it is consumed. The manager supplies the selected conversation when appropriate. newConversation clears the selection and prepares a draft created atomically with its first message. Without a selection or explicit ID, a run creates a new conversation. Use updateConversation to rename or change visibility of an existing selection; run options do not edit it.
import { useVortexify } from "@vortexify/react";
export function AssistantPanel() {
const { client, ready, agentManager } = useVortexify();
async function send() {
if (!ready || agentManager.isRunning) return;
try {
await agentManager.consume(client.runAgent("order_assistant", {
message: "Explain the current order workflow",
data: { screen: "orders" },
}));
} catch {
// The manager also records the failure in error for rendering below.
}
}
return <section>
<button disabled={!ready || agentManager.isRunning} onClick={() => void send()}>Ask</button>
<p>{agentManager.text}</p>
{agentManager.error && <p role="alert">{agentManager.error.message}</p>}
</section>;
}For conversation navigation, call agentManager.loadConversations("order_assistant") once the client is ready. Render that list, use selectConversation(id) when someone opens a conversation, and use newConversation({visibility: "private"}) for a fresh draft. Show isLoading while history is loading and offer loadMoreMessages() when nextMessageOffset is not null.
This minimal panel renders current stream text. A full conversation UI should also
render persisted messages and conversation selection without duplicating the final
turn: the manager refreshes history after successful completion. State includes
agentName, conversations, activeConversation, messages, conversationId, messageId,
runId, text, tools, isRunning, isLoading, error, nextConversationOffset and
nextMessageOffset. A null next offset means no next page. Conversation/message
pages default to 50 and allow up to 100. Load later conversations with
{offset: agentManager.nextConversationOffset} when it is not null.
Visibility is private or public; default public means the authorized project audience, not anonymous internet access. Server permission checks govern reads, creation, submission and management. Switching conversations or unmounting cancels local consumption; it does not stop an active backend turn or reconnect to it. Only one active backend turn is allowed per conversation. Stream failures reject consume and populate error; user cancellation resolves normally. Streams are not automatically replayed/retried. Default stream timeout is configured timeout or five minutes. Explicit cancelSignal is supported.
Low-level client conversation APIs are listAgentConversations(name, page?, options?), getAgentConversation(name, id, options?), listAgentMessages(name, id, page?, options?), createAgentConversation(name, {name?, visibility?}?, options?) and updateAgentConversation(name, id, {name?, visibility?}, options?). Prefer the manager for ordinary React UI. Advanced direct AgentRun iteration yields events without executing frontend actions or managing React state.
Inspect conversations in the platform
The agent resource's Conversations view lists saved conversations and opens their messages and tool activity. Files shows its attached document previews, and Code shows the Python declaration. These are useful for checking what happened in a real app conversation; the app itself owns its chat composer and browser interactions.
Data and frontend actions
Agent data is a JSON object and may contain nested arrays/objects, unlike scalar API inputs. Filter before sending and bound every data array to at most 100 items. Message/data/action definitions together have a 256 KiB UTF-8 request limit and JSON depth is limited to 32. Include sample metadata such as totalRows; oversize requests reject rather than silently truncate. Never send secrets unnecessarily.
For example, use a backend API tool to retrieve a customer, and a browser action to open that customer's screen. Give the action a clear description so the assistant can choose it, and a schema so the manager can validate its arguments.
An action contains description, parameters and a local execute handler:
import type { FrontendActions } from "@vortexify/react";
const actions: FrontendActions = {
openCustomer: {
description: "Open a customer in this application.",
parameters: {
type: "object",
properties: { customerId: { type: "string" } },
required: ["customerId"],
additionalProperties: false,
},
execute: ({ customerId }) => {
if (typeof customerId !== "string") throw new Error("Invalid customer ID");
return openCustomerPage(customerId).then(() => ({
success: true as const, message: "Customer page opened.",
})); // App helper: resolve when destination view is ready.
},
},
};Pass actions in runAgent options. Names/descriptions/schemas reach the backend; execute functions remain in the browser. Supported schema keys: type, description, properties, required, boolean additionalProperties, items, enum. Types are object, array, string, number, integer, boolean, null; the root must be object. Unsupported keywords reject. Names match [A-Za-z0-9_-]{1,64} and cannot collide with backend tools.
The manager validates completed arguments and executes each frontend call once per consumed run. It submits AgentToolResult to the backend, which waits and continues with the actual result. History reads and read-only stream replays do not dispatch actions. Canceling cannot undo side effects already started.
See custom agent declarations, analysis charts, and add an assistant.
Frontend action results
Every action execute handler must return an AgentToolResult, synchronously or
through a promise:
type AgentToolResult =
| { success: true; message?: string; data?: JsonValue }
| { success: false; message: string; data?: JsonValue };The manager automatically submits the result to the backend. Thrown/rejected
errors, invalid arguments, missing handlers and invalid return values become
{ success: false, message }. Returning void or a boolean is invalid. JSON results
have a 256 KiB limit and maximum depth 32. Success means the operation described
by the handler completed: drafting a note is different from saving it.
Each action can set timeoutMs (integer 1000–120000, default 60000). The server
starts its deadline before dispatching completed arguments, persists pending state,
and polls every 0.5 seconds. The agent waits for a response before continuing with
the actual result. A timeout reports that the execution outcome is unknown.
The total run timeout is separate (five minutes by default).
The manager keeps reading SSE while executing browser handlers in dispatch order.
Canonical frontend results, including timeouts, arrive as tool_call_result
events and update tools[].result, status and error. History reads and read-only
replays do not execute handlers. Cancel/unmount stops local dispatch and response
submission; it cannot undo already-started side effects. Browser disconnects leave
the backend waiting until the tool deadline; explicit server stop/reset cancels
pending calls. Transport failures while submitting results reject consume.
For direct AgentRun iteration or a manual approval UI, use:
await client.respondToToolCall(
agentName, conversationId, runId, callId,
{ success: true, message: "Approved", data: { approved: true } },
);
// An initialized AgentRun also exposes run.respondToToolCall(callId, result).Only the user who started the run may answer its calls, with project editor access.
Identical accepted retries are idempotent. Conflicting, canceled or expired replies
return HTTP 409. The manager follows the canonical streamed result if a timeout
wins a response race. Messages expose nullable interactiveRunId,
interactiveCallId, responseStatus, responseDeadline, response, and
respondedAt; status is pending/responded/timed_out/canceled. Older messages keep
null metadata.
Active page context
Register one active page with useAgentView inside VortexifyProvider:
import { useAgentView } from "@vortexify/react";
useAgentView({
id: "orders",
description: "Orders list with shipping filters and selection.",
getContext: () => ({
shippingFilter,
selectedOrderId,
visibleOrders: orders.slice(0, 50),
totalOrders: orders.length,
}),
});The provider registers on mount, reads the latest callback after renders, and unregisters on unmount. Keep one active view per provider; the last mounted view wins, and an old registration's cleanup cannot remove a newer registration. Changing the view ID replaces its registration. This does not continuously send UI changes to the backend.
At consumption time, runAgent adds a snapshot under the reserved
data.current_view key (viewId, description, context; null without an active
view). Explicit data fields are retained except that reserved key. Initial
context uses the request's existing limits: 100 items per array, depth 32, and
256 KiB including message and action definitions. getContext can return a
JsonObject or a promise; errors reject initial run preparation.
Provider clients also register the reserved get_current_view_context interactive
tool. It reads the active registration when called, so an existing run can follow
navigation and request fresh context. No active view or navigation during an
asynchronous context read returns a structured failure. Fresh tool results are
JSON, depth at most 32 and at most 256 KiB. Expose only the context the agent needs.
Actions declared in a runAgent invocation remain fixed for that run; changing
pages does not add new action definitions mid-run. A navigation action should
resolve after its destination view is ready if later tools depend on that view.
Use SPA navigation: a full document reload can disconnect the stream before the
SDK submits the result.