stratus

Sessions

Multi-turn conversations with persistent message history

Sessions provide a high-level API for multi-turn conversations. Messages persist across send()/stream() cycles, so the model sees the full conversation history on every turn.

Creating a Session

session.ts
import { createSession } from "stratus-sdk/core";

const session = createSession({
  model,
  instructions: "You are a weather assistant.",
  tools: [getWeather],
});

Send and Stream

The session API follows a simple two-step pattern:

Queue a message

send(message) queues a user message synchronously - no API call is made.

session.send("What's the weather in NYC?");

Stream the response

stream() runs the agent loop, yielding streaming events.

for await (const event of session.stream()) {
  if (event.type === "content_delta") {
    process.stdout.write(event.content);
  }
}

Multi-Turn

Just call send() and stream() again. Previous messages are automatically included:

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

// The model sees the full conversation so far
session.send("What about London?"); 
for await (const event of session.stream()) {
  if (event.type === "content_delta") process.stdout.write(event.content);
}

Multimodal Messages

send() accepts either a string or a ContentPart[] array for multimodal input:

multimodal.ts
import type { ContentPart } from "stratus-sdk/core";

const parts: ContentPart[] = [
  { type: "text", text: "What is in this image?" },
  { type: "image_url", image_url: { url: "https://example.com/photo.png" } },
];

session.send(parts); 
for await (const event of session.stream()) {
  if (event.type === "content_delta") process.stdout.write(event.content);
}

Content Part Types

TextContentPart - { type: "text", text: string }

ImageContentPart - { type: "image_url", image_url: { url: string, detail?: "auto" | "low" | "high" } }

The prompt() function also accepts ContentPart[]:

const result = await prompt(parts, { model });

Accessing Results

After consuming the stream, access the result via session.result:

session.send("Summarize our conversation.");
for await (const event of session.stream()) { /* ... */ }

const result = await session.result;
console.log(result.output);        // Raw string output
console.log(result.finishReason);   // "stop", "tool_calls", etc.
console.log(result.usage);          // Token usage across this turn
console.log(result.lastAgent);      // Agent that handled this turn

Message History

Access the accumulated conversation history at any time:

const messages = session.messages;
// Returns a copy - mutating it won't affect the session

Messages include all user, assistant, and tool messages from previous turns. System messages are managed internally and not included.

Save, Resume, and Fork

Sessions can be saved to a snapshot and resumed or forked later. This enables persistence, branching conversations, and recovery from failures.

save.ts
const snapshot = session.save();
// snapshot.id - same as session.id
// snapshot.messages - deep copy of the conversation history

save() throws if the session is closed or currently streaming.

Resume a session with the same ID and conversation history:

resume.ts
import { resumeSession } from "stratus-sdk/core";

const session2 = resumeSession(snapshot, {
  model,
  instructions: "You are a helpful assistant.",
});

// session2.id === snapshot.id
session2.send("Continue where we left off.");
for await (const event of session2.stream()) { /* ... */ }

Fork creates a new session (new ID) with a copy of the conversation history:

fork.ts
import { forkSession } from "stratus-sdk/core";

const forked = forkSession(snapshot, {
  model,
  instructions: "You are a helpful assistant.",
});

// forked.id !== snapshot.id
forked.send("Take a different direction.");
for await (const event of forked.stream()) { /* ... */ }

Abort Signal

Pass an AbortSignal to stream() to cancel a running turn:

abort.ts
import { RunAbortedError } from "stratus-sdk/core";

const ac = new AbortController();

session.send("Write a very long essay.");
try {
  for await (const event of session.stream({ signal: ac.signal })) { 
    if (event.type === "content_delta") process.stdout.write(event.content);
  }
} catch (error) {
  if (error instanceof RunAbortedError) {
    console.log("Stream was cancelled");
  }
}

The signal is per-invocation, not per-session. See Streaming - Abort Signal for more details.

Cleanup

Sessions support both explicit cleanup and await using:

await using session = createSession({ model });
// session.close() is called automatically when the block exits
session.close();
// After closing, send(), stream(), and save() will throw

One-Shot with prompt()

For single-turn use cases, prompt() is a convenience that creates a session, sends a message, drains the stream, and returns the result:

import { prompt } from "stratus-sdk/core";

const result = await prompt("What is 2 + 2?", { model });
console.log(result.output); // "4"

Session Config

SessionConfig accepts the same options as AgentConfig (except name), plus context and maxTurns:

PropertyTypeDescription
modelModelRequired. The LLM model
instructionsInstructionsSystem prompt
toolsFunctionTool[]Available tools
subagentsSubAgent[]Sub-agents that run as tool calls
modelSettingsModelSettingsTemperature, max tokens, etc.
outputTypez.ZodTypeStructured output schema
handoffsHandoffInput[]Handoff targets
inputGuardrailsInputGuardrail[]Input guardrails
outputGuardrailsOutputGuardrail[]Output guardrails
hooksAgentHooksLifecycle hooks
toolUseBehaviorToolUseBehaviorPost-tool-call behavior
contextTContextShared context object
maxTurnsnumberMax model calls per stream() (default: 10)
costEstimatorCostEstimatorFunction that converts UsageInfo to a dollar cost. Enables totalCostUsd on results
maxBudgetUsdnumberMaximum dollar budget per stream(). Throws MaxBudgetExceededError when exceeded
Edit on GitHub

Last updated on

On this page