CharClaw / Blog / Launching the Agents SDK

Launching the CharClaw Agents SDK

Today we're shipping @charclaw/agents 0.2.0 — the TypeScript SDK that powers every agent run inside CharClaw, now live on npm and free for you to use in your own projects.

One API. Six coding agents. Two sandbox runtimes. Background execution that survives a serverless cold-start. No polling glue, no per-CLI parser headaches, no shell-quoting bugs. That's the goal, and that's what's in this release.

The problem nobody really wants to solve

When you sit down with Claude Code, Codex, or Gemini in a terminal, things feel great. You type, the agent responds, tools fire, code gets written. The CLIs are good.

The instant you try to put one of those CLIs inside a product — a web app, a desktop daemon, a Slack bot, a scheduled autopilot — the experience falls apart in four directions at once:

  • Turns outlive HTTP requests. A 90-second refactor starts inside a Vercel function that ends in 60.
  • You can't read stdin/stdout pipes once the spawning process is gone. Polling-friendly transport or bust.
  • Each agent emits a different JSON event shape. Anthropic uses one. OpenAI uses another. Google has its own. You end up writing six parsers nobody's tested together.
  • Credentials in the same process as the agent is a bad time. You want isolation, not a hope-and-pray process.env.

Inside CharClaw we hit all four problems on day one. We hit them again every time we added a new agent. So we extracted the solution.

What's in 0.2.0

A single createSession(provider, options) entry point that abstracts all of the above. It returns a BackgroundSession that you can start(), getEvents(), cancel(), and getSession() back into from a different process.

import { Daytona } from "@daytonaio/sdk"
import { adaptDaytonaSandbox, createSession } from "@charclaw/agents"

const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY! })
const sandbox = adaptDaytonaSandbox(await daytona.create())

const session = await createSession("claude", {
  sandbox,
  env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
  model: "sonnet",
})

await session.start("Add input validation to /signup.")

while ((await session.getEvents()).running) {
  await new Promise(r => setTimeout(r, 1000))
}

Same six lines work for Codex, Gemini, Goose, OpenCode, and Pi — just change "claude" to whatever you like. Each agent's events are normalized to a single union:

type Event =
  | { type: "session"; id: string }
  | { type: "token"; text: string }
  | { type: "tool_start"; name: string; input?: unknown }
  | { type: "tool_end"; output?: string }
  | { type: "end"; error?: string }
  // …

Tool names get canonicalized too — "Bash", "shell", "command", and "exec" all collapse to shell so your UI doesn't need a switch statement per agent.

Restart-tolerant by default

Every session writes its config and parser cursor to ~/.charclaw-sessions/<id>/ inside the sandbox. If the Node process running your poll loop dies — serverless cold-start, deploy, transient crash — a fresh process can call getSession(id, { sandbox }) and pick up exactly where the previous one left off. No event replay, no double-counting.

Two runtimes, one interface

The same BackgroundSession works against either:

  • A Daytona cloud sandbox. Spin up a fresh micro-VM per turn or per workspace. Credentials never enter the sandbox unless you explicitly pass them as env.
  • The local host. For desktop apps, hobby projects, or anywhere a cloud sandbox is overkill. Uses spawn(detached: true) under the hood with the same log-file polling pattern.

Why AGPL-3.0

@charclaw/agents is AGPL-3.0-or-later. That choice is deliberate. The SDK does real work — wrapping six flaky CLIs, normalizing six event shapes, threading restart-tolerant state through two runtimes — and we'd rather not see that work become the proprietary moat of a SaaS competitor without anyone giving back.

For self-hosted use, internal tooling, or open-source forks, AGPL is a non-issue: build whatever you want. If you want to run a modified version as a closed-source network service, that's exactly the case AGPL is designed to surface — get in touch at anitc98@gmail.com and we'll talk about commercial licensing.

Honest about the seams

Two caveats worth flagging on day one of v0.2.0:

  • The Claude parser is stable; Codex, Gemini, Goose, OpenCode, and Pi parsers accept a tolerant superset of common JSON-Lines event shapes. They work for the cases I've actually run, but the JSON formats of those CLIs evolve faster than I can chase. If you find a mismatch, open an issue or a PR — the parsers are 100 lines each and easy to extend.
  • The mock agent replaces the previous Eliza built-in. It's an inline shell echo that emits a couple of structured events — useful for tests, deterministic, no API key needed. If you were using Eliza for anything, switch to "mock".

What's next

The roadmap (loose, not promises):

  • Tighten the non-Claude parsers based on real-world reports.
  • A tool_delta path so streaming tool input lands as it's typed, not all at once.
  • An optional resume flag on session.start so you can chain follow-up turns without reattaching.
  • SSH-based remote runtime adapter for users running CharClaw against a long-lived dev server instead of Daytona.

Get it

npm install @charclaw/agents @daytonaio/sdk

Full reference is in the SDK documentation. The source lives at github.com/AnitChaudhry/charclaw/packages/agents — issues, PRs, and questions all welcome there.

Thanks for reading. Build something with it and tell me how it goes.

— Anit