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:
- Caller-supplied
config.plugins(if any). domainPlugin.extraPluginsin 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.DomainPluginLikeis 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 standardsandPlugin contract. - Verdict order is plugin-array order. The domain plugin
registers before
extraPlugins, so itscontributeStreamObserversamend verdicts compose first. If a cross-cutting plugin (observability, compliance) needs first-look, put it inconfig.pluginsinstead ofextraPlugins. - Plugin instances are not deduplicated. Pass the same plugin object twice and it will register twice. Hooks fire in registration order.
See also
@pleach/recipesoverview — every recipe in one page.Plugin contract— theHarnessPlugininterface this recipe forwards.Plugin authoring standards— conventions forid,name, and hook scope.Plugin bundles— packaging patterns for distributing a domain plugin as a sibling SKU.
instrumentedCodingAgent
Coding-agent runtime whose executeStep(...) calls inherit a @pleach/observe sub-agent attribution scope. Every recordCall(...) downstream of the step carries [subagent, ...] without threading a recorder argument.
subagentSwarm
Bounded fan-out worker pool over any Chatbot. You supply the workers, the decompose function, and an optional reduce. The recipe enforces a maxFanOut concurrency cap and an optional per-root-turn USD cost ceiling — the application-layer defense for the runaway-loop case.