@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 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
+ ```