@alexkroman1/aai 0.3.0

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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli.js +3436 -0
  3. package/package.json +78 -0
  4. package/sdk/_internal_types.ts +89 -0
  5. package/sdk/_mock_ws.ts +172 -0
  6. package/sdk/_timeout.ts +24 -0
  7. package/sdk/builtin_tools.ts +309 -0
  8. package/sdk/capnweb.ts +341 -0
  9. package/sdk/define_agent.ts +70 -0
  10. package/sdk/direct_executor.ts +195 -0
  11. package/sdk/kv.ts +183 -0
  12. package/sdk/mod.ts +35 -0
  13. package/sdk/protocol.ts +313 -0
  14. package/sdk/runtime.ts +65 -0
  15. package/sdk/s2s.ts +271 -0
  16. package/sdk/server.ts +198 -0
  17. package/sdk/session.ts +438 -0
  18. package/sdk/system_prompt.ts +47 -0
  19. package/sdk/types.ts +406 -0
  20. package/sdk/vector.ts +133 -0
  21. package/sdk/winterc_server.ts +141 -0
  22. package/sdk/worker_entry.ts +99 -0
  23. package/sdk/worker_shim.ts +170 -0
  24. package/sdk/ws_handler.ts +190 -0
  25. package/templates/_shared/.env.example +5 -0
  26. package/templates/_shared/package.json +17 -0
  27. package/templates/code-interpreter/agent.ts +27 -0
  28. package/templates/code-interpreter/client.tsx +2 -0
  29. package/templates/dispatch-center/agent.ts +1536 -0
  30. package/templates/dispatch-center/client.tsx +504 -0
  31. package/templates/embedded-assets/agent.ts +49 -0
  32. package/templates/embedded-assets/client.tsx +2 -0
  33. package/templates/embedded-assets/knowledge.json +20 -0
  34. package/templates/health-assistant/agent.ts +160 -0
  35. package/templates/health-assistant/client.tsx +2 -0
  36. package/templates/infocom-adventure/agent.ts +164 -0
  37. package/templates/infocom-adventure/client.tsx +299 -0
  38. package/templates/math-buddy/agent.ts +21 -0
  39. package/templates/math-buddy/client.tsx +2 -0
  40. package/templates/memory-agent/agent.ts +74 -0
  41. package/templates/memory-agent/client.tsx +2 -0
  42. package/templates/night-owl/agent.ts +98 -0
  43. package/templates/night-owl/client.tsx +28 -0
  44. package/templates/personal-finance/agent.ts +26 -0
  45. package/templates/personal-finance/client.tsx +2 -0
  46. package/templates/simple/agent.ts +6 -0
  47. package/templates/simple/client.tsx +2 -0
  48. package/templates/smart-research/agent.ts +164 -0
  49. package/templates/smart-research/client.tsx +2 -0
  50. package/templates/support/README.md +62 -0
  51. package/templates/support/agent.ts +19 -0
  52. package/templates/support/client.tsx +2 -0
  53. package/templates/travel-concierge/agent.ts +29 -0
  54. package/templates/travel-concierge/client.tsx +2 -0
  55. package/templates/web-researcher/agent.ts +17 -0
  56. package/templates/web-researcher/client.tsx +2 -0
  57. package/ui/_components/app.tsx +37 -0
  58. package/ui/_components/chat_view.tsx +36 -0
  59. package/ui/_components/controls.tsx +32 -0
  60. package/ui/_components/error_banner.tsx +18 -0
  61. package/ui/_components/message_bubble.tsx +21 -0
  62. package/ui/_components/message_list.tsx +61 -0
  63. package/ui/_components/state_indicator.tsx +17 -0
  64. package/ui/_components/thinking_indicator.tsx +19 -0
  65. package/ui/_components/tool_call_block.tsx +110 -0
  66. package/ui/_components/tool_icons.tsx +101 -0
  67. package/ui/_components/transcript.tsx +20 -0
  68. package/ui/audio.ts +170 -0
  69. package/ui/components.ts +49 -0
  70. package/ui/components_mod.ts +37 -0
  71. package/ui/mod.ts +48 -0
  72. package/ui/mount.tsx +112 -0
  73. package/ui/mount_context.ts +19 -0
  74. package/ui/session.ts +456 -0
  75. package/ui/session_mod.ts +27 -0
  76. package/ui/signals.ts +111 -0
  77. package/ui/types.ts +50 -0
  78. package/ui/worklets/capture-processor.js +62 -0
  79. package/ui/worklets/playback-processor.js +110 -0
