Build
The AI Assistant
The Strategy Builder turns an idea into a first draft. The AI Assistant is the other half: one conversational surface that answers questions, explains what you're looking at, and changes code you already have. Ask it for a change in plain English and it reads your workspace, proposes an edit, runs the checks, and shows every step as it goes — and, just like the builder, it applies nothing until you accept.
Where it lives
The Assistant is the dock in the right rail, and it is available on every page of the app — there is no separate “agent chat” button anymore; the Assistant is the single AI chat surface. Open it anywhere to ask questions (it can attach context about the page you're on); open it with a workspace and it can also edit your code: add a guard, tighten a filter, refactor a helper, fix something the Problems panel flagged. Where the AI Strategy Builder is a guided four-stage flow for creating a strategy from scratch, the Assistant is free-form and conversational. It is deliberate about intent: questions get answers, and it stages code edits only when you explicitly ask for a change.
The read → propose → check loop
Type what you want — you can also attach files: paste or drag in any file
type. Screenshots and images (PNG, JPEG, WebP, GIF) go to the model as
images; documents are read as text — PDF, Word, Excel, PowerPoint,
OpenDocument, Jupyter notebooks and Parquet, as well as any plain text file
(.py, .csv, .json, .log …).
Use @ File to
attach a file that is already in the workspace. Then press Send. The
Assistant then works in visible turns, and the transcript renders each one as it
streams:
- Reasoning. A short note on what it intends to do next.
- Tool calls. The concrete actions — List files, Read file, Search files, Read attachment, Propose edit, Run checks, Run tests, and the app reads described under It can see everything you can see — each shown with a tick or a cross so you can see exactly what it looked at and what it did.
- A proposed edit, shown as a diff. When the Assistant changes code it stages a draft proposal. You accept or reject it right in the Assistant, and the accept is per scope: a button appears for each kind of change the proposal actually contains — production code, test content, and the test setup that runs around your tests. Each is its own decision. Accepting the production code applies only the production code; everything else stays exactly as it was and stays yours to accept, and the draft keeps its place in the drafts inbox until every scope it contains has been decided. Reading the change happens in one place: Review changes (or any named file in the reply) opens the workspace editor, where that file's diff fills the width, side-by-side or inline. Nothing is applied until you accept.
- Checks. The agent runs the fast verification pass over its own proposal — compile, import policy, and the strategy SDK contract — and reports Checks passed or Checks failed with the specific findings. It can iterate to fix its own findings before handing the change to you. The workspace's write rules (which file types it can hold, how a path must look, how large a reply may be) are stated to the model up front, and a reply that still breaks one is refused whole and handed straight back to the model to fix — nothing from a refused reply is ever stored.
- Your tests, actually run — before you see the proposal. When the Assistant edits code it runs your workspace's own test suite in the sandbox, against the workspace as its change would leave it. If the suite is red it does not hand you the change: it reads the failing assertions, revises, and runs again, up to a small budget of runs per turn. You watch each attempt go by (Running tests · run 2 of 6 · 35s) and you get the verdict in words — Tests passed — 13 tests, none failed. If it cannot reach green it says so plainly and lists which tests still fail, rather than presenting a broken change as finished work. Two outcomes are reported honestly rather than repaired: a workspace with no tests (nothing was executed) and a platform that could not run them. The same honesty covers the cheaper load check: if the platform could not run a check the turn asked for, the reply says so in plain words, names what that check would have proved, and the turn is flagged rather than presented as finished — unless it ended by asking you something or by hitting a step or token limit, in which case it is already waiting on you and says so in the reply.
When it asks you instead of guessing
If your request is ambiguous, or the next move is a decision that is yours to make — scope, spend, a tradeoff — the Assistant ends its turn with a question card rather than picking for you. The card carries the question, two to four one-click answers each with a one-line consequence, and Other…, which opens a box inside the card for an answer none of the options covers. You never have to abandon the question to type somewhere else — though the message box works too, and the card says so. Only the newest question is answerable; earlier ones stay in the transcript as a record of what was asked. If a draft's tests are red when it asks, the question itself says so.
Two things it is not supposed to do with a question, and neither is left purely to the model's good behaviour — the server watches for both. It should not ask you for anything this app already has: your workspace's file list rides every turn, the documentation is its own to read, and your run's log, artifacts and tables are one call away, so it reads them itself instead of asking you to paste them. And it should not ask you the same question twice in one conversation — your earlier answer is already in the thread. When the server catches one, the question is handed back to the model to correct, which is normally the end of it; if it insists, the turn ends with a plain-words reply saying what happened rather than putting the card in front of you.
Being straight about how strong that is: both checks read the wording of the question. The held-content one recognises the usual ways of asking — "paste strategy.py", "what files are in the workspace?", "I need the run log" — and the repeat check matches a question you have already been asked word for word, so a re-worded repeat can still reach you. Neither is a hard wall, and the wall is not what keeps them rare: the assistant already has this material, and its instructions say to read it. These checks are the backstop for when it forgets. If one ever does reach you, the fastest answer is one line telling it to read the file or the log itself.
What it can look at
The Assistant answers from the record rather than from memory, so it can open the things you can open — and nothing else:
- Your workspace code. List the files, read several at once, and search the whole tree for a symbol or a phrase.
- Your runs. List them without you pasting an id, read a run's stored scorecard, read its log tail and recorded error — so “why did my backtest fail?” gets a real answer — see what artifacts it stored, and read a slice of a stored table.
- Your data. The instrument registry and the coverage your account is entitled to, per dataset, including how many days can actually be run.
- The web. It can read a public page you point it at, and it can search the web. Search works out of the box — there is nothing to configure and nothing to pay for; if it ever tells you search is unconfigured, that is a bug. Because the default search reads a public search engine's own page, a busy day can come back as “I could not read the search reply” — that is honest, and it is never reported as “there are no results”; point it at a specific site instead. Anything it reads online is treated as untrusted: it will not take instructions from a web page, and a page that tries is flagged in the transcript.
All of this is scoped to your account — it cannot see another user's runs, files or data. And it still cannot run shell commands, launch runs, buy data, or accept its own proposals.
What it remembers about you
The Assistant can carry a few durable facts between conversations — what you trade, how you like answers, a standing constraint. It never saves one on its own. It asks, you see the exact wording, and nothing is stored until you accept it. You can reword it first.
Everything it holds is listed in one place — Settings → What the Assistant remembers — including what it is still waiting on and what you have declined. You can edit any of it and delete any of it. Declining is remembered too, so the same suggestion is not offered again; deleting that record is what lets the question come back.
Remembered facts are used only in your personal Assistant chats. They are never sent into a workspace conversation, because a workspace conversation is readable by everyone on that project — so a fact about you stays yours.
Long waits are always cancellable. While a reply is streaming, Stop halts it; and when you ask for Fix all with AI — a repair pass over the findings in the Problems panel — a Stop fixing button cancels the in-flight repair and halts the work on the server, so nothing keeps churning (or spending) after you've changed your mind.
How hard it tries is yours to set. When the AI writes code that does not hold up — a syntax error, a contract finding, a test it weakened — it gets a few turns to fix its own work before handing you what it has. More turns means fewer leftover findings but a slower, more expensive build; fewer means faster answers with more left for you. Set your own number under Settings → AI self-repair turns, or leave it blank to follow the platform default. A reply the workspace refuses outright — a file type it cannot hold, a denied import — gets its own small number of retries with the exact refusal fed back (an administrator setting, two by default); if the model still cannot comply, the refusal stands and says how many tries it had. Nothing is ever discarded either way: whatever it last produced is still yours to review, with any remaining findings shown rather than hidden. On a team platform an administrator can set the default for everyone, and can turn off per-member values — if they have, the setting says so and tells you what your builds are using.
Attachments stay with the message. An attached image is clickable — before you send it and afterwards in the sent message — so you can go back through a conversation and see exactly what was attached. A file already in the workspace is attached as a reference the Assistant reads on demand; a file from your computer travels with the message, and a long one is read in full through the Assistant's Read attachment tool.
A document is turned into text on the server before the Assistant sees it, and the
text is marked up so it can tell you WHERE something came from — a page number for a
PDF, a real cell address like Sheet2!B7 for a spreadsheet, a slide
number, a notebook cell number. PDF pages are never turned into pictures: the text is
what carries the meaning, and a page image would cost a great deal to send and tell
the model less. A Parquet file arrives as its full column list plus the first rows,
and the Assistant is told to say so rather than pretend it read the whole file.
Anything with no text in it — video, audio, archives, executables, or a scanned PDF that is really a photograph of a page — is refused when you send, and the refusal names the type you tried and what to attach instead ("this PDF has no text layer — send a screenshot of the part you care about", "attach the one file from inside the .zip"). Your message and your other files stay in the box, and the file it could not read is the one marked. Nothing is ever attached silently: every file you drop ends up either read, seen as an image, or refused in words.
Image attachments have no fixed limits by default; the operator of your instance can set count and size limits, and an over-limit turn is refused with a plain explanation. Models without vision support say so honestly instead of ignoring the image, and every reply shows which provider · model actually served it. On the AI dock, the AI Coach and the one-shot summary action, a reply served by a lower-ranked key of yours also says so beside that label (“— your preferred key (anthropic) was rate-limited”). In this Assistant chat the label names the model that served and carries no such note yet, so read it as which model answered rather than as proof that your first choice was the one that did.
Read and answer sizes are unlimited by default. What the Assistant reads from a page, a file, an upload or a reference document is not truncated, and its answer to you is not cut short. The conversation is the one exception, and it ships bounded rather than unlimited: a long thread is summarised automatically as it fills, at 70% of its budget by default (see below). An operator can set any of those limits from the admin console, and when one is in force the Assistant is told what it did not see and how to fetch the rest — so a shortened read is always stated, never silent. One bound is not an operator setting and stays: a web page stops downloading after 1.5 MB, which protects the service from whatever an arbitrary site sends rather than limiting what the Assistant may read.
A long conversation is summarised, never dropped
Every model has a limit on how much conversation it can hold at once. When a thread grows past your chosen share of its budget — and that budget is the context window of the model the thread would actually use, where the platform knows that window, and your operator’s own ceiling otherwise — the earliest exchanges are summarised into a carry-forward note that the Assistant keeps reading — decisions you made, files that were touched, constraints you stated. They are not deleted and they are not dropped: every message stays in the thread and on your screen, and a labelled band at the top of the conversation says how many turns are being read as a summary and lets you open the note itself.
The context symbol beside the composer shows how full the conversation is and shifts colour as it fills. Click it to see the numbers, to compact now yourself at any point — including when your operator has set the ceiling to zero, which switches automatic summarising off for everyone whatever any model’s window says — to switch automatic summarising on or off for yourself (on by default), and to choose the percentage it starts at — 70% by default. Writing the summary uses one AI call, the same as sending a message. If the summariser is unavailable when a fold happens, the Assistant keeps a plain list of what those turns did instead and says so in the thread. What never happens is the earliest part of your conversation quietly disappearing from the Assistant's memory while the transcript still shows it.
A worked request
“Add a maximum-daily-loss guard that flattens the position and stops trading
for the session.” The agent reads risk.py and
exits.py, proposes the guard plus a test that exercises it, runs the
checks, and presents the diff. You review it and decide.
It can see everything you can see
The Assistant is not limited to the files in one workspace. Anything you can open in the app, it can read — live, and only ever as you: your runs and their artifacts, audits and reviews; your usage, quota, credits and plan; your data coverage and entitlements; your journal trades, notes, goals and playbooks; your strategies, versions and briefs; optimization studies, screener sweeps, prop-firm evaluations and firm rules; notifications, projects and workspaces. Ask “how much quota do I have left?” or “what did my last backtest do?” and it looks the answer up rather than guessing or asking you to paste it.
Coverage is derived from the app itself rather than a hand-picked list, so a new page becomes readable to the Assistant as soon as it ships. Each read runs through exactly the same permission checks your browser hits, carrying your own session and nothing else — so it can never see more than you can. Today that is 139 of the platform's 218 read endpoints for a normal account and 167 for an admin; the difference is the admin console, and the rest of the gap is the four categories below. Those numbers are counted by a test, not estimated, and the test fails if coverage ever shrinks.
Three things it can now read that it could not before, because they used to exist only on the API-token surface that automated tools use:
- Why a run failed — not just that it did. The Assistant reads the run's own event log: the phase it reached, the warnings it emitted, and the error it ended on.
- What a chart is made of. It can open a run's stored files and read the rows of a stored table, so it checks a figure against the numbers instead of describing a picture.
- Which building blocks exist — the feature-primitive catalogue, including whether each value is knowable at a bar's open or only at its close. That distinction is what separates a legitimate signal from one that peeks at the future.
Four things it will tell you it cannot read, and why:
- Secrets and credentials. API keys are write-once: nothing in the app hands them back, to you or to the Assistant. It can tell you a key exists, never what it is.
- Anyone else's data. It holds only your session, so another user's or team's records are invisible to it exactly as they are to you.
- The admin console, unless you are an admin. The Assistant acts as you, never as the platform.
- Live streams, downloads and sign-in redirects. These aren't readable snapshots; the Assistant points you at the equivalent read instead.
Everything it reads is treated as data, never as instructions. If stored content — a note, a run log, an artifact name — contains text shaped like a command to the AI, the Assistant flags it to you visibly instead of acting on it. That matters most once a project is shared, when the content it reads may not all be yours.
Nothing is applied until you accept
Everything the Assistant produces is an inert draft. It cannot write to your files, and it cannot run your strategy on data. Applying a change is always your deliberate action — and, exactly as in the Strategy Builder, production code, tests and test setup are accepted separately — a button appears for each kind of change the proposal actually contains:
- Accept production — applies only the strategy files.
- Accept tests — applies only the test files.
-
Accept test setup — applies only the fixtures that run around your
tests (
conftest.pyand test helpers). They run before every test and can replace the very code under test, so a suite can go green without the bug being fixed — that is why this is never folded into the production accept. - Reject — discards the whole proposal, changing nothing.
Proposals don't vanish if you step away: every draft the agent stages also lands in the
AI-drafts inbox at /app/drafts, so you can find it and
accept it there later rather than only from the link it returned.
The per-scope split exists for the same reason it does in the builder: the tests are the leash on the code. A strategy cannot run until its required tests pass, so the app never lets a single click rewrite the code and the tests — or the fixtures those tests run against — that are supposed to catch its mistakes. Give the test diff the same scrutiny as the production diff — ask whether those tests would still catch the failure modes you care about. See Testing your strategy for why the suite matters.
The agent's output is text, not commands
The model's replies are treated as untrusted data: rendered as reasoning and diffs, never obeyed as instructions to the app. If a proposal would touch tests, or if the request text looks like it is trying to instruct the app rather than describe a change, the panel says so before you accept. The one thing that does happen to proposed code automatically is a sandboxed run of the workspace's own test suite, so the review panel can tell you whether the code works — it writes nothing, uses no market data, and accepts nothing. For proposals staged by an external agent over MCP, that automatic run draws from the machine token's daily verification cap — 50 a day, the same cap as agent-enqueued runs — and past it the proposal still lands, carrying an honest skipped verification note with the reset time instead of a test verdict. Applying code, and running it on real data, stay yours.
Choosing the provider and model
Generation runs on your own AI key, or on platform-provided models where the operator of your instance has enabled them. The provider catalog covers the major labs — Anthropic, OpenAI, Google, DeepSeek, Qwen, Kimi and more — each as a bring-your-own-key connection. A Model switcher sits in the composer, next to Send, so you can change which provider and model serve you without leaving the conversation — pick a faster model for quick edits, a stronger one for gnarly refactors. The choice is saved on the connection itself (the same secure store the connections manager writes), so it survives reloads and applies everywhere that key is used.
- An explicit model pick is strict. Pin a model and the call uses exactly that model — if it is unavailable or fails, you get a clear, recoverable error, never a silent substitute. Leave the pick on automatic and the app keeps ranked failover across your available providers.
- With more than one key configured, the switcher also lets you pick which key to use; the model list is derived from that provider — its default model plus any overrides you have set. Nothing is hardcoded.
- With no key configured, you can pick a platform-provided model where your instance offers them; otherwise the control says so and links you to Settings → AI keys & connections to add a key.
- Point a key at the features you want it to handle. Editing a connection shows a “Use this connection for:” list of the app’s real AI features — the Assistant panel, building a strategy, code edits and tests, explaining runs and code, the independent run review, and the journal-side coaching surfaces. Ticking one makes that key the first choice there; it is a preference, not a restriction, so a key with nothing ticked stays a general key that serves everything and backs up a ticked key that is rate-limited or fails.
AI usage is metered — daily AI call and token quotas are set by your plan (see Pricing), and compute is metered like other work — see Data & credits.
Where to go next
- The AI Strategy Builder — create a strategy from a plain-English idea.
- Writing strategies by hand — the files, the contract, and the safety rails the agent works within.
- External agents (MCP) — drive the same read/propose loop from your own coding agent outside the app.