# Upgrading to @pleach/core (/docs/quickstart/upgrading-to-core)



The quickstart surface — `createPleachRoute()` and
`simpleChatbot()` — covers most "I want a streaming chat" cases.
When you need more control, you drop one layer down to
`@pleach/core` directly. This page walks the upgrade.

## When to upgrade [#when-to-upgrade]

Reach for `@pleach/core` directly when you need:

* **A `HarnessPlugin`** — custom prompt contributions, intent
  resolvers, tool-call validators, stream observers, post-tool
  enrichment. See [Plugin contract](/docs/plugin-contract).
* **Custom tools** — anything you'd write as a tool definition the
  agent can invoke. See [Tools](/docs/tools) and [Base tools](/docs/base-tools).
* **Custom `orchestratorConfig`** — pinned model, family-strict
  cascade rules, provider hedge policies, custom transport
  (Bedrock, Vertex, Azure OpenAI). See [Providers](/docs/providers).
* **Custom storage** — Postgres / Supabase / your own
  `SessionStorageAdapter`. See [Storage](/docs/storage).
* **Custom rendering** — your own React UI, your own streaming
  protocol, your own non-HTTP transport.
* **Multi-tenant isolation** — `tenant_id` scoping, per-tenant
  rate limiting, per-tenant cost rollup. See
  [Multi-tenant](/docs/multi-tenant).
* **Interrupts** — pause-and-edit flows, human-in-the-loop. See
  [Interrupts](/docs/interrupts).
* **Checkpointing + time travel** — rollback, replay, divergent
  history. See [Checkpointing](/docs/checkpointing) and
  [Time travel](/docs/time-travel).

If none of the above fit, you don't need to upgrade. Stay on the
quickstart surface.

## Escape hatch: `bot.runtime` [#escape-hatch-botruntime]

If you're already using `simpleChatbot` from `@pleach/recipes` and
just need to reach under the hood, the `Chatbot` returned by
`simpleChatbot()` exposes its underlying `SessionRuntime`
directly:

```typescript
import { simpleChatbot } from "@pleach/recipes/chatbot";

const bot = simpleChatbot({ /* ... */ });

// `bot.runtime` IS the SessionRuntime — same type, same identity.
// Reach for it directly:
const session = await bot.runtime.createSession();
const stream = bot.runtime.executeMessage(session.id, "hello");

for await (const event of stream) {
  // handle the StreamEvent discriminated union yourself
}
```

The escape hatch is the documented progressive-disclosure path. The
factory contract at `packages/recipes/src/chatbot.ts` line 80
declares `readonly runtime: SessionRuntime` and the README marks it
as the supported escape route. You can mix-and-match: use
`bot.ask()` for the easy path, reach into `bot.runtime` for the
hard parts.

## Direct `createPleachRuntime` use [#direct-createpleachruntime-use]

Skip the recipes wrapper entirely:

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

// At app boot: register your provider adapter ctor once.
setOrchestratorAdapterCtor(MyOrchestratorAdapter);

const runtime = createPleachRuntime({
  host: {
    strategies: {
      orchestratorConfig: {
        provider: "anthropic",
        model: "claude-sonnet-4",
        apiKey: process.env.ANTHROPIC_API_KEY!,
      },
    },
  },
  plugins: [myPlugin],
});

const session = await runtime.createSession();

for await (const event of runtime.executeMessage(session.id, "hello")) {
  switch (event.type) {
    case "text":
      process.stdout.write(event.delta);
      break;
    case "tool-call":
      console.log("tool:", event.toolName, event.args);
      break;
    case "error":
      console.error("stream error:", event.message);
      break;
  }
}
```

The `SessionRuntime` returned is the same type `simpleChatbot()`
wraps. Session semantics are preserved: `createSession()` mints a
session id; `executeMessage(sessionId, message)` appends a user turn
and streams the assistant response; multi-turn memory is the
default (use a fresh `createSession()` to start over).

## Why a subpath for `createPleachRuntime`? [#why-a-subpath-for-createpleachruntime]

The factory imports from `@pleach/core/runtime`, not the top-level
`@pleach/core` barrel. This is intentional and documented at
`packages/recipes/src/chatbot.ts` lines 18-26.

The bundle-local `_registeredOrchestratorAdapterCtor` state that
the factory reads is the SAME state that consumers register via
`setOrchestratorAdapterCtor`, which is also exposed on the
`@pleach/core/runtime` subpath. The top-level `@pleach/core` ESM
bundle does NOT re-export the setter today; the subpath does. Using
the subpath in both places makes the orchestrator-config plumbing
actually wire end-to-end. See [Subpath exports](/docs/subpath-exports)
for the full subpath surface.

## Trade-off: route handler vs direct runtime [#trade-off-route-handler-vs-direct-runtime]

| Want                                                  | Use                                          |
| ----------------------------------------------------- | -------------------------------------------- |
| HTTP boundary, streaming over fetch                   | `createPleachRoute()`                        |
| Node/CLI/script, in-process                           | `simpleChatbot()` or `createPleachRuntime()` |
| React UI, no custom transport                         | `<ChatBox />` + `createPleachRoute()`        |
| Custom transport (WebSocket, SSE-with-custom-framing) | `createPleachRuntime()` directly             |
| Custom plugins, tools, intent resolvers               | `createPleachRuntime({ plugins })`           |

Mix freely. The route handler is a thin shell around
`createPleachRuntime`; the recipes `simpleChatbot` is a thin shell
around `createPleachRuntime`; both pass options through.

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

<Cards>
  <Card title="Runtime construction" href="/docs/runtime-construction" description="`SessionRuntime` constructor + `createPleachRuntime` factory in full detail." />

  <Card title="Session runtime" href="/docs/session-runtime" description="The 17-method `SessionRuntime` surface — sessions, messages, plugins, observers." />

  <Card title="Plugin contract" href="/docs/plugin-contract" description="`HarnessPlugin` — the 50+ contribution hooks for customizing runtime behavior." />

  <Card title="Subpath exports" href="/docs/subpath-exports" description="`@pleach/core` ships 40+ subpaths. Which one to import from." />

  <Card title="Providers" href="/docs/providers" description="Provider switching, family-strict cascade, model resolution." />
</Cards>
