Prompts
Prompt files are the source, the database is the runtime, and editing one without syncing the other changes nothing.
Every module has two prompts, a system prompt and a user prompt. They exist in two places, and the relationship between them is the single most common source of “I changed it and nothing happened”.
| Location | Role |
|---|---|
prompts/<dir>/system.md, user.md |
The source of truth. Reviewable, diffable, in git. |
prompts table |
The runtime. This is what actually gets sent to the model. |
/admin/prompts/<moduleId> |
The admin UI, which edits the database directly. |
Syncing
bun run sync:prompts --diff <slug> # inspect the difference
bun run sync:prompts --apply <slug> # file wins, push to the database
bun run sync:prompts --pull <slug> # database wins, pull the admin edit back into the file
Use --pull when someone edited a prompt in the admin UI and that edit is the newer truth. Use --apply when the repo is ahead. Deciding which direction is correct is a judgement call; the tool will not guess for you.
The truth check
bun run check:truth
It asserts three things agree: lib/registry.ts, the module and prompts rows in the database, and the files under prompts/. It fails loudly on drift, which is the whole point. The failure class it was built to prevent is a feature being silently dead for weeks because a slug was renamed in one place.
It also asserts the AI Gateway BYOK alias matches across the registry, the agents worker config, and the worker’s fallback constant.
Run it after touching modules, prompts, or migrations. bun run check and the wrap-up routine both include it.
Prompt folders
Every module with a promptDir has a folder under prompts/. Three are worth knowing about specifically:
prompts/host/is the chat host’s own system prompt. It contains the sections that teach the host how to behave:<image_generation>,<video_generation>,<character_creation>,<element_creation>,<cross_offer_reference>. Changing host behaviour usually means editing this file and syncing it.prompts/image-prompt-engine/is the enhancer every image passes through - chat, Creative Studio and character portraits alike. It owns style profiles, shot-type detection, camera mechanics and the realism guards.prompts/advertorial-longform/brief/sits one level deeper, because that module’s prompt is a brief-generation step.
Prompt caching
The agents Worker applies cache_control: ephemeral to the system message and to the last tool definition. System prompts are long and stable, so this is where the savings are.
Models
Models are not hardcoded. They live in the ai_models table with a type of llm, image, or video, and a JSONB config. Admin Settings has one sub-tab per type with a starred default, backed by the default_model, default_image_model, and default_video_model keys in app_settings.
For video models the config carries everything the UI needs to render itself: apiModelPath, aspectRatios, modes, durationOptions, features (sound, start frame, end frame, reference images, talking head), paramMapping, pricing, and defaults. The prompt box derives its controls from the selected model, so adding a model is a database row plus a migration for its config, not a frontend change.
Pricing comes in two shapes: pricing.perSecond (rate times duration) and pricing.creditTiers (a fixed lookup by mode, duration, and variant).