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

veragent

v0.5.1

Published

Vendor-neutral control plane client for AI agents — capture and govern agent activity. Node/TypeScript SDK.

Readme

Veragent Node SDK

The client library that makes Veragent a vendor-neutral control plane for agents built in JavaScript/TypeScript — the Node peer of the Python SDK, speaking the exact same wire contract so your backend treats both identically.

The core is dependency-free (Node built-ins only: fetch, AsyncLocalStorage). Requires Node 18+.

Install

npm install veragent

Quick start

import { Veragent } from "veragent";

const va = new Veragent({ agent: "support-agent" }); // reads VERAGENT_API_KEY

// Report any action. Non-blocking — buffers and returns immediately.
va.track("refund issued", {
  eventType: "tool_call",        // the governable surface
  tool: "issue_refund",
  inputs: { orderId: "A-1001", amount: 49 },
});

Group a run

Nested track() calls inside run() automatically inherit one correlation id (via AsyncLocalStorage):

await va.run("nightly-job", async () => {
  va.track("step one", { eventType: "tool_call", tool: "fetch" });
  va.track("step two", { eventType: "tool_call", tool: "write" });
});

Wrap a function

const issueRefund = va.trackAction("issue_refund", async (orderId: string, amount: number) => {
  // ... your logic ...
  return { ok: true };
});

Authorize — ask permission before acting

va.authorize(...) is the pre-action decision point. It calls the live /api/authorize endpoint with the same agent name + API key you configured the client with — you never re-pass credentials. It stays inert (reporting-only, allowed: true) until you turn enforcement on, so it's safe to wire in first:

const va = new Veragent({ agent: "crypto-paper-trader", enforcementEnabled: true });

Blocking (the simple case)

const decision = await va.authorize("polymarket.place_order", {
  context: { market: "BTC-100k", size: 250 },
});

if (decision.allowed) {
  placeOrder();                 // ✅ authorized
} else {
  // MUST handle the not-allowed branch — these mean different things:
  if (decision.status === "denied") {
    console.warn(`blocked by policy: ${decision.reason}`);
  } else if (decision.status === "timed_out") {
    console.warn("no human answered in time; server fail mode applied");
  } else if (decision.status === "error") {
    console.error(`couldn't reach Veragent: ${decision.reason}`);
  }
  skipOrRollback();
}

If the action is auto-decided, the answer comes back on the first call with zero added latency. If it escalates to a human, authorize() blocks and polls until the decision is terminal or timeoutSeconds (default 300, server clamps 10–3600) elapses.

Async (high-throughput loops)

Don't block a hot loop on a human. Fire the request, do other work, poll later:

const decision = await va.authorize("refund.issue", {
  context: { order: "A-1001" },
  wait: "async",
}); // returns immediately — terminal OR status === "pending"

// ...later, poll yourself:
const latest = await va.getDecision(decision.decisionId);
if (latest.status === "pending") {
  // still waiting on a human; check again later
} else if (latest.allowed) {
  issueRefund();
}

The decision object

interface Decision {
  allowed: boolean;          // the one thing you must check
  status: string;            // "allowed" | "denied" | "timed_out" | "error" | "pending"
  decisionId: string;
  reason: string;
  decidedBy?: string | null;
  resolvedAt?: string | null;
}

Pass { raiseOnDeny: true } to throw PolicyDenied on a terminal not-allowed decision instead of returning it.

Treat authorize() as fallible. It is a network call. A client-side inability to reach Veragent returns status: "error", allowed: false (fail-safe — never silently "allowed"), which is not the same as a policy denied. Distinguish denied (a real no) from timed_out (nobody answered → the server's fail mode decided) from error (couldn't get an answer at all). Prefer wait: "async" for high-throughput loops, and keep privileged/irreversible actions late and rollback-able so a denial or error is cheap to honor. Unlike track() (which drops failures silently), authorize failures are legible — surfaced in the returned Decision.

Instrument Vercel AI SDK tools

Wrap the tools you pass to generateText / streamText — every tool call is captured, with no changes to the tools themselves:

import { generateText } from "ai";
import { instrumentTools } from "veragent/vercel-ai";

