Module: ClaudeAgentSDK

Defined in:
lib/claude_agent_sdk.rb,
lib/claude_agent_sdk/query.rb,
lib/claude_agent_sdk/types.rb,
lib/claude_agent_sdk/errors.rb,
lib/claude_agent_sdk/version.rb,
lib/claude_agent_sdk/observer.rb,
lib/claude_agent_sdk/sessions.rb,
lib/claude_agent_sdk/streaming.rb,
lib/claude_agent_sdk/transport.rb,
lib/claude_agent_sdk/configuration.rb,
lib/claude_agent_sdk/session_store.rb,
lib/claude_agent_sdk/fiber_boundary.rb,
lib/claude_agent_sdk/message_parser.rb,
lib/claude_agent_sdk/sdk_mcp_server.rb,
lib/claude_agent_sdk/session_resume.rb,
lib/claude_agent_sdk/command_builder.rb,
lib/claude_agent_sdk/session_summary.rb,
lib/claude_agent_sdk/session_mutations.rb,
lib/claude_agent_sdk/instrumentation/otel.rb,
lib/claude_agent_sdk/subprocess_cli_transport.rb,
lib/claude_agent_sdk/transcript_mirror_batcher.rb,
lib/claude_agent_sdk/testing/session_store_conformance.rb

Overview

Claude Agent SDK for Ruby

Defined Under Namespace

Modules: FiberBoundary, Instrumentation, Observer, SessionMutations, SessionResume, SessionStores, SessionSummary, Sessions, Streaming, Testing Classes: APIRetryMessage, AgentDefinition, AssistantMessage, AsyncHookJSONOutput, AuthStatusMessage, BaseHookInput, CLIConnectionError, CLIJSONDecodeError, CLINotFoundError, ClaudeAgentOptions, ClaudeSDKError, Client, CommandBuilder, CompactBoundaryMessage, CompactMetadata, ConfigChangeHookInput, Configuration, ControlRequestTimeoutError, CwdChangedHookInput, CwdChangedHookSpecificOutput, DeferredToolUse, ElicitationCompleteMessage, ElicitationHookInput, ElicitationResultHookInput, FileChangedHookInput, FileChangedHookSpecificOutput, FilesPersistedMessage, ForkSessionResult, HookContext, HookMatcher, HookProgressMessage, HookResponseMessage, HookStartedMessage, InMemorySessionStore, InitMessage, InstructionsLoadedHookInput, LocalCommandOutputMessage, MaterializedResume, McpClaudeAIProxyServerConfig, McpHttpServerConfig, McpSSEServerConfig, McpSdkServerConfig, McpSdkServerConfigStatus, McpServerInfo, McpServerStatus, McpStatusResponse, McpStdioServerConfig, McpToolAnnotations, McpToolInfo, MessageParseError, MessageParser, MirrorErrorMessage, NotificationHookInput, NotificationHookSpecificOutput, PermissionDeniedHookInput, PermissionDeniedHookSpecificOutput, PermissionRequestHookInput, PermissionRequestHookSpecificOutput, PermissionResultAllow, PermissionResultDeny, PermissionRuleValue, PermissionUpdate, PostCompactHookInput, PostToolUseFailureHookInput, PostToolUseFailureHookSpecificOutput, PostToolUseHookInput, PostToolUseHookSpecificOutput, PreCompactHookInput, PreToolUseHookInput, PreToolUseHookSpecificOutput, ProcessError, PromptSuggestionMessage, Query, RateLimitEvent, RateLimitInfo, ResultMessage, SDKSessionInfo, SandboxFilesystemConfig, SandboxNetworkConfig, SandboxSettings, SdkMcpPrompt, SdkMcpResource, SdkMcpServer, SdkMcpTool, SdkPluginConfig, ServerToolResultBlock, ServerToolUseBlock, SessionEndHookInput, SessionMessage, SessionStartHookInput, SessionStartHookSpecificOutput, SessionStateChangedMessage, SessionStore, SetupHookInput, SetupHookSpecificOutput, StatusMessage, StopFailureHookInput, StopHookInput, StreamEvent, SubagentStartHookInput, SubagentStartHookSpecificOutput, SubagentStopHookInput, SubprocessCLITransport, SyncHookJSONOutput, SystemMessage, SystemPromptFile, SystemPromptPreset, TaskBudget, TaskCompletedHookInput, TaskCreatedHookInput, TaskNotificationMessage, TaskProgressMessage, TaskStartedMessage, TaskUsage, TeammateIdleHookInput, TextBlock, ThinkingBlock, ThinkingConfigAdaptive, ThinkingConfigDisabled, ThinkingConfigEnabled, ToolPermissionContext, ToolProgressMessage, ToolResultBlock, ToolUseBlock, ToolUseSummaryMessage, ToolsPreset, TranscriptMirrorBatcher, Transport, Type, UnknownBlock, UserMessage, UserPromptSubmitHookInput, UserPromptSubmitHookSpecificOutput, WorktreeCreateHookInput, WorktreeRemoveHookInput

