Agents and chat
Every capability in AdStack is an agent reachable from chat. This is the catalogue and the rules for adding to it.
The product has one interaction model: the user types, a host LLM decides what to do, and agents produce output. There are no dedicated feature pages. Adding a capability means adding an agent, not adding a screen.
Two registries
They serve different jobs and both must stay in sync with the database.
| Registry | Answers |
|---|---|
lib/registry.ts |
What modules exist, their stable ids, names, slugs, prompt folders, plus worker, queue, domain, and bucket names |
lib/agents/registry.ts |
Which modules the chat host can call as tools, what each requires, and how its output renders |
lib/registry.ts currently lists 33 modules. lib/agents/registry.ts lists 23 agent definitions, because some modules are internal helpers rather than user-facing agents (the attachment summarizer, the character map prompt engine, the cast list extractor, the image prompt engine, the storyboard frames agent, the shared guidelines module).
A module being in the registry does not mean a user can reach it. See what is actually wired up for the audit of live entry points and dead ones.
The agent catalogue
| Slug | Requires | Notes |
|---|---|---|
offer-intelligence |
a URL | Scrapes and analyzes an offer |
market-research |
offer | Real customer language from review sources |
buyer-persona |
offer | Belief-shift-framework persona |
viral-angle-miner |
offer | Angles mined from viral content |
| Slug | Requires |
|---|---|
ad-hooks |
offer |
headlines |
offer |
facebook-ads |
offer |
advertorial |
offer |
advertorial-longform |
offer |
in-feed-vsl |
offer |
vsl |
offer |
tsl |
offer |
bullets-fascinations |
offer |
copy-chief |
- |
| Slug | Requires |
|---|---|
landing-page |
offer |
quiz |
offer |
listicle |
offer |
vsl-longform-page |
offer |
upsell |
offer |
| Slug | Notes |
|---|---|
ugc-script-writer |
Paid video ads. 16 format archetypes, format_slug required - the user picks the format, never the host |
viral-shorts |
Organic short-form content, 15-60s for TikTok, Reels and Shorts. No offer, no ad format, entertainment first |
storyboard-director |
Turns a script into scenes with element and motion notes |
cast-list |
Extracts locations, props, and products from a script |
The two script agents are easy to confuse and the host is told the difference explicitly: “content”, “organic”, “post”, “for my TikTok” go to viral-shorts with no format cards at all; “ad”, “creative”, “campaign” go to ugc-script-writer through the format flow.
| Slug | Notes |
|---|---|
campaign-builder |
Multi-stage pipeline. Excluded from the host’s tool list; it is started explicitly with the start_campaign tool |
What an agent definition carries
{
slug: "ad-hooks",
displayName: "Ad Hooks",
actionLabel: "Writing ad hooks", // shown in the run timeline
aliases: ["generate_hooks"], // tolerated older tool names
requires: { offer: true }, // gates the tool call
outputRendererKey: "ad-hooks", // picks the output card component
supportsRevision: true, // can be asked to revise its last output
thinking: true, // extended thinking on
}
requires is enforced before the agent runs. If an offer or persona is missing, the host gets a validation error as a tool result, enriched with context: if a persona is currently being generated by a background job, the host is told that instead of being handed an empty picker.
Adding an agent
Registry first
Add the MODULES entry in lib/registry.ts: slug, name, type, promptDir. Nothing else happens until this exists.
Prompt files
Create prompts/<dir>/system.md and user.md. These are the source of truth for prompt content.
Migration
Add a migration in db/migrations/ inserting the module row and a default prompts row. Set is_active false for internal-only modules; getPromptSettings matches on name regardless of active state. Run bun run db:migrate.
Runtime loader
Mirror lib/talking-head-engine.ts or lib/attachment-summarizer.ts: call getPromptSettings(NAME) with an inline fallback so a missing database row never hard-fails.
Agent definition
If the host should be able to call it, add it to AGENT_DEFINITIONS in lib/agents/registry.ts and create an output renderer in components/shared/module-outputs/.
Verify
bun run check:truth must pass, then bun run smoke to confirm the prompt resolves at runtime. Prompt edits after this point are runtime-dead until bun run sync:prompts --apply <slug>.