@hsafa/ui-sdk 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ # 00 — Overview
2
+
3
+ This overview orients you across the SDK codebase, the Agent Studio client, and the server agent runtime.
4
+
5
+ - SDK package root: `sdk/`
6
+ - Public entry: `sdk/src/index.ts`
7
+ - Provider: `sdk/src/providers/HsafaProvider.tsx`
8
+ - Primary UI: `sdk/src/components/HsafaChat.tsx`
9
+ - Headless hooks: `sdk/src/hooks/*`
10
+ - Dynamic pages: `sdk/src/components/dynamic-page/`
11
+ - Built-in tools/UI for the agent: `sdk/src/components/hsafa-chat/utils/`
12
+ - Client Agent Studio: `client/src/features/agent/`
13
+ - Server agent runtime: `server/src/modules/agents/`
14
+
15
+ ## Repository map (selected)
16
+
17
+ - `sdk/`
18
+ - `src/index.ts` — re-exports components, hooks, providers, dynamic page utilities, types.
19
+ - `src/providers/HsafaProvider.tsx` — context with config, streaming/open state, dynamic page type registration.
20
+ - `src/components/HsafaChat.tsx` — production chat component built on Vercel AI SDK v5. Integrates tools, UI injection, persistence, and dynamic page tools.
21
+ - `src/components/hsafa-chat/` — child UI parts (header, input, history, message list, editor, error boundary) and streaming helpers.
22
+ - `src/components/hsafa-chat/utils/`
23
+ - `transport.ts` — `createHsafaTransport(baseUrl, agentId, chatId)` using `DefaultChatTransport`.
24
+ - `builtInTools.ts` — `getDomComponents`, `controlCursor`, `fillActiveInput`, `requestInput`.
25
+ - `renderUserForm.ts` — runtime-rendered inline forms for `requestInput` tool.
26
+ - `src/components/web-controler/` — DOM discovery and cursor control utilities for built-in tools.
27
+ - `src/hooks/` — `useHsafaAgent`, `useChatStorage`, `useFileUploadHook`, `useMessageEditor`, `useAutoScroll`.
28
+ - `src/components/dynamic-page/` — dynamic page runtime, operations, storage, and tools factory.
29
+ - `src/types/` — shared public types (`chat.ts`, `messages.ts`).
30
+ - `docs/` — Advanced guide and generated API (`docs/api/`).
31
+
32
+ - `client/src/features/agent/` — Agent Studio (builder):
33
+ - `AgentPage.tsx`, `components/AgentFlow.tsx`, `hooks/useAgentFlow.ts`, `components/nodes/*`.
34
+ - Designs agents as flow graphs (nodes/edges); persists in database via GraphQL/backend.
35
+
36
+ - `server/src/modules/agents/`
37
+ - `run-agent-handler.ts` — HTTP handler for `POST /api/run/:id` streaming via Vercel AI SDK v5 (`streamText` + `toUIMessageStream`).
38
+ - `run-core/*` — model providers, MCP, tools setup, prompts, utilities.
39
+ - `agent-service.ts` — CRUD for agents with Prisma.
40
+
41
+ ## High-level data flow
42
+
43
+ ```mermaid
44
+ flowchart LR
45
+ UI[HsafaChat/useHsafaAgent] -- UIMessage[] --> /api/run/:agentId
46
+ Server -- stream NDJSON --> UI
47
+ subgraph Client
48
+ UI --> Tools[built-in + custom tools]
49
+ Tools --> UI
50
+ UI --> Storage[useChatStorage]
51
+ end
52
+ subgraph Server
53
+ Handler[run-agent-handler.ts]\nconvertToModelMessages
54
+ Handler --> streamText --> UIStream
55
+ UIStream --> toUIMessageStream --> NDJSON
56
+ ToolsSetup[run-core/tools.ts]\nMCP
57
+ end
58
+ ```
59
+
60
+ ## Public exports (from `src/index.ts`)
61
+
62
+ - Components: `HsafaChat`, `ContentContainer`, `Button`, `FloatingChatButton`
63
+ - Provider: `HsafaProvider`, `useHsafa`
64
+ - Hooks: `useHsafaAgent`, `useChatStorage`, `useFileUploadHook`, `useAutoScroll`, `useMessageEditor`
65
+ - Dynamic pages: `DynamicPage`, `useDynamicPage`, `createDynamicPageTools`, storage helpers, types
66
+ - Web controller tools: `getDomComponents`, `guideCursor`, `controlCursor`, `FillActiveInput`, `CursorController`
67
+ - Types: selected chat types
68
+
69
+ See also `sdk/docs/README.md` and `sdk/docs/api/`.
@@ -0,0 +1,133 @@
1
+ # 01 — Quickstart
2
+
3
+ This quickstart shows how to install the SDK, render the chat UI, and optionally build a custom headless UI.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @hsafa/ui-sdk
9
+ # or
10
+ yarn add @hsafa/ui-sdk
11
+ # or
12
+ npm i @hsafa/ui-sdk
13
+ ```
14
+
15
+ Peer deps: React 18+, React DOM 18+.
16
+
17
+ ## Minimal UI setup
18
+
19
+ Wrap your app with the provider and render `HsafaChat`.
20
+
21
+ ```tsx
22
+ import { HsafaProvider, HsafaChat } from '@hsafa/ui-sdk';
23
+
24
+ export default function App() {
25
+ return (
26
+ <HsafaProvider baseUrl={process.env.NEXT_PUBLIC_API_BASE || ''}>
27
+ <HsafaChat agentId="my-agent" theme="dark" />
28
+ </HsafaProvider>
29
+ );
30
+ }
31
+ ```
32
+
33
+ - `baseUrl` points to your backend. The chat posts to `POST {baseUrl}/api/run/:agentId`.
34
+ - File uploads post to `POST {baseUrl}/api/uploads`.
35
+
36
+ ## Customizing the chat
37
+
38
+ `HsafaChat` supports theming and UI options (see `sdk/src/types/chat.ts`).
39
+
40
+ ```tsx
41
+ <HsafaChat
42
+ agentId="my-agent"
43
+ theme="light"
44
+ primaryColor="#3b82f6"
45
+ backgroundColor="#ffffff"
46
+ borderColor="#e5e7eb"
47
+ textColor="#111827"
48
+ accentColor="#F9FAFB"
49
+ />
50
+ ```
51
+
52
+ ## Adding custom UI components and tools
53
+
54
+ - UI components: pass via `HsafaUI` prop or via the headless hook (see below). The agent can render them by name via the `ui` tool.
55
+
56
+ ```tsx
57
+ function ProductCard({ name, price }: { name: string; price: number }) {
58
+ return <div>{name}: ${price}</div>;
59
+ }
60
+
61
+ <HsafaChat agentId="my-agent" HsafaUI={{ ProductCard }} />
62
+ ```
63
+
64
+ - Frontend tools: pass as `HsafaTools` to `HsafaChat`. Each tool is a function or `{ tool, executeEachToken }`.
65
+
66
+ ```tsx
67
+ const tools = {
68
+ add: async ({ a, b }: any) => ({ sum: a + b })
69
+ };
70
+
71
+ <HsafaChat agentId="my-agent" HsafaTools={tools} />
72
+ ```
73
+
74
+ ## Headless: build your own chat UI
75
+
76
+ Use `useHsafaAgent` for full control over rendering.
77
+
78
+ ```tsx
79
+ import { useHsafaAgent } from '@hsafa/ui-sdk';
80
+
81
+ export function MyChat() {
82
+ const agent = useHsafaAgent({
83
+ agentId: 'my-agent',
84
+ baseUrl: '',
85
+ tools: {
86
+ add: async ({ a, b }) => ({ sum: a + b }),
87
+ },
88
+ uiComponents: {
89
+ ProductCard: ({ name, price }: any) => <div>{name}: ${price}</div>,
90
+ },
91
+ });
92
+
93
+ return (
94
+ <div>
95
+ {agent.messages.map((m) => (
96
+ <pre key={m.id}>{JSON.stringify(m, null, 2)}</pre>
97
+ ))}
98
+ <input value={agent.input} onChange={(e) => agent.setInput(e.target.value)} />
99
+ <button onClick={() => agent.sendMessage()}>Send</button>
100
+ </div>
101
+ );
102
+ }
103
+ ```
104
+
105
+ ## Persist chat history
106
+
107
+ Use `useChatStorage` to manage chat history, switching, and metadata. `HsafaChat` uses the same storage utility under the hood.
108
+
109
+ ```tsx
110
+ import { useChatStorage } from '@hsafa/ui-sdk';
111
+
112
+ function History({ agentId, chatId, messages, setMessages }: any) {
113
+ const storage = useChatStorage({ agentId, chatId, messages, isLoading: false });
114
+ return (
115
+ <div>
116
+ {storage.chatList.map((m) => (
117
+ <button key={m.id} onClick={() => storage.switchToChat(m.id, setMessages)}>
118
+ {m.title}
119
+ </button>
120
+ ))}
121
+ </div>
122
+ );
123
+ }
124
+ ```
125
+
126
+ ## Backend endpoint
127
+
128
+ Implement `POST /api/run/:agentId` using the Vercel AI SDK v5. Reference: `server/src/modules/agents/run-agent-handler.ts`.
129
+
130
+ - Input: `{ messages: UIMessage[], ... }` (the SDK automatically adds `chatId` via `createHsafaTransport`).
131
+ - Output: NDJSON stream produced via `createUIMessageStreamResponse`.
132
+
133
+ If you already use the provided server code, the SDK will work out of the box.
@@ -0,0 +1,75 @@
1
+ # 02 — Architecture
2
+
3
+ This section explains how the SDK layers fit together across UI, hooks, storage, transport, and the server runtime.
4
+
5
+ ## Layers
6
+
7
+ - **Provider**: `sdk/src/providers/HsafaProvider.tsx`
8
+ - Holds SDK config (`baseUrl`, `dir`, `theme`).
9
+ - Tracks per-chat streaming/open state and dynamic page types.
10
+ - **Chat UI**: `sdk/src/components/HsafaChat.tsx`
11
+ - Vercel AI SDK v5 `useChat` based.
12
+ - Integrates tools (`HsafaTools`) and renderable UI components (`HsafaUI`), plus inline form rendering for `requestInput`.
13
+ - Persists chat history via `createChatStorage()` and `useChatStorage()`.
14
+ - **Headless Agent**: `sdk/src/hooks/useHsafaAgent.ts`
15
+ - Same semantics as `HsafaChat` without UI.
16
+ - Exposes `messages`, `sendMessage()`, `stop()`, `chatId`, `setChatId()`, tool/UI plumbing, and form handling.
17
+ - **Storage**: `sdk/src/hooks/useChatStorage.ts`, `sdk/src/utils/chat-storage.ts`
18
+ - LocalStorage-based persistence per `agentId`.
19
+ - **Transport**: `sdk/src/components/hsafa-chat/utils/transport.ts`
20
+ - `createHsafaTransport(baseUrl, agentId, chatId)` wraps `DefaultChatTransport` and injects `chatId`.
21
+ - **Tools & UI**: `sdk/src/components/hsafa-chat/utils/`
22
+ - Built-in browser tools (cursor control, DOM discovery, fill input) and inline form renderer for `requestInput`.
23
+ - **Dynamic Pages**: `sdk/src/components/dynamic-page/*`
24
+ - Runtime for rendering complex, tool-driven UI pages.
25
+
26
+ ## Data contracts
27
+
28
+ - **Client → Server**: UI messages via Vercel AI SDK v5 `useChat()`.
29
+ - The SDK posts to `POST {baseUrl}/api/run/:agentId` with body `{ messages: UIMessage[], chatId, ... }`.
30
+ - **Server → Client**: NDJSON stream created by `createUIMessageStreamResponse()`.
31
+ - Contains deltas for text, tool calls/results, reasoning, and final response items.
32
+
33
+ ## Message flow
34
+
35
+ ```mermaid
36
+ sequenceDiagram
37
+ participant UI as HsafaChat/useHsafaAgent
38
+ participant S as Server run-agent-handler
39
+ UI->>S: POST /api/run/:agentId (UIMessage[])
40
+ S-->>UI: NDJSON stream (toUIMessageStream)
41
+ UI->>UI: render deltas (text, tool-calls, ui, reasoning)
42
+ UI->>UI: addToolResult() after tools/inline UI
43
+ ```
44
+
45
+ ## Tool execution
46
+
47
+ - **Frontend tools**: provided as functions in `HsafaTools` (or `tools` for headless). Executed inside `onToolCall` when the tool name matches.
48
+ - **UI tools**: the agent can request `ui` or a registered component name. Rendering is deferred; result posted with `addToolResult()` on success/error.
49
+ - **Request input**: special tool that renders a form inside a message using `renderUserForm.ts` and waits for user.
50
+
51
+ ## Persistence model
52
+
53
+ Storage keys (from `createChatStorage()`):
54
+ - Index: `hsafaChat_${agentId}.chats`
55
+ - Chat data: `hsafaChat_${agentId}.chat.${chatId}`
56
+ - Current chat id: `hsafaChat_${agentId}.currentChatId`
57
+ - Show chat: `hsafaChat_${agentId}.showChat`
58
+
59
+ ## Dynamic Page integration
60
+
61
+ - Register dynamic page types via `HsafaProvider.registerDynamicPageTypes()`.
62
+ - Use `useDynamicPage(chatId, types)` and `createDynamicPageTools(getOperations)` to expose operations to the agent.
63
+
64
+ ## Server architecture (overview)
65
+
66
+ - `server/src/modules/agents/run-agent-handler.ts`:
67
+ - Loads agent config (nodes/edges) from Prisma.
68
+ - Builds model/tool stack (including MCP) via `run-core/*`.
69
+ - Converts `UIMessage[]` to model messages (`convertToModelMessages`).
70
+ - Streams via `streamText(...).toUIMessageStream()` and returns `createUIMessageStreamResponse()`.
71
+ - `run-core/` includes:
72
+ - `models.ts` (provider factories),
73
+ - `mcp.ts` (MCP runtime),
74
+ - `tools/` (HTTP, web_control, etc.),
75
+ - `prompts.ts` (system prompt construction).
@@ -0,0 +1,81 @@
1
+ # 03 — Components & Hooks
2
+
3
+ This is a practical catalogue of public components, hooks, and types, with examples and source references.
4
+
5
+ ## Components
6
+
7
+ - **`HsafaChat`** (`sdk/src/components/HsafaChat.tsx`)
8
+ - Full-featured chat UI built on Vercel AI SDK v5.
9
+ - Props (selected — see `sdk/src/types/chat.ts` for all):
10
+ - `agentId: string` (required)
11
+ - `theme?: 'dark' | 'light'`
12
+ - Colors: `primaryColor`, `primaryColorDark`, `primaryColorLight`, `backgroundColor`, `borderColor`, `textColor`, `accentColor`
13
+ - Layout: `width`, `height`, `defaultOpen`, `floatingButtonPosition`
14
+ - Behavior/UI: `componentAboveInput`, `editProcessContent`
15
+ - Extensions: `HsafaTools?: Record<string, HsafaTool>`, `HsafaUI?: Record<string, React.ComponentType<any>>`, `dynamicPageTypes?: any[]`
16
+ - Example:
17
+ ```tsx
18
+ <HsafaChat
19
+ agentId="my-agent"
20
+ theme="dark"
21
+ HsafaTools={{ add: async ({ a, b }) => ({ sum: a + b }) }}
22
+ HsafaUI={{ ProductCard: (p: any) => <div>{p.name}</div> }}
23
+ />
24
+ ```
25
+
26
+ - **`ContentContainer`** (`sdk/src/components/ContentContainer.tsx`)
27
+ - Generic content wrapper used across docs/examples.
28
+
29
+ - **`FloatingChatButton`** (`sdk/src/components/FloatingChatButton.tsx`)
30
+ - Floating FAB to reopen a minimized chat.
31
+
32
+ ## Provider
33
+
34
+ - **`HsafaProvider`** (`sdk/src/providers/HsafaProvider.tsx`)
35
+ - Props: `baseUrl?: string`, `dir?: 'ltr' | 'rtl'`, `theme?: 'dark' | 'light'`.
36
+ - Context via `useHsafa()`:
37
+ - Streaming/open state per chat: `setStreamingState(chatId, isStreaming)`, `setChatOpenState(chatId, isOpen)`.
38
+ - `currentChatId`, dynamic page type registration.
39
+ - Extensions are passed directly to `HsafaChat` (`HsafaTools`, `HsafaUI`) or to `useHsafaAgent` (`tools`, `uiComponents`).
40
+
41
+ ## Hooks
42
+
43
+ - **`useHsafaAgent(config)`** (`sdk/src/hooks/useHsafaAgent.ts`)
44
+ - Headless agent hook used by `HsafaChat`.
45
+ - Input (selected): `{ agentId, baseUrl?, tools: HsafaTools?, uiComponents: HsafaUI?, dynamicPageTypes?, onFinish?, onError?, initialMessages?, onMessagesChange? }`
46
+ - Output (selected):
47
+ - State: `input`, `messages`, `isLoading`, `status`, `error`
48
+ - Actions: `setInput`, `sendMessage({ text?, files? })`, `stop()`, `newChat()`, `setMessages(messages)`
49
+ - Advanced: `chatApi`, `chatId`, `setChatId(id)`, `tools`, `uiComponents`, `dynamicPage`
50
+ - Inline forms/UI: `formHostRef`, `formStateRef`, `cleanupForms()`, `onUISuccess()`, `onUIError()`
51
+ - Example:
52
+ ```tsx
53
+ const agent = useHsafaAgent({ agentId: 'my-agent', baseUrl: '' });
54
+ agent.setInput('Hello');
55
+ await agent.sendMessage();
56
+ ```
57
+
58
+ - **`useChatStorage(config)`** (`sdk/src/hooks/useChatStorage.ts`)
59
+ - Input: `{ agentId, chatId, messages, isLoading?, autoSave?, autoRestore? }`
60
+ - Output: `{ chatList, currentChatMeta, refreshChatList, loadChat, saveCurrentChat, deleteChat, switchToChat, createNewChat, searchChats, storage }`
61
+ - `storage` is the raw util from `sdk/src/utils/chat-storage.ts` with keys prefixed by `hsafaChat_${agentId}`.
62
+
63
+ - **`useFileUploadHook(baseUrl?)`** (`sdk/src/hooks/useFileUploadHook.ts`)
64
+ - Output: `{ attachments, uploading, fileInputRef, formatBytes, handleRemoveAttachment, handleFileSelection, buildUserContent, clearAttachments, setAttachments }`
65
+ - Posts to `{baseUrl}/api/uploads`.
66
+
67
+ - **`useMessageEditor(config)`** (`sdk/src/hooks/useMessageEditor.ts`)
68
+ - Utilities for message editing+regeneration from a point in history.
69
+ - Output: `{ editingMessageId, editingText, setEditingText, editAttachments, setEditAttachments, editUploading, startEdit, cancelEdit, saveEdit, isEditing, addEditAttachments, removeEditAttachment }`
70
+
71
+ - **`useAutoScroll<T>()`** (`sdk/src/hooks/useAutoScroll.ts`)
72
+ - Auto-scroll container to bottom while streaming.
73
+
74
+ ## Types
75
+
76
+ - From `sdk/src/types/chat.ts` (selected):
77
+ - `Attachment`, `UserContentPart`, `AssistantContentPart`, `ChatMessage`
78
+ - `HsafaTool`
79
+ - `HsafaChatProps`, `EditProcessContent`
80
+
81
+ Refer to `sdk/src/index.ts` for the full export surface.
@@ -0,0 +1,73 @@
1
+ # 04 — Streaming & Transport
2
+
3
+ The SDK uses Vercel AI SDK v5 for robust, incremental streaming of assistant responses, tool calls, and UI instructions.
4
+
5
+ ## Transport
6
+
7
+ - File: `sdk/src/components/hsafa-chat/utils/transport.ts`
8
+ - Function: `createHsafaTransport(baseUrl, agentId, chatId)`
9
+ - Wraps `DefaultChatTransport` to point to `POST {baseUrl}/api/run/:agentId`.
10
+ - Injects `chatId` into the request body so the server can correlate sessions.
11
+
12
+ ```ts
13
+ import { DefaultChatTransport } from 'ai';
14
+
15
+ export function createHsafaTransport(baseUrl: string, agentId: string, chatId: string) {
16
+ return new DefaultChatTransport({
17
+ api: `${baseUrl}/api/run/${agentId}`,
18
+ fetch: async (input, init) => {
19
+ const body = init?.body ? JSON.parse(init.body as string) : {};
20
+ const enhancedBody = { ...body, chatId };
21
+ return fetch(input, { ...init, body: JSON.stringify(enhancedBody) });
22
+ },
23
+ });
24
+ }
25
+ ```
26
+
27
+ ## Client streaming: useChat (v5)
28
+
29
+ Both `HsafaChat` and `useHsafaAgent` wire `useChat({ transport, sendAutomaticallyWhen, onToolCall, onFinish, onError })`.
30
+
31
+ - `sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls` — automatically resubmits after tool results are added.
32
+ - `onToolCall` — resolves frontend tools and UI rendering.
33
+ - `addToolResult({ tool, toolCallId, output | state: 'output-error', errorText })` — posts results/errors back to the assistant message.
34
+
35
+ ### UI tool calls
36
+
37
+ - If the tool name is `'ui'` or matches a registered UI component, rendering is deferred to the message renderer (`MessageList/AssistantMassage`).
38
+ - Success/error is acknowledged via `onUISuccess(toolCallId, toolName)` / `onUIError(toolCallId, toolName, error)` which then call `addToolResult`.
39
+
40
+ ### Request input (inline forms)
41
+
42
+ - Special tool `'requestInput'` renders a form inside the message using `renderUserForm.ts`.
43
+ - The form posts a tool result when submitted or skipped.
44
+
45
+ ## Server streaming
46
+
47
+ - File: `server/src/modules/agents/run-agent-handler.ts`
48
+ - Core pipeline:
49
+ 1) Parse `UIMessage[]` from request body (AI SDK v5 format) and convert to model messages via `convertToModelMessages()`.
50
+ 2) Build model/tools (including MCP) using `run-core/*`.
51
+ 3) Call `streamText({ model, tools, messages, ... })`.
52
+ 4) Convert to a UI message stream: `const uiStream = agentResponse.toUIMessageStream({ originalMessages: uiMessages })`.
53
+ 5) Send with `createUIMessageStreamResponse({ stream, originalMessages })`.
54
+
55
+ ### Event types
56
+
57
+ Common NDJSON events (consumed by the AI SDK v5 UI layer):
58
+
59
+ - Text: `text-delta`, `text-end`
60
+ - Reasoning: `reasoning-start`, `reasoning-delta`, `reasoning-end`
61
+ - Tools: `tool-call`, `tool-result`, `tool-error`
62
+ - Final: `final` (with `value.items`)
63
+ - Error: `error`
64
+
65
+ The client’s `useChat` integrates these automatically into assistant messages.
66
+
67
+ ## File uploads
68
+
69
+ - Hook: `useFileUploadHook(baseUrl?)`
70
+ - Endpoint: `POST {baseUrl}/api/uploads`
71
+ - Response JSON should include: `{ id, name, url, mimeType, size }`.
72
+
73
+ Uploaded files are attached to the next user message as AI SDK v5 file parts (the SDK converts `url` to proper `data` where needed).
@@ -0,0 +1,73 @@
1
+ # 05 — Tools & UI
2
+
3
+ This section documents built-in tools/UI and how to add your own front-end tools and renderable UI components.
4
+
5
+ ## Built-in tools (frontend)
6
+
7
+ File: `sdk/src/components/hsafa-chat/utils/builtInTools.ts`
8
+
9
+ - **`getDomComponents`**
10
+ - Discovers DOM components (optionally including hidden) for web control scenarios.
11
+ - Signature: `{ includeHidden?: boolean, selector?: string }` → DOM metadata
12
+ - **`controlCursor`**
13
+ - Guides a visual cursor to move/click/drag on elements for demos or automation.
14
+ - Signature: `{ target: string | { x,y }, action?: 'move'|'click'|'drag', anchor?, durationMs?, dragTo? }`
15
+ - **`fillActiveInput`**
16
+ - Fills the currently focused input.
17
+ - Signature: `{ value: string }`
18
+ - **`requestInput`**
19
+ - Special tool to request form input from the user inside a message. The form is rendered inline and its submission posts a tool result.
20
+
21
+ All built-ins are available automatically via `useHsafaAgent`/`HsafaChat` under their tool names.
22
+
23
+ ### Progressive tool execution
24
+
25
+ If a tool config has `{ executeEachToken: true }`, it will be executed on streaming deltas via `useStreamingToolInput.ts` when the input changes.
26
+
27
+ ```ts
28
+ // Example from dynamic page tools
29
+ editObject: {
30
+ executeEachToken: true,
31
+ tool: async (input) => { /* ... */ }
32
+ }
33
+ ```
34
+
35
+ ## Built-in UI components
36
+
37
+ File: `sdk/src/components/hsafa-chat/utils/builtInUI.tsx`
38
+
39
+ - **`plainText`**: renders markdown text with Mermaid support using `MarkdownRendererWithMermaid`.
40
+ - Always available (no need to register in `HsafaUI`).
41
+
42
+ ## Rendering custom UI from agent
43
+
44
+ Expose UI to the agent by passing `HsafaUI` to `HsafaChat` (or `uiComponents` to `useHsafaAgent`). The agent can then emit a UI tool call to render the component by name.
45
+
46
+ ```tsx
47
+ <HsafaChat agentId="my-agent" HsafaUI={{ ProductCard }} />
48
+ ```
49
+
50
+ The agent can request a UI component by returning a tool call named `'ui'` or the exact component name. Rendering is deferred and when complete, `addToolResult` is emitted with `{ rendered: true }`.
51
+
52
+ ## Inline forms: `requestInput`
53
+
54
+ File: `sdk/src/components/hsafa-chat/utils/renderUserForm.ts`
55
+
56
+ - Renders a form host inside the assistant message for a specific `toolCallId`.
57
+ - Persists form state across rerenders (submitted/skipped/values).
58
+ - Posts results via `addToolResult({ tool: 'requestInput', toolCallId, output })`.
59
+
60
+ ## Adding your own tools
61
+
62
+ Pass a map of tools to the chat component or the headless hook.
63
+
64
+ ```tsx
65
+ const tools = {
66
+ add: async ({ a, b }: any) => ({ sum: a + b }),
67
+ myTool: { executeEachToken: false, tool: async (input: any) => ({ ok: true }) },
68
+ };
69
+
70
+ <HsafaChat agentId="my-agent" HsafaTools={tools} />
71
+ ```
72
+
73
+ Each tool receives `input` from the agent. Return values are serialized and sent back via `addToolResult`.
@@ -0,0 +1,63 @@
1
+ # 06 — Storage & History
2
+
3
+ Local persistence is handled via `useChatStorage` and the lower-level `createChatStorage` utility.
4
+
5
+ ## Storage utility
6
+
7
+ File: `sdk/src/utils/chat-storage.ts`
8
+
9
+ - Prefix: `hsafaChat_${agentId}`
10
+ - Keys:
11
+ - Index: `hsafaChat_${agentId}.chats` — array of `{ id, title, createdAt, updatedAt }`
12
+ - Chat data: `hsafaChat_${agentId}.chat.${chatId}` — `{ id, messages, agentId? }`
13
+ - Current chat id: `hsafaChat_${agentId}.currentChatId`
14
+ - Show chat flag: `hsafaChat_${agentId}.showChat`
15
+ - API:
16
+ - `loadChatsIndex()`, `saveChatsIndex(list)`
17
+ - `loadChat(id)`, `saveChat(data)`
18
+ - `upsertChatMeta(meta)`
19
+ - `deleteChatMeta(id)`, `deleteChatData(id)`, `deleteChat(id)`
20
+ - `loadShowChatPreference(default)`, `saveShowChatPreference(value)`
21
+ - `loadCurrentChatId()`, `saveCurrentChatId(id)`, `removeCurrentChatId()`
22
+
23
+ ## Hook: useChatStorage
24
+
25
+ File: `sdk/src/hooks/useChatStorage.ts`
26
+
27
+ Input:
28
+ ```ts
29
+ useChatStorage({ agentId, chatId, messages, isLoading?, autoSave = true, autoRestore = true })
30
+ ```
31
+
32
+ Output (selected):
33
+ - `chatList: ChatMetadata[]`
34
+ - `currentChatMeta: ChatMetadata | null`
35
+ - `refreshChatList()`
36
+ - `loadChat(chatId)`
37
+ - `saveCurrentChat()`
38
+ - `deleteChat(chatId)`
39
+ - `switchToChat(chatId, setMessages)` — loads and applies stored messages
40
+ - `createNewChat(onNewChat)` — not a saver; executes `onNewChat()` which should set a new `chatId`
41
+ - `searchChats(query)`
42
+ - `storage` — underlying `createChatStorage(agentId)` instance
43
+
44
+ ### Auto-save behavior
45
+
46
+ - On first user message, inserts a chat meta with a title derived from the first user text part.
47
+ - On subsequent message updates (and when `!isLoading`), saves chat data and updates `updatedAt`.
48
+ - After a streaming session finishes, saves one more time to ensure the final assistant message is captured.
49
+
50
+ ### Using with `HsafaChat` or headless
51
+
52
+ - `HsafaChat` uses the same storage semantics, saving UI preferences (`.showChat`) as well.
53
+ - In headless UIs, couple `useChatStorage` with `useHsafaAgent` by wiring `chatId`, `messages`, and `setMessages`.
54
+
55
+ ### Example: chat switching
56
+
57
+ ```tsx
58
+ const storage = useChatStorage({ agentId, chatId, messages, isLoading });
59
+
60
+ function openChat(id: string) {
61
+ storage.switchToChat(id, agent.setMessages);
62
+ }
63
+ ```
@@ -0,0 +1,49 @@
1
+ # 07 — Dynamic Pages
2
+
3
+ Dynamic Pages let the agent construct and manipulate a UI page (grid of objects/components) by calling dedicated tools. This is useful for dashboards, inspectors, and interactive builders.
4
+
5
+ ## Files
6
+
7
+ - Runtime: `sdk/src/components/dynamic-page/DynamicPage.tsx`
8
+ - Operations: `sdk/src/components/dynamic-page/operations.ts`
9
+ - Tools factory: `sdk/src/components/dynamic-page/tools.ts`
10
+ - Storage: `sdk/src/components/dynamic-page/storage.ts`
11
+ - Types: `sdk/src/components/dynamic-page/types.ts`
12
+ - Hook: `sdk/src/components/dynamic-page/useDynamicPage.ts`
13
+
14
+ ## Enabling in HsafaChat
15
+
16
+ 1) Declare supported dynamic page types in your app and pass as `dynamicPageTypes` prop (or via `useHsafaAgent`).
17
+ 2) `HsafaChat` registers types with `HsafaProvider` and creates dynamic page tools using `createDynamicPageTools(getOperations)`.
18
+
19
+ ```tsx
20
+ <HsafaChat
21
+ agentId="my-agent"
22
+ dynamicPageTypes={[ /* type configs */ ]}
23
+ />
24
+ ```
25
+
26
+ ## Tools
27
+
28
+ From `createDynamicPageTools(getOperations)`:
29
+
30
+ - `setGrid(input)` / `readGrid()` / `validateGrid()`
31
+ - `readAvailableTypes()`
32
+ - `setObject(input)` (streaming friendly)
33
+ - `editObject(input)` (streaming friendly; merges options for safe defaults)
34
+ - `deleteObject(input)`
35
+ - `readAllObjects()` / `readObject(input)` / `readAtPointer(input)`
36
+ - `renameObject(input)` / `moveObject(input)`
37
+ - `readComponentErrors(input)` / `clearComponentErrors()`
38
+
39
+ All functions call into `DynamicPageOperations` returned by `useDynamicPage().getOperations`.
40
+
41
+ ## Storage helpers
42
+
43
+ - `saveDynamicPageState(id, state)` / `loadDynamicPageState(id)` / `deleteDynamicPageState(id)` / `dynamicPageExists(id)`
44
+
45
+ ## Recommended usage pattern
46
+
47
+ - Enable dynamic pages only for specific chats where needed.
48
+ - Let the agent progressively build the page while streaming tool inputs.
49
+ - Use UI components to visualize objects; integrate with your own component library.