Constant Summary collapse

PERMISSION_MODES =

Type constants for permission modes

%w[default acceptEdits plan bypassPermissions dontAsk auto].freeze
SETTING_SOURCES =

Type constants for setting sources

%w[user project local].freeze
EFFORT_LEVELS =

Effort levels for ‘ClaudeAgentOptions#effort`. The CLI (Claude Code 2.1.111+) accepts these values; the set of supported levels is model-dependent (e.g. `xhigh` is only supported on Opus 4.7 and falls back to `high` on Opus 4.6 / Sonnet 4.6). An Integer is also accepted and forwarded verbatim.

%w[low medium high xhigh max].freeze
PERMISSION_UPDATE_DESTINATIONS =

Type constants for permission update destinations

%w[userSettings projectSettings localSettings session].freeze
PERMISSION_BEHAVIORS =

Type constants for permission behaviors

%w[allow deny ask].freeze
HOOK_EVENTS =

Type constants for hook events

%w[
  PreToolUse
  PostToolUse
  PostToolUseFailure
  Notification
  UserPromptSubmit
  SessionStart
  SessionEnd
  Stop
  StopFailure
  SubagentStart
  SubagentStop
  PreCompact
  PostCompact
  PermissionRequest
  PermissionDenied
  Setup
  TeammateIdle
  TaskCreated
  TaskCompleted
  Elicitation
  ElicitationResult
  ConfigChange
  WorktreeCreate
  WorktreeRemove
  InstructionsLoaded
  CwdChanged
  FileChanged
].freeze
ASSISTANT_MESSAGE_ERRORS =

Type constants for assistant message errors

%w[authentication_failed billing_error rate_limit invalid_request server_error max_output_tokens unknown].freeze
SDK_BETAS =

Type constants for SDK beta features Available beta features that can be enabled via the betas option

%w[context-1m-2025-08-07].freeze
TASK_NOTIFICATION_STATUSES =

Task lifecycle notification statuses

%w[completed failed stopped].freeze
RATE_LIMIT_STATUSES =

Type constants for rate limit statuses

%w[allowed allowed_warning rejected].freeze
RATE_LIMIT_TYPES =

Type constants for rate limit types

%w[five_hour seven_day seven_day_opus seven_day_sonnet overage].freeze
THINKING_DISPLAY_VALUES =

Thinking configuration types

‘display` controls how thinking content appears in responses. Valid values are `“summarized”` (plaintext summary) and `“omitted”` (empty thinking field, signature only). Defaults are model-dependent: Opus 4.6/Sonnet 4.6 default to `“summarized”`; Opus 4.7 and Mythos Preview default to `“omitted”`. Pass `display: “summarized”` explicitly on Opus 4.7 to get visible thinking text. Not supported with `ThinkingConfigDisabled`.

%w[summarized omitted].freeze
MCP_SERVER_CONNECTION_STATUSES =

MCP server connection status values

%w[connected failed needs-auth pending disabled].freeze
VERSION =
'0.17.0'
SESSION_STORE_FLUSH_MODES =

Controls when transcript-mirror entries are flushed to a SessionStore.

  • “batched” (default): buffer entries and flush once per turn (on the ‘result` message) or when the pending buffer exceeds 500 entries / 1 MiB.

  • “eager”: trigger a background flush after every transcript_mirror frame so SessionStore#append sees entries in near real time.

%w[batched eager].freeze

Class Method Summary collapse

Class Method Details

.configurationConfiguration

Get the configuration object

Returns:



58
59
60
# File 'lib/claude_agent_sdk/configuration.rb', line 58

def configuration
  @configuration ||= Configuration.new
end

.configure {|Configuration| ... } ⇒ Object

Configure the SDK with default options

Examples:

Set default env and other options

ClaudeAgentSDK.configure do |config|
  config.default_options = {
    env: { 'API_KEY' => 'xxx' },
    permission_mode: 'bypassPermissions'
  }
end

Yields:



51
52
53
# File 'lib/claude_agent_sdk/configuration.rb', line 51

def configure
  yield(configuration)
end

.create_prompt(name:, description: nil, arguments: nil, &generator) ⇒ SdkMcpPrompt

