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

@diabolicallabs/agent-sdk

v3.2.7

Published

Cost-tracking middleware for @diabolicallabs/llm-client. Async fire-and-forget ingestion to Agent Spend Dashboard. © Diabolical Labs

Readme

@diabolicallabs/agent-sdk

Cost-tracking middleware for @diabolicallabs/llm-client. Drop-in wrapper that captures call records and dispatches them asynchronously to the Agent Spend Dashboard. © Diabolical Labs

Stable. Requires @diabolicallabs/llm-client@^5.0.0. Public API committed: instrumentClient, CallRecord, setAgentSdkLogger.

Status

Published — v3.2.3. instrumentClient() wraps all five LlmClient methods: complete(), stream(), structured(), streamStructured(), withTools(). Cost propagation (v1.1.0), failover requestedModel tracking (v1.2.0), and streamStructured() (v1.3.0) are included.

v3.2.0 — UUID validation at startup: instrumentClient() now validates identity.agentId (required UUID) and identity.projectId (optional — validated if present) at call time. Non-UUID values emit a console.warn with the offending field names and flip the returned client to no-op/disabled mode rather than silently dispatching records that the dashboard will reject. See Common pitfalls.

v2.0.0 — architecture migration complete: all 5 call types now route through a single buildAfterCallDispatch() function. The stream() and streamStructured() bespoke usage-capture wrappers retained in v1.4.0 are deleted. LlmAfterCallContext.usage is now populated by [email protected] for streaming paths, so agent-sdk no longer needs its own generator iteration for usage capture. Public API is unchanged.

v3.2.3 — files namespace passthrough: InstrumentedLlmClient.files now delegates to the underlying LlmClient.files. Callers can use the Files API (upload, refresh, waitForActive, delete) directly on the instrumented client without unwrapping it. Requires llm-client@^5.1.0 for the full Files API surface.

v3.2.2 — peer-dep bump to llm-client@^5.0.0: Follows the [email protected] breaking change to LlmToolSchema (tool inputSchema discriminated union). No API change in agent-sdk itself.

v3.0.1 — peer-dep cleanup: @diabolicallabs/llm-pricing is no longer declared as a peer dependency. The only usage was two import type statements compiled away at build time. The LlmCost type is now defined inline in agent-sdk — no consumer-side install change required. If your project uses @diabolicallabs/llm-client with pricing enabled, install @diabolicallabs/llm-pricing via the llm-client peer-dep path (or directly) — not as an agent-sdk requirement.

Install

pnpm add @diabolicallabs/agent-sdk @diabolicallabs/llm-client

Usage

import { createClientFromEnv } from '@diabolicallabs/llm-client';
import { instrumentClient } from '@diabolicallabs/agent-sdk';

const base = createClientFromEnv('anthropic', 'claude-sonnet-4-6');

const client = instrumentClient(base, {
  identity: { agentId: process.env.AGENT_ID!, taskLabel: 'geo-audit' },
  ingestionUrl: process.env.SPEND_INGESTION_URL!,
  ingestionKey: process.env.SPEND_INGESTION_KEY!,
});

// complete() — non-streaming
const response = await client.complete([{ role: 'user', content: 'Hello' }]);
// CallRecord dispatched asynchronously — response returned immediately

// streamStructured() — token streaming + validated output (v1.3.0)
const { z } = await import('zod');
const schema = z.object({ name: z.string(), score: z.number() });
for await (const event of client.streamStructured(messages, schema)) {
  if (event.type === 'token') process.stdout.write(event.token);
  if (event.type === 'done') console.log(event.data, event.usage);
}
// One CallRecord dispatched after the done event — not per token

API

instrumentClient(client, config): InstrumentedLlmClient

Wraps any LlmClient with cost-tracking middleware. The returned InstrumentedLlmClient is a drop-in replacement — it implements the same interface.

Config:

