claude-agent-sdk 0.13.1 → 0.14.0
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 +29 -0
- data/README.md +46 -2
- data/lib/claude_agent_sdk/message_parser.rb +5 -1
- data/lib/claude_agent_sdk/query.rb +16 -2
- data/lib/claude_agent_sdk/sdk_mcp_server.rb +11 -2
- data/lib/claude_agent_sdk/session_mutations.rb +244 -3
- data/lib/claude_agent_sdk/sessions.rb +4 -2
- data/lib/claude_agent_sdk/subprocess_cli_transport.rb +17 -1
- data/lib/claude_agent_sdk/types.rb +67 -11
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/claude_agent_sdk.rb +30 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 49a855001019149f2348998742683b3589966a6b97464e815b1fd44abb01ab51
|
|
4
|
+
data.tar.gz: af1826c40f4e8cb6c1910b6ca81b2fa3e01dc83442b4373f5e912b0a1d4af871
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b5322fb7d911240d064985742c6817d8f9ccb0918c30ec7b27aca012c657ee0b5c1a648f6714cc471ca32b15acf8d7f72d6bf2f938d8b4811d9a0ba497a3f662
|
|
7
|
+
data.tar.gz: 3ea02aa3ef4da8a98e876c18605f2bffcd5edca9e6ca719bfa70de925463030e8cb1793916bf1681f7fcb432f044398dfdbb5480fb295a11673736977c772347
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.14.0] - 2026-04-08 — Python SDK v0.1.51–0.1.56 Parity
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
#### Type Completeness
|
|
13
|
+
- `AssistantMessage`: `message_id`, `stop_reason`, `session_id`, `uuid` fields (populated from CLI message data)
|
|
14
|
+
- `AgentDefinition`: `disallowed_tools`, `max_turns`, `initial_prompt`, `background`, `effort`, `permission_mode` fields (serialized to CLI via initialize request)
|
|
15
|
+
- `ToolPermissionContext`: `tool_use_id`, `agent_id` fields for distinguishing parallel permission requests and sub-agent context
|
|
16
|
+
- `PERMISSION_MODES`: added `dontAsk` and `auto` values
|
|
17
|
+
|
|
18
|
+
#### New Types and Options
|
|
19
|
+
- `SystemPromptFile` class — loads system prompt from a file path via `--system-prompt-file` CLI flag
|
|
20
|
+
- `TaskBudget` class — API-side token budget, passed as `--task-budget` CLI flag
|
|
21
|
+
- `ForkSessionResult` class — returned by `fork_session()` with the new session ID
|
|
22
|
+
- `session_id` option on `ClaudeAgentOptions` — specify a custom session ID via `--session-id` CLI flag
|
|
23
|
+
- `task_budget` option on `ClaudeAgentOptions`
|
|
24
|
+
|
|
25
|
+
#### Session Management
|
|
26
|
+
- `ClaudeAgentSDK.delete_session(session_id:, directory:)` — hard-deletes a session JSONL file
|
|
27
|
+
- `ClaudeAgentSDK.fork_session(session_id:, directory:, up_to_message_id:, title:)` — filesystem-level fork with UUID remapping, sidechain filtering, content-replacement forwarding, and auto-generated titles
|
|
28
|
+
- `offset` parameter on `ClaudeAgentSDK.list_sessions` for cursor-based pagination
|
|
29
|
+
|
|
30
|
+
#### Client Introspection
|
|
31
|
+
- `Client#get_context_usage` / `Query#get_context_usage` — sends `get_context_usage` control request for context window breakdown (tokens by category, model, MCP tools, memory files, etc.)
|
|
32
|
+
|
|
33
|
+
#### MCP Robustness
|
|
34
|
+
- `SdkMcpTool#meta` field and `_meta` forwarding in `tools/list` responses — prevents silent truncation of large tool results (>50K chars) by forwarding `anthropic/maxResultSizeChars` through the MCP `_meta` field
|
|
35
|
+
- `create_tool` auto-populates `_meta` from `annotations[:maxResultSizeChars]` when present
|
|
36
|
+
|
|
8
37
|
## [0.13.1] - 2026-04-05
|
|
9
38
|
|
|
10
39
|
### Fixed
|
data/README.md
CHANGED
|
@@ -106,7 +106,7 @@ Add this line to your application's Gemfile:
|
|
|
106
106
|
gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
|
|
107
107
|
|
|
108
108
|
# Or use a stable version from RubyGems
|
|
109
|
-
gem 'claude-agent-sdk', '~> 0.13.
|
|
109
|
+
gem 'claude-agent-sdk', '~> 0.13.1'
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
And then execute:
|
|
@@ -130,6 +130,20 @@ gem install claude-agent-sdk
|
|
|
130
130
|
|
|
131
131
|
If you're using [Claude Code](https://claude.ai/claude-code) or another agentic coding tool that supports [skills](https://skills.sh), you can install the SDK skill:
|
|
132
132
|
|
|
133
|
+
**Option 1: Via Plugin Marketplace (recommended)**
|
|
134
|
+
|
|
135
|
+
This repo is a Claude Code plugin marketplace. Add it once, then install the skill:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Add the marketplace
|
|
139
|
+
/plugin marketplace add ya-luotao/claude-agent-sdk-ruby
|
|
140
|
+
|
|
141
|
+
# Install the plugin
|
|
142
|
+
/plugin install claude-agent-ruby@claude-agent-sdk-ruby
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Option 2: Via skills.sh**
|
|
146
|
+
|
|
133
147
|
```bash
|
|
134
148
|
npx skills add https://github.com/ya-luotao/claude-agent-sdk-ruby --skill claude-agent-sdk-ruby
|
|
135
149
|
```
|
|
@@ -949,6 +963,9 @@ end
|
|
|
949
963
|
# List sessions for a specific directory
|
|
950
964
|
sessions = ClaudeAgentSDK.list_sessions(directory: '/path/to/project', limit: 10)
|
|
951
965
|
|
|
966
|
+
# Paginate with offset
|
|
967
|
+
page2 = ClaudeAgentSDK.list_sessions(directory: '.', limit: 10, offset: 10)
|
|
968
|
+
|
|
952
969
|
# Include git worktree sessions
|
|
953
970
|
sessions = ClaudeAgentSDK.list_sessions(directory: '.', include_worktrees: true)
|
|
954
971
|
```
|
|
@@ -1005,7 +1022,34 @@ ClaudeAgentSDK.tag_session(
|
|
|
1005
1022
|
)
|
|
1006
1023
|
```
|
|
1007
1024
|
|
|
1008
|
-
|
|
1025
|
+
### Deleting a Session
|
|
1026
|
+
|
|
1027
|
+
```ruby
|
|
1028
|
+
# Hard-delete a session (removes the JSONL file permanently)
|
|
1029
|
+
ClaudeAgentSDK.delete_session(
|
|
1030
|
+
session_id: '550e8400-e29b-41d4-a716-446655440000',
|
|
1031
|
+
directory: '/path/to/project' # optional
|
|
1032
|
+
)
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
### Forking a Session
|
|
1036
|
+
|
|
1037
|
+
```ruby
|
|
1038
|
+
# Fork a session into a new branch with fresh UUIDs
|
|
1039
|
+
result = ClaudeAgentSDK.fork_session(
|
|
1040
|
+
session_id: '550e8400-e29b-41d4-a716-446655440000',
|
|
1041
|
+
title: 'Experiment branch' # optional, auto-generated if omitted
|
|
1042
|
+
)
|
|
1043
|
+
puts result.session_id # UUID of the new forked session
|
|
1044
|
+
|
|
1045
|
+
# Fork up to a specific message (partial fork)
|
|
1046
|
+
result = ClaudeAgentSDK.fork_session(
|
|
1047
|
+
session_id: '550e8400-e29b-41d4-a716-446655440000',
|
|
1048
|
+
up_to_message_id: 'message-uuid-here'
|
|
1049
|
+
)
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
> **Note:** Session mutations use append-only JSONL writes with `O_WRONLY | O_APPEND` (no `O_CREAT`) for TOCTOU safety. They are safe to call while the session is open in a CLI process. `fork_session` uses `O_CREAT | O_EXCL` to prevent race conditions.
|
|
1009
1053
|
|
|
1010
1054
|
## Observability (OpenTelemetry / Langfuse)
|
|
1011
1055
|
|
|
@@ -70,7 +70,11 @@ module ClaudeAgentSDK
|
|
|
70
70
|
model: data.dig(:message, :model),
|
|
71
71
|
parent_tool_use_id: data[:parent_tool_use_id],
|
|
72
72
|
error: data[:error], # authentication_failed, billing_error, rate_limit, invalid_request, server_error, unknown
|
|
73
|
-
usage: data.dig(:message, :usage)
|
|
73
|
+
usage: data.dig(:message, :usage),
|
|
74
|
+
message_id: data.dig(:message, :id),
|
|
75
|
+
stop_reason: data.dig(:message, :stop_reason),
|
|
76
|
+
session_id: data[:session_id],
|
|
77
|
+
uuid: data[:uuid]
|
|
74
78
|
)
|
|
75
79
|
end
|
|
76
80
|
|
|
@@ -89,10 +89,16 @@ module ClaudeAgentSDK
|
|
|
89
89
|
description: agent_def.description,
|
|
90
90
|
prompt: agent_def.prompt,
|
|
91
91
|
tools: agent_def.tools,
|
|
92
|
+
disallowedTools: agent_def.disallowed_tools,
|
|
92
93
|
model: agent_def.model,
|
|
93
94
|
skills: agent_def.skills,
|
|
94
95
|
memory: agent_def.memory,
|
|
95
|
-
mcpServers: agent_def.mcp_servers
|
|
96
|
+
mcpServers: agent_def.mcp_servers,
|
|
97
|
+
initialPrompt: agent_def.initial_prompt,
|
|
98
|
+
maxTurns: agent_def.max_turns,
|
|
99
|
+
background: agent_def.background,
|
|
100
|
+
effort: agent_def.effort,
|
|
101
|
+
permissionMode: agent_def.permission_mode
|
|
96
102
|
}.compact
|
|
97
103
|
end
|
|
98
104
|
end
|
|
@@ -283,7 +289,9 @@ module ClaudeAgentSDK
|
|
|
283
289
|
|
|
284
290
|
context = ToolPermissionContext.new(
|
|
285
291
|
signal: nil,
|
|
286
|
-
suggestions: request_data[:permission_suggestions] || []
|
|
292
|
+
suggestions: request_data[:permission_suggestions] || [],
|
|
293
|
+
tool_use_id: request_data[:tool_use_id],
|
|
294
|
+
agent_id: request_data[:agent_id]
|
|
287
295
|
)
|
|
288
296
|
|
|
289
297
|
response = @can_use_tool.call(
|
|
@@ -805,6 +813,12 @@ module ClaudeAgentSDK
|
|
|
805
813
|
|
|
806
814
|
public
|
|
807
815
|
|
|
816
|
+
# Get a breakdown of current context window usage by category.
|
|
817
|
+
# @return [Hash] Context usage response with categories, totalTokens, maxTokens, etc.
|
|
818
|
+
def get_context_usage
|
|
819
|
+
send_control_request({ subtype: 'get_context_usage' })
|
|
820
|
+
end
|
|
821
|
+
|
|
808
822
|
# Get current MCP server connection status (only works with streaming mode)
|
|
809
823
|
# @return [Hash] MCP status information, including mcpServers list
|
|
810
824
|
def get_mcp_status
|
|
@@ -66,6 +66,7 @@ module ClaudeAgentSDK
|
|
|
66
66
|
inputSchema: convert_input_schema(tool.input_schema)
|
|
67
67
|
}
|
|
68
68
|
entry[:annotations] = tool.annotations if tool.annotations
|
|
69
|
+
entry[:_meta] = tool.meta if tool.meta
|
|
69
70
|
entry
|
|
70
71
|
end
|
|
71
72
|
end
|
|
@@ -400,15 +401,23 @@ module ClaudeAgentSDK
|
|
|
400
401
|
# { content: [{ type: 'text', text: "Result: #{result}" }] }
|
|
401
402
|
# end
|
|
402
403
|
# end
|
|
403
|
-
def self.create_tool(name, description, input_schema, annotations: nil, &handler)
|
|
404
|
+
def self.create_tool(name, description, input_schema, annotations: nil, meta: nil, &handler)
|
|
404
405
|
raise ArgumentError, 'Block required for tool handler' unless handler
|
|
405
406
|
|
|
407
|
+
# Auto-populate _meta with maxResultSizeChars from annotations if present
|
|
408
|
+
resolved_meta = meta
|
|
409
|
+
if resolved_meta.nil? && annotations
|
|
410
|
+
max_chars = annotations[:maxResultSizeChars] || annotations['maxResultSizeChars']
|
|
411
|
+
resolved_meta = { 'anthropic/maxResultSizeChars' => max_chars } if max_chars
|
|
412
|
+
end
|
|
413
|
+
|
|
406
414
|
SdkMcpTool.new(
|
|
407
415
|
name: name,
|
|
408
416
|
description: description,
|
|
409
417
|
input_schema: input_schema,
|
|
410
418
|
handler: handler,
|
|
411
|
-
annotations: annotations
|
|
419
|
+
annotations: annotations,
|
|
420
|
+
meta: resolved_meta
|
|
412
421
|
)
|
|
413
422
|
end
|
|
414
423
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
|
+
require 'securerandom'
|
|
4
5
|
require_relative 'sessions'
|
|
5
6
|
|
|
6
7
|
module ClaudeAgentSDK
|
|
7
|
-
# Session mutation functions: rename and
|
|
8
|
+
# Session mutation functions: rename, tag, delete, and fork sessions.
|
|
8
9
|
#
|
|
9
10
|
# Ported from Python SDK's _internal/session_mutations.py.
|
|
10
11
|
# Appends typed metadata entries to the session's JSONL file,
|
|
11
12
|
# matching the CLI pattern. Safe to call from any SDK host process.
|
|
12
|
-
module SessionMutations
|
|
13
|
+
module SessionMutations # rubocop:disable Metrics/ModuleLength
|
|
13
14
|
module_function
|
|
14
15
|
|
|
15
16
|
# Rename a session by appending a custom-title entry.
|
|
@@ -60,8 +61,245 @@ module ClaudeAgentSDK
|
|
|
60
61
|
append_to_session(session_id, data, directory)
|
|
61
62
|
end
|
|
62
63
|
|
|
64
|
+
# Delete a session by removing its JSONL file.
|
|
65
|
+
#
|
|
66
|
+
# This is a hard delete — the file is removed permanently. For soft-delete
|
|
67
|
+
# semantics, use tag_session(id, '__hidden') and filter on listing instead.
|
|
68
|
+
#
|
|
69
|
+
# @param session_id [String] UUID of the session to delete
|
|
70
|
+
# @param directory [String, nil] Project directory path
|
|
71
|
+
# @raise [ArgumentError] if session_id is invalid
|
|
72
|
+
# @raise [Errno::ENOENT] if the session file cannot be found
|
|
73
|
+
def delete_session(session_id:, directory: nil)
|
|
74
|
+
raise ArgumentError, "Invalid session_id: #{session_id}" unless session_id.match?(Sessions::UUID_RE)
|
|
75
|
+
|
|
76
|
+
result = find_session_file_with_dir(session_id, directory)
|
|
77
|
+
raise Errno::ENOENT, "Session #{session_id} not found#{" in project directory for #{directory}" if directory}" unless result
|
|
78
|
+
|
|
79
|
+
path = result[0]
|
|
80
|
+
|
|
81
|
+
begin
|
|
82
|
+
File.delete(path)
|
|
83
|
+
rescue Errno::ENOENT
|
|
84
|
+
raise Errno::ENOENT, "Session #{session_id} not found"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Fork a session into a new branch with fresh UUIDs.
|
|
89
|
+
#
|
|
90
|
+
# Creates a copy of the session transcript (or a prefix up to up_to_message_id)
|
|
91
|
+
# with remapped UUIDs and a new session ID. Sidechains are filtered out,
|
|
92
|
+
# progress entries are excluded from the written output but used for
|
|
93
|
+
# parentUuid chain walking.
|
|
94
|
+
#
|
|
95
|
+
# @param session_id [String] UUID of the session to fork
|
|
96
|
+
# @param directory [String, nil] Project directory path
|
|
97
|
+
# @param up_to_message_id [String, nil] Truncate the fork at this message UUID
|
|
98
|
+
# @param title [String, nil] Custom title for the fork (auto-generated if omitted)
|
|
99
|
+
# @return [ForkSessionResult] Result containing the new session ID
|
|
100
|
+
# @raise [ArgumentError] if session_id or up_to_message_id is invalid
|
|
101
|
+
# @raise [Errno::ENOENT] if the session file cannot be found
|
|
102
|
+
def fork_session(session_id:, directory: nil, up_to_message_id: nil, title: nil) # rubocop:disable Metrics/MethodLength
|
|
103
|
+
raise ArgumentError, "Invalid session_id: #{session_id}" unless session_id.match?(Sessions::UUID_RE)
|
|
104
|
+
|
|
105
|
+
raise ArgumentError, "Invalid up_to_message_id: #{up_to_message_id}" if up_to_message_id && !up_to_message_id.match?(Sessions::UUID_RE)
|
|
106
|
+
|
|
107
|
+
result = find_session_file_with_dir(session_id, directory)
|
|
108
|
+
raise Errno::ENOENT, "Session #{session_id} not found#{" in project directory for #{directory}" if directory}" unless result
|
|
109
|
+
|
|
110
|
+
file_path, project_dir = result
|
|
111
|
+
content = File.read(file_path)
|
|
112
|
+
raise ArgumentError, "Session #{session_id} has no messages to fork" if content.empty?
|
|
113
|
+
|
|
114
|
+
transcript, content_replacements = parse_fork_transcript(content, session_id)
|
|
115
|
+
transcript.reject! { |e| e['isSidechain'] }
|
|
116
|
+
raise ArgumentError, "Session #{session_id} has no messages to fork" if transcript.empty?
|
|
117
|
+
|
|
118
|
+
if up_to_message_id
|
|
119
|
+
cutoff = transcript.index { |e| e['uuid'] == up_to_message_id }
|
|
120
|
+
raise ArgumentError, "Message #{up_to_message_id} not found in session #{session_id}" unless cutoff
|
|
121
|
+
|
|
122
|
+
transcript = transcript[0..cutoff]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Build UUID mapping (including progress entries for parentUuid chain walk)
|
|
126
|
+
uuid_mapping = {}
|
|
127
|
+
transcript.each { |e| uuid_mapping[e['uuid']] = SecureRandom.uuid }
|
|
128
|
+
|
|
129
|
+
by_uuid = transcript.to_h { |e| [e['uuid'], e] }
|
|
130
|
+
|
|
131
|
+
# Filter out progress messages from written output
|
|
132
|
+
writable = transcript.reject { |e| e['type'] == 'progress' }
|
|
133
|
+
raise ArgumentError, "Session #{session_id} has no messages to fork" if writable.empty?
|
|
134
|
+
|
|
135
|
+
forked_session_id = SecureRandom.uuid
|
|
136
|
+
now = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%S.%3NZ')
|
|
137
|
+
|
|
138
|
+
lines = writable.each_with_index.map do |original, i|
|
|
139
|
+
build_forked_entry(original, i, writable.size, uuid_mapping, by_uuid,
|
|
140
|
+
forked_session_id, session_id, now)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Append content-replacement entry if any
|
|
144
|
+
if content_replacements && !content_replacements.empty?
|
|
145
|
+
lines << JSON.generate({
|
|
146
|
+
'type' => 'content-replacement',
|
|
147
|
+
'sessionId' => forked_session_id,
|
|
148
|
+
'replacements' => content_replacements
|
|
149
|
+
})
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Derive title
|
|
153
|
+
fork_title = title&.strip
|
|
154
|
+
if fork_title.nil? || fork_title.empty?
|
|
155
|
+
head = content[0, Sessions::LITE_READ_BUF_SIZE] || ''
|
|
156
|
+
tail = content.length > Sessions::LITE_READ_BUF_SIZE ? content[-Sessions::LITE_READ_BUF_SIZE..] : head
|
|
157
|
+
base = Sessions.extract_json_string_field(tail, 'customTitle', last: true) ||
|
|
158
|
+
Sessions.extract_json_string_field(head, 'customTitle', last: true) ||
|
|
159
|
+
Sessions.extract_json_string_field(tail, 'aiTitle', last: true) ||
|
|
160
|
+
Sessions.extract_json_string_field(head, 'aiTitle', last: true) ||
|
|
161
|
+
Sessions.extract_first_prompt_from_head(head) ||
|
|
162
|
+
'Forked session'
|
|
163
|
+
fork_title = "#{base} (fork)"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
lines << JSON.generate({
|
|
167
|
+
'type' => 'custom-title',
|
|
168
|
+
'sessionId' => forked_session_id,
|
|
169
|
+
'customTitle' => fork_title
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
fork_path = File.join(project_dir, "#{forked_session_id}.jsonl")
|
|
173
|
+
io = nil
|
|
174
|
+
fd = IO.sysopen(fork_path, File::WRONLY | File::CREAT | File::EXCL, 0o600)
|
|
175
|
+
begin
|
|
176
|
+
io = IO.new(fd)
|
|
177
|
+
io.write("#{lines.join("\n")}\n")
|
|
178
|
+
ensure
|
|
179
|
+
if io
|
|
180
|
+
io.close
|
|
181
|
+
else
|
|
182
|
+
IO.for_fd(fd).close rescue nil # rubocop:disable Style/RescueModifier
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
ForkSessionResult.new(session_id: forked_session_id)
|
|
187
|
+
end
|
|
188
|
+
|
|
63
189
|
# -- Private helpers --
|
|
64
190
|
|
|
191
|
+
# Locate the JSONL file for a session and return [file_path, project_dir].
|
|
192
|
+
def find_session_file_with_dir(session_id, directory)
|
|
193
|
+
file_name = "#{session_id}.jsonl"
|
|
194
|
+
return find_in_directory(file_name, directory) if directory
|
|
195
|
+
|
|
196
|
+
find_in_all_projects(file_name)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def find_in_directory(file_name, directory)
|
|
200
|
+
path = File.realpath(directory).unicode_normalize(:nfc)
|
|
201
|
+
result = try_project_dir(file_name, Sessions.find_project_dir(path))
|
|
202
|
+
return result if result
|
|
203
|
+
|
|
204
|
+
worktree_paths = begin
|
|
205
|
+
Sessions.detect_worktrees(path)
|
|
206
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
207
|
+
[]
|
|
208
|
+
end
|
|
209
|
+
worktree_paths.each do |wt_path|
|
|
210
|
+
next if wt_path == path
|
|
211
|
+
|
|
212
|
+
result = try_project_dir(file_name, Sessions.find_project_dir(wt_path))
|
|
213
|
+
return result if result
|
|
214
|
+
end
|
|
215
|
+
nil
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def try_project_dir(file_name, project_dir)
|
|
219
|
+
return nil unless project_dir
|
|
220
|
+
|
|
221
|
+
candidate = File.join(project_dir, file_name)
|
|
222
|
+
File.exist?(candidate) ? [candidate, project_dir] : nil
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def find_in_all_projects(file_name)
|
|
226
|
+
projects_dir = File.join(Sessions.config_dir, 'projects')
|
|
227
|
+
return nil unless File.directory?(projects_dir)
|
|
228
|
+
|
|
229
|
+
Dir.children(projects_dir).each do |child|
|
|
230
|
+
pd = File.join(projects_dir, child)
|
|
231
|
+
next unless File.directory?(pd)
|
|
232
|
+
|
|
233
|
+
candidate = File.join(pd, file_name)
|
|
234
|
+
return [candidate, pd] if File.exist?(candidate)
|
|
235
|
+
end
|
|
236
|
+
nil
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Parse a fork transcript, extracting entries and content-replacement data.
|
|
240
|
+
def parse_fork_transcript(content, _session_id)
|
|
241
|
+
transcript = []
|
|
242
|
+
content_replacements = nil
|
|
243
|
+
|
|
244
|
+
content.each_line do |line|
|
|
245
|
+
entry = JSON.parse(line.strip)
|
|
246
|
+
next unless entry.is_a?(Hash) && entry['uuid']
|
|
247
|
+
|
|
248
|
+
if entry['type'] == 'content-replacement'
|
|
249
|
+
content_replacements = entry['replacements']
|
|
250
|
+
next
|
|
251
|
+
end
|
|
252
|
+
transcript << entry
|
|
253
|
+
rescue JSON::ParserError
|
|
254
|
+
next
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
[transcript, content_replacements]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Build a single forked entry with remapped UUIDs.
|
|
261
|
+
def build_forked_entry(original, index, total, uuid_mapping, by_uuid,
|
|
262
|
+
forked_session_id, source_session_id, now)
|
|
263
|
+
new_uuid = uuid_mapping[original['uuid']]
|
|
264
|
+
|
|
265
|
+
# Resolve parentUuid, skipping progress ancestors
|
|
266
|
+
new_parent_uuid = resolve_parent_uuid(original['parentUuid'], by_uuid, uuid_mapping)
|
|
267
|
+
|
|
268
|
+
# Only update timestamp on the last message
|
|
269
|
+
timestamp = index == total - 1 ? now : (original['timestamp'] || now)
|
|
270
|
+
|
|
271
|
+
# Remap logicalParentUuid — unlike parentUuid (which walks the chain and nils on miss),
|
|
272
|
+
# logicalParentUuid preserves the original UUID when unmapped because it may reference
|
|
273
|
+
# a message outside the forked range (e.g., a prior conversation branch).
|
|
274
|
+
logical_parent = original['logicalParentUuid']
|
|
275
|
+
new_logical_parent = logical_parent ? (uuid_mapping[logical_parent] || logical_parent) : logical_parent
|
|
276
|
+
|
|
277
|
+
forked = original.merge(
|
|
278
|
+
'uuid' => new_uuid,
|
|
279
|
+
'parentUuid' => new_parent_uuid,
|
|
280
|
+
'logicalParentUuid' => new_logical_parent,
|
|
281
|
+
'sessionId' => forked_session_id,
|
|
282
|
+
'timestamp' => timestamp,
|
|
283
|
+
'isSidechain' => false,
|
|
284
|
+
'forkedFrom' => { 'sessionId' => source_session_id, 'messageUuid' => original['uuid'] }
|
|
285
|
+
)
|
|
286
|
+
%w[teamName agentName slug sourceToolAssistantUUID].each { |k| forked.delete(k) }
|
|
287
|
+
|
|
288
|
+
JSON.generate(forked)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Walk up parentUuid chain skipping progress entries.
|
|
292
|
+
def resolve_parent_uuid(parent_id, by_uuid, uuid_mapping)
|
|
293
|
+
while parent_id
|
|
294
|
+
parent = by_uuid[parent_id]
|
|
295
|
+
break unless parent
|
|
296
|
+
return uuid_mapping[parent_id] if parent['type'] != 'progress'
|
|
297
|
+
|
|
298
|
+
parent_id = parent['parentUuid']
|
|
299
|
+
end
|
|
300
|
+
nil
|
|
301
|
+
end
|
|
302
|
+
|
|
65
303
|
def append_to_session(session_id, data, directory)
|
|
66
304
|
file_name = "#{session_id}.jsonl"
|
|
67
305
|
|
|
@@ -156,7 +394,10 @@ module ClaudeAgentSDK
|
|
|
156
394
|
'Other'
|
|
157
395
|
end
|
|
158
396
|
|
|
159
|
-
private_class_method :
|
|
397
|
+
private_class_method :find_session_file_with_dir,
|
|
398
|
+
:find_in_directory, :try_project_dir, :find_in_all_projects,
|
|
399
|
+
:parse_fork_transcript, :build_forked_entry, :resolve_parent_uuid,
|
|
400
|
+
:append_to_session, :append_to_session_in_directory,
|
|
160
401
|
:append_to_session_global, :try_append, :sanitize_unicode, :unicode_category
|
|
161
402
|
end
|
|
162
403
|
end
|
|
@@ -302,17 +302,19 @@ module ClaudeAgentSDK
|
|
|
302
302
|
# List sessions for a directory (or all sessions)
|
|
303
303
|
# @param directory [String, nil] Working directory to list sessions for
|
|
304
304
|
# @param limit [Integer, nil] Maximum number of sessions to return
|
|
305
|
+
# @param offset [Integer] Number of sessions to skip (for pagination)
|
|
305
306
|
# @param include_worktrees [Boolean] Whether to include git worktree sessions
|
|
306
307
|
# @return [Array<SDKSessionInfo>] Sessions sorted by last_modified descending
|
|
307
|
-
def list_sessions(directory: nil, limit: nil, include_worktrees: true)
|
|
308
|
+
def list_sessions(directory: nil, limit: nil, offset: 0, include_worktrees: true)
|
|
308
309
|
sessions = if directory
|
|
309
310
|
list_sessions_for_directory(directory, include_worktrees)
|
|
310
311
|
else
|
|
311
312
|
list_all_sessions
|
|
312
313
|
end
|
|
313
314
|
|
|
314
|
-
# Sort by last_modified descending
|
|
315
|
+
# Sort by last_modified descending, then apply offset and limit
|
|
315
316
|
sessions.sort_by! { |s| -s.last_modified }
|
|
317
|
+
sessions = sessions[offset..] || [] if offset.positive?
|
|
316
318
|
sessions = sessions.first(limit) if limit
|
|
317
319
|
sessions
|
|
318
320
|
end
|
|
@@ -74,13 +74,18 @@ module ClaudeAgentSDK
|
|
|
74
74
|
cmd.concat(['--system-prompt', ''])
|
|
75
75
|
elsif @options.system_prompt.is_a?(String)
|
|
76
76
|
cmd.concat(['--system-prompt', @options.system_prompt])
|
|
77
|
+
elsif @options.system_prompt.is_a?(SystemPromptFile)
|
|
78
|
+
cmd.concat(['--system-prompt-file', @options.system_prompt.path])
|
|
77
79
|
elsif @options.system_prompt.is_a?(SystemPromptPreset)
|
|
78
80
|
# Preset activates the default Claude Code system prompt by not passing --system-prompt ""
|
|
79
81
|
# Only --append-system-prompt is passed if append text is provided
|
|
80
82
|
cmd.concat(['--append-system-prompt', @options.system_prompt.append]) if @options.system_prompt.append
|
|
81
83
|
elsif @options.system_prompt.is_a?(Hash)
|
|
82
84
|
prompt_type = @options.system_prompt[:type] || @options.system_prompt['type']
|
|
83
|
-
if prompt_type == '
|
|
85
|
+
if prompt_type == 'file'
|
|
86
|
+
prompt_path = @options.system_prompt[:path] || @options.system_prompt['path']
|
|
87
|
+
cmd.concat(['--system-prompt-file', prompt_path]) if prompt_path
|
|
88
|
+
elsif prompt_type == 'preset'
|
|
84
89
|
append = @options.system_prompt[:append] || @options.system_prompt['append']
|
|
85
90
|
# Preset activates the default Claude Code system prompt by not passing --system-prompt ""
|
|
86
91
|
cmd.concat(['--append-system-prompt', append]) if append
|
|
@@ -96,6 +101,7 @@ module ClaudeAgentSDK
|
|
|
96
101
|
cmd.concat(['--permission-mode', @options.permission_mode]) if @options.permission_mode
|
|
97
102
|
cmd << '--continue' if @options.continue_conversation
|
|
98
103
|
cmd.concat(['--resume', @options.resume]) if @options.resume
|
|
104
|
+
cmd.concat(['--session-id', @options.session_id]) if @options.session_id
|
|
99
105
|
|
|
100
106
|
# Settings handling with sandbox merge
|
|
101
107
|
build_settings_args(cmd)
|
|
@@ -103,6 +109,16 @@ module ClaudeAgentSDK
|
|
|
103
109
|
# Budget limit option
|
|
104
110
|
cmd.concat(['--max-budget-usd', @options.max_budget_usd.to_s]) if @options.max_budget_usd
|
|
105
111
|
|
|
112
|
+
# Task budget (API-side token budget)
|
|
113
|
+
if @options.task_budget
|
|
114
|
+
total = if @options.task_budget.is_a?(TaskBudget)
|
|
115
|
+
@options.task_budget.total
|
|
116
|
+
else
|
|
117
|
+
@options.task_budget[:total] || @options.task_budget['total']
|
|
118
|
+
end
|
|
119
|
+
cmd.concat(['--task-budget', total.to_s]) if total
|
|
120
|
+
end
|
|
121
|
+
|
|
106
122
|
# Thinking configuration (takes precedence over deprecated max_thinking_tokens)
|
|
107
123
|
thinking_tokens = resolve_thinking_tokens
|
|
108
124
|
cmd.concat(['--max-thinking-tokens', thinking_tokens.to_s]) unless thinking_tokens.nil?
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module ClaudeAgentSDK
|
|
4
4
|
# Type constants for permission modes
|
|
5
|
-
PERMISSION_MODES = %w[default acceptEdits plan bypassPermissions].freeze
|
|
5
|
+
PERMISSION_MODES = %w[default acceptEdits plan bypassPermissions dontAsk auto].freeze
|
|
6
6
|
|
|
7
7
|
# Type constants for setting sources
|
|
8
8
|
SETTING_SOURCES = %w[user project local].freeze
|
|
@@ -121,14 +121,20 @@ module ClaudeAgentSDK
|
|
|
121
121
|
|
|
122
122
|
# Assistant message with content blocks
|
|
123
123
|
class AssistantMessage
|
|
124
|
-
attr_accessor :content, :model, :parent_tool_use_id, :error, :usage
|
|
124
|
+
attr_accessor :content, :model, :parent_tool_use_id, :error, :usage,
|
|
125
|
+
:message_id, :stop_reason, :session_id, :uuid
|
|
125
126
|
|
|
126
|
-
def initialize(content:, model:, parent_tool_use_id: nil, error: nil, usage: nil
|
|
127
|
+
def initialize(content:, model:, parent_tool_use_id: nil, error: nil, usage: nil,
|
|
128
|
+
message_id: nil, stop_reason: nil, session_id: nil, uuid: nil)
|
|
127
129
|
@content = content
|
|
128
130
|
@model = model
|
|
129
131
|
@parent_tool_use_id = parent_tool_use_id
|
|
130
132
|
@error = error # One of: authentication_failed, billing_error, rate_limit, invalid_request, server_error, unknown
|
|
131
133
|
@usage = usage # Token usage info from the API response
|
|
134
|
+
@message_id = message_id # Unique message identifier from the API (message.id)
|
|
135
|
+
@stop_reason = stop_reason # Why the assistant stopped (e.g., "end_turn", "max_tokens")
|
|
136
|
+
@session_id = session_id # Session the message belongs to
|
|
137
|
+
@uuid = uuid # Unique message UUID in the session transcript
|
|
132
138
|
end
|
|
133
139
|
end
|
|
134
140
|
|
|
@@ -597,16 +603,25 @@ module ClaudeAgentSDK
|
|
|
597
603
|
|
|
598
604
|
# Agent definition configuration
|
|
599
605
|
class AgentDefinition
|
|
600
|
-
attr_accessor :description, :prompt, :tools, :model, :skills, :memory, :mcp_servers
|
|
606
|
+
attr_accessor :description, :prompt, :tools, :disallowed_tools, :model, :skills, :memory, :mcp_servers,
|
|
607
|
+
:initial_prompt, :max_turns, :background, :effort, :permission_mode
|
|
601
608
|
|
|
602
|
-
def initialize(description:, prompt:, tools: nil,
|
|
609
|
+
def initialize(description:, prompt:, tools: nil, disallowed_tools: nil, model: nil, skills: nil,
|
|
610
|
+
memory: nil, mcp_servers: nil, initial_prompt: nil, max_turns: nil,
|
|
611
|
+
background: nil, effort: nil, permission_mode: nil)
|
|
603
612
|
@description = description
|
|
604
613
|
@prompt = prompt
|
|
605
614
|
@tools = tools
|
|
615
|
+
@disallowed_tools = disallowed_tools # Array of tool names to disallow
|
|
606
616
|
@model = model
|
|
607
617
|
@skills = skills # Array of skill names
|
|
608
618
|
@memory = memory # One of: 'user', 'project', 'local'
|
|
609
619
|
@mcp_servers = mcp_servers # Array of server names or config hashes
|
|
620
|
+
@initial_prompt = initial_prompt # Initial prompt sent when agent starts
|
|
621
|
+
@max_turns = max_turns # Maximum conversation turns for the agent
|
|
622
|
+
@background = background # Whether this agent runs in background
|
|
623
|
+
@effort = effort # "low", "medium", "high", "max", or Integer
|
|
624
|
+
@permission_mode = permission_mode # Permission mode for the agent
|
|
610
625
|
end
|
|
611
626
|
end
|
|
612
627
|
|
|
@@ -660,11 +675,13 @@ module ClaudeAgentSDK
|
|
|
660
675
|
|
|
661
676
|
# Tool permission context
|
|
662
677
|
class ToolPermissionContext
|
|
663
|
-
attr_accessor :signal, :suggestions
|
|
678
|
+
attr_accessor :signal, :suggestions, :tool_use_id, :agent_id
|
|
664
679
|
|
|
665
|
-
def initialize(signal: nil, suggestions: [])
|
|
680
|
+
def initialize(signal: nil, suggestions: [], tool_use_id: nil, agent_id: nil)
|
|
666
681
|
@signal = signal
|
|
667
682
|
@suggestions = suggestions
|
|
683
|
+
@tool_use_id = tool_use_id # Unique ID for this tool call within the assistant message
|
|
684
|
+
@agent_id = agent_id # Sub-agent ID if running within an agent context
|
|
668
685
|
end
|
|
669
686
|
end
|
|
670
687
|
|
|
@@ -1678,6 +1695,44 @@ module ClaudeAgentSDK
|
|
|
1678
1695
|
end
|
|
1679
1696
|
end
|
|
1680
1697
|
|
|
1698
|
+
# Result of a session fork operation
|
|
1699
|
+
class ForkSessionResult
|
|
1700
|
+
attr_accessor :session_id
|
|
1701
|
+
|
|
1702
|
+
def initialize(session_id:)
|
|
1703
|
+
@session_id = session_id
|
|
1704
|
+
end
|
|
1705
|
+
end
|
|
1706
|
+
|
|
1707
|
+
# API-side task budget in tokens.
|
|
1708
|
+
# When set, the model is made aware of its remaining token budget so it can
|
|
1709
|
+
# pace tool use and wrap up before the limit.
|
|
1710
|
+
class TaskBudget
|
|
1711
|
+
attr_accessor :total
|
|
1712
|
+
|
|
1713
|
+
def initialize(total:)
|
|
1714
|
+
@total = total
|
|
1715
|
+
end
|
|
1716
|
+
|
|
1717
|
+
def to_h
|
|
1718
|
+
{ total: @total }
|
|
1719
|
+
end
|
|
1720
|
+
end
|
|
1721
|
+
|
|
1722
|
+
# System prompt file configuration — loads system prompt from a file path
|
|
1723
|
+
class SystemPromptFile
|
|
1724
|
+
attr_accessor :type, :path
|
|
1725
|
+
|
|
1726
|
+
def initialize(path:)
|
|
1727
|
+
@type = 'file'
|
|
1728
|
+
@path = path
|
|
1729
|
+
end
|
|
1730
|
+
|
|
1731
|
+
def to_h
|
|
1732
|
+
{ type: @type, path: @path }
|
|
1733
|
+
end
|
|
1734
|
+
end
|
|
1735
|
+
|
|
1681
1736
|
# System prompt preset configuration
|
|
1682
1737
|
class SystemPromptPreset
|
|
1683
1738
|
attr_accessor :type, :preset, :append
|
|
@@ -1712,7 +1767,7 @@ module ClaudeAgentSDK
|
|
|
1712
1767
|
# Claude Agent Options for configuring queries
|
|
1713
1768
|
class ClaudeAgentOptions
|
|
1714
1769
|
attr_accessor :allowed_tools, :system_prompt, :mcp_servers, :permission_mode,
|
|
1715
|
-
:continue_conversation, :resume, :max_turns, :disallowed_tools,
|
|
1770
|
+
:continue_conversation, :resume, :session_id, :max_turns, :disallowed_tools,
|
|
1716
1771
|
:model, :permission_prompt_tool_name, :cwd, :cli_path, :settings,
|
|
1717
1772
|
:add_dirs, :env, :extra_args, :max_buffer_size, :stderr,
|
|
1718
1773
|
:can_use_tool, :hooks, :user, :include_partial_messages,
|
|
@@ -1720,7 +1775,7 @@ module ClaudeAgentSDK
|
|
|
1720
1775
|
:output_format, :max_budget_usd, :max_thinking_tokens,
|
|
1721
1776
|
:fallback_model, :plugins, :debug_stderr,
|
|
1722
1777
|
:betas, :tools, :sandbox, :enable_file_checkpointing, :append_allowed_tools,
|
|
1723
|
-
:thinking, :effort, :bare, :observers
|
|
1778
|
+
:thinking, :effort, :bare, :observers, :task_budget
|
|
1724
1779
|
|
|
1725
1780
|
# Non-nil defaults for options that need them.
|
|
1726
1781
|
# Keys absent from here default to nil.
|
|
@@ -1783,14 +1838,15 @@ module ClaudeAgentSDK
|
|
|
1783
1838
|
|
|
1784
1839
|
# SDK MCP Tool definition
|
|
1785
1840
|
class SdkMcpTool
|
|
1786
|
-
attr_accessor :name, :description, :input_schema, :handler, :annotations
|
|
1841
|
+
attr_accessor :name, :description, :input_schema, :handler, :annotations, :meta
|
|
1787
1842
|
|
|
1788
|
-
def initialize(name:, description:, input_schema:, handler:, annotations: nil)
|
|
1843
|
+
def initialize(name:, description:, input_schema:, handler:, annotations: nil, meta: nil)
|
|
1789
1844
|
@name = name
|
|
1790
1845
|
@description = description
|
|
1791
1846
|
@input_schema = input_schema
|
|
1792
1847
|
@handler = handler
|
|
1793
1848
|
@annotations = annotations # MCP tool annotations (e.g., { title: '...', readOnlyHint: true })
|
|
1849
|
+
@meta = meta # MCP _meta field (e.g., { 'anthropic/maxResultSizeChars' => 100000 })
|
|
1794
1850
|
end
|
|
1795
1851
|
end
|
|
1796
1852
|
|
data/lib/claude_agent_sdk.rb
CHANGED
|
@@ -82,10 +82,11 @@ module ClaudeAgentSDK
|
|
|
82
82
|
# List sessions for a directory (or all sessions)
|
|
83
83
|
# @param directory [String, nil] Working directory to list sessions for
|
|
84
84
|
# @param limit [Integer, nil] Maximum number of sessions to return
|
|
85
|
+
# @param offset [Integer] Number of sessions to skip (for pagination)
|
|
85
86
|
# @param include_worktrees [Boolean] Whether to include git worktree sessions
|
|
86
87
|
# @return [Array<SDKSessionInfo>] Sessions sorted by last_modified descending
|
|
87
|
-
def self.list_sessions(directory: nil, limit: nil, include_worktrees: true)
|
|
88
|
-
Sessions.list_sessions(directory: directory, limit: limit, include_worktrees: include_worktrees)
|
|
88
|
+
def self.list_sessions(directory: nil, limit: nil, offset: 0, include_worktrees: true)
|
|
89
|
+
Sessions.list_sessions(directory: directory, limit: limit, offset: offset, include_worktrees: include_worktrees)
|
|
89
90
|
end
|
|
90
91
|
|
|
91
92
|
# Read metadata for a single session by ID (no full directory scan)
|
|
@@ -122,6 +123,24 @@ module ClaudeAgentSDK
|
|
|
122
123
|
SessionMutations.tag_session(session_id: session_id, tag: tag, directory: directory)
|
|
123
124
|
end
|
|
124
125
|
|
|
126
|
+
# Delete a session by removing its JSONL file (hard delete).
|
|
127
|
+
# @param session_id [String] UUID of the session to delete
|
|
128
|
+
# @param directory [String, nil] Project directory path
|
|
129
|
+
def self.delete_session(session_id:, directory: nil)
|
|
130
|
+
SessionMutations.delete_session(session_id: session_id, directory: directory)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Fork a session into a new branch with fresh UUIDs.
|
|
134
|
+
# @param session_id [String] UUID of the session to fork
|
|
135
|
+
# @param directory [String, nil] Project directory path
|
|
136
|
+
# @param up_to_message_id [String, nil] Truncate the fork at this message UUID
|
|
137
|
+
# @param title [String, nil] Custom title for the fork
|
|
138
|
+
# @return [ForkSessionResult] Result containing the new session ID
|
|
139
|
+
def self.fork_session(session_id:, directory: nil, up_to_message_id: nil, title: nil)
|
|
140
|
+
SessionMutations.fork_session(session_id: session_id, directory: directory,
|
|
141
|
+
up_to_message_id: up_to_message_id, title: title)
|
|
142
|
+
end
|
|
143
|
+
|
|
125
144
|
def self.query(prompt:, options: nil, &block)
|
|
126
145
|
return enum_for(:query, prompt: prompt, options: options) unless block
|
|
127
146
|
|
|
@@ -455,6 +474,15 @@ module ClaudeAgentSDK
|
|
|
455
474
|
@query_handler&.instance_variable_get(:@initialization_result)
|
|
456
475
|
end
|
|
457
476
|
|
|
477
|
+
# Get a breakdown of current context window usage by category.
|
|
478
|
+
# Returns token counts per category (system prompt, tools, messages, etc.),
|
|
479
|
+
# total/max tokens, model info, MCP tools, memory files, and more.
|
|
480
|
+
# @return [Hash] Context usage response
|
|
481
|
+
def get_context_usage
|
|
482
|
+
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
483
|
+
@query_handler.get_context_usage
|
|
484
|
+
end
|
|
485
|
+
|
|
458
486
|
# Get current MCP server connection status (only works with streaming mode)
|
|
459
487
|
# @return [Hash] MCP status information, including mcpServers list
|
|
460
488
|
def get_mcp_status
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04-
|
|
10
|
+
date: 2026-04-07 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: async
|