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

@sensu-ai/sdk

v0.11.0

Published

The official Node.js SDK for [Sensu](https://sensu-ai.com) — observability for AI agents.

Downloads

112

Readme

@sensu-ai/sdk

The official Node.js SDK for Sensu — observability for AI agents.

Instrument your agents to track LLM calls, tool use, token spend, latency, and cost. Events are buffered and sent in batches so there's no impact on your agent's performance.

Installation

npm install @sensu-ai/sdk

Quick start

High-level API (recommended for Node.js)

Uses AsyncLocalStorage so concurrent requests each get an isolated run context automatically — no run handle passing required.

import { SensuClient } from '@sensu-ai/sdk';
import { wrapAnthropic } from '@sensu-ai/sdk/integrations/anthropic';
import Anthropic from '@anthropic-ai/sdk';

const sensu = new SensuClient({
  apiKey: process.env.SENSU_API_KEY,
  agentId: 'my-agent',
});

const anthropic = wrapAnthropic(new Anthropic(), { client: sensu });

// sensu.run() creates a run, propagates context automatically, and ends on completion
await sensu.run({ sessionId: 'abc' }, async (run) => {
  // All wrapAnthropic calls inside here are automatically attributed to this run
  const response = await anthropic.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 1024,
    messages: [{ role: 'user', content: 'Hello' }],
  });

  // Client-level helpers — no step handle needed
  const result = await sensu.trackTool('search_web', () => searchWeb(query));
});

Low-level API

import { SensuClient } from '@sensu-ai/sdk';

const sensu = new SensuClient({
  apiKey: process.env.SENSU_API_KEY,
  agentId: 'my-agent',
});

const run = sensu.startRun();
const step = run.startStep({ name: 'plan' });

// Wrap an LLM call — latency and token usage are captured automatically
const response = await step.trackLlm({
  provider: 'anthropic',
  model: 'claude-sonnet-4-6',
  fn: () => anthropic.messages.create({ ... }),
});

// Wrap a tool call
const result = await step.trackTool({
  toolName: 'search_web',
  fn: () => searchWeb(query),
});

await step.end();
await run.end();

Configuration

const sensu = new SensuClient({
  apiKey: 'sns_live_...',      // Required. Your Sensu API key
  agentId: 'my-agent',         // Required. Identifies this agent in the dashboard
  baseUrl: 'https://...',      // Default: http://localhost:3001
  orgId: '',                   // Optional. Populated automatically from your API key
  batchSize: 10,               // Flush after N events (default: 10)
  flushIntervalMs: 2000,       // Flush every N ms (default: 2000)
  disabled: false,             // Set true to disable all telemetry (e.g. in tests)
  debugMode: false,            // Print one-line event summaries to the console during dev
});

debugMode: true prints a human-readable line to console.log for every event before it is flushed. Events are still sent to the API — this flag observes only.

You can also load config from environment variables:

const sensu = new SensuClient({ fromEnv: true });

| Variable | Description | |---|---| | SENSU_API_KEY | Your Sensu API key | | SENSU_AGENT_ID | Agent identifier | | SENSU_BASE_URL | API base URL | | SENSU_ORG_ID | Organisation ID (optional) |

API

SensuClient

| Method | Description | |---|---| | run(opts, fn) | Execute fn inside a new run context (Node.js only — uses AsyncLocalStorage). Ends the run automatically when fn resolves or throws. | | startRun(opts?) | Start a new agent run. Returns a RunHandle. | | getActiveRun() | Returns the RunHandle for the current async context, or undefined if called outside sensu.run(). | | trackTool(name, fn, opts?) | Track a tool call inside the active sensu.run() context. No-op if called outside a run. | | trackRetrieval(storeId, opts, fn) | Track a retrieval call inside the active sensu.run() context. No-op if called outside a run. | | trackEmbedding(model, opts, fn) | Track an embedding call inside the active sensu.run() context. No-op if called outside a run. | | trackGuardrail(id, type, fn) | Track a guardrail check inside the active sensu.run() context. No-op if called outside a run. | | flush() | Manually flush buffered events to the API. | | destroy() | Stop the background flush timer and deregister the beforeExit handler. |

RunHandle

| Method | Description | |---|---| | startStep(opts?) | Start a step within the run. Returns a StepHandle. | | end(status?) | End the run. status is 'completed' (default) or 'failed'. |

StepHandle

| Method | Description | |---|---| | trackLlm(opts) | Wrap an LLM call — measures latency and extracts token usage from the response. | | recordLlm(opts) | Emit a raw LLM event when you already have the stats. | | trackTool(opts) | Wrap a tool call — measures latency and output size. | | trackRetrieval(opts) | Track a vector store retrieval. | | recordRetrieval(opts) | Emit a raw retrieval event when you already have the stats. | | trackEmbedding(opts) | Track an embedding call. | | trackGuardrail(opts) | Track a guardrail check. Returns the check result ('pass' | 'fail' | 'modified'). | | end() | End the step. |

Integrations

Anthropic (recommended)

Wrap the Anthropic client to track all messages.create() calls automatically.

