@alexkroman1/aai 0.7.12 → 0.8.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.
- package/dist/aai.js +1 -1
- package/dist/cli.js +462 -352
- package/dist/sdk/_internal_types.d.ts +0 -1
- package/dist/sdk/_internal_types.d.ts.map +1 -1
- package/dist/sdk/_internal_types.js.map +1 -1
- package/dist/sdk/_render_check.d.ts +10 -0
- package/dist/sdk/_render_check.d.ts.map +1 -0
- package/dist/sdk/_render_check.js +42 -0
- package/dist/sdk/_render_check.js.map +1 -0
- package/dist/sdk/builtin_tools.d.ts.map +1 -1
- package/dist/sdk/builtin_tools.js +21 -0
- package/dist/sdk/builtin_tools.js.map +1 -1
- package/dist/sdk/define_agent.d.ts.map +1 -1
- package/dist/sdk/define_agent.js +0 -1
- package/dist/sdk/define_agent.js.map +1 -1
- package/dist/sdk/direct_executor.d.ts.map +1 -1
- package/dist/sdk/direct_executor.js +0 -1
- package/dist/sdk/direct_executor.js.map +1 -1
- package/dist/sdk/memory_tools.d.ts +38 -0
- package/dist/sdk/memory_tools.d.ts.map +1 -0
- package/dist/sdk/memory_tools.js +77 -0
- package/dist/sdk/memory_tools.js.map +1 -0
- package/dist/sdk/mod.d.ts +3 -1
- package/dist/sdk/mod.d.ts.map +1 -1
- package/dist/sdk/mod.js +2 -0
- package/dist/sdk/mod.js.map +1 -1
- package/dist/sdk/protocol.d.ts +3 -3
- package/dist/sdk/protocol.js +1 -1
- package/dist/sdk/s2s.d.ts.map +1 -1
- package/dist/sdk/s2s.js +3 -1
- package/dist/sdk/s2s.js.map +1 -1
- package/dist/sdk/session.d.ts.map +1 -1
- package/dist/sdk/session.js +2 -0
- package/dist/sdk/session.js.map +1 -1
- package/dist/sdk/types.d.ts +23 -14
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/sdk/types.js +29 -0
- package/dist/sdk/types.js.map +1 -1
- package/dist/ui/_components/app.d.ts.map +1 -1
- package/dist/ui/_components/app.js +6 -7
- package/dist/ui/_components/app.js.map +1 -1
- package/dist/ui/_components/sidebar_layout.d.ts +19 -0
- package/dist/ui/_components/sidebar_layout.d.ts.map +1 -0
- package/dist/ui/_components/sidebar_layout.js +21 -0
- package/dist/ui/_components/sidebar_layout.js.map +1 -0
- package/dist/ui/_components/start_screen.d.ts +24 -0
- package/dist/ui/_components/start_screen.d.ts.map +1 -0
- package/dist/ui/_components/start_screen.js +25 -0
- package/dist/ui/_components/start_screen.js.map +1 -0
- package/dist/ui/_hooks.d.ts +21 -0
- package/dist/ui/_hooks.d.ts.map +1 -0
- package/dist/ui/_hooks.js +35 -0
- package/dist/ui/_hooks.js.map +1 -0
- package/dist/ui/components.d.ts +20 -0
- package/dist/ui/components.d.ts.map +1 -1
- package/dist/ui/components.js +12 -0
- package/dist/ui/components.js.map +1 -1
- package/dist/ui/components_mod.d.ts +2 -1
- package/dist/ui/components_mod.d.ts.map +1 -1
- package/dist/ui/components_mod.js +2 -1
- package/dist/ui/components_mod.js.map +1 -1
- package/dist/ui/mod.d.ts +2 -1
- package/dist/ui/mod.d.ts.map +1 -1
- package/dist/ui/mod.js +2 -1
- package/dist/ui/mod.js.map +1 -1
- package/dist/ui/signals.d.ts.map +1 -1
- package/dist/ui/signals.js +5 -2
- package/dist/ui/signals.js.map +1 -1
- package/package.json +17 -1
- package/templates/_shared/CLAUDE.md +241 -121
- package/templates/_shared/package.json +4 -1
- package/templates/dispatch-center/agent.ts +43 -72
- package/templates/embedded-assets/agent.ts +4 -5
- package/templates/health-assistant/agent.ts +7 -7
- package/templates/infocom-adventure/agent.ts +20 -20
- package/templates/memory-agent/agent.ts +1 -55
- package/templates/night-owl/agent.ts +4 -4
- package/templates/night-owl/client.tsx +6 -23
- package/templates/pizza-ordering/agent.ts +14 -15
- package/templates/pizza-ordering/client.tsx +41 -101
- package/templates/smart-research/agent.ts +10 -10
|
@@ -31,6 +31,7 @@ You are helping a user build a voice agent using the **aai** framework.
|
|
|
31
31
|
aai init # Scaffold a new agent (uses simple template)
|
|
32
32
|
aai init -t <template> # Scaffold from a specific template
|
|
33
33
|
aai dev # Start local dev server
|
|
34
|
+
aai build # Bundle and validate (no server or deploy)
|
|
34
35
|
aai deploy # Bundle and deploy to production
|
|
35
36
|
aai deploy -y # Deploy without prompts
|
|
36
37
|
aai deploy --dry-run # Validate and bundle without deploying
|
|
@@ -75,15 +76,14 @@ export default defineAgent({
|
|
|
75
76
|
name: "My Agent",
|
|
76
77
|
instructions: "You are a helpful assistant that...",
|
|
77
78
|
greeting: "Hey there. What can I help you with?",
|
|
78
|
-
voice: "694f9389-aac1-45b6-b726-9d9369183238", // Sarah
|
|
79
79
|
});
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
### Imports
|
|
83
83
|
|
|
84
84
|
```ts
|
|
85
|
-
import { defineAgent } from "aai"; //
|
|
86
|
-
import type { BeforeStepResult, HookContext, ToolContext } from "aai";
|
|
85
|
+
import { defineAgent, memoryTools, tool } from "aai"; // defineAgent + helpers
|
|
86
|
+
import type { BeforeStepResult, BuiltinTool, HookContext, StepInfo, ToolContext } from "aai";
|
|
87
87
|
import { z } from "zod"; // Tools with typed params (included in package.json)
|
|
88
88
|
```
|
|
89
89
|
|
|
@@ -95,8 +95,6 @@ defineAgent({
|
|
|
95
95
|
name: string; // Required: display name
|
|
96
96
|
instructions?: string; // System prompt (default: general voice assistant)
|
|
97
97
|
greeting?: string; // Spoken on connect (default: "Hey, how can I help you?")
|
|
98
|
-
voice?: Voice; // Cartesia voice UUID (default: Sarah 694f9389...)
|
|
99
|
-
|
|
100
98
|
// Speech
|
|
101
99
|
sttPrompt?: string; // STT guidance for jargon, names, acronyms
|
|
102
100
|
|
|
@@ -121,42 +119,11 @@ defineAgent({
|
|
|
121
119
|
});
|
|
122
120
|
```
|
|
123
121
|
|
|
124
|
-
### Voices
|
|
125
|
-
|
|
126
|
-
Voices use Cartesia voice UUIDs. Browse all voices at
|
|
127
|
-
[play.cartesia.ai](https://play.cartesia.ai).
|
|
128
|
-
|
|
129
|
-
Common voices:
|
|
130
|
-
|
|
131
|
-
| Name | Voice ID |
|
|
132
|
-
| --------------------- | -------------------------------------- |
|
|
133
|
-
| Sarah (default) | `694f9389-aac1-45b6-b726-9d9369183238` |
|
|
134
|
-
| Customer Support Man | `a167e0f3-df7e-4d52-a9c3-f949145efdab` |
|
|
135
|
-
| Customer Support Lady | `829ccd10-f8b3-43cd-b8a0-4aeaa81f3b30` |
|
|
136
|
-
| Helpful Woman | `156fb8d2-335b-4950-9cb3-a2d33befec77` |
|
|
137
|
-
| Professional Woman | `248be419-c632-4f23-adf1-5324ed7dbf1d` |
|
|
138
|
-
| Sweet Lady | `e3827ec5-697a-4b7c-9704-1a23041bbc51` |
|
|
139
|
-
| British Lady | `79a125e8-cd45-4c13-8a67-188112f4dd22` |
|
|
140
|
-
| Calm Lady | `00a77add-48d5-4ef6-8157-71e5437b282d` |
|
|
141
|
-
| Laidback Woman | `21b81c14-f85b-436d-aff5-43f2e788ecf8` |
|
|
142
|
-
| Storyteller Lady | `996a8b96-4804-46f0-8e05-3fd4ef1a87cd` |
|
|
143
|
-
| Newslady | `bf991597-6c13-47e4-8411-91ec2de5c466` |
|
|
144
|
-
| Friendly Reading Man | `69267136-1bdc-412f-ad78-0caad210fb40` |
|
|
145
|
-
| Confident British Man | `63ff761f-c1e8-414b-b969-d1833d1c870c` |
|
|
146
|
-
| New York Man | `34575e71-908f-4ab6-ab54-b08c95d6597d` |
|
|
147
|
-
| California Girl | `b7d50908-b17c-442d-ad8d-810c63997ed9` |
|
|
148
|
-
| Newsman | `d46abd1d-2d02-43e8-819f-51fb652c1c61` |
|
|
149
|
-
| Salesman | `820a3788-2b37-4d21-847a-b65d8a68c99a` |
|
|
150
|
-
| Wise Man | `b043dea0-a007-4bbe-a708-769dc0d0c569` |
|
|
151
|
-
| Child | `2ee87190-8f84-4925-97da-e52547f9462c` |
|
|
152
|
-
|
|
153
|
-
Any Cartesia voice UUID works — the list above is just a starting point.
|
|
154
|
-
|
|
155
122
|
Use `sttPrompt` for domain-specific vocabulary:
|
|
156
123
|
|
|
157
124
|
```ts
|
|
158
125
|
export default defineAgent({
|
|
159
|
-
|
|
126
|
+
name: "Tech Support",
|
|
160
127
|
sttPrompt: "Transcribe technical terms: Kubernetes, gRPC, PostgreSQL",
|
|
161
128
|
});
|
|
162
129
|
```
|
|
@@ -196,17 +163,6 @@ aai env pull
|
|
|
196
163
|
aai env rm MY_API_KEY
|
|
197
164
|
```
|
|
198
165
|
|
|
199
|
-
Declare required env vars in the agent config so the CLI validates them at
|
|
200
|
-
deploy time:
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
export default defineAgent({
|
|
204
|
-
name: "API Agent",
|
|
205
|
-
env: ["ASSEMBLYAI_API_KEY", "MY_API_KEY"],
|
|
206
|
-
// ...
|
|
207
|
-
});
|
|
208
|
-
```
|
|
209
|
-
|
|
210
166
|
Access secrets in tool code via `ctx.env`:
|
|
211
167
|
|
|
212
168
|
```ts
|
|
@@ -215,7 +171,6 @@ import { z } from "zod";
|
|
|
215
171
|
|
|
216
172
|
export default defineAgent({
|
|
217
173
|
name: "API Agent",
|
|
218
|
-
env: ["ASSEMBLYAI_API_KEY", "MY_API_KEY"],
|
|
219
174
|
tools: {
|
|
220
175
|
call_api: {
|
|
221
176
|
description: "Call an external API",
|
|
@@ -239,26 +194,27 @@ Define tools as plain objects in the `tools` record. The `parameters` field
|
|
|
239
194
|
takes a Zod schema for type-safe argument inference:
|
|
240
195
|
|
|
241
196
|
```ts
|
|
242
|
-
import { defineAgent } from "aai";
|
|
197
|
+
import { defineAgent, tool } from "aai";
|
|
243
198
|
import { z } from "zod";
|
|
244
199
|
|
|
245
200
|
export default defineAgent({
|
|
246
201
|
name: "Weather Agent",
|
|
247
202
|
tools: {
|
|
248
|
-
get_weather: {
|
|
203
|
+
get_weather: tool({
|
|
249
204
|
description: "Get current weather for a city",
|
|
250
205
|
parameters: z.object({
|
|
251
206
|
city: z.string().describe("City name"),
|
|
252
207
|
}),
|
|
253
|
-
execute: async (
|
|
208
|
+
execute: async ({ city }, ctx) => {
|
|
209
|
+
// city is typed as string (inferred from Zod schema)
|
|
254
210
|
const data = await fetch(
|
|
255
|
-
`https://api.example.com/weather?q=${
|
|
211
|
+
`https://api.example.com/weather?q=${city}`,
|
|
256
212
|
);
|
|
257
213
|
return data.json();
|
|
258
214
|
},
|
|
259
|
-
},
|
|
215
|
+
}),
|
|
260
216
|
|
|
261
|
-
// No-parameter tools — omit `parameters`
|
|
217
|
+
// No-parameter tools — omit `parameters` and `tool()` wrapper
|
|
262
218
|
list_items: {
|
|
263
219
|
description: "List all items",
|
|
264
220
|
execute: () => items,
|
|
@@ -267,6 +223,9 @@ export default defineAgent({
|
|
|
267
223
|
});
|
|
268
224
|
```
|
|
269
225
|
|
|
226
|
+
**Important:** Wrap tool definitions in `tool()` to get typed `args` inferred
|
|
227
|
+
from the Zod `parameters` schema. Without `tool()`, args are untyped.
|
|
228
|
+
|
|
270
229
|
Zod schema patterns:
|
|
271
230
|
|
|
272
231
|
```ts
|
|
@@ -289,6 +248,7 @@ Enable via `builtinTools`.
|
|
|
289
248
|
| `fetch_json` | HTTP GET a JSON API | `url`, `headers?` |
|
|
290
249
|
| `run_code` | Execute JS in sandbox (no net/fs, 30s timeout) | `code` |
|
|
291
250
|
| `vector_search` | Search the agent's RAG knowledge base | `query`, `topK?` (default 5) |
|
|
251
|
+
| `memory` | Persistent KV memory (4 tools, see below) | — |
|
|
292
252
|
|
|
293
253
|
The agentic loop runs up to `maxSteps` iterations (default 5) and stops when the
|
|
294
254
|
LLM produces a text response.
|
|
@@ -303,10 +263,17 @@ ctx.env; // Record<string, string> — secrets from `aai env add`
|
|
|
303
263
|
ctx.abortSignal; // AbortSignal — cancelled on interruption (tools only)
|
|
304
264
|
ctx.state; // per-session state
|
|
305
265
|
ctx.kv; // persistent KV store
|
|
266
|
+
ctx.vector; // VectorStore — vector store for RAG (tools only)
|
|
306
267
|
ctx.messages; // readonly Message[] — conversation history (tools only)
|
|
307
268
|
```
|
|
308
269
|
|
|
309
|
-
Hooks get `HookContext` (same but without `abortSignal` and
|
|
270
|
+
Hooks get `HookContext` (same but without `abortSignal`, `vector`, and
|
|
271
|
+
`messages`).
|
|
272
|
+
|
|
273
|
+
**Timeouts:** Tool execution times out after **30 seconds** (`abortSignal`
|
|
274
|
+
fires). Lifecycle hooks (`onConnect`, `onTurn`, etc.) time out after **5
|
|
275
|
+
seconds**. Long-running tools should pass `ctx.abortSignal` to `fetch` and
|
|
276
|
+
check `ctx.abortSignal.aborted` in loops.
|
|
310
277
|
|
|
311
278
|
### Fetching external APIs
|
|
312
279
|
|
|
@@ -369,6 +336,37 @@ Keys are strings; use colon-separated prefixes (`"user:123"`). Max value: 64 KB.
|
|
|
369
336
|
`kv.list()` returns `KvEntry[]` where each entry has
|
|
370
337
|
`{ key: string, value: T }`.
|
|
371
338
|
|
|
339
|
+
### Memory tools (pre-built KV tools)
|
|
340
|
+
|
|
341
|
+
Add `"memory"` to `builtinTools` to give the agent four persistent KV tools:
|
|
342
|
+
`save_memory`, `recall_memory`, `list_memories`, and `forget_memory`.
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
import { defineAgent } from "aai";
|
|
346
|
+
|
|
347
|
+
export default defineAgent({
|
|
348
|
+
name: "My Agent",
|
|
349
|
+
builtinTools: ["memory"],
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Keys use colon-separated prefixes (`"user:name"`, `"preference:color"`).
|
|
354
|
+
|
|
355
|
+
You can also spread `memoryTools()` into `tools` if you want to combine them
|
|
356
|
+
with custom tools or override individual tools:
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { defineAgent, memoryTools } from "aai";
|
|
360
|
+
|
|
361
|
+
export default defineAgent({
|
|
362
|
+
name: "My Agent",
|
|
363
|
+
tools: {
|
|
364
|
+
...memoryTools(),
|
|
365
|
+
// your other tools...
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
372
370
|
## Advanced patterns
|
|
373
371
|
|
|
374
372
|
### Step hooks
|
|
@@ -552,18 +550,41 @@ mount(App, {
|
|
|
552
550
|
|
|
553
551
|
Import from `aai/ui`:
|
|
554
552
|
|
|
555
|
-
|
|
556
|
-
| ------------------- | -------------------------------------------------- |
|
|
557
|
-
| `App` | Default full UI (start screen + ChatView) |
|
|
558
|
-
| `ChatView` | Chat interface with header, messages, and controls |
|
|
559
|
-
| `MessageBubble` | Single message (user right-aligned, agent left) |
|
|
560
|
-
| `Transcript` | Live STT text display |
|
|
561
|
-
| `StateIndicator` | Colored dot + agent state label |
|
|
562
|
-
| `ErrorBanner` | Red error box with message |
|
|
563
|
-
| `ThinkingIndicator` | Animated dots during processing |
|
|
564
|
-
| `ToolCallBlock` | Collapsible tool call display (name, args, result) |
|
|
553
|
+
**Layout components:**
|
|
565
554
|
|
|
566
|
-
|
|
555
|
+
| Component | Description |
|
|
556
|
+
| --------------- | ---------------------------------------------------- |
|
|
557
|
+
| `App` | Default full UI (StartScreen + ChatView) |
|
|
558
|
+
| `StartScreen` | Centered start card; renders children after start |
|
|
559
|
+
| `ChatView` | Chat interface (header + messages + controls) |
|
|
560
|
+
| `SidebarLayout` | Two-column layout with sidebar + main area |
|
|
561
|
+
| `Controls` | Stop/Resume + New Conversation buttons |
|
|
562
|
+
| `MessageList` | Messages with auto-scroll, tool calls, transcript |
|
|
563
|
+
|
|
564
|
+
`StartScreen` props: `{ children, icon?, title?, subtitle?, buttonText? }`
|
|
565
|
+
`SidebarLayout` props: `{ sidebar, children, width?, side? }`
|
|
566
|
+
|
|
567
|
+
**Atomic components:**
|
|
568
|
+
|
|
569
|
+
| Component | Props | Description |
|
|
570
|
+
| ------------------- | --------------------------------------- | ------------------------------- |
|
|
571
|
+
| `MessageBubble` | `{ message: Message }` | Single message bubble |
|
|
572
|
+
| `Transcript` | `{ userUtterance: Signal<str\|null> }` | Live STT text display |
|
|
573
|
+
| `StateIndicator` | `{ state: Signal<AgentState> }` | Colored dot + state label |
|
|
574
|
+
| `ErrorBanner` | `{ error: Signal<SessionError\|null> }` | Red error box with message |
|
|
575
|
+
| `ThinkingIndicator` | none | Animated dots during processing |
|
|
576
|
+
| `ToolCallBlock` | `{ toolCall: ToolCallInfo }` | Collapsible tool call display |
|
|
577
|
+
|
|
578
|
+
**Hooks:**
|
|
579
|
+
|
|
580
|
+
- `useAutoScroll()` — returns a `RefObject<HTMLDivElement>` to attach to a
|
|
581
|
+
sentinel div. Auto-scrolls when messages or utterances change.
|
|
582
|
+
- `useMountConfig()` — returns the `title` and `theme` passed to `mount()`.
|
|
583
|
+
|
|
584
|
+
**Important:** Components that accept `Signal<T>` props (like `StateIndicator`,
|
|
585
|
+
`Transcript`, `ErrorBanner`) expect the Signal object itself, NOT `.value`. Pass
|
|
586
|
+
`session.state`, not `session.state.value`. Passing `.value` compiles but breaks
|
|
587
|
+
reactivity silently.
|
|
567
588
|
|
|
568
589
|
### Session signals (`useSession()`)
|
|
569
590
|
|
|
@@ -571,16 +592,17 @@ Use `useMountConfig()` to access the `title` and `theme` passed to `mount()`.
|
|
|
571
592
|
`{ session, started, running, start, toggle, reset, dispose }`. Reactive agent
|
|
572
593
|
data lives on `session` (a `VoiceSession`); UI-only controls are top-level.
|
|
573
594
|
|
|
574
|
-
| Signal / field
|
|
575
|
-
|
|
|
576
|
-
| `session.state.value`
|
|
577
|
-
| `session.messages.value`
|
|
578
|
-
| `session.toolCalls.value`
|
|
579
|
-
| `session.userUtterance.value`
|
|
580
|
-
| `session.
|
|
581
|
-
| `session.
|
|
582
|
-
| `
|
|
583
|
-
| `
|
|
595
|
+
| Signal / field | Type | Description |
|
|
596
|
+
| ------------------------------ | ---------------------- | --------------------------------------------------------------- |
|
|
597
|
+
| `session.state.value` | `AgentState` | "disconnected", "connecting", "ready", "listening", etc. |
|
|
598
|
+
| `session.messages.value` | `Message[]` | `{ role, text }` objects |
|
|
599
|
+
| `session.toolCalls.value` | `ToolCallInfo[]` | `{ toolCallId, toolName, args, status, result? }` — tool calls |
|
|
600
|
+
| `session.userUtterance.value` | `string \| null` | `null` = not speaking, `""` = speech detected, string = text |
|
|
601
|
+
| `session.agentUtterance.value` | `string \| null` | `null` = not speaking, string = streaming agent response text |
|
|
602
|
+
| `session.error.value` | `SessionError \| null` | `{ code, message }` |
|
|
603
|
+
| `session.disconnected.value` | `object \| null` | `{ intentional: boolean }` when disconnected, `null` otherwise |
|
|
604
|
+
| `started.value` | `boolean` | Whether session has been started |
|
|
605
|
+
| `running.value` | `boolean` | Whether session is active |
|
|
584
606
|
|
|
585
607
|
**Methods:** `start()`, `toggle()`, `reset()`, `dispose()`
|
|
586
608
|
|
|
@@ -590,6 +612,20 @@ data lives on `session` (a `VoiceSession`); UI-only controls are top-level.
|
|
|
590
612
|
| --------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
591
613
|
| `useToolResult((toolName, result, tc) => {})` | Fires once per completed tool call with parsed JSON result. Use for carts, scoreboards, etc. |
|
|
592
614
|
|
|
615
|
+
**Signal semantics for utterances:**
|
|
616
|
+
|
|
617
|
+
- `userUtterance`: `null` = user is not speaking, `""` = speech detected but
|
|
618
|
+
no text yet (show "..."), non-empty string = partial/final transcript
|
|
619
|
+
- `agentUtterance`: `null` = agent is not speaking, non-empty string =
|
|
620
|
+
streaming response text (cleared when final `chat` message arrives)
|
|
621
|
+
- `disconnected`: `null` = connected, `{ intentional: true }` = user
|
|
622
|
+
disconnected, `{ intentional: false }` = unexpected disconnect (show
|
|
623
|
+
reconnect UI)
|
|
624
|
+
|
|
625
|
+
**UI Message type:** `{ role: "user" | "assistant"; text: string }`. Note: UI
|
|
626
|
+
messages use `text` (not `content`). The SDK `Message` type uses `content` — do
|
|
627
|
+
not mix them up in custom UIs.
|
|
628
|
+
|
|
593
629
|
### Showing tool calls in custom UI
|
|
594
630
|
|
|
595
631
|
```tsx
|
|
@@ -629,12 +665,11 @@ the parsed JSON result, handling deduplication internally.
|
|
|
629
665
|
```tsx
|
|
630
666
|
import "aai/ui/styles.css";
|
|
631
667
|
import { useState } from "preact/hooks";
|
|
632
|
-
import { ChatView,
|
|
668
|
+
import { ChatView, SidebarLayout, StartScreen, mount, useToolResult } from "aai/ui";
|
|
633
669
|
|
|
634
670
|
interface CartItem { id: number; name: string; price: number }
|
|
635
671
|
|
|
636
672
|
function ShopAgent() {
|
|
637
|
-
const { started, start } = useSession();
|
|
638
673
|
const [cart, setCart] = useState<CartItem[]>([]);
|
|
639
674
|
|
|
640
675
|
useToolResult((toolName, result: any) => {
|
|
@@ -651,16 +686,19 @@ function ShopAgent() {
|
|
|
651
686
|
}
|
|
652
687
|
});
|
|
653
688
|
|
|
654
|
-
|
|
689
|
+
const sidebar = (
|
|
690
|
+
<div class="p-4">
|
|
691
|
+
<h3 class="text-aai-text font-bold">Cart ({cart.length})</h3>
|
|
692
|
+
{cart.map((i) => <p key={i.id} class="text-aai-text text-sm">{i.name} — ${i.price}</p>)}
|
|
693
|
+
</div>
|
|
694
|
+
);
|
|
655
695
|
|
|
656
696
|
return (
|
|
657
|
-
<
|
|
658
|
-
<
|
|
659
|
-
<
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
<div class="flex-1"><ChatView /></div>
|
|
663
|
-
</div>
|
|
697
|
+
<StartScreen title="Shop" buttonText="Start Shopping">
|
|
698
|
+
<SidebarLayout sidebar={sidebar}>
|
|
699
|
+
<ChatView />
|
|
700
|
+
</SidebarLayout>
|
|
701
|
+
</StartScreen>
|
|
664
702
|
);
|
|
665
703
|
}
|
|
666
704
|
|
|
@@ -702,11 +740,15 @@ mount(App);
|
|
|
702
740
|
|
|
703
741
|
### Styling custom UIs
|
|
704
742
|
|
|
705
|
-
The framework uses **Tailwind CSS v4** (compiled at bundle time).
|
|
706
|
-
|
|
743
|
+
The framework uses **Tailwind CSS v4** (compiled at bundle time). Prefer
|
|
744
|
+
Tailwind classes over inline styles — all design tokens work as classes:
|
|
745
|
+
`bg-aai-surface` not `style={{ background: "var(--color-aai-surface)" }}`,
|
|
746
|
+
`border-t border-aai-border` not `style={{ borderTop: "1px solid var(--)" }}`.
|
|
707
747
|
|
|
708
|
-
|
|
709
|
-
|
|
748
|
+
Three approaches:
|
|
749
|
+
|
|
750
|
+
1. **Tailwind classes** — `class="flex items-center gap-2 bg-aai-surface"`
|
|
751
|
+
2. **Inline styles** — only for dynamic values (`style={{ width: pixels }}`)
|
|
710
752
|
3. **Injected `<style>` tags** — for keyframes, selectors, media queries:
|
|
711
753
|
|
|
712
754
|
```tsx
|
|
@@ -726,11 +768,65 @@ function App() {
|
|
|
726
768
|
}
|
|
727
769
|
```
|
|
728
770
|
|
|
729
|
-
**CSS custom properties
|
|
771
|
+
**Design tokens** — available as CSS custom properties and Tailwind classes
|
|
772
|
+
(e.g. `bg-aai-bg`, `text-aai-text-muted`, `rounded-aai`, `font-aai`):
|
|
773
|
+
|
|
774
|
+
| Token | Tailwind class | Default |
|
|
775
|
+
| ---------------------------- | ------------------------- | ------------------------- |
|
|
776
|
+
| `--color-aai-bg` | `bg-aai-bg` | `#101010` |
|
|
777
|
+
| `--color-aai-surface` | `bg-aai-surface` | `#151515` |
|
|
778
|
+
| `--color-aai-surface-faint` | `bg-aai-surface-faint` | `rgba(255,255,255,0.031)` |
|
|
779
|
+
| `--color-aai-surface-hover` | `bg-aai-surface-hover` | `rgba(255,255,255,0.059)` |
|
|
780
|
+
| `--color-aai-border` | `border-aai-border` | `#282828` |
|
|
781
|
+
| `--color-aai-primary` | `text-aai-primary` | `#fab283` |
|
|
782
|
+
| `--color-aai-text` | `text-aai-text` | `rgba(255,255,255,0.936)` |
|
|
783
|
+
| `--color-aai-text-secondary` | `text-aai-text-secondary` | `rgba(255,255,255,0.618)` |
|
|
784
|
+
| `--color-aai-text-muted` | `text-aai-text-muted` | `rgba(255,255,255,0.284)` |
|
|
785
|
+
| `--color-aai-text-dim` | `text-aai-text-dim` | `rgba(255,255,255,0.422)` |
|
|
786
|
+
| `--color-aai-error` | `text-aai-error` | `#e06c75` |
|
|
787
|
+
| `--color-aai-ring` | `ring-aai-ring` | `#56b6c2` |
|
|
788
|
+
| `--color-aai-state-{state}` | `text-aai-state-{state}` | per-state colors |
|
|
789
|
+
| `--radius-aai` | `rounded-aai` | `6px` |
|
|
790
|
+
| `--font-aai` | `font-aai` | Inter, sans-serif |
|
|
791
|
+
| `--font-aai-mono` | `font-aai-mono` | IBM Plex Mono, mono |
|
|
792
|
+
|
|
793
|
+
The 5 core colors (`bg`, `primary`, `text`, `surface`, `border`) can be
|
|
794
|
+
overridden via `mount()` theme options. All other tokens use fixed defaults.
|
|
795
|
+
|
|
796
|
+
### Common UI patterns
|
|
797
|
+
|
|
798
|
+
**Auto-scrolling messages** — use `useAutoScroll` for custom message lists:
|
|
799
|
+
|
|
800
|
+
```tsx
|
|
801
|
+
import { useAutoScroll, useSession } from "aai/ui";
|
|
802
|
+
|
|
803
|
+
function MyChat() {
|
|
804
|
+
const { session } = useSession();
|
|
805
|
+
const bottomRef = useAutoScroll();
|
|
730
806
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
807
|
+
return (
|
|
808
|
+
<div class="overflow-y-auto">
|
|
809
|
+
{session.messages.value.map((m, i) => <p key={i}>{m.text}</p>)}
|
|
810
|
+
<div ref={bottomRef} />
|
|
811
|
+
</div>
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
Note: `MessageList` and `ChatView` already include auto-scroll. Only use
|
|
817
|
+
`useAutoScroll` when building a fully custom message list.
|
|
818
|
+
|
|
819
|
+
**Reading signal values in render:** Extract `.value` once at the top of the
|
|
820
|
+
component to avoid redundant signal subscriptions:
|
|
821
|
+
|
|
822
|
+
```tsx
|
|
823
|
+
function MyComponent() {
|
|
824
|
+
const { session } = useSession();
|
|
825
|
+
const state = session.state.value;
|
|
826
|
+
const msgs = session.messages.value;
|
|
827
|
+
// Use `state` and `msgs` as plain values throughout the render
|
|
828
|
+
}
|
|
829
|
+
```
|
|
734
830
|
|
|
735
831
|
## Self-hosting with `createServer()`
|
|
736
832
|
|
|
@@ -782,41 +878,65 @@ Drug interactions (RxNorm):
|
|
|
782
878
|
|
|
783
879
|
Use `fetch_json` builtin tool or `fetch` in custom tools to call these.
|
|
784
880
|
|
|
785
|
-
##
|
|
881
|
+
## Custom start screen with `StartScreen`
|
|
786
882
|
|
|
787
|
-
|
|
883
|
+
Use `StartScreen` for a branded start card that transitions to `ChatView`:
|
|
788
884
|
|
|
789
885
|
```tsx
|
|
790
886
|
import "aai/ui/styles.css";
|
|
791
|
-
import { ChatView,
|
|
887
|
+
import { ChatView, StartScreen, mount } from "aai/ui";
|
|
792
888
|
|
|
793
889
|
function MyAgent() {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
<div class="flex flex-col items-center gap-6">
|
|
800
|
-
<h1 class="text-xl text-aai-text">My Agent</h1>
|
|
801
|
-
<button
|
|
802
|
-
class="px-8 py-3 rounded-aai bg-aai-primary text-white border-none cursor-pointer"
|
|
803
|
-
onClick={start}
|
|
804
|
-
>
|
|
805
|
-
Start
|
|
806
|
-
</button>
|
|
807
|
-
</div>
|
|
808
|
-
</div>
|
|
809
|
-
);
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
return <ChatView />;
|
|
890
|
+
return (
|
|
891
|
+
<StartScreen icon={<span>🎤</span>} title="My Agent" subtitle="Ask me anything">
|
|
892
|
+
<ChatView />
|
|
893
|
+
</StartScreen>
|
|
894
|
+
);
|
|
813
895
|
}
|
|
814
896
|
|
|
815
897
|
mount(MyAgent);
|
|
816
898
|
```
|
|
817
899
|
|
|
818
|
-
|
|
819
|
-
|
|
900
|
+
`StartScreen` handles the started/not-started transition automatically. Pass
|
|
901
|
+
`icon`, `title`, `subtitle`, and `buttonText` to customize the card.
|
|
902
|
+
|
|
903
|
+
## Sidebar layout with `SidebarLayout`
|
|
904
|
+
|
|
905
|
+
Use `SidebarLayout` for apps with a persistent side panel (cart, dashboard):
|
|
906
|
+
|
|
907
|
+
```tsx
|
|
908
|
+
import "aai/ui/styles.css";
|
|
909
|
+
import { useState } from "preact/hooks";
|
|
910
|
+
import { ChatView, SidebarLayout, StartScreen, mount, useToolResult } from "aai/ui";
|
|
911
|
+
|
|
912
|
+
function ShopAgent() {
|
|
913
|
+
const [cart, setCart] = useState<{ id: number; name: string }[]>([]);
|
|
914
|
+
|
|
915
|
+
useToolResult((toolName, result: any) => {
|
|
916
|
+
if (toolName === "add_item") setCart((prev) => [...prev, result.item]);
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
const sidebar = (
|
|
920
|
+
<div class="p-4">
|
|
921
|
+
<h3 class="text-aai-text font-bold">Cart ({cart.length})</h3>
|
|
922
|
+
{cart.map((i) => <p key={i.id} class="text-aai-text text-sm">{i.name}</p>)}
|
|
923
|
+
</div>
|
|
924
|
+
);
|
|
925
|
+
|
|
926
|
+
return (
|
|
927
|
+
<StartScreen title="Shop" buttonText="Start Shopping">
|
|
928
|
+
<SidebarLayout sidebar={sidebar}>
|
|
929
|
+
<ChatView />
|
|
930
|
+
</SidebarLayout>
|
|
931
|
+
</StartScreen>
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
mount(ShopAgent);
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
`SidebarLayout` accepts `width` (default `"20rem"`) and `side` (`"left"` or
|
|
939
|
+
`"right"`).
|
|
820
940
|
|
|
821
941
|
## Project structure
|
|
822
942
|
|
|
@@ -912,8 +1032,8 @@ Use directional words naturally: "To the north you see..." not "N: forest"
|
|
|
912
1032
|
- **Telling the agent to be verbose** — Voice responses should be 1-3 sentences.
|
|
913
1033
|
If your `instructions` say "provide detailed explanations", the agent will
|
|
914
1034
|
monologue. Instruct it to be brief and let the user ask follow-ups.
|
|
915
|
-
- **Not
|
|
916
|
-
|
|
1035
|
+
- **Not setting env vars before deploying** — If your agent needs custom env
|
|
1036
|
+
vars, set them with `aai env add MY_KEY` before deploying.
|
|
917
1037
|
- **Forgetting SSRF restrictions on `fetch`** — The host validates all proxied
|
|
918
1038
|
fetch URLs. Requests to private/internal IP addresses (localhost, 10.x,
|
|
919
1039
|
192.168.x, etc.) are blocked.
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
"lint:fix": "biome check --write ."
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@alexkroman1/aai": "*"
|
|
11
|
+
"@alexkroman1/aai": "*",
|
|
12
|
+
"@preact/signals": "^2.8.2",
|
|
13
|
+
"preact": "^10.29.0",
|
|
14
|
+
"tailwindcss": "^4.2.1"
|
|
12
15
|
},
|
|
13
16
|
"devDependencies": {
|
|
14
17
|
"@biomejs/biome": "^2",
|