@pleach/*
The agent runtime built around the audit row.
Every model call your agent makes writes one row you own. That's all it takes to bill each customer, answer an auditor in SQL, and replay last week's bug exactly.
The name is horticultural. Pleaching weaves living branches into one load-bearing hedge. Pleach weaves every call, tool, and subagent your agent produces into one structure you can query, prune, and replay.
Free and fair-source under FSL-1.1-Apache-2.0. Runs on your own database — no phone-home, no license check, no account. In production today.
| family | rows | tokens | est. cost |
|---|---|---|---|
| openai | 2 | 142 | $0.0004 |
No provider key. npm run dev runs a keyless demo — the real graph, base-tools, and audit ledger — and /audit shows the rows. Set ANTHROPIC_API_KEY to go live.
import { createPleachRuntime } from "@pleach/core";
const runtime = createPleachRuntime();
const session = await runtime.sessions.create();
for await (const event of runtime.executeMessage(session.id, "Hello")) {
console.log(event.type, event);
}Defaults to in-memory storage + mock mode. Add a provider, a Supabase adapter, or plugins as you need them. To see the audit rows themselves, npx pleach init scaffolds a keyless demo and an /audit view.
SELECT * FROM harness_auditable_calls ORDER BY created_at DESC LIMIT 1;
prev_hash links it to the call before it; subagent spend rolls up to parent_turn_id.What you can do with it
We graded twenty agent-runtime capabilities against the market — Helicone, Langfuse, LangSmith, Portkey, LangGraph, Temporal, and the rest. These are the few the field admits it doesn't ship. The honest version, in both directions, is the landscape.
See every tool call and subagent, rolled up to the turn that caused it
Each LLM call writes one typed row keyed to the turn — which tool ran, how deep the subagent went, how many tokens it burned. Subagent spend rolls up to the parent turn as a stored field, not a trace-tree walk at query time. Per-customer billing then falls out of one SQL query.
Subagent & tool-call rollupReplay any run, byte-identical
Same version, same input, same checkpoint replays exactly — across Node, Browser, and Edge — or strict mode throws and names the first event that diverged. Durable-execution platforms journal the output; most frameworks re-fire the call and may drift. This replays the path.
DeterminismA tamper-evident record only you hold
Every row lands in your Postgres with a prev-hash chain. Backfill or delete a row and the chain breaks at the verifier pass. No observability or gateway tool we surveyed ships a hash chain over the call log.
The hash chainRouting that keeps replay honest
At session start the lock freezes the tokenizer, prompt-cache key, tool-call dialect, and refusal pattern. A transient fallback stays in the same family; any cross-provider change is logged on the row. Replay you can trust, because the lock keeps it valid.
Family-lockRewind to before the bad decision
Fork a session at any past event — not just a node boundary — then try a different path. Per-channel checkpoints make the rewind exact, so an agent can catch its own mistake instead of escalating.
Time-travel & forkSELECT tenant_id,
SUM(token_usage) AS spend
FROM harness_auditable_calls
GROUP BY tenant_id, turn_id;turn_id as row 1, subagent_depth = 1. Finance reads the per-turn rollup as a single GROUP BY rather than stitching child sessions together after the fact.How it works
Three structural decisions, not three best practices. Everything above reduces to these. A turn walks a fixed four-stage lattice — a shape a regulator can read off this page.
A turn walks a four-stage lattice
Each turn moves through anchor-plan, tool-loop, synthesize, and post-turn, under a discipline that fires exactly one final answer. The stages are a fixed shape a regulator can read off this page — which frees the audit row to carry what changes.
One append-only audit row per call
Each row carries the turn, the tool, the model, and the tokens, with subagent cost rolled up to the parent turn. Joinable to your billing or compliance schema today; replayable against the fingerprint stream.
Routing stays in one model family
Tokenizer, prompt-cache key, tool-call dialect, and refusal pattern freeze at session start. A transient fallback stays in-family; any cross-provider change is logged on the row.
The runtime is a contract, not a TypeScript surface — HTTP + SSE routes, the audit row, the checkpoint envelope, the version vector. @pleach/coreis the reference implementation; a Go implementation round-trips the same recorded turns, so the wire shapes aren't TypeScript-flavored. Read the contract or the architecture deep-dive.
@pleach/core sits in exactly one of these four stages. The audit:graph-stages CI gate fails the build on an out-of-lattice edge — the diagram is the invariant, not a best-practice diagram.Should you stay where you are?
The honest version. If your shape fits one of these, reach for it — you'll ship faster. The full comparison adds LlamaIndex, Mastra, Goose, OpenHands, AutoGen, and CrewAI.
Multi-tenant SaaS, an audit-bound vertical, replay-as- regression-test, or the agent isthe product? That's the target. The same agent built four ways shows what you write yourself versus what the runtime hands you.
Vercel AI SDK
Reach for it when: A single-shot chat with tools. No persistence, no per-customer cost, no audit obligation.
Give up vs Pleach: No turn-keyed audit row, no replay determinism, no family-locked routing.
LangChain / LangGraph
Reach for it when: Chain abstractions, a large component catalog, hosted LangSmith for tracing.
Give up vs Pleach: Callbacks without shared row identity. Per-call cost is manual. No language-agnostic wire contract.
Claude Code / Goose
Reach for it when: A dev harness you run on your own machine — code review, refactor, terminal-native agents.
Give up vs Pleach: Single-user shell, not embeddable in a customer-facing SaaS. No multi-tenant audit.
Inngest / Trigger.dev
Reach for it when: Durable function chains, step-memoized retries, cron, fan-out, a hosted run dashboard.
Give up vs Pleach: Run history is the vendor's shape, not your billing schema. No family-lock, no hash chain. Use both — see Pleach + Inngest.
what you give up detail.Keep your stack
Already shipping on the AI SDK or LangChain? Add the audit row without a rewrite.
@pleach/observe is the brownfield entry point. Wrap the agent loop you already run in about fifteen lines and write one typed audit row per LLM call to a backend you pick. No runtime migration, no hosted control plane, no new vendor.
Wrap the loop you already have
It sits in front of the Vercel AI SDK, LangChain, the OpenAI or Anthropic SDK called directly, or a hand-rolled loop. Three moving parts: init, a per-turn recorder, one destination plug. BYOK observability →
Your storage, your control plane
The row lands in the destination you pass — your Postgres, Supabase, an OpenTelemetry collector, or an in-memory buffer. The OTel path is buyer-callback only: zero @opentelemetry/* runtime dependencies. @pleach/observe →
A runway, not a dead end
The ObserveRow is a strict subset of core's AuditableCall, so rows the SDK writes today stay valid rows the runtime reads tomorrow. Adopt @pleach/core later and the history comes with you — the migration is monotonic by construction. Adoption paths →
Per-customer attribution falls out of the row — (tenantId, turnId, toolName, model, tokens, costUSD) in one GROUP BY. Need replay determinism, family-locked routing, channels, or time-travel? Those live in the runtime, the greenfield path through @pleach/core. @pleach/observe is in alpha; the SDK layer stays free.
import { init, startTurn } from "@pleach/observe";
import { postgres } from "@pleach/observe/destinations";
init({ destination: postgres({ pgClient }) });
const turn = startTurn({ tenantId, sessionId });
await turn.recordCall({ model, tokens, costUSD }); // one row per call
await turn.end();ObserveRowis a strict subset of core's AuditableCall, so rows the SDK writes today stay valid rows the runtime reads tomorrow — adopt @pleach/core later and the history comes with you.Already paying the lab
On Anthropic or OpenAI Enterprise? Pleach composes underneath it.
Your contract keeps doing its job — SSO, ZDR, Workspaces or Projects, the Admin API, prompt caching, snapshot pinning. Pleach is an npm install plus a Postgres table you already run: no new vendor, no new SOC 2 boundary, no new ZDR review. It adds the three walls the contract doesn't cover.
Per-customer rollup inside one Workspace
The Admin API reports the Workspace or Project total — not the customers, teams, or cost centers inside it. The row carries an opaque tenant id on every call; wire it to whatever you bill or audit against. One GROUP BY, and the sums reconcile to the vendor total. Multi-tenant →
Tamper-evident row in your own DB
ZDR governs what the vendor stores. The downstream auditor asks about what you stored. Every row lands in your Postgres with a prev-hash chain that breaks at the verifier on any backfill or removal. Hash chain →
Replay across snapshots, not just inside one
A pinned snapshot doesn't make a turn replayable. The fingerprint records the inputs per turn, so drift between two snapshots surfaces as a diff in CI — early warning before a customer ticket. Determinism →
Anthropic Enterprise
Keep SSO, ZDR, Workspaces, Admin API
The Admin API tells you what the Workspace owed Anthropic. Pleach tells you which of your customers caused it.
OpenAI Enterprise
Keep SSO/SCIM, ZDR, Projects, Usage API
The Usage API tells you what the Project spent. Pleach tells you which of your customers caused it.
Bedrock, Azure OpenAI, or Vertex in the path? Same walls, different transport — cloud-mediated adapters land in @pleach/gateway. Two siblings ride the same contract footprint: the sandboxed coding agent and the language-agnostic contract that answers "is this TypeScript-only?" before IT asks.
SELECT tenant_id,
SUM(token_usage) AS spend
FROM harness_auditable_calls
WHERE workspace_id = 'ws_acme' -- one Admin-API Workspace
GROUP BY tenant_id; -- sums reconcile to the vendor totalworkspace total. Pleach adds the tenant_id on every row, so one GROUP BY tells you which customer caused it — and the per-customer sums add back up to what the Admin API billed.Quick answers
The short version. The full FAQ covers the rest.
Built so an agent can read its own history and rewind itself — the same typed rows and checkpoints a developer reads. The docs render to /llms.txt and /llms-full.txt.
- What is Pleach?
- Pleach is a free, fair-source agent runtime. Every LLM call your agent makes writes one typed row to a database you own — which gives you per-customer cost, compliance you can query in SQL, and deterministic replay. @pleach/core is the TypeScript runtime, in production today.
- How does Pleach attribute LLM cost per customer?
- Every audit row carries which tenant, which turn, which model, and how many tokens — with subagent spend rolled up to the turn that caused it. Per-customer billing falls out of one SQL GROUP BY, not a separate cost pipeline.
- Already on Anthropic Enterprise or OpenAI Enterprise — what does Pleach add?
- It composes underneath. Your contract keeps doing its job; Pleach adds per-customer cost rollup inside one Workspace or Project, a tamper-evident audit row in your own Postgres, and replay across model snapshots. No new vendor — an npm install plus a table. See /docs/migrating-from-anthropic-enterprise.
- What license does Pleach use? Is it open source?
- Fair source — FSL-1.1-Apache-2.0. Source-available, free in production. Each release converts to Apache-2.0 two years after it ships. Not OSI-approved open source during that window.
- How is Pleach different from the AI SDK or LangChain?
- They ship faster for single-shot agents and broad chain composition. Pleach is the runtime when the agent is the product: per-customer cost, deterministic replay, time-travel checkpoints, and a typed audit row joinable to your billing and compliance schemas. See /docs/comparison.
SELECT seq, event, payload FROM session_manifest WHERE session_id = 's_91c' ORDER BY seq;
seq. An agent reads it back to know what it did; you replay or fork from any event — ckpt_7 makes the rewind exact.