@copilotz/chat-adapter 0.1.0 → 0.1.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/README.md CHANGED
@@ -1,27 +1,537 @@
1
1
  # @copilotz/chat-adapter
2
2
 
3
- Copilotz API adapter and ready-to-use `CopilotzChat` wrapper for the chat UI.
3
+ **From zero to agentic chat in one component.**
4
4
 
5
- ## Install
5
+ You built your AI with [Copilotz](https://github.com/copilotzhq/ts-lib). Now you need a frontend. You could wire up SSE streaming, thread persistence, tool call status updates, asset resolution, audio conversion, and context management. Or you could ship today.
6
+
7
+ [![npm](https://img.shields.io/npm/v/@copilotz/chat-adapter)](https://www.npmjs.com/package/@copilotz/chat-adapter)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178c6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
9
+ [![React](https://img.shields.io/badge/React-18+-61dafb?logo=react&logoColor=white)](https://react.dev/)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
11
+
12
+ ---
13
+
14
+ ## The Problem
15
+
16
+ Your Copilotz backend handles memory, RAG, tool calling, and multi-tenancy. Now you need to connect it to a chat interface.
17
+
18
+ You could:
19
+ - Set up SSE streaming and parse token events
20
+ - Manage thread state and sync with the server
21
+ - Track tool call status in real-time (pending → running → completed)
22
+ - Convert browser-recorded audio to a format the API accepts
23
+ - Resolve `asset://` references to displayable data URLs
24
+ - Handle optimistic updates for instant feedback
25
+ - Wire up user context and memory sync
26
+
27
+ Or you could use one component.
28
+
29
+ ## The Solution
30
+
31
+ `@copilotz/chat-adapter` is the official frontend binding for Copilotz. It wraps `@copilotz/chat-ui` with full API integration:
32
+
33
+ | What You'd Build | What This Gives You |
34
+ |------------------|---------------------|
35
+ | SSE parsing | Token-by-token streaming with cursor animation |
36
+ | Thread sync | Automatic fetch, create, rename, archive, delete |
37
+ | Tool status | Real-time updates as tools run |
38
+ | Audio handling | WebM/Opus → WAV conversion for API compatibility |
39
+ | Asset resolution | `asset://` refs → data URLs automatically |
40
+ | Context management | Shared user context across components |
41
+ | Error handling | Graceful fallbacks and retry logic |
42
+
43
+ **One component. Full Copilotz integration.**
44
+
45
+ ---
46
+
47
+ ## Quick Start
6
48
 
7
49
  ```bash
8
50
  npm install @copilotz/chat-adapter
9
51
  ```
10
52
 
11
- ## Usage
53
+ Import the styles (from the UI package):
54
+
55
+ ```tsx
56
+ import '@copilotz/chat-ui/styles.css';
57
+ ```
58
+
59
+ Drop in the component:
60
+
61
+ ```tsx
62
+ import { CopilotzChat } from '@copilotz/chat-adapter';
63
+
64
+ function App() {
65
+ return (
66
+ <CopilotzChat
67
+ userId="user-123"
68
+ userName="Alex"
69
+ config={{
70
+ branding: {
71
+ title: 'Acme Assistant',
72
+ subtitle: 'How can I help you today?',
73
+ },
74
+ }}
75
+ />
76
+ );
77
+ }
78
+ ```
79
+
80
+ That's it. You have a production chat interface connected to your Copilotz backend.
81
+
82
+ ---
83
+
84
+ ## Environment Variables
85
+
86
+ Configure the API connection:
87
+
88
+ ```bash
89
+ # Base URL for the Copilotz API (default: /api)
90
+ VITE_API_URL=https://api.example.com
91
+
92
+ # Optional: API key for authenticated requests
93
+ VITE_API_KEY=your-api-key
94
+ # or
95
+ VITE_COPILOTZ_API_KEY=your-api-key
96
+ ```
97
+
98
+ ---
99
+
100
+ ## Features
101
+
102
+ ### Real-Time Streaming
103
+
104
+ Messages stream token-by-token with a thinking indicator while waiting for the first token. No configuration needed — it just works.
105
+
106
+ ```tsx
107
+ <CopilotzChat userId="user-123" />
108
+ ```
109
+
110
+ ### Tool Calls with Live Status
111
+
112
+ When your agent calls tools, the UI shows real-time status updates:
113
+
114
+ 1. **Pending** — Tool call received
115
+ 2. **Running** — Tool is executing
116
+ 3. **Completed/Failed** — Result displayed with execution time
117
+
118
+ All automatic. Just enable tool display in config:
119
+
120
+ ```tsx
121
+ <CopilotzChat
122
+ userId="user-123"
123
+ config={{
124
+ features: { enableToolCallsDisplay: true },
125
+ }}
126
+ />
127
+ ```
128
+
129
+ ### Bootstrap Conversations
130
+
131
+ Start conversations with an initial message or trigger tool calls immediately:
132
+
133
+ ```tsx
134
+ <CopilotzChat
135
+ userId="user-123"
136
+ bootstrap={{
137
+ initialMessage: "Hello! I'm looking for help with my order.",
138
+ initialToolCalls: [
139
+ { name: 'get_user_orders', args: { limit: 5 } },
140
+ ],
141
+ }}
142
+ />
143
+ ```
144
+
145
+ ### Audio Recording
146
+
147
+ Record voice messages directly in the chat. The adapter automatically converts browser-recorded audio (WebM/Opus) to WAV format for API compatibility.
148
+
149
+ ### Asset Resolution
150
+
151
+ When your agent generates images or files, they're stored as `asset://` references. The adapter automatically resolves these to displayable data URLs.
152
+
153
+ ### User Context
154
+
155
+ Share context across your app and sync with the Copilotz backend:
156
+
157
+ ```tsx
158
+ <CopilotzChat
159
+ userId="user-123"
160
+ initialContext={{
161
+ profile: { subscription: 'pro', preferences: { theme: 'dark' } },
162
+ customFields: [
163
+ { key: 'company', label: 'Company', value: 'Acme Corp' },
164
+ ],
165
+ }}
166
+ onToolOutput={(output) => {
167
+ // React to tool outputs (e.g., context updates)
168
+ console.log('Tool output:', output);
169
+ }}
170
+ />
171
+ ```
172
+
173
+ ### Agent Selector
174
+
175
+ Switch between multiple agents with a ChatGPT-style dropdown. The selected agent's name is passed to the Copilotz backend via `preferredAgentName`.
176
+
177
+ ```tsx
178
+ const [selectedAgent, setSelectedAgent] = useState('assistant');
179
+
180
+ const agents = [
181
+ { id: 'assistant', name: 'Assistant', description: 'General purpose helper' },
182
+ { id: 'coder', name: 'Code Expert', description: 'Specialized in programming' },
183
+ ];
184
+
185
+ <CopilotzChat
186
+ userId="user-123"
187
+ agentOptions={agents}
188
+ selectedAgentId={selectedAgent}
189
+ onSelectAgent={setSelectedAgent}
190
+ />
191
+ ```
192
+
193
+ ### URL State Sync
194
+
195
+ Persist chat state in URL parameters for shareable links, bookmarks, and deep linking from external sources.
196
+
197
+ **Features:**
198
+ - `?thread=abc123` — Open a specific conversation
199
+ - `?agent=support-bot` — Pre-select an agent
200
+ - `?prompt=Hello` — Pre-fill or auto-send a message
201
+
202
+ ```tsx
203
+ // Basic usage - enable URL sync
204
+ <CopilotzChat
205
+ userId="user-123"
206
+ urlSync={{ enabled: true }}
207
+ />
208
+
209
+ // With custom parameter names
210
+ <CopilotzChat
211
+ userId="user-123"
212
+ urlSync={{
213
+ enabled: true,
214
+ params: { thread: 't', agent: 'a', prompt: 'q' },
215
+ }}
216
+ />
217
+
218
+ // Auto-send the prompt instead of pre-filling
219
+ <CopilotzChat
220
+ userId="user-123"
221
+ urlSync={{
222
+ enabled: true,
223
+ promptBehavior: 'auto-send',
224
+ }}
225
+ />
226
+ ```
227
+
228
+ **URL Sync Options:**
229
+
230
+ | Option | Type | Default | Description |
231
+ |--------|------|---------|-------------|
232
+ | `enabled` | `boolean` | `true` | Enable/disable URL sync |
233
+ | `mode` | `'push' \| 'replace' \| 'read-only'` | `'replace'` | How to update URL |
234
+ | `params.thread` | `string` | `'thread'` | URL param name for thread ID |
235
+ | `params.agent` | `string` | `'agent'` | URL param name for agent ID |
236
+ | `params.prompt` | `string` | `'prompt'` | URL param name for initial prompt |
237
+ | `promptBehavior` | `'prefill' \| 'auto-send'` | `'prefill'` | How to handle the prompt param |
238
+ | `clearPromptAfterRead` | `boolean` | `true` | Remove prompt from URL after use |
239
+
240
+ **Example URLs:**
241
+ ```
242
+ /chat?thread=abc123 # Open specific thread
243
+ /chat?agent=support-bot # Pre-select agent
244
+ /chat?prompt=How%20do%20I%20reset... # Pre-fill message
245
+ /chat?thread=abc123&agent=support&prompt=Hi # Combined
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Props Reference
251
+
252
+ ### CopilotzChat
253
+
254
+ | Prop | Type | Description |
255
+ |------|------|-------------|
256
+ | `userId` | `string` | Required. User identifier for thread filtering |
257
+ | `userName` | `string` | Display name for the user |
258
+ | `userAvatar` | `string` | URL to user's avatar image |
259
+ | `userEmail` | `string` | User's email address |
260
+ | `initialContext` | `ChatUserContext` | Initial context to seed the conversation |
261
+ | `bootstrap` | `{ initialMessage?, initialToolCalls? }` | Start conversation with message/tools |
262
+ | `config` | `ChatConfig` | UI configuration (same as `@copilotz/chat-ui`) |
263
+ | `callbacks` | `Partial<ChatCallbacks>` | Additional event handlers |
264
+ | `customComponent` | `ReactNode \| Function` | Custom right sidebar panel |
265
+ | `onToolOutput` | `(output) => void` | Called when a tool produces output |
266
+ | `onLogout` | `() => void` | Called when user clicks logout |
267
+ | `onViewProfile` | `() => void` | Called when user clicks view profile |
268
+ | `onAddMemory` | `(content, category?) => void` | Called when user adds a memory |
269
+ | `onUpdateMemory` | `(memoryId, content) => void` | Called when user updates a memory |
270
+ | `onDeleteMemory` | `(memoryId) => void` | Called when user deletes a memory |
271
+ | `suggestions` | `string[]` | Suggested prompts shown when no messages |
272
+ | `agentOptions` | `AgentOption[]` | Available agents for the selector dropdown |
273
+ | `selectedAgentId` | `string \| null` | Currently selected agent ID |
274
+ | `onSelectAgent` | `(agentId: string) => void` | Called when user selects an agent |
275
+ | `urlSync` | `UrlSyncConfig` | URL state synchronization config (see URL State Sync section) |
276
+ | `className` | `string` | Additional CSS classes |
277
+
278
+ ---
279
+
280
+ ## Hooks
281
+
282
+ ### useCopilotz
283
+
284
+ For custom integrations, use the hook directly:
285
+
286
+ ```tsx
287
+ import { useCopilotz } from '@copilotz/chat-adapter';
288
+
289
+ function CustomChat() {
290
+ const {
291
+ messages,
292
+ threads,
293
+ currentThreadId,
294
+ isStreaming,
295
+ userContextSeed,
296
+ sendMessage,
297
+ createThread,
298
+ selectThread,
299
+ renameThread,
300
+ archiveThread,
301
+ deleteThread,
302
+ stopGeneration,
303
+ reset,
304
+ } = useCopilotz({
305
+ userId: 'user-123',
306
+ initialContext: { /* ... */ },
307
+ bootstrap: { initialMessage: 'Hello!' },
308
+ defaultThreadName: 'Support Chat',
309
+ onToolOutput: (output) => console.log(output),
310
+ });
311
+
312
+ return (
313
+ <div>
314
+ {messages.map((msg) => (
315
+ <div key={msg.id}>{msg.content}</div>
316
+ ))}
317
+ <button onClick={() => sendMessage('Hello!')}>Send</button>
318
+ </div>
319
+ );
320
+ }
321
+ ```
322
+
323
+ ### Hook Options
324
+
325
+ | Option | Type | Description |
326
+ |--------|------|-------------|
327
+ | `userId` | `string \| null` | User identifier (null resets state) |
328
+ | `initialContext` | `ChatUserContext` | Initial context seed |
329
+ | `bootstrap` | `{ initialMessage?, initialToolCalls? }` | Auto-start conversation |
330
+ | `defaultThreadName` | `string` | Name for bootstrap thread |
331
+ | `onToolOutput` | `(output) => void` | Tool output callback |
332
+ | `preferredAgentName` | `string \| null` | Agent name to use for requests |
333
+ | `urlSync` | `UrlSyncConfig` | URL state synchronization config |
334
+
335
+ ### Hook Returns
336
+
337
+ | Property | Type | Description |
338
+ |----------|------|-------------|
339
+ | `messages` | `ChatMessage[]` | Current thread messages |
340
+ | `threads` | `ChatThread[]` | All user threads |
341
+ | `currentThreadId` | `string \| null` | Selected thread ID |
342
+ | `isStreaming` | `boolean` | Whether response is streaming |
343
+ | `userContextSeed` | `ChatUserContext` | Current user context |
344
+ | `sendMessage` | `(content, attachments?) => Promise` | Send a message |
345
+ | `createThread` | `(title?) => void` | Create new thread |
346
+ | `selectThread` | `(threadId) => Promise` | Switch threads |
347
+ | `renameThread` | `(threadId, title) => Promise` | Rename thread |
348
+ | `archiveThread` | `(threadId) => Promise` | Archive/unarchive |
349
+ | `deleteThread` | `(threadId) => Promise` | Delete thread |
350
+ | `stopGeneration` | `() => void` | Stop streaming |
351
+ | `reset` | `() => void` | Clear all state |
352
+ | `initialPrompt` | `string \| null` | Initial prompt from URL (if urlSync enabled) |
353
+ | `clearInitialPrompt` | `() => void` | Clear initial prompt from URL |
354
+ | `urlAgentId` | `string \| null` | Agent ID from URL (if urlSync enabled) |
355
+ | `setUrlAgentId` | `(agentId) => void` | Update agent ID in URL |
356
+
357
+ ---
358
+
359
+ ## Services
360
+
361
+ ### copilotzService
362
+
363
+ Low-level API client for direct backend communication:
364
+
365
+ ```tsx
366
+ import {
367
+ runCopilotzStream,
368
+ fetchThreads,
369
+ fetchThreadMessages,
370
+ updateThread,
371
+ deleteThread,
372
+ } from '@copilotz/chat-adapter';
373
+
374
+ // Stream a message
375
+ const result = await runCopilotzStream({
376
+ content: 'Hello!',
377
+ user: { externalId: 'user-123', name: 'Alex' },
378
+ threadExternalId: 'thread-456',
379
+ onToken: (text, isComplete) => console.log(text),
380
+ onMessageEvent: (event) => console.log(event),
381
+ onAssetEvent: (asset) => console.log(asset),
382
+ });
383
+
384
+ // Fetch threads
385
+ const threads = await fetchThreads('user-123');
386
+
387
+ // Fetch messages
388
+ const messages = await fetchThreadMessages('thread-456');
389
+ ```
390
+
391
+ ### assetsService
392
+
393
+ Resolve asset references to data URLs:
394
+
395
+ ```tsx
396
+ import { getAssetDataUrl, resolveAssetsInMessages } from '@copilotz/chat-adapter';
397
+
398
+ // Single asset
399
+ const { dataUrl, mime } = await getAssetDataUrl('asset://abc123');
400
+
401
+ // Batch resolve in messages
402
+ const messagesWithAssets = await resolveAssetsInMessages(messages);
403
+ ```
404
+
405
+ ---
406
+
407
+ ## Exports
408
+
409
+ ```tsx
410
+ // Components
411
+ export { CopilotzChat } from './CopilotzChat';
412
+
413
+ // Hooks
414
+ export { useCopilotz } from './useCopilotzChat';
415
+ export { useUrlState } from './useUrlState';
416
+
417
+ // Services
418
+ export {
419
+ runCopilotzStream,
420
+ fetchThreads,
421
+ fetchThreadMessages,
422
+ updateThread,
423
+ deleteThread,
424
+ copilotzService,
425
+ } from './copilotzService';
426
+
427
+ export {
428
+ getAssetDataUrl,
429
+ resolveAssetsInMessages,
430
+ } from './assetsService';
431
+
432
+ // Re-exported types from @copilotz/chat-ui
433
+ export type {
434
+ ChatConfig,
435
+ ChatCallbacks,
436
+ ChatUserContext,
437
+ ChatMessage,
438
+ ChatThread,
439
+ MediaAttachment,
440
+ MemoryItem,
441
+ } from '@copilotz/chat-ui';
442
+
443
+ // URL state types
444
+ export type {
445
+ UrlSyncConfig,
446
+ UrlParamsConfig,
447
+ UrlState,
448
+ UseUrlStateReturn,
449
+ } from './useUrlState';
450
+ ```
451
+
452
+ ---
453
+
454
+ ## Full Example
12
455
 
13
456
  ```tsx
14
457
  import { CopilotzChat } from '@copilotz/chat-adapter';
15
458
  import '@copilotz/chat-ui/styles.css';
16
459
 
17
- export function Example() {
460
+ function App() {
461
+ const handleLogout = () => {
462
+ // Clear session, redirect to login
463
+ };
464
+
465
+ const handleToolOutput = (output) => {
466
+ // React to tool outputs
467
+ if (output.userContext) {
468
+ // Context was updated by a tool
469
+ }
470
+ };
471
+
18
472
  return (
19
473
  <CopilotzChat
20
- userId="user-1"
474
+ userId="user-123"
475
+ userName="Alex"
476
+ userEmail="alex@example.com"
477
+ initialContext={{
478
+ customFields: [
479
+ { key: 'plan', label: 'Plan', value: 'Pro' },
480
+ ],
481
+ }}
482
+ bootstrap={{
483
+ initialMessage: 'Hello! I need help with my account.',
484
+ }}
21
485
  config={{
22
- branding: { title: 'Copilotz' }
486
+ branding: {
487
+ title: 'Acme Support',
488
+ subtitle: 'We typically reply in a few seconds',
489
+ },
490
+ features: {
491
+ enableToolCallsDisplay: true,
492
+ enableFileUpload: true,
493
+ enableAudioRecording: true,
494
+ },
495
+ labels: {
496
+ inputPlaceholder: 'Describe your issue...',
497
+ },
23
498
  }}
499
+ customComponent={({ onClose, isMobile }) => (
500
+ <div className="p-4">
501
+ <h2>Account Details</h2>
502
+ <button onClick={onClose}>Close</button>
503
+ </div>
504
+ )}
505
+ onToolOutput={handleToolOutput}
506
+ onLogout={handleLogout}
24
507
  />
25
508
  );
26
509
  }
27
510
  ```
511
+
512
+ ---
513
+
514
+ ## Requirements
515
+
516
+ - React 18+
517
+ - Copilotz backend with `/v1/providers/web` endpoint
518
+ - Environment variables configured
519
+
520
+ ---
521
+
522
+ ## Related Packages
523
+
524
+ - **[@copilotz/chat-ui](../copilotz-chat-ui)** — The underlying UI components (backend-agnostic)
525
+ - **[copilotz](https://github.com/copilotzhq/ts-lib)** — The full-stack framework for AI applications
526
+
527
+ ---
528
+
529
+ ## License
530
+
531
+ MIT — see [LICENSE](./LICENSE)
532
+
533
+ ---
534
+
535
+ <p align="center">
536
+ <strong>Your agent is ready. Your UI should be too.</strong>
537
+ </p>