Class: ClaudeAgentSDK::MessageParser

Inherits:
Object
  • Object
show all
Defined in:
lib/claude_agent_sdk/message_parser.rb

Overview

Parse message from CLI output into typed Message objects

Constant Summary collapse

SYSTEM_MESSAGE_CLASSES =

Typed SystemMessage subclasses inherit from ‘Type` and accept the raw CLI hash directly — camelCase and snake_case keys are normalized by the base class, and the full hash is captured as `#data`.

{
  'init' => InitMessage,
  'compact_boundary' => CompactBoundaryMessage,
  'status' => StatusMessage,
  'api_retry' => APIRetryMessage,
  'local_command_output' => LocalCommandOutputMessage,
  'mirror_error' => MirrorErrorMessage,
  'hook_started' => HookStartedMessage,
  'hook_progress' => HookProgressMessage,
  'hook_response' => HookResponseMessage,
  'session_state_changed' => SessionStateChangedMessage,
  'files_persisted' => FilesPersistedMessage,
  'elicitation_complete' => ElicitationCompleteMessage,
  'task_started' => TaskStartedMessage,
  'task_progress' => TaskProgressMessage,
  'task_notification' => TaskNotificationMessage
}.freeze

Class Method Summary collapse

Class Method Details

.parse(data) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/claude_agent_sdk/message_parser.rb', line 9

def self.parse(data)
  raise MessageParseError.new("Invalid message data type", data: data) unless data.is_a?(Hash)

  message_type = data[:type]
  raise MessageParseError.new("Message missing 'type' field", data: data) unless message_type

  case message_type
  when 'user'
    parse_user_message(data)
  when 'assistant'
    parse_assistant_message(data)
  when 'system'
    parse_system_message(data)
  when 'result'
    parse_result_message(data)
  when 'stream_event'
    parse_stream_event(data)
  when 'rate_limit_event'
    parse_rate_limit_event(data)
  when 'tool_progress'
    parse_tool_progress_message(data)
  when 'auth_status'
    parse_auth_status_message(data)
  when 'tool_use_summary'
    parse_tool_use_summary_message(data)
  when 'prompt_suggestion'
    parse_prompt_suggestion_message(data)
  end
  # Forward-compatible: returns nil for unrecognized message types so
  # newer CLI versions don't crash older SDK versions.
rescue KeyError => e
  raise MessageParseError.new("Missing required field: #{e.message}", data: data)
end

.parse_assistant_message(data) ⇒ Object

Raises:



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/claude_agent_sdk/message_parser.rb', line 63

def self.parse_assistant_message(data)
  content = data.dig(:message, :content)
  raise MessageParseError.new("Missing content in assistant message", data: data) unless content

  content_blocks = content.map { |block| parse_content_block(block) }
  AssistantMessage.new(
    content: content_blocks,
    model: data.dig(:message, :model),
    parent_tool_use_id: data[:parent_tool_use_id],
    error: data[:error], # authentication_failed, billing_error, rate_limit, invalid_request, server_error, unknown
    usage: data.dig(:message, :usage),
    message_id: data.dig(:message, :id),
    stop_reason: data.dig(:message, :stop_reason),
    session_id: data[:session_id],
    uuid: data[:uuid]
  )
end

.parse_auth_status_message(data) ⇒ Object



123
124
125
# File 'lib/claude_agent_sdk/message_parser.rb', line 123

def self.parse_auth_status_message(data)
  AuthStatusMessage.new(data)
end

.parse_content_block(block) ⇒ Object

Accepts blocks with either symbol or string keys — live CLI messages arrive symbol-keyed (parsed via ‘symbolize_names: true`), session transcripts arrive string-keyed (parsed via `symbolize_names: false`). Uses a nil-aware fallback so `is_error: false` survives.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/claude_agent_sdk/message_parser.rb', line 139

def self.parse_content_block(block)
  get = lambda do |key|
    v = block[key]
    v.nil? ? block[key.to_s] : v
  end
  case get.call(:type)
  when 'text'
    TextBlock.new(text: get.call(:text))
  when 'thinking'
    ThinkingBlock.new(thinking: get.call(:thinking), signature: get.call(:signature))
  when 'tool_use'
    ToolUseBlock.new(id: get.call(:id), name: get.call(:name), input: get.call(:input))
  when 'tool_result'
    ToolResultBlock.new(
      tool_use_id: get.call(:tool_use_id),
      content: get.call(:content),
      is_error: get.call(:is_error)
    )
  when 'server_tool_use'
    ServerToolUseBlock.new(id: get.call(:id), name: get.call(:name), input: get.call(:input))
  when 'server_tool_result'
    ServerToolResultBlock.new(
      tool_use_id: get.call(:tool_use_id),
      content: get.call(:content),
      is_error: get.call(:is_error)
    )
  else
    # Forward-compatible: preserve unrecognized content block types (e.g., "document", "image")
    # so newer CLI versions don't crash older SDK versions.
    UnknownBlock.new(type: get.call(:type), data: block)
  end
end

.parse_prompt_suggestion_message(data) ⇒ Object



131
132
133
# File 'lib/claude_agent_sdk/message_parser.rb', line 131

def self.parse_prompt_suggestion_message(data)
  PromptSuggestionMessage.new(data)
end

.parse_rate_limit_event(data) ⇒ Object



115
116
117
# File 'lib/claude_agent_sdk/message_parser.rb', line 115

def self.parse_rate_limit_event(data)
  RateLimitEvent.new(data.merge(raw_data: data))
end

.parse_result_message(data) ⇒ Object



107
108
109
# File 'lib/claude_agent_sdk/message_parser.rb', line 107

def self.parse_result_message(data)
  ResultMessage.new(data)
end

.parse_stream_event(data) ⇒ Object



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

def self.parse_stream_event(data)
  StreamEvent.new(data)
end

.parse_system_message(data) ⇒ Object



102
103
104
105
# File 'lib/claude_agent_sdk/message_parser.rb', line 102

def self.parse_system_message(data)
  klass = SYSTEM_MESSAGE_CLASSES[data[:subtype]] || SystemMessage
  klass.new(data)
end

.parse_tool_progress_message(data) ⇒ Object



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

def self.parse_tool_progress_message(data)
  ToolProgressMessage.new(data)
end

.parse_tool_use_summary_message(data) ⇒ Object



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

def self.parse_tool_use_summary_message(data)
  ToolUseSummaryMessage.new(data)
end

.parse_user_message(data) ⇒ Object

Raises:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/claude_agent_sdk/message_parser.rb', line 43

def self.parse_user_message(data)
  parent_tool_use_id = data[:parent_tool_use_id]
  uuid = data[:uuid] # UUID for rewind support
  tool_use_result = data[:tool_use_result]
  message_data = data[:message]
  raise MessageParseError.new("Missing message field in user message", data: data) unless message_data

  content = message_data[:content]
  raise MessageParseError.new("Missing content in user message", data: data) unless content

  if content.is_a?(Array)
    content_blocks = content.map { |block| parse_content_block(block) }
    UserMessage.new(content: content_blocks, uuid: uuid, parent_tool_use_id: parent_tool_use_id,
                    tool_use_result: tool_use_result)
  else
    UserMessage.new(content: content, uuid: uuid, parent_tool_use_id: parent_tool_use_id,
                    tool_use_result: tool_use_result)
  end
end