Project layout
Pleach doesn't prescribe a layout. The runtime is constructed in user code; adapters are constructor args. A working default plus per-use-case shapes.
Pleach is a library, not a framework. There's no pleach.config.ts,
no defineConfig, no required directory. The runtime is a class
you construct; adapters are constructor arguments; storage paths
live in whichever adapter you wire.
This page exists because that freedom is unhelpful on day one. So: what's actually load-bearing, what a layout looks like when you stop thinking about it, and what each use case adds on top.
What the runtime actually requires
Three things. Everything else is convention.
| Requirement | Where it lives | Page |
|---|---|---|
Exactly one SessionRuntime per host process | A module you write — typically exports the instance | SessionRuntime |
A ProviderDecisionLedger you've picked | Passed into the SessionRuntime constructor | Audit ledger |
| A host adapter, if you serve HTTP | A route handler in your web framework | Host adapter · API routes |
The ledger choice is the one that catches people. The default is
MemoryProviderDecisionLedger — fine for tests, lost on restart.
NoopProviderDecisionLedger writes nowhere; pick it deliberately,
not by accident. Anything durable is an adapter you wire.
A layout that works
A Next.js App Router app with one agent, one tool, and an HTTP route. Move any of these — the runtime resolves whatever your imports resolve.
my-app/
src/
pleach/
runtime.ts # constructs and exports `runtime`
tools/
search.ts # → /docs/tools
prompts/
triage.ts # → /docs/prompt-builder
app/
api/
agents/[id]/route.ts # → /docs/api-routes
page.tsx # → /docs/react
package.jsonThe split is convention, not contract:
src/pleach/runtime.ts— the single placenew SessionRuntime(...)is called. Exports the instance so the host route and any background worker import the same one.src/pleach/tools/— each file exports adefineTool(...)call. The runtime doesn't scan this directory; you import the tools where you register them.src/pleach/prompts/— plain modules that build prompt objects. The runtime doesn't scan this directory either.src/app/api/agents/[id]/route.ts— exportscreatePleachRoute()from@pleach/core/quickstart. This is the shipped host adapter the runtime expects when it's behind HTTP.
Read the inverse to internalize the model: nothing the runtime
does depends on directory names. Rename pleach/ to agents/,
flatten everything into src/, split per-feature — the runtime
boots the same. The only thing it sees is the SessionRuntime
you handed it.
A common second instinct: "where do checkpoints and the audit
database live on disk?" Wherever the adapter you wired writes
them. MemoryProviderDecisionLedger writes nowhere; the
SQLite recipe
writes to a path you pass in; a managed-DB adapter writes to a
connection string. Pleach doesn't pick the path because Pleach
doesn't know whether you're on one machine, a Lambda, or a
multi-region cluster.
Descend into a use case
Each shape adds a small, predictable delta to the baseline above. Open the page for the shape closest to yours; the layout grows in one of a few directions, not many.
Customer support
Baseline layout fits. Add a tool module per integration; the ledger row is what your QA team reads.
Research
Adds a subagent registry and a deterministic replay path so 'why did it cite that?' is one command, not a log dive.
Coding agent
Adds a sandboxed tool surface and a checkpoint-per-edit pattern. Survives restarts means a durable checkpoint adapter, not the in-memory default.
Internal knowledge
Adds an index/embeddings module the tools call into, plus scrubbers on the audit-ledger plug-points.
Multi-tenant SaaS
Build the runtime per request from the tenant resolver. The tenant facet keys the cache, ledger, and OTel attributes.
Regulated domain
Adds @pleach/compliance — tamper-evident hash chain on the ledger, PII scrubbers on the row, GDPR soft-delete on the request.
Filesystem-touching recipes
Recipes are the small, copy-pasteable pieces. Three touch the project's on-disk shape directly:
| Recipe | What it adds |
|---|---|
| Next.js App Router chat with streaming | The app/api/agents/[id]/route.ts + client component pair the baseline assumes. |
| Custom storage adapter: SQLite | A durable adapter that picks a real on-disk path for the audit ledger. The first thing past MemoryProviderDecisionLedger. |
| Building a custom event-log projection | Where projection state lives — typically alongside the ledger, in whatever store the ledger writes to. |
What Pleach won't tell you to do
A short list because each of these is a deliberate non-choice.
- No config file. The runtime is wired in code so the
TypeScript compiler sees every adapter you pass and your IDE
jumps to the definition. A
pleach.config.tswould buy convention at the cost of static analysis. - No reserved directory. Nothing called
.pleach/is created by the runtime. Storage paths live in whichever adapter you wire — the deployment shape (one machine vs. Lambda vs. managed DB) decides the path, not the library. - No
agents/folder convention. Theagents/<specName>/prefix you'll see in Agents is an internal channel-path prefix the runtime uses to namespace agent state. It's a string in the runtime's address space, not a directory on your disk. - No required test layout. Eval and replay reads recorded sessions, not test files. Put tests wherever your test runner finds them.
If a piece of structure isn't on this page, the runtime doesn't have an opinion on it — and that's the contract.
Adoption paths
Two ways to adopt Pleach — brownfield (hook @pleach/observe into your existing agent loop) or greenfield (take @pleach/core as your substrate).
What's new — June 2026
June 2026 substrate across @pleach/core, gateway, compliance, replay, eval, coding-agent, and recipes — multi-tenant gateway primitives, replay-fork, swarm spawn substrate, and real recipe bodies.