@ziggy-ai/client-sdk 0.1.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.
- package/LICENSE +21 -0
- package/README.md +395 -0
- package/dist/index.d.ts +874 -0
- package/dist/index.js +738 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ziggy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# @ziggy-ai/client-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for integrating host applications with Ziggy's v1 API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @ziggy-ai/client-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { ZiggyClient } from '@ziggy-ai/client-sdk';
|
|
15
|
+
|
|
16
|
+
const ziggy = new ZiggyClient({
|
|
17
|
+
baseUrl: 'https://ziggy.example.com',
|
|
18
|
+
appId: 'my-app',
|
|
19
|
+
apiKey: 'pk_live_abc123',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Connect (creates session, starts context batching + analytics)
|
|
23
|
+
const session = await ziggy.connect({
|
|
24
|
+
userId: 'user-1',
|
|
25
|
+
context: {
|
|
26
|
+
page: { path: '/dashboard', type: 'dashboard', title: 'Dashboard' },
|
|
27
|
+
user: { id: 'user-1', tier: 'pro', sessionCount: 42 },
|
|
28
|
+
},
|
|
29
|
+
onTrigger: (response) => {
|
|
30
|
+
if (response.trigger) showProactiveBubble(response.trigger);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Streaming chat
|
|
35
|
+
for await (const event of ziggy.chat.stream({ message: 'Why did my transform fail?' })) {
|
|
36
|
+
switch (event.type) {
|
|
37
|
+
case 'content.delta': appendToUI(event.content); break;
|
|
38
|
+
case 'tool.start': showToolIndicator(event.toolName); break;
|
|
39
|
+
case 'metadata': renderActions(event.quickActions); break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Disconnect (flushes analytics, stops batching, ends session)
|
|
44
|
+
await ziggy.disconnect();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### ZiggyClient
|
|
50
|
+
|
|
51
|
+
The main entry point. Composes specialized sub-clients for each API area.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const ziggy = new ZiggyClient({
|
|
55
|
+
baseUrl: string; // Ziggy API base URL
|
|
56
|
+
appId: string; // Your app identifier
|
|
57
|
+
apiKey: string; // API key (from App node in Ziggy)
|
|
58
|
+
timeout?: number; // Request timeout in ms (default: 30000)
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### Lifecycle
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// Connect — creates session, wires userId, starts batching
|
|
66
|
+
const session = await ziggy.connect({
|
|
67
|
+
userId: string;
|
|
68
|
+
context: Record<string, unknown>;
|
|
69
|
+
capabilities?: string[]; // default: ['streaming', 'actions']
|
|
70
|
+
onTrigger?: (response: ContextPushResponse) => void;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Disconnect — flush analytics, stop batching, destroy session
|
|
74
|
+
await ziggy.disconnect();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### Chat
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Non-streaming
|
|
83
|
+
const response = await ziggy.chat.send({ message: 'Help me debug this' });
|
|
84
|
+
console.log(response.response.content);
|
|
85
|
+
console.log(response.response.agentId); // 'knuckles', 'ziggy', etc.
|
|
86
|
+
|
|
87
|
+
// Streaming (ndjson, MCP 2025-03-26 standard)
|
|
88
|
+
for await (const event of ziggy.chat.stream({ message: 'Help me debug this' })) {
|
|
89
|
+
// event.type: 'stream.start' | 'agent.start' | 'content.delta' |
|
|
90
|
+
// 'content.done' | 'tool.start' | 'tool.result' |
|
|
91
|
+
// 'metadata' | 'stream.end' | 'error'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Resume interrupted stream
|
|
95
|
+
for await (const event of ziggy.chat.resume(streamId, lastSeqNumber)) {
|
|
96
|
+
// Pick up where you left off
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Chat request options:**
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
{
|
|
104
|
+
message: string;
|
|
105
|
+
attachments?: Array<{ type: 'screenshot' | 'file_reference'; id: string }>;
|
|
106
|
+
context?: Record<string, unknown>; // Bundled context update
|
|
107
|
+
requestId?: string; // Client-generated dedup ID
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### Context
|
|
114
|
+
|
|
115
|
+
Context updates are auto-batched every 10 seconds. Important changes (page navigation, errors, process status) flush immediately.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// Queue a context update (merges with pending batch)
|
|
119
|
+
ziggy.context.update({
|
|
120
|
+
page: { path: '/settings', type: 'settings', title: 'Settings' },
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Queue an event
|
|
124
|
+
ziggy.context.addEvent({
|
|
125
|
+
type: 'button_click',
|
|
126
|
+
timestamp: new Date().toISOString(),
|
|
127
|
+
data: { buttonId: 'retry' },
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Force flush
|
|
131
|
+
await ziggy.context.flush();
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
### App Configuration
|
|
137
|
+
|
|
138
|
+
Register triggers, quick actions, brand identity, and credentials.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Brand
|
|
142
|
+
await ziggy.app.setBrand({
|
|
143
|
+
identity: { name: 'Coach', tagline: 'Your training partner' },
|
|
144
|
+
voice: { tone: ['motivational', 'knowledgeable'], formality: 'casual-professional' },
|
|
145
|
+
theme: { primaryColor: '#2E7D32', chatPosition: 'bottom-right' },
|
|
146
|
+
});
|
|
147
|
+
const brand = await ziggy.app.getBrand();
|
|
148
|
+
|
|
149
|
+
// Triggers (full sync — replaces all existing)
|
|
150
|
+
await ziggy.app.registerTriggers({
|
|
151
|
+
triggers: [{
|
|
152
|
+
id: 'transform-error',
|
|
153
|
+
name: 'Transform Error',
|
|
154
|
+
condition: { type: 'process_status', value: 'error' },
|
|
155
|
+
priority: 'critical',
|
|
156
|
+
message: "That didn't work — want me to figure out why?",
|
|
157
|
+
}],
|
|
158
|
+
});
|
|
159
|
+
const { triggers } = await ziggy.app.listTriggers();
|
|
160
|
+
await ziggy.app.deleteTrigger('transform-error');
|
|
161
|
+
|
|
162
|
+
// Quick Actions (full sync)
|
|
163
|
+
await ziggy.app.registerQuickActions({
|
|
164
|
+
sets: [{
|
|
165
|
+
id: 'dashboard-empty',
|
|
166
|
+
contextState: 'dashboard-empty',
|
|
167
|
+
conditions: { pageTypes: ['dashboard'] },
|
|
168
|
+
actions: [{ id: 'start', label: 'Get started', action: 'onboarding.start', priority: 1 }],
|
|
169
|
+
}],
|
|
170
|
+
});
|
|
171
|
+
const { sets } = await ziggy.app.listQuickActions();
|
|
172
|
+
await ziggy.app.deleteQuickActionSet('dashboard-empty');
|
|
173
|
+
|
|
174
|
+
// Credentials (encrypted at rest, AES-256-GCM)
|
|
175
|
+
await ziggy.app.setCredentials({ github_token: 'ghp_...', supabase_key: 'eyJ...' });
|
|
176
|
+
const { credentials, updatedAt } = await ziggy.app.getCredentials(); // keys only, never values
|
|
177
|
+
await ziggy.app.deleteCredentials();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### Callback Handler
|
|
183
|
+
|
|
184
|
+
Handle tool execution requests from Ziggy. When Ziggy needs your app to do something (navigate, retry a process, etc.), it POSTs to your registered callback URL.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { CallbackHandler } from '@ziggy-ai/client-sdk';
|
|
188
|
+
|
|
189
|
+
const handler = new CallbackHandler();
|
|
190
|
+
|
|
191
|
+
handler
|
|
192
|
+
.on('support.navigate', async (params) => {
|
|
193
|
+
router.push(params.path as string);
|
|
194
|
+
return { navigated: true };
|
|
195
|
+
})
|
|
196
|
+
.on('support.retry_process', async (params) => {
|
|
197
|
+
await processes.retry(params.processId as string);
|
|
198
|
+
return { retried: true };
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Mount in your API framework
|
|
202
|
+
app.post('/api/ziggy/callback', async (req, res) => {
|
|
203
|
+
const result = await handler.handle(req.body);
|
|
204
|
+
res.json(result);
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### Tools
|
|
211
|
+
|
|
212
|
+
Invoke tools directly (e.g., from quick action buttons).
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const result = await ziggy.tools.invoke('support.navigate', { path: '/settings' });
|
|
216
|
+
|
|
217
|
+
// Streaming tool invocation
|
|
218
|
+
for await (const event of ziggy.tools.invokeStream('support.respond', { message: 'Hi' })) {
|
|
219
|
+
// event.type: 'tool.start' | 'tool.result'
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### Feedback
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Quick reaction
|
|
229
|
+
await ziggy.feedback.react('msg-1', 'helpful');
|
|
230
|
+
// reaction: 'helpful' | 'not_helpful' | 'wrong' | 'perfect'
|
|
231
|
+
|
|
232
|
+
// Detailed feedback
|
|
233
|
+
await ziggy.feedback.submit({
|
|
234
|
+
conversationId: 'conv-1',
|
|
235
|
+
type: 'accuracy', // 'accuracy' | 'helpfulness' | 'tone' | 'action' | 'correction'
|
|
236
|
+
rating: 4,
|
|
237
|
+
comment: 'Good answer but missed one detail',
|
|
238
|
+
userId: 'user-1',
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Record coordination outcome (for learning loop)
|
|
242
|
+
await ziggy.feedback.recordOutcome({
|
|
243
|
+
coordinationId: 'coord-1',
|
|
244
|
+
success: true,
|
|
245
|
+
agentIds: ['knuckles'],
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### Quests
|
|
252
|
+
|
|
253
|
+
Track multi-step workflows.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const { quest } = await ziggy.quests.create({
|
|
257
|
+
title: 'Investigate transform failure',
|
|
258
|
+
sessionId: session.sessionId,
|
|
259
|
+
initiatorId: 'knuckles',
|
|
260
|
+
steps: [
|
|
261
|
+
{ label: 'Check logs', agentId: 'ziggy' },
|
|
262
|
+
{ label: 'Analyze error' },
|
|
263
|
+
],
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await ziggy.quests.updateStep(quest.id, quest.steps[0].id, {
|
|
267
|
+
status: 'complete',
|
|
268
|
+
update: { message: 'Found 3 errors in logs' },
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Stream quest updates
|
|
272
|
+
for await (const event of ziggy.quests.stream(quest.id)) {
|
|
273
|
+
// event.type: 'quest.status' | 'quest.step_update' | 'quest.complete'
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### Analytics
|
|
280
|
+
|
|
281
|
+
Events are auto-batched every 30 seconds and flushed on disconnect.
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
ziggy.analytics.track('feature_used', { feature: 'dark-mode' });
|
|
285
|
+
ziggy.analytics.track('page_viewed', { path: '/settings' });
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Stream Events
|
|
291
|
+
|
|
292
|
+
All streaming endpoints use ndjson (newline-delimited JSON) with sequence numbers for resumption.
|
|
293
|
+
|
|
294
|
+
| Event Type | Key Fields | Description |
|
|
295
|
+
|------------|------------|-------------|
|
|
296
|
+
| `stream.start` | `streamId`, `messageId` | Always first |
|
|
297
|
+
| `agent.start` | `agentId`, `agentRole` | Routed agent begins |
|
|
298
|
+
| `agent.switch` | `fromAgentId`, `toAgentId` | Agent handoff |
|
|
299
|
+
| `content.delta` | `content` | Partial text |
|
|
300
|
+
| `content.done` | `fullContent` | Complete text |
|
|
301
|
+
| `tool.start` | `toolId`, `toolName` | Tool invoked |
|
|
302
|
+
| `tool.progress` | `toolId`, `progress` | Tool progress (0-1) |
|
|
303
|
+
| `tool.result` | `toolId`, `success` | Tool completed |
|
|
304
|
+
| `metadata` | `confidence`, `quickActions` | Response metadata |
|
|
305
|
+
| `stream.end` | `messageId`, `durationMs` | Always last |
|
|
306
|
+
| `error` | `code`, `message`, `retryable` | Error during stream |
|
|
307
|
+
|
|
308
|
+
### Stream Resumption
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { SequenceTracker } from '@ziggy-ai/client-sdk';
|
|
312
|
+
|
|
313
|
+
const tracker = new SequenceTracker();
|
|
314
|
+
|
|
315
|
+
for await (const event of ziggy.chat.stream({ message: 'Hello' })) {
|
|
316
|
+
tracker.update(event);
|
|
317
|
+
// ... process event
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// If disconnected, resume:
|
|
321
|
+
if (tracker.streamId) {
|
|
322
|
+
for await (const event of ziggy.chat.resume(tracker.streamId, tracker.lastSeq)) {
|
|
323
|
+
// Picks up from where you left off
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Error Handling
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { ZiggyError, ZiggyConnectionError } from '@ziggy-ai/client-sdk';
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
await ziggy.chat.send({ message: 'Hello' });
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (error instanceof ZiggyConnectionError) {
|
|
339
|
+
// Network/timeout — error.retryable is true
|
|
340
|
+
console.log('Connection failed, will retry');
|
|
341
|
+
} else if (error instanceof ZiggyError) {
|
|
342
|
+
// API error — check error.status
|
|
343
|
+
console.log(`API error ${error.status}: ${error.message}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Retry Utility
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
import { withRetry } from '@ziggy-ai/client-sdk';
|
|
352
|
+
|
|
353
|
+
const result = await withRetry(
|
|
354
|
+
() => ziggy.chat.send({ message: 'Hello' }),
|
|
355
|
+
{ maxRetries: 3, baseDelayMs: 1000, maxDelayMs: 30000 },
|
|
356
|
+
);
|
|
357
|
+
// Retries with exponential backoff + jitter. Skips retry for 4xx errors.
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Architecture
|
|
363
|
+
|
|
364
|
+
The SDK is a thin client that maps to Ziggy's v1 REST API:
|
|
365
|
+
|
|
366
|
+
```
|
|
367
|
+
ZiggyClient
|
|
368
|
+
├── sessions → POST/DELETE /v1/sessions
|
|
369
|
+
├── chat → POST /v1/chat, POST /v1/chat/stream
|
|
370
|
+
├── context → POST /v1/sessions/:id/context (auto-batched)
|
|
371
|
+
├── tools → POST /v1/tools/invoke
|
|
372
|
+
├── feedback → POST /v1/feedback/*
|
|
373
|
+
├── quests → /v1/quests/*
|
|
374
|
+
├── analytics → POST /v1/feedback/analytics/events (auto-batched)
|
|
375
|
+
├── app → /v1/apps/* (triggers, quick actions, brand, credentials)
|
|
376
|
+
└── callbacks → CallbackHandler (host-side, receives Ziggy tool requests)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Key design decisions:**
|
|
380
|
+
|
|
381
|
+
- **Auto-batching** — Context (10s) and analytics (30s) are batched to reduce API calls. Important context changes flush immediately.
|
|
382
|
+
- **Streaming** — Uses Streamable HTTP with ndjson (MCP 2025-03-26 standard). Sequence numbers enable reconnection.
|
|
383
|
+
- **Composition** — Each API area is a separate module composed by `ZiggyClient`.
|
|
384
|
+
- **Zero dependencies** — Uses only `fetch` (built into Node 18+ and all modern browsers).
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Development
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
npm run build # Build with tsup (ESM + .d.ts)
|
|
392
|
+
npm run dev # Watch mode
|
|
393
|
+
npm run typecheck # Type check without emit
|
|
394
|
+
npm test # Run tests (vitest)
|
|
395
|
+
```
|