npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ziggy-ai/client-sdk

v1.1.1

Published

Client SDK for integrating with Ziggy's v1 API

Readme

@ziggy-ai/client-sdk

TypeScript SDK for integrating host applications with Ziggy's v1 API.

Install

npm install @ziggy-ai/client-sdk

Quick Start

import { ZiggyClient } from '@ziggy-ai/client-sdk';

const ziggy = new ZiggyClient({
  baseUrl: 'https://ziggy.example.com',
  appId: 'my-app',
  apiKey: 'pk_live_abc123',
});

// Connect (creates session, starts context batching + analytics)
const session = await ziggy.connect({
  userId: 'user-1',
  context: {
    page: { path: '/dashboard', type: 'dashboard', title: 'Dashboard' },
    user: { id: 'user-1', tier: 'pro', sessionCount: 42 },
  },
  onTrigger: (response) => {
    if (response.trigger) showProactiveBubble(response.trigger);
  },
});

// Streaming chat
for await (const event of ziggy.chat.stream({ message: 'Why did my transform fail?' })) {
  switch (event.type) {
    case 'content.delta': appendToUI(event.content); break;
    case 'tool.start': showToolIndicator(event.toolName); break;
    case 'metadata': renderActions(event.quickActions); break;
  }
}

// Disconnect (flushes analytics, stops batching, ends session)
await ziggy.disconnect();

API

ZiggyClient

The main entry point. Composes specialized sub-clients for each API area.

const ziggy = new ZiggyClient({
  baseUrl: string;   // Ziggy API base URL
  appId: string;     // Your app identifier
  apiKey: string;    // API key (from App node in Ziggy)
  timeout?: number;  // Request timeout in ms (default: 30000)
});

Lifecycle

// Connect — creates session, wires userId, starts batching
const session = await ziggy.connect({
  userId: string;
  context: Record<string, unknown>;
  capabilities?: string[];  // default: ['streaming', 'actions']
  onTrigger?: (response: ContextPushResponse) => void;
});

// Disconnect — flush analytics, stop batching, destroy session
await ziggy.disconnect();

Chat

// Streaming (ndjson, MCP 2025-03-26 standard)
for await (const event of ziggy.chat.stream({ message: 'Help me debug this' })) {
  // event.type: 'stream.start' | 'agent.start' | 'content.delta' |
  //             'content.done' | 'tool.start' | 'tool.result' |
  //             'metadata' | 'stream.end' | 'error'
}

// Resume interrupted stream
for await (const event of ziggy.chat.resume(streamId, lastSeqNumber)) {
  // Pick up where you left off
}

Chat request options:

{
  message: string;
  attachments?: Array<{ type: 'screenshot' | 'file_reference'; id: string }>;
  context?: Record<string, unknown>;  // Bundled context update
  requestId?: string;                  // Client-generated dedup ID
}

Context

Context updates are auto-batched every 10 seconds. Important changes (page navigation, errors, process status) flush immediately.

// Queue a context update (merges with pending batch)
ziggy.context.update({
  page: { path: '/settings', type: 'settings', title: 'Settings' },
});

// Queue an event
ziggy.context.addEvent({
  type: 'button_click',
  timestamp: new Date().toISOString(),
  data: { buttonId: 'retry' },
});

// Force flush
await ziggy.context.flush();

App Configuration

Register triggers, quick actions, brand identity, and credentials.

// Brand
await ziggy.app.setBrand({
  identity: { name: 'Coach', tagline: 'Your training partner' },
  voice: { tone: ['motivational', 'knowledgeable'], formality: 'casual-professional' },
  theme: { primaryColor: '#2E7D32', chatPosition: 'bottom-right' },
});
const brand = await ziggy.app.getBrand();

// Triggers (full sync — replaces all existing)
await ziggy.app.registerTriggers({
  triggers: [{
    id: 'transform-error',
    name: 'Transform Error',
    condition: { type: 'process_status', value: 'error' },
    priority: 'critical',
    message: "That didn't work — want me to figure out why?",
  }],
});
const { triggers } = await ziggy.app.listTriggers();
await ziggy.app.deleteTrigger('transform-error');

// Quick Actions (full sync)
await ziggy.app.registerQuickActions({
  sets: [{
    id: 'dashboard-empty',
    contextState: 'dashboard-empty',
    conditions: { pageTypes: ['dashboard'] },
    actions: [{ id: 'start', label: 'Get started', action: 'onboarding.start', priority: 1 }],
  }],
});
const { sets } = await ziggy.app.listQuickActions();
await ziggy.app.deleteQuickActionSet('dashboard-empty');

// Credentials (encrypted at rest, AES-256-GCM)
await ziggy.app.setCredentials({ github_token: 'ghp_...', supabase_key: 'eyJ...' });
const { credentials, updatedAt } = await ziggy.app.getCredentials(); // keys only, never values
await ziggy.app.deleteCredentials();

Callback Handler

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.

import { CallbackHandler } from '@ziggy-ai/client-sdk';

const handler = new CallbackHandler();

handler
  .on('support.navigate', async (params) => {
    router.push(params.path as string);
    return { navigated: true };
  })
  .on('support.retry_process', async (params) => {
    await processes.retry(params.processId as string);
    return { retried: true };
  });

