From 1ca6ecb5e0920b5061a136b704bf1d9b84c9fe1f Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Wed, 4 Feb 2026 10:13:36 -0800 Subject: [PATCH] docs: propose UI library approach inspired by Vercel/TanStack useChat --- .turbo | 1 + dist | 1 + node_modules | 1 + research/ui-library-proposal.md | 109 ++++++++++++++++++++++++++++++++ target | 1 + 5 files changed, 113 insertions(+) create mode 120000 .turbo create mode 120000 dist create mode 120000 node_modules create mode 100644 research/ui-library-proposal.md create mode 120000 target diff --git a/.turbo b/.turbo new file mode 120000 index 0000000..0b7d9ca --- /dev/null +++ b/.turbo @@ -0,0 +1 @@ +/home/nathan/sandbox-agent/.turbo \ No newline at end of file diff --git a/dist b/dist new file mode 120000 index 0000000..f02d77f --- /dev/null +++ b/dist @@ -0,0 +1 @@ +/home/nathan/sandbox-agent/dist \ No newline at end of file diff --git a/node_modules b/node_modules new file mode 120000 index 0000000..501480b --- /dev/null +++ b/node_modules @@ -0,0 +1 @@ +/home/nathan/sandbox-agent/node_modules \ No newline at end of file diff --git a/research/ui-library-proposal.md b/research/ui-library-proposal.md new file mode 100644 index 0000000..4287237 --- /dev/null +++ b/research/ui-library-proposal.md @@ -0,0 +1,109 @@ +# Proposal: UI Library (useChat-style) + +## Summary +This proposes a component library modeled after Vercel AI SDK `useChat` and TanStack AI `useChat`, adapted to Sandbox Agent's universal event stream and HITL flows. The plan is headless-first, with React bindings and optional UI components. + +## References Reviewed +- Vercel AI SDK `useChat` API docs (transport-based, UIMessage parts, tool outputs). +- TanStack AI `useChat` source (`packages/typescript/ai-react/src/use-chat.ts`) and types. + +## Observations (Patterns to Copy) +- Headless core + framework bindings. +- Transport/connection abstraction; default transport with overrides. +- Hook owns message state, status, and error; returns imperative helpers. +- Message model is part-based (text, tool call/result, status, media, etc.). +- Tool/HITL outputs are first-class actions. + +## Proposal + +### Package Structure +1) `@sandbox-agent/ui-core` + - Event to UI reducers. + - Derive `UIMessage[]` with parts from universal events. + - Shared types for message parts, permissions, questions, and status. + +2) `@sandbox-agent/ui-react` + - `useChat` hook on top of core store. + - Stable API shape inspired by Vercel/TanStack. + +3) `@sandbox-agent/ui-components` (optional) + - Composable UI primitives that render content parts. + - HITL UI components (permission and question prompts). + +4) `@sandbox-agent/ui-transports` + - `sdkTransport(client)` using the TypeScript SDK. + - `sseTransport({ baseUrl, token })` for bare HTTP/SSE. + - `turnTransport` for send + stream in one call. + +### Message Model +`UIMessage { id, role, status, parts[] }` + +`parts[]` includes: +- `text` +- `tool_call` +- `tool_result` +- `file_ref` (diffs, actions) +- `status` +- `reasoning` +- `image` + +This aligns with `docs/building-chat-ui.mdx` and Vercel's `UIMessage.parts` concept. + +### Headless Core API (Sketch) +```ts +type ChatTransport = { + sendMessage: (sessionId: string, input: string) => Promise + streamEvents: (sessionId: string, opts: { offset: number }) => AsyncIterable + replyPermission: (sessionId: string, id: string, reply: "once" | "always" | "reject") => Promise + replyQuestion: (sessionId: string, id: string, answers: string[][]) => Promise + rejectQuestion: (sessionId: string, id: string) => Promise + terminate: (sessionId: string) => Promise +} + +type ChatStore = { + state: ChatState + applyEvent: (event: UniversalEvent) => void + subscribe: (listener: () => void) => () => void +} + +createChatStore({ transport, sessionId, initialEvents? }) +``` + +### React Hook API (Sketch) +```ts +const { + messages, + status, + error, + isLoading, + sendMessage, + stop, + resume, + clear, + reloadLast, + replyPermission, + replyQuestion, + rejectQuestion, + addToolOutput, +} = useChat({ sessionId, transport, initialMessages }) +``` + +Notes: +- Status should map to: `ready | submitted | streaming | error` (Vercel-style). +- `addToolOutput` is optional and only relevant when tools complete on the client. + +### UI Components (Optional) +- `MessagePartRenderer` with render props. +- `ToolCall`, `ToolResult`, `FileDiff`, `StatusChip`, `Reasoning`, `ImagePart`. +- `PermissionRequest`, `QuestionPrompt`. + +## Why This Fits Sandbox Agent +- Universal event stream already defines the canonical state. +- HITL flows (permissions/questions) map directly to hook actions and components. +- Inspector is a reference implementation; reducers can be extracted from it. + +## Adoption Path +1) Extract event reducers from Inspector into `@sandbox-agent/ui-core`. +2) Implement `useChat` in `@sandbox-agent/ui-react` using a store and transport. +3) Add UI components for content parts + HITL. +4) Update `docs/building-chat-ui.mdx` with the new package usage. diff --git a/target b/target new file mode 120000 index 0000000..3d6ad8c --- /dev/null +++ b/target @@ -0,0 +1 @@ +/home/nathan/sandbox-agent/target \ No newline at end of file