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

@runeward/sdk

v0.3.0

Published

TypeScript SDK for governing AI agent execution with policy, approvals, isolation, budgets, and signed evidence.

Readme

@runeward/sdk (TypeScript)

A dependency-light TypeScript client and Vercel AI SDK tool wrappers for the Runeward agent governance harness. Put policy, human approvals, isolated Citadels, Rationing, and signed Chronicles around an existing agent without replacing its model or framework.

The core RunewardClient uses the global fetch and has no runtime dependencies (Node 18+, Deno, Bun, browsers). The Vercel AI SDK wrappers require ai and zod; the LangChain.js wrappers require @langchain/core and zod; the Strands wrappers require @strands-agents/sdk and zod. All are optional peer dependencies, imported lazily.

Install

npm install @runeward/sdk                           # core client only
npm install @runeward/sdk ai zod                    # + Vercel AI SDK tools
npm install @runeward/sdk @langchain/core zod       # + LangChain.js tools
npm install @runeward/sdk @strands-agents/sdk zod   # + Strands Agents SDK tools

Build from this directory during development:

npm install
npm run build      # emits ./dist

Quick start

Start the control plane first (runeward serve, default http://localhost:8080), then:

import { RunewardClient, RunewardDenied, RunewardApprovalRequired } from "@runeward/sdk";

const rw = new RunewardClient({ baseUrl: "http://localhost:8080" }); // uses RUNEWARD_API_TOKEN in Node when set

const sbx = await rw.createSandbox("dev");
const version = await rw.shell(sbx.id, ["python3", "--version"]);
console.log(version.stdout); // "Python 3.11.2\n"

await rw.writeFile(sbx.id, "main.py", "print(2 + 2)");
const run = await rw.python(sbx.id, "exec(open('/workspace/main.py').read())");
console.log(run.stdout); // "4\n"

await rw.killSandbox(sbx.id); // always tear down when done

Use allowInsecure: true (or RUNEWARD_ALLOW_INSECURE_HTTP=1) only when you must call a non-loopback http:// control-plane endpoint.

Handling governance verdicts

The two governance outcomes are thrown as typed errors. A denial must not be blindly retried; an approval gate must pause for a human:

try {
  await rw.shell(sbx.id, ["rm", "-rf", "/"]);
} catch (err) {
  if (err instanceof RunewardDenied) {
    console.log("blocked by policy:", err.reason); // do NOT retry the same action
  }
}

try {
  await rw.writeFile(sbx.id, "/etc/hosts", "127.0.0.1 example");
} catch (err) {
  if (err instanceof RunewardApprovalRequired) {
    console.log("needs a human:", err.approvalId); // pause; approve/deny out-of-band
  }
}

Client method surface

| Method | REST endpoint | | --- | --- | | healthz() | GET /healthz | | listProfiles() | GET /v1/charters | | whoami() / readiness(profile) / simulatePolicy(...) | Identity, setup, and dry-run policy APIs | | listRuns() / getRun(id) | Durable provider-neutral Run lineage | | createSandbox(profile) | POST /v1/citadels | | listSandboxes() / getSandbox(id) / killSandbox(id) | GET/GET/DELETE /v1/citadels[/{id}] | | shell(sandbox, command, workdir?) | POST .../shell/exec | | python(sandbox, code) / node(sandbox, code) | POST .../code/{python,node} | | readFile / writeFile / listFiles / searchFiles | POST .../file/{read,write,list,search} | | audit(sandbox) / verifyAudit() | GET .../chronicle, GET /v1/chronicle/verify | | exportEvidence(sandbox) | Portable resolved Charter + signed Chronicle evidence | | createCohort / listCohorts / addTask / claimTask | Cohort lifecycle and leased work queue | | heartbeatTask / completeTask / failTask | Signed-lease task transitions | | createSnapshot / listSnapshots / restoreSnapshot | Tenant-scoped recovery | | listApprovals() / approve(id) / deny(id) | GET /v1/conclave, POST /v1/conclave/{id}/{approve,deny} |

Vercel AI SDK tools

import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { RunewardClient } from "@runeward/sdk";
import { makeRunewardTools } from "@runeward/sdk/ai-tools";

const tools = await makeRunewardTools(new RunewardClient());

const { text } = await generateText({
  model: openai("gpt-4o"),
  tools,
  maxSteps: 8,
  prompt: "Create a dev sandbox, run `node --version` in it, then tear it down.",
});

Tool names match the runeward MCP tools (runeward_create_citadel, runeward_shell, …). Governance verdicts are returned to the model as descriptive strings so it can react to a denial or an approval gate instead of crashing the run.

LangChain.js tools

import { ChatOpenAI } from "@langchain/openai";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { RunewardClient } from "@runeward/sdk";
import { makeRunewardTools } from "@runeward/sdk/langchain-tools";

const tools = await makeRunewardTools(new RunewardClient());
const agent = createReactAgent({ llm: new ChatOpenAI({ model: "gpt-4o" }), tools });

const res = await agent.invoke({
  messages: [{ role: "user", content: "Create a dev sandbox, run `node --version`, then tear it down." }],
});

Returns DynamicStructuredTool instances (from @langchain/core/tools) with the same runeward tool names and the same string-based verdict handling as above.

Strands Agents SDK

import { Agent } from "@strands-agents/sdk";
import { RunewardClient } from "@runeward/sdk";
import { makeRunewardTools } from "@runeward/sdk/strands-tools";

const tools = await makeRunewardTools(new RunewardClient());
const agent = new Agent({ tools });

const res = await agent.invoke("Create a dev sandbox, run `node --version`, then tear it down.");

Returns Strands tools built with tool({ name, description, inputSchema, callback }) (Zod schemas), with the same runeward tool names and string-based verdict handling.

Notes

  • deny is a policy decision, not a transient error. Don't retry the same action; pick a different, allowed approach.
  • require-approval is a hard pause. Surface the approvalId to a human and wait for the outcome.
  • Prefer the tightest profile that lets the task succeed.