// Mount in your API framework
app.post('/api/ziggy/callback', async (req, res) => {
  const result = await handler.handle(req.body);
  res.json(result);
});

Tools

Invoke tools directly (e.g., from quick action buttons).

const result = await ziggy.tools.invoke('support.navigate', { path: '/settings' });

// Streaming tool invocation
for await (const event of ziggy.tools.invokeStream('support.respond', { message: 'Hi' })) {
  // event.type: 'tool.start' | 'tool.result'
}

Feedback

// Quick reaction
await ziggy.feedback.react('msg-1', 'helpful');
// reaction: 'helpful' | 'not_helpful' | 'wrong' | 'perfect'

// Detailed feedback
await ziggy.feedback.submit({
  conversationId: 'conv-1',
  type: 'accuracy',  // 'accuracy' | 'helpfulness' | 'tone' | 'action' | 'correction'
  rating: 4,
  comment: 'Good answer but missed one detail',
  userId: 'user-1',
});

// Record coordination outcome (for learning loop)
await ziggy.feedback.recordOutcome({
  coordinationId: 'coord-1',
  success: true,
  agentIds: ['knuckles'],
});

Quests

Track multi-step workflows.

const { quest } = await ziggy.quests.create({
  title: 'Investigate transform failure',
  sessionId: session.sessionId,
  initiatorId: 'knuckles',
  steps: [
    { label: 'Check logs', agentId: 'ziggy' },
    { label: 'Analyze error' },
  ],
});

await ziggy.quests.updateStep(quest.id, quest.steps[0].id, {
  status: 'complete',
  update: { message: 'Found 3 errors in logs' },
});

// Stream quest updates
for await (const event of ziggy.quests.stream(quest.id)) {
  // event.type: 'quest.status' | 'quest.step_update' | 'quest.complete'
}

Analytics

Events are auto-batched every 30 seconds and flushed on disconnect.

ziggy.analytics.track('feature_used', { feature: 'dark-mode' });
ziggy.analytics.track('page_viewed', { path: '/settings' });

Stream Events

All streaming endpoints use ndjson (newline-delimited JSON) with sequence numbers for resumption.

| Event Type | Key Fields | Description | |------------|------------|-------------| | stream.start | streamId, messageId | Always first | | agent.start | agentId, agentRole | Routed agent begins | | agent.switch | fromAgentId, toAgentId | Agent handoff | | content.delta | content | Partial text | | content.done | fullContent | Complete text | | tool.start | toolId, toolName | Tool invoked | | tool.progress | toolId, progress | Tool progress (0-1) | | tool.result | toolId, success | Tool completed | | metadata | confidence, quickActions | Response metadata | | stream.end | messageId, durationMs | Always last | | error | code, message, retryable | Error during stream |

Stream Resumption

import { SequenceTracker } from '@ziggy-ai/client-sdk';

const tracker = new SequenceTracker();

for await (const event of ziggy.chat.stream({ message: 'Hello' })) {
  tracker.update(event);
  // ... process event
}

// If disconnected, resume:
if (tracker.streamId) {
  for await (const event of ziggy.chat.resume(tracker.streamId, tracker.lastSeq)) {
    // Picks up from where you left off
  }
}

Error Handling

import { ZiggyError, ZiggyConnectionError } from '@ziggy-ai/client-sdk';

try {
  for await (const event of ziggy.chat.stream({ message: 'Hello' })) { /* ... */ }
} catch (error) {
  if (error instanceof ZiggyConnectionError) {
    // Network/timeout — error.retryable is true
    console.log('Connection failed, will retry');
  } else if (error instanceof ZiggyError) {
    // API error — check error.status
    console.log(`API error ${error.status}: ${error.message}`);
  }
}

Retry Utility

import { withRetry } from '@ziggy-ai/client-sdk';

const result = await withRetry(
  () => ziggy.chat.action({ actionId: 'support.explain_limits' }),
  { maxRetries: 3, baseDelayMs: 1000, maxDelayMs: 30000 },
);
// Retries with exponential backoff + jitter. Skips retry for 4xx errors.

Architecture

The SDK is a thin client that maps to Ziggy's v1 REST API:

ZiggyClient
├── sessions    → POST/DELETE /v1/sessions
├── chat        → POST /v1/chat/stream, POST /v1/chat/action/stream
├── context     → POST /v1/sessions/:id/context (auto-batched)
├── tools       → POST /v1/tools/invoke
├── feedback    → POST /v1/feedback/*
├── quests      → /v1/quests/*
├── analytics   → POST /v1/feedback/analytics/events (auto-batched)
├── app         → /v1/apps/* (triggers, quick actions, brand, credentials)
└── callbacks   → CallbackHandler (host-side, receives Ziggy tool requests)

Key design decisions:

  • Auto-batching — Context (10s) and analytics (30s) are batched to reduce API calls. Important context changes flush immediately.
  • Streaming — Uses Streamable HTTP with ndjson (MCP 2025-03-26 standard). Sequence numbers enable reconnection.
  • Composition — Each API area is a separate module composed by ZiggyClient.
  • Zero dependencies — Uses only fetch (built into Node 18+ and all modern browsers).

Development

npm run build       # Build with tsup (ESM + .d.ts)
npm run dev         # Watch mode
npm run typecheck   # Type check without emit
npm test            # Run tests (vitest)