GenerateSaaS

AI prompts

Every prompt layer the product injects, which lane receives which, and where to edit each one.

Every system prompt is a stack of layers, each gated on a condition. The injected strings live in packages/config/src/ai/prompts.ts (@repo/config/ai/prompts); the persona lives with the agent; the desktop persona is one config value.

The layers

LayerSaysEdit at
PersonaThe assistant's characterpackages/api/src/mastra/agents/assistant/config.ts
Identity lineWhich product and that the session runs in its desktop appAutomatic (from your product name) - not a string to edit
Desktop instructionsYour desktop personaconfig.desktop.agents.instructions in desktop.mjs
terminalPreambleOne sentence: "you are in a terminal session here"aiPrompts
capability"Act through your tools; discover with list_* first"aiPrompts
webResearch"Search rather than guess; cite real URLs"aiPrompts
Active projectThe project the chat works in, by name and idAutomatic (read server-side under the session's scope)

Who gets what

✓ always · ◐ on its condition · - never

LayerChat (web + desktop cloud)Cloud automationDesktop CLI chatDesktop Terminal tab
Persona✓✓--
Identity line--✓✓
Desktop instructions--◐◐
terminalPreamble---◐
capability◐◐◐◐
webResearch◐◐--
Active project◐---
ConditionGates
The run's toolset is non-emptycapability on every lane
The turn registered the app's own web_searchwebResearch (never on provider-native search or extract-only deployments)
The app's tools are mounted (user signed in)capability + terminalPreamble on the desktop local lanes
You set config.desktop.agents.instructionsDesktop instructions
Projects are on and the chat's project is one the session can seeActive project

Layers always compose in the table's top-to-bottom order. An automation's own prompt is the user message, never a system layer - see Built-in automations.

Prompt caching

The static layers are sent first and carry a cache breakpoint; per-turn context (the active project) goes after it, so tools and static layers are re-read from the provider's cache on every step. Anthropic models, direct or through OpenRouter, honour the breakpoint; OpenAI and most others cache the stable prefix automatically.

Keep per-turn text out of the static layers. A timestamp or id in aiPrompts or the persona changes the prefix every turn and turns every request into a cache miss. Add it through agentSystemMessages in packages/api/src/ai/system-prompt.ts instead.

One voice everywhere

capability is the same string on every lane - the desktop stages it into the on-device runtime config - so one edit re-voices web, cloud, and desktop together. terminalPreamble is shared by both terminal kinds the same way.

Utility prompts

Three keys in the same file never enter a conversation:

KeyUsed by
chatTitleThe run that names a conversation from its first exchange, on every naming lane - built-in, BYOK, and desktop CLI
compactionSummaryConversation compaction runs
mcpInstructionsWhat the outward MCP server advertises to a connecting external agent

Gate a new layer on its capability. One that ships on every request tells a model to do things this deployment may have no tools for, and bills those tokens every turn. Cut before you add: a line a current model already follows naturally ("answer clearly", "never invent facts") buys nothing and costs context on every run.

On this page