/**
* Sandbox Example — Client
*
* Split layout: chat on the left, execution log + customer data on the right.
* The agent writes code that runs in a sandboxed isolate — the execution log
* shows the code and its captured console output.
*/
import { Suspense, useCallback, useState, useEffect, useRef } from "react";
import { useAgent } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
import { isToolUIPart, getToolName } from "ai";
import type { UIMessage } from "ai";
import {
Button,
Badge,
InputArea,
Empty,
Surface,
Text
} from "@cloudflare/kumo";
import {
ConnectionIndicator,
ModeToggle,
PoweredByAgents,
type ConnectionStatus
} from "@cloudflare/agents-ui";
import {
PaperPlaneRightIcon,
TrashIcon,
GearIcon,
CodeIcon,
DatabaseIcon,
TerminalIcon,
PlayIcon
} from "@phosphor-icons/react";
import { Streamdown } from "streamdown";
import type { SandboxState, ExecutionRecord, CustomerRecord } from "./server";
function getMessageText(message: UIMessage): string {
return message.parts
.filter((part) => part.type === "text")
.map((part) => (part as { type: "text"; text: string }).text)
.join("");
}
// ─── Execution Log ─────────────────────────────────────────────────────────
function ExecutionLog({ executions }: { executions: ExecutionRecord[] }) {
if (executions.length === 0) {
return (
}
title="No executions yet"
description='Ask the agent to write code — e.g. "List all Gold tier customers"'
/>
);
}
return (
{executions.map((exec) => (
Code
{exec.error ? (
Error
) : (
OK
)}
{exec.timestamp}
{/* Code */}
{exec.code.length > 500
? exec.code.slice(0, 500) + "\n// ... truncated"
: exec.code}
{/* Output */}
Output
{exec.output || "(no output)"}
))}
);
}
// ─── Customer Table ────────────────────────────────────────────────────────
function CustomerTable({ customers }: { customers: CustomerRecord[] }) {
if (customers.length === 0) {
return (
}
title="No customers"
description="The database is empty"
/>
);
}
return (
|
Name
|
Email
|
Tier
|
Region
|
{customers.map((c) => (
| {c.name} |
{c.email} |
{c.tier}
|
{c.region} |
))}
);
}
// ─── Main ──────────────────────────────────────────────────────────────────
function ChatPanel() {
const [connectionStatus, setConnectionStatus] =
useState("connecting");
const [input, setInput] = useState("");
const [sandboxState, setSandboxState] = useState(null);
const messagesEndRef = useRef(null);
const agent = useAgent({
agent: "SandboxAgent",
onOpen: useCallback(() => setConnectionStatus("connected"), []),
onClose: useCallback(() => setConnectionStatus("disconnected"), []),
onError: useCallback(
(error: Event) => console.error("WebSocket error:", error),
[]
),
onStateUpdate: useCallback(
(state: SandboxState) => setSandboxState(state),
[]
)
});
const { messages, sendMessage, clearHistory, status } = useAgentChat({
agent
});
const isStreaming = status === "streaming";
const isConnected = connectionStatus === "connected";
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const send = useCallback(() => {
const text = input.trim();
if (!text || isStreaming) return;
setInput("");
sendMessage({ role: "user", parts: [{ type: "text", text }] });
}, [input, isStreaming, sendMessage]);
const [rightTab, setRightTab] = useState<"executions" | "data">("executions");
return (
{/* Left: Chat */}
{messages.length === 0 && (
}
title="Talk to the sandbox agent"
description={`Try: "Write code to count customers by tier" or "Find customers whose email contains 'example'"`}
/>
)}
{messages.map((message, index) => {
const isUser = message.role === "user";
const isLastAssistant =
message.role === "assistant" && index === messages.length - 1;
return (
{isUser ? (
{getMessageText(message)}
) : (
getMessageText(message) && (
{getMessageText(message)}
)
)}
{message.parts
.filter((part) => isToolUIPart(part))
.map((part) => {
if (!isToolUIPart(part)) return null;
const toolName = getToolName(part);
if (part.state === "output-available") {
return (
{toolName === "executeCode" ? (
) : (
)}
{toolName === "executeCode"
? "Code executed in sandbox"
: "Query executed"}
);
}
if (
part.state === "input-available" ||
part.state === "input-streaming"
) {
return (
{toolName === "executeCode"
? "Running code in sandbox..."
: `Running ${toolName}...`}
);
}
return null;
})}
);
})}
{/* Right: Execution Log + Data */}
{rightTab === "executions" && sandboxState && (
)}
{rightTab === "data" && sandboxState && (
)}
{!sandboxState && (
Connecting...
)}
);
}
export default function App() {
return (
Loading...
}
>
);
}