# Runtime construction (/docs/runtime-construction)



[Getting started](/docs/getting-started) shows the route-handler
path — `createPleachRoute()` wraps everything below into one
fetch handler. This page is for the cases where you need direct
access to the runtime:

* A headless script, eval harness, or test fixture (no HTTP boundary).
* A multi-tenant SaaS with per-tenant storage / policies / plugins.
* A custom transport that doesn't fit the `Request → Response` shape.
* An existing React app that wants the hook surface without the route handler.

Three entry points, ordered by depth.

## `createPleachRuntime` — the factory [#createpleachruntime--the-factory]

One line, sensible defaults baked in: in-memory storage, the
default `memoryCacheBackend`, no plugins, no safety policies.
Use it in tutorials, eval harnesses, and headless scripts.

```typescript
import { createPleachRuntime } from "@pleach/core";

const runtime = createPleachRuntime();

const { id: sessionId } = await runtime.sessions.create();

for await (const event of runtime.executeMessage(sessionId, "Hello")) {
  console.log(event.type, event);
}
```

Every field on `CreatePleachRuntimeConfig` is optional. The
typical form layers on a persistent storage adapter, an explicit
`tenantId`, and a plugin set:

```typescript
import { createPleachRuntime } from "@pleach/core";
import { SupabaseAdapter } from "@pleach/core/sessions";

const runtime = createPleachRuntime({
  userId: "alice@example.com",
  tenantId: "acme-corp",
  storage: new SupabaseAdapter({ client: supabase }),
  plugins: [myAppPlugin],
});
```

Model and provider are not locked at factory creation —
per-session locking is the canonical pattern. Pass `provider` and
`model` when you mint the session:

```typescript
const session = await runtime.sessions.create({
  provider: { type: "anthropic" },
  model:    { id: "claude-sonnet-4-5" },
});
```

The factory is a thin layer over the `SessionRuntime`
constructor; the `advanced?: Partial<SessionRuntimeConfig>`
pass-through reaches the full config surface.

## `SessionRuntime` — the constructor [#sessionruntime--the-constructor]

When you need persistent storage, checkpointing, or per-request
user identity, drop to the constructor directly:

```typescript
import { SessionRuntime } from "@pleach/core";
import { SupabaseAdapter } from "@pleach/core/sessions";
import { SupabaseSaver } from "@pleach/core/checkpointing";

const runtime = new SessionRuntime({
  storage:      new SupabaseAdapter({ client: supabase }),
  checkpointer: new SupabaseSaver({ client: supabase }),
  userId:       "user_123",
});

const { id: sessionId } = await runtime.sessions.create();

for await (const event of runtime.executeMessage(sessionId, "Hello")) {
  console.log(event.type, event);
}
```

The full constructor surface — including plugin sets, the
provider, the channel registry, the interrupt manager, and the
extension loader — lives at
[SessionRuntime](/docs/session-runtime).

## `HarnessProvider` — the React surface [#harnessprovider--the-react-surface]

If you want the hook surface without the route handler — for
example, embedding the runtime in an Electron app or a React
Native shell — use `@pleach/core/react` directly:

```tsx
// app/layout.tsx
import { HarnessProvider, useHarness } from "@pleach/core/react";

function App() {
  return (
    <HarnessProvider userId="user_123">
      <ChatComponent />
    </HarnessProvider>
  );
}

function ChatComponent() {
  const { createSession, sendMessage, messages } = useHarness();

  return (
    <div>
      <button onClick={() => createSession()}>New Session</button>
      {messages.map((m) => (
        <div key={m.id}>{m.content}</div>
      ))}
    </div>
  );
}
```

The provider owns one `SessionRuntime` instance per mount and
hands it down through context. See the
[React](/docs/react) page for the full hook + facade surface.

## Mock mode for tests and CI [#mock-mode-for-tests-and-ci]

For local development without a database or provider account,
`HARNESS_MOCK_MODE=true` gates the mock **tool executor** — a
host-side utility (`createMockToolExecutor` / `isMockModeEnabled`)
that synthesizes tool results. It is the only thing that reads the
env var; the runtime constructors (`new SessionRuntime`,
`createPleachRuntime`) do not, so wiring mock mode into a runtime is
a manual step — construct the mock executor and pass it in, and pair
it with the in-memory `MemoryAdapter` + `MemorySaver` yourself. The
mock executor does not classify seams or write ledger rows; it only
produces synthesized tool outputs. (The default provider-decision
ledger is `NoopProviderDecisionLedger`, which drops all writes unless
a host supplies a real ledger.) A regression test (planned:
`@pleach/eval`; DIY today) composes these pieces itself.

## Choosing between the three [#choosing-between-the-three]

| If you're building...                                       | Reach for                                          |
| ----------------------------------------------------------- | -------------------------------------------------- |
| A tutorial, eval harness, headless smoke script             | `createPleachRuntime`                              |
| An HTTP chat handler + UI in five minutes                   | [`@pleach/core/quickstart`](/docs/getting-started) |
| A multi-tenant SaaS, regulated deployment, custom transport | `new SessionRuntime(...)` directly                 |
| An existing React app composing the runtime                 | `HarnessProvider` + `useHarness`                   |

The four are the same substrate at different abstraction layers
— a `createPleachRoute` handler internally constructs a
`SessionRuntime` per request; `createPleachRuntime` is the
substrate factory the handler delegates to; `HarnessProvider`
wraps the same constructor for React.

## Already on Anthropic Enterprise or OpenAI Enterprise? [#already-on-anthropic-enterprise-or-openai-enterprise]

Pleach runs underneath. `AnthropicSdkProvider` and the AI SDK's
OpenAI provider both accept your existing workspace or project
admin key; the runtime stamps `tenantId` on every audit row so
per-axis rollup runs inside the Workspace or Project you already
pay for. See
[Migrating from Anthropic Enterprise](/docs/migrating-from-anthropic-enterprise)
and
[Migrating from OpenAI Enterprise](/docs/migrating-from-openai-enterprise)
for the contract-composition walk-through.

## Where to go next [#where-to-go-next]

<Cards>
  <Card title="SessionRuntime" href="/docs/session-runtime" description="Full constructor surface — storage, checkpointer, plugins, channels, extensions." />

  <Card title="Providers" href="/docs/providers" description="Wire a provider — AI SDK, Anthropic SDK, or your own AgentProvider implementation." />

  <Card title="Turn lifecycle" href="/docs/turn-lifecycle" description="What executeMessage does — the event stream, the four seams, the audit row." />

  <Card title="Subpath exports" href="/docs/subpath-exports" description="The full @pleach/* subpath surface, per package." />
</Cards>
