pleach
Build

Heartwood

A persistent agent identity — a HEARTWOOD.md persona composed into the frozen prefix of the system prompt, the same way a hosted app loads identity from a database.

Heartwood is the agent's persistent identity — a persona that leads every turn's system prompt. A standalone @pleach/core consumer gives its agent a stable voice from a HEARTWOOD.md file (the "SOUL.md" pattern); a hosted app loads the same section from a database. Either way the identity is composed into the frozen prefix — the head of the system prompt that does not change turn-to-turn — so it sits inside the cacheable region rather than rotating the cache key.

Ships from the @pleach/core/quickstart barrel: createFileHeartwoodLoader, createHeartwoodPlugin, formatHeartwoodSection, and the HeartwoodSource type.

The file loader

createFileHeartwoodLoader() reads ./HEARTWOOD.md and formats it into an identity section. Point it elsewhere with path, and cap the working size with maxChars (default 2000).

import { createPleachAgent } from "@pleach/core";
import { createFileHeartwoodLoader } from "@pleach/core/quickstart";

const agent = createPleachAgent({
  context: process.env.OPENROUTER_API_KEY,
  heartwoodSource: createFileHeartwoodLoader(), // reads ./HEARTWOOD.md
});

A HEARTWOOD.md is plain markdown. An optional ## Voice & Tone heading splits persona from voice; the identity leads and the voice follows.

You are Atlas, a meticulous research companion. You cite every
claim and never guess at a number you have not seen.

## Voice & Tone
Terse, precise, no hedging.

The source contract

heartwoodSource is a HeartwoodSource() => string | null (or its async form). Return the identity text, or null for "no identity this run" (which leaves the prompt byte-identical to having no heartwood at all). createFileHeartwoodLoader is one implementation; a hosted app passes its own loader that reads a row from a database.

import type { HeartwoodSource } from "@pleach/core/quickstart";

const fromDb: HeartwoodSource = () => loadPersonaRowSync(userId)?.identity ?? null;
FieldTypeNotes
heartwoodSourceHeartwoodSource | undefined() => string | null (sync or async). Omit ⇒ no identity section.
heartwoodMaxCharsnumber | undefinedRoute-level HARD ceiling on the resolved section, default 8000.

A sync source is resolved once so the first turn already carries the identity; an async source primes fire-and-forget (the first turn may miss it, later turns have it). The heartwoodMaxChars ceiling is the cost backstop for the never-dropped frozen-prefix slot — a loader's own maxChars below it is honored verbatim, a raw huge source is bounded here.

As a plugin

Under the façade, createHeartwoodPlugin(source) contributes the identity through contributeRuntimeAwarePrompts as a prepend, so it composes ahead of the core baseline and lands at the stable head. Use it directly when you compose the runtime yourself instead of through createPleachAgent.

import { createHeartwoodPlugin } from "@pleach/core/quickstart";

const plugin = createHeartwoodPlugin(() => "You are Atlas.");

Where to go next

On this page