Data flow: params, vars & context
Because each session runs in its own isolated LLM context, the interesting part of writing a plan is moving data around — getting a user's input into a session, carrying a fetch result forward, and handing one session's output to the next.
FML gives you exactly three namespaces to do this, available everywhere in templates and expressions: params, vars, and context. This guide explains what each holds, when to reach for it, and how data flows from the plan's inputs to its final output.
Prerequisites
- The Anatomy of a session guide — you should understand that each session has its own context.
The three namespaces
| Namespace | Holds | Written by | Read as (template / Expr) |
|---|---|---|---|
params | User-supplied inputs | parameter(...) declarations | {{ .params.x }} / params.x |
vars | Globals and PreCall results | set and call(...) -> var | {{ .vars.x }} / vars.x |
context | Completed session outputs | sessions finishing (keyed by name or target) | {{ .context.s }} / context.s |
Think of it as inputs → working memory → results. params come in at the start, vars are the scratch space you populate as the plan runs, and context accumulates the finished output of each session.
params — inputs from the caller
Parameters are declared at the root of the plan and supplied by whoever runs it (via the API, SDK, or CLI):
Once declared, a parameter is readable in every session:
Parameters are read-only. They're the stable inputs the whole plan is written against.
vars — globals and fetched data
vars is your working memory. Two things land here:
Globals set with set — plan-wide or session-scoped constants:
PreCall results routed to a var — a deterministic fetch, saved for later:
After that call, vars.openTickets holds the fetched data. You can inject it into a prompt ({{ json .vars.openTickets }}), pass it to another tool as a typed value ($(vars.openTickets)), or gate a session on it (expect="vars.openTickets != null"). See PreCalls deep-dive for routing details.
context — outputs from other sessions
When a session finishes, its typed output is stored under its name in context. A session named overview with this schema:
...publishes context.overview.summary and context.overview.keyPoints. Any later session can read them.
But reading context does not happen automatically — a fresh session doesn't see prior output until you either transfer it in or reference it in a directive. There are two ways to bring it into a session:
1. The context directive injects prior output into the session's LLM context so the model can read it:
Use context true to dump the entire accumulated context as JSON, or a template string (as above) to include just what's relevant. Either form requires after so the source session has already run.
2. Expressions in session arguments reference context to sequence or gate work — expect="len(context.overview.keyPoints) > 0", iterate="context.overview.keyPoints". These also require after. See Control the flow of a plan.
The golden rule for
context: if you readcontext.*anywhere in a session — in acontextdirective,expect, oriterate— that session must declareafteron the session that produced it. Otherwise the value may not exist yet.
How it flows end to end
Here's the full loop — a parameter comes in, a PreCall stores data in a var, one session produces output into context, and a second session reads all three:
params.topic— the caller's input, read in both sessions.vars.rawDocs/vars.tone— fetched data and a global constant.context.gather.findings— the first session's output, transferred into the second (withafterset).
Common mistakes
| Mistake | Fix |
|---|---|
Reading context.other without after="other" | Always pair a context read with after. |
| Expecting a session to "just know" earlier output | Nothing crosses sessions implicitly — transfer it with a context directive. |
Passing an array to a tool as "{{ .vars.list }}" | Templates render to strings; use $(vars.list) to preserve the type — see Templates and expressions. |
Trying to reassign a param | Parameters are read-only inputs; use vars for working state. |
Next steps
- Understand string-vs-typed values in Templates and expressions.
- Populate
varsdeterministically with the PreCalls deep-dive. - Sequence and gate sessions in Control the flow of a plan.
How data moves through a plan across the three namespaces — caller inputs, working memory, and session outputs — and the rule for reading context safely.