claude_agent 0.7.8 → 0.7.10
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -0
- data/README.md +603 -47
- data/SPEC.md +92 -28
- data/lib/claude_agent/client.rb +181 -7
- data/lib/claude_agent/content_blocks.rb +193 -5
- data/lib/claude_agent/control_protocol.rb +97 -11
- data/lib/claude_agent/conversation.rb +248 -0
- data/lib/claude_agent/cumulative_usage.rb +106 -0
- data/lib/claude_agent/event_handler.rb +152 -0
- data/lib/claude_agent/get_session_messages.rb +236 -0
- data/lib/claude_agent/hooks.rb +104 -253
- data/lib/claude_agent/list_sessions.rb +398 -0
- data/lib/claude_agent/mcp/server.rb +3 -3
- data/lib/claude_agent/mcp/tool.rb +4 -4
- data/lib/claude_agent/message_parser.rb +201 -185
- data/lib/claude_agent/messages.rb +86 -13
- data/lib/claude_agent/options.rb +5 -4
- data/lib/claude_agent/permission_queue.rb +87 -0
- data/lib/claude_agent/permission_request.rb +151 -0
- data/lib/claude_agent/permissions.rb +4 -2
- data/lib/claude_agent/query.rb +34 -0
- data/lib/claude_agent/session.rb +71 -3
- data/lib/claude_agent/session_message_relation.rb +59 -0
- data/lib/claude_agent/session_paths.rb +120 -0
- data/lib/claude_agent/tool_activity.rb +78 -0
- data/lib/claude_agent/turn_result.rb +239 -0
- data/lib/claude_agent/types.rb +45 -0
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +58 -2
- data/sig/claude_agent.rbs +336 -7
- metadata +12 -1
data/SPEC.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
This document provides a comprehensive specification of the Claude Agent SDK, comparing feature parity across the official TypeScript and Python SDKs with this Ruby implementation.
|
|
4
4
|
|
|
5
5
|
**Reference Versions:**
|
|
6
|
-
- TypeScript SDK: v0.2.
|
|
7
|
-
- Python SDK: v0.1.
|
|
6
|
+
- TypeScript SDK: v0.2.61 (npm package)
|
|
7
|
+
- Python SDK: v0.1.44 from GitHub (commit 7297bdc)
|
|
8
8
|
- Ruby SDK: This repository
|
|
9
9
|
|
|
10
|
-
**Last Updated:** 2026-02-
|
|
10
|
+
**Last Updated:** 2026-02-26
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -111,6 +111,7 @@ Messages exchanged between SDK and CLI.
|
|
|
111
111
|
| `TaskNotificationMessage` | ✅ | ❌ | ✅ | Background task completion |
|
|
112
112
|
| `ToolUseSummaryMessage` | ✅ | ❌ | ✅ | Summary of tool use (collapsed) |
|
|
113
113
|
| `TaskStartedMessage` | ✅ | ❌ | ✅ | Subagent task registered (v0.2.45) |
|
|
114
|
+
| `TaskProgressMessage` | ✅ | ❌ | ✅ | Background task progress (v0.2.51) |
|
|
114
115
|
| `RateLimitEvent` | ✅ | ❌ | ✅ | Rate limit status changes |
|
|
115
116
|
| `PromptSuggestionMessage` | ✅ | ❌ | ✅ | Suggested next prompt (v0.2.47) |
|
|
116
117
|
| `FilesPersistedEvent` | ✅ | ❌ | ✅ | File persistence confirmation |
|
|
@@ -232,6 +233,8 @@ Bidirectional control protocol for SDK-CLI communication.
|
|
|
232
233
|
| `mcp_reconnect` | ✅ | ❌ | ✅ | Reconnect to MCP server |
|
|
233
234
|
| `mcp_toggle` | ✅ | ❌ | ✅ | Enable/disable MCP server |
|
|
234
235
|
| `stop_task` | ✅ | ❌ | ✅ | Stop a running background task |
|
|
236
|
+
| `mcp_authenticate` | ✅ | ❌ | ✅ | Authenticate MCP server (v0.2.52) |
|
|
237
|
+
| `mcp_clear_auth` | ✅ | ❌ | ✅ | Clear MCP server auth (v0.2.52) |
|
|
235
238
|
| `supported_commands` | ✅ | ❌ | ✅ | Get available slash commands |
|
|
236
239
|
| `supported_models` | ✅ | ❌ | ✅ | Get available models |
|
|
237
240
|
| `account_info` | ✅ | ❌ | ✅ | Get account information |
|
|
@@ -519,6 +522,51 @@ Session management and resumption.
|
|
|
519
522
|
| Persist session | ✅ | ❌ | ✅ | `persistSession` option |
|
|
520
523
|
| Continue most recent | ✅ | ✅ | ✅ | `continue` option |
|
|
521
524
|
|
|
525
|
+
### Session Discovery
|
|
526
|
+
|
|
527
|
+
| Feature | TypeScript | Python | Ruby | Notes |
|
|
528
|
+
|------------------------|:----------:|:------:|:----:|--------------------------------------------------|
|
|
529
|
+
| `listSessions()` | ✅ | ❌ | ✅ | List past sessions with metadata (v0.2.53) |
|
|
530
|
+
| `getSessionMessages()` | ✅ | ❌ | ✅ | Read session transcript messages (v0.2.59) |
|
|
531
|
+
|
|
532
|
+
#### ListSessionsOptions
|
|
533
|
+
|
|
534
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
535
|
+
|---------|:----------:|:------:|:----:|-------------------------------------------|
|
|
536
|
+
| `dir` | ✅ | ❌ | ✅ | Project directory (includes worktrees) |
|
|
537
|
+
| `limit` | ✅ | ❌ | ✅ | Maximum number of sessions to return |
|
|
538
|
+
|
|
539
|
+
#### GetSessionMessagesOptions
|
|
540
|
+
|
|
541
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
542
|
+
|----------|:----------:|:------:|:----:|----------------------------------------------|
|
|
543
|
+
| `dir` | ✅ | ❌ | ✅ | Project directory to find session in |
|
|
544
|
+
| `limit` | ✅ | ❌ | ✅ | Maximum number of messages to return |
|
|
545
|
+
| `offset` | ✅ | ❌ | ✅ | Number of messages to skip from the start |
|
|
546
|
+
|
|
547
|
+
#### SessionMessage Fields
|
|
548
|
+
|
|
549
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
550
|
+
|----------------------|:----------:|:------:|:----:|----------------------------------|
|
|
551
|
+
| `type` | ✅ | ❌ | ✅ | 'user' or 'assistant' |
|
|
552
|
+
| `uuid` | ✅ | ❌ | ✅ | Message UUID |
|
|
553
|
+
| `session_id` | ✅ | ❌ | ✅ | Session ID |
|
|
554
|
+
| `message` | ✅ | ❌ | ✅ | Raw message content |
|
|
555
|
+
| `parent_tool_use_id` | ✅ | ❌ | ✅ | Parent tool use ID (always null) |
|
|
556
|
+
|
|
557
|
+
#### SDKSessionInfo Fields
|
|
558
|
+
|
|
559
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
560
|
+
|----------------|:----------:|:------:|:----:|-------------------------------------|
|
|
561
|
+
| `sessionId` | ✅ | ❌ | ✅ | Session UUID |
|
|
562
|
+
| `summary` | ✅ | ❌ | ✅ | Display title/summary |
|
|
563
|
+
| `lastModified` | ✅ | ❌ | ✅ | Last modified time (ms since epoch) |
|
|
564
|
+
| `fileSize` | ✅ | ❌ | ✅ | Session file size in bytes |
|
|
565
|
+
| `customTitle` | ✅ | ❌ | ✅ | User-set title via /rename |
|
|
566
|
+
| `firstPrompt` | ✅ | ❌ | ✅ | First meaningful user prompt |
|
|
567
|
+
| `gitBranch` | ✅ | ❌ | ✅ | Git branch at end of session |
|
|
568
|
+
| `cwd` | ✅ | ❌ | ✅ | Working directory for session |
|
|
569
|
+
|
|
522
570
|
### V2 Session API (Unstable)
|
|
523
571
|
|
|
524
572
|
| Feature | TypeScript | Python | Ruby | Notes |
|
|
@@ -598,11 +646,11 @@ Error types and hierarchy.
|
|
|
598
646
|
|----------------------|:----------:|:------:|:----:|--------------------------------|
|
|
599
647
|
| Base Error | ✅ | ✅ | ✅ | `Error` / `ClaudeAgent::Error` |
|
|
600
648
|
| `AbortError` | ✅ | ❌ | ✅ | Operation cancelled |
|
|
601
|
-
| `CLINotFoundError` | ❌ |
|
|
649
|
+
| `CLINotFoundError` | ❌ | ✅ | ✅ | CLI not found |
|
|
602
650
|
| `CLIVersionError` | ❌ | ❌ | ✅ | CLI version too old |
|
|
603
|
-
| `CLIConnectionError` | ❌ |
|
|
604
|
-
| `ProcessError` | ❌ |
|
|
605
|
-
| `JSONDecodeError` | ❌ |
|
|
651
|
+
| `CLIConnectionError` | ❌ | ✅ | ✅ | Connection failed |
|
|
652
|
+
| `ProcessError` | ❌ | ✅ | ✅ | CLI process failed |
|
|
653
|
+
| `JSONDecodeError` | ❌ | ✅ | ✅ | JSON parsing failed |
|
|
606
654
|
| `MessageParseError` | ❌ | ❌ | ✅ | Message parsing failed |
|
|
607
655
|
| `TimeoutError` | ❌ | ❌ | ✅ | Control request timeout |
|
|
608
656
|
| `ConfigurationError` | ❌ | ❌ | ✅ | Invalid configuration |
|
|
@@ -625,6 +673,13 @@ Error types and hierarchy.
|
|
|
625
673
|
|
|
626
674
|
Public API surface for SDK clients.
|
|
627
675
|
|
|
676
|
+
### Standalone Functions
|
|
677
|
+
|
|
678
|
+
| Feature | TypeScript | Python | Ruby | Notes |
|
|
679
|
+
|------------------------|:----------:|:------:|:----:|--------------------------------------------|
|
|
680
|
+
| `listSessions()` | ✅ | ❌ | ✅ | List past sessions with metadata (v0.2.53) |
|
|
681
|
+
| `getSessionMessages()` | ✅ | ❌ | ✅ | Read session transcript (v0.2.59) |
|
|
682
|
+
|
|
628
683
|
### Query Interface
|
|
629
684
|
|
|
630
685
|
| Feature | TypeScript | Python | Ruby | Notes |
|
|
@@ -634,24 +689,24 @@ Public API surface for SDK clients.
|
|
|
634
689
|
|
|
635
690
|
### Query Control Methods
|
|
636
691
|
|
|
637
|
-
| Method | TypeScript | Python | Ruby | Notes
|
|
638
|
-
|
|
639
|
-
| `interrupt()` | ✅ | ✅ | ✅ | Interrupt execution
|
|
640
|
-
| `setPermissionMode()` | ✅ | ✅ | ✅ | Change permission mode
|
|
641
|
-
| `setModel()` | ✅ | ✅ | ✅ | Change model
|
|
642
|
-
| `setMaxThinkingTokens()` | ✅ | ❌ | ✅ | Set thinking limit
|
|
643
|
-
| `supportedCommands()` | ✅ | ❌ | ✅ | Get slash commands
|
|
644
|
-
| `supportedModels()` | ✅ | ❌ | ✅ | Get available models
|
|
645
|
-
| `mcpServerStatus()` | ✅ | ✅ | ✅ | Get MCP status
|
|
646
|
-
| `accountInfo()` | ✅ | ❌ | ✅ | Get account info
|
|
647
|
-
| `rewindFiles()` | ✅ | ✅ | ✅ | Rewind file changes
|
|
648
|
-
| `setMcpServers()` | ✅ | ❌ | ✅ | Dynamic MCP servers
|
|
649
|
-
| `reconnectMcpServer()` | ✅ | ❌ | ✅ | Reconnect MCP server
|
|
650
|
-
| `toggleMcpServer()` | ✅ | ❌ | ✅ | Enable/disable MCP
|
|
651
|
-
| `stopTask()` | ✅ | ❌ | ✅ | Stop running task
|
|
652
|
-
| `streamInput()` | ✅ | ✅ | ✅ | Stream user input
|
|
653
|
-
| `initializationResult()` | ✅ |
|
|
654
|
-
| `close()` | ✅ | ✅ | ✅ | Close query/session
|
|
692
|
+
| Method | TypeScript | Python | Ruby | Notes |
|
|
693
|
+
|--------------------------|:----------:|:------:|:----:|----------------------------------------------|
|
|
694
|
+
| `interrupt()` | ✅ | ✅ | ✅ | Interrupt execution |
|
|
695
|
+
| `setPermissionMode()` | ✅ | ✅ | ✅ | Change permission mode |
|
|
696
|
+
| `setModel()` | ✅ | ✅ | ✅ | Change model |
|
|
697
|
+
| `setMaxThinkingTokens()` | ✅ | ❌ | ✅ | Set thinking limit |
|
|
698
|
+
| `supportedCommands()` | ✅ | ❌ | ✅ | Get slash commands |
|
|
699
|
+
| `supportedModels()` | ✅ | ❌ | ✅ | Get available models |
|
|
700
|
+
| `mcpServerStatus()` | ✅ | ✅ | ✅ | Get MCP status |
|
|
701
|
+
| `accountInfo()` | ✅ | ❌ | ✅ | Get account info |
|
|
702
|
+
| `rewindFiles()` | ✅ | ✅ | ✅ | Rewind file changes |
|
|
703
|
+
| `setMcpServers()` | ✅ | ❌ | ✅ | Dynamic MCP servers |
|
|
704
|
+
| `reconnectMcpServer()` | ✅ | ❌ | ✅ | Reconnect MCP server |
|
|
705
|
+
| `toggleMcpServer()` | ✅ | ❌ | ✅ | Enable/disable MCP |
|
|
706
|
+
| `stopTask()` | ✅ | ❌ | ✅ | Stop running task |
|
|
707
|
+
| `streamInput()` | ✅ | ✅ | ✅ | Stream user input |
|
|
708
|
+
| `initializationResult()` | ✅ | ✅ | ✅ | Full init response (Py: `get_server_info()`) |
|
|
709
|
+
| `close()` | ✅ | ✅ | ✅ | Close query/session |
|
|
655
710
|
|
|
656
711
|
### Client Class
|
|
657
712
|
|
|
@@ -694,22 +749,31 @@ Public API surface for SDK clients.
|
|
|
694
749
|
- `executable`/`executableArgs` are JS-specific (`node`/`bun`/`deno`)
|
|
695
750
|
- v0.2.45: Added `TaskStartedMessage`, `RateLimitEvent` message types
|
|
696
751
|
- v0.2.47: Added `promptSuggestions` option and `PromptSuggestionMessage`
|
|
697
|
-
- v0.2.49: Added `ConfigChange` hook event, `SandboxFilesystemConfig
|
|
752
|
+
- v0.2.49: Added `ConfigChange` hook event, `SandboxFilesystemConfig`, ModelInfo capability fields
|
|
698
753
|
- v0.2.50: Added `WorktreeCreate`/`WorktreeRemove` hook events, `apply_flag_settings` control request
|
|
754
|
+
- v0.2.51: Added `TaskProgressMessage` for real-time background agent progress reporting
|
|
755
|
+
- v0.2.52: Added `mcp_authenticate`/`mcp_clear_auth` control requests for MCP server authentication
|
|
756
|
+
- v0.2.53: Added `listSessions()` for discovering and listing past sessions with `SDKSessionInfo` metadata
|
|
757
|
+
- v0.2.54 – v0.2.58: CLI parity updates (no new SDK-facing features)
|
|
758
|
+
- v0.2.59: Added `getSessionMessages()` for reading session transcript history with pagination (limit/offset)
|
|
759
|
+
- v0.2.61: CLI parity update (no new SDK-facing features)
|
|
699
760
|
|
|
700
761
|
### Python SDK
|
|
701
762
|
- Full source available with `Transport` abstract class
|
|
702
763
|
- Partial control protocol: query and client support interrupt, setPermissionMode, setModel, rewindFiles, mcpStatus
|
|
764
|
+
- Has `CLINotFoundError`, `CLIConnectionError`, `ProcessError`, `CLIJSONDecodeError` error types
|
|
703
765
|
- Missing hooks: SessionStart, SessionEnd, Setup, TeammateIdle, TaskCompleted, ConfigChange, WorktreeCreate, WorktreeRemove
|
|
704
766
|
- Missing permission modes: `dontAsk`
|
|
705
767
|
- Missing options: `allowDangerouslySkipPermissions`, `persistSession`, `resumeSessionAt`, `sessionId`, `strictMcpConfig`, `init`/`initOnly`/`maintenance`, `debug`/`debugFile`, `promptSuggestions`
|
|
706
768
|
- `ToolPermissionContext` missing `blockedPath`, `decisionReason`, `toolUseID`, `agentID`, `description`
|
|
707
769
|
- Has SDK MCP server support with `tool()` helper and annotations
|
|
708
770
|
- Added `thinking` config and `effort` option in v0.1.36
|
|
709
|
-
- Handles `rate_limit_event` and unknown message types gracefully (v0.1.
|
|
771
|
+
- Handles `rate_limit_event` and unknown message types gracefully (v0.1.40)
|
|
772
|
+
- Client has `get_server_info()` for accessing the initialization result (v0.1.31+)
|
|
773
|
+
- v0.1.42 – v0.1.44: CLI parity updates (no new SDK-facing features; latest commit bumps bundled CLI to v2.1.61)
|
|
710
774
|
|
|
711
775
|
### Ruby SDK (This Repository)
|
|
712
|
-
- Feature parity with TypeScript SDK v0.2.
|
|
776
|
+
- Feature parity with TypeScript SDK v0.2.61
|
|
713
777
|
- Ruby-idiomatic patterns (Data.define, snake_case)
|
|
714
778
|
- Complete control protocol, hook, and V2 Session API support
|
|
715
779
|
- Dedicated Client class for multi-turn conversations
|
data/lib/claude_agent/client.rb
CHANGED
|
@@ -32,7 +32,7 @@ module ClaudeAgent
|
|
|
32
32
|
# end
|
|
33
33
|
#
|
|
34
34
|
class Client
|
|
35
|
-
attr_reader :options, :transport, :server_info
|
|
35
|
+
attr_reader :options, :transport, :server_info, :cumulative_usage, :event_handler, :permission_queue
|
|
36
36
|
|
|
37
37
|
# Open a client with automatic cleanup
|
|
38
38
|
#
|
|
@@ -61,6 +61,9 @@ module ClaudeAgent
|
|
|
61
61
|
@protocol = nil
|
|
62
62
|
@server_info = nil
|
|
63
63
|
@connected = false
|
|
64
|
+
@cumulative_usage = CumulativeUsage.new
|
|
65
|
+
@event_handler = EventHandler.new
|
|
66
|
+
@permission_queue = PermissionQueue.new
|
|
64
67
|
end
|
|
65
68
|
|
|
66
69
|
# Connect to the CLI
|
|
@@ -74,6 +77,7 @@ module ClaudeAgent
|
|
|
74
77
|
|
|
75
78
|
logger.info("client") { "Connecting" }
|
|
76
79
|
@protocol = ControlProtocol.new(transport: @transport, options: @options)
|
|
80
|
+
@protocol.permission_queue = @permission_queue
|
|
77
81
|
@server_info = @protocol.start(streaming: true)
|
|
78
82
|
@connected = true
|
|
79
83
|
logger.info("client") { "Connected" }
|
|
@@ -88,6 +92,7 @@ module ClaudeAgent
|
|
|
88
92
|
return unless @connected
|
|
89
93
|
|
|
90
94
|
logger.info("client") { "Disconnecting" }
|
|
95
|
+
@permission_queue.drain!(reason: "Client disconnected")
|
|
91
96
|
@protocol&.stop
|
|
92
97
|
@protocol = nil
|
|
93
98
|
@connected = false
|
|
@@ -119,20 +124,133 @@ module ClaudeAgent
|
|
|
119
124
|
#
|
|
120
125
|
# @yield [Message] Received messages
|
|
121
126
|
# @return [Enumerator<Message>] If no block given
|
|
122
|
-
def receive_messages
|
|
127
|
+
def receive_messages
|
|
123
128
|
require_connection!
|
|
124
129
|
|
|
125
|
-
|
|
130
|
+
if block_given?
|
|
131
|
+
@protocol.each_message do |message|
|
|
132
|
+
@cumulative_usage.track(message)
|
|
133
|
+
yield message
|
|
134
|
+
end
|
|
135
|
+
else
|
|
136
|
+
enum_for(:receive_messages)
|
|
137
|
+
end
|
|
126
138
|
end
|
|
127
139
|
|
|
128
140
|
# Receive messages until a ResultMessage is received
|
|
129
141
|
#
|
|
130
142
|
# @yield [Message] Received messages
|
|
131
143
|
# @return [Enumerator<Message>] If no block given
|
|
132
|
-
def receive_response
|
|
144
|
+
def receive_response
|
|
145
|
+
require_connection!
|
|
146
|
+
|
|
147
|
+
if block_given?
|
|
148
|
+
@protocol.receive_response do |message|
|
|
149
|
+
@cumulative_usage.track(message)
|
|
150
|
+
yield message
|
|
151
|
+
end
|
|
152
|
+
else
|
|
153
|
+
enum_for(:receive_response)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Register an event handler
|
|
158
|
+
#
|
|
159
|
+
# Handlers persist across turns and fire automatically during
|
|
160
|
+
# {#receive_turn} and {#send_and_receive}.
|
|
161
|
+
#
|
|
162
|
+
# @param event [Symbol] Event name (:message, :text, :thinking, :tool_use, :tool_result, :result)
|
|
163
|
+
# @yield Event-specific arguments
|
|
164
|
+
# @return [self]
|
|
165
|
+
#
|
|
166
|
+
# @example
|
|
167
|
+
# client.on(:text) { |text| print text }
|
|
168
|
+
# client.on(:tool_use) { |tool| show_spinner(tool) }
|
|
169
|
+
#
|
|
170
|
+
def on(event, &block)
|
|
171
|
+
@event_handler.on(event, &block)
|
|
172
|
+
self
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# @!method on_text(&block)
|
|
176
|
+
# Register a handler for assistant text content
|
|
177
|
+
# @yield [String] Text from the AssistantMessage
|
|
178
|
+
# @return [self]
|
|
179
|
+
|
|
180
|
+
# @!method on_thinking(&block)
|
|
181
|
+
# Register a handler for assistant thinking content
|
|
182
|
+
# @yield [String] Thinking from the AssistantMessage
|
|
183
|
+
# @return [self]
|
|
184
|
+
|
|
185
|
+
# @!method on_tool_use(&block)
|
|
186
|
+
# Register a handler for tool use requests
|
|
187
|
+
# @yield [ToolUseBlock, ServerToolUseBlock] The tool use block
|
|
188
|
+
# @return [self]
|
|
189
|
+
|
|
190
|
+
# @!method on_tool_result(&block)
|
|
191
|
+
# Register a handler for tool results, paired with the original request
|
|
192
|
+
# @yield [ToolResultBlock, ToolUseBlock|nil] Result block and matched tool use
|
|
193
|
+
# @return [self]
|
|
194
|
+
|
|
195
|
+
# @!method on_result(&block)
|
|
196
|
+
# Register a handler for the final ResultMessage
|
|
197
|
+
# @yield [ResultMessage] The result
|
|
198
|
+
# @return [self]
|
|
199
|
+
|
|
200
|
+
# @!method on_message(&block)
|
|
201
|
+
# Register a handler for every message (catch-all)
|
|
202
|
+
# @yield [message] Any message object
|
|
203
|
+
# @return [self]
|
|
204
|
+
|
|
205
|
+
%i[message text thinking tool_use tool_result result].each do |event|
|
|
206
|
+
define_method(:"on_#{event}") { |&block| on(event, &block) }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Receive messages until a ResultMessage, accumulating into a TurnResult
|
|
210
|
+
#
|
|
211
|
+
# Dispatches events to registered handlers (see {#on}).
|
|
212
|
+
#
|
|
213
|
+
# @yield [Message] Each message as it arrives (optional)
|
|
214
|
+
# @return [TurnResult] The completed turn
|
|
215
|
+
def receive_turn
|
|
133
216
|
require_connection!
|
|
134
217
|
|
|
135
|
-
|
|
218
|
+
turn = TurnResult.new
|
|
219
|
+
receive_response do |message|
|
|
220
|
+
turn << message
|
|
221
|
+
@event_handler.handle(message)
|
|
222
|
+
yield message if block_given?
|
|
223
|
+
end
|
|
224
|
+
@event_handler.reset!
|
|
225
|
+
turn
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Send a message and receive the complete turn result
|
|
229
|
+
#
|
|
230
|
+
# Combines {#send_message} and {#receive_turn} into a single call.
|
|
231
|
+
#
|
|
232
|
+
# @param content [String, Array] Message content
|
|
233
|
+
# @param session_id [String] Session ID
|
|
234
|
+
# @param uuid [String, nil] Message UUID for file checkpointing
|
|
235
|
+
# @yield [Message] Each message as it arrives (optional)
|
|
236
|
+
# @return [TurnResult] The completed turn
|
|
237
|
+
#
|
|
238
|
+
# @example Simple
|
|
239
|
+
# turn = client.send_and_receive("Fix the bug")
|
|
240
|
+
# puts turn.text
|
|
241
|
+
# puts "Cost: $#{turn.cost}"
|
|
242
|
+
#
|
|
243
|
+
# @example With streaming
|
|
244
|
+
# turn = client.send_and_receive("Fix the bug") do |msg|
|
|
245
|
+
# case msg
|
|
246
|
+
# when ClaudeAgent::AssistantMessage
|
|
247
|
+
# print msg.text
|
|
248
|
+
# end
|
|
249
|
+
# end
|
|
250
|
+
#
|
|
251
|
+
def send_and_receive(content, session_id: "default", uuid: nil, &block)
|
|
252
|
+
send_message(content, session_id: session_id, uuid: uuid)
|
|
253
|
+
receive_turn(&block)
|
|
136
254
|
end
|
|
137
255
|
|
|
138
256
|
# Stream user input from an enumerable (TypeScript SDK parity)
|
|
@@ -161,11 +279,14 @@ module ClaudeAgent
|
|
|
161
279
|
# end
|
|
162
280
|
# end
|
|
163
281
|
#
|
|
164
|
-
def stream_input(stream, session_id: "default"
|
|
282
|
+
def stream_input(stream, session_id: "default")
|
|
165
283
|
require_connection!
|
|
166
284
|
|
|
167
285
|
if block_given?
|
|
168
|
-
@protocol.stream_conversation(stream, session_id: session_id
|
|
286
|
+
@protocol.stream_conversation(stream, session_id: session_id) do |message|
|
|
287
|
+
@cumulative_usage.track(message)
|
|
288
|
+
yield message
|
|
289
|
+
end
|
|
169
290
|
else
|
|
170
291
|
@protocol.stream_input(stream, session_id: session_id)
|
|
171
292
|
end
|
|
@@ -191,6 +312,7 @@ module ClaudeAgent
|
|
|
191
312
|
def abort!(reason = nil)
|
|
192
313
|
return unless @connected
|
|
193
314
|
|
|
315
|
+
@permission_queue.drain!(reason: reason || "Operation aborted")
|
|
194
316
|
@options.abort_controller&.abort(reason)
|
|
195
317
|
@protocol&.abort!
|
|
196
318
|
end
|
|
@@ -371,6 +493,58 @@ module ClaudeAgent
|
|
|
371
493
|
@protocol.mcp_toggle(server_name, enabled: enabled)
|
|
372
494
|
end
|
|
373
495
|
|
|
496
|
+
# Initiate OAuth authentication for an MCP server (TypeScript SDK v0.2.52 parity)
|
|
497
|
+
#
|
|
498
|
+
# @param server_name [String] Name of the MCP server to authenticate
|
|
499
|
+
# @return [Hash] Response from the CLI
|
|
500
|
+
#
|
|
501
|
+
# @example
|
|
502
|
+
# client.mcp_authenticate("my-remote-server")
|
|
503
|
+
#
|
|
504
|
+
def mcp_authenticate(server_name)
|
|
505
|
+
require_connection!
|
|
506
|
+
|
|
507
|
+
@protocol.mcp_authenticate(server_name)
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Clear stored auth credentials for an MCP server (TypeScript SDK v0.2.52 parity)
|
|
511
|
+
#
|
|
512
|
+
# @param server_name [String] Name of the MCP server to clear auth for
|
|
513
|
+
# @return [Hash] Response from the CLI
|
|
514
|
+
#
|
|
515
|
+
# @example
|
|
516
|
+
# client.mcp_clear_auth("my-remote-server")
|
|
517
|
+
#
|
|
518
|
+
def mcp_clear_auth(server_name)
|
|
519
|
+
require_connection!
|
|
520
|
+
|
|
521
|
+
@protocol.mcp_clear_auth(server_name)
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# Non-blocking poll for the next pending permission request.
|
|
525
|
+
#
|
|
526
|
+
# Returns the next {PermissionRequest} from the queue, or nil if
|
|
527
|
+
# no requests are pending. Call {PermissionRequest#allow!} or
|
|
528
|
+
# {PermissionRequest#deny!} to resolve it.
|
|
529
|
+
#
|
|
530
|
+
# @return [PermissionRequest, nil] The next pending request, or nil
|
|
531
|
+
#
|
|
532
|
+
# @example UI poll loop
|
|
533
|
+
# if request = client.pending_permission
|
|
534
|
+
# show_permission_dialog(request)
|
|
535
|
+
# end
|
|
536
|
+
#
|
|
537
|
+
def pending_permission
|
|
538
|
+
@permission_queue.poll
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
# Check if there are any pending permission requests.
|
|
542
|
+
#
|
|
543
|
+
# @return [Boolean]
|
|
544
|
+
def pending_permissions?
|
|
545
|
+
!@permission_queue.empty?
|
|
546
|
+
end
|
|
547
|
+
|
|
374
548
|
private
|
|
375
549
|
|
|
376
550
|
def logger
|