claude_agent 0.7.11 → 0.7.13

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 (56) 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 +52 -0
  6. data/CLAUDE.md +2 -0
  7. data/README.md +47 -1
  8. data/Rakefile +17 -0
  9. data/SPEC.md +314 -133
  10. data/lib/claude_agent/client/commands.rb +225 -0
  11. data/lib/claude_agent/client.rb +4 -204
  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 +113 -0
  23. data/lib/claude_agent/control_protocol/messaging.rb +166 -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 +27 -861
  27. data/lib/claude_agent/event_handler.rb +1 -0
  28. data/lib/claude_agent/get_session_info.rb +86 -0
  29. data/lib/claude_agent/hooks.rb +23 -2
  30. data/lib/claude_agent/list_sessions.rb +22 -13
  31. data/lib/claude_agent/message_parser.rb +27 -4
  32. data/lib/claude_agent/messages/conversation.rb +138 -0
  33. data/lib/claude_agent/messages/generic.rb +39 -0
  34. data/lib/claude_agent/messages/hook_lifecycle.rb +158 -0
  35. data/lib/claude_agent/messages/result.rb +80 -0
  36. data/lib/claude_agent/messages/streaming.rb +84 -0
  37. data/lib/claude_agent/messages/system.rb +67 -0
  38. data/lib/claude_agent/messages/task_lifecycle.rb +240 -0
  39. data/lib/claude_agent/messages/tool_lifecycle.rb +95 -0
  40. data/lib/claude_agent/messages.rb +11 -827
  41. data/lib/claude_agent/options/serializer.rb +194 -0
  42. data/lib/claude_agent/options.rb +11 -176
  43. data/lib/claude_agent/sandbox_settings.rb +3 -0
  44. data/lib/claude_agent/session.rb +0 -204
  45. data/lib/claude_agent/session_mutations.rb +148 -0
  46. data/lib/claude_agent/types/mcp.rb +30 -0
  47. data/lib/claude_agent/types/models.rb +146 -0
  48. data/lib/claude_agent/types/operations.rb +38 -0
  49. data/lib/claude_agent/types/sessions.rb +50 -0
  50. data/lib/claude_agent/types/tools.rb +32 -0
  51. data/lib/claude_agent/types.rb +6 -264
  52. data/lib/claude_agent/v2_session.rb +207 -0
  53. data/lib/claude_agent/version.rb +1 -1
  54. data/lib/claude_agent.rb +37 -3
  55. data/sig/claude_agent.rbs +146 -13
  56. metadata +33 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 965fad355090a487a22e76e097ed84aa7426fe4db396a28e0165e6ceaa09b1e2
4
- data.tar.gz: fe03eeffb6dfa608c5a40bc55d1b04b31f30bd0ec5d8a50322b2dc7bf127bdea
3
+ metadata.gz: 6e5c5f3de29ca5a58accdb4c6dcc0937ed867fd87579c4909c42ae4d7c82e8d7
4
+ data.tar.gz: 9209ef4868fc7d6267c8f215c6e46222d69cf672a8223c4277db35131510c466
5
5
  SHA512:
