Theo Agents

Theo Agentsare the user-visible brand for OpenCharts' persistent agent platform. Each Theo Agent owns a name, an icon, a trigger, a step graph, a sharing visibility, and an optional verification badge — and runs on the same EVI runtime that powered the legacy Automation Designer. Build one in the Designer, run it manually or on a schedule, share it with your org, and the Library lets your teammates install a verified copy with one click.

Where Theo Agents liveThe dashboard is at /theo-agents. The Designer is at /theo-agents/[id] and opens as a dedicated full-page workspace — the regular sidebar + dashboard header step out of the way so you can focus on the agent. The Library is at /theo-agents/library. Run history at /theo-agents/[id]/runs. Insights at /theo-agents/[id]/insights. Legacy /automations and /steelagents URLs 308 to the matching Theo Agents route so existing bookmarks keep working.

Two kinds of agents

Workflow agents

Deterministic step-by-step pipelines built in the visual Designer. Best when the shape of the work is known in advance — fetch, transform, post, notify.

Autonomous agents (coming soon)

Goal-driven agents that compose their own steps at runtime are on the roadmap. The Workflow ↔ Autonomous toggle is hidden in the Designer until that runtime ships — today every agent runs as a deterministic Workflow, so build your agents as explicit step graphs.

In the Designer

  • Dedicated full-page canvas — the Designer drops the global sidebar + header so the agent is the only thing on screen. The header centers a frosted Designer / Runs / Results / Insights switcher whose highlight glides with you between views (icon-only on phones, so every view is reachable), and one click on the emblem takes you back to Mission Control.
  • Frosted-glass canvas (2026-07) — trigger and step cards sit on strong, milky frosted panes with color-coded step identities: every step kind carries its own filled icon tile and accent (orange app actions, violet Theo actions, blue branches, cyan approvals, amber delays…), app steps show their real brand logo, and the trigger tile is the lane's one neon moment. While a run or test is live the connectors visibly conduct energy and the executing step wears a traveling light beam; going live, finishing a run, passing a test, and Theo building your first flow each fire their own celebration. All motion respects your reduced-motion preference.
  • Run vs Test — the Test button does a free dry-run with live per-step status, while the Run button fires a real, credit-consuming run that lands in Run history. For a manual-trigger agent the Run button is the primary action (nothing fires it automatically); you can also run any agent on demand from its card’s actions menu or the Run history page.
  • Step panel (2026-07) — one right-side panel for adding and setting up steps. Add mode is a searchable catalog organized into color-coded groups — Research & Decide, Create Content, Create Media, Send & Notify, Boards, Logic, Data, and your connected apps by category — so each family of steps is easy to tell apart at a glance, and every step lands exactly where you armed it — the end of the chain, between two steps, or inside a specific branch — and the panel stays open so you can add several steps in a row. Set up mode is the inspector for whichever card you click: rename the step, fill in its configuration, tune per-step AI settings, jump to its results, or delete it. Cards never expand in place anymore — click a card and the panel does the rest. The live step kinds: action (connector), theo_action (AI action), path (Branch — If / Else), approval (human gate), delay, and call_agent (sub-agent dispatch). The agent’s purpose note now lives in the header’s ⋯ menu (“Purpose…”) instead of a fixed sticky note. The bottom bar’s “Add step” pill appears only while the panel is closed — open the panel and the pill tucks away.
  • TheoBuilderRail — natural-language co-builder. Tell Theo "add a step that summarises last week's Stripe payments and posts to Slack" and watch the canvas update. The builder can author full Branch (If / Else) steps — conditions, nested steps, and the Else lane — from one sentence; if a turn times out it keeps whatever it already applied, tells you honestly, and offers a one-tap Try again. One clear close button on the rail, an ‘Ask’ pill in the bottom bar that brings it back — shown only while the rail is closed (with both the builder and Step panels open, the bottom bar disappears entirely) — and your collapse choice is remembered across reloads.
  • Builder-first empty state — a brand-new agent opens with a centered “What should this agent do?” composer (plus template chips) that sends your description through the builder, so agents start from a working flow instead of an empty lane. “Start from scratch instead” keeps manual assembly one click away.
  • Readable step cards — every card shows its position number, a human one-liner of what it's configured to do (“To ari@… — ‘Weekly recap’”), and an amber “Needs setup” chip when required fields are still empty (advisory — it never blocks Test or Go Live).
  • Reliable long runs — with background runs enabled, Run dispatches the execution off the request path and returns immediately with a pollable run id; the Designer's run drawer streams live per-step status from the run row, links straight to Run history, and reattaches to an in-flight run if you reload mid-run. Long agents can no longer die with a browser timeout.
  • Delay step — pause the flow for a short, fixed wait (up to 2 minutes) before the next step runs. Longer or recurring waits belong on a scheduled trigger instead.
  • Wait for Approval (human gate) — the agent pauses and pings you in Telegram (and the in-app bell) with Accept / Reject buttons. Approving resumes the run right where it left off; a paused agent waiting on you is never counted as a failure.
  • Per-step model + temperature override for theo_action steps so you can pin a specific engine on a step without changing the rest of the agent.
  • Scheduled agents get the same time budget as a manual run, so a multi-step agent on a schedule finishes instead of being cut off early.
  • Board event triggers — with Theo Boards enabled, the Event trigger swaps the raw event-name field for structured controls: pick a board.card.* event, scope it to one board or all of them, add list / status / card-type conditions, and read the live plain-English preview (the same sentence the board's Automations panel shows). Agents Theo designs from a board's Ask Theo rail land here too — rail-built ones stay editable from the board, and anything you hand-tune lives on in the Designer.

Steps are still authored as the same AutomationStep[] JSON the legacy Automations Designer used — Theo Agents is a brand-level rename plus the new agent-aware features (per-step model controls, verification, library, feedback). The underlying /api/automations/* + /api/steelagents/* endpoints and the automations collection are unchanged.

Branching (If / Else)

A Branch (If / Else) step routes the run down exactly one lane: Theo runs the FIRST branch whose condition matches; if none match, the Else branch runs. That first-match contract is printed right on the surface — the header reads “Theo runs the FIRST branch whose condition matches. If none match, Else runs.” and the join bar reads “The matching branch continues here”. (The legacy “parallel branches” framing is gone — it described the opposite of what the runtime does.)

The Designer renders the branches as side-by-side standalone columns that fan out below the Branch card on the open canvas — wider than the main lane, connected by real fork + join rails, with nothing squeezed into an enclosing box. Each column starts with a small condition card carrying a numbered evaluation-order badge (the Else column shows a catch-all glyph instead), an IF / ELSE IF / ELSE chip, an optional display name, and a one-row condition written as filter pills (field · operator · value — it reads like a sentence). The Else column is amber and always pinned last — matching the runtime fall-through order — and once you have more columns than fit, the row scroll-snaps horizontally like a board.

Because the first match wins, lane order is real routing control: hover a condition lane and use the ◀ ▶ arrows to check it earlier or later — the order badges update to show the new evaluation sequence. The lane menu also offers Duplicate branch, which copies the condition and every nested step (with fresh internal ids) right after the original — handy for building tiered rules like score > 90 / score > 70 / everything else.

Names never change routing. The name input on each lane is a display label only; whether a branch runs is decided exclusively by the condition below it. (Legacy agents that used the freeform matcher string keep working — that mode lives under the lane kebab as Custom text matcher (advanced), matched verbatim against the previous step's decision / result / value.)

Every condition lane's pill sentence has three controls:

  • Field — the upstream output key to test, picked from a context-aware dropdown that groups From trigger first, then one section per upstream step (e.g. From Step 1 — Theo Decision). Each entry shows a one-line description from the skill’s output schema so you know why decision, summary, or sent appears. Choose Custom path… at the bottom of the list to type a freeform dot-path like triggerData.customer.email for connector-specific or webhook-payload keys the registry doesn’t model.
  • Operator — one of equals, does not equal,>, >=,<, <=,contains, starts with, plus the unary checks exists, is truthy, and is falsy / empty. Numeric operators coerce both sides; contains and starts with are case-insensitive.
  • Value — what the field is compared to. Hidden automatically when the operator is unary (exists / truthy / falsy).

Below each condition card hangs the branch's own step chain — the same full-size step cards the main lane uses, joined by connectors with insert points, within-branch drag reorder, and an + Add step button. A branch with no steps is a legal pass-through: the column says “Empty branch — the run passes straight through” and the run continues at the join. The condition card's kebab (⋯) hosts Make this the Else branch, the Custom text matcher escape hatch, Duplicate branch, and Delete branch.

Runtime contract. At save time the EVI adapter rewrites each branch onto an edge labelled branch_0, branch_1, … (or default for the Else branch). The switch executor walks the branches in order, evaluates each expression against the upstream input, and emits { decision: "branch_<i>" }for the first match — run summaries use the branch's display name, but routing always uses those synthetic labels. If nothing matches and there is no Else, the step fails with an actionable error rather than dead-ending silently.

Every new Branch step starts seeded with one condition lane (field intentionally blank — pick one) plus an explicit Else. A Branch step without an Else is a blocking validation issue: the banner explains it and offers a one-click Add the missing Else branch fix.

Heads-up validation. When a branch references a field key no upstream trigger or step is known to emit — or a step still has required config fields empty (the amber Needs setup chip) — the Designer surfaces a neutral Heads up note above the canvas. These are advisory only — they never block Test or Go Live because custom connectors and webhook payloads can legitimately emit keys the registry doesn’t model, and the runtime deliberately feeds upstream output into empty content fields.

Research a list (web enrichment)

Theo Agents can take a list — a CSV/Excel you upload or names & companies you paste — crawl the public web for each row, and hand back an enriched file. A classic example: upload a list of people and have Theo find each one's public social / LinkedIn profile, then download the list with a new linkedin_url column.

The easiest way — drop a CSV in the Theo Builder. Open the Theo Builder rail on the right of the Designer, click the paperclip to attach your CSV, and type what you want in plain language — “add each person's LinkedIn and current company”. Theo reads your file's column headers and builds the whole Import → Find / Enrich → Export flow for you, pre-filled with your real columns — no step assembly, no comma-typing. Or, on a brand-new agent, click Or enrich a list from a CSV under Add your first step (and the Enrich a List starter in the template gallery) to drop the same three-step recipe in one click.

Two ways to run it:

  • Open-ended (chat with a worker). Attach a file to a worker and just say what you want — “find each person's LinkedIn and email these results to me”. The worker reads the rows, searches the web for each, and can export a CSV (and even attach it to an email). Best when the task is fuzzy or one-off.
  • Repeatable (Designer pipeline). Build it once in the Designer: Import CSV / JSON (or Read Google Sheets / Airtable) → Find / Enrich Export to Spreadsheet. Run it on demand or on a schedule against a fresh list each time. The Import / Read steps live under the new Data tab in the step picker; Find / Enrich is in AI Actions.

Giving the Import step your list. Open the Import CSV / JSON step and pick one of three sources: Upload file(browse for a CSV, Excel (.xlsx), TSV, or JSON file — it's re-read fresh on every run; Excel workbooks are parsed straight into rows, no need to export to CSV first), Paste data (drop rows straight into a box; Theo shows a live count of the rows and columns it detected), or From URL (a public link to a CSV/TSV/JSON file Theo fetches each run). Read Google Sheets and Read Airtable instead connect your account and ask for the spreadsheet ID + range, or the base ID + table name.

Personalize from your list. Any creation or messaging step downstream of an Import / Read step can weave the data into its fields with {{column}} placeholders — e.g. a document titled Welcome packet for {{company}} or an email body Hi {{first_name}}. The step config shows the columns you can use; at run time each {{column}}is filled from the incoming row. Placeholders that don't match a column are left exactly as typed, so brace syntax in code or JSON steps is never clobbered.

Find / Enrichtakes a plain-English goal (“find each person's current company”) plus the columns to add. You don't type comma-separated text — click to add each column as a chip, and Theo seeds suggestions straight from your uploaded list's real headers, so the identifying columns are one tap away. For every row it runs a focused web search built from the row's identifying columns, reads the most promising public results, and fills in the requested fields — attaching a _source link and a _confidencelevel. When it can't find a confident match it leaves the field blank rather than guessing, so you can trust what comes back.

Already have a website or profile URL? If your rows carry a website / domain / link column, name it in the optional Website / URL column picker (Theo also auto-detects obvious ones like website or url). Theo then scrapes that page directly instead of running a blind web search — faster and far more reliable when you already know the source. If the page can't be reached it quietly falls back to a web search for that row.

The export is your spreadsheet — plus the new columns. Export to Spreadsheet automatically detects the spreadsheet the agent started from. Leave its config blank and the downloaded file keeps the imported file's name (e.g. insurance-agents.csv) and every original column in its original order, with the new columns (like linkedin_url, plus _source / _confidence) appended at the end — so the result reads as the same spreadsheet with new columns, every run. This works even when an Approval or AI step sits between Find / Enrich and the export. To override, open the Export step's File name field — it shows a live .csv preview of exactly what downloads — or use its optional column picker.

What to expect. Enrichment uses public search results only — it surfaces public profile links and never logs in or pulls anything behind a login or paywall. There's no row limit to set — Theo enriches your whole list. A real run hands any list bigger than a small sample to a background worker, so the run returns right away and the Designer plus your Run history show it as Running in background(not Completed) while Theo works through every row (lists of thousands of rows are fine — they just take longer). When it finishes, the run flips to Completed and you get an email and an in-app notification with the finished spreadsheet's download link (the export step's same-spreadsheet naming and column rules still apply). If your AI credits run out mid-list, you still get the file — enriched up to where the credits stopped, with the rest included unenriched. Rows that error out are kept (flagged, never dropped) so one bad lookup never sinks the whole batch. Note: the Designer's Test button only enriches the first few rows as a quick sample — use a real run for the full list.

Library, install, and fork

The Library shows every verifiedTheo Agent in the user's org. Clicking Install mints a personal fork of the source agent (verified flag cleared, visibility set to private). Re-installing the same source is idempotent — you always get back the existing fork instead of a duplicate.

The fork carries a forkOfId pointer so future version-pinning work can resolve the right published version. Today the runtime dispatches the live target document; the executor records target.publishedVersion on everycall_agent step output so the insights aggregator can attribute future runs.

How verification works

Verification is what unlocks two things at once: org-wide Library install and cross-user chat dispatch. Until an admin flips the badge, a Theo Agent can only be dispatched by its owner.

  1. Share with the org. In the Designer header, open the Sharing control (the Private / Shared pill next to the status badges) and pick your organization. Personal-private agents cannot be verified — they’re scoped to the owner and never appear in the Library or the team chat.
  2. Ask an admin to verify. Owners and admins of the agent’s org see a Verify button next to the badge cluster in the Designer. Editors and viewers don’t see the button at all — the verify-context route hides it for them.
  3. Click Verify. The Designer POSTs to /api/steelagents/[id]/verify, which runs the canonical gate end-to-end. On success the badge shows Verified and the agent is immediately routable from Theo Chat and installable from the Library. Click again to unverify if you need to pull it back.

All three steps are gated on the verify_steelagents org permission, which defaults to owner + admin.

Dispatch another agent from chat

Theo can route any chat request to one of your routable Theo Agents instead of handling it directly. The agents your org has shared (plus your own private agents) appear in Theo’s Routable Theo Agents system-prompt block; Theo picks the best match and calls route_to_agent to dispatch.

You can also nudge Theo by mentioning an agent explicitly:

  • Use the agent’s slug with an @ prefix — e.g. “kick off the @daily-standup-brief and send it to me”. The chat tool matches case-insensitively and accepts the slug with or without the leading @.
  • Paste the agent’s $id directly when you already have it. Theo Chat looks up the registry first, then falls back to a direct service lookup so the dispatch works even when the agent isn’t in the in-prompt block.

Once Theo dispatches, the chat shows a confirmation card with the agent name, the run link (/theo-agents/[id]/runs), and the thumbs-up / thumbs-down feedback widget. Your rating feeds the agent’s Insights page — every run is attributable to the user who triggered it, so an admin can see real quality signal from chat dispatches.

Three guardrails mirror the runtime call_agent rules above:

  • You can always dispatch agents you own from chat, even when they’re private.
  • Agents owned by another user must be verified and in an org you belong to. Cross-org dispatch is refused.
  • Soft-deleted agents are refused, and Theo will tell you when a route fails so you can correct the reference inline.

Chat-side dispatch is currently gated by the STEELAGENTS_ROUTER server flag. While the flag is off the registry block is empty and Theo Chat treats the tool as disabled.

Run agents from team chat

Theo Agents can also be dispatched straight from your team's chat (/teams/[org]/chat). Type /agent in the composer to see every routable agent in the org, pick one, optionally add an input prompt, and hit Enter — e.g. “/agent daily-standup-brief summarize yesterday”.

  • A live run card appears in the channel for everyone: status, the final output summary, links to anything the agent produced, and a View run link to the runs page.
  • The same guardrails as Theo Chat apply — you can run agents you own plus verified agents shared in that org, and the dispatcher's plan + AI credits are used.
  • Runs that pause at an Approval step show an Awaiting approval card — resolve the gate from the runs page (or Telegram) and the run continues.

Team-chat dispatch additionally requires the NEXT_PUBLIC_TEAMS_CHAT_AGENTS client flag; the /agent commands stay hidden until both flags are on.

Sub-agent dispatch (call_agent)

A Theo Agent can call another Theo Agent as a step. Three hard rules govern who can invoke whom:

Same-org-only sub-agent dispatch

A Theo Agent's `call_agent` step can only invoke another Theo Agent in the same org. Cross-org calls are rejected by the executor with a typed permission error — even when the target carries the verified badge.

Cycle detection

The runtime tracks every ancestor automation id in the trigger payload's call stack. Re-entering an agent that's already on the stack (A → B → A) fails fast before any work runs.

Verified-or-owned

A Theo Agent can only call agents it owns or agents that carry the platform `verified` badge. Org admins flip the badge via the `verify_steelagents` permission (env-var + RBAC action names retained for backwards compatibility).

The runtime stamps the caller's org id and a call stack onto the EVI trigger payload before each run; the evi/call-agent executor reads those annotations and short-circuits cross-org / cyclic dispatch before invoking the target.

Feedback & insights

1

Every run gets attribution

Each Theo Agent execution writes an `execution_runs` row keyed by the agent id. The runner is also stamped — only the user who actually executed a run can rate it.

2

Thumbs-up / thumbs-down + comment

The RunFeedbackWidget posts to `POST /api/steelagents/feedback` (API namespace retained for backwards compatibility). Resubmitting upserts the existing row so users can flip their rating without leaving duplicates behind.

3

Insights aggregator

The Insights tab at `/theo-agents/[id]/insights` reads the last 100 runs + every feedback row and renders success rate, average duration, credits per run, feedback score, and the top failure clusters.

Marketing steps (branded campaigns)

The Designer's Marketing section (collapsed by default in the step panel) is a set of first-party actions for building branded ad campaigns. Every step reads from and writes back into ONE shared campaign, so each later step builds on the earlier ones:

  • Build Campaign Brief — turns a product, goal, and audience into a structured brief (objective, message pillars, channels, brand voice).
  • Analyze Reference Ads — you upload ads you admire and Theo extracts reusable insight (layout, hook, tone, format) without copying protected creative.
  • Generate Ad Copy Variants — A/B-ready copy variants (headline, body, CTA, angle) grounded in the brief and reference insights.
  • Create Ad Creative Specs — per-variant dimensions, visual direction, required copy, and brand assets an image or design step can execute directly.

Upload your logoson the brief (or reference ads on the analyze step) right in the step's setup panel. Only a secure file reference is stored — never the raw image — and Theo re-checks that you own each file before reading it. Chain these steps into Create Image, Create Design, Create Document, or a spreadsheet step to produce and package the finished campaign.

You choose where the campaign runs with a visual picker instead of typing channel names: tap the platform logos you're building for (Instagram, Facebook, TikTok, LinkedIn, X, YouTube, Pinterest, Threads, Email, Web), then pick each placement beneath it (Feed, Story / Reel, Portrait, Landscape…). Theo writes one copy variant and one creative spec per placement you select — each with the correct dimensions for that placement. Pick your placements once on the brief and the later steps inherit them, so you never have to guess sizes or repeat yourself.

Org permissions

Two new actions live on the org permission matrix:

  • manage_steelagents_library — gates the org admin UI for curating categories and approving agents submitted by org members.
  • verify_steelagents — flips the platform-levelautomations.verified badge. Verified agents become eligible for cross-user call_agent dispatch and library install.

Both default to owner and admin.

How Skills relate to Theo Agents

Skills (from theSkill Store) and Theo Agentsare two distinct surfaces that share Theo's underlying capabilities but live in their own runtimes:

  • Skillsextend Theo's chat tool surface. You install one, then mention it in chat and Theo picks the right tools automatically. Triggered by manual prompts, keywords, scheduled heartbeats, or platform events.
  • Theo Agents run as deterministic step-by-step automations — manual, scheduled, webhook, event, or Telegram-triggered. Each step calls a specific canvas skill (evi/send-email, evi/ai-summarize, etc.).

The two systems intersect through a single canvas step: Run Theo Skill (evi/run-autonomy-skill). Drop it into an agent step graph, choose a Skill Store skill by slug, and the runtime dispatches the skill mid-run with a credit-capped budget (Haiku + 3 sub-steps). Use this when you want a workflow to hand off a self-contained creative task (e.g. generate a business plan) without rebuilding the skill's prompt graph inline.

Two namespace gotchas worth knowing:

  • Different slug namespaces. Each Theo Agent step has a canvasSkillSlug like evi/research-deep — that names a canvas building block, NOT a Skill Store skill. Skill Store slugs (e.g. biz-plan-generator) only appear inside the config of a Run Theo Skill step. (The on-disk JSON wire key on `automations.steps` is still skillSlug for back-compat; the rename to canvasSkillSlug happens in-memory via the `parseSteps` / `serializeSteps` adapter.)
  • Different categories. Skills use { productivity, domain, integration, automation, creative }; Theo Agents use department categories ({ sales, support, hr, engineering, marketing, it, opencharts, starters }). The Library's category chip row is the canonical place to filter agents; the Skill Store has its own filter.

Feature manifest

The canonical surface inventory lives at .manifests/theo-agents.json. It enumerates every type, route, collection, service, test, and hard rule that belongs to Theo Agents. Update it whenever you add or remove a Theo Agents surface so the contract test in __tests__/contracts/registryVerification.test.tskeeps catching drift.

Run history

Every execution writes a row to execution_runs with the per-node status, total duration, credits used, and any error message. The run history page at /theo-agents/[id]/runs renders these for owners, and the Insights aggregator scans them when building its snapshot.

With background runs enabled, pressing Run creates the run row up front and dispatches the execution off the request — the Designer, Mission Control, and the Runs page all poll that same row for live per-step progress, so a long agent keeps running even if you close the tab, and the row never strands at “running” if something fails to start.

Was this article helpful?

Related Articles