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, UnknownHookInput, 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.18.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
-
.configuration ⇒ Configuration
Get the configuration object.
-
.configure {|Configuration| ... } ⇒ Object
Configure the SDK with default options.
-
.create_prompt(name:, description: nil, arguments: nil, &generator) ⇒ SdkMcpPrompt
Helper function to create a prompt definition.
-
.create_resource(uri:, name:, description: nil, mime_type: nil, &reader) ⇒ SdkMcpResource
Helper function to create a resource definition.
-
.create_sdk_mcp_server(name:, version: '1.0.0', tools: [], resources: [], prompts: []) ⇒ Hash
Create an SDK MCP server.
-
.create_tool(name, description, input_schema, annotations: nil, meta: nil, &handler) ⇒ SdkMcpTool
Helper function to create a tool definition.
-
.deep_normalize_schema(obj) ⇒ Object
Like deep_symbolize_keys, but also converts Symbol VALUES to strings so a prebuilt schema written with symbols ({ type: :object, … }) emits clean wire-format JSON Schema.
-
.deep_symbolize_keys(obj) ⇒ Object
Recursively convert all hash keys to symbols.
-
.default_options ⇒ Hash
Get merged default options for use with ClaudeAgentOptions.
-
.delete_session(session_id:, directory: nil) ⇒ Object
Delete a session by removing its JSONL file (hard delete).
-
.delete_session_via_store(session_store:, session_id:, directory: nil) ⇒ Object
Delete a session from a SessionStore (store-backed counterpart to delete_session).
-
.extract_user_prompt_text(message) ⇒ Object
Extract the user-visible prompt text from a streamed input item, or nil when there is none (non-user messages, tool_result-only content, …).
-
.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.
-
.fold_session_summary(prev, key, entries) ⇒ Hash
Fold a batch of appended transcript entries into a running session summary.
-
.fork_session(session_id:, directory: nil, up_to_message_id: nil, title: nil) ⇒ ForkSessionResult
Fork a session into a new branch with fresh UUIDs.
-
.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).
-
.get_session_info(session_id:, directory: nil) ⇒ SDKSessionInfo?
Read metadata for a single session by ID (no full directory scan).
-
.get_session_info_from_store(session_store:, session_id:, directory: nil) ⇒ SDKSessionInfo?
Read metadata for a single session from a SessionStore.
-
.get_session_messages(session_id:, directory: nil, limit: nil, offset: 0) ⇒ Array<SessionMessage>
Get messages from a session transcript.
-
.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.
-
.get_subagent_messages(session_id:, agent_id:, directory: nil, limit: nil, offset: 0) ⇒ Array<SessionMessage>
Read a subagent’s conversation messages from local disk.
-
.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.
-
.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).
-
.list_sessions(directory: nil, limit: nil, offset: 0, include_worktrees: true) ⇒ Array<SDKSessionInfo>
List sessions for a directory (or all sessions).
-
.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).
-
.list_subagents(session_id:, directory: nil) ⇒ Array<String>
List subagent IDs recorded for a session on local disk.
-
.list_subagents_from_store(session_store:, session_id:, directory: nil) ⇒ Array<String>
List subagent IDs for a session from a SessionStore (requires list_subkeys).
-
.normalize_tool_schema(schema) ⇒ Object
Single source of truth for tool input schemas: prebuilt schemas are normalized (symbol keys, string values); simple { name: :type } hashes become a full JSON Schema with every param required (string keys).
-
.notify_observers(observers, method, *args) ⇒ Object
Safely call a method on each observer, suppressing any errors.
-
.observing_prompt_stream(prompt, observers) ⇒ Object
Wrap a streaming-input enumerable so observers get on_user_prompt for each user message before it is written to stdin.
-
.prebuilt_json_schema?(schema) ⇒ Boolean
A prebuilt JSON Schema is detected by type == ‘object’ (String or Symbol) AND a Hash properties value.
-
.project_key_for_directory(directory = nil) ⇒ String
Derive the SessionStore
project_keyfor a directory (default: cwd). -
.prompt_text_from_content(content) ⇒ Object
Text from a user-message content payload: the string itself, or the newline-joined non-empty top-level text blocks.
-
.query(prompt:, options: nil, transport: nil) {|Message| ... } ⇒ Enumerator
Query Claude Code for one-shot or unidirectional streaming interactions.
-
.rename_session(session_id:, title:, directory: nil) ⇒ Object
Rename a session by appending a custom-title entry.
-
.rename_session_via_store(session_store:, session_id:, title:, directory: nil) ⇒ Object
Rename a session in a SessionStore (store-backed counterpart to rename_session).
-
.reset_configuration ⇒ Object
Reset configuration to defaults (useful for testing).
-
.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.
- .ruby_type_to_json_schema(type) ⇒ Object
-
.tag_session(session_id:, tag:, directory: nil) ⇒ Object
Tag a session.
-
.tag_session_via_store(session_store:, session_id:, tag:, directory: nil) ⇒ Object
Tag a session in a SessionStore (store-backed counterpart to tag_session).
Class Method Details
.configuration ⇒ Configuration
Get the configuration object
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
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
582 583 584 585 586 587 588 589 590 591 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 582 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
524 525 526 527 528 529 530 531 532 533 534 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 524 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
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 634 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
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 463 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 = if .nil? && annotations max_chars = annotations[:maxResultSizeChars] || annotations['maxResultSizeChars'] = { 'anthropic/maxResultSizeChars' => max_chars } if max_chars end SdkMcpTool.new( name: name, description: description, input_schema: input_schema, handler: handler, annotations: annotations, meta: ) end |
.deep_normalize_schema(obj) ⇒ Object
Like deep_symbolize_keys, but also converts Symbol VALUES to strings so a prebuilt schema written with symbols ({ type: :object, … }) emits clean wire-format JSON Schema.
18 19 20 21 22 23 24 25 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 18 def self.deep_normalize_schema(obj) case obj when Hash then obj.transform_keys(&:to_sym).transform_values { |v| deep_normalize_schema(v) } when Array then obj.map { |v| deep_normalize_schema(v) } when Symbol then obj.to_s else obj end 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_options ⇒ Hash
Get merged default options for use with ClaudeAgentOptions
70 71 72 |
# File 'lib/claude_agent_sdk/configuration.rb', line 70 def configuration. || {} end |
.delete_session(session_id:, directory: nil) ⇒ Object
Delete a session by removing its JSONL file (hard delete).
192 193 194 |
# File 'lib/claude_agent_sdk.rb', line 192 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).
282 283 284 285 |
# File 'lib/claude_agent_sdk.rb', line 282 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 |
.extract_user_prompt_text(message) ⇒ Object
Extract the user-visible prompt text from a streamed input item, or nil when there is none (non-user messages, tool_result-only content, …). Only Hash and JSON-string items are inspected; arbitrary objects written via to_s are never notified.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/claude_agent_sdk.rb', line 54 def self.extract_user_prompt_text() data = case when Hash then when String # Cheap prefilter: skip the full parse for items that cannot be # user messages (e.g. multi-MB tool_result frames) — parsing # would block the reactor fiber for the duration. False # positives just cost one parse; correctness is unchanged. return nil unless .include?('user') begin JSON.parse() rescue JSON::ParserError nil end end return nil unless data.is_a?(Hash) return nil unless (data[:type] || data['type']) == 'user' inner = data[:message] || data['message'] return nil unless inner.is_a?(Hash) prompt_text_from_content(inner[:content] || inner['content']) 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.
117 118 119 120 121 122 123 |
# File 'lib/claude_agent_sdk.rb', line 117 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).
223 224 225 |
# File 'lib/claude_agent_sdk.rb', line 223 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.
202 203 204 205 |
# File 'lib/claude_agent_sdk.rb', line 202 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: , 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).
292 293 294 295 |
# File 'lib/claude_agent_sdk.rb', line 292 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: , title: title) end |
.get_session_info(session_id:, directory: nil) ⇒ SDKSessionInfo?
Read metadata for a single session by ID (no full directory scan)
139 140 141 |
# File 'lib/claude_agent_sdk.rb', line 139 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.
236 237 238 |
# File 'lib/claude_agent_sdk.rb', line 236 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
149 150 151 |
# File 'lib/claude_agent_sdk.rb', line 149 def self.(session_id:, directory: nil, limit: nil, offset: 0) Sessions.(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.
242 243 244 245 |
# File 'lib/claude_agent_sdk.rb', line 242 def self.(session_store:, session_id:, directory: nil, limit: nil, offset: 0) Sessions.(session_store: session_store, session_id: session_id, directory: directory, limit: limit, offset: offset) end |
.get_subagent_messages(session_id:, agent_id:, directory: nil, limit: nil, offset: 0) ⇒ Array<SessionMessage>
Read a subagent’s conversation messages from local disk
168 169 170 171 |
# File 'lib/claude_agent_sdk.rb', line 168 def self.(session_id:, agent_id:, directory: nil, limit: nil, offset: 0) Sessions.(session_id: session_id, agent_id: agent_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.
255 256 257 258 259 |
# File 'lib/claude_agent_sdk.rb', line 255 def self.(session_store:, session_id:, agent_id:, directory: nil, limit: nil, offset: 0) Sessions.(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.
302 303 304 305 306 |
# File 'lib/claude_agent_sdk.rb', line 302 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) ⇒ Array<SDKSessionInfo>
List sessions for a directory (or all sessions)
131 132 133 |
# File 'lib/claude_agent_sdk.rb', line 131 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).
230 231 232 |
# File 'lib/claude_agent_sdk.rb', line 230 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(session_id:, directory: nil) ⇒ Array<String>
List subagent IDs recorded for a session on local disk
157 158 159 |
# File 'lib/claude_agent_sdk.rb', line 157 def self.list_subagents(session_id:, directory: nil) Sessions.list_subagents(session_id: session_id, directory: directory) 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).
249 250 251 |
# File 'lib/claude_agent_sdk.rb', line 249 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 |
.normalize_tool_schema(schema) ⇒ Object
Single source of truth for tool input schemas: prebuilt schemas are normalized (symbol keys, string values); simple { name: :type } hashes become a full JSON Schema with every param required (string keys).
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 43 def self.normalize_tool_schema(schema) return deep_normalize_schema(schema) if prebuilt_json_schema?(schema) if schema.is_a?(Hash) properties = schema.to_h { |param, type| [param.to_sym, ruby_type_to_json_schema(type)] } result = { type: 'object', properties: properties } result[:required] = properties.keys.map(&:to_s) unless properties.empty? return result end { type: 'object', properties: {} } 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 46 47 48 |
# 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, ScriptError # ScriptError too: NotImplementedError < ScriptError (not # StandardError), and a stubbed observer must never mask the original # error being notified or abort connect/teardown cleanup. nil end end |
.observing_prompt_stream(prompt, observers) ⇒ Object
Wrap a streaming-input enumerable so observers get on_user_prompt for each user message before it is written to stdin. Identity when no observers are configured.
103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/claude_agent_sdk.rb', line 103 def self.observing_prompt_stream(prompt, observers) return prompt if observers.empty? Enumerator.new do |yielder| prompt.each do || text = extract_user_prompt_text() notify_observers(observers, :on_user_prompt, text) if text yielder << end end end |
.prebuilt_json_schema?(schema) ⇒ Boolean
A prebuilt JSON Schema is detected by type == ‘object’ (String or Symbol) AND a Hash properties value. Deliberately stricter than Python’s rule: Ruby’s simple-schema idiom uses Symbols as type VALUES, so { type: :string, properties: :string } is a legal simple schema with params literally named type/properties.
32 33 34 35 36 37 38 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 32 def self.prebuilt_json_schema?(schema) return false unless schema.is_a?(Hash) type_val = schema[:type] || schema['type'] props_val = schema[:properties] || schema['properties'] (type_val.is_a?(String) || type_val.is_a?(Symbol)) && type_val.to_s == 'object' && props_val.is_a?(Hash) 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.
212 213 214 |
# File 'lib/claude_agent_sdk.rb', line 212 def self.project_key_for_directory(directory = nil) Sessions.project_key_for_directory(directory) end |
.prompt_text_from_content(content) ⇒ Object
Text from a user-message content payload: the string itself, or the newline-joined non-empty top-level text blocks. Returns nil (never ”) when there is no extractable text — on_user_prompt(”) would latch OTelObserver’s first-prompt buffer while never setting the attribute, permanently suppressing later real prompts.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/claude_agent_sdk.rb', line 84 def self.prompt_text_from_content(content) case content when String content.empty? ? nil : content when Array texts = content.filter_map do |block| next unless block.is_a?(Hash) next unless (block[:type] || block['type']) == 'text' text = block[:text] || block['text'] text unless text.to_s.empty? end texts.empty? ? nil : texts.join("\n") end end |
.query(prompt:, options: nil, transport: nil) {|Message| ... } ⇒ Enumerator
An attempted #next may still spawn the CLI subprocess before failing and leaves the query unusable.
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.
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 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 |
# File 'lib/claude_agent_sdk.rb', line 343 def self.query(prompt:, options: nil, transport: nil, &block) return enum_for(:query, prompt: prompt, options: , transport: transport) unless block ||= ClaudeAgentOptions.new = if .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 . = .dup_with(permission_prompt_tool_name: 'stdio') end # Fail fast on invalid session_store combinations before spawning the CLI. SessionStores.() # Resolve callable observers into fresh instances (thread-safe for global defaults) resolved_observers = ClaudeAgentSDK.resolve_observers(.observers) raise ArgumentError, 'transport must respond to #connect (see ClaudeAgentSDK::Transport)' if transport && !transport.respond_to?(:connect) Async do materialized = nil query_handler = nil begin if transport.nil? # 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. Skipped # entirely for an injected transport — the materialized # env/--resume only apply to the CLI subprocess (Python parity: # client.py skips materialization when a transport is supplied). materialized = SessionResume.materialize_resume_session() = SessionResume.(, 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() end # Deliberate deviation from Python: the ensure below also closes an # injected transport whose #connect raised (Python leaves it # unclosed); Transport#close must be idempotent. transport.connect # Extract SDK MCP servers sdk_mcp_servers = {} if .mcp_servers.is_a?(Hash) .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 .hooks hooks = {} .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: .can_use_tool, hooks: hooks, agents: .agents, sdk_mcp_servers: sdk_mcp_servers, skills: .skills ) # Mirror transcripts to the session_store, if configured. Installed # before #start so the read loop captures transcript_mirror frames. if .session_store query_handler.set_transcript_mirror_batcher( SessionResume.build_mirror_batcher( store: .session_store, env: .env, on_error: ->(key, ) { query_handler.report_mirror_error(key, ) }, eager: .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) = { type: 'user', message: { role: 'user', content: prompt }, parent_tool_use_id: nil, session_id: '' } transport.write(JSON.generate() + "\n") # Background-spawn so messages stream to the user block while stdin # close waits (without timeout) for the first result; a synchronous # call would defer all delivery until the turn completes (mirrors # Python's query.spawn_task(query.wait_for_result_and_end_input())). query_handler.spawn_task { query_handler.wait_for_result_and_end_input } elsif prompt.is_a?(Enumerator) || prompt.respond_to?(:each) # Tracked on the Query so close() stops it; an untracked Async task # here kept the root reactor alive forever when the read loop died # while the user enumerator was still blocked (matches Python's # query.spawn_task(query.stream_input(prompt))). observed_prompt = ClaudeAgentSDK.observing_prompt_stream(prompt, resolved_observers) query_handler.spawn_task { query_handler.stream_input(observed_prompt) } 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. do |data| = MessageParser.parse(data) next unless ClaudeAgentSDK.notify_observers(resolved_observers, :on_message, ) signal = FiberBoundary.invoke_iteration(block, ) break signal.value if signal.is_a?(FiberBoundary::Break) end rescue StandardError => e # One notify point for every error surfacing from query() — transport # connect, initialize, stream errors re-raised from the message queue, # parse errors, and user-block errors. StandardError only: Async::Stop # is cancellation, not an error. Bare raise preserves the backtrace; # the ensure below still fires on_close after on_error. ClaudeAgentSDK.notify_observers(resolved_observers, :on_error, e) raise 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
177 178 179 |
# File 'lib/claude_agent_sdk.rb', line 177 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.
265 266 267 268 |
# File 'lib/claude_agent_sdk.rb', line 265 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_configuration ⇒ Object
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 |
.ruby_type_to_json_schema(type) ⇒ Object
56 57 58 59 60 61 62 63 64 |
# File 'lib/claude_agent_sdk/sdk_mcp_server.rb', line 56 def self.ruby_type_to_json_schema(type) case type when :string, String then { type: 'string' } when :integer, Integer then { type: 'integer' } when :float, Float, :number then { type: 'number' } when :boolean, TrueClass, FalseClass then { type: 'boolean' } else { type: 'string' } # Default fallback end end |
.tag_session(session_id:, tag:, directory: nil) ⇒ Object
Tag a session. Pass nil to clear the tag.
185 186 187 |
# File 'lib/claude_agent_sdk.rb', line 185 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.
273 274 275 276 |
# File 'lib/claude_agent_sdk.rb', line 273 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 |