claude_agent 0.7.12 → 0.7.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/rules/testing.md +51 -10
  3. data/.claude/settings.json +1 -0
  4. data/ARCHITECTURE.md +237 -0
  5. data/CHANGELOG.md +53 -0
  6. data/CLAUDE.md +2 -0
  7. data/README.md +46 -1
  8. data/Rakefile +17 -0
  9. data/SPEC.md +214 -125
  10. data/lib/claude_agent/client/commands.rb +225 -0
  11. data/lib/claude_agent/client.rb +4 -206
  12. data/lib/claude_agent/content_blocks/generic_block.rb +39 -0
  13. data/lib/claude_agent/content_blocks/image_content_block.rb +54 -0
  14. data/lib/claude_agent/content_blocks/server_tool_result_block.rb +22 -0
  15. data/lib/claude_agent/content_blocks/server_tool_use_block.rb +48 -0
  16. data/lib/claude_agent/content_blocks/text_block.rb +19 -0
  17. data/lib/claude_agent/content_blocks/thinking_block.rb +19 -0
  18. data/lib/claude_agent/content_blocks/tool_result_block.rb +25 -0
  19. data/lib/claude_agent/content_blocks/tool_use_block.rb +134 -0
  20. data/lib/claude_agent/content_blocks.rb +8 -335
  21. data/lib/claude_agent/control_protocol/commands.rb +304 -0
  22. data/lib/claude_agent/control_protocol/lifecycle.rb +116 -0
  23. data/lib/claude_agent/control_protocol/messaging.rb +163 -0
  24. data/lib/claude_agent/control_protocol/primitives.rb +168 -0
  25. data/lib/claude_agent/control_protocol/request_handling.rb +231 -0
  26. data/lib/claude_agent/control_protocol.rb +50 -882
  27. data/lib/claude_agent/conversation.rb +8 -1
  28. data/lib/claude_agent/event_handler.rb +1 -0
  29. data/lib/claude_agent/get_session_info.rb +86 -0
  30. data/lib/claude_agent/hooks.rb +23 -2
  31. data/lib/claude_agent/list_sessions.rb +22 -13
  32. data/lib/claude_agent/message_parser.rb +26 -4
  33. data/lib/claude_agent/messages/conversation.rb +138 -0
  34. data/lib/claude_agent/messages/generic.rb +39 -0
  35. data/lib/claude_agent/messages/hook_lifecycle.rb +158 -0
  36. data/lib/claude_agent/messages/result.rb +80 -0
  37. data/lib/claude_agent/messages/streaming.rb +84 -0
  38. data/lib/claude_agent/messages/system.rb +67 -0
  39. data/lib/claude_agent/messages/task_lifecycle.rb +240 -0
  40. data/lib/claude_agent/messages/tool_lifecycle.rb +95 -0
  41. data/lib/claude_agent/messages.rb +11 -829
  42. data/lib/claude_agent/options/serializer.rb +194 -0
  43. data/lib/claude_agent/options.rb +11 -176
  44. data/lib/claude_agent/query.rb +0 -2
  45. data/lib/claude_agent/sandbox_settings.rb +3 -0
  46. data/lib/claude_agent/session.rb +0 -204
  47. data/lib/claude_agent/session_mutations.rb +148 -0
  48. data/lib/claude_agent/transport/subprocess.rb +2 -2
  49. data/lib/claude_agent/types/mcp.rb +30 -0
  50. data/lib/claude_agent/types/models.rb +146 -0
  51. data/lib/claude_agent/types/operations.rb +38 -0
  52. data/lib/claude_agent/types/sessions.rb +50 -0
  53. data/lib/claude_agent/types/tools.rb +32 -0
  54. data/lib/claude_agent/types.rb +6 -264
  55. data/lib/claude_agent/v2_session.rb +207 -0
  56. data/lib/claude_agent/version.rb +1 -1
  57. data/lib/claude_agent.rb +37 -3
  58. data/sig/claude_agent.rbs +144 -13
  59. metadata +33 -1
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Result message (final message with usage/cost info) - TypeScript SDK parity
5
+ #
6
+ # @example Success result
7
+ # msg = ResultMessage.new(
8
+ # subtype: "success",
9
+ # duration_ms: 1500,
10
+ # duration_api_ms: 1200,
11
+ # is_error: false,
12
+ # num_turns: 3,
13
+ # session_id: "session-abc",
14
+ # total_cost_usd: 0.05,
15
+ # usage: {input_tokens: 100, output_tokens: 50}
16
+ # )
17
+ #
18
+ # @example Error result
19
+ # msg = ResultMessage.new(
20
+ # subtype: "error_max_turns",
21
+ # errors: ["Maximum turns exceeded"],
22
+ # ...
23
+ # )
24
+ #
25
+ ResultMessage = Data.define(
26
+ :subtype,
27
+ :duration_ms,
28
+ :duration_api_ms,
29
+ :is_error,
30
+ :num_turns,
31
+ :session_id,
32
+ :uuid,
33
+ :total_cost_usd,
34
+ :usage,
35
+ :result,
36
+ :structured_output,
37
+ :errors, # Array<String> for error subtypes
38
+ :permission_denials, # Array<SDKPermissionDenial>
39
+ :model_usage, # Hash with per-model usage breakdown
40
+ :stop_reason, # Why the model stopped generating (TypeScript SDK parity)
41
+ :fast_mode_state # Fast mode state (TypeScript SDK v0.2.63 parity)
42
+ ) do
43
+ def initialize(
44
+ subtype:,
45
+ duration_ms:,
46
+ duration_api_ms:,
47
+ is_error:,
48
+ num_turns:,
49
+ session_id:,
50
+ uuid: nil,
51
+ total_cost_usd: nil,
52
+ usage: nil,
53
+ result: nil,
54
+ structured_output: nil,
55
+ errors: nil,
56
+ permission_denials: nil,
57
+ model_usage: nil,
58
+ stop_reason: nil,
59
+ fast_mode_state: nil
60
+ )
61
+ super
62
+ end
63
+
64
+ def type
65
+ :result
66
+ end
67
+
68
+ # Check if this was an error result
69
+ # @return [Boolean]
70
+ def error?
71
+ is_error
72
+ end
73
+
74
+ # Check if this was a successful result
75
+ # @return [Boolean]
76
+ def success?
77
+ !is_error
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Stream event (partial message during streaming)
5
+ #
6
+ # @example
7
+ # event = StreamEvent.new(
8
+ # uuid: "evt-123",
9
+ # session_id: "session-abc",
10
+ # event: {type: "content_block_delta", delta: {type: "text_delta", text: "Hello"}}
11
+ # )
12
+ #
13
+ StreamEvent = Data.define(:uuid, :session_id, :event, :parent_tool_use_id) do
14
+ def initialize(uuid:, session_id:, event:, parent_tool_use_id: nil)
15
+ super
16
+ end
17
+
18
+ def type
19
+ :stream_event
20
+ end
21
+
22
+ # Get the event type from the raw event
23
+ # @return [String, nil]
24
+ def event_type
25
+ event[:type]
26
+ end
27
+ end
28
+
29
+ # Rate limit event (TypeScript SDK v0.2.45 parity)
30
+ #
31
+ # Reports rate limit status and utilization information.
32
+ #
33
+ # @example
34
+ # msg = RateLimitEvent.new(
35
+ # uuid: "msg-123",
36
+ # session_id: "session-abc",
37
+ # rate_limit_info: {
38
+ # status: "allowed_warning",
39
+ # resetsAt: 1700000000,
40
+ # rateLimitType: "five_hour",
41
+ # utilization: 0.85,
42
+ # isUsingOverage: false,
43
+ # overageStatus: "available"
44
+ # }
45
+ # )
46
+ # msg.status # => "allowed_warning"
47
+ #
48
+ RateLimitEvent = Data.define(:rate_limit_info, :uuid, :session_id) do
49
+ def initialize(rate_limit_info:, uuid: nil, session_id: nil)
50
+ super
51
+ end
52
+
53
+ def type
54
+ :rate_limit_event
55
+ end
56
+
57
+ # Get the rate limit status
58
+ # @return [String, nil]
59
+ def status
60
+ rate_limit_info[:status]
61
+ end
62
+ end
63
+
64
+ # Prompt suggestion message (TypeScript SDK v0.2.47 parity)
65
+ #
66
+ # Contains a suggested prompt for the user.
67
+ #
68
+ # @example
69
+ # msg = PromptSuggestionMessage.new(
70
+ # uuid: "msg-123",
71
+ # session_id: "session-abc",
72
+ # suggestion: "Tell me about this project"
73
+ # )
74
+ #
75
+ PromptSuggestionMessage = Data.define(:uuid, :session_id, :suggestion) do
76
+ def initialize(uuid: nil, session_id: nil, suggestion:)
77
+ super
78
+ end
79
+
80
+ def type
81
+ :prompt_suggestion
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # System message (internal events)
5
+ #
6
+ # @example
7
+ # msg = SystemMessage.new(subtype: "init", data: {version: "2.0.0"})
8
+ #
9
+ SystemMessage = Data.define(:subtype, :data) do
10
+ def type
11
+ :system
12
+ end
13
+ end
14
+
15
+ # Compact boundary message (conversation compaction marker) - TypeScript SDK parity
16
+ #
17
+ # Sent when the conversation is compacted to reduce context size.
18
+ # Contains metadata about the compaction operation.
19
+ #
20
+ # @example
21
+ # msg = CompactBoundaryMessage.new(
22
+ # uuid: "msg-123",
23
+ # session_id: "session-abc",
24
+ # compact_metadata: { trigger: "auto", pre_tokens: 50000 }
25
+ # )
26
+ # msg.trigger # => "auto"
27
+ # msg.pre_tokens # => 50000
28
+ #
29
+ CompactBoundaryMessage = Data.define(:uuid, :session_id, :compact_metadata) do
30
+ def type
31
+ :compact_boundary
32
+ end
33
+
34
+ # Get the compaction trigger type
35
+ # @return [String] "manual" or "auto"
36
+ def trigger
37
+ compact_metadata[:trigger]
38
+ end
39
+
40
+ # Get the token count before compaction
41
+ # @return [Integer, nil]
42
+ def pre_tokens
43
+ compact_metadata[:pre_tokens]
44
+ end
45
+ end
46
+
47
+ # Status message (TypeScript SDK parity)
48
+ #
49
+ # Reports session status like 'compacting' during operations.
50
+ #
51
+ # @example
52
+ # msg = StatusMessage.new(
53
+ # uuid: "msg-123",
54
+ # session_id: "session-abc",
55
+ # status: "compacting"
56
+ # )
57
+ #
58
+ StatusMessage = Data.define(:uuid, :session_id, :status, :permission_mode) do
59
+ def initialize(uuid:, session_id:, status:, permission_mode: nil)
60
+ super
61
+ end
62
+
63
+ def type
64
+ :status
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Task started message (TypeScript SDK v0.2.45 parity)
5
+ #
6
+ # Sent when a new task (subagent) is started.
7
+ #
8
+ # @example
9
+ # msg = TaskStartedMessage.new(
10
+ # uuid: "msg-123",
11
+ # session_id: "session-abc",
12
+ # task_id: "task-456",
13
+ # tool_use_id: "tool-789",
14
+ # description: "Running tests",
15
+ # task_type: "bash"
16
+ # )
17
+ #
18
+ TaskStartedMessage = Data.define(
19
+ :uuid,
20
+ :session_id,
21
+ :task_id,
22
+ :tool_use_id,
23
+ :description,
24
+ :task_type,
25
+ :prompt
26
+ ) do
27
+ def initialize(
28
+ uuid:,
29
+ session_id:,
30
+ task_id:,
31
+ tool_use_id: nil,
32
+ description: nil,
33
+ task_type: nil,
34
+ prompt: nil
35
+ )
36
+ super
37
+ end
38
+
39
+ def type
40
+ :task_started
41
+ end
42
+ end
43
+
44
+ # Task progress message (TypeScript SDK v0.2.51 parity)
45
+ #
46
+ # Reports progress during background task (subagent) execution.
47
+ # Contains usage information and description of what the task is doing.
48
+ #
49
+ # @example
50
+ # msg = TaskProgressMessage.new(
51
+ # uuid: "msg-123",
52
+ # session_id: "session-abc",
53
+ # task_id: "task-456",
54
+ # description: "Searching codebase for patterns",
55
+ # usage: { total_tokens: 5000, tool_uses: 3, duration_ms: 2500 }
56
+ # )
57
+ #
58
+ TaskProgressMessage = Data.define(
59
+ :uuid, :session_id, :task_id, :tool_use_id,
60
+ :description, :usage, :last_tool_name, :summary
61
+ ) do
62
+ def initialize(
63
+ uuid:,
64
+ session_id:,
65
+ task_id:,
66
+ description:,
67
+ usage: nil,
68
+ tool_use_id: nil,
69
+ last_tool_name: nil,
70
+ summary: nil
71
+ )
72
+ super
73
+ end
74
+
75
+ def type
76
+ :task_progress
77
+ end
78
+ end
79
+
80
+ # Task notification message (TypeScript SDK parity)
81
+ #
82
+ # Sent when a background task completes, fails, or is stopped.
83
+ # Used for tracking async task execution status.
84
+ #
85
+ # @example
86
+ # msg = TaskNotificationMessage.new(
87
+ # uuid: "msg-123",
88
+ # session_id: "session-abc",
89
+ # task_id: "task-456",
90
+ # status: "completed",
91
+ # output_file: "/path/to/output.txt",
92
+ # summary: "Task completed successfully"
93
+ # )
94
+ # msg.completed? # => true
95
+ # msg.failed? # => false
96
+ #
97
+ # Status values:
98
+ # - "completed" - Task finished successfully
99
+ # - "failed" - Task encountered an error
100
+ # - "stopped" - Task was manually stopped
101
+ #
102
+ TaskNotificationMessage = Data.define(
103
+ :uuid,
104
+ :session_id,
105
+ :task_id,
106
+ :status,
107
+ :output_file,
108
+ :summary,
109
+ :tool_use_id,
110
+ :usage
111
+ ) do
112
+ def initialize(
113
+ uuid:,
114
+ session_id:,
115
+ task_id:,
116
+ status:,
117
+ output_file:,
118
+ summary:,
119
+ tool_use_id: nil,
120
+ usage: nil
121
+ )
122
+ super
123
+ end
124
+
125
+ def type
126
+ :task_notification
127
+ end
128
+
129
+ # Check if task completed successfully
130
+ # @return [Boolean]
131
+ def completed?
132
+ status == "completed"
133
+ end
134
+
135
+ # Check if task failed
136
+ # @return [Boolean]
137
+ def failed?
138
+ status == "failed"
139
+ end
140
+
141
+ # Check if task was stopped
142
+ # @return [Boolean]
143
+ def stopped?
144
+ status == "stopped"
145
+ end
146
+ end
147
+
148
+ # Files persisted event (TypeScript SDK v0.2.25 parity)
149
+ #
150
+ # Sent when files are persisted to storage during a session.
151
+ # Contains lists of successfully persisted files and any failures.
152
+ #
153
+ # @example
154
+ # msg = FilesPersistedEvent.new(
155
+ # uuid: "msg-123",
156
+ # session_id: "session-abc",
157
+ # files: [{ filename: "test.rb", file_id: "file-456" }],
158
+ # failed: [],
159
+ # processed_at: "2026-01-30T12:00:00Z"
160
+ # )
161
+ # msg.files.first[:filename] # => "test.rb"
162
+ #
163
+ FilesPersistedEvent = Data.define(
164
+ :uuid,
165
+ :session_id,
166
+ :files,
167
+ :failed,
168
+ :processed_at
169
+ ) do
170
+ def initialize(
171
+ uuid:,
172
+ session_id:,
173
+ files: [],
174
+ failed: [],
175
+ processed_at: nil
176
+ )
177
+ super
178
+ end
179
+
180
+ def type
181
+ :files_persisted
182
+ end
183
+ end
184
+
185
+ # Elicitation complete message (TypeScript SDK v0.2.63 parity)
186
+ #
187
+ # Sent when an MCP server elicitation request completes.
188
+ #
189
+ # @example
190
+ # msg = ElicitationCompleteMessage.new(
191
+ # uuid: "msg-123",
192
+ # session_id: "session-abc",
193
+ # mcp_server_name: "my-server",
194
+ # elicitation_id: "elic-456"
195
+ # )
196
+ #
197
+ ElicitationCompleteMessage = Data.define(:uuid, :session_id, :mcp_server_name, :elicitation_id) do
198
+ def initialize(uuid: "", session_id: "", mcp_server_name: "", elicitation_id: "")
199
+ super
200
+ end
201
+
202
+ def type
203
+ :elicitation_complete
204
+ end
205
+ end
206
+
207
+ # Auth status message (TypeScript SDK parity)
208
+ #
209
+ # Reports authentication status during login flows.
210
+ #
211
+ # @example
212
+ # msg = AuthStatusMessage.new(
213
+ # uuid: "msg-123",
214
+ # session_id: "session-abc",
215
+ # is_authenticating: true,
216
+ # output: ["Waiting for browser..."]
217
+ # )
218
+ #
219
+ AuthStatusMessage = Data.define(
220
+ :uuid,
221
+ :session_id,
222
+ :is_authenticating,
223
+ :output,
224
+ :error
225
+ ) do
226
+ def initialize(
227
+ uuid:,
228
+ session_id:,
229
+ is_authenticating:,
230
+ output: [],
231
+ error: nil
232
+ )
233
+ super
234
+ end
235
+
236
+ def type
237
+ :auth_status
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Tool progress message (TypeScript SDK parity)
5
+ #
6
+ # Reports progress during long-running tool executions.
7
+ #
8
+ # @example
9
+ # msg = ToolProgressMessage.new(
10
+ # uuid: "msg-123",
11
+ # session_id: "session-abc",
12
+ # tool_use_id: "tool-456",
13
+ # tool_name: "Bash",
14
+ # elapsed_time_seconds: 5.2
15
+ # )
16
+ #
17
+ ToolProgressMessage = Data.define(
18
+ :uuid,
19
+ :session_id,
20
+ :tool_use_id,
21
+ :tool_name,
22
+ :parent_tool_use_id,
23
+ :elapsed_time_seconds,
24
+ :task_id
25
+ ) do
26
+ def initialize(
27
+ uuid:,
28
+ session_id:,
29
+ tool_use_id:,
30
+ tool_name:,
31
+ elapsed_time_seconds:,
32
+ parent_tool_use_id: nil,
33
+ task_id: nil
34
+ )
35
+ super
36
+ end
37
+
38
+ def type
39
+ :tool_progress
40
+ end
41
+ end
42
+
43
+ # Tool use summary message (TypeScript SDK parity)
44
+ #
45
+ # Contains a summary of tool use for collapsed display.
46
+ #
47
+ # @example
48
+ # msg = ToolUseSummaryMessage.new(
49
+ # uuid: "msg-123",
50
+ # session_id: "session-abc",
51
+ # summary: "Read 3 files",
52
+ # preceding_tool_use_ids: ["tool-1", "tool-2", "tool-3"]
53
+ # )
54
+ #
55
+ ToolUseSummaryMessage = Data.define(
56
+ :uuid,
57
+ :session_id,
58
+ :summary,
59
+ :preceding_tool_use_ids
60
+ ) do
61
+ def initialize(
62
+ uuid:,
63
+ session_id:,
64
+ summary:,
65
+ preceding_tool_use_ids: []
66
+ )
67
+ super
68
+ end
69
+
70
+ def type
71
+ :tool_use_summary
72
+ end
73
+ end
74
+
75
+ # Local command output message (TypeScript SDK v0.2.63 parity)
76
+ #
77
+ # Contains output from a local command execution.
78
+ #
79
+ # @example
80
+ # msg = LocalCommandOutputMessage.new(
81
+ # uuid: "msg-123",
82
+ # session_id: "session-abc",
83
+ # content: "command output here"
84
+ # )
85
+ #
86
+ LocalCommandOutputMessage = Data.define(:uuid, :session_id, :content) do
87
+ def initialize(uuid: "", session_id: "", content: "")
88
+ super
89
+ end
90
+
91
+ def type
92
+ :local_command_output
93
+ end
94
+ end
95
+ end