@@ -0,0 +1,504 @@
1
+ import { mount, useSession } from "@alexkroman1/aai/ui";
2
+ import type { Message } from "@alexkroman1/aai/ui";
3
+ import { useEffect, useRef } from "preact/hooks";
4
+
5
+ const CSS = `
6
+ @keyframes dc-pulse {
7
+ 0%, 100% { opacity: 1; }
8
+ 50% { opacity: 0.5; }
9
+ }
10
+ @keyframes dc-slide-in {
11
+ from { transform: translateY(10px); opacity: 0; }
12
+ to { transform: translateY(0); opacity: 1; }
13
+ }
14
+ .dc-messages::-webkit-scrollbar { width: 6px; }
15
+ .dc-messages::-webkit-scrollbar-track { background: transparent; }
16
+ .dc-messages::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
17
+ .dc-sidebar::-webkit-scrollbar { width: 6px; }
18
+ .dc-sidebar::-webkit-scrollbar-track { background: transparent; }
19
+ .dc-sidebar::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
20
+ @media (max-width: 900px) {
21
+ .dc-main { grid-template-columns: 1fr !important; grid-template-rows: auto 1fr !important; }
22
+ }
23
+ `;
24
+
25
+ const alertColors: Record<string, string> = {
26
+ green: "#22c55e",
27
+ yellow: "#eab308",
28
+ orange: "#f97316",
29
+ red: "#ef4444",
30
+ };
31
+
32
+ const severityColors: Record<string, string> = {
33
+ critical: "#ef4444",
34
+ urgent: "#f97316",
35
+ moderate: "#eab308",
36
+ minor: "#22c55e",
37
+ };
38
+
39
+ const statusColors: Record<string, string> = {
40
+ incoming: "#818cf8",
41
+ triaged: "#a78bfa",
42
+ dispatched: "#f59e0b",
43
+ "en_route": "#3b82f6",
44
+ "on_scene": "#22c55e",
45
+ resolved: "#6b7280",
46
+ escalated: "#ef4444",
47
+ };
48
+
49
+ interface Incident {
50
+ id: string;
51
+ mentioned: number;
52
+ severity?: string;
53
+ status?: string;
54
+ location?: string;
55
+ }
56
+
57
+ function extractIncidents(
58
+ messages: { role: string; text: string }[],
59
+ ): Map<string, Incident> {
60
+ const incidents = new Map<string, Incident>();
61
+ for (const msg of messages) {
62
+ const incMatches = msg.text.matchAll(/INC-\d{4}/g);
63
+ for (const m of incMatches) {
64
+ const id = m[0];
65
+ if (!incidents.has(id)) {
66
+ incidents.set(id, { id, mentioned: 0 });
67
+ }
68
+ incidents.get(id)!.mentioned++;
69
+ }
70
+
71
+ const lines = msg.text.split("\n");
72
+ for (const line of lines) {
73
+ const idMatch = line.match(/INC-\d{4}/);
74
+ if (!idMatch) continue;
75
+ const id = idMatch[0];
76
+ const inc = incidents.get(id) || { id, mentioned: 0 };
77
+
78
+ for (const sev of ["critical", "urgent", "moderate", "minor"]) {
79
+ if (line.toLowerCase().includes(sev)) inc.severity = sev;
80
+ }
81
+ for (
82
+ const st of [
83
+ "incoming",
84
+ "triaged",
85
+ "dispatched",
86
+ "en_route",
87
+ "on_scene",
88
+ "resolved",
89
+ "escalated",
90
+ ]
91
+ ) {
92
+ if (
93
+ line.toLowerCase().includes(st.replace("_", " ")) ||
94
+ line.toLowerCase().includes(st)
95
+ ) inc.status = st;
96
+ }
97
+ const locMatch = line.match(/(?:at|to|location:?)\s+([^,.\n]{5,50})/i);
98
+ if (locMatch) inc.location = locMatch[1]!.trim();
99
+
100
+ incidents.set(id, inc);
101
+ }
102
+ }
103
+ return incidents;
104
+ }
105
+
106
+ function extractAlertLevel(
107
+ messages: { role: string; text: string }[],
108
+ ): string {
109
+ let level = "green";
110
+ for (const msg of messages) {
111
+ const match = msg.text.match(/alert level[:\s]+(\w+)/i);
112
+ if (match) level = match[1]!.toLowerCase();
113
+ if (
114
+ msg.text.includes("alert level is red") ||
115
+ msg.text.includes("ALERT: RED")
116
+ ) level = "red";
117
+ if (msg.text.includes("alert level is orange")) level = "orange";
118
+ if (msg.text.includes("alert level is yellow")) level = "yellow";
119
+ }
120
+ return level;
121
+ }
122
+
123
+ function stateColor(state: string): string {
124
+ return state === "listening"
125
+ ? "#22c55e"
126
+ : state === "thinking"
127
+ ? "#eab308"
128
+ : state === "speaking"
129
+ ? "#3b82f6"
130
+ : state === "ready"
131
+ ? "#22c55e"
132
+ : state === "error"
133
+ ? "#ef4444"
134
+ : "#6b7280";
135
+ }
136
+
137
+ function Panel(
138
+ { title, children }: { title: string; children: preact.ComponentChildren },
139
+ ) {
140
+ return (
141
+ <div
142
+ class="rounded-lg p-3"
143
+ style={{ background: "#1a1a2e", border: "1px solid #1e293b" }}
144
+ >
145
+ <div
146
+ class="text-[10px] font-bold uppercase tracking-[1.5px] mb-2.5"
147
+ style={{ color: "#64748b" }}
148
+ >
149
+ {title}
150
+ </div>
151
+ {children}
152
+ </div>
153
+ );
154
+ }
155
+
156
+ function StatRow(
157
+ { label, value, color }: { label: string; value: number; color?: string },
158
+ ) {
159
+ return (
160
+ <div class="flex justify-between items-center py-1 text-xs">
161
+ <span style={{ color: "#94a3b8" }}>{label}</span>
162
+ <span class="font-bold" style={{ color: color || "#e2e8f0" }}>
163
+ {value}
164
+ </span>
165
+ </div>
166
+ );
167
+ }
168
+
169
+ function App() {
170
+ const ctrl = useSession();
171
+ const { session } = ctrl;
172
+ const msgs = session.messages.value;
173
+ const tx = session.userUtterance.value;
174
+ const state = session.state.value;
175
+ const error = session.error.value;
176
+ const messagesEndRef = useRef<HTMLDivElement>(null);
177
+
178
+ const incidents = extractIncidents(msgs);
179
+ const alertLevel = extractAlertLevel(msgs);
180
+
181
+ useEffect(() => {
182
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
183
+ }, [msgs.length]);
184
+
185
+ const incidentList = Array.from(incidents.values()).reverse();
186
+ const activeIncidents = incidentList.filter((i) => i.status !== "resolved");
187
+ const resolvedCount = incidentList.filter((i) => i.status === "resolved")
188
+ .length;
189
+
190
+ const alertBg = alertColors[alertLevel] || "#6b7280";
191
+ const alertTextColor = alertLevel === "yellow" ? "#000" : "#fff";
192
+
193
+ return (
194
+ <>
195
+ <style>{CSS}</style>
196
+ <div
197
+ class="flex flex-col min-h-screen m-0 p-0 font-mono"
198
+ style={{ background: "#0a0a0f", color: "#e2e8f0" }}
199
+ >
200
+ {/* Header */}
201
+ <div
202
+ class="flex items-center justify-between px-6 py-4 gap-4 flex-wrap shrink-0"
203
+ style={{
204
+ background: "linear-gradient(135deg, #1a1a2e, #16213e)",
205
+ borderBottom: "1px solid #1e293b",
206
+ }}
207
+ >
208
+ <div
209
+ class="flex items-center gap-2.5 text-lg font-bold uppercase tracking-wider"
210
+ style={{ color: "#f1f5f9" }}
211
+ >
212
+ <span style={{ color: "#3b82f6" }}>&#9670;</span>
213
+ Dispatch Command Center
214
+ <span
215
+ class="w-2.5 h-2.5 rounded-full inline-block"
216
+ style={{
217
+ background: stateColor(state),
218
+ animation: state === "listening"
219
+ ? "dc-pulse 1.5s ease-in-out infinite"
220
+ : state === "thinking"
221
+ ? "dc-pulse 0.8s ease-in-out infinite"
222
+ : "none",
223
+ }}
224
+ title={state}
225
+ />
226
+ <span
227
+ class="text-[11px] font-normal normal-case"
228
+ style={{ color: "#64748b" }}
229
+ >
230
+ {state === "listening"
231
+ ? "LISTENING"
232
+ : state === "thinking"
233
+ ? "PROCESSING"
234
+ : state === "speaking"
235
+ ? "TRANSMITTING"
236
+ : state.toUpperCase()}
237
+ </span>
238
+ </div>
239
+ <div class="flex gap-2 items-center">
240
+ <span
241
+ class="text-[10px] tracking-wider"
242
+ style={{ color: "#64748b" }}
243
+ >
244
+ SYSTEM ALERT:
245
+ </span>
246
+ <span
247
+ class="px-3 py-1 rounded text-[11px] font-bold uppercase tracking-wider"
248
+ style={{
249
+ background: alertBg,
250
+ color: alertTextColor,
251
+ animation: alertLevel === "red"
252
+ ? "dc-pulse 1s ease-in-out infinite"
253
+ : "none",
254
+ }}
255
+ >
256
+ {alertLevel.toUpperCase()}
257
+ </span>
258
+ </div>
259
+ </div>
260
+
261
+ {/* Main content */}
262
+ <div
263
+ class="dc-main flex-1 grid overflow-hidden"
264
+ style={{ gridTemplateColumns: "1fr 320px" }}
265
+ >
266
+ {/* Left: conversation feed */}
267
+ <div
268
+ class="flex flex-col overflow-hidden"
269
+ style={{ borderRight: "1px solid #1e293b" }}
270
+ >
271
+ <div class="dc-messages flex-1 overflow-y-auto p-4 flex flex-col gap-2">
272
+ {msgs.length === 0 && (
273
+ <div
274
+ class="text-center p-10 text-[13px]"
275
+ style={{ color: "#475569" }}
276
+ >
277
+ Dispatch Command Center standing by. Click START to begin
278
+ operations.
279
+ </div>
280
+ )}
281
+ {msgs.map((m: Message, i: number) => (
282
+ <div
283
+ key={i}
284
+ class="rounded-lg text-[13px] max-w-[85%] px-3.5 py-2.5"
285
+ style={{
286
+ lineHeight: 1.6,
287
+ alignSelf: m.role === "assistant"
288
+ ? "flex-start"
289
+ : "flex-end",
290
+ background: m.role === "assistant" ? "#1e293b" : "#172554",
291
+ animation: "dc-slide-in 0.2s ease-out",
292
+ borderLeft: m.role === "assistant"
293
+ ? "3px solid #3b82f6"
294
+ : "none",
295
+ borderRight: m.role !== "assistant"
296
+ ? "3px solid #22d3ee"
297
+ : "none",
298
+ }}
299
+ >
300
+ <div
301
+ class="text-[10px] uppercase tracking-wider mb-1"
302
+ style={{ color: "#64748b" }}
303
+ >
304
+ {m.role === "assistant" ? "DISPATCH" : "OPERATOR"}
305
+ </div>
306
+ {m.text}
307
+ </div>
308
+ ))}
309
+ <div ref={messagesEndRef} />
310
+ </div>
311
+
312
+ {tx !== null && (
313
+ <div
314
+ class="flex items-center px-4 py-2 text-xs italic min-h-8"
315
+ style={{
316
+ background: "#111827",
317
+ borderTop: "1px solid #1e293b",
318
+ color: "#64748b",
319
+ }}
320
+ >
321
+ <span
322
+ class="w-2.5 h-2.5 rounded-full inline-block mr-2"
323
+ style={{
324
+ background: "#22c55e",
325
+ animation: "dc-pulse 1.5s ease-in-out infinite",
326
+ }}
327
+ />
328
+ {tx || "..."}
329
+ </div>
330
+ )}
331
+ {error && (
332
+ <div
333
+ class="px-4 py-2 text-xs"
334
+ style={{
335
+ background: "#450a0a",
336
+ color: "#fca5a5",
337
+ borderTop: "1px solid #991b1b",
338
+ }}
339
+ >
340
+ ERROR: {error.message} ({error.code})
341
+ </div>
342
+ )}
343
+
344
+ <div
345
+ class="flex items-center gap-2.5 px-4 py-3"
346
+ style={{ background: "#111827", borderTop: "1px solid #1e293b" }}
347
+ >
348
+ {!ctrl.started.value
349
+ ? (
350
+ <button
351
+ type="button"
352
+ class="px-4 py-2 border-none rounded-md font-mono text-xs font-semibold uppercase tracking-wider cursor-pointer text-white"
353
+ style={{ background: "#2563eb" }}
354
+ onClick={() => ctrl.start()}
355
+ >
356
+ Start Dispatch
357
+ </button>
358
+ )
359
+ : (
360
+ <>
361
+ <button
362
+ type="button"
363
+ class="px-4 py-2 border-none rounded-md font-mono text-xs font-semibold uppercase tracking-wider cursor-pointer"
364
+ style={{
365
+ background: ctrl.running.value ? "#334155" : "#2563eb",
366
+ color: ctrl.running.value ? "#e2e8f0" : "white",
367
+ }}
368
+ onClick={() => ctrl.toggle()}
369
+ >
370
+ {ctrl.running.value ? "Pause" : "Resume"}
371
+ </button>
372
+ <button
373
+ type="button"
374
+ class="px-4 py-2 border-none rounded-md font-mono text-xs font-semibold uppercase tracking-wider cursor-pointer text-white"
375
+ style={{ background: "#dc2626" }}
376
+ onClick={() => ctrl.reset()}
377
+ >
378
+ Reset
379
+ </button>
380
+ </>
381
+ )}
382
+ <div class="flex-1" />
383
+ <span class="text-[10px]" style={{ color: "#475569" }}>
384
+ {incidentList.length} incident
385
+ {incidentList.length !== 1 ? "s" : ""} logged
386
+ </span>
387
+ </div>
388
+ </div>
389
+
390
+ {/* Right: sidebar dashboard */}
391
+ <div
392
+ class="dc-sidebar overflow-y-auto p-4 flex flex-col gap-4"
393
+ style={{ background: "#111827" }}
394
+ >
395
+ <Panel title="Operations Summary">
396
+ <StatRow
397
+ label="Active Incidents"
398
+ value={activeIncidents.length}
399
+ color={activeIncidents.length > 3 ? "#ef4444" : "#e2e8f0"}
400
+ />
401
+ <StatRow label="Resolved" value={resolvedCount} color="#22c55e" />
402
+ <StatRow label="Total Logged" value={incidentList.length} />
403
+ </Panel>
404
+
405
+ <Panel title="Active Incidents">
406
+ {activeIncidents.length === 0
407
+ ? (
408
+ <div
409
+ class="text-xs text-center py-2"
410
+ style={{ color: "#475569" }}
411
+ >
412
+ No active incidents
413
+ </div>
414
+ )
415
+ : activeIncidents.map((inc) => (
416
+ <div
417
+ key={inc.id}
418
+ class="rounded-md p-2.5 mb-2"
419
+ style={{
420
+ background: "#0f172a",
421
+ animation: "dc-slide-in 0.3s ease-out",
422
+ border: `1px solid ${
423
+ severityColors[inc.severity ?? ""] || "#334155"
424
+ }40`,
425
+ borderLeft: `3px solid ${
426
+ severityColors[inc.severity ?? ""] || "#334155"
427
+ }`,
428
+ }}
429
+ >
430
+ <div class="flex justify-between items-center mb-1">
431
+ <span
432
+ class="text-xs font-bold"
433
+ style={{ color: "#f1f5f9" }}
434
+ >
435
+ {inc.id}
436
+ </span>
437
+ {inc.severity && (
438
+ <span
439
+ class="text-[9px] px-1.5 py-0.5 rounded font-bold uppercase"
440
+ style={{
441
+ background: `${
442
+ severityColors[inc.severity ?? ""]
443
+ }30`,
444
+ color: severityColors[inc.severity ?? ""],
445
+ }}
446
+ >
447
+ {inc.severity}
448
+ </span>
449
+ )}
450
+ </div>
451
+ {inc.location && (
452
+ <div
453
+ class="text-[11px] mb-0.5"
454
+ style={{ color: "#94a3b8" }}
455
+ >
456
+ {inc.location}
457
+ </div>
458
+ )}
459
+ {inc.status && (
460
+ <div
461
+ class="text-[10px] uppercase tracking-wider"
462
+ style={{ color: statusColors[inc.status] || "#6b7280" }}
463
+ >
464
+ {inc.status.replace("_", " ")}
465
+ </div>
466
+ )}
467
+ </div>
468
+ ))}
469
+ </Panel>
470
+
471
+ <Panel title="Severity Legend">
472
+ {Object.entries(severityColors).map(([sev, color]) => (
473
+ <div key={sev} class="flex items-center gap-2 py-0.5">
474
+ <span
475
+ class="w-2.5 h-2.5 rounded-sm"
476
+ style={{ background: color }}
477
+ />
478
+ <span
479
+ class="text-[11px] capitalize"
480
+ style={{ color: "#94a3b8" }}
481
+ >
482
+ {sev}
483
+ </span>
484
+ </div>
485
+ ))}
486
+ </Panel>
487
+
488
+ <Panel title="Training Scenarios">
489
+ <div
490
+ class="text-[11px] leading-relaxed"
491
+ style={{ color: "#64748b" }}
492
+ >
493
+ Say "run mass casualty scenario" or "simulate active shooter" to
494
+ test dispatch operations with complex multi-incident drills.
495
+ </div>
496
+ </Panel>
497
+ </div>
498
+ </div>
499
+ </div>
500
+ </>
501
+ );
502
+ }
503
+
504
+ mount(App);
@@ -0,0 +1,49 @@
1
+ import { defineAgent } from "@alexkroman1/aai";
2
+ import { z } from "zod";
3
+ import knowledge from "./knowledge.json" with { type: "json" };
4
+
5
+ type FaqEntry = { question: string; answer: string };
6
+ const faqs: FaqEntry[] = knowledge.faqs;
7
+
8
+ export default defineAgent({
9
+ name: "FAQ Bot",
10
+ instructions:
11
+ `You are a friendly FAQ assistant. Answer questions using ONLY the information \
12
+ from your embedded knowledge base. If the user asks something not covered by your \
13
+ knowledge base, say you don't have that information and suggest they check the official \
14
+ documentation.
15
+
16
+ Rules:
17
+ - Keep answers concise and conversational — this is a voice agent
18
+ - Quote the knowledge base accurately, do not embellish
19
+ - If a question is ambiguous, ask the user to clarify
20
+ - Use 'search_knowledge' to find answers to specific questions
21
+ - Use 'list_topics' to see all available FAQ topics
22
+ - Always be helpful and polite`,
23
+ greeting:
24
+ "Hi! I'm your FAQ assistant. Ask me anything about the AAI agent framework and I'll look it up in my knowledge base.",
25
+ voice: "156fb8d2-335b-4950-9cb3-a2d33befec77", // Helpful Woman
26
+ tools: {
27
+ search_knowledge: {
28
+ description:
29
+ "Search the embedded FAQ knowledge base for an answer matching the user's question.",
30
+ parameters: z.object({
31
+ query: z.string().describe("The user's question to search for"),
32
+ }),
33
+ execute: ({ query }) => {
34
+ const q = query.toLowerCase();
35
+ const match = faqs.find((f) =>
36
+ f.question.toLowerCase().includes(q) ||
37
+ q.includes(f.question.toLowerCase()) ||
38
+ f.answer.toLowerCase().includes(q)
39
+ );
40
+ return match ?? { result: "No matching FAQ found." };
41
+ },
42
+ },
43
+ list_topics: {
44
+ description:
45
+ "List all available topics in the embedded FAQ knowledge base.",
46
+ execute: () => faqs.map((f) => f.question),
47
+ },
48
+ },
49
+ });
@@ -0,0 +1,2 @@
1
+ import { App, mount } from "@alexkroman1/aai/ui";
2
+ mount(App);
@@ -0,0 +1,20 @@
1
+ {
2
+ "faqs": [
3
+ {
4
+ "question": "What is AAI?",
5
+ "answer": "AAI is a framework for building voice-powered AI agents that run on Deno."
6
+ },
7
+ {
8
+ "question": "How do agents handle tools?",
9
+ "answer": "Agents define tools with a schema and handler function. The LLM decides when to call them based on the conversation."
10
+ },
11
+ {
12
+ "question": "What speech providers are supported?",
13
+ "answer": "AAI supports AssemblyAI for speech-to-text and text-to-speech."
14
+ },
15
+ {
16
+ "question": "Can agents access the internet?",
17
+ "answer": "Yes, agents run with network access and can make HTTP requests via the fetch API or built-in tools like web_search."
18
+ }
19
+ ]
20
+ }