Node.js — concurrent-safe via AsyncLocalStorage:

import { wrapAnthropic } from '@sensu-ai/sdk/integrations/anthropic';
import Anthropic from '@anthropic-ai/sdk';

const anthropic = wrapAnthropic(new Anthropic(), { client: sensu });

await sensu.run({ sessionId }, async () => {
  // Automatically tracked — no run handle needed
  const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', ... });
});

Browser / Edge Runtime — explicit run handle:

const run = sensu.startRun({ sessionId });
const anthropic = wrapAnthropic(new Anthropic(), { client: sensu, runHandle: run });

const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', ... });
await run.end();

OpenAI

Wrap the OpenAI client to track all completions automatically:

import { wrapOpenAI } from '@sensu-ai/sdk/integrations/openai';
import OpenAI from 'openai';

const openai = wrapOpenAI(new OpenAI({ apiKey }), {
  client: sensu,
  runHandle: run,
});

// All calls to openai.chat.completions.create() are now tracked
const response = await openai.chat.completions.create({ model: 'gpt-4o', ... });

LangChain

Drop the Sensu callback handler into any LangChain chain, agent, or LLM. Chain boundaries, LLM calls (with streaming TTFT and retry/fallback detection), and tool calls are captured automatically.

import { SensuClient } from '@sensu-ai/sdk';
import { SensuCallbackHandler } from '@sensu-ai/sdk/integrations/langchain';
import { ChatAnthropic } from '@langchain/anthropic';
import { ChatPromptTemplate } from '@langchain/core/prompts';

const sensu = new SensuClient({
  apiKey: process.env.SENSU_API_KEY,
  agentId: 'my-langchain-agent',
});

const handler = new SensuCallbackHandler({ client: sensu });

const prompt = ChatPromptTemplate.fromMessages([['human', '{question}']]);
const llm = new ChatAnthropic({ model: 'claude-sonnet-4-6' });
const chain = prompt.pipe(llm);

const result = await chain.invoke(
  { question: 'What is observability?' },
  { callbacks: [handler] },
);

Tying events to a specific run. By default the handler creates its own sessionId/runId UUIDs. Pass them explicitly to correlate LangChain telemetry with a run started elsewhere:

const run = sensu.startRun({ sessionId: 'user-session-1' });
const handler = new SensuCallbackHandler({
  client: sensu,
  sessionId: 'user-session-1',
  runId: run.runId,
});

What's captured. Chain start/end → agent.step.*; LLM start/end/error → llm.request.* (provider, model, tokens, latency, TTFT); streaming tokens → stream.token.received every 10th token; tool start/end/error → tool.call.* with retry_of when the same tool re-invokes after error, and is_fallback on the next LLM after an error.

Limitations. LangChain's callback interface exposes aggregate token counts only — per-role context breakdown is not surfaced through this path. For context-window analysis, use the low-level trackLlm() / recordLlm() APIs directly.

Requires langchain >= 0.1.0 (declared as an optional peer dependency).

LangGraph

Building agentic workflows with LangGraph? Use SensuLangGraphHandler to get per-node steps in the Sensu trace tree. Each node execution emits an agent.step.started event with step_type='langgraph_node' and the node name; LangGraph's internal ChannelWrite plumbing wrappers (tagged langsmith:hidden) are filtered out so the event stream stays clean.

import { SensuClient } from '@sensu-ai/sdk';
import { SensuLangGraphHandler } from '@sensu-ai/sdk/integrations/langgraph';
import { StateGraph, Annotation, START, END } from '@langchain/langgraph';

const sensu = new SensuClient({ apiKey: process.env.SENSU_API_KEY, agentId: 'my-graph' });
const handler = new SensuLangGraphHandler({ client: sensu });

const State = Annotation.Root({
  topic:   Annotation<string>(),
  outline: Annotation<string>(),
});

const graph = new StateGraph(State)
  .addNode('plan_step', async (s) => ({ outline: `outline for: ${s.topic}` }))
  .addEdge(START, 'plan_step')
  .addEdge('plan_step', END)
  .compile();

const result = await graph.invoke(
  { topic: 'observability' },
  { callbacks: [handler] },
);

What's added beyond the LangChain handler. A node execution emits step_type='langgraph_node' (vs 'chain'), with extra fields:

  • node_name — the addNode() name (e.g. "plan_step")
  • langgraph_step — LangGraph's monotonic step counter

Everything else — LLM calls, tool calls, streaming TTFT, retry/fallback — works identically to the LangChain integration, because SensuLangGraphHandler subclasses SensuCallbackHandler. Mixed LangChain + LangGraph projects work with either handler; the parent class auto-detects LangGraph nodes too.

Requires @langchain/langgraph >= 0.2.0 (declared as an optional peer dependency).

Supported models (cost estimation)

The SDK automatically estimates cost for the following models:

  • Anthropic: claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5, claude-3-5-sonnet, claude-3-5-haiku, claude-3-opus
  • OpenAI: gpt-4o, gpt-4o-mini, gpt-4-turbo