6
- metadata.gz: b7353c7235415c842e29ba73c315f10ba52d6731324d0e326ecfe56f321272246a6877f07fd2e451007b3ac9252c007f312da15650c77f8ef4c99d06a1e907d1
7
- data.tar.gz: ece71bf88dddd47d40da3dd13e660c8e39eba12484daaf1fa0b87731ff130b8b6ddfcb215e352d2f34c587eb55998a9d1669bbe88363a2587e342190c5b80c0e
6
+ metadata.gz: c78cf5cc5e99c4b97130950763ce04192cbe10dccc4def0f245d9dae298123a1a56e54a75ea079f23f1d9da1415ff9758f7b2a75943ba34027a602fff12cdf5d
7
+ data.tar.gz: 7c3146bd079c24011f2bf95dbd1e12c6a0a81a0dad1634221827138516467df9f344e54813d2281f76bc1e6bd0ff97dd04b0ea81b854222b992f467e541d6c4d
@@ -7,12 +7,14 @@ SDK-specific testing guidance. For general patterns (base classes, mocking, stru
7
7
  ```bash
8
8
  bundle exec rake test # Unit tests only
9
9
  bundle exec rake test_integration # Integration tests (requires CLI v2.0.0+)
10
+ bundle exec rake test_smoke # Smoke tests against local LLM (e.g. Ollama)
10
11
  bundle exec rake test_all # All tests
11
12
  bundle exec ruby -Itest test/claude_agent/test_foo.rb # Single file
12
13
 
13
14
  # Binstubs
14
15
  bin/test # Unit tests only
15
16
  bin/test-integration # Integration tests
17
+ bin/test-smoke # Smoke tests (Ollama)
16
18
  bin/test-all # All tests
17
19
  ```
18
20
 
@@ -22,6 +24,7 @@ bin/test-all # All tests
22
24
  test/
23
25
  ├── test_helper.rb # Central setup, requires, base class
24
26
  ├── integration_helper.rb # Base class for integration tests
27
+ ├── smoke_helper.rb # Base class for smoke tests (Ollama)
25
28
  ├── support/ # Shared mocks, test transports, helpers
26
29
  │ └── mock_transport.rb
27
30
  ├── claude_agent/ # Unit tests (mirrors lib/claude_agent/)
@@ -29,10 +32,15 @@ test/
29
32
  │ ├── test_options.rb
30
33
  │ └── mcp/
31
34
  │ └── test_tool.rb
32
- ├── integration/ # Integration tests (require Claude CLI)
33
- │ ├── test_query.rb
34
- │ ├── test_client.rb
35
- └── ...
35
+ ├── integration/ # Scenario tests (require Claude CLI)
36
+ │ ├── test_query_scenarios.rb
37
+ │ ├── test_client_scenarios.rb
38
+ ├── test_conversation_scenarios.rb
39
+ │ ├── test_session_scenarios.rb
40
+ │ ├── test_permissions_and_tools_scenarios.rb
41
+ │ └── test_transport.rb
42
+ ├── smoke/ # Smoke tests (local LLM via Ollama)
43
+ │ └── test_basic.rb
36
44
  └── fixtures/ # JSON fixtures for parser tests
37
45
  ├── assistant_message.json
38
46
  └── tool_use_response.json
@@ -225,19 +233,27 @@ end
225
233
 
226
234
  ## Integration Tests
227
235
 
236
+ Integration tests are **scenario tests** that consolidate multiple assertions per CLI process spawn. Each test exercises a complete workflow rather than testing a single field.
237
+
228
238
  Integration tests live in `test/integration/` and inherit from `IntegrationTestCase`:
229
239
 
230
240
  ```ruby
231
- # test/integration/test_query.rb
241
+ # test/integration/test_query_scenarios.rb
232
242
  require_relative "../integration_helper"
233
243
 
234
- class TestIntegrationQuery < IntegrationTestCase
235
- test "real query returns result" do
236
- messages = ClaudeAgent.query(prompt: "Say hello", options: test_options).to_a
237
- result = messages.find { |m| m.is_a?(ClaudeAgent::ResultMessage) }
244
+ class TestIntegrationQueryScenarios < IntegrationTestCase
245
+ test "query lifecycle: messages, fields, and content" do
246
+ messages = ClaudeAgent.query(prompt: "Reply with exactly: HELLO", options: test_options).to_a
238
247
 
248
+ # Assert system, assistant, and result messages in one spawn
249
+ system_msg = messages.find { |m| m.is_a?(ClaudeAgent::SystemMessage) }
250
+ assert_not_nil system_msg
251
+ assert_equal "init", system_msg.subtype
252
+
253
+ result = messages.find { |m| m.is_a?(ClaudeAgent::ResultMessage) }
239
254
  assert_not_nil result
240
- assert result.success?
255
+ assert_equal false, result.is_error
256
+ assert_not_nil result.session_id
241
257
  end
242
258
  end
243
259
  ```
@@ -247,6 +263,31 @@ The `IntegrationTestCase` base class:
247
263
  - Skips if Claude CLI is not installed
248
264
  - Provides `test_options` helper with sensible defaults
249
265
 
266
+ ## Smoke Tests
267
+
268
+ Smoke tests run the same core paths against a local LLM (e.g. Ollama) for fast feedback without Anthropic API costs.
269
+
270
+ Smoke tests live in `test/smoke/` and inherit from `SmokeTestCase`:
271
+
272
+ ```ruby
273
+ # test/smoke/test_basic.rb
274
+ require_relative "../smoke_helper"
275
+
276
+ class TestSmokeBasic < SmokeTestCase
277
+ test "basic query returns result" do
278
+ messages = ClaudeAgent.query(prompt: "Reply with exactly: PING", options: test_options).to_a
279
+ result = messages.find { |m| m.is_a?(ClaudeAgent::ResultMessage) }
280
+ assert_not_nil result
281
+ end
282
+ end
283
+ ```
284
+
285
+ The `SmokeTestCase` base class:
286
+ - Extends `IntegrationTestCase` (requires CLI + INTEGRATION=true)
287
+ - Skips unless `SMOKE=true` is set (automatic with `rake test_smoke`)
288
+ - Checks Ollama availability before running
289
+ - `rake test_smoke` auto-sets `ANTHROPIC_BASE_URL`, `ANTHROPIC_API_KEY`, and `ANTHROPIC_AUTH_TOKEN`
290
+
250
291
  ## What to Test
251
292
 
252
293
  | Component | Focus Areas |
@@ -30,6 +30,7 @@
30
30
  "Bash(bin/setup:*)",
31
31
  "Bash(bin/test:*)",
32
32
  "Bash(bin/test-integration:*)",
33
+ "Bash(bin/test-smoke:*)",
33
34
  "Bash(bin/test-all:*)",
34
35
  "Bash(bin/update-reference-sdks:*)",
35
36
  "WebFetch(domain:docs.anthropic.com)",
data/ARCHITECTURE.md ADDED
@@ -0,0 +1,237 @@
1
+ # TypeScript SDK Architecture: Data Flow
2
+
3
+ How data flows through the `@anthropic-ai/claude-agent-sdk`. Not about specific classes — about **concepts**, what they wrap, and where data lives.
4
+
5
+ ## The Big Picture
6
+
7
+ The SDK is a **process bridge**. It spawns the Claude Code CLI as a subprocess and communicates over JSON Lines via stdin/stdout. Everything flows through this pipe.
8
+
9
+ ```mermaid
10
+ graph TB
11
+ subgraph "SDK Process (your app)"
12
+ USER["Your Code"]
13
+ Q["query() / SDKSession"]
14
+ OPT["Options"]
15
+ CP["Control Protocol"]
16
+ HOOKS["Hook Callbacks"]
17
+ MCP_SDK["SDK MCP Servers"]
18
+ PERM["canUseTool Callback"]
19
+ end
20
+
21
+ subgraph "CLI Subprocess"
22
+ CLI["Claude Code CLI"]
23
+ CLAUDE_API["Claude API"]
24
+ MCP_EXT["External MCP Servers"]
25
+ TOOLS["Built-in Tools\n(Bash, Read, Edit...)"]
26
+ end
27
+
28
+ USER -->|"prompt + options"| Q
29
+ Q -->|"builds CLI args + env"| OPT
30
+ OPT -->|"spawns process"| CLI
31
+ Q <-->|"JSON Lines\nstdin/stdout"| CP
32
+ CP <-->|"control_request/\ncontrol_response"| CLI
33
+ CLI <-->|"API calls"| CLAUDE_API
34
+ CLI <-->|"stdio/SSE/HTTP"| MCP_EXT
35
+ CLI -->|"executes"| TOOLS
36
+ CP -->|"routes"| HOOKS
37
+ CP -->|"routes"| MCP_SDK
38
+ CP -->|"routes"| PERM
39
+ ```
40
+
41
+ ## Two Message Channels on One Pipe
42
+
43
+ All messages flow through one stdin/stdout pipe, but the Control Protocol splits them into two logical channels:
44
+
45
+ ```mermaid
46
+ graph LR
47
+ subgraph "stdout from CLI"
48
+ RAW["Raw JSON Lines"]
49
+ end
50
+
51
+ RAW -->|"type: control_request\ntype: control_response"| CTRL["Control Channel\n(handled in-process)"]
52
+ RAW -->|"type: assistant\ntype: result\ntype: system\ntype: user\n..."| SDK_Q["SDK Channel\n(queued for your code)"]
53
+
54
+ CTRL -->|"can_use_tool"| PERM["Permission Callback"]
55
+ CTRL -->|"hook_callback"| HOOK["Hook Callbacks"]
56
+ CTRL -->|"mcp_message"| MCP["SDK MCP Server"]
57
+ CTRL -->|"elicitation"| ELICIT["Elicitation Callback"]
58
+
59
+ SDK_Q --> PARSE["Message Parser"]
60
+ PARSE --> TYPED["Typed SDK Messages\n(SDKMessage union)"]
61
+ ```
62
+
63
+ ## Data Flow: A Complete Turn
64
+
65
+ ```mermaid
66
+ sequenceDiagram
67
+ participant App as Your Code
68
+ participant Q as query()
69
+ participant CP as Control Protocol
70
+ participant T as Transport (stdin/stdout)
71
+ participant CLI as Claude Code CLI
72
+ participant API as Claude API
73
+
74
+ Note over Q,T: 1. SETUP
75
+ App->>Q: query({ prompt, options })
76
+ Q->>T: spawn("claude", [...args])
77
+ T->>CLI: process starts
78
+ Q->>CP: start(streaming: true)
79
+ CP->>T: write: {type: "control_request",\nrequest: {subtype: "initialize",\nhooks, sdkMcpServers, agents...}}
80
+ CLI-->>T: {type: "control_response",\nresponse: {commands, models, account...}}
81
+ CP-->>Q: initialized
82
+
83
+ Note over Q,T: 2. SEND PROMPT
84
+ CP->>T: write: {type: "user",\nmessage: {role: "user", content: "..."}}
85
+
86
+ Note over CLI,API: 3. CLI PROCESSES
87
+ CLI->>API: messages.create(...)
88
+ API-->>CLI: streaming response
89
+
90
+ Note over Q,T: 4. PERMISSION CHECK (if tool use)
91
+ CLI-->>T: {type: "control_request",\nrequest: {subtype: "can_use_tool",\ntool_name: "Bash"...}}
92
+ CP->>App: canUseTool("Bash", input, options)
93
+ App-->>CP: {behavior: "allow"}
94
+ CP->>T: write: {type: "control_response",\nresponse: {behavior: "allow"}}
95
+
96
+ Note over Q,T: 5. STREAM RESULTS
97
+ CLI-->>T: {type: "assistant",\nmessage: {content: [...]}}
98
+ T-->>CP: routes to SDK queue
99
+ CP-->>Q: SDKAssistantMessage
100
+ Q-->>App: yield message
101
+
102
+ CLI-->>T: {type: "result",\nsubtype: "success", result: "...", usage: {...}}
103
+ T-->>CP: routes to SDK queue
104
+ CP-->>Q: SDKResultMessage
105
+ Q-->>App: yield message (iteration ends)
106
+ ```
107
+
108
+ ## What Wraps CLI Data vs. What's an SDK Abstraction
109
+
110
+ ### SDK Abstractions
111
+
112
+ Invented by the SDK — not in the CLI's JSON protocol.
113
+
114
+ | Concept | What it does |
115
+ |------------------------|-------------------------------------------------------------------------------------------------------------------------|
116
+ | `query()` | Entry point. Returns `AsyncGenerator<SDKMessage>`. Spawns process, runs handshake, sends prompt, yields typed messages. |
117
+ | `SDKSession` | V2 multi-turn session interface. `.send()` / `.stream()` / `.close()`. |
118
+ | `Options` | Converts user config into CLI args + env vars. The SDK's configuration surface. |
119
+ | Control Protocol | Routes messages between two channels. Manages request/response matching with IDs. |
120
+ | `canUseTool` | Permission callback. CLI sends a control request, SDK routes it to your function. |
121
+ | `HookCallback` | In-process hook execution. CLI sends hook input, SDK calls your function, returns result. |
122
+ | `createSdkMcpServer()` | Hosts an MCP server in your process. CLI routes MCP messages to it via control protocol. |
123
+ | `onElicitation` | User input callback for MCP OAuth flows. |
124
+ | Query methods | `.interrupt()`, `.setModel()`, `.rewindFiles()`, `.setMcpServers()` — all send control requests. |
125
+ | `listSessions()` | Reads the CLI's session JSONL files from disk. No subprocess involved. |
126
+ | `getSessionMessages()` | Parses the CLI's transcript format from disk. |
127
+
128
+ ### CLI Protocol Wrappers
129
+
130
+ Direct 1:1 mapping of JSON the CLI sends/receives. The SDK adds TypeScript types but doesn't transform the data.
131
+
132
+ | Type | CLI JSON it wraps |
133
+ |----------------------------------------|--------------------------------------------------------------------------------------------------------------|
134
+ | `SDKAssistantMessage` | `{type: "assistant", message: BetaMessage}` |
135
+ | `SDKUserMessage` | `{type: "user", message: MessageParam}` |
136
+ | `SDKResultMessage` | `{type: "result", subtype: "success"\|"error_*"}` |
137
+ | `SDKSystemMessage` | `{type: "system", subtype: "init"}` |
138
+ | `SDKStatusMessage` | `{type: "system", subtype: "status"}` |
139
+ | `SDKPartialAssistantMessage` | `{type: "stream_event", event: ...}` |
140
+ | `SDKHookStarted/Progress/Response` | Hook lifecycle messages |
141
+ | `SDKTaskStarted/Progress/Notification` | Subagent task messages |
142
+ | `SDKToolProgressMessage` | Tool execution progress |
143
+ | `SDKRateLimitEvent` | Rate limit state changes |
144
+ | `SDKCompactBoundaryMessage` | Context compaction markers |
145
+ | `SDKFilesPersistedEvent` | File checkpoint events |
146
+ | `SDKElicitationCompleteMessage` | MCP elicitation completion |
147
+ | `SDKPromptSuggestionMessage` | Predicted next prompt |
148
+ | Control request/response types | `initialize`, `can_use_tool`, `hook_callback`, `mcp_message`, `interrupt`, `set_model`, `rewind_files`, etc. |
149
+
150
+ ### Configuration Wrappers
151
+
152
+ SDK types that map to CLI flags, env vars, or config JSON.
153
+
154
+ | SDK Type | Maps to |
155
+ |------------------------|----------------------------------------------|
156
+ | `PermissionMode` | `--permission-mode` flag |
157
+ | `McpStdioServerConfig` | MCP server config JSON |
158
+ | `AgentDefinition` | `--agents` JSON config |
159
+ | `SandboxSettings` | `--sandbox-settings` JSON |
160
+ | `OutputFormat` | `--output-format` config |
161
+ | `ThinkingConfig` | `--thinking` / `--max-thinking-tokens` |
162
+ | `SystemPrompt` | `--system-prompt` / `--append-system-prompt` |
163
+ | `SettingSource` | `--settings-sources` flag |
164
+
165
+ ### Pass-through Types
166
+
167
+ Types from upstream protocols that flow through untouched. The SDK re-exports them for convenience.
168
+
169
+ | Type | Source |
170
+ |-----------------------------|-----------------------------------------------|
171
+ | `BetaMessage` | `@anthropic-ai/sdk` (Anthropic API response) |
172
+ | `BetaUsage` | `@anthropic-ai/sdk` (token counts from API) |
173
+ | `MessageParam` | `@anthropic-ai/sdk` (API input format) |
174
+ | `BetaRawMessageStreamEvent` | `@anthropic-ai/sdk` (streaming chunks) |
175
+ | `JSONRPCMessage` | `@modelcontextprotocol/sdk` (MCP wire format) |
176
+ | `CallToolResult` | `@modelcontextprotocol/sdk` (MCP tool output) |
177
+ | `ElicitResult` | `@modelcontextprotocol/sdk` (MCP user input) |
178
+
179
+ ## The Three Layers
180
+
181
+ ```mermaid
182
+ graph TB
183
+ subgraph L3["Layer 3: SDK Abstractions"]
184
+ direction LR
185
+ query["query()"]
186
+ session["SDKSession"]
187
+ opts["Options builder"]
188
+ ctrl["Control Protocol"]
189
+ callbacks["Callbacks\n(canUseTool, hooks,\nonElicitation)"]
190
+ session_mgmt["Session Management\n(listSessions,\ngetSessionMessages)"]
191
+ end
192
+
193
+ subgraph L2["Layer 2: CLI Protocol Types"]
194
+ direction LR
195
+ msgs["SDKMessage union\n(21+ message types)"]
196
+ ctrl_req["Control Requests\n(initialize, can_use_tool,\nhook_callback, ...)"]
197
+ ctrl_resp["Control Responses"]
198
+ end
199
+
200
+ subgraph L1["Layer 1: Pass-through Types"]
201
+ direction LR
202
+ beta["BetaMessage\n(Anthropic API)"]
203
+ usage["BetaUsage\n(Anthropic API)"]
204
+ msgparam["MessageParam\n(Anthropic API)"]
205
+ jsonrpc["JSONRPCMessage\n(MCP protocol)"]
206
+ content["Content Blocks\n(text, tool_use, thinking)"]
207
+ end
208
+
209
+ L3 --> L2
210
+ L2 --> L1
211
+
212
+ style L3 fill:#e1f5fe
213
+ style L2 fill:#fff3e0
214
+ style L1 fill:#f3e5f5
215
+ ```
216
+
217
+ **Layer 1 (Pass-through):** Types from the Anthropic API and MCP protocol. The CLI's `SDKAssistantMessage.message` is literally a `BetaMessage` from the API. The SDK doesn't interpret content blocks — they're whatever the API returned.
218
+
219
+ **Layer 2 (CLI Protocol):** The JSON shapes the CLI sends/receives over JSON Lines. The SDK defines TypeScript types for them but doesn't transform the data. It's a typed window into what the CLI emits.
220
+
221
+ **Layer 3 (SDK Abstractions):** Things the SDK invents. `query()` is not a CLI concept — it's an ergonomic wrapper that spawns a process, runs the control protocol handshake, sends the prompt, and yields typed messages. The Control Protocol's routing logic is an SDK concept. The CLI just sends a control request and blocks until it gets a response.
222
+
223
+ ## Key Insight: The SDK is Thin Where It Matters
224
+
225
+ The SDK deliberately avoids re-interpreting CLI data. It doesn't parse content blocks into rich objects, doesn't build conversation trees, doesn't accumulate state. It's a **typed streaming bridge**:
226
+
227
+ ```
228
+ Your prompt --> Options --> CLI args --> subprocess --> JSON Lines --> typed messages --> your code
229
+ ^ |
230
+ +-- control requests <---------+
231
+ (permissions, hooks, MCP)
232
+ ```
233
+
234
+ The "intelligence" lives in three places:
235
+ 1. **The CLI** — runs Claude, manages tools, handles the API
236
+ 2. **The Control Protocol** — routes bidirectional requests between CLI and your callbacks
237
+ 3. **Your code** — decides permissions, handles hooks, processes messages
data/CHANGELOG.md CHANGED
@@ -7,6 +7,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.13] - 2026-03-14
11
+
12
+ ### Added
13
+ - Ollama smoke test profile (`rake test_smoke`, `bin/test-smoke`) for fast local testing against local LLMs
14
+ - `SmokeTestCase` base class with Ollama availability check and configurable `SMOKE_MODEL` env var
15
+
16
+ ### Changed
17
+ - Restructured integration test suite: removed 92 misplaced unit tests, consolidated 52 CLI tests into 16 scenario tests across 5 files
18
+ - Integration tests now use scenario-based structure (`test_*_scenarios.rb`) that exercises multiple assertions per CLI process spawn
19
+ - Split `content_blocks.rb` (352 lines) into `content_blocks/` directory with 8 focused files + barrel file
20
+ - Split `types.rb` (300 lines) into `types/` directory with 5 domain-grouped files + barrel file
21
+ - Split `messages.rb` (908 lines) into `messages/` directory with 8 semantic-domain files + barrel file
22
+ - Extracted `control_protocol.rb` (1,010 lines) into 5 mixin modules (`Primitives`, `Lifecycle`, `Messaging`, `Commands`, `RequestHandling`) + shell class
23
+ - Extracted `Client::Commands` mixin from `client.rb` (545 lines) for CLI command delegations
24
+ - Extracted `Options::Serializer` mixin from `options.rb` (344 lines) for CLI arg/env serialization
25
+ - Split `session.rb` into `v2_session.rb` (V2 Session API) and `session.rb` (historical finder)
26
+ - Split monolithic test files to mirror lib/ directory structure (`test/claude_agent/{messages,content_blocks,types,control_protocol}/`)
27
+ - Added RBS module declarations for `ControlProtocol`, `Client`, and `Options` mixins
28
+
29
+ ### Added
30
+ - `AgentInfo` type with `name`, `description`, and `model` fields (TypeScript SDK v0.2.63 parity)
31
+ - `supported_agents` control request on `ControlProtocol` and `Client` for querying available subagents (TypeScript SDK v0.2.63 parity)
32
+ - `agents` field on `InitializationResult` returning `AgentInfo[]`
33
+ - `fast_mode_state` field on `ResultMessage` (TypeScript SDK v0.2.63 parity)
34
+ - `ElicitationCompleteMessage` for MCP elicitation completion events (TypeScript SDK v0.2.63 parity)
35
+ - `LocalCommandOutputMessage` for local command output events (TypeScript SDK v0.2.63 parity)
36
+ - `on_elicitation` option for handling MCP elicitation requests via callback (TypeScript SDK v0.2.63 parity)
37
+ - `Elicitation` and `ElicitationResult` hook events with input types (TypeScript SDK v0.2.63 parity)
38
+ - Elicitation control protocol handling with callback support and default decline behavior
39
+ - `on_elicitation_complete` and `on_local_command_output` event handler methods
40
+ - `tag` and `created_at` fields on `SessionInfo` (TypeScript SDK v0.2.75 parity)
41
+ - `supports_auto_mode` field on `ModelInfo` (TypeScript SDK v0.2.75 parity)
42
+ - `offset` parameter on `list_sessions` for pagination (TypeScript SDK v0.2.75 parity)
43
+ - `rename_session(session_id, title)` for renaming session files (TypeScript SDK v0.2.74 parity)
44
+ - `tag_session(session_id, tag)` for tagging sessions with Unicode sanitization (TypeScript SDK v0.2.75 parity)
45
+ - `get_session_info(session_id)` for single-session lookup by UUID (TypeScript SDK v0.2.75 parity)
46
+ - `agent_progress_summaries` option for periodic AI-generated progress summaries (TypeScript SDK v0.2.72 parity)
47
+ - `prompt` field on `TaskStartedMessage` (TypeScript SDK v0.2.75 parity)
48
+ - `summary` field on `TaskProgressMessage` for AI-generated progress summaries (TypeScript SDK v0.2.72 parity)
49
+ - `fast_mode_state` field on `InitializationResult` (TypeScript SDK v0.2.75 parity)
50
+ - RBS signatures for all new types, fields, and methods
51
+
52
+ ### Removed
53
+ - `get_settings` from SPEC.md — not in TypeScript SDK public API (`sdk.d.ts`)
54
+
55
+ ## [0.7.12] - 2026-02-27
56
+
57
+ ### Added
58
+ - `ResultMessage#uuid` field (TypeScript SDK parity — every other message type already had it)
59
+ - `sdkMcpServers` is now sent in the initialize request when SDK MCP servers are configured (TypeScript SDK parity)
60
+ - `abort!` now sends `control_cancel_request` for each pending request before failing them locally (TypeScript SDK parity)
61
+
10
62
  ## [0.7.11] - 2026-02-27
