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.
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.
send() returns a durable Turn after Snowflake accepts that turn. If a previous turn on this conversation is still
running, send() waits for it to finish first. result() waits and returns a memoized AgentResponse.
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.
To reconnect to an existing run’s event stream, pass turn.id to client.runs.stream() on the direct API client.
Call result(), events(), and cancel() on the Turn that send() returned.
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:
Select a named agent object:
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.
For approval-based flows, create a coding conversation with permissionPolicy: "always_ask". send() and
sendPermissionDecisions() both use that conversation’s agent. An inline conversation, such as convo above, has no
coding tools and does not emit permission requests. Stream events from the returned turn. When a
response.tool_use event includes permission options, show those options to the approver. Use
sendPermissionDecisions() to continue the conversation after the approver selects options. The helper waits for the
paused turn and uses its assistant message as the parent of the follow-up turn. If a turn requests multiple
permissions, include every selected decision in one sendPermissionDecisions() call. Any pending request omitted
from that call is denied. When you pass a PermissionRequest, the SDK rejects a decision that is not one of the
request’s options. To explain a denial to the model, include an optional reason in the decision object.
Execute client-side tools¶
To configure a generic client-side tool, include it in tools and omit a matching entry in tool_resources. The
service emits that tool use with client_side_execute: true. Call clientToolUses() on the pending turn, execute
every returned invocation in your application, then send every result in one sendToolResults() call. The helper
constructs the required tool_result content and uses the paused assistant message as the follow-up parent.
For other structured client content, pass a ConversationInput array to send():
Select alternate agent types:
For coding-agent turns, permission_policy: "always_allow" automatically approves tool operations that would
otherwise require approval. Use it only with trusted inputs and appropriately scoped workspace and account access. Use
an approval-based permission policy when your application is prepared to handle permission-request events and submit
approval decisions.
For approval-based flows, create a coding conversation with permission_policy set to "always_ask". send() and
send_permission_decisions() both use that conversation’s agent. An inline conversation, such as conversation
above, has no coding tools and does not emit permission requests. Stream events from the returned turn. When a
response.tool_use event includes permission options, show those options to the approver. Use
send_permission_decisions() to continue the conversation after the approver selects options. The helper waits for
the paused turn and uses its assistant message as the parent of the follow-up turn. If a turn requests multiple
permissions, include every selected decision in one send_permission_decisions() call. Any pending request omitted
from that call is denied. When you pass a PermissionRequest, the SDK rejects a decision that is not one of the
request’s options. To explain a denial to the model, add an optional third reason item to the decision tuple.
Execute client-side tools¶
To configure a generic client-side tool, include it in tools and omit a matching entry in tool_resources. The
service emits that tool use with client_side_execute: true. Call client_tool_uses() on the pending turn, execute
every returned invocation in your application, then send every result in one send_tool_results() call. The helper
constructs the required tool_result content and uses the paused assistant message as the follow-up parent.
For other structured client content, pass a ConversationInput array to send():
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.
Inspect history and manage the thread:
attach() continues from the latest assistant message. In Python, pass message_id to start from a specific message
instead. branch() creates a local branch from a specific message without changing the original object.
The agent passed to create() lives only on that conversation object. attach() does not copy it, so pass agent on
each later send(). If you omit it, the next turn uses an empty inline agent.
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.
List and search threads¶
TypeScript client.conversations can list and search threads without dropping to the direct API. Python
ConversationsResource does not include these methods; use the direct thread API described in
Use the direct Cortex Agent API.
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¶
- Direct Agent API: Use low-level run, thread, and agent-object resources
- SDK reference: High-level return types and raw transport wrappers
Legal notices¶
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 classification | Output data classification | Designation |
|---|---|---|
| Usage Data | Customer Data | Covered AI Features [1] |
For additional information, refer to Snowflake AI and ML.