claude-agent-sdk 0.31.0 → 0.33.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 +32 -0
- data/README.md +110 -225
- data/docs/cli-installer.md +57 -0
- data/docs/client.md +4 -0
- data/docs/configuration.md +55 -0
- data/docs/hooks-and-permissions.md +66 -2
- data/docs/sessions.md +28 -0
- data/docs/subagents.md +198 -0
- data/docs/types.md +37 -5
- data/lib/claude_agent_sdk/cancellation_signal.rb +28 -0
- data/lib/claude_agent_sdk/command_builder.rb +18 -0
- data/lib/claude_agent_sdk/message_parser.rb +3 -1
- data/lib/claude_agent_sdk/query.rb +111 -21
- data/lib/claude_agent_sdk/sessions.rb +37 -0
- data/lib/claude_agent_sdk/types.rb +259 -14
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/claude_agent_sdk.rb +68 -2
- metadata +5 -2
|
@@ -421,12 +421,51 @@ module ClaudeAgentSDK
|
|
|
421
421
|
# Task started system message (subagent/background task started)
|
|
422
422
|
class TaskStartedMessage < SystemMessage
|
|
423
423
|
attr_accessor :task_id, :description, :uuid, :session_id, :tool_use_id, :task_type,
|
|
424
|
-
:workflow_name, :prompt
|
|
424
|
+
:workflow_name, :prompt,
|
|
425
|
+
:subagent_type # Subagent type, for Task/Agent tool subagents; nil otherwise
|
|
426
|
+
|
|
427
|
+
# Whether the task was registered in the background (`true`) or in the
|
|
428
|
+
# foreground with the spawning tool call blocking on it (`false`). `nil`
|
|
429
|
+
# means the CLI did not say (the field is optional, and only set for
|
|
430
|
+
# `local_agent` and `local_bash` tasks) — so test `== false`, never
|
|
431
|
+
# falsiness, to detect a foreground/blocking task. A resumed subagent is
|
|
432
|
+
# always registered in the background. A later move to the background does
|
|
433
|
+
# not re-emit task_started; it arrives as {TaskUpdatedMessage#is_backgrounded}.
|
|
434
|
+
#
|
|
435
|
+
# @return [Boolean, nil]
|
|
436
|
+
attr_accessor :is_backgrounded
|
|
437
|
+
|
|
438
|
+
# Nesting depth of a spawned subagent (`local_agent`) task: 1 for a
|
|
439
|
+
# top-level spawn, N+1 when spawned from inside a depth-N agent. `nil` on
|
|
440
|
+
# other task types and on CLIs that do not report it.
|
|
441
|
+
#
|
|
442
|
+
# @return [Integer, nil]
|
|
443
|
+
attr_accessor :spawn_depth
|
|
444
|
+
|
|
445
|
+
# Display flags, passed through for the host to act on — the SDK never
|
|
446
|
+
# filters frames or computes activity from them. Both are optional
|
|
447
|
+
# Booleans: `nil` when absent, an explicit `false` preserved.
|
|
448
|
+
#
|
|
449
|
+
# - `skip_transcript`: an ambient/housekeeping task. Hide it from the
|
|
450
|
+
# inline transcript; it may still appear in a tasks panel.
|
|
451
|
+
# - `ambient`: true for tasks that are not activity — every
|
|
452
|
+
# `skip_transcript` task, plus every live-update watcher (requested or
|
|
453
|
+
# auto-started). Exclude these from activity indicators.
|
|
454
|
+
#
|
|
455
|
+
# @return [Boolean, nil]
|
|
456
|
+
attr_accessor :skip_transcript, :ambient
|
|
425
457
|
end
|
|
426
458
|
|
|
427
|
-
# Task progress system message (periodic update from a running task)
|
|
459
|
+
# Task progress system message (periodic update from a running task).
|
|
460
|
+
#
|
|
461
|
+
# `summary` is an optional one-line status for the task's row — `nil` on any
|
|
462
|
+
# frame that lacks one. For a `local_agent` task it is the model-generated
|
|
463
|
+
# progress summary, which the CLI produces only while generation is enabled
|
|
464
|
+
# (see {ClaudeAgentOptions#agent_progress_summaries}); for a backgrounded
|
|
465
|
+
# `mcp_task` it is the MCP server's own status message and needs no option.
|
|
428
466
|
class TaskProgressMessage < SystemMessage
|
|
429
|
-
attr_accessor :task_id, :description, :usage, :uuid, :session_id, :tool_use_id, :last_tool_name, :summary
|
|
467
|
+
attr_accessor :task_id, :description, :usage, :uuid, :session_id, :tool_use_id, :last_tool_name, :summary,
|
|
468
|
+
:subagent_type # Subagent type, for Task/Agent tool subagents; nil otherwise
|
|
430
469
|
end
|
|
431
470
|
|
|
432
471
|
# Task notification system message (task completed/failed/stopped).
|
|
@@ -437,6 +476,42 @@ module ClaudeAgentSDK
|
|
|
437
476
|
# should clear them on a terminal status from *either* message.
|
|
438
477
|
class TaskNotificationMessage < SystemMessage
|
|
439
478
|
attr_accessor :task_id, :status, :output_file, :summary, :uuid, :session_id, :tool_use_id, :usage
|
|
479
|
+
|
|
480
|
+
# Machine-readable cause, set only when the task did not end through an
|
|
481
|
+
# ordinary completion, failure, or stop. The one known value is
|
|
482
|
+
# `'worker_restart'` (the worker process restarted and the resumed process
|
|
483
|
+
# found the task orphaned; always with status `'stopped'`). Documentation,
|
|
484
|
+
# not validation: newer CLIs may add values.
|
|
485
|
+
#
|
|
486
|
+
# @return [String, nil]
|
|
487
|
+
attr_accessor :reason
|
|
488
|
+
|
|
489
|
+
# For a backgrounded MCP task (`task_type: 'mcp_task'`) that completed: the
|
|
490
|
+
# `resource_link` content blocks of its final result — the files it
|
|
491
|
+
# returned by reference. A backgrounded task's tool_result is placeholder
|
|
492
|
+
# text, so this is where a host learns which files the call produced; join
|
|
493
|
+
# to the originating call via `tool_use_id`. `nil` when the result had
|
|
494
|
+
# none or the task is any other type.
|
|
495
|
+
#
|
|
496
|
+
# Passed through from the CLI untouched, so each element is a Hash whose
|
|
497
|
+
# keys are **Symbols with the wire spelling preserved**: `:uri` and `:name`
|
|
498
|
+
# (Strings, always present), and optionally `:title`, `:description`,
|
|
499
|
+
# `:mimeType` (camelCase — a `:mime_type` lookup returns nil), `:size` (a
|
|
500
|
+
# Number, not necessarily an Integer), `:annotations` (a Hash of arbitrary
|
|
501
|
+
# values). Elements carry no `type: 'resource_link'` discriminator. The CLI
|
|
502
|
+
# describes its own output as at most 50 links / 64 KiB serialized; that is
|
|
503
|
+
# a producer-side note, and the SDK neither enforces nor truncates.
|
|
504
|
+
#
|
|
505
|
+
# @return [Array<Hash{Symbol => Object}>, nil]
|
|
506
|
+
attr_accessor :resource_links
|
|
507
|
+
|
|
508
|
+
# Display flags with the same meaning as on {TaskStartedMessage}:
|
|
509
|
+
# `skip_transcript` (hide from the inline transcript) and `ambient` (not
|
|
510
|
+
# activity — exclude from activity indicators). Optional Booleans: `nil`
|
|
511
|
+
# when absent, an explicit `false` preserved. The SDK does not act on them.
|
|
512
|
+
#
|
|
513
|
+
# @return [Boolean, nil]
|
|
514
|
+
attr_accessor :skip_transcript, :ambient
|
|
440
515
|
end
|
|
441
516
|
|
|
442
517
|
# Task updated system message (background task lifecycle state change).
|
|
@@ -455,18 +530,105 @@ module ClaudeAgentSDK
|
|
|
455
530
|
# or absent patch falls back to {}; and `task_id` defaults to "" (never nil,
|
|
456
531
|
# matching the Python SDK) so consumers can rely on it always being a String.
|
|
457
532
|
# The full patch is preserved on `#patch` for callers that need more than the
|
|
458
|
-
#
|
|
533
|
+
# derived readers.
|
|
534
|
+
#
|
|
535
|
+
# A patch carries only the fields that changed, so every derived reader is
|
|
536
|
+
# `nil` when its field is absent. That matters most for `is_backgrounded`:
|
|
537
|
+
# `true` means the task just moved to the background (e.g. after
|
|
538
|
+
# {Client#background_tasks}), while `nil` means "this patch does not mention
|
|
539
|
+
# it" — not "foreground". Merge patches into your own task map rather than
|
|
540
|
+
# reading any single one as the task's full state.
|
|
459
541
|
class TaskUpdatedMessage < SystemMessage
|
|
460
|
-
attr_accessor :task_id, :patch, :status, :uuid, :session_id
|
|
542
|
+
attr_accessor :task_id, :patch, :status, :uuid, :session_id,
|
|
543
|
+
:description, # patch[:description] — String, nil when unchanged
|
|
544
|
+
:error, # patch[:error] — String, nil when unchanged
|
|
545
|
+
:end_time, # patch[:end_time] — Integer (epoch ms), nil when unchanged
|
|
546
|
+
:total_paused_ms, # patch[:total_paused_ms] — Integer, nil when unchanged
|
|
547
|
+
:is_backgrounded # patch[:is_backgrounded] — true/false, nil when unchanged
|
|
461
548
|
|
|
462
549
|
def initialize(attributes = {})
|
|
463
550
|
super
|
|
464
551
|
@task_id ||= ''
|
|
465
552
|
@patch = {} unless @patch.is_a?(Hash)
|
|
466
|
-
@status =
|
|
553
|
+
@status = patch_value(:status)
|
|
554
|
+
@description = patch_value(:description)
|
|
555
|
+
@error = patch_value(:error)
|
|
556
|
+
@end_time = patch_value(:end_time)
|
|
557
|
+
@total_paused_ms = patch_value(:total_paused_ms)
|
|
558
|
+
@is_backgrounded = patch_value(:is_backgrounded)
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
private
|
|
562
|
+
|
|
563
|
+
# The parser always hands over a symbol-keyed patch; a hand-built message
|
|
564
|
+
# may use string keys. `fetch` with a block (not `||`) keeps an explicit
|
|
565
|
+
# `false` from falling through to the string-key lookup's nil.
|
|
566
|
+
def patch_value(key)
|
|
567
|
+
@patch.fetch(key) { @patch[key.to_s] }
|
|
467
568
|
end
|
|
468
569
|
end
|
|
469
570
|
|
|
571
|
+
# Background tasks changed system message: the full set of live background
|
|
572
|
+
# tasks, emitted whenever membership changes (start, completion, kill, a
|
|
573
|
+
# foreground agent being backgrounded) or an entry's `ambient` flag flips.
|
|
574
|
+
#
|
|
575
|
+
# A **level** signal with **REPLACE semantics** — `tasks` is every live
|
|
576
|
+
# background task after the change, so swap your set for each payload rather
|
|
577
|
+
# than pairing task_started / task_notification edges; a missed edge then
|
|
578
|
+
# cannot wedge a stale "running" indicator. Per the CLI's contract:
|
|
579
|
+
#
|
|
580
|
+
# - Ordering relative to the edge frames for the same transition is
|
|
581
|
+
# unspecified, and the payload carries ids only — do not correlate it
|
|
582
|
+
# with the edge stream.
|
|
583
|
+
# - The level is per-process: nothing is emitted at startup, so reset to the
|
|
584
|
+
# empty set whenever the session's CLI process (re)starts.
|
|
585
|
+
# - `tasks: []` is an authoritative empty snapshot for that process, not a
|
|
586
|
+
# missing value.
|
|
587
|
+
# - A repeated `initialize` on an already-running process is answered with a
|
|
588
|
+
# snapshot of the current set (even an empty one) right behind its success
|
|
589
|
+
# response; older CLIs send nothing there. This SDK initializes once per
|
|
590
|
+
# connection, so that only matters to custom transports that reconnect.
|
|
591
|
+
# - It covers *background* tasks only. A foreground subagent (the spawning
|
|
592
|
+
# tool call still blocking) is not listed until it is backgrounded.
|
|
593
|
+
#
|
|
594
|
+
# `tasks` is passed through untouched: an Array of symbol-keyed Hashes
|
|
595
|
+
# `{ task_id:, task_type:, description:, ambient: }`. `:ambient` is optional;
|
|
596
|
+
# true marks tasks that are not activity (housekeeping, live-update
|
|
597
|
+
# watchers), which hosts should exclude from activity indicators.
|
|
598
|
+
#
|
|
599
|
+
# The SDK itself deliberately does not consume this frame for its own
|
|
600
|
+
# stdin-close bookkeeping; it is typed purely for consumers.
|
|
601
|
+
class BackgroundTasksChangedMessage < SystemMessage
|
|
602
|
+
attr_accessor :tasks, :uuid, :session_id
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
# Permission denied system message: a tool call was auto-denied without an
|
|
606
|
+
# interactive permission prompt (auto-mode classifier, dontAsk mode,
|
|
607
|
+
# headless-agent auto-deny, a deny rule, or — with no can_use_tool callback —
|
|
608
|
+
# an "ask" decision that nobody can answer). The "ask" path with a callback
|
|
609
|
+
# surfaces through can_use_tool instead.
|
|
610
|
+
#
|
|
611
|
+
# **Best-effort advisory, not a complete denial feed**:
|
|
612
|
+
# {ResultMessage#permission_denials} is the authoritative record. In rare
|
|
613
|
+
# races a booked denial has no frame, or a frame has no booked denial — so do
|
|
614
|
+
# not derive counts or permission state from this stream. Not covered at all:
|
|
615
|
+
# PreToolUse hook denies, deny-rule overrides of a hook's allow/ask decision,
|
|
616
|
+
# Read/Edit/Write calls refused by a path-scoped deny rule (all resolve before
|
|
617
|
+
# the permission check), and the MCP `--permission-prompt-tool` surface.
|
|
618
|
+
#
|
|
619
|
+
# `agent_id` is a subagent id for host-side routing; it is NOT a permission
|
|
620
|
+
# `request_id`, and this message is not a pending permission request.
|
|
621
|
+
# `decision_reason_type` is an open String (the values below are examples,
|
|
622
|
+
# not an enum). The CLI's `decision_reason_code` is marked internal and is
|
|
623
|
+
# left to `#data` with no stability promise.
|
|
624
|
+
class PermissionDeniedMessage < SystemMessage
|
|
625
|
+
attr_accessor :uuid, :session_id, :tool_name, :tool_use_id,
|
|
626
|
+
:agent_id, # Subagent ID when the denied call originated inside a subagent; nil otherwise
|
|
627
|
+
:decision_reason_type, # Open String, e.g. "classifier", "asyncAgent", "mode", "rule"; nil when not reported
|
|
628
|
+
:decision_reason, # Human-readable reason from the deciding component; nil when not reported
|
|
629
|
+
:message # The rejection message returned to the model in the tool_result
|
|
630
|
+
end
|
|
631
|
+
|
|
470
632
|
# Result message with cost and usage information
|
|
471
633
|
class ResultMessage < Type
|
|
472
634
|
# model_usage maps model name => per-model usage Hash, passed through
|
|
@@ -742,8 +904,10 @@ module ClaudeAgentSDK
|
|
|
742
904
|
# `decision_reason`) so the SDK consumer can render the same prompt UI
|
|
743
905
|
# the CLI would have shown. Older fields (`signal`, `suggestions`,
|
|
744
906
|
# `tool_use_id`, `agent_id`) remain unchanged.
|
|
907
|
+
# `signal` is a CancellationSignal on dispatched callbacks; `request_id`
|
|
908
|
+
# identifies this permission request (distinct from the tool invocation).
|
|
745
909
|
class ToolPermissionContext < Type
|
|
746
|
-
attr_accessor :signal, :suggestions, :tool_use_id, :agent_id,
|
|
910
|
+
attr_accessor :signal, :request_id, :suggestions, :tool_use_id, :agent_id,
|
|
747
911
|
:title, :display_name, :description,
|
|
748
912
|
:blocked_path, :decision_reason
|
|
749
913
|
|
|
@@ -786,9 +950,10 @@ module ClaudeAgentSDK
|
|
|
786
950
|
end
|
|
787
951
|
end
|
|
788
952
|
|
|
789
|
-
# Hook context passed to hook callbacks
|
|
953
|
+
# Hook context passed to hook callbacks. Dispatched hooks receive the control
|
|
954
|
+
# request ID and a CancellationSignal (including on HookMatcher timeout).
|
|
790
955
|
class HookContext < Type
|
|
791
|
-
attr_accessor :signal
|
|
956
|
+
attr_accessor :signal, :request_id
|
|
792
957
|
end
|
|
793
958
|
|
|
794
959
|
# Base hook input with common fields
|
|
@@ -828,8 +993,11 @@ module ClaudeAgentSDK
|
|
|
828
993
|
end
|
|
829
994
|
|
|
830
995
|
# Stop hook input
|
|
996
|
+
# Snapshot arrays are passed through unchanged. nil means unavailable;
|
|
997
|
+
# [] means the CLI provided an empty snapshot. They cover the parent
|
|
998
|
+
# session's background work, NOT all foreground and background agents.
|
|
831
999
|
class StopHookInput < BaseHookInput
|
|
832
|
-
attr_accessor :stop_hook_active, :last_assistant_message
|
|
1000
|
+
attr_accessor :stop_hook_active, :last_assistant_message, :background_tasks, :session_crons
|
|
833
1001
|
|
|
834
1002
|
def initialize(attributes = {})
|
|
835
1003
|
super
|
|
@@ -841,7 +1009,7 @@ module ClaudeAgentSDK
|
|
|
841
1009
|
# SubagentStop hook input
|
|
842
1010
|
class SubagentStopHookInput < BaseHookInput
|
|
843
1011
|
attr_accessor :stop_hook_active, :agent_id, :agent_transcript_path, :agent_type,
|
|
844
|
-
:last_assistant_message
|
|
1012
|
+
:last_assistant_message, :background_tasks, :session_crons
|
|
845
1013
|
|
|
846
1014
|
def initialize(attributes = {})
|
|
847
1015
|
super
|
|
@@ -1640,10 +1808,22 @@ module ClaudeAgentSDK
|
|
|
1640
1808
|
end
|
|
1641
1809
|
end
|
|
1642
1810
|
|
|
1643
|
-
# System prompt preset configuration
|
|
1811
|
+
# System prompt preset configuration.
|
|
1812
|
+
#
|
|
1813
|
+
# +snapshot+ controls whether the session keeps the system prompt it
|
|
1814
|
+
# recorded on its first request. When true, every later request (including
|
|
1815
|
+
# after resume) sends the recorded prompt, so a changed +append+ has no
|
|
1816
|
+
# effect until the session is compacted or a new session starts. When
|
|
1817
|
+
# false, the prompt is rebuilt on every request — useful while iterating on
|
|
1818
|
+
# +append+ text across calls that resume the same session. When nil
|
|
1819
|
+
# (omitted), the CLI treats it as true, except in bare mode (+--bare+),
|
|
1820
|
+
# where it acts as false. Sent on the control-protocol +initialize+ request
|
|
1821
|
+
# (never as a CLI flag); requires Claude Code CLI 2.1.257 or later, and
|
|
1822
|
+
# before 2.1.265 a session with an +append+ prompt recorded it only when
|
|
1823
|
+
# +snapshot+ was true. Older CLIs silently ignore it.
|
|
1644
1824
|
class SystemPromptPreset < Type
|
|
1645
1825
|
attr_reader :type
|
|
1646
|
-
attr_accessor :preset, :append, :exclude_dynamic_sections
|
|
1826
|
+
attr_accessor :preset, :append, :exclude_dynamic_sections, :snapshot
|
|
1647
1827
|
|
|
1648
1828
|
def initialize(attributes = {})
|
|
1649
1829
|
super
|
|
@@ -1654,6 +1834,27 @@ module ClaudeAgentSDK
|
|
|
1654
1834
|
result = { type: @type, preset: @preset }
|
|
1655
1835
|
result[:append] = @append if @append
|
|
1656
1836
|
result[:exclude_dynamic_sections] = @exclude_dynamic_sections unless @exclude_dynamic_sections.nil?
|
|
1837
|
+
result[:snapshot] = @snapshot unless @snapshot.nil?
|
|
1838
|
+
result
|
|
1839
|
+
end
|
|
1840
|
+
end
|
|
1841
|
+
|
|
1842
|
+
# Custom system prompt configuration — the object form of passing a String
|
|
1843
|
+
# as +system_prompt+. Reaches the CLI the same way a String does
|
|
1844
|
+
# (+--system-prompt <prompt>+); the object form exists so +snapshot+ can be
|
|
1845
|
+
# set alongside it (see SystemPromptPreset#snapshot for its semantics).
|
|
1846
|
+
class SystemPromptCustom < Type
|
|
1847
|
+
attr_reader :type
|
|
1848
|
+
attr_accessor :prompt, :snapshot
|
|
1849
|
+
|
|
1850
|
+
def initialize(attributes = {})
|
|
1851
|
+
super
|
|
1852
|
+
@type = 'custom'
|
|
1853
|
+
end
|
|
1854
|
+
|
|
1855
|
+
def to_h
|
|
1856
|
+
result = { type: @type, prompt: @prompt }
|
|
1857
|
+
result[:snapshot] = @snapshot unless @snapshot.nil?
|
|
1657
1858
|
result
|
|
1658
1859
|
end
|
|
1659
1860
|
end
|
|
@@ -1855,6 +2056,34 @@ module ClaudeAgentSDK
|
|
|
1855
2056
|
@forward_subagent_text = coerce_boolean(value)
|
|
1856
2057
|
end
|
|
1857
2058
|
|
|
2059
|
+
# Request model-generated progress summaries for subagent (`local_agent`)
|
|
2060
|
+
# tasks. `true` *requests* generation: while the CLI has it enabled, a
|
|
2061
|
+
# subagent's {TaskProgressMessage#summary} **may** carry a one-line status.
|
|
2062
|
+
# `summary` stays optional on the wire even then — not every progress
|
|
2063
|
+
# frame has one — so read it nil-safely. `false` / `nil` do not enable
|
|
2064
|
+
# generation; they do not promise that `summary` is absent (a process that
|
|
2065
|
+
# already enabled summaries keeps them, and a backgrounded `mcp_task`
|
|
2066
|
+
# reports its own status there regardless of this option). Matches the
|
|
2067
|
+
# CLI's `agentProgressSummaries` initialize field.
|
|
2068
|
+
#
|
|
2069
|
+
# Defaults to `nil` (unset): the key is omitted from the `initialize`
|
|
2070
|
+
# control request. `true` and `false` are forwarded verbatim. This is an
|
|
2071
|
+
# enable switch, not a live toggle: CLI 2.1.278 only acts on a truthy
|
|
2072
|
+
# value, so `false` is schema-valid but equivalent to leaving the option
|
|
2073
|
+
# unset — it does not switch summaries off on a process that already
|
|
2074
|
+
# enabled them. Both {ClaudeAgentSDK.query} and {Client} run the control
|
|
2075
|
+
# protocol, so the option applies to either entry point.
|
|
2076
|
+
#
|
|
2077
|
+
# Assigning coerces to a Boolean and keeps `nil` as `nil`.
|
|
2078
|
+
#
|
|
2079
|
+
# @return [Boolean, nil]
|
|
2080
|
+
attr_reader :agent_progress_summaries
|
|
2081
|
+
|
|
2082
|
+
# @see #agent_progress_summaries
|
|
2083
|
+
def agent_progress_summaries=(value)
|
|
2084
|
+
@agent_progress_summaries = coerce_boolean(value)
|
|
2085
|
+
end
|
|
2086
|
+
|
|
1858
2087
|
CALLBACK_SCHEDULING_MODES = %i[thread inline].freeze
|
|
1859
2088
|
|
|
1860
2089
|
# Where user callbacks (hooks, can_use_tool, SDK MCP handlers, message
|
|
@@ -1919,6 +2148,12 @@ module ClaudeAgentSDK
|
|
|
1919
2148
|
# Merge caller-provided attributes with configured defaults.
|
|
1920
2149
|
# Only keys the caller explicitly passed are treated as overrides;
|
|
1921
2150
|
# method-signature defaults ([], {}, false) are NOT present unless the caller wrote them.
|
|
2151
|
+
#
|
|
2152
|
+
# Both sides are keyed by the option they name, not by their literal
|
|
2153
|
+
# spelling: Type accepts symbol/string and snake_case/camelCase names, so a
|
|
2154
|
+
# caller's `'permissionMode' => nil` must still inherit a configured
|
|
2155
|
+
# `permission_mode:` (and a Hash must still merge into it) rather than
|
|
2156
|
+
# riding along as a second entry that overwrites the default on assignment.
|
|
1922
2157
|
def merge_with_defaults(attributes)
|
|
1923
2158
|
return attributes unless defined?(ClaudeAgentSDK) && ClaudeAgentSDK.respond_to?(:default_options)
|
|
1924
2159
|
|
|
@@ -1929,8 +2164,10 @@ module ClaudeAgentSDK
|
|
|
1929
2164
|
# duped so per-instance mutation (options.allowed_tools << 'Bash')
|
|
1930
2165
|
# can never corrupt the global defaults; non-container leaves
|
|
1931
2166
|
# (Strings, Procs, SdkMcpServer instances) intentionally keep identity.
|
|
1932
|
-
result =
|
|
2167
|
+
result = {}
|
|
2168
|
+
defaults.each { |key, value| result[option_key(key)] = deep_dup_containers(value) }
|
|
1933
2169
|
attributes.each do |key, value|
|
|
2170
|
+
key = option_key(key)
|
|
1934
2171
|
default_val = result[key]
|
|
1935
2172
|
result[key] = if value.nil?
|
|
1936
2173
|
default_val # nil means "no preference" — keep the configured default
|
|
@@ -1943,6 +2180,14 @@ module ClaudeAgentSDK
|
|
|
1943
2180
|
result
|
|
1944
2181
|
end
|
|
1945
2182
|
|
|
2183
|
+
# The canonical Symbol for a known option, whatever its spelling. An
|
|
2184
|
+
# unknown name is returned untouched so assign_attribute's strict check
|
|
2185
|
+
# reports the typo exactly as the developer wrote it.
|
|
2186
|
+
def option_key(name)
|
|
2187
|
+
normalized = normalize_name(name)
|
|
2188
|
+
respond_to?(:"#{normalized}=") ? normalized.to_sym : name
|
|
2189
|
+
end
|
|
2190
|
+
|
|
1946
2191
|
# Recurse ONLY into Hash/Array; leaves keep object identity (observer
|
|
1947
2192
|
# factories, callbacks, SDK MCP server instances must not be duped).
|
|
1948
2193
|
# Rebuild via dup.clear (never Hash#to_h / Array#map) to preserve
|
data/lib/claude_agent_sdk.rb
CHANGED
|
@@ -109,6 +109,26 @@ module ClaudeAgentSDK
|
|
|
109
109
|
nil
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
+
# Internal: pull snapshot out of a preset or custom system prompt for the
|
|
113
|
+
# initialize request (older CLIs ignore unknown initialize fields). A
|
|
114
|
+
# String or file prompt has no snapshot, and only a genuine true/false is
|
|
115
|
+
# forwarded — `snapshot: false` is the primary use case, so the Hash lookup
|
|
116
|
+
# must not collapse it to nil. Shared by Client#connect and query().
|
|
117
|
+
def self.extract_system_prompt_snapshot(system_prompt)
|
|
118
|
+
case system_prompt
|
|
119
|
+
when SystemPromptPreset, SystemPromptCustom
|
|
120
|
+
snapshot = system_prompt.snapshot
|
|
121
|
+
return snapshot if [true, false].include?(snapshot)
|
|
122
|
+
when Hash
|
|
123
|
+
type = system_prompt[:type] || system_prompt['type']
|
|
124
|
+
if %w[preset custom].include?(type)
|
|
125
|
+
snapshot = system_prompt.fetch(:snapshot) { system_prompt['snapshot'] }
|
|
126
|
+
return snapshot if [true, false].include?(snapshot)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
nil
|
|
130
|
+
end
|
|
131
|
+
|
|
112
132
|
# Safely call a method on each observer, suppressing any errors.
|
|
113
133
|
# Each observer is invoked through FiberBoundary so that user code runs
|
|
114
134
|
# on a plain thread (no Fiber scheduler) even when called from inside
|
|
@@ -282,6 +302,15 @@ module ClaudeAgentSDK
|
|
|
282
302
|
Sessions.list_subagents(session_id: session_id, directory: directory)
|
|
283
303
|
end
|
|
284
304
|
|
|
305
|
+
# Read a subagent's optional metadata (not live status) from local disk.
|
|
306
|
+
# @param session_id [String] The parent session UUID
|
|
307
|
+
# @param agent_id [String] The subagent ID, without the agent- prefix
|
|
308
|
+
# @param directory [String, nil] Project directory to search in
|
|
309
|
+
# @return [Hash{String => Object}, nil] CLI metadata, or nil if unavailable
|
|
310
|
+
def self.get_subagent_metadata(session_id:, agent_id:, directory: nil)
|
|
311
|
+
Sessions.get_subagent_metadata(session_id: session_id, agent_id: agent_id, directory: directory)
|
|
312
|
+
end
|
|
313
|
+
|
|
285
314
|
# Read a subagent's conversation messages from local disk
|
|
286
315
|
# @param session_id [String] The session UUID
|
|
287
316
|
# @param agent_id [String] The subagent ID (without the agent- prefix)
|
|
@@ -374,6 +403,13 @@ module ClaudeAgentSDK
|
|
|
374
403
|
Sessions.list_subagents_from_store(session_store: session_store, session_id: session_id, directory: directory)
|
|
375
404
|
end
|
|
376
405
|
|
|
406
|
+
# Read the latest subagent metadata from a SessionStore, without its synthetic type marker.
|
|
407
|
+
# @return [Hash{String => Object}, nil]
|
|
408
|
+
def self.get_subagent_metadata_from_store(session_store:, session_id:, agent_id:, directory: nil)
|
|
409
|
+
Sessions.get_subagent_metadata_from_store(session_store: session_store, session_id: session_id,
|
|
410
|
+
agent_id: agent_id, directory: directory)
|
|
411
|
+
end
|
|
412
|
+
|
|
377
413
|
# Read a subagent's conversation messages from a SessionStore.
|
|
378
414
|
# @return [Array<SessionMessage>]
|
|
379
415
|
def self.get_subagent_messages_from_store(session_store:, session_id:, agent_id:, directory: nil, limit: nil,
|
|
@@ -549,8 +585,10 @@ module ClaudeAgentSDK
|
|
|
549
585
|
agents: configured_options.agents,
|
|
550
586
|
sdk_mcp_servers: sdk_mcp_servers,
|
|
551
587
|
exclude_dynamic_sections: ClaudeAgentSDK.extract_exclude_dynamic_sections(configured_options.system_prompt),
|
|
588
|
+
system_prompt_snapshot: ClaudeAgentSDK.extract_system_prompt_snapshot(configured_options.system_prompt),
|
|
552
589
|
skills: configured_options.skills,
|
|
553
590
|
forward_subagent_text: configured_options.forward_subagent_text?,
|
|
591
|
+
agent_progress_summaries: configured_options.agent_progress_summaries,
|
|
554
592
|
callback_scheduling: callback_scheduling,
|
|
555
593
|
callback_wrapper: callback_wrapper
|
|
556
594
|
)
|
|
@@ -948,6 +986,31 @@ module ClaudeAgentSDK
|
|
|
948
986
|
@query_handler.stop_task(task_id)
|
|
949
987
|
end
|
|
950
988
|
|
|
989
|
+
# Background in-flight foreground tasks (Bash commands and subagents) — the
|
|
990
|
+
# control-request equivalent of pressing Ctrl+B in the terminal. Each
|
|
991
|
+
# blocking tool call returns a "running in the background" tool_result and
|
|
992
|
+
# the turn continues; the task keeps running and emits a
|
|
993
|
+
# TaskNotificationMessage when it settles.
|
|
994
|
+
#
|
|
995
|
+
# The targeted form reports its outcome: `{ backgrounded: true }`, or
|
|
996
|
+
# `{ backgrounded: false }` — a definitive miss (no matching foreground
|
|
997
|
+
# task), so do not wait for an event after it. The all-tasks form returns
|
|
998
|
+
# `{}` and says nothing about whether any task existed. Observe
|
|
999
|
+
# TaskUpdatedMessage#is_backgrounded / BackgroundTasksChangedMessage for the
|
|
1000
|
+
# lifecycle state that follows.
|
|
1001
|
+
#
|
|
1002
|
+
# @param tool_use_id [String, nil] The id of the tool_use block that spawned
|
|
1003
|
+
# the task — NOT a task_id or agent_id. nil is the explicit all-tasks form.
|
|
1004
|
+
# Never substitute nil or '' for a per-task id you do not have yet
|
|
1005
|
+
# (TaskStartedMessage#tool_use_id is optional on the wire)
|
|
1006
|
+
# @return [Hash] `{ backgrounded: true/false }` when tool_use_id was given,
|
|
1007
|
+
# `{}` otherwise
|
|
1008
|
+
# @raise [ArgumentError] if tool_use_id is neither nil nor a non-empty String
|
|
1009
|
+
def background_tasks(tool_use_id: nil)
|
|
1010
|
+
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1011
|
+
@query_handler.background_tasks(tool_use_id: tool_use_id)
|
|
1012
|
+
end
|
|
1013
|
+
|
|
951
1014
|
# Rewind files to a previous checkpoint (v0.1.15+)
|
|
952
1015
|
# Restores file state to what it was at the given user message
|
|
953
1016
|
# Requires enable_file_checkpointing to be true in options
|
|
@@ -1064,9 +1127,10 @@ module ClaudeAgentSDK
|
|
|
1064
1127
|
# Convert hooks to internal format
|
|
1065
1128
|
hooks = convert_hooks_to_internal_format(configured_options.hooks) if configured_options.hooks
|
|
1066
1129
|
|
|
1067
|
-
# Extract exclude_dynamic_sections from
|
|
1068
|
-
# initialize request (older CLIs ignore unknown initialize fields)
|
|
1130
|
+
# Extract exclude_dynamic_sections and snapshot from the system prompt
|
|
1131
|
+
# for the initialize request (older CLIs ignore unknown initialize fields)
|
|
1069
1132
|
exclude_dynamic_sections = ClaudeAgentSDK.extract_exclude_dynamic_sections(configured_options.system_prompt)
|
|
1133
|
+
system_prompt_snapshot = ClaudeAgentSDK.extract_system_prompt_snapshot(configured_options.system_prompt)
|
|
1070
1134
|
|
|
1071
1135
|
# Create Query handler
|
|
1072
1136
|
@query_handler = Query.new(
|
|
@@ -1077,8 +1141,10 @@ module ClaudeAgentSDK
|
|
|
1077
1141
|
sdk_mcp_servers: sdk_mcp_servers,
|
|
1078
1142
|
agents: configured_options.agents,
|
|
1079
1143
|
exclude_dynamic_sections: exclude_dynamic_sections,
|
|
1144
|
+
system_prompt_snapshot: system_prompt_snapshot,
|
|
1080
1145
|
skills: configured_options.skills,
|
|
1081
1146
|
forward_subagent_text: configured_options.forward_subagent_text?,
|
|
1147
|
+
agent_progress_summaries: configured_options.agent_progress_summaries,
|
|
1082
1148
|
callback_scheduling: @callback_scheduling,
|
|
1083
1149
|
callback_wrapper: @callback_wrapper
|
|
1084
1150
|
)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.33.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: async
|
|
@@ -110,6 +110,7 @@ files:
|
|
|
110
110
|
- CHANGELOG.md
|
|
111
111
|
- LICENSE
|
|
112
112
|
- README.md
|
|
113
|
+
- docs/cli-installer.md
|
|
113
114
|
- docs/client.md
|
|
114
115
|
- docs/configuration.md
|
|
115
116
|
- docs/errors.md
|
|
@@ -118,9 +119,11 @@ files:
|
|
|
118
119
|
- docs/observability.md
|
|
119
120
|
- docs/rails.md
|
|
120
121
|
- docs/sessions.md
|
|
122
|
+
- docs/subagents.md
|
|
121
123
|
- docs/types.md
|
|
122
124
|
- lib/claude-agent-sdk.rb
|
|
123
125
|
- lib/claude_agent_sdk.rb
|
|
126
|
+
- lib/claude_agent_sdk/cancellation_signal.rb
|
|
124
127
|
- lib/claude_agent_sdk/cli_installer.rb
|
|
125
128
|
- lib/claude_agent_sdk/command_builder.rb
|
|
126
129
|
- lib/claude_agent_sdk/configuration.rb
|