pleach
Cookbook

verticalAgent

Chatbot pre-wired with your domain plugin. You write the HarnessPlugin (citation extraction, entity recognition, post-tool-tier enrichment, custom event channels); the recipe wires it into createPleachRuntime so every ask() turn picks up your domain logic without manual plumbing.

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. The defining constraint is "We'll write a plugin; we won't write a runtime." This recipe is that wiring layer.

Quickstart

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:

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

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

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

  • 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 and 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

On this page