Primitives
On this page
Stratos provides a few building blocks that compose through ordinary TypeScript. Their inputs and outputs are typed, failures are catchable, and the workflow owns the resources they create. Use the smallest building block that fits the task rather than asking an agent to handle every step.
| Primitive | Detailed documentation |
|---|---|
| Workflows and children | Contracts and composition |
| Agents | Configuration, conversations, and lifecycle |
| MCP tools | Setup and typed calls |
| Decision | Questions, schemas, providers, and retries |
Logs, events, trace export, and measurements for every primitive are covered in Telemetry.
API at a glance
The similar-looking factories have different lifecycles. Create execution
objects inside a workflow’s run function and await their work before returning.
| API | Result | Reuse |
|---|---|---|
workflow({input, output, run}) |
A workflow definition, default-exported by its entrypoint | Run through the CLI or a generated child wrapper |
createAgent(options) |
An agent; execute(prompt, variables?) returns text or schema-typed output |
One conversation; sequential calls only |
Generated child factory createChild({overrides}?) |
A child reference; execute(input) returns the child’s output |
A fresh invocation per call; parallel calls allowed |
Generated MCP factory await createServer() |
A ready client with typed tool methods | Reuse its connection; concurrent calls allowed |
decision({state, questions, ...settings}) |
A promise of typed answers and usage | Each call is a separate decision request |
choice, score, noul |
Typed question objects | Pure builders, with no network call or execution context |
Named agent and Decision settings live in workflow.yaml. Explicit settings
in code take precedence over invocation overrides, which take precedence over
the named YAML settings. Unspecified fields retain their lower-priority values;
arrays replace rather than append. See
configuration precedence and
Decision settings.
Workflows and child workflows
A workflow is a reusable function with a Zod input/output contract and a
workflow.yaml manifest. The TypeScript module default-exports
workflow({input, output, run}); run holds the actual work.
Use strict object inputs and describe every input/output field. Stratos derives
TypeScript types and JSON Schema from the same declarations, validates values,
and compiles the workflow before running it. Keep actions inside run, because
preparation evaluates modules to read their schemas.
A parent can call smaller workflows through generated, typed wrappers:
import createGreet from '../.generated/greet.js';
// Inside the parent workflow's run function:
const greet = createGreet();
const result = await greet.execute({name: 'Ada', loud: true});
This uses the greeting workflow from the complete first-workflow example. Build the parent to prepare the child and its wrapper; do not write generated files yourself.
Each call starts an independent child invocation with its own configuration, validation, and cleanup. Children must be in the same discovery root. Parent configuration does not leak into a child; bind overrides explicitly when needed. Parallel calls are allowed, while calling an active ancestor is rejected.
Read the complete child example and composition contract.
Agents
Use an agent when a task needs a conversation, reasoning, or a sequence of tool
actions. Stratos integrates Claude Code, Codex, and OpenCode through a common
createAgent interface.
import {createAgent} from '@stratoscli/stratos';
// Inside run, using a named agent configuration in workflow.yaml:
const reviewer = createAgent({configId: 'reviewer'});
const review = await reviewer.execute('Review the changes in this workspace.');
const summary = await reviewer.execute('Summarize the main findings.');
The example requires a reviewer entry in the manifest and working native
harness/provider authentication. See the
complete configured agent example
before running it; reviewing a workspace can involve real tool calls.
Creation is synchronous and native initialization happens on first execution. Reusing one object continues its conversation sequentially. Separate objects have separate conversations and can run in parallel. An agent’s model, harness, system prompt, tools, and other settings remain fixed; only its user prompt and variables change between requests.
Without an output schema, execute returns text. Supply outputSchema with a
Zod schema to request and validate structured output. Stratos does not repair
arbitrary prose into an apparently successful result.
Workflow cleanup releases owned resources. Cancellation is a signal, not proof that all work has stopped; see the native lifecycle limits.
Choose an agent’s tools
An agent’s harness supplies built-in tools such as shell execution and file editing. These are separate from the external tools provided by MCP servers. First inspect the names supported by the harness you intend to use:
stratos tools list
stratos tools list opencode
stratos tools list codex --json
These commands read Stratos’s tool catalog without starting a harness or
requiring credentials. The target is the harness, not the model provider.
Use the returned names exactly: some are shared names such as shell, while
others are harness-specific, such as Claude Code’s NotebookEdit.
The CLI reference explains the output.
Use allowedTools when you want to keep a small set of built-in tools. For
example, this named configuration retains OpenCode’s file-reading and search
tools while removing its other mapped built-in tools:
agents:
reviewer:
harness: opencode
model: [openai/gpt-6-astra]
allowedTools: [read, glob, grep]
allowedMcps: []
Add the entry under agents in your workflow’s manifest and use it with
createAgent({configId: 'reviewer'}). The model is an example; configure
working harness/provider access before execution.
Use disallowedTools instead when you want to keep the defaults except for
selected tools, for example disallowedTools: [shell, web_search]. If both
lists are present, denial wins: a name in both lists is removed.
| Setting | Effect |
|---|---|
| Both built-in lists omitted | Keep the harness’s default built-in tools |
allowedTools: [...] |
Remove mapped, removable tools not in the list |
allowedTools: [] |
Remove every mapped, removable built-in tool, not necessarily every tool |
disallowedTools: [...] |
Remove the listed tools; [] removes none through this list |
allowedMcps: [...] |
Select workflow-defined MCP servers independently; [] selects none |
The same fields work in createAgent and invocation overrides. Explicit code
settings win over overrides, which win over YAML. Each list replaces the
lower-priority value of that field rather than appending; overriding
allowedTools does not clear an inherited disallowedTools. Selection stays
fixed for that agent object.
Tool selection only removes tools; it does not grant new capabilities or provide a sandbox. A listed tool that the harness leaves off stays off. Unknown names fail validation, while a known name absent from the selected harness has no effect.
Pay attention to the catalog’s always-on tools and the harness-specific limits.
Codex’s edit and write cannot be removed with these lists. OpenCode must keep
or exclude edit and write together. On Claude Code, excluding shell
does not exclude the separate Monitor tool, which can also run commands.
Tools outside Stratos’s mappings are not filtered, and structured-output
delivery remains available.
Remaining tools are approved by default. Use control hooks when you need to inspect arguments, deny a particular call, or modify its inputs. Read the full selection contract and harness mappings before relying on a restriction.
MCP tools
Use direct MCP clients when code knows which tool to call and does not need a
model to select it. Put server configuration in workflow.yaml, then run
stratos build <workflow> to discover tools and generate typed wrappers.
Explicit builds can prepare these wrappers before the entrypoint exists.
import createCatalog from './generated/mcp/catalog.js';
// Inside run, after discovery of a tool with this contract:
const catalog = await createCatalog();
const response = await catalog.lookup({key: 'example'});
catalog and lookup are illustrative: use the actual configured server and
discovered tool signature, not assumed endpoints or methods. Follow the
MCP tool guide for setup,
authentication, generated types, and a complete workflow.
The awaited factory returns a ready client owned by the workflow. Tool calls
reuse that connection and may run concurrently. Results retain the MCP envelope;
a declared output schema types and validates response.structuredContent.
Without a declared schema, validate unknown data before using it.
Tool errors and transport failures throw. Requests have a five-minute timeout and are not automatically retried, since replaying a tool can repeat side effects. Agents use their harness’s MCP connections instead of these direct clients.
Decision
Use decision for structured decisions rather than a full agent conversation.
Jev and compatible local Ollama decision models expose the native
/v1/systemone API. Focused decisions can reduce cost and latency while letting code retain
control of the larger automation.
| Helper | Question | Answer |
|---|---|---|
choice |
Which option fits? | Selected option, probabilities, and confidence |
score |
Where does this fall on ordered levels? | Numeric score, which can be fractional |
noul |
How likely is this condition to be true? | Probability in [0, 1], not a boolean |
The helpers only build questions. decision makes the request:
import {choice, decision} from '@stratoscli/stratos';
// Inside run, using a named Decision configuration:
const result = await decision({
configId: 'classifier',
state: 'I need help with an invoice.',
questions: {
category: choice('What kind of request is this?', {
billing: 'Payments, invoices, or refunds',
technical: 'Broken functionality or setup',
}),
},
});
const category = result.answers.category.choice;
Configure decision.classifier with the intended provider, model, and
credential reference before running this code. The
complete Decision example
includes the manifest and workflow contract.
Several independent questions can share state in one request. If a later question depends on an earlier answer, make another call. Workflow code owns thresholds and routing; Stratos preserves validated provider answers rather than recalculating them. Compatible response shapes do not promise identical accuracy or confidence calibration across models.
Read the Decision reference for provider requirements, input/output schemas, and retries, and Telemetry for its measurements.
Ordinary code
Use functions, loops, conditions, Promise.all, and try/catch to connect the
primitives. No additional graph language or public parallel helper is needed.
For example, a workflow can classify a request with Decision, select a child
workflow, call a tool, and invoke an agent only for the part requiring reasoning.
Await owned work before returning. Catch expected failures where the workflow can genuinely recover; do not hide failures behind empty successful values. An unhandled error fails the invocation and triggers cleanup. Whole-workflow retry and crash resume are not automatic, so starting again can repeat completed side effects.