Conversations with the Cortex Agent SDK

Use conversations to maintain context across turns, processes, and application sessions. Start here for most applications. Use the lower-level thread, run, and agent-object APIs only when you need direct control over stored messages, thread metadata, run IDs, or wire-level Agent API requests.

Use Conversation

Conversation is the recommended interface for most applications. A conversation represents a persistent thread and its history. Set a default agent when you create it, and override that agent on individual turns when needed.

import type { AgentResponse, Conversation, Turn } from "@snowflake/cortex-agent-sdk";

const convo: Conversation = await client.conversations.create({
  thread: { origin_application: "my-app" },
  agent: {
    type: "inline",
    spec: { models: { orchestration: "claude-sonnet-4-5" } },
  },
});

const first: Turn = await convo.send("What is Snowflake?");
console.log(first.id);

const answer: AgentResponse = await first.result();
console.log(answer.text);

const second: Turn = await convo.send("How does it compare to a data lake?", {
  agent: {
    type: "data",
    spec: { database: "MY_DB", schema: "PUBLIC", name: "MY_AGENT" },
  },
});
const secondAnswer: AgentResponse = await second.result();
console.log(secondAnswer.text);

client.conversations.create() resolves to Promise<Conversation> because it creates the thread before returning. Each send() uses the default agent unless its options specify an inline, data, or coding agent override.

Work with a turn

Use the Turn returned by send() to wait for the final response, read replayable events, or cancel the work. turn.id is the server-assigned run ID.

const turn: Turn = await convo.send("Analyze five years of revenue.", {
  agent: { type: "inline", spec: { models: { orchestration: "claude-sonnet-4-5" } } },
});

for await (const event of turn.events()) {
  if (event.event === "response.text.delta") {
    process.stdout.write(event.text ?? "");
  }
}

const result: AgentResponse = await turn.result();
await turn.cancel();

To reconnect to an existing run’s event stream, pass turn.id to client.runs.stream() on the direct API client.

Select agents and send content

Select the agent and pass its configuration for each turn. You can also pass a typed content-item array instead of a plain string to return a client-side tool result or send a document already uploaded to a Snowflake stage.

Select an inline agent:

const inlineTurn: Turn = await convo.send("Summarize Q1 revenue.", {
  agent: {
    type: "inline",
    spec: {
      models: { orchestration: "claude-sonnet-4-5" },
      instructions: { response: "Answer in two sentences." },
    },
  },
});

Select a named agent object:

const dataTurn: Turn = await convo.send("Which product line grew fastest?", {
  agent: {
    type: "data",
    spec: {
      database: "MY_DB",
      schema: "PUBLIC",
      name: "my_agent",
      version: "VERSION$2", // Optional.
    },
  },
});

Select the coding agent:

always_allow automatically approves coding-agent tool operations that would otherwise require approval, including destructive-looking shell commands. Use it only when your application intentionally trusts the input source and scopes workspace mounts, stages, credentials, and network access for that workload. Use an approval-based permission policy when a person or application approval flow must inspect tool requests before they run.

const codingTurn: Turn = await convo.send("Write a summary to /workspace/summary.txt", {
  agent: {
    type: "coding",
    spec: {
      permissionPolicy: "always_allow",
      workspaceMounts: [{ name: "USER$MYUSER.PUBLIC.DEFAULT$" }],
      models: { orchestration: "claude-sonnet-4-5" },
    },
  },
});

For approval-based flows, set permissionPolicy: "always_ask" and stream events from the returned turn. When a response.tool_use event includes permission options, show those options to the approver. Send the selected option back as a permission_decision content item on the next turn.

Send client-supplied content:

import type { ConversationInput } from "@snowflake/cortex-agent-sdk";

const input: ConversationInput = [
  {
    type: "tool_result",
    tool_result: {
      tool_use_id: toolUseId,
      name: "weather",
      status: "success",
      content: [{ type: "json", json: { temperature_c: 21, condition: "clear" } }],
    },
  },
  { type: "text", text: "Give the user a one-sentence forecast." },
];
const toolTurn: Turn = await convo.send(input);

const document: ConversationInput = [
  {
    type: "document",
    source: {
      url: "@MY_DB.MY_SCHEMA.MY_STAGE/quarterly-results.txt",
      file_name: "quarterly-results.txt",
      media_type: "text/plain",
    },
  },
  { type: "text", text: "Summarize this document." },
];
const documentTurn: Turn = await convo.send(document);

Continue a conversation

Attach to an existing thread to continue from the latest assistant message. Branch from an earlier assistant message when you want the next turn to use that message as its parent.

const attached: Conversation = await client.conversations.attach(convo.id);
const attachedTurn: Turn = await attached.send("Where did we leave off?", {
  agent: { type: "inline", spec: { models: { orchestration: "claude-sonnet-4-5" } } },
});

const branch: Conversation = convo.branch(firstAssistantMessageId);
const branchTurn: Turn = await branch.send("Follow up on the first thing you said.", {
  agent: { type: "inline", spec: { models: { orchestration: "claude-sonnet-4-5" } } },
});

Inspect history and manage the thread:

import type { ConversationMessage, SuccessResponse } from "@snowflake/cortex-agent-sdk";

convo.id; // string

const messages: ConversationMessage[] = await convo.history();
for (const message of messages) {
  console.log(`#${message.id} [${message.role}]`, message.text.slice(0, 80));
}

const renamed: SuccessResponse = await convo.rename("Q1 revenue analysis");
const deleted: SuccessResponse = await convo.delete();

Calls are serialized

Calls to send() are serialized inside one Conversation instance. Before the SDK dispatches another turn, it resolves the prior turn and uses the latest assistant message as the parent. This guarantees correct parent message ordering. A failed turn does not advance the conversation.

Note

If another client adds turns to the same thread, call client.conversations.attach(threadId) again when you need to resync with the newest assistant message.

Use the direct API when needed

In most cases, use client.conversations. The conversation helper creates the thread, tracks the current parent message, starts durable turns, exposes result/event/cancel helpers, and keeps follow-up turns in order.

Use the direct API only when you want to manage thread_id and parent_message_id yourself, inspect raw transport wrappers, or build a workflow around lower-level REST resources. For those APIs, see Use the direct Cortex Agent API.

Next steps

Where your configuration of Cortex Code uses a model provided on the Model and Service Pass-Through Terms, your use of that model is further subject to the terms for that model on that page.

The data classification of inputs and outputs are as set forth in the following table.

Input data classificationOutput data classificationDesignation
Usage DataCustomer Data

Covered AI Features

[1]

For additional information, refer to Snowflake AI and ML.