Running Agents
Execute agents with run(), stream(), or prompt()
Three ways to execute an agent. All handle the full tool loop, guardrails, hooks, and tracing automatically.
run()-- Returns the final result. Best when you don't need real-time output.stream()-- Yields events as they arrive. Best for real-time UIs and CLIs.prompt()-- One-shot convenience. Single turn in, result out.
run()
run() takes an agent and input, executes the full tool loop, and returns a RunResult when done. You get no intermediate output -- just the final result.
import { Agent, run } from "stratus-sdk/core";
import { AzureResponsesModel } from "stratus-sdk";
const model = new AzureResponsesModel({
endpoint: process.env.AZURE_ENDPOINT!,
apiKey: process.env.AZURE_API_KEY!,
deployment: "gpt-5.2",
});
const agent = new Agent({
name: "assistant",
model,
instructions: "You are a helpful assistant.",
});
const result = await run(agent, "What is the capital of France?");
console.log(result.output); // "The capital of France is Paris."
console.log(result.messages); // Full message history (system, user, assistant, tool)
console.log(result.lastAgent); // The agent that produced the final response
console.log(result.usage); // { promptTokens, completionTokens, totalTokens }
console.log(result.finishReason); // "stop"
console.log(result.finalOutput); // undefined (no outputType set)If the agent has an outputType, finalOutput contains the parsed and validated object. See Structured Output for details.
RunResult
| Property | Type | Description |
|---|---|---|
output | string | Raw text output from the last model response |
finalOutput | TOutput | Parsed structured output (if outputType is set on the agent) |
messages | ChatMessage[] | Full message history for the run, including system, user, assistant, and tool messages |
usage | UsageInfo | Accumulated token usage across all model calls in this run |
lastAgent | Agent | The agent that produced the final response (differs from the entry agent after a handoff) |
finishReason | FinishReason? | The model's finish reason from the last call ("stop", "tool_calls", "length", "content_filter") |
numTurns | number | Number of model calls made during the run |
totalCostUsd | number | Estimated cost in USD (requires costEstimator in options, otherwise 0) |
UsageInfo includes promptTokens, completionTokens, totalTokens, optional cacheReadTokens, cacheCreationTokens, and reasoningTokens fields.
stream()
stream() returns two things: an async generator of StreamEvent objects and a Promise<RunResult>. You must drain the stream before awaiting the result.
import { Agent, stream } from "stratus-sdk/core";
const agent = new Agent({
name: "writer",
model,
instructions: "You are a creative writer.",
});
const { stream: s, result } = stream(agent, "Write a haiku about TypeScript");
for await (const event of s) {
if (event.type === "content_delta") {
process.stdout.write(event.content);
}
}
const finalResult = await result;
console.log(finalResult.output);
console.log(finalResult.usage);You must fully consume the stream before awaiting result. If you skip the stream, the result promise never resolves.
Stream Events
| Event | Fields | Description |
|---|---|---|
content_delta | content: string | A chunk of text content from the model |
tool_call_start | toolCall: { id, name } | A tool call has started |
tool_call_delta | toolCallId, arguments | Incremental tool call argument data |
tool_call_done | toolCallId | Tool call arguments are complete |
done | response: ModelResponse | The model finished a response |
When the model makes tool calls, you see multiple rounds of events. Each round starts with tool call events, followed by content events after the tools execute and the model responds again.
for await (const event of s) {
switch (event.type) {
case "tool_call_start":
console.log(`Calling: ${event.toolCall.name}`);
break;
case "content_delta":
process.stdout.write(event.content);
break;
case "done":
// One 'done' per model call - multiple if tools are used
console.log(`Tokens: ${event.response.usage?.totalTokens}`);
break;
}
}prompt()
prompt() is the simplest way to get a response. It creates a temporary session, sends your message, drains the stream, and returns the result.
import { prompt } from "stratus-sdk/core";
const result = await prompt("What is 2 + 2?", {
model,
instructions: "You are a math tutor.",
tools: [calculator],
});
console.log(result.output); // "4"prompt() creates a temporary session under the hood. For multi-turn conversations, use createSession() instead.
prompt() accepts the same configuration options as createSession() -- including tools, instructions, outputType, guardrails, and hooks.
Options
run() and stream() accept an optional RunOptions object as the third argument:
| Option | Type | Default | Description |
|---|---|---|---|
context | TContext | undefined | Shared context object passed to instructions, tools, guardrails, and hooks |
maxTurns | number | 10 | Maximum number of model calls before throwing MaxTurnsExceededError |
signal | AbortSignal | undefined | Abort signal for cancellation. Throws RunAbortedError when aborted |
model | Model | Agent's model | Override the agent's model for this run |
costEstimator | CostEstimator | undefined | Function that converts UsageInfo to a dollar cost. Enables totalCostUsd on the result |
maxBudgetUsd | number | undefined | Maximum dollar budget. Throws MaxBudgetExceededError when exceeded. Requires costEstimator |
const ac = new AbortController();
setTimeout(() => ac.abort(), 10_000);
const result = await run(agent, "Summarize this document", {
context: { userId: "user_123", db: myDatabase },
maxTurns: 5,
signal: ac.signal,
});Passing input
You can pass input as a plain string, an array of ChatMessage objects, or a ContentPart[] array for multimodal content.
The most common form. Stratus wraps it in a user message automatically.
const result = await run(agent, "Hello, world!");Pass a full message array when you need to prefill conversation history or include system messages:
import type { ChatMessage } from "stratus-sdk/core";
const messages: ChatMessage[] = [
{ role: "user", content: "My name is Alice." },
{ role: "assistant", content: "Hello Alice! How can I help?" },
{ role: "user", content: "What is my name?" },
];
const result = await run(agent, messages);Use ContentPart[] inside a message to send images alongside text:
import type { ChatMessage, ContentPart } from "stratus-sdk/core";
const messages: ChatMessage[] = [
{
role: "user",
content: [
{ type: "text", text: "What is in this image?" },
{ type: "image_url", image_url: { url: "https://example.com/photo.png" } },
],
},
];
const result = await run(agent, messages);Multi-turn with sessions
run() and stream() are stateless -- they don't preserve messages between calls. For multi-turn conversations, use sessions:
import { createSession } from "stratus-sdk/core";
await using session = createSession({
model,
instructions: "You are a helpful assistant.",
tools: [getWeather],
});
session.send("What's the weather in NYC?");
for await (const event of session.stream()) {
if (event.type === "content_delta") process.stdout.write(event.content);
}
session.send("What about London?");
for await (const event of session.stream()) {
if (event.type === "content_delta") process.stdout.write(event.content);
}See Sessions for the full API, including save/resume/fork and Symbol.asyncDispose.
Next steps
Last updated on