Helper function to create a prompt definition

Examples:

Simple prompt

prompt = create_prompt(
  name: 'code_review',
  description: 'Review code for best practices'
) do |args|
  {
    messages: [
      {
        role: 'user',
        content: {
          type: 'text',
          text: 'Please review this code for best practices and suggest improvements.'
        }
      }
    ]
  }
end

Prompt with arguments

prompt = create_prompt(
  name: 'git_commit',
  description: 'Generate a git commit message',
  arguments: [
    { name: 'changes', description: 'Description of changes', required: true }
  ]
) do |args|
  {
    messages: [
      {
        role: 'user',
        content: {
          type: 'text',
          text: "Generate a concise git commit message for: #{args[:changes]}"
        }
      }
    ]
  }
end

Parameters:

  • name (String)

    Unique identifier for the prompt

  • description (String, nil) (defaults to: nil)

    Optional description

  • arguments (Array<Hash>, nil) (defaults to: nil)

    Optional argument definitions

  • generator (Proc)

    Block that generates prompt messages

Returns:

Raises:

  • (ArgumentError)


531
532
533
534
535
536
537
538
539
540
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 531

def self.create_prompt(name:, description: nil, arguments: nil, &generator)
  raise ArgumentError, 'Block required for prompt generator' unless generator

  SdkMcpPrompt.new(
    name: name,
    description: description,
    arguments: arguments,
    generator: generator
  )
end

.create_resource(uri:, name:, description: nil, mime_type: nil, &reader) ⇒ SdkMcpResource

Helper function to create a resource definition

Examples:

File resource

resource = create_resource(
  uri: 'file:///config/settings.json',
  name: 'Application Settings',
  description: 'Current application configuration',
  mime_type: 'application/json'
) do
  content = File.read('/path/to/settings.json')
  {
    contents: [{
      uri: 'file:///config/settings.json',
      mimeType: 'application/json',
      text: content
    }]
  }
end

Database resource

resource = create_resource(
  uri: 'db://users/count',
  name: 'User Count',
  description: 'Total number of registered users'
) do
  count = User.count
  {
    contents: [{
      uri: 'db://users/count',
      mimeType: 'text/plain',
      text: count.to_s
    }]
  }
end

