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

kenpachi

v0.3.17

Published

A production-shaped agent SDK: typed tools, time-travel context, self-healing tool calls, saga rollback, and dynamic tool synthesis.

Downloads

3,046

Readme

kenpachi

Logo

A small, typed agent SDK for building tool-using LLM agents in TypeScript — built from scratch on top of raw provider fetch calls (no vendor SDK dependency) with a few things most minimal agent loops skip:

📖 Full Documentation: https://kenpachi.mintlify.site/introduction

  • Time-travel context — every turn is snapshotted; branch and resume from any prior point without re-calling the model for turns you already ran.
  • Argument pre-coercion & validation — primitive arguments (like numeric or boolean strings) are automatically pre-coerced before Zod schema validation.
  • Saga rollback — register a compensating action per tool call; if a later step in the same batch fails, already-completed steps are undone in reverse.
  • KenpachiSDK dynamic tool synthesis — the model can author pure-logic tools (math, parsing, formatting) at runtime. Anything needing a credential goes through a ConnectorRegistry you configure ahead of time — the model writes the glue code, never the secret.
  • Pluggable memoryInMemoryStore with tokenized keyword matching & synonym search, plus Mem0MemoryStore adapter included.
  • Multi-agent handoffs — wrap specialist agents as tools with automatic message ordering safeguards.

Install

npm install kenpachi

Quick start

import { z } from "zod";
import { Agent, defineTool, createAnthropicProvider } from "kenpachi";

const getWeather = defineTool({
  name: "get_weather",
  description: "Get the current weather for a city",
  schema: z.object({ city: z.string() }),
  async execute({ city }) {
    return { city, tempC: 24, condition: "sunny" };
  },
});

const provider = createAnthropicProvider({
  apiKey: process.env.ANTHROPIC_API_KEY!,
  model: "claude-sonnet-4-6",
});
const agent = new Agent(provider, [getWeather]);

const result = await agent.run("What's the weather in Nashik?");
console.log(result.text);

Time-travel

await agent.run("first message");
const snap = agent.context.listSnapshots().at(-1)!;

// Branch back to that point and try a different follow-up, without
// re-running the first exchange against the model.
const branched = agent.context.branchAt(snap.turnIndex);

Dynamic tools with a connector registry

import { ConnectorRegistry, synthesizeTool } from "kenpachi";

const registry = new ConnectorRegistry();
registry.register("weather", {
  baseUrl: "https://api.openweathermap.org/data/2.5",
  authEnvVar: "WEATHER_API_KEY", // secret lives in env, never in model output
  description: "OpenWeatherMap current conditions",
});

const tool = synthesizeTool(
  {
    name: "get_weather",
    description: "Fetches current weather for a city",
    parameters: { city: "string" },
    jsBody: `return await callConnector("/forecast?q=" + args.city);`,
    connector: "weather",
  },
  registry
);

Streaming

// Full event stream — token-by-token when the provider supports it
for await (const event of agent.stream("Tell me a story")) {
  if (event.type === "text_delta") process.stdout.write(event.text);
  if (event.type === "tool_call_start") console.log("\ncalling", event.name);
}

// Or the simpler shorthand on run():
const result = await agent.run("Tell me a story", {
  onText: (chunk) => process.stdout.write(chunk),
});
console.log(result.text);

agent.run() still works exactly as before — it's implemented as a thin wrapper around stream() that returns the final AgentRunResult. Providers without streamTurn() still work with both APIs; they just won't emit token-level deltas.

Handoffs

import { handoff } from "kenpachi";

const billingHandoff = handoff(billingAgent, "Use for billing or payment questions", {
  id: "billing", // tool name becomes handoff_billing
});

const triageAgent = new Agent(provider, [billingHandoff, techSupportHandoff]);

A handoff is a normal tool from the parent agent's point of view. Internally it spawns the target agent with a fresh context (via Agent.spawn()), seeded with the parent conversation according to context ("full" by default, or "summary" / "none"), runs it to completion, and returns its answer as plain text.

Security notes

  • The sandbox (src/sandbox.ts) uses Node's vm module for isolation, not a hard security boundary. It blocks accidental misuse (stray require, filesystem access) but is not a substitute for OS-level sandboxing (a separate worker process, gVisor, Firecracker) if you're running untrusted model output in a real production deployment.
  • Synthesized tools never receive raw secrets. Credentials are read from process.env inside the connector layer and injected into outgoing requests server-side — the model only ever sees the connector name.

Development

npm install
npm run typecheck
npm run test
npm run build

License

MIT