11
63
 
12
64
  ### Added
data/CLAUDE.md CHANGED
@@ -17,6 +17,7 @@ bin/setup # Install dependencies
17
17
  bundle exec rake # Run unit tests + rbs + rubocop (default)
18
18
  bundle exec rake test # Unit tests only
19
19
  bundle exec rake test_integration # Integration tests (requires CLI v2.0.0+)
20
+ bundle exec rake test_smoke # Smoke tests against local LLM (e.g. Ollama)
20
21
  bundle exec rake test_all # All tests (requires CLI v2.0.0+)
21
22
  bundle exec rake rbs # Validate RBS signatures
22
23
  bundle exec rake rbs:parse # RBS syntax check only (faster)
@@ -28,6 +29,7 @@ bin/console # IRB with gem loaded
28
29
  bin/test # Unit tests only
29
30
  bin/test-integration # Integration tests
30
31
  bin/test-all # All tests
32
+ bin/test-smoke # Smoke tests (Ollama)
31
33
  bin/rbs-validate # Validate RBS signatures
32
34
  bin/release VERSION # Release gem (e.g., bin/release 1.2.0)
33
35
  ```
data/README.md CHANGED
@@ -488,6 +488,7 @@ message.has_tool_use? # Check if tools are being used
488
488
  Final message with usage statistics:
489
489
 
490
490
  ```ruby