Parameters:

  • uri (String)

    Unique identifier for the resource (e.g., “file:///path/to/file”)

  • name (String)

    Human-readable name

  • description (String, nil) (defaults to: nil)

    Optional description

  • mime_type (String, nil) (defaults to: nil)

    Optional MIME type (e.g., “text/plain”, “application/json”)

  • reader (Proc)

    Block that returns the resource content

Returns:

Raises:

  • (ArgumentError)


473
474
475
476
477
478
479
480
481
482
483
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 473

def self.create_resource(uri:, name:, description: nil, mime_type: nil, &reader)
  raise ArgumentError, 'Block required for resource reader' unless reader

  SdkMcpResource.new(
    uri: uri,
    name: name,
    description: description,
    mime_type: mime_type,
    reader: reader
  )
end

.create_sdk_mcp_server(name:, version: '1.0.0', tools: [], resources: [], prompts: []) ⇒ Hash

Create an SDK MCP server

Examples:

Simple calculator server

add_tool = ClaudeAgentSDK.create_tool('add', 'Add numbers', { a: :number, b: :number }) do |args|
  { content: [{ type: 'text', text: "Sum: #{args[:a] + args[:b]}" }] }
end

calculator = ClaudeAgentSDK.create_sdk_mcp_server(
  name: 'calculator',
  version: '2.0.0',
  tools: [add_tool]
)

options = ClaudeAgentOptions.new(
  mcp_servers: { calc: calculator },
  allowed_tools: ['mcp__calc__add']
)

Server with resources and prompts

config_resource = ClaudeAgentSDK.create_resource(
  uri: 'config://app',
  name: 'App Config'
) { { contents: [{ uri: 'config://app', text: 'config data' }] } }

review_prompt = ClaudeAgentSDK.create_prompt(
  name: 'review',
  description: 'Code review'
) { { messages: [{ role: 'user', content: { type: 'text', text: 'Review this' } }] } }

server = ClaudeAgentSDK.create_sdk_mcp_server(
  name: 'dev-tools',
  resources: [config_resource],
  prompts: [review_prompt]
)

Parameters:

  • name (String)

    Unique identifier for the server

  • version (String) (defaults to: '1.0.0')

    Server version (default: ‘1.0.0’)

  • tools (Array<SdkMcpTool>) (defaults to: [])

    List of tool definitions

  • resources (Array<SdkMcpResource>) (defaults to: [])

    List of resource definitions

  • prompts (Array<SdkMcpPrompt>) (defaults to: [])

    List of prompt definitions

Returns:

  • (Hash)

    MCP server configuration for ClaudeAgentOptions



583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 583

def self.create_sdk_mcp_server(name:, version: '1.0.0', tools: [], resources: [], prompts: [])
  server = SdkMcpServer.new(
    name: name,
    version: version,
    tools: tools,
    resources: resources,
    prompts: prompts
  )

  # Return configuration for ClaudeAgentOptions
  {
    type: 'sdk',
    name: name,
    instance: server
  }
end

.create_tool(name, description, input_schema, annotations: nil, meta: nil, &handler) ⇒ SdkMcpTool

Helper function to create a tool definition

Examples:

Simple tool

tool = create_tool('greet', 'Greet a user', { name: :string }) do |args|
  { content: [{ type: 'text', text: "Hello, #{args[:name]}!" }] }
end

Tool with multiple parameters

tool = create_tool('add', 'Add two numbers', { a: :number, b: :number }) do |args|
  result = args[:a] + args[:b]
  { content: [{ type: 'text', text: "Result: #{result}" }] }
end

Tool with error handling

tool = create_tool('divide', 'Divide numbers', { a: :number, b: :number }) do |args|
  if args[:b] == 0
    { content: [{ type: 'text', text: 'Error: Division by zero' }], is_error: true }
  else
    result = args[:a] / args[:b]
    { content: [{ type: 'text', text: "Result: #{result}" }] }
  end
end

Parameters:

  • name (String)

    Unique identifier for the tool

  • description (String)

    Human-readable description

  • input_schema (Hash)

    Schema defining input parameters

  • handler (Proc)

    Block that implements the tool logic

Returns:

Raises:

  • (ArgumentError)


412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 412

def self.create_tool(name, description, input_schema, annotations: nil, meta: nil, &handler)
  raise ArgumentError, 'Block required for tool handler' unless handler

  # Auto-populate _meta with maxResultSizeChars from annotations if present
  resolved_meta = meta
  if resolved_meta.nil? && annotations
    max_chars = annotations[:maxResultSizeChars] || annotations['maxResultSizeChars']
    resolved_meta = { 'anthropic/maxResultSizeChars' => max_chars } if max_chars
  end

  SdkMcpTool.new(
    name: name,
    description: description,
    input_schema: input_schema,
    handler: handler,
    annotations: annotations,
    meta: resolved_meta
  )
end

.deep_symbolize_keys(obj) ⇒ Object

Recursively convert all hash keys to symbols



7
8
9
10
11
12
13
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 7

def self.deep_symbolize_keys(obj)
  case obj
  when Hash then obj.transform_keys(&:to_sym).transform_values { |v| deep_symbolize_keys(v) }
  when Array then obj.map { |v| deep_symbolize_keys(v) }
  else obj
  end
end

.default_optionsHash

Get merged default options for use with ClaudeAgentOptions

Returns:

  • (Hash)

    Default options hash



70
71
72
# File 'lib/claude_agent_sdk/configuration.rb', line 70

def default_options
  configuration.default_options || {}
end

.delete_session(session_id:, directory: nil) ⇒ Object

Delete a session by removing its JSONL file (hard delete).

Parameters:

  • session_id (String)

    UUID of the session to delete

  • directory (String, nil) (defaults to: nil)

    Project directory path



133
134
135
# File 'lib/claude_agent_sdk.rb', line 133

def self.delete_session(session_id:, directory: nil)
  SessionMutations.delete_session(session_id: session_id, directory: directory)
end

.delete_session_via_store(session_store:, session_id:, directory: nil) ⇒ Object

Delete a session from a SessionStore (store-backed counterpart to delete_session). No-op when the store does not implement #delete (WORM/append-only backends).

Raises:

  • (ArgumentError)

    if session_id is invalid



223
224
225
226
# File 'lib/claude_agent_sdk.rb', line 223

def self.delete_session_via_store(session_store:, session_id:, directory: nil)
  SessionMutations.delete_session_via_store(session_store: session_store, session_id: session_id,
                                            directory: directory)
end

.flexible_fetch(hash, camel_key, snake_key) ⇒ Object

Look up a value in a hash that may use symbol or string keys in camelCase or snake_case. Returns the first non-nil value found, preserving false as a meaningful value.



49
50
51
52
53
54
55
# File 'lib/claude_agent_sdk.rb', line 49

def self.flexible_fetch(hash, camel_key, snake_key)
  val = hash[camel_key.to_sym]
  val = hash[camel_key.to_s] if val.nil?
  val = hash[snake_key.to_sym] if val.nil?
  val = hash[snake_key.to_s] if val.nil?
  val
end

.fold_session_summary(prev, key, entries) ⇒ Hash

Fold a batch of appended transcript entries into a running session summary. SessionStore adapters call this inside #append to maintain a summary sidecar incrementally (see SessionStore#list_session_summaries).

Parameters:

  • prev (Hash, nil)

    previous summary entry for this key

  • key (Hash)

    the SessionKey (string keys)

  • entries (Array<Hash>)

    newly appended transcript entries

Returns:

  • (Hash)

    the updated summary entry



164
165
166
# File 'lib/claude_agent_sdk.rb', line 164

def self.fold_session_summary(prev, key, entries)
  SessionSummary.fold_session_summary(prev, key, entries)
end

.fork_session(session_id:, directory: nil, up_to_message_id: nil, title: nil) ⇒ ForkSessionResult

Fork a session into a new branch with fresh UUIDs.

Parameters:

  • session_id (String)

    UUID of the session to fork

  • directory (String, nil) (defaults to: nil)

    Project directory path

  • up_to_message_id (String, nil) (defaults to: nil)

    Truncate the fork at this message UUID

  • title (String, nil) (defaults to: nil)

    Custom title for the fork

Returns:



143
144
145
146
# File 'lib/claude_agent_sdk.rb', line 143

def self.fork_session(session_id:, directory: nil, up_to_message_id: nil, title: nil)
  SessionMutations.fork_session(session_id: session_id, directory: directory,
                                up_to_message_id: up_to_message_id, title: title)
end

.fork_session_via_store(session_store:, session_id:, directory: nil, up_to_message_id: nil, title: nil) ⇒ ForkSessionResult

Fork a session in a SessionStore into a new branch with fresh UUIDs (store-backed counterpart to fork_session).

Returns:

Raises:

  • (ArgumentError)

    if session_id/up_to_message_id is invalid or there are no messages

  • (Errno::ENOENT)

    if the source session is not found in the store



233
234
235
236
# File 'lib/claude_agent_sdk.rb', line 233

def self.fork_session_via_store(session_store:, session_id:, directory: nil, up_to_message_id: nil, title: nil)
  SessionMutations.fork_session_via_store(session_store: session_store, session_id: session_id,
                                          directory: directory, up_to_message_id: up_to_message_id, title: title)
end

.get_session_info(session_id:, directory: nil) ⇒ SDKSessionInfo?

Read metadata for a single session by ID (no full directory scan)

Parameters:

  • session_id (String)

    UUID of the session to look up

  • directory (String, nil) (defaults to: nil)

    Project directory path

Returns:



100
101
102
# File 'lib/claude_agent_sdk.rb', line 100

def self.get_session_info(session_id:, directory: nil)
  Sessions.get_session_info(session_id: session_id, directory: directory)
end

.get_session_info_from_store(session_store:, session_id:, directory: nil) ⇒ SDKSessionInfo?

Read metadata for a single session from a SessionStore.

Returns:



177
178
179
# File 'lib/claude_agent_sdk.rb', line 177

def self.get_session_info_from_store(session_store:, session_id:, directory: nil)
  Sessions.get_session_info_from_store(session_store: session_store, session_id: session_id, directory: directory)
end

.get_session_messages(session_id:, directory: nil, limit: nil, offset: 0) ⇒ Array<SessionMessage>

Get messages from a session transcript

Parameters:

  • session_id (String)

    The session UUID

  • directory (String, nil) (defaults to: nil)

    Working directory to search in

  • limit (Integer, nil) (defaults to: nil)

    Maximum number of messages

  • offset (Integer) (defaults to: 0)

    Number of messages to skip

Returns:



110
111
112
# File 'lib/claude_agent_sdk.rb', line 110

def self.get_session_messages(session_id:, directory: nil, limit: nil, offset: 0)
  Sessions.get_session_messages(session_id: session_id, directory: directory, limit: limit, offset: offset)
end

.get_session_messages_from_store(session_store:, session_id:, directory: nil, limit: nil, offset: 0) ⇒ Array<SessionMessage>

Read a session’s conversation messages from a SessionStore.

Returns:



183
184
185
186
# File 'lib/claude_agent_sdk.rb', line 183

def self.get_session_messages_from_store(session_store:, session_id:, directory: nil, limit: nil, offset: 0)
  Sessions.get_session_messages_from_store(session_store: session_store, session_id: session_id,
                                           directory: directory, limit: limit, offset: offset)
end

.get_subagent_messages_from_store(session_store:, session_id:, agent_id:, directory: nil, limit: nil, offset: 0) ⇒ Array<SessionMessage>

Read a subagent’s conversation messages from a SessionStore.

Returns:



196
197
198
199
200
# File 'lib/claude_agent_sdk.rb', line 196

def self.get_subagent_messages_from_store(session_store:, session_id:, agent_id:, directory: nil, limit: nil,
                                          offset: 0)
  Sessions.get_subagent_messages_from_store(session_store: session_store, session_id: session_id,
                                            agent_id: agent_id, directory: directory, limit: limit, offset: offset)
end

.import_session_to_store(session_id:, session_store:, directory: nil, include_subagents: true, batch_size: TranscriptMirrorBatcher::MAX_PENDING_ENTRIES) ⇒ Object

Replay a local on-disk session transcript into a SessionStore (migration / gap-backfill). Keys under the on-disk project dir so the imported session is resumable via session_store + resume from the original cwd.

Raises:

  • (ArgumentError)

    if session_id is not a valid UUID

  • (Errno::ENOENT)

    if the session JSONL cannot be found



243
244
245
246
247
# File 'lib/claude_agent_sdk.rb', line 243

def self.import_session_to_store(session_id:, session_store:, directory: nil, include_subagents: true,
                                 batch_size: TranscriptMirrorBatcher::MAX_PENDING_ENTRIES)
  Sessions.import_session_to_store(session_id: session_id, session_store: session_store, directory: directory,
                                   include_subagents: include_subagents, batch_size: batch_size)
end

.list_sessions(directory: nil, limit: nil, offset: 0, include_worktrees: true) {|Message| ... } ⇒ Enumerator, Array<SDKSessionInfo>

Query Claude Code for one-shot or unidirectional streaming interactions

This function is ideal for simple, stateless queries where you don’t need bidirectional communication or conversation management.

List sessions for a directory (or all sessions)

Examples:

Simple query

ClaudeAgentSDK.query(prompt: "What is 2 + 2?") do |message|
  puts message
end

With options

options = ClaudeAgentSDK::ClaudeAgentOptions.new(
  allowed_tools: ['Read', 'Bash'],
  permission_mode: 'acceptEdits'
)
ClaudeAgentSDK.query(prompt: "Create a hello.rb file", options: options) do |msg|
  puts msg.text if msg.is_a?(ClaudeAgentSDK::AssistantMessage)
end

Streaming input

messages = Streaming.from_array(['Hello', 'What is 2+2?', 'Thanks!'])
ClaudeAgentSDK.query(prompt: messages) do |message|
  puts message
end

Parameters:

  • prompt (String, Enumerator)

    The prompt to send to Claude, or an Enumerator for streaming input

  • options (ClaudeAgentOptions)

    Optional configuration

  • directory (String, nil) (defaults to: nil)

    Working directory to list sessions for

  • limit (Integer, nil) (defaults to: nil)

    Maximum number of sessions to return

  • offset (Integer) (defaults to: 0)

    Number of sessions to skip (for pagination)

  • include_worktrees (Boolean) (defaults to: true)

    Whether to include git worktree sessions

Yields:

  • (Message)

    Each message from the conversation

Returns:

  • (Enumerator)

    if no block given

  • (Array<SDKSessionInfo>)

    Sessions sorted by last_modified descending



92
93
94
# File 'lib/claude_agent_sdk.rb', line 92

def self.list_sessions(directory: nil, limit: nil, offset: 0, include_worktrees: true)
  Sessions.list_sessions(directory: directory, limit: limit, offset: offset, include_worktrees: include_worktrees)
end

.list_sessions_from_store(session_store:, directory: nil, limit: nil, offset: 0) ⇒ Array<SDKSessionInfo>

List sessions from a SessionStore (store-backed counterpart to list_sessions).

Parameters:

Returns:



171
172
173
# File 'lib/claude_agent_sdk.rb', line 171

def self.list_sessions_from_store(session_store:, directory: nil, limit: nil, offset: 0)
  Sessions.list_sessions_from_store(session_store: session_store, directory: directory, limit: limit, offset: offset)
end

.list_subagents_from_store(session_store:, session_id:, directory: nil) ⇒ Array<String>

List subagent IDs for a session from a SessionStore (requires list_subkeys).

Returns:

  • (Array<String>)


190
191
192
# File 'lib/claude_agent_sdk.rb', line 190

def self.list_subagents_from_store(session_store:, session_id:, directory: nil)
  Sessions.list_subagents_from_store(session_store: session_store, session_id: session_id, directory: directory)
end

.notify_observers(observers, method, *args) ⇒ Object

Safely call a method on each observer, suppressing any errors. Each observer is invoked through FiberBoundary so that user code runs on a plain thread (no Fiber scheduler) even when called from inside the SDK’s Async reactor.



39
40
41
42
43
44
45
# File 'lib/claude_agent_sdk.rb', line 39

def self.notify_observers(observers, method, *args)
  observers.each do |obs|
    FiberBoundary.invoke { obs.send(method, *args) }
  rescue StandardError
    nil
  end
end

.project_key_for_directory(directory = nil) ⇒ String

Derive the SessionStore project_key for a directory (default: cwd). Matches the CLI’s project-directory naming so keys align between local-disk and store-mirrored transcripts.

Parameters:

  • directory (String, Pathname, nil) (defaults to: nil)

    Directory to key (nil = cwd)

Returns:

  • (String)

    The project key



153
154
155
# File 'lib/claude_agent_sdk.rb', line 153

def self.project_key_for_directory(directory = nil)
  Sessions.project_key_for_directory(directory)
end

.query(prompt:, options: nil, &block) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/claude_agent_sdk.rb', line 249

def self.query(prompt:, options: nil, &block)
  return enum_for(:query, prompt: prompt, options: options) unless block

  options ||= ClaudeAgentOptions.new

  configured_options = options
  if options.can_use_tool
    if prompt.is_a?(String)
      raise ArgumentError,
            'can_use_tool callback requires streaming mode. Please provide prompt as an Enumerator instead of a String.'
    end

    raise ArgumentError, 'can_use_tool callback cannot be used with permission_prompt_tool_name' if options.permission_prompt_tool_name

    configured_options = options.dup_with(permission_prompt_tool_name: 'stdio')
  end

  # Fail fast on invalid session_store combinations before spawning the CLI.
  SessionStores.validate_session_store_options(configured_options)

  # Resolve callable observers into fresh instances (thread-safe for global defaults)
  resolved_observers = ClaudeAgentSDK.resolve_observers(configured_options.observers)

  Async do
    materialized = nil
    transport = nil
    query_handler = nil
    begin
      # Resume-from-store: when a session_store is set and resume/continue is
      # requested, load the session into a temp CLAUDE_CONFIG_DIR and repoint
      # options at it (env + --resume) BEFORE spawning. Returns options
      # unchanged when no materialization applies. query() always uses the
      # default subprocess transport, so no custom-transport gate is needed.
      materialized = SessionResume.materialize_resume_session(configured_options)
      configured_options = SessionResume.apply_materialized_options(configured_options, materialized) if materialized

      # Always use streaming mode with control protocol (matches Python SDK).
      # This sends agents via initialize request instead of CLI args,
      # avoiding OS ARG_MAX limits.
      transport = SubprocessCLITransport.new(configured_options)
      transport.connect

      # Extract SDK MCP servers
      sdk_mcp_servers = {}
      if configured_options.mcp_servers.is_a?(Hash)
        configured_options.mcp_servers.each do |name, config|
          sdk_mcp_servers[name] = config[:instance] if config.is_a?(Hash) && config[:type] == 'sdk'
        end
      end

      hooks = nil
      if configured_options.hooks
        hooks = {}
        configured_options.hooks.each do |event, matchers|
          next if matchers.nil? || matchers.empty?

          entries = []
          matchers.each do |matcher|
            config = {
              matcher: matcher.matcher,
              hooks: matcher.hooks
            }
            config[:timeout] = matcher.timeout if matcher.timeout
            entries << config
          end
          hooks[event.to_s] = entries unless entries.empty?
        end
        hooks = nil if hooks.empty?
      end

      # Create Query handler for control protocol
      query_handler = Query.new(
        transport: transport,
        is_streaming_mode: true,
        can_use_tool: configured_options.can_use_tool,
        hooks: hooks,
        agents: configured_options.agents,
        sdk_mcp_servers: sdk_mcp_servers
      )

      # Mirror transcripts to the session_store, if configured. Installed
      # before #start so the read loop captures transcript_mirror frames.
      if configured_options.session_store
        query_handler.set_transcript_mirror_batcher(
          SessionResume.build_mirror_batcher(
            store: configured_options.session_store,
            env: configured_options.env,
            on_error: ->(key, message) { query_handler.report_mirror_error(key, message) },
            eager: configured_options.session_store_flush.to_s == 'eager'
          )
        )
      end

      # Start reading messages in background
      query_handler.start

      # Initialize the control protocol (sends agents)
      query_handler.initialize_protocol

      # Send prompt(s) as user messages, then close stdin
      if prompt.is_a?(String)
        ClaudeAgentSDK.notify_observers(resolved_observers, :on_user_prompt, prompt)
        message = {
          type: 'user',
          message: { role: 'user', content: prompt },
          parent_tool_use_id: nil,
          session_id: ''
        }
        transport.write(JSON.generate(message) + "\n")
        query_handler.wait_for_result_and_end_input
      elsif prompt.is_a?(Enumerator) || prompt.respond_to?(:each)
        Async do
          query_handler.stream_input(prompt)
        end
      end

      # Read and yield messages from the query handler (filters out control messages).
      # User block is invoked through FiberBoundary so ActiveRecord / PG calls
      # inside it don't see the async gem's Fiber scheduler.
      query_handler.receive_messages do |data|
        message = MessageParser.parse(data)
        if message
          ClaudeAgentSDK.notify_observers(resolved_observers, :on_message, message)
          FiberBoundary.invoke { block.call(message) }
        end
      end
    ensure
      ClaudeAgentSDK.notify_observers(resolved_observers, :on_close)
      # query_handler.close stops the background read task and closes the
      # transport (flushing the mirror batcher first). Fall back to a bare
      # transport close when the handler was never built.
      begin
        if query_handler
          query_handler.close
        elsif transport
          transport.close
        end
      ensure
        # Remove the materialized resume temp dir (which holds a redacted
        # .credentials.json copy) AFTER the subprocess has exited, even when
        # close itself raises.
        materialized&.cleanup
      end
    end
  end.wait
end

.rename_session(session_id:, title:, directory: nil) ⇒ Object

Rename a session by appending a custom-title entry

Parameters:

  • session_id (String)

    UUID of the session to rename

  • title (String)

    New session title

  • directory (String, nil) (defaults to: nil)

    Project directory path



118
119
120
# File 'lib/claude_agent_sdk.rb', line 118

def self.rename_session(session_id:, title:, directory: nil)
  SessionMutations.rename_session(session_id: session_id, title: title, directory: directory)
end

.rename_session_via_store(session_store:, session_id:, title:, directory: nil) ⇒ Object

Rename a session in a SessionStore (store-backed counterpart to rename_session). Appends a custom-title entry carrying a fresh uuid + timestamp via SessionStore#append.

Raises:

  • (ArgumentError)

    if session_id is invalid or title is empty



206
207
208
209
# File 'lib/claude_agent_sdk.rb', line 206

def self.rename_session_via_store(session_store:, session_id:, title:, directory: nil)
  SessionMutations.rename_session_via_store(session_store: session_store, session_id: session_id,
                                            title: title, directory: directory)
end

.reset_configurationObject

Reset configuration to defaults (useful for testing)



63
64
65
# File 'lib/claude_agent_sdk/configuration.rb', line 63

def reset_configuration
  @configuration = Configuration.new
end

.resolve_observers(observers) ⇒ Object

Resolve observers array: callables (Proc/lambda) are invoked to produce a fresh instance per query/session (thread-safe); plain objects are used as-is. Array() guards against nil (e.g., when observers: nil is passed explicitly).



29
30
31
32
33
# File 'lib/claude_agent_sdk.rb', line 29

def self.resolve_observers(observers)
  Array(observers).map do |obs|
    obs.respond_to?(:call) ? obs.call : obs
  end
end

.tag_session(session_id:, tag:, directory: nil) ⇒ Object

Tag a session. Pass nil to clear the tag.

Parameters:

  • session_id (String)

    UUID of the session to tag

  • tag (String, nil)

    Tag string, or nil to clear

  • directory (String, nil) (defaults to: nil)

    Project directory path



126
127
128
# File 'lib/claude_agent_sdk.rb', line 126

def self.tag_session(session_id:, tag:, directory: nil)
  SessionMutations.tag_session(session_id: session_id, tag: tag, directory: directory)
end

.tag_session_via_store(session_store:, session_id:, tag:, directory: nil) ⇒ Object

Tag a session in a SessionStore (store-backed counterpart to tag_session). Pass nil to clear the tag.

Raises:

  • (ArgumentError)

    if session_id is invalid or tag is empty after sanitization



214
215
216
217
# File 'lib/claude_agent_sdk.rb', line 214

def self.tag_session_via_store(session_store:, session_id:, tag:, directory: nil)
  SessionMutations.tag_session_via_store(session_store: session_store, session_id: session_id,
                                         tag: tag, directory: directory)
end