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_deltapath so streaming tool input lands as it's typed, not all at once. - An optional
resumeflag onsession.startso 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