System reference · high-level architecture
One recorded sales call goes in. A working, evidence-checked product demo comes out — its own database, its own dashboard, its own AI assistant that answers questions about that client’s business.
DEMO-BUILDER/ · platform/
Two agent systems, easy to confuse. Keep them apart.
The builder is the agent that manufactures a demo. The supervisor is the agent that ships inside it. The builder runs for twenty minutes on a laptop and is never seen by the client; the supervisor runs in front of the client for months.
Everything below follows that split. The first half is the factory floor: how the builder plans, acts, checks its own work, and refuses to hand over a demo it could not verify. The second half is the product: the agent runtime the demo actually is, and the memory and connectors that keep it useful past the first question.
The two meet at exactly one place — integrate.py, which
merges what the builder generated into a copy of the base app.
integrate.py reads the
bundle’s QA verdict first and refuses anything marked NOT READY.agent.py → make_agent()
deepagents · LangGraph
SqliteSaver checkpoints
reasoning effort: medium
The builder is a single agent in a loop. It is given a system prompt that
names seven phases, a folder of skills, nine tools, two subagents it can
delegate to, and a sandboxed filesystem rooted at DEMO-BUILDER/.
Nothing about the pipeline is hard-coded as a state machine — the phases
live in the prompt and in the skills, and the model walks them.
What makes that trustworthy rather than hopeful is the rule the prompt opens with: never claim something works without having run the corresponding tool. Every phase ends in an artifact on disk and a tool call that proves the artifact is good.
Running with --approve-loads registers an interrupt on the one
tool that writes to the database. The graph pauses there and waits for your
approval before any data lands in Postgres. Everything else runs unattended.
DEMO-BUILDER/skills/
Seven folders, one SKILL.md each. Loaded on entry to a phase, not held in the prompt.
A skill is a markdown file with a name, a one-line description, and a procedure. The builder’s system prompt names the seven phases and says read the skill before starting the phase — so the detailed instructions stay on disk until the moment they apply, instead of crowding the context from the first token. Editing a skill changes how every future demo is built; no code changes.
| # | Skill | Produces | Run by |
|---|---|---|---|
| 1 | transcript-extraction | Client profile and executive summary. Searches the web only to fill gaps the transcript left. | orchestrator |
| 2 | data-modeling | The star schema for this client — data_model.json, schema.sql. | orchestrator |
| 3 | demo-narrative | The story bible: scale anchors taken from what the client said, client-aligned names, planted insights and their target magnitudes. | orchestrator |
| 4 | mock-data-generation | A deterministic generator that renders the story bible into 18–24 months of CSV, loaded into Postgres. | data-engineer |
| 5 | dashboard-generation | KPI cards, charts and detail tables — every query executed before it ships. | orchestrator |
| 6 | prism-integration | The platform artifacts: supervisor prompt context, data-subagent blocks, front-end constants, demo questions. | orchestrator |
| 7 | testing-qa | qa/qa_report.md — the verdict the whole build is judged on. | qa-verifier |
Phase 3 is deliberately kept by the orchestrator rather than delegated: the story bible needs the full transcript in context, and it is where the demo’s credibility is won or lost. Every number the client stated must be reproducible from the generated data; every complaint they voiced must have a signal someone can find.
DEMO-BUILDER/tools.py
Nine tools. Five of them exist only to disprove the model’s own claims.
Tools split cleanly into two groups, and the second group is the larger investment. Anything the builder asserts about the demo has a tool that can contradict it.
run_python_script — executes the data generator, iterating until its reconciliation table prints clean.load_csvs_to_postgres — loads a run into its own <slug>_demo schema.install_dashboard_html — places the 20 KB dashboard template, which the model is forbidden to hand-write because long writes truncate.internet_search — a few targeted queries when the transcript names a tool or company it cannot place. Never overrides a stated fact.read_platform_file — reads the base app, so generated artifacts match what they will be pasted into.run_query — SQL against the loaded schema. This is the source of truth: if a planted insight is not visible in query results, the generator gets fixed, not the claim.test_dashboard_config — runs every chart’s query and validates the shape it returns.check_dashboard_server — confirms the preview actually serves the built dashboard.validate_demo_guide — confirms the generated JavaScript constants parse and export what the app imports.The filesystem the builder writes through is wrapped so that a malformed path comes back as a failed tool call the model can read and retry, rather than an exception that unwinds the whole run. A twenty-minute build used to be lost to one stray dot in a filename. Genuine path traversal is still rejected outright, one layer earlier.
agent.py → subagents[]
data-engineer — write access
qa-verifier — read only
The builder can hand work to two subagents. Each is a fresh agent with its own context, its own system prompt, and a deliberately narrow toolset.
data-engineer owns phase 4. It gets the three tools that write and read data, and its instructions are to iterate until the numbers reconcile, then prove them with SQL and report the evidence as “client said X → data shows Y” pairs.
qa-verifier owns phase 7 and cannot fix anything. It holds four tools, all read-only. It runs the checklist, writes the report, and returns a verdict plus the exact list of failures. Grading and repairing are separated on purpose: an agent that can quietly patch what it is grading will always find its work acceptable.
skills/testing-qa/ · agent.py
Verify → route → repair → verify again. Cap: 3 rounds.
Phase 7 is not a final check, it is a loop the orchestrator owns. The verifier reports; the orchestrator routes each failure to whoever can fix that class of problem; then the verifier runs again. The rule that makes it hold together: after the last repair, one more verification pass always runs. A repair that is never re-verified leaves the report describing the broken state, and the gate downstream reads the report, not the repair.
DEMO-BUILDER/integrate.py
Eight targeted patches, then a ninth pass that verifies them.
The bundle is not the demo. integrate.py copies the base app,
then patches nine named places in it — the backend and frontend
environments, the supervisor’s prompt context, the data subagent’s
schema blocks, the shared data context, the dashboard branding, the demo guide,
and the two dashboard files — and finally re-reads its own work.
It patches by exact name and path, which makes those names a contract rather than a detail. Renaming one of them in the base app without updating the integrator produces a build that looks fine and is missing its client. A dry run exists precisely so that contract can be proven before a change lands.
Two guarantees are worth stating plainly: the base app is copied and never edited, and the integrator reads the QA verdict before it starts. A bundle marked NOT READY is refused.
Everything from here describes the demo itself — the agent runtime the client talks to, and what keeps it coherent past the first question.
backend2/app/main.py
One supervisor, four nested agents, plus plain tools and connectors.
The generated demo is a chat application over a WebSocket. Behind it sits a supervisor agent holding a mixed toolbelt: four of its “tools” are whole agents with their own loops and their own memory, and the rest are plain calls out to systems. The supervisor does not know the difference — that is the point of the pattern — but the interface does, so the user sees “Data subagent working” rather than a raw function name.
session_manager.py
subagent_checkpoint.py
utils/context_middleware.py
Three kinds of memory. They solve different problems and are easy to conflate.
“Agent memory” covers three separate mechanisms here, and it is worth naming them apart.
The builder checkpoints every step to a SQLite file keyed by thread id. Point a new process at the same thread and it picks up the recorded state. Before this was durable, the “resume” hint the builder printed was a lie: the state lived in the process that died with it.
The running demo persists conversations to MongoDB. The supervisor’s thread is the chat session itself; each nested agent gets a derived thread of its own, so the data agent’s SQL history does not leak into the document agent’s context, and neither floods the supervisor’s.
When a thread crosses its trigger, a small model folds the older messages into a summary and the recent ones are kept verbatim. Two details matter. The budgets cover the message history only — system prompts are never touched, which is why the data agent, carrying a large injected schema, has a tighter budget than the supervisor. And the thresholds are set so that every model call stays well inside the range where SQL precision holds up, rather than filling the context window because it is there.
mcp_config_loader.py
routers/auth.py
Connectors are configuration, not code. Identity is Auth0.
Outside systems reach the supervisor through three doors.
Identity is Auth0. A JWT is validated when the socket opens, roles are read from the Management API, and — because those lookups are slow and rate limited — both the management token and the role map are cached and invalidated when a role actually changes. The full set of granted roles is carried into the tool context, not just the first one returned, so an access check honours everything the token granted.
tools_utilities/tool_timeout.py
llm_config.py
Four rails that exist because each one failed at least once.
| Rail | Setting | What it prevents |
|---|---|---|
| Nested-agent deadline | 150 s | One slow subagent freezing a whole turn’s tool batch. |
| Plain-tool deadline | 60 s | A wedged MCP server or blocked auth prompt holding the user on a spinner. |
| LLM stall window | 120 s · 1 retry | A stalled provider connection burning the SDK’s ten-minute default. It is a gap-between-chunks limit, so a long answer that keeps streaming is never cut off. |
| Per-run supervisor | no cache | A shared executor serving one user’s access scope to everyone — the prompt carries caller-specific role framing. |
One honest caveat is worth carrying: synchronous tools run in worker threads that Python cannot cancel. A timed-out sync tool keeps running in the background and its result is discarded — the user is unblocked, the work is not actually stopped.
agent.py · server.py
integrate.py · cleanup.py
Four commands, in order.
| Step | Command | Result |
|---|---|---|
| Build | agent.py --transcript <file> --slug acme | 10–25 min, unattended. Writes the bundle and loads the data. |
| Preview | server.py --run acme | Serves the generated dashboard on its own. |
| Integrate | integrate.py --run acme | A runnable copy of the app. Add --dry-run to prove the patch contract first. |
| Remove | cleanup.py --run acme | Deletes the run folder, the build copy and the schema. It only ever touches *_demo schemas, so it cannot reach real data. |
Every build prints a per-phase timing table and writes it to
build_meta.json — model, reasoning effort, seconds per
phase, tool-call count — and appends the run to a history file that
survives a rebuild, so builds stay comparable across models. Timings are
written even when a build fails.
A demo is finished when three things are true at once: every number the client stated is reproducible from the generated data; every complaint they voiced has a signal someone can actually find, at the magnitude claimed; and the QA report says READY after a verification pass that ran after the last repair. Anything less is a bundle the integrator will refuse.