491
+ result.uuid # Message UUID
491
492
  result.session_id # Session identifier
492
493
  result.num_turns # Number of conversation turns
493
494
  result.duration_ms # Total duration in milliseconds
@@ -500,6 +501,7 @@ result.error? # Convenience method
500
501
  result.errors # Array of error messages (if any)
501
502
  result.permission_denials # Array of SDKPermissionDenial (if any)
502
503
  result.stop_reason # Why the model stopped generating (e.g. "end_turn", "tool_use")
504
+ result.fast_mode_state # Fast mode status (if applicable)
503
505
  ```
504
506
 
505
507
  ### UserMessageReplay
@@ -689,6 +691,27 @@ Suggested follow-up prompts (requires `prompt_suggestions: true`):
689
691
  suggestion.suggestion # The suggested prompt text
690
692
  ```
691
693
 
694
+ ### ElicitationCompleteMessage
695
+
696
+ MCP elicitation completion:
697
+
698
+ ```ruby
699
+ elicitation.uuid # Message UUID
700
+ elicitation.session_id # Session identifier
701
+ elicitation.mcp_server_name # MCP server that requested elicitation
702
+ elicitation.elicitation_id # Elicitation identifier
703
+ ```
704
+
705
+ ### LocalCommandOutputMessage
706
+
707
+ Local command output:
708
+
709
+ ```ruby
710
+ output.uuid # Message UUID
711
+ output.session_id # Session identifier
712
+ output.content # Command output content
713
+ ```
714
+
692
715
  ### GenericMessage
