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.
- checksums.yaml +4 -4
- data/.claude/rules/testing.md +51 -10
- data/.claude/settings.json +1 -0
- data/ARCHITECTURE.md +237 -0
- data/CHANGELOG.md +52 -0
- data/CLAUDE.md +2 -0
- data/README.md +47 -1
- data/Rakefile +17 -0
- data/SPEC.md +314 -133
- data/lib/claude_agent/client/commands.rb +225 -0
- data/lib/claude_agent/client.rb +4 -204
- data/lib/claude_agent/content_blocks/generic_block.rb +39 -0
- data/lib/claude_agent/content_blocks/image_content_block.rb +54 -0
- data/lib/claude_agent/content_blocks/server_tool_result_block.rb +22 -0
- data/lib/claude_agent/content_blocks/server_tool_use_block.rb +48 -0
- data/lib/claude_agent/content_blocks/text_block.rb +19 -0
- data/lib/claude_agent/content_blocks/thinking_block.rb +19 -0
- data/lib/claude_agent/content_blocks/tool_result_block.rb +25 -0
- data/lib/claude_agent/content_blocks/tool_use_block.rb +134 -0
- data/lib/claude_agent/content_blocks.rb +8 -335
- data/lib/claude_agent/control_protocol/commands.rb +304 -0
- data/lib/claude_agent/control_protocol/lifecycle.rb +113 -0
- data/lib/claude_agent/control_protocol/messaging.rb +166 -0
- data/lib/claude_agent/control_protocol/primitives.rb +168 -0
- data/lib/claude_agent/control_protocol/request_handling.rb +231 -0
- data/lib/claude_agent/control_protocol.rb +27 -861
- data/lib/claude_agent/event_handler.rb +1 -0
- data/lib/claude_agent/get_session_info.rb +86 -0
- data/lib/claude_agent/hooks.rb +23 -2
- data/lib/claude_agent/list_sessions.rb +22 -13
- data/lib/claude_agent/message_parser.rb +27 -4
- data/lib/claude_agent/messages/conversation.rb +138 -0
- data/lib/claude_agent/messages/generic.rb +39 -0
- data/lib/claude_agent/messages/hook_lifecycle.rb +158 -0
- data/lib/claude_agent/messages/result.rb +80 -0
- data/lib/claude_agent/messages/streaming.rb +84 -0
- data/lib/claude_agent/messages/system.rb +67 -0
- data/lib/claude_agent/messages/task_lifecycle.rb +240 -0
- data/lib/claude_agent/messages/tool_lifecycle.rb +95 -0
- data/lib/claude_agent/messages.rb +11 -827
- data/lib/claude_agent/options/serializer.rb +194 -0
- data/lib/claude_agent/options.rb +11 -176
- data/lib/claude_agent/sandbox_settings.rb +3 -0
- data/lib/claude_agent/session.rb +0 -204
- data/lib/claude_agent/session_mutations.rb +148 -0
- data/lib/claude_agent/types/mcp.rb +30 -0
- data/lib/claude_agent/types/models.rb +146 -0
- data/lib/claude_agent/types/operations.rb +38 -0
- data/lib/claude_agent/types/sessions.rb +50 -0
- data/lib/claude_agent/types/tools.rb +32 -0
- data/lib/claude_agent/types.rb +6 -264
- data/lib/claude_agent/v2_session.rb +207 -0
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +37 -3
- data/sig/claude_agent.rbs +146 -13
- metadata +33 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e5c5f3de29ca5a58accdb4c6dcc0937ed867fd87579c4909c42ae4d7c82e8d7
|
|
4
|
+
data.tar.gz: 9209ef4868fc7d6267c8f215c6e46222d69cf672a8223c4277db35131510c466
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c78cf5cc5e99c4b97130950763ce04192cbe10dccc4def0f245d9dae298123a1a56e54a75ea079f23f1d9da1415ff9758f7b2a75943ba34027a602fff12cdf5d
|
|
7
|
+
data.tar.gz: 7c3146bd079c24011f2bf95dbd1e12c6a0a81a0dad1634221827138516467df9f344e54813d2281f76bc1e6bd0ff97dd04b0ea81b854222b992f467e541d6c4d
|
data/.claude/rules/testing.md
CHANGED
|
@@ -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/ #
|
|
33
|
-
│ ├──
|
|
34
|
-
│ ├──
|
|
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/
|
|
241
|
+
# test/integration/test_query_scenarios.rb
|
|
232
242
|
require_relative "../integration_helper"
|
|
233
243
|
|
|
234
|
-
class
|
|
235
|
-
test "
|
|
236
|
-
messages = ClaudeAgent.query(prompt: "
|
|
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
|
-
|
|
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 |
|
data/.claude/settings.json
CHANGED
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
|