const result = await generateText({
  model,
  tools: instrumentTools(myTools, va),                  // capture every tool call
  // tools: instrumentTools(myTools, va, { enforce: true }), // also gate them
  prompt: "...",
});

Tool execution is the governable surface, so calls are tracked as eventType: "tool_call". With enforce: true, each call is authorized first and a denied one is blocked before it runs. The adapter has no dependency on the ai package (it duck-types the tools object) and preserves your tools type.

Instrument MCP tool calls

Wrap an MCP Client so every tool call through it is captured (and optionally gated):

import { instrumentMcpClient } from "veragent/mcp";

instrumentMcpClient(client, va);                  // capture every tool call
// instrumentMcpClient(client, va, { enforce: true }); // also gate each call

MCP is the framework-agnostic tool layer, so instrumenting client.callTool captures the governable surface no matter what drives the client. With enforce: true, a denied call is blocked before it runs (returns an isError result). Dependency-free — it duck-types the client.

Instrument LangChain.js

instrument(va) returns a LangChain callback handler — pass it in the callbacks array:

import { instrument } from "veragent/langchain";

await chain.invoke(input, { callbacks: [instrument(va)] });

Every LLM call, tool call, and error in the run is captured; tool calls as eventType: "tool_call". Dependency-free (it's a plain callback-methods object, no @langchain/core import). Capture-only — callbacks fire alongside execution, not before it, so use authorize() or the MCP interceptor for enforcement.

Instrument the OpenAI Agents SDK

instrument(va) registers a Veragent tracing processor with @openai/agents, so every span the SDK emits is captured:

import { instrument } from "veragent/openai-agents";

instrument(va);                  // capture every span the Agents SDK emits
// ...then run agents as usual.

Spans map onto events by type: function → tool_call, generation/response → llm_call, agent → lifecycle, handoff/guardrail → decision, anything else → action. Registration is additive (addTraceProcessor), so existing processors keep running. Dependency-free — it duck-types the SDK's TracingProcessor/Span shapes (the package is only needed at runtime). Capture-only by design: the tracing API notifies on span end, after the action happened, so there is no pre-action hook to gate against — use authorize() or the MCP interceptor for enforcement. Every span mapping is wrapped so a capture error never throws into your agent run.

Design guarantees

  • Safe. track() only buffers; all network I/O is async and batched. Instrumentation never blocks, slows, or throws into your code path. If Veragent is unreachable, events are dropped with a log line — your agent keeps running.
  • Non-blocking. Events flush on an interval / size threshold and once on beforeExit. Call await va.close() for a deterministic final flush (e.g. before a short-lived process exits).
  • Redaction-first. Inputs/outputs run through a redactor before leaving the process (sensitive keys masked, long strings truncated). On by default; pass redact: false or a custom function to override.
  • Enforcement-ready. va.authorize(action, { context }) is the pre-action decision point, calling the live /api/authorize endpoint. It is safe to wire in today — while enforcementEnabled is off it returns allowed: true (reporting-only); turn it on to get real allow/deny verdicts. Unlike track(), authorize failures are legible, not silent: if Veragent can't be reached you get status: "error", allowed: false so the agent never proceeds thinking it was authorized. See Authorize.
  • Wire-compatible. Emits the same { agent, action, severity, metadata } envelope as the Python SDK, tagged sdk: "veragent-node/0.2.0".

Configuration

new Veragent({
  apiKey,              // or env VERAGENT_API_KEY
  agent: "agent",
  endpoint,            // default https://www.veragent.io/api/ingest-event
  redact: true,        // true | false | (value) => value
  flushInterval: 2,    // seconds
  maxBatch: 50,
  maxQueue: 10000,
  timeout: 10,         // seconds
  enforcementEnabled: false,
  failClosed: false,
});

Adapter roadmap

| Surface | Status | |---|---| | Core (track, run, trackAction, authorize) | ✅ shipped | | Vercel AI SDK (instrumentTools) | ✅ shipped | | LangChain.js (instrument callback) | ✅ shipped | | MCP (TypeScript) interceptor (instrumentMcpClient) | ✅ shipped |