claude_code 0.0.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.
data/docs/streaming.md ADDED
@@ -0,0 +1,316 @@
1
+ # Streaming Support
2
+
3
+ The Ruby Claude Code SDK provides **real-time streaming** of messages as they arrive from the Claude CLI, enabling responsive user experiences and efficient processing of long-running operations.
4
+
5
+ ## Overview
6
+
7
+ All queries return lazy enumerators that stream messages in real-time, providing immediate feedback and lower memory usage compared to collecting all messages before processing.
8
+
9
+ ## Streaming Methods
10
+
11
+ ### 1. Default Streaming (query method)
12
+
13
+ ```ruby
14
+ # All queries stream by default
15
+ ClaudeCodeSDK.query(
16
+ prompt: "Explain Ruby blocks",
17
+ options: ClaudeCodeOptions.new(max_turns: 1)
18
+ ).each do |message|
19
+ case message
20
+ when ClaudeCodeSDK::SystemMessage
21
+ puts "šŸ”§ System: #{message.subtype}"
22
+ when ClaudeCodeSDK::AssistantMessage
23
+ message.content.each do |block|
24
+ if block.is_a?(ClaudeCodeSDK::TextBlock)
25
+ puts "šŸ’¬ #{block.text}"
26
+ end
27
+ end
28
+ when ClaudeCodeSDK::ResultMessage
29
+ puts "āœ… Cost: $#{message.total_cost_usd}"
30
+ end
31
+ end
32
+ ```
33
+
34
+ ### 1.5. Streaming JSON Input (Multi-turn Conversations)
35
+
36
+ For multiple conversation turns without restarting the CLI:
37
+
38
+ ```ruby
39
+ # Create multiple user messages
40
+ messages = [
41
+ ClaudeCodeSDK::JSONLHelpers.create_user_message("Hello! I need help with Ruby."),
42
+ ClaudeCodeSDK::JSONLHelpers.create_user_message("Can you explain how blocks work?"),
43
+ ClaudeCodeSDK::JSONLHelpers.create_user_message("Show me a practical example.")
44
+ ]
45
+
46
+ # Process all messages in a single streaming session
47
+ ClaudeCodeSDK.stream_json_query(messages) do |message|
48
+ case message
49
+ when ClaudeCodeSDK::SystemMessage
50
+ puts "šŸ”§ System: #{message.subtype}"
51
+ when ClaudeCodeSDK::AssistantMessage
52
+ message.content.each do |block|
53
+ if block.is_a?(ClaudeCodeSDK::TextBlock)
54
+ puts "šŸ’¬ #{block.text}"
55
+ end
56
+ end
57
+ when ClaudeCodeSDK::ResultMessage
58
+ puts "āœ… Completed #{message.num_turns} turns - Cost: $#{message.total_cost_usd}"
59
+ end
60
+ end
61
+ ```
62
+
63
+ **Equivalent CLI command:**
64
+ ```bash
65
+ echo '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Explain this code"}]}}' | claude -p --output-format=stream-json --input-format=stream-json --verbose
66
+ ```
67
+
68
+ ### 2. Auto-formatted Streaming
69
+
70
+ ```ruby
71
+ # Automatic pretty-printing
72
+ ClaudeCodeSDK.stream_query(
73
+ prompt: "Count from 1 to 5",
74
+ options: ClaudeCodeOptions.new(max_turns: 1)
75
+ )
76
+ # Output:
77
+ # šŸ’¬ 1 - The first number...
78
+ # šŸ’¬ 2 - The second number...
79
+ # āœ… Cost: $0.002345
80
+ ```
81
+
82
+ ### 3. Custom Streaming with Block
83
+
84
+ ```ruby
85
+ start_time = Time.now
86
+
87
+ ClaudeCodeSDK.stream_query(
88
+ prompt: "Explain inheritance in Ruby"
89
+ ) do |message, index|
90
+ timestamp = Time.now - start_time
91
+
92
+ case message
93
+ when ClaudeCodeSDK::AssistantMessage
94
+ message.content.each do |block|
95
+ if block.is_a?(ClaudeCodeSDK::TextBlock)
96
+ puts "[#{format('%.2f', timestamp)}s] #{block.text}"
97
+ end
98
+ end
99
+ when ClaudeCodeSDK::ResultMessage
100
+ puts "[#{format('%.2f', timestamp)}s] šŸ’° $#{format('%.6f', message.total_cost_usd || 0)}"
101
+ end
102
+ end
103
+ ```
104
+
105
+ ## Message Flow
106
+
107
+ Messages arrive in this typical order:
108
+
109
+ 1. **SystemMessage** (`subtype: "init"`) - Session initialization with metadata
110
+ 2. **AssistantMessage** - Claude's response with content blocks
111
+ 3. **ResultMessage** - Final statistics and cost information
112
+
113
+ ### System Message (First)
114
+ ```ruby
115
+ when ClaudeCodeSDK::SystemMessage
116
+ if message.subtype == "init"
117
+ puts "Session: #{message.data['session_id']}"
118
+ puts "Model: #{message.data['model']}"
119
+ puts "Tools: #{message.data['tools'].length} available"
120
+ puts "MCP Servers: #{message.data['mcp_servers'].length}"
121
+ end
122
+ ```
123
+
124
+ ### Assistant Message (Main Content)
125
+ ```ruby
126
+ when ClaudeCodeSDK::AssistantMessage
127
+ message.content.each do |block|
128
+ case block
129
+ when ClaudeCodeSDK::TextBlock
130
+ puts "Text: #{block.text}"
131
+ when ClaudeCodeSDK::ToolUseBlock
132
+ puts "Tool: #{block.name} with #{block.input}"
133
+ when ClaudeCodeSDK::ToolResultBlock
134
+ puts "Result: #{block.content}"
135
+ end
136
+ end
137
+ ```
138
+
139
+ ### Result Message (Last)
140
+ ```ruby
141
+ when ClaudeCodeSDK::ResultMessage
142
+ puts "Duration: #{message.duration_ms}ms"
143
+ puts "API Time: #{message.duration_api_ms}ms"
144
+ puts "Turns: #{message.num_turns}"
145
+ puts "Cost: $#{message.total_cost_usd}"
146
+ puts "Session: #{message.session_id}"
147
+ end
148
+ ```
149
+
150
+ ## MCP Streaming
151
+
152
+ MCP tool calls also stream in real-time:
153
+
154
+ ```ruby
155
+ ClaudeCodeSDK.quick_mcp_query(
156
+ "Use the about tool",
157
+ server_name: "ninja",
158
+ server_url: "https://mcp-creator-ninja-v1-4-0.mcp.soy/",
159
+ tools: "about"
160
+ ).each do |message|
161
+ case message
162
+ when ClaudeCodeSDK::AssistantMessage
163
+ message.content.each do |block|
164
+ case block
165
+ when ClaudeCodeSDK::TextBlock
166
+ puts "šŸ“ #{block.text}"
167
+ when ClaudeCodeSDK::ToolUseBlock
168
+ puts "šŸ”§ Using: #{block.name}"
169
+ puts "šŸ“„ Input: #{block.input}"
170
+ when ClaudeCodeSDK::ToolResultBlock
171
+ puts "šŸ“¤ Result: #{block.content}"
172
+ end
173
+ end
174
+ end
175
+ end
176
+ ```
177
+
178
+ ## Advanced Streaming Patterns
179
+
180
+ ### Progress Tracking
181
+ ```ruby
182
+ message_count = 0
183
+ total_text_length = 0
184
+
185
+ ClaudeCodeSDK.query(prompt: "Write a long story").each do |message|
186
+ message_count += 1
187
+
188
+ if message.is_a?(ClaudeCodeSDK::AssistantMessage)
189
+ message.content.each do |block|
190
+ if block.is_a?(ClaudeCodeSDK::TextBlock)
191
+ total_text_length += block.text.length
192
+ puts "Progress: #{message_count} messages, #{total_text_length} characters"
193
+ end
194
+ end
195
+ end
196
+
197
+ # Early termination if needed
198
+ break if total_text_length > 10000
199
+ end
200
+ ```
201
+
202
+ ### Error Handling During Streaming
203
+ ```ruby
204
+ begin
205
+ ClaudeCodeSDK.stream_query(prompt: "Complex operation") do |message, index|
206
+ case message
207
+ when ClaudeCodeSDK::ResultMessage
208
+ if message.is_error
209
+ puts "āŒ Error detected: #{message.subtype}"
210
+ # Handle error immediately
211
+ end
212
+ end
213
+ end
214
+ rescue ClaudeCodeSDK::ProcessError => e
215
+ puts "Process failed: #{e.message}"
216
+ rescue ClaudeCodeSDK::CLIJSONDecodeError => e
217
+ puts "JSON parsing failed: #{e.message}"
218
+ end
219
+ ```
220
+
221
+ ### Tool Call Monitoring
222
+ ```ruby
223
+ tool_calls = []
224
+
225
+ ClaudeCodeSDK.query(
226
+ prompt: "Analyze this codebase using available tools",
227
+ options: ClaudeCodeOptions.new(
228
+ allowed_tools: ["Read", "Bash", "Grep"],
229
+ max_turns: 5
230
+ )
231
+ ).each do |message|
232
+ if message.is_a?(ClaudeCodeSDK::AssistantMessage)
233
+ message.content.each do |block|
234
+ if block.is_a?(ClaudeCodeSDK::ToolUseBlock)
235
+ tool_calls << { name: block.name, input: block.input, time: Time.now }
236
+ puts "šŸ”§ Tool #{tool_calls.length}: #{block.name}"
237
+
238
+ # React to specific tools
239
+ case block.name
240
+ when "Bash"
241
+ puts " Running command: #{block.input['command']}"
242
+ when "Read"
243
+ puts " Reading file: #{block.input['file_path']}"
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ puts "Total tools used: #{tool_calls.length}"
251
+ ```
252
+
253
+ ### Memory-Efficient Processing
254
+ ```ruby
255
+ # Process large responses without storing everything in memory
256
+ text_chunks = []
257
+
258
+ ClaudeCodeSDK.query(prompt: "Generate a very long document").each do |message|
259
+ if message.is_a?(ClaudeCodeSDK::AssistantMessage)
260
+ message.content.each do |block|
261
+ if block.is_a?(ClaudeCodeSDK::TextBlock)
262
+ # Process each chunk immediately
263
+ process_text_chunk(block.text)
264
+
265
+ # Only keep recent chunks in memory
266
+ text_chunks << block.text
267
+ text_chunks.shift if text_chunks.length > 10
268
+ end
269
+ end
270
+ end
271
+ end
272
+ ```
273
+
274
+ ## Performance Benefits
275
+
276
+ - **Memory Efficient**: No need to collect all messages before processing
277
+ - **Responsive UI**: Immediate feedback for long-running operations
278
+ - **Early Termination**: Stop processing when you have enough information
279
+ - **Real-time Monitoring**: Watch tool calls and results as they happen
280
+ - **Progress Tracking**: Monitor complex multi-step operations
281
+
282
+ ## Rails Integration
283
+
284
+ For Rails applications, combine streaming with ActionCable for real-time WebSocket updates:
285
+
286
+ ```ruby
287
+ # In a Sidekiq job
288
+ ClaudeCodeSDK.query(prompt: prompt, options: options).each do |message|
289
+ ActionCable.server.broadcast("claude_#{user_id}", {
290
+ type: 'claude_message',
291
+ data: serialize_message(message),
292
+ timestamp: Time.current
293
+ })
294
+ end
295
+ ```
296
+
297
+ See `examples/rails_sidekiq_example.rb` for a complete Rails + Sidekiq + ActionCable integration example.
298
+
299
+ ## IRB Helpers
300
+
301
+ For quick testing in IRB:
302
+
303
+ ```ruby
304
+ require_relative 'examples/irb_helpers'
305
+
306
+ # Auto-formatted streaming
307
+ auto_stream("Count to 5")
308
+
309
+ # Streaming with timestamps
310
+ stream_claude("What is Ruby?")
311
+
312
+ # MCP streaming
313
+ ninja_test("Tell me about yourself")
314
+ ```
315
+
316
+ The streaming support makes the Ruby SDK perfect for building responsive, interactive applications that provide immediate feedback to users.
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/claude_code'
5
+
6
+ # Example: Different authentication methods for Claude Code SDK
7
+
8
+ puts "=== Authentication Examples ==="
9
+
10
+ # Method 1: Using ANTHROPIC_API_KEY environment variable
11
+ puts "\n1. Using ANTHROPIC_API_KEY environment variable"
12
+ puts "Set your API key:"
13
+ puts "export ANTHROPIC_API_KEY='your-api-key-here'"
14
+
15
+ if ENV['ANTHROPIC_API_KEY']
16
+ puts "āœ… ANTHROPIC_API_KEY is set"
17
+
18
+ # Test basic query with API key authentication
19
+ puts "\nTesting with API key authentication..."
20
+ ClaudeCode.query("What is 2 + 2?") do |message|
21
+ case message
22
+ when ClaudeCode::AssistantMessage
23
+ message.content.each do |block|
24
+ if block.is_a?(ClaudeCode::TextBlock)
25
+ puts "šŸ¤– #{block.text}"
26
+ break
27
+ end
28
+ end
29
+ when ClaudeCode::ResultMessage
30
+ puts "šŸ’° Cost: $#{format('%.6f', message.total_cost_usd || 0)}"
31
+ end
32
+ end
33
+ else
34
+ puts "āŒ ANTHROPIC_API_KEY not set"
35
+ puts "Please set your API key before running this example"
36
+ end
37
+
38
+ # Method 2: Amazon Bedrock authentication
39
+ puts "\n2. Using Amazon Bedrock"
40
+ puts "Set environment variables:"
41
+ puts "export CLAUDE_CODE_USE_BEDROCK=1"
42
+ puts "export AWS_ACCESS_KEY_ID='your-aws-access-key'"
43
+ puts "export AWS_SECRET_ACCESS_KEY='your-aws-secret-key'"
44
+ puts "export AWS_REGION='us-west-2'"
45
+
46
+ if ENV['CLAUDE_CODE_USE_BEDROCK']
47
+ puts "āœ… Amazon Bedrock authentication enabled"
48
+
49
+ # Test with Bedrock
50
+ puts "\nTesting with Bedrock authentication..."
51
+ begin
52
+ ClaudeCode.query("Hello from Bedrock!") do |message|
53
+ case message
54
+ when ClaudeCode::AssistantMessage
55
+ message.content.each do |block|
56
+ if block.is_a?(ClaudeCode::TextBlock)
57
+ puts "šŸ¤– #{block.text}"
58
+ break
59
+ end
60
+ end
61
+ when ClaudeCode::ResultMessage
62
+ puts "šŸ’° Cost: $#{format('%.6f', message.total_cost_usd || 0)}"
63
+ end
64
+ end
65
+ rescue ClaudeCode::CLIConnectionError => e
66
+ puts "āŒ Bedrock connection failed: #{e.message}"
67
+ end
68
+ else
69
+ puts "āŒ Amazon Bedrock not configured"
70
+ end
71
+
72
+ # Method 3: Google Vertex AI authentication
73
+ puts "\n3. Using Google Vertex AI"
74
+ puts "Set environment variables:"
75
+ puts "export CLAUDE_CODE_USE_VERTEX=1"
76
+ puts "export GOOGLE_APPLICATION_CREDENTIALS='path/to/service-account.json'"
77
+ puts "export GOOGLE_CLOUD_PROJECT='your-project-id'"
78
+
79
+ if ENV['CLAUDE_CODE_USE_VERTEX']
80
+ puts "āœ… Google Vertex AI authentication enabled"
81
+
82
+ # Test with Vertex AI
83
+ puts "\nTesting with Vertex AI authentication..."
84
+ begin
85
+ ClaudeCode.query("Hello from Vertex AI!") do |message|
86
+ case message
87
+ when ClaudeCode::AssistantMessage
88
+ message.content.each do |block|
89
+ if block.is_a?(ClaudeCode::TextBlock)
90
+ puts "šŸ¤– #{block.text}"
91
+ break
92
+ end
93
+ end
94
+ when ClaudeCode::ResultMessage
95
+ puts "šŸ’° Cost: $#{format('%.6f', message.total_cost_usd || 0)}"
96
+ end
97
+ end
98
+ rescue ClaudeCode::CLIConnectionError => e
99
+ puts "āŒ Vertex AI connection failed: #{e.message}"
100
+ end
101
+ else
102
+ puts "āŒ Google Vertex AI not configured"
103
+ end
104
+
105
+ # Method 4: Programmatically setting environment variables
106
+ puts "\n4. Setting authentication programmatically"
107
+ puts "You can also set authentication in your Ruby code:"
108
+
109
+ puts <<~RUBY
110
+ # Set API key programmatically (not recommended for production)
111
+ ENV['ANTHROPIC_API_KEY'] = 'your-api-key-here'
112
+
113
+ # Or for Amazon Bedrock
114
+ ENV['CLAUDE_CODE_USE_BEDROCK'] = '1'
115
+ ENV['AWS_ACCESS_KEY_ID'] = 'your-access-key'
116
+ ENV['AWS_SECRET_ACCESS_KEY'] = 'your-secret-key'
117
+ ENV['AWS_REGION'] = 'us-west-2'
118
+
119
+ # Or for Google Vertex AI
120
+ ENV['CLAUDE_CODE_USE_VERTEX'] = '1'
121
+ ENV['GOOGLE_APPLICATION_CREDENTIALS'] = 'path/to/service-account.json'
122
+ ENV['GOOGLE_CLOUD_PROJECT'] = 'your-project-id'
123
+ RUBY
124
+
125
+ puts "\nāš ļø Security Note:"
126
+ puts "For production applications, use secure methods to store credentials:"
127
+ puts "- Environment variables"
128
+ puts "- AWS IAM roles"
129
+ puts "- Google Cloud service accounts"
130
+ puts "- Secret management services"
131
+ puts "- Never commit API keys to version control"
132
+
133
+ puts "\nāœ… Authentication examples completed!"
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ # Basic usage examples for Ruby Claude Code SDK
3
+
4
+ require_relative '../lib/claude_code'
5
+
6
+ def test_basic_query
7
+ puts "=== Testing Basic Query ==="
8
+
9
+ # Use the specific Claude path for your system
10
+ claude_path = "/Users/admin/.claude/local/claude"
11
+
12
+ begin
13
+ ClaudeCode.query(
14
+ prompt: "What is 2 + 2? Answer in one sentence.",
15
+ cli_path: claude_path
16
+ ).each do |message|
17
+ puts "Message type: #{message.class}"
18
+
19
+ if message.is_a?(ClaudeCode::AssistantMessage)
20
+ message.content.each do |block|
21
+ if block.is_a?(ClaudeCode::TextBlock)
22
+ puts "Claude: #{block.text}"
23
+ elsif block.is_a?(ClaudeCode::ToolUseBlock)
24
+ puts "Tool Use: #{block.name} with input #{block.input}"
25
+ end
26
+ end
27
+ elsif message.is_a?(ClaudeCode::ResultMessage)
28
+ puts "Result: #{message.result}"
29
+ puts "Cost: $#{message.total_cost_usd}" if message.total_cost_usd
30
+ else
31
+ puts "Other message: #{message.inspect}"
32
+ end
33
+ end
34
+ rescue => e
35
+ puts "Error: #{e.class} - #{e.message}"
36
+ puts e.backtrace.first(5)
37
+ end
38
+ end
39
+
40
+ def test_with_options
41
+ puts "\n=== Testing with Options ==="
42
+
43
+ claude_path = "/Users/admin/.claude/local/claude"
44
+
45
+ options = ClaudeCode::ClaudeCodeOptions.new(
46
+ max_turns: 1,
47
+ system_prompt: "You are a helpful assistant. Be very concise."
48
+ )
49
+
50
+ begin
51
+ ClaudeCode.query(
52
+ prompt: "Explain Ruby in one sentence.",
53
+ options: options,
54
+ cli_path: claude_path
55
+ ).each do |message|
56
+ if message.is_a?(ClaudeCode::AssistantMessage)
57
+ message.content.each do |block|
58
+ if block.is_a?(ClaudeCode::TextBlock)
59
+ puts "Claude: #{block.text}"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ rescue => e
65
+ puts "Error: #{e.class} - #{e.message}"
66
+ puts e.backtrace.first(5)
67
+ end
68
+ end
69
+
70
+ if __FILE__ == $0
71
+ test_basic_query
72
+ test_with_options
73
+ end
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/claude_code'
5
+
6
+ # Example: Conversation resuming and continuation
7
+
8
+ puts "=== Conversation Resuming Example ==="
9
+
10
+ # Set your API key (you can also set this as an environment variable)
11
+ # ENV['ANTHROPIC_API_KEY'] = 'your-api-key-here'
12
+
13
+ # Start an initial conversation
14
+ puts "\n1. Starting initial conversation..."
15
+ session_id = nil
16
+
17
+ ClaudeCode.query("Hello! My name is John and I'm learning Ruby programming.") do |message|
18
+ case message
19
+ when ClaudeCode::AssistantMessage
20
+ message.content.each do |block|
21
+ if block.is_a?(ClaudeCode::TextBlock)
22
+ puts "šŸ¤– #{block.text}"
23
+ end
24
+ end
25
+ when ClaudeCode::ResultMessage
26
+ session_id = message.session_id
27
+ puts "\nšŸ“‹ Session ID: #{session_id}"
28
+ puts "šŸ’° Cost: $#{format('%.6f', message.total_cost_usd || 0)}"
29
+ end
30
+ end
31
+
32
+ # Continue the most recent conversation (without session ID)
33
+ puts "\n2. Continuing the conversation..."
34
+ ClaudeCode.continue_conversation("What are some good Ruby resources for beginners?") do |message|
35
+ case message
36
+ when ClaudeCode::AssistantMessage
37
+ message.content.each do |block|
38
+ if block.is_a?(ClaudeCode::TextBlock)
39
+ puts "šŸ¤– #{block.text}"
40
+ end
41
+ end
42
+ when ClaudeCode::ResultMessage
43
+ puts "\nšŸ’° Cost: $#{format('%.6f', message.total_cost_usd || 0)}"
44
+ end
45
+ end
46
+
47
+ # Resume a specific conversation by session ID
48
+ if session_id
49
+ puts "\n3. Resuming specific session: #{session_id}"
50
+ ClaudeCode.resume_conversation(
51
+ session_id,
52
+ "Can you recommend a specific Ruby book?"
53
+ ) do |message|
54
+ case message
55
+ when ClaudeCode::AssistantMessage
56
+ message.content.each do |block|
57
+ if block.is_a?(ClaudeCode::TextBlock)
58
+ puts "šŸ¤– #{block.text}"
59
+ end
60
+ end
61
+ when ClaudeCode::ResultMessage
62
+ puts "\nšŸ’° Cost: $#{format('%.6f', message.total_cost_usd || 0)}"
63
+ end
64
+ end
65
+ end
66
+
67
+ # Continue with additional options
68
+ puts "\n4. Continuing with custom options..."
69
+ options = ClaudeCode::ClaudeCodeOptions.new(
70
+ max_turns: 2,
71
+ system_prompt: "You are a Ruby programming tutor. Keep responses concise and practical.",
72
+ model: "claude-3-haiku"
73
+ )
74
+
75
+ ClaudeCode.continue_conversation(
76
+ "Show me a simple Ruby class example",
77
+ options: options
78
+ ) do |message|
79
+ case message
80
+ when ClaudeCode::AssistantMessage
81
+ message.content.each do |block|
82
+ if block.is_a?(ClaudeCode::TextBlock)
83
+ puts "šŸ¤– #{block.text}"
84
+ end
85
+ end
86
+ when ClaudeCode::ResultMessage
87
+ puts "\nšŸ’° Cost: $#{format('%.6f', message.total_cost_usd || 0)}"
88
+ end
89
+ end
90
+
91
+ puts "\nāœ… Conversation resuming examples completed!"
92
+ puts "\nNotes:"
93
+ puts "- Set ANTHROPIC_API_KEY environment variable for authentication"
94
+ puts "- Session IDs are returned in ResultMessage.session_id"
95
+ puts "- Use continue_conversation() to continue the most recent session"
96
+ puts "- Use resume_conversation(session_id) to resume a specific session"