All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Replaced all 63
Data.definetypes with plain class inheritance fromImmutableRecordbase class Messagemodule is nowincluded instead ofprepended into message and content block types- RBS signatures now use real supertype inheritance (
< ImmutableRecord) - Replaced 23 per-event
*Inputclasses (PreToolUseInput,StopInput, etc.) with a single dynamicHookInputclass that usesmethod_missingfor event-specific fields - Renamed
HookMatchertoHookbase class;HookRegistry.registernow generates typed subclasses (e.g.,PreToolUseHook,SessionStartHook) and DSL methods from a single CLI event name HookRegistryis nowEnumerableand persists as the data structure inOptions#hooks— no more compile-to-hash step- Moved hooks code into
hooks/directory (hook.rb,hook_context.rb,hook_input.rb,hook_registry.rb)
HookRegistry.register(cli_event)— registers a new hook event, generating both a*Hooksubclass and a DSL method by conventionHookRegistry.wrap(input)— normalizesHookRegistry,Hash, ornilinto aHookRegistryHookRegistry.from_hash(hash)— builds a registry from a raw hooks hashHook#to_config— builds CLI config entry and registers callbacks, replacing inline logic inbuild_hooks_configHook#dispatch— wraps raw CLI data inHookInputandHookContextbefore invoking callbacksHook#event_name— CLI event name derived from the class name by convention (e.g.,PreToolUseHook→"PreToolUse")Hook::CallbackEntry— nestedImmutableRecordpairing a hook with a callback in the registry
HOOK_EVENTSconstant (no longer needed — the CLI is the source of truth)BaseHookInputclass anddefine_inputDSL (replaced byHookInput)HookRegistry::EVENT_MAPconstant (replaced byHookRegistry.registerandKNOWN_EVENTS)HookMatcherclass (renamed toHook)HookRegistry#to_hooks_hash(registry is now used directly)
StopFailurehook event withStopFailureInputtype (error,error_details,last_assistant_messagefields) for handling API error-triggered stops (TypeScript SDK v0.2.80 parity)on_stop_failureDSL method onHookRegistryfor the new event
APIRetryMessagesystem message type withattempt,max_retries,retry_delay_ms,error_status, anderrorfields (TypeScript SDK v0.2.77 parity)titleanddisplay_namefields onToolPermissionContextfor richer permission prompt context (TypeScript SDK v0.2.77 parity)allow_readandallow_managed_read_paths_onlyfields onSandboxFilesystemConfigfor re-allowing reads within deny regions (TypeScript SDK v0.2.77 parity)
AbortError#partial_turncarries the in-progressTurnResultwhen a turn is aborted, eliminating the need for separate extraction paths on cancellationStreamEventtyped accessors:delta_text,delta_type,thinking_text,content_indexfor convenient access without hash-digging into raw event dataPermissionRequest#display_labeland#summarydelegate toToolUseBlockformatting, removing the need to construct dummy blocks for displayAbortController#reset!andAbortSignal#reset!allow reuse across turns;Conversation#sayauto-resets at turn startPostCompacthook event withPostCompactInputtype (triggerandcompact_summaryfields) (TypeScript SDK v0.2.76 parity)cancel_async_messagecontrol request to drop queued user messages by UUID (TypeScript SDK v0.2.76 parity)get_settingscontrol request to read effective merged settings (TypeScript SDK v0.2.76 parity)fork_sessionstandalone function to branch a session from a specific point with UUID remapping (TypeScript SDK v0.2.76 parity)ForkSessionResultdata type returned byfork_session
TurnResult#textnow falls back to accumulated streaming deltas when noAssistantMessagetext is available (e.g., on abort mid-stream)Conversation#sayresets the tool tracker at the start of each turn instead of the end, so tracker data survives abort and remains accessible until the nextsay()callClient#receive_turncatchesAbortErrorand re-raises with the partialTurnResultattached
- Replace busy-wait polling (
Queue#pop(true)+sleep 0.01) inControlProtocol::Messaging#each_messagewith blockingQueue#popand:donesentinel, eliminating CPU waste and up-to-10ms per-message latency - Fix
Conversation#partition_kwargsmisroutingon_elicitation(and any futureon_*Options attributes) as event callbacks instead of forwarding to Options; now uses explicit allowlist derived fromEventHandler::EVENTS - Remove global
ENV["CLAUDE_CODE_ENTRYPOINT"]mutation fromClient#connectandClaudeAgent.query; the subprocess already receives this viaOptions#to_env - Log stderr callback errors instead of silently swallowing them in
Transport::Subprocess
- Ollama smoke test profile (
rake test_smoke,bin/test-smoke) for fast local testing against local LLMs SmokeTestCasebase class with Ollama availability check and configurableSMOKE_MODELenv var
- Restructured integration test suite: removed 92 misplaced unit tests, consolidated 52 CLI tests into 16 scenario tests across 5 files
- Integration tests now use scenario-based structure (
test_*_scenarios.rb) that exercises multiple assertions per CLI process spawn - Split
content_blocks.rb(352 lines) intocontent_blocks/directory with 8 focused files + barrel file - Split
types.rb(300 lines) intotypes/directory with 5 domain-grouped files + barrel file - Split
messages.rb(908 lines) intomessages/directory with 8 semantic-domain files + barrel file - Extracted
control_protocol.rb(1,010 lines) into 5 mixin modules (Primitives,Lifecycle,Messaging,Commands,RequestHandling) + shell class - Extracted
Client::Commandsmixin fromclient.rb(545 lines) for CLI command delegations - Extracted
Options::Serializermixin fromoptions.rb(344 lines) for CLI arg/env serialization - Split
session.rbintov2_session.rb(V2 Session API) andsession.rb(historical finder) - Split monolithic test files to mirror lib/ directory structure (
test/claude_agent/{messages,content_blocks,types,control_protocol}/) - Added RBS module declarations for
ControlProtocol,Client, andOptionsmixins
AgentInfotype withname,description, andmodelfields (TypeScript SDK v0.2.63 parity)supported_agentscontrol request onControlProtocolandClientfor querying available subagents (TypeScript SDK v0.2.63 parity)agentsfield onInitializationResultreturningAgentInfo[]fast_mode_statefield onResultMessage(TypeScript SDK v0.2.63 parity)ElicitationCompleteMessagefor MCP elicitation completion events (TypeScript SDK v0.2.63 parity)LocalCommandOutputMessagefor local command output events (TypeScript SDK v0.2.63 parity)on_elicitationoption for handling MCP elicitation requests via callback (TypeScript SDK v0.2.63 parity)ElicitationandElicitationResulthook events with input types (TypeScript SDK v0.2.63 parity)- Elicitation control protocol handling with callback support and default decline behavior
on_elicitation_completeandon_local_command_outputevent handler methodstagandcreated_atfields onSessionInfo(TypeScript SDK v0.2.75 parity)supports_auto_modefield onModelInfo(TypeScript SDK v0.2.75 parity)offsetparameter onlist_sessionsfor pagination (TypeScript SDK v0.2.75 parity)rename_session(session_id, title)for renaming session files (TypeScript SDK v0.2.74 parity)tag_session(session_id, tag)for tagging sessions with Unicode sanitization (TypeScript SDK v0.2.75 parity)get_session_info(session_id)for single-session lookup by UUID (TypeScript SDK v0.2.75 parity)agent_progress_summariesoption for periodic AI-generated progress summaries (TypeScript SDK v0.2.72 parity)promptfield onTaskStartedMessage(TypeScript SDK v0.2.75 parity)summaryfield onTaskProgressMessagefor AI-generated progress summaries (TypeScript SDK v0.2.72 parity)fast_mode_statefield onInitializationResult(TypeScript SDK v0.2.75 parity)- RBS signatures for all new types, fields, and methods
get_settingsfrom SPEC.md — not in TypeScript SDK public API (sdk.d.ts)
ResultMessage#uuidfield (TypeScript SDK parity — every other message type already had it)sdkMcpServersis now sent in the initialize request when SDK MCP servers are configured (TypeScript SDK parity)abort!now sendscontrol_cancel_requestfor each pending request before failing them locally (TypeScript SDK parity)
LiveToolActivity— mutable, real-time tool status tracker (:running→:done/:error) with elapsed time and delegation toToolUseBlockToolActivityTracker—Enumerablecollection that auto-wires toEventHandlerorClientvia.attach, withon_start/on_complete/on_progresscallbacks,on_changecatch-all,running/done/erroredfiltered views, andreset!Conversationacceptstrack_tools: trueto opt into live tool tracking viatool_trackeraccessor- Convention-based event dispatch — every message type now auto-fires a dedicated event based on
message.type(e.g.:assistant,:stream_event,:status,:tool_progress) EventHandler::EVENTS,TYPE_EVENTS,DECOMPOSED_EVENTS,META_EVENTSconstants enumerating all known eventson_assistant,on_user,on_stream_event,on_status,on_tool_progress,on_hook_response,on_auth_status,on_task_notification,on_hook_started,on_hook_progress,on_tool_use_summary,on_task_started,on_task_progress,on_rate_limit_event,on_prompt_suggestion,on_files_persistedconvenience methods onEventHandlerandClientConversationnow accepts anyon_*keyword argument as a callback (pattern-based, no longer limited to a hardcoded list)GenericMessagefires its dynamic type symbol, soon(:fancy_new_type)works for future/unknown CLI message typesTaskNotificationMessage#tool_use_idand#usagefields with newTaskUsagetype (TypeScript SDK parity)ToolProgressMessage#task_idfield (TypeScript SDK parity)StatusMessage#permission_modefield (TypeScript SDK parity)ModelInfo#supports_effort,#supported_effort_levels,#supports_adaptive_thinkingfields (TypeScript SDK parity)McpServerStatus#error,#config,#scope,#toolsfields (TypeScript SDK parity)StopInput#last_assistant_messagehook field (TypeScript SDK parity)SubagentStopInput#agent_typeand#last_assistant_messagehook fields (TypeScript SDK parity)'oauth'source inAPI_KEY_SOURCESconstant (TypeScript SDK parity)- RBS signatures for all new fields
EventHandler#handlenow fires three layers per message::message(catch-all) →message.type(type-based) → decomposed (:text,:thinking,:tool_use,:tool_result)Conversation::CONVERSATION_KEYSnow contains only infrastructure keys (client,options,on_permission); callback detection is pattern-based viaon_*prefix
Session.find(id, dir:)— find a past session by UUID, returnsSessionornilSession.all— list all past sessions asSessionobjectsSession.where(dir:, limit:)— query sessions with optional directory and limit filtersSession#messages— returns a chainable,EnumerableSessionMessageRelationfor reading transcript messagesSessionMessageRelation#where(limit:, offset:)— paginate messages with immutable chainingClaudeAgent.get_session_messages(session_id, dir:, limit:, offset:)— read session transcripts from disk (TypeScript SDK v0.2.59 parity)SessionMessagetype — message from a session transcript withtype,uuid,session_id,messageSessionPathsmodule — shared path infrastructure for session discovery (extracted fromListSessions)
- Renamed
Session(V2 multi-turn API) toV2Sessionto free theSessionname for the finder API unstable_v2_create_session,unstable_v2_resume_session, andunstable_v2_promptnow returnV2Session
Conversation— high-level wrapper managing the full conversation lifecycle with auto-connect, multi-turn history, callbacks, and tool activity timelineConversation#say(prompt)— send a message and receive aTurnResult, auto-connecting on first callConversation.open { |c| ... }— block form with automatic cleanupConversation.resume(session_id)— resume a previous conversationConversationcallbacks:on_text,on_stream,on_tool_use,on_tool_result,on_thinking,on_result,on_message,on_permissionConversation#total_cost,#session_id,#usage,#pending_permission,#pending_permissions?— convenience accessorsClaudeAgent.conversation(**kwargs)andClaudeAgent.resume_conversation(session_id)— module-level convenience methodsToolActivity— immutableData.definepairing aToolUseBlockwith itsToolResultBlock, turn index, and timing; delegatesname,display_label,summary,file_path,idto the tool use blockConversation#tool_activity— unified timeline of all tool executions across turns with duration trackingPermissionRequest— deferred permission promise that can be resolved from any thread viaallow!/deny!, withdefer!for hybrid callback/queue modePermissionQueue— thread-safe queue of pendingPermissionRequestobjects withpoll,pop(timeout:), anddrain!Client#permission_queue— returns the queue; always available on connected clientsClient#pending_permission— non-blocking poll for the next pending permission requestClient#pending_permissions?— check if any permission requests are waitingOptions#permission_queue— set totrueto enable queue-based permissions (auto-setspermission_prompt_tool_name: "stdio")ToolPermissionContext#request— access thePermissionRequestfrom within acan_use_toolcallback for hybrid defer modeControlProtocolnow supports three permission modes: synchronous callback, queue-based, and hybrid (callback with selectivedefer!)TurnResult— represents a complete agent turn, accumulating all messages between sending a prompt and receiving theResultMessageTurnResult#text,#thinking— concatenated text/thinking across all assistant messagesTurnResult#tool_uses,#tool_results,#tool_executions— tool use blocks, result blocks, and matched use/result pairsTurnResult#usage,#cost,#duration_ms,#session_id,#model,#success?,#error?— convenient accessors from the resultClient#send_and_receive(content)— send a message and receive the completeTurnResultin one callClient#receive_turn— likereceive_responsebut returns aTurnResultinstead of yielding raw messagesClaudeAgent.query_turn(prompt:)— one-shot query returning aTurnResultEventHandler— register typed event callbacks (:text,:thinking,:tool_use,:tool_result,:result,:message) instead of writingcasestatements over raw messagesClient#on(event, &block)and#on_text,#on_tool_use,#on_tool_result,#on_result, etc. — register persistent event handlers that fire duringreceive_turnandsend_and_receiveClaudeAgent.query_turnnow acceptsevents:parameter for standaloneEventHandleruseTaskProgressMessagefor real-time background task (subagent) progress reporting (TypeScript SDK v0.2.51 parity)mcp_authenticatecontrol request onControlProtocolandClientfor MCP server OAuth authentication (TypeScript SDK v0.2.52 parity)mcp_clear_authcontrol request onControlProtocolandClientfor clearing MCP server credentials (TypeScript SDK v0.2.52 parity)- RBS signatures for all new types and methods
CumulativeUsage— built-in cumulative usage tracking across conversation turns; automatically updated byClientasResultMessages are receivedClient#cumulative_usage— returns the usage accumulator withinput_tokens,output_tokens,cache_read_input_tokens,cache_creation_input_tokens,total_cost_usd,num_turns,duration_ms,duration_api_msGenericMessagetype — wraps unknown top-level message types instead of raisingMessageParseError, with dynamic field access via[]andmethod_missingGenericBlocktype — wraps unknown content block types instead of returning raw Hashes, with dynamic field access via[]andmethod_missingToolUseBlock#file_path— returns the file path for file-based tools (Read,Write,Edit,NotebookEdit), nil otherwiseToolUseBlock#display_label— one-line human-readable label (e.g."Read lib/foo.rb","Bash: git status","WebFetch: example.com")ToolUseBlock#summary(max:)— detailed summary with truncation (e.g."Write: /path.rb (3 lines)","Grep: pattern in /path (*.rb)")ServerToolUseBlock#file_path,#display_label,#summary(max:)— same interface with server context (e.g."server_name/tool_name")
BaseHookInput.define_input— declarative macro for generating hook input subclasses from a single declaration; replaces 18 hand-written classes with one-line definitionsMessageParsernow uses a registry pattern (MessageParser.register) instead of nestedcasestatements for message routing; adding a new message type is oneregistercallMessageParser#parseno longer raisesMessageParseErrorfor unknown message types; returnsGenericMessageinsteadMessageParser#parse_content_blockno longer returns raw Hashes for unknown content block types; returnsGenericBlockinstead- All parsed message hashes now use snake_case symbol keys throughout (e.g.
msg.usage[:input_tokens],event.files.first[:filename],event.event[:type]) MessageParser#parseapplies a singledeep_transform_keyspass that normalizes camelCase→snake_case and string→symbol for the entire hash treecan_use_toolcallbacks receive symbol-keyedinputhashes- Hook callbacks receive symbol-keyed
inputhashes - MCP tool handlers receive symbol-keyed
argumentshashes
MessageParser#fetch_dual— no longer needed now that keys are normalized at the entry point
WorktreeCreatehook event withWorktreeCreateInputclass (TypeScript SDK v0.2.50 parity)WorktreeRemovehook event withWorktreeRemoveInputclass (TypeScript SDK v0.2.50 parity)apply_flag_settingscontrol request onControlProtocolandClientfor merging settings into the flag layer (TypeScript SDK v0.2.50 parity)- RBS signatures for all new types
TaskStartedMessagefor background task start notifications (TypeScript SDK v0.2.43 parity)RateLimitEventmessage type for rate limit visibility (TypeScript SDK v0.2.44 parity)PromptSuggestionMessagetype andprompt_suggestionsoption for suggested follow-up prompts (TypeScript SDK v0.2.46 parity)ConfigChangehook event withConfigChangeInputclass (TypeScript SDK v0.2.47 parity)SandboxFilesystemConfigwithallow_write,deny_write,deny_readfields (TypeScript SDK v0.2.49 parity)- RBS signatures for all new types
stop_taskcontrol request andClient#stop_taskmethod for stopping running background tasks (TypeScript SDK parity)
thinkingoption for controlling extended thinking mode ({ type: "adaptive" },{ type: "enabled", budgetTokens: N },{ type: "disabled" })effortoption for response effort level ("low","medium","high","max")max_output_tokensassistant message error type
- Configurable logging via
ClaudeAgent.logger(module-level) andOptions#logger(per-query)NullLoggerdefault for zero overhead when logging is not configuredClaudeAgent.debug!convenience method for quick stderr debug logging- Backward-compatible with
CLAUDE_AGENT_DEBUGenv var - Log points across transport, control protocol, message parser, MCP server, query, and client
can_use_toolcallback now works without hooks or MCP servers configuredPermissionResultAllowandPermissionResultDeny(Data.definetypes) are now correctly recognized inhandle_can_use_toolinstead of silently falling through to allownormalize_hook_responsenow handlesData.definereturn types from hook callbacks
- Allow responses without explicit
updated_inputnow fall back to the original input (Python SDK parity)
- Always use streaming mode with control protocol initialization (Python/TypeScript SDK parity)
- Removes fragile conditional gate on hooks/MCP/can_use_tool
send_initializehandshake is now always sent in streaming mode
- Auto-set
permission_prompt_tool_nameto"stdio"whencan_use_toolis configured (Python/TypeScript SDK parity)
session_idoption for custom conversation UUIDs (--session-idCLI flag)TeammateIdlehook event withTeammateIdleInput(TypeScript SDK v0.2.33 parity)TaskCompletedhook event withTaskCompletedInput(TypeScript SDK v0.2.33 parity)
- Updated SPEC.md to reflect full TypeScript SDK v0.2.34 parity
descriptionfield onToolPermissionContext(TypeScript SDK v0.2.32 parity)allow_managed_domains_onlyfield onSandboxNetworkConfiginitialization_resultmethod onControlProtocolandClientwithInitializationResult,SlashCommand,ModelInfo, andAccountInfotypes
- Updated SPEC.md to reference TypeScript SDK v0.2.32 and Python SDK v0.1.30
debugoption for verbose debug logging (--debugCLI flag)debug_fileoption for writing debug logs to a file (--debug-fileCLI flag)stop_reasonfield onResultMessageindicating why the model stopped generating
- Updated SPEC.md to reference TypeScript SDK v0.2.31 and Python SDK v0.1.29
- MCP tool annotations support (
readOnlyHint,destructiveHint,idempotentHint,openWorldHint,title) onMCP::ToolandMCP.toolconvenience method (TypeScript SDK v0.2.27 parity) - README documentation for
UserMessageReplay,HookStartedMessage,HookProgressMessage,ToolUseSummaryMessage,FilesPersistedEventmessage types - README documentation for
mcp_reconnectandmcp_toggleclient methods - README documentation for MCP tool annotations
- Updated SPEC.md to reference TypeScript SDK v0.2.27 and Python SDK v0.1.26
FilesPersistedEventmessage type for file persistence confirmation (TypeScript SDK v0.2.25 parity)claudeai-proxyMCP server type support via Hash-based config passthrough
- Updated SPEC.md to reference TypeScript SDK v0.2.25 and Python SDK v0.1.25
HookStartedMessagefor hook lifecycle visibilityHookProgressMessagefor hook progress updatesToolUseSummaryMessagefor tool use summariesmcp_reconnectandmcp_togglecontrol methods for MCP server lifecycle managementhook_id,output, andoutcomefields onHookResponseMessage- Helper methods on
HookResponseMessage:success?,error?,cancelled?
- Updated release workflow to use trusted publisher
- Release script now uses
bundle install(Bundler 4.x compatibility)
- Release script prompts for RubyGems OTP upfront
- Release script creates GitHub releases automatically
- Simplified release script to match Kamal's approach
TaskNotificationMessagefor background task completion notificationsSetuphook event withSetupInputfor init/maintenance triggersskillsandmax_turnsfields inAgentDefinition(TypeScript SDK v0.2.12 parity)init,init_only,maintenanceoptions for running Setup hooksClaudeAgent.run_setupconvenience method for CI/CD pipelines- Hook-specific output fields documentation (
additionalContext,permissionDecision,updatedMCPToolOutput, etc.) - Document
settingsoption accepts JSON strings (for plansDirectory, etc.)
agentoption for specifying main thread agent name (TypeScript SDK v0.2.9 parity)modelfield inSessionStartInputhook input
- V2 Session API for multi-turn conversations (
unstable_v2_create_session,unstable_v2_resume_session,unstable_v2_prompt) Sessionclass for stateful conversation managementSessionOptionsdata type for V2 API configuration
Options#initializenow correctly handles nil values without overriding defaults
- MVP implementation of the Claude Agent SDK for Ruby