stratus

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.

run.ts
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

PropertyTypeDescription
outputstringRaw text output from the last model response
finalOutputTOutputParsed structured output (if outputType is set on the agent)
messagesChatMessage[]Full message history for the run, including system, user, assistant, and tool messages
usageUsageInfoAccumulated token usage across all model calls in this run
lastAgentAgentThe agent that produced the final response (differs from the entry agent after a handoff)
finishReasonFinishReason?The model's finish reason from the last call ("stop", "tool_calls", "length", "content_filter")
numTurnsnumberNumber of model calls made during the run
totalCostUsdnumberEstimated 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.

stream.ts
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

EventFieldsDescription
content_deltacontent: stringA chunk of text content from the model
tool_call_starttoolCall: { id, name }A tool call has started
tool_call_deltatoolCallId, argumentsIncremental tool call argument data
tool_call_donetoolCallIdTool call arguments are complete
doneresponse: ModelResponseThe 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.

multi-round-events.ts
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.

prompt.ts
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:

OptionTypeDefaultDescription
contextTContextundefinedShared context object passed to instructions, tools, guardrails, and hooks
maxTurnsnumber10Maximum number of model calls before throwing MaxTurnsExceededError
signalAbortSignalundefinedAbort signal for cancellation. Throws RunAbortedError when aborted
modelModelAgent's modelOverride the agent's model for this run
costEstimatorCostEstimatorundefinedFunction that converts UsageInfo to a dollar cost. Enables totalCostUsd on the result
maxBudgetUsdnumberundefinedMaximum dollar budget. Throws MaxBudgetExceededError when exceeded. Requires costEstimator
options.ts
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:

multi-turn.ts
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

Edit on GitHub

Last updated on

On this page