mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 22:03:48 +00:00
3.6 KiB
3.6 KiB
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
useChatAPI docs (transport-based, UIMessage parts, tool outputs). - TanStack AI
useChatsource (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
-
@sandbox-agent/ui-core- Event to UI reducers.
- Derive
UIMessage[]with parts from universal events. - Shared types for message parts, permissions, questions, and status.
-
@sandbox-agent/ui-reactuseChathook on top of core store.- Stable API shape inspired by Vercel/TanStack.
-
@sandbox-agent/ui-components(optional)- Composable UI primitives that render content parts.
- HITL UI components (permission and question prompts).
-
@sandbox-agent/ui-transportssdkTransport(client)using the TypeScript SDK.sseTransport({ baseUrl, token })for bare HTTP/SSE.turnTransportfor send + stream in one call.
Message Model
UIMessage { id, role, status, parts[] }
parts[] includes:
texttool_calltool_resultfile_ref(diffs, actions)statusreasoningimage
This aligns with docs/building-chat-ui.mdx and Vercel's UIMessage.parts concept.
Headless Core API (Sketch)
type ChatTransport = {
sendMessage: (sessionId: string, input: string) => Promise<void>
streamEvents: (sessionId: string, opts: { offset: number }) => AsyncIterable<UniversalEvent>
replyPermission: (sessionId: string, id: string, reply: "once" | "always" | "reject") => Promise<void>
replyQuestion: (sessionId: string, id: string, answers: string[][]) => Promise<void>
rejectQuestion: (sessionId: string, id: string) => Promise<void>
terminate: (sessionId: string) => Promise<void>
}
type ChatStore = {
state: ChatState
applyEvent: (event: UniversalEvent) => void
subscribe: (listener: () => void) => () => void
}
createChatStore({ transport, sessionId, initialEvents? })
React Hook API (Sketch)
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). addToolOutputis optional and only relevant when tools complete on the client.
UI Components (Optional)
MessagePartRendererwith 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
- Extract event reducers from Inspector into
@sandbox-agent/ui-core. - Implement
useChatin@sandbox-agent/ui-reactusing a store and transport. - Add UI components for content parts + HITL.
- Update
docs/building-chat-ui.mdxwith the new package usage.