Class: ClaudeSwarm::ClaudeCodeExecutor

Inherits:
BaseExecutor show all
Defined in:
lib/claude_swarm/claude_code_executor.rb

Instance Attribute Summary

Attributes inherited from BaseExecutor

#instance_info, #last_response, #logger, #session_id, #session_json_path, #session_path, #working_directory

Instance Method Summary collapse

Methods inherited from BaseExecutor

#has_session?, #initialize, #reset_session

Constructor Details

This class inherits a constructor from ClaudeSwarm::BaseExecutor

Instance Method Details

#execute(prompt, options = {}) ⇒ Object



5
6
7
8
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/claude_swarm/claude_code_executor.rb', line 5

def execute(prompt, options = {})
  # Log the request
  log_request(prompt)

  # Build SDK options
  sdk_options = build_sdk_options(prompt, options)

  # Variables to collect output
  all_messages = []
  result_response = nil

  # Execute with streaming
  begin
    ClaudeSDK.query(prompt, options: sdk_options) do |message|
      # Convert message to hash for logging
      message_hash = message_to_hash(message)
      all_messages << message_hash

      # Log streaming event BEFORE we modify anything
      log_streaming_event(message_hash)

      # Process specific message types
      case message
      when ClaudeSDK::Messages::System
        # Capture session_id from system init
        if message.subtype == "init" && message.data.is_a?(Hash)
          # For init messages, session_id is in the data hash
          session_id = message.data[:session_id] || message.data["session_id"]

          if session_id
            @session_id = session_id
            write_instance_state
          end
        end
      when ClaudeSDK::Messages::Assistant
        # Assistant messages only contain content blocks
        # No need to track for result extraction - result comes from Result message
      when ClaudeSDK::Messages::Result
        # Validate that we have actual result content
        if message.result.nil? || (message.result.is_a?(String) && message.result.strip.empty?)
          raise ExecutionError, "Claude SDK returned an empty result. The agent completed execution but provided no response content."
        end

        # Build result response in expected format
        result_response = {
          "type" => "result",
          "subtype" => message.subtype || "success",
          "cost_usd" => message.total_cost_usd,
          "is_error" => message.is_error || false,
          "duration_ms" => message.duration_ms,
          "result" => message.result, # Result text is directly in message.result
          "total_cost" => message.total_cost_usd,
          "session_id" => message.session_id,
        }
      end
    end
  rescue StandardError => e
    logger.error { "Execution error for #{@instance_name}: #{e.class} - #{e.message}" }
    logger.error { "Backtrace: #{e.backtrace.join("\n")}" }
    raise ExecutionError, "Claude Code execution failed: #{e.message}"
  end

  # Ensure we got a result
  raise ParseError, "No result found in SDK response" unless result_response

  # Write session JSON log
  all_messages.each do |msg|
    append_to_session_json(msg)
  end

  result_response
rescue StandardError => e
  logger.error { "Unexpected error for #{@instance_name}: #{e.class} - #{e.message}" }
  logger.error { "Backtrace: #{e.backtrace.join("\n")}" }
  raise
end