693
716
 
694
717
  Wraps unknown/future message types instead of raising errors:
@@ -1083,6 +1106,27 @@ update = ClaudeAgent::PermissionUpdate.new(
1083
1106
  )
1084
1107
  ```
1085
1108
 
1109
+ ## MCP Elicitation
1110
+
1111
+ Handle MCP server elicitation requests (e.g. OAuth flows, form input):
1112
+
1113
+ ```ruby
1114
+ options = ClaudeAgent::Options.new(
1115
+ on_elicitation: ->(request, signal:) {
1116
+ # request contains: server_name, message, mode, url, elicitation_id, requested_schema
1117
+ case request[:mode]
1118
+ when "oauth"
1119
+ # Handle OAuth flow
1120
+ { action: "accept", content: { token: "..." } }
1121
+ else
1122
+ { action: "decline" }
1123
+ end
1124
+ }
1125
+ )
1126
+ ```
1127
+
1128
+ Without `on_elicitation`, all elicitation requests are declined by default.
1129
+
1086
1130
  ## Error Handling
1087
1131
 
1088
1132
  The SDK provides specific error types:
@@ -1212,6 +1256,7 @@ client.cumulative_usage # CumulativeUsage with totals across all turns
1212
1256
  # Query capabilities
1213
1257
  client.supported_commands.each { |cmd| puts "#{cmd.name}: #{cmd.description}" }
1214
1258
  client.supported_models.each { |model| puts "#{model.value}: #{model.display_name}" }
1259
+ client.supported_agents.each { |agent| puts "#{agent.name}: #{agent.description}" }
1215
1260
  client.mcp_server_status.each { |s| puts "#{s.name}: #{s.status}" }
1216
1261
  puts client.account_info.email
1217
1262
 
@@ -1390,7 +1435,7 @@ session = ClaudeAgent.unstable_v2_create_session(options)
1390
1435
  | Type | Purpose |
1391
1436
  |--------------------------|----------------------------------------------------------------------------------|
1392
1437
  | `TurnResult` | Complete agent turn with text, tools, usage, and status accessors |
1393
- | `ToolActivity` | Tool use/result pair with turn index and timing (immutable, post-turn) |
1438
+ | `ToolActivity` | Tool use/result pair with turn index and timing (immutable, post-turn) |
1394
1439
  | `LiveToolActivity` | Mutable real-time tool status (running/done/error) with elapsed time |
1395
1440
  | `ToolActivityTracker` | Enumerable collection of `LiveToolActivity` with auto-wiring and `on_change` |
1396
1441
  | `CumulativeUsage` | Running totals of tokens, cost, turns, and duration |
@@ -1399,6 +1444,7 @@ session = ClaudeAgent.unstable_v2_create_session(options)
1399
1444
  | `EventHandler` | Typed event callback registry |
1400
1445
  | `SlashCommand` | Available slash commands (name, description, argument_hint) |
1401
1446
  | `ModelInfo` | Available models (value, display_name, description) |
1447
+ | `AgentInfo` | Available agents (name, description, model) |
1402
1448
  | `McpServerStatus` | MCP server status (name, status, server_info) |
1403
1449
  | `AccountInfo` | Account information (email, organization, subscription_type) |
1404
1450
  | `ModelUsage` | Per-model usage stats (input_tokens, output_tokens, cost_usd) |
data/Rakefile CHANGED
@@ -35,6 +35,23 @@ task :test_integration do
35
35
  Rake::Task[:_integration].invoke
36
36
  end
37
37
 
38
+ # Internal task for running smoke tests
39
+ Minitest::TestTask.create(:_smoke) do |t|
40
+ t.test_globs = [ "test/smoke/**/test_*.rb" ]
41
+ t.warning = false
42
+ end
43
+
44
+ # Smoke tests - wrapper that sets INTEGRATION + SMOKE + Ollama defaults
45
+ desc "Run smoke tests against local LLM (e.g. Ollama)"
46
+ task :test_smoke do
47
+ ENV["INTEGRATION"] = "true"
48
+ ENV["SMOKE"] = "true"
49
+ ENV["ANTHROPIC_BASE_URL"] ||= "http://localhost:11434"
50
+ ENV["ANTHROPIC_API_KEY"] ||= "ollama"
51
+ ENV["ANTHROPIC_AUTH_TOKEN"] ||= "ollama"
52
+ Rake::Task[:_smoke].invoke
53
+ end
54
+
38
55
  require "rubocop/rake_task"
39
56
 
40
57
  RuboCop::RakeTask.new