| Field | Type | Default | Description | |---|---|---|---| | identity.agentId | string | required | Must be a UUID from the Spend Dashboard agent registry. Non-UUID values flip the client to disabled mode with a console.warn at call time. | | identity.taskLabel | string? | — | Optional label for this call (max 200 chars) | | identity.projectId | string? | — | Optional project override. Must be a UUID if supplied; non-UUID values flip the client to disabled mode with a console.warn. | | ingestionUrl | string | required | Agent Spend Dashboard /api/ingest endpoint | | ingestionKey | string | required | Agent-scoped bearer token | | maxIngestionRetries | number | 3 | Retries before dropping the record | | ingestionTimeoutMs | number | 5000 | Ingestion request timeout — never blocks the LLM call | | disabled | boolean | false | Set true in test/dev to skip all instrumentation |

Instrumented methods and CallRecord behavior:

| Method | CallRecord timing | Cost propagated | Notes | |---|---|---|---| | complete() | After response | Yes | requestedModel included on failover | | stream() | After final chunk | No | Usage from the chunk that carries usage | | structured() | After response | Yes | requestedModel included on failover | | streamStructured() | After done event | No | One record per call, usage from the done event | | withTools() | After response | Yes | tool_calls array included for per-tool attribution |

Ingestion contract

Every LLM call produces a CallRecord dispatched to the ingestion URL:

interface CallRecord {
  agent_id: string;
  model: string;
  requestedModel?: string; // Present when provider failover fired (v1.2.0+)
  prompt_tokens: number;
  completion_tokens: number;
  cache_creation_tokens?: number; // Anthropic prompt cache only
  cache_read_tokens?: number;     // Anthropic prompt cache only
  latency_ms: number;
  task_label?: string;
  project_id?: string;
  timestamp: string;   // ISO 8601 UTC
  call_id: string;     // UUID v4 — idempotency key
  tool_calls?: LlmToolCall[]; // withTools() only, omitted when array is empty
  cost?: LlmCost;     // Present when LlmClient has pricing configured (v1.1.0+)
}

When to use instrumentClient vs raw createClient({ hooks })

@diabolicallabs/llm-client v1.5.0+ ships a native hooks API (beforeCall/afterCall) on createClient(). Use it directly when you want request-level interception: PII redaction, cache short-circuit, custom logging.

Use instrumentClient() when you want structured CallRecord ingestion to the Agent Spend Dashboard. It owns the CallRecord schema and the ingestion retry/backoff contract. In v2.0.0, all 5 call types dispatch through a single uniform afterCall handler — the public entry point is always instrumentClient(client, config).

Composition: if you set hooks on a LlmClient config and then pass that client to instrumentClient(), both hooks run. The consumer's afterCall fires first; the ingestion dispatch fires second.

const base = createClient({
  provider: 'anthropic',
  model: 'claude-sonnet-4-6',
  apiKey: process.env.ANTHROPIC_API_KEY!,
  hooks: {
    beforeCall: async (ctx) => ({
      messages: redactPii(ctx.messages),
    }),
  },
});

// Both the PII hook and ingestion dispatch run on every call
const client = instrumentClient(base, {
  identity: { agentId: process.env.AGENT_ID! },
  ingestionUrl: process.env.SPEND_INGESTION_URL!,
  ingestionKey: process.env.SPEND_INGESTION_KEY!,
});

Failure behavior

Ingestion failures are always silent — they never surface to the LLM caller.

  • Endpoint down or slow: retried up to maxIngestionRetries with exponential backoff
  • Retries exhausted: record dropped, structured warning logged (includes call_id for audit)
  • disabled: true: all instrumentation skipped, underlying client returned directly

Common pitfalls

Non-UUID agentId or projectId — zero records appear in the dashboard

Before v3.2.0, if you passed a non-UUID agentId (for example a slug like "fitcheck-llm") or a non-UUID projectId, the SDK silently dispatched records to the dashboard, which rejected them with HTTP 400 VALIDATION_ERROR. The SDK retried 4 times and dropped the record — the only signal was a JSON-formatted warn on stdout that was easy to miss in Railway logs.

From v3.2.0, instrumentClient() validates both fields at call time. If either is not a valid UUID, the SDK emits a console.warn naming the malformed field(s) and flips the returned client to no-op mode immediately — no records are dispatched. Register your agent in the Spend Dashboard to obtain the correct UUIDs, then set them via environment variables (for example AGENT_SPEND_AGENT_ID and AGENT_SPEND_PROJECT_ID).