# verticalAgent (/docs/recipes/vertical-agent)



`verticalAgent` is the wiring layer for vertical-AI products
built on `@pleach/core`. You author a `HarnessPlugin` for your
domain — the plugin contributes stream observers, tool policies,
prompt context, post-tool enrichers, or custom event channels —
and the recipe forwards it (plus any `extraPlugins`) to
`createPleachRuntime({plugins: [...]})`. The returned `Chatbot`
shape matches `simpleChatbot`.

Best fit: **a vertical AI startup*&#x2A;. The defining
constraint is &#x2A;"We'll write a plugin;
we won't write a runtime."* This recipe is that wiring layer.

## Quickstart [#quickstart]

```ts
import { verticalAgent } from "@pleach/recipes/vertical-agent";
import { myLegalDomainPlugin } from "./plugins/legal";

const bot = verticalAgent({
  domainPlugin: myLegalDomainPlugin,
  systemPrompt: "You are a contract-review assistant.",
  orchestratorConfig: {
    provider: "anthropic",
    model: "claude-sonnet-4-6",
    apiKey: process.env.ANTHROPIC_API_KEY!,
  },
});

console.log(await bot.ask("Review clause 4.2 of the attached contract."));
```

Compose cross-cutting concerns via `extraPlugins`:

```ts
const bot = verticalAgent({
  domainPlugin: myLegalDomainPlugin,
  extraPlugins: [observePlugin, metricsPlugin],
});
```

## What it does [#what-it-does]

The factory destructures `domainPlugin` and `extraPlugins`
from config, concatenates them with any
`config.plugins` the caller already supplied, and forwards
the merged array as the `plugins:` field of
`createPleachRuntime(...)`. Plugin order is:

1. Caller-supplied `config.plugins` (if any).
2. `domainPlugin`.
3. `extraPlugins` in order.

Every `ask()` call walks the same hook ladder a hand-rolled
`SessionRuntime` would — `contributeStreamObservers`,
`contributeApprovalFlow`, `contributeContinuationPolicy`
(its `allowedTools`), `contributePromptContextBridge`,
post-tool-tier enrichers, custom event channels — with the
domain plugin's verdicts composing first in the verdict
ladder. The recipe itself adds no domain logic.

## Config reference [#config-reference]

```ts
interface DomainPluginLike {
  readonly id?: string;
  readonly name?: string;
  // All contributeX hooks are optional; not enumerated here
  // because that would couple the recipe to a specific
  // @pleach/core version. The installed core enforces the
  // real HarnessPlugin contract at construction time.
  readonly [key: string]: unknown;
}

interface VerticalAgentConfig extends CreatePleachRuntimeConfig {
  /** The consumer's HarnessPlugin for their domain. */
  domainPlugin: DomainPluginLike;
  /** Cross-cutting plugins registered AFTER the domain plugin. */
  extraPlugins?: readonly DomainPluginLike[];
  systemPrompt?: string;
  orchestratorConfig?: Record<string, unknown>;
}
```

## Common gotchas [#common-gotchas]

* **The plugin contract lives in `@pleach/core`, not here.**
  `DomainPluginLike` is intentionally loose so the recipe
  does not couple to a specific core version. The installed
  core validates the plugin's hook shapes at construction
  time; runtime errors surface there, not in the recipe.
* **The recipe does not author the plugin.** That's the
  vertical builder's value-add. See the canonical 4-example plugin template at
  [`Plugin authoring standards`](/docs/plugin-authoring-standards)
  and [`Plugin contract`](/docs/plugin-contract).
* **Verdict order is plugin-array order.** The domain plugin
  registers before `extraPlugins`, so its `contributeStreamObservers`
  amend verdicts compose first. If a cross-cutting plugin
  (observability, compliance) needs first-look, put it in
  `config.plugins` instead of `extraPlugins`.
* **Plugin instances are not deduplicated.** Pass the same
  plugin object twice and it will register twice. Hooks fire
  in registration order.

## See also [#see-also]

* [`@pleach/recipes` overview](/docs/recipes-pleach-recipes) —
  every recipe in one page.
* [`Plugin contract`](/docs/plugin-contract) — the
  `HarnessPlugin` interface this recipe forwards.
* [`Plugin authoring standards`](/docs/plugin-authoring-standards)
  — conventions for `id`, `name`, and hook scope.
* [`Plugin bundles`](/docs/plugin-bundles) — packaging
  patterns for distributing a domain plugin as a sibling SKU.
