claude-agent-sdk 1.1.0 → 1.2.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +10 -0
  3. data/CHANGELOG.md +90 -0
  4. data/README.md +43 -31
  5. data/docs/cli-installer.md +26 -4
  6. data/docs/client.md +29 -11
  7. data/docs/configuration.md +164 -1
  8. data/docs/errors.md +32 -2
  9. data/docs/hooks-and-permissions.md +30 -10
  10. data/docs/mcp-servers.md +30 -9
  11. data/docs/observability.md +61 -10
  12. data/docs/options.md +232 -0
  13. data/docs/rails.md +263 -18
  14. data/docs/sessions.md +40 -12
  15. data/docs/subagents.md +1 -1
  16. data/docs/types.md +100 -11
  17. data/lib/claude_agent_sdk/cli_installer.rb +140 -19
  18. data/lib/claude_agent_sdk/command_builder.rb +84 -27
  19. data/lib/claude_agent_sdk/fiber_boundary.rb +45 -2
  20. data/lib/claude_agent_sdk/instrumentation/otel.rb +90 -28
  21. data/lib/claude_agent_sdk/query.rb +228 -77
  22. data/lib/claude_agent_sdk/railtie.rb +27 -2
  23. data/lib/claude_agent_sdk/sdk_mcp_server.rb +78 -26
  24. data/lib/claude_agent_sdk/session_mutations.rb +112 -92
  25. data/lib/claude_agent_sdk/session_resume.rb +356 -39
  26. data/lib/claude_agent_sdk/session_store.rb +31 -2
  27. data/lib/claude_agent_sdk/sessions.rb +720 -138
  28. data/lib/claude_agent_sdk/subprocess_cli_transport.rb +227 -29
  29. data/lib/claude_agent_sdk/testing/session_store_conformance.rb +18 -7
  30. data/lib/claude_agent_sdk/transcript_mirror_batcher.rb +45 -37
  31. data/lib/claude_agent_sdk/transport.rb +28 -12
  32. data/lib/claude_agent_sdk/types/attributes.rb +9 -0
  33. data/lib/claude_agent_sdk/types/base.rb +85 -15
  34. data/lib/claude_agent_sdk/types/hooks.rb +73 -0
  35. data/lib/claude_agent_sdk/types/mcp.rb +37 -1
  36. data/lib/claude_agent_sdk/types/option_values.rb +186 -4
  37. data/lib/claude_agent_sdk/types/options.rb +35 -5
  38. data/lib/claude_agent_sdk/types/permissions.rb +18 -9
  39. data/lib/claude_agent_sdk/version.rb +1 -1
  40. data/lib/claude_agent_sdk.rb +94 -46
  41. data/lib/generators/claude_agent_sdk/install/templates/claude_agent_sdk.rb.tt +6 -0
  42. data/sig/claude_agent_sdk/types/hooks.rbs +6 -3
  43. data/sig/claude_agent_sdk/types/option_values.rbs +23 -6
  44. data/sig/claude_agent_sdk/types/options.rbs +11 -7
  45. data/sig/claude_agent_sdk/types/permissions.rbs +4 -2
  46. metadata +6 -4
data/docs/options.md ADDED
@@ -0,0 +1,232 @@
1
+ # Options Reference
2
+
3
+ Every attribute of `ClaudeAgentSDK::ClaudeAgentOptions`, with its type, its default, and what it turns into: a flag on the `claude` command line, a field of the control protocol's `initialize` request, a variable in the CLI's environment, or something the SDK handles on its own side. The topic guides explain how to use the larger features; this page is the complete list.
4
+
5
+ ```ruby
6
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(model: 'claude-sonnet-5', max_turns: 5)
7
+ ClaudeAgentSDK.query(prompt: 'Hello', options: options) { |message| puts message }
8
+ ```
9
+
10
+ - An unknown option name raises `ArgumentError`, in `.new`, in `#[]=` and in `#dup_with`. Names may be Symbols or Strings, snake_case or camelCase.
11
+ - `options.dup_with(max_turns: 1)` returns a changed copy and leaves the original alone.
12
+ - `ClaudeAgentSDK.configure { |config| config.default_options = { ... } }` sets defaults for every `ClaudeAgentOptions` built afterwards. A value you pass replaces the configured one, with two exceptions: `nil` keeps the configured value, and a Hash is merged into a configured Hash (that holds for every Hash-valued option, `env`, `mcp_servers`, `settings` and a Hash `system_prompt` or `output_format` included).
13
+
14
+ ## Reading the tables
15
+
16
+ - **Type** is what `.new` accepts for the option: the type of the constructor's keyword in the gem's RBS signatures (`sig/claude_agent_sdk/types/options.rbs`). Every option also accepts `nil`. `bool` is `true` or `false`. A name that starts with an underscore is an interface, so any object with the right method fits: `call` for the callbacks, `puts` for `_Puts`, `append` and `load` for `_SessionStore`.
17
+ - **Default** is what the option reads after a `ClaudeAgentOptions.new` that leaves it out, with no configured defaults. [Leaving an option out, and passing `nil`](#leaving-an-option-out-and-passing-nil) has the other cases.
18
+ - **Sent to the CLI as** names the flag, the `initialize` field (`initialize.hooks` is the `hooks` field of that request) or the environment variable. "Nothing (SDK only)" marks an option that changes only what the SDK does in your process.
19
+
20
+ ### Leaving an option out, and passing `nil`
21
+
22
+ What an option reads after `.new` depends on what you pass for it, and on whether `ClaudeAgentSDK.configure` has set a default for it:
23
+
24
+ | You pass | No default is configured for the option | A default is configured for it |
25
+ |----------|------------------------------------------|--------------------------------|
26
+ | nothing | the Default column | the configured value |
27
+ | `nil` | the Default column, except where it says "explicit `nil` stays `nil`" | the configured value |
28
+ | another value | that value | that value; a Hash is merged into a configured Hash |
29
+
30
+ The exception is every option whose default is `false`. With no default configured for it, `ClaudeAgentOptions.new(fork_session: nil).fork_session` is `nil`, not `false`. The option still acts as `false`: it adds nothing to what the SDK sends to the CLI, and its predicate (`fork_session?`) returns `false`. Only code that compares the reader's value with `false` sees the difference.
31
+
32
+ Three types in the tables are aliases:
33
+
34
+ | Alias | Stands for |
35
+ |-------|------------|
36
+ | `system_prompt_config` | `String`, `SystemPromptPreset`, `SystemPromptCustom`, `SystemPromptFile`, or the equivalent Hash (`{ type: 'preset' \| 'custom' \| 'file', ... }`) |
37
+ | `thinking_config` | `ThinkingConfigAdaptive`, `ThinkingConfigEnabled`, `ThinkingConfigDisabled`, or the equivalent Hash |
38
+ | `mcp_server_config` | `McpStdioServerConfig`, `McpSSEServerConfig`, `McpHttpServerConfig`, `McpSdkServerConfig`, or the equivalent Hash |
39
+
40
+ ## Prompt, model and limits
41
+
42
+ Guides: [Structured Output](configuration.md#structured-output), [Thinking Configuration](configuration.md#thinking-configuration), [Budget Control](configuration.md#budget-control), [Fallback Model](configuration.md#fallback-model), [Advisor Model](configuration.md#advisor-model), [Beta Features](configuration.md#beta-features).
43
+
44
+ | Option | Type | Default | Sent to the CLI as |
45
+ |--------|------|---------|--------------------|
46
+ | `system_prompt` | `system_prompt_config` | `nil` | `--system-prompt`, `--system-prompt-file` or `--append-system-prompt`, and the fields `initialize.excludeDynamicSections` and `initialize.systemPromptSnapshot`. See [System prompt forms](#system-prompt-forms) |
47
+ | `model` | `String` | `nil` | `--model` |
48
+ | `fallback_model` | `String` | `nil` | `--fallback-model` |
49
+ | `advisor_model` | `String` | `nil` | `--advisor` |
50
+ | `max_turns` | `Integer` | `nil` | `--max-turns` |
51
+ | `max_budget_usd` | `Numeric` | `nil` | `--max-budget-usd` |
52
+ | `task_budget` | `TaskBudget \| Hash[Symbol \| String, untyped]` | `nil` | `--task-budget <total>`. See [`task_budget`](#task_budget) |
53
+ | `thinking` | `thinking_config` | `nil` | `--thinking adaptive`, `--thinking disabled` or `--max-thinking-tokens <budget_tokens>`; `display:` adds `--thinking-display` |
54
+ | `effort` | `String \| Symbol \| Integer` | `nil` | `--effort` |
55
+ | `max_thinking_tokens` | `Integer` | `nil` | `--max-thinking-tokens`. Deprecated: used only when `thinking` is unset |
56
+ | `betas` | `Array[String]` | `nil` | `--betas` (comma-joined) |
57
+ | `output_format` | `Hash[Symbol \| String, untyped] \| String` | `nil` | `--json-schema <schema as JSON>` |
58
+
59
+ `effort` takes one of `ClaudeAgentSDK::EFFORT_LEVELS`; which levels a model supports is in [Thinking Configuration](configuration.md#thinking-configuration).
60
+
61
+ ### System prompt forms
62
+
63
+ | `system_prompt` | Command line |
64
+ |-----------------|--------------|
65
+ | `nil` (the default) | `--system-prompt ""`: an empty prompt, not Claude Code's default one |
66
+ | a String, `SystemPromptCustom`, or `{ type: 'custom', prompt: '...' }` | `--system-prompt <text>` |
67
+ | `SystemPromptFile`, or `{ type: 'file', path: '...' }` (the path a String or a `Pathname`) | `--system-prompt-file <path>` |
68
+ | `SystemPromptPreset`, or `{ type: 'preset', preset: 'claude_code' }` | no system prompt flag, so the CLI uses its default prompt; `append:` adds `--append-system-prompt <text>` |
69
+
70
+ `exclude_dynamic_sections` on a preset is sent as `initialize.excludeDynamicSections` ([Cross-User Prompt Caching](configuration.md#cross-user-prompt-caching)), and `snapshot` on a preset or a custom prompt as `initialize.systemPromptSnapshot` ([System Prompt Snapshot](configuration.md#system-prompt-snapshot)). Both are left out when unset.
71
+
72
+ ### `task_budget`
73
+
74
+ A token budget for the task, counted on the API side: the model is told how much of it is left, so it can pace its tool use and wrap up before the limit. Pass `ClaudeAgentSDK::TaskBudget.new(total: 50_000)` or `{ total: 50_000 }`. It is not `max_budget_usd`, which is a spending limit in dollars.
75
+
76
+ ## Tools and permissions
77
+
78
+ Guides: [Tools Configuration](configuration.md#tools-configuration), [Skills](configuration.md#skills), [Sandbox Settings](configuration.md#sandbox-settings), [Hooks & Permission Callbacks](hooks-and-permissions.md).
79
+
80
+ | Option | Type | Default | Sent to the CLI as |
81
+ |--------|------|---------|--------------------|
82
+ | `tools` | `Array[String] \| ToolsPreset \| Hash[Symbol \| String, untyped] \| String` | `nil` | `--tools <comma-joined names>`; a String is sent as written, in the CLI's own comma-separated form. `[]` sends `--tools ""` (no built-in tools); a `ToolsPreset`, or `{ type: 'preset' }`, sends `--tools default` |
83
+ | `allowed_tools` | `Array[String]` | `[]` | `--allowedTools` (comma-joined; no flag when empty) |
84
+ | `disallowed_tools` | `Array[String]` | `[]` | `--disallowedTools` (comma-joined; no flag when empty) |
85
+ | `permission_mode` | `String` | `nil` | `--permission-mode` |
86
+ | `can_use_tool` | `_CanUseTool` | `nil` | `--permission-prompt-tool stdio`: the CLI asks over the control protocol and the SDK calls the callback |
87
+ | `permission_prompt_tool_name` | `String` | `nil` | `--permission-prompt-tool <name>`. Combining it with `can_use_tool` raises `ArgumentError` |
88
+ | `hooks` | `Hash[String \| Symbol, Array[HookMatcher]?]` | `nil` | `initialize.hooks` (matchers, callback ids, timeouts). The callbacks run in your process |
89
+ | `skills` | `String \| Array[String]` | `nil` | `Skill` or `Skill(name)` entries in `--allowedTools`; an Array is also sent as `initialize.skills`; `--setting-sources user,project` when `setting_sources` is `nil` |
90
+ | `sandbox` | `SandboxSettings \| Hash[Symbol \| String, untyped] \| bool` | `nil` | The `sandbox` key of `--settings` |
91
+
92
+ `permission_mode` takes one of `ClaudeAgentSDK::PERMISSION_MODES`.
93
+
94
+ ## MCP servers, agents and plugins
95
+
96
+ Guides: [Custom Tools (SDK MCP Servers)](mcp-servers.md), [Subagent capabilities](subagents.md).
97
+
98
+ | Option | Type | Default | Sent to the CLI as |
99
+ |--------|------|---------|--------------------|
100
+ | `mcp_servers` | `Hash[String \| Symbol, mcp_server_config] \| String` | `{}` | `--mcp-config`: a Hash as `{"mcpServers": {...}}` JSON, a String (a file path or JSON) as given. An SDK server contributes only its `type` and `name`; its tools are served in your process over the control protocol |
101
+ | `strict_mcp_config` | `bool` | `false`; explicit `nil` stays `nil` | `--strict-mcp-config`. See [`strict_mcp_config`](#strict_mcp_config) |
102
+ | `agents` | `Hash[String \| Symbol, AgentDefinition \| Hash[Symbol \| String, untyped]]` | `nil` | `initialize.agents` |
103
+ | `plugins` | `Array[SdkPluginConfig \| Hash[Symbol \| String, untyped]]` | `nil` | `--plugin-dir <path>`, once per plugin. The path may be a String or a `Pathname` |
104
+
105
+ ### `strict_mcp_config`
106
+
107
+ `true` makes the CLI use only the servers in `mcp_servers` and ignore every other MCP configuration (the CLI's own description of `--strict-mcp-config`: "Only use MCP servers from --mcp-config, ignoring all other MCP configurations").
108
+
109
+ "Every other" includes the MCP servers connected to the claude.ai account the CLI is logged in with. A session loads those by default, and `setting_sources: []` does not keep them out, because they do not come from a settings file. `client.mcp_status` shows them: their `:config` has `type: 'claudeai-proxy'`. A host that runs sessions for other people under its own login should set `strict_mcp_config: true`.
110
+
111
+ ## Settings and context
112
+
113
+ Guides: [Bare Mode](configuration.md#bare-mode), [Verbatim Prompts](configuration.md#verbatim-prompts), [Session Isolation](configuration.md#session-isolation).
114
+
115
+ | Option | Type | Default | Sent to the CLI as |
116
+ |--------|------|---------|--------------------|
117
+ | `settings` | `String \| Pathname \| Hash[Symbol \| String, untyped]` | `nil` | `--settings`: a Hash as JSON, a String (a file path or JSON) as given, a `Pathname` as the path of a settings file. With `sandbox` set as well, one merged JSON value |
118
+ | `setting_sources` | `Array[String]` | `nil` | `--setting-sources` (comma-joined). `[]` sends `--setting-sources ""`; `nil` sends no flag |
119
+ | `add_dirs` | `Array[String \| Pathname]` | `[]` | `--add-dir`, once per directory |
120
+ | `bare` | `bool` | `nil` | `--bare` |
121
+ | `verbatim_prompts` | `bool` | `false`; explicit `nil` stays `nil` | `client_composed: true` on every user message the SDK writes |
122
+
123
+ `setting_sources` takes entries of `ClaudeAgentSDK::SETTING_SOURCES` (`user`, `project`, `local`). With `nil`, the default, the CLI decides, and it loads the user's and the project's settings and `CLAUDE.md` files. `[]` loads none of the three sources. Neither value affects the auto-memory: see [Session Isolation](configuration.md#session-isolation).
124
+
125
+ ## Sessions
126
+
127
+ Guide: [Session Browsing & Mutations](sessions.md), which also covers `session_store`. [File Checkpointing & Rewind](configuration.md#file-checkpointing--rewind) is in the configuration guide.
128
+
129
+ | Option | Type | Default | Sent to the CLI as |
130
+ |--------|------|---------|--------------------|
131
+ | `resume` | `String` | `nil` | `--resume=<session id>` |
132
+ | `continue_conversation` | `bool` | `false`; explicit `nil` stays `nil` | `--continue`. Combining it with `resume` raises `ArgumentError` |
133
+ | `fork_session` | `bool` | `false`; explicit `nil` stays `nil` | `--fork-session` |
134
+ | `session_id` | `String` | `nil` | `--session-id=<uuid>` |
135
+ | `resume_session_at` | `String` | `nil` | `--resume-session-at=<uuid>`. Without `resume` it raises `ArgumentError` |
136
+ | `resume_drops_turn` | `String` | `nil` | `--resume-drops-turn=<uuid>` |
137
+ | `enable_file_checkpointing` | `bool` | `false`; explicit `nil` stays `nil` | `CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING=true` in the CLI's environment |
138
+ | `session_store` | `_SessionStore` | `nil` | `--session-mirror`. The SDK appends the mirrored transcript to the store and can resume from it |
139
+ | `session_store_flush` | `String \| Symbol` | `"batched"` | Nothing (SDK only): `'batched'` or `'eager'` |
140
+ | `load_timeout_ms` | `Numeric` | `60000` | Nothing (SDK only): the limit, in milliseconds, for each store call while a resume loads the transcript |
141
+
142
+ To find the session to resume, `ClaudeAgentSDK.list_sessions` lists sessions and `ClaudeAgentSDK.get_session_info(session_id:, directory: nil, session_store: nil)` reads the metadata of one session without listing the others. It returns an `SDKSessionInfo`, or `nil` when there is no session to report under that id. Both are plain functions that need neither a `Client` nor the CLI; the sessions guide has the rest of them.
143
+
144
+ ## Message stream
145
+
146
+ Guides: [Forwarding Subagent Text](configuration.md#forwarding-subagent-text), [Subagent Progress Summaries](configuration.md#subagent-progress-summaries). The message classes are in the [types reference](types.md#message-types).
147
+
148
+ | Option | Type | Default | Sent to the CLI as |
149
+ |--------|------|---------|--------------------|
150
+ | `include_partial_messages` | `bool` | `false`; explicit `nil` stays `nil` | `--include-partial-messages`. See [`include_partial_messages`](#include_partial_messages) |
151
+ | `include_hook_events` | `bool` | `false`; explicit `nil` stays `nil` | `--include-hook-events`. See [`include_hook_events`](#include_hook_events) |
152
+ | `forward_subagent_text` | `bool` | `false`; explicit `nil` stays `nil` | `initialize.forwardSubagentText`, sent only when `true` |
153
+ | `agent_progress_summaries` | `bool` | `nil` | `initialize.agentProgressSummaries`, left out when `nil` |
154
+
155
+ ### `include_partial_messages`
156
+
157
+ Asks the CLI for partial message chunks while the model is still producing a message. Each chunk arrives as a `StreamEvent` whose `event` is the raw API stream event, a Hash with Symbol keys; the complete `AssistantMessage` still follows. Without the option the CLI sends no `StreamEvent`.
158
+
159
+ ### `include_hook_events`
160
+
161
+ Asks the CLI to put all hook lifecycle events into the message stream, as `HookStartedMessage`, `HookProgressMessage` and `HookResponseMessage` (see [System and progress messages](types.md#system-and-progress-messages)). It only adds messages: the callbacks you register with `hooks` are called whether or not it is set.
162
+
163
+ ## The CLI process
164
+
165
+ Guides: [Vendoring the CLI](cli-installer.md), [Custom Transport](client.md#custom-transport).
166
+
167
+ | Option | Type | Default | Sent to the CLI as |
168
+ |--------|------|---------|--------------------|
169
+ | `cli_path` | `String \| Pathname` | `nil` | The executable the SDK starts. `nil`: the [discovery order](cli-installer.md#cli-discovery-order) |
170
+ | `cwd` | `String \| Pathname` | `nil` | The working directory of the CLI process (and its `PWD`) |
171
+ | `env` | `Hash[String \| Symbol, String?]` | `{}` | Merged over the environment the CLI process inherits. A `nil` value unsets the variable |
172
+ | `user` | `String \| Integer` | `nil` | The user the CLI process runs as (name or uid; Unix, and the SDK process needs the privilege to switch) |
173
+ | `extra_args` | `Hash[String \| Symbol, untyped]` | `{}` | `--<flag> <value>` per pair, `--<flag>` for a `nil` value. See [`extra_args`](#extra_args) |
174
+ | `stderr` | `_StderrCallback` | `nil` | Nothing (SDK only): called with each line the CLI writes to stderr |
175
+ | `debug_stderr` | `_Puts \| String` | `nil` | Nothing (SDK only): an IO-like object (`puts`) or a file path that receives each CLI stderr line |
176
+ | `max_buffer_size` | `Integer` | `nil` | Nothing (SDK only): the largest single message, in bytes, accepted from the CLI. `nil` means 1 MiB |
177
+
178
+ These options describe the process the SDK starts itself. A [custom transport](client.md#custom-transport) starts the CLI its own way and decides what to do with them.
179
+
180
+ ### `extra_args`
181
+
182
+ The way to pass a CLI flag the SDK has no option for. A key is the flag's name without the leading dashes; the value follows it, and `nil` means a flag without a value:
183
+
184
+ ```ruby
185
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
186
+ extra_args: {
187
+ 'no-session-persistence' => nil, # --no-session-persistence
188
+ 'agent' => 'reviewer' # --agent reviewer
189
+ }
190
+ )
191
+ ```
192
+
193
+ - A flag name may contain only lowercase letters, digits and hyphens. Anything else raises `ArgumentError` when the command line is built, before the CLI starts.
194
+ - Values are converted with `to_s`. One that starts with `-` is sent as `--flag=value`, so the CLI cannot read it as another flag.
195
+ - The flags go at the end of the command line, after everything the options above produce. The SDK does not check that the CLI knows them: an unknown flag makes the CLI exit at startup, which raises `ProcessError` (`error: unknown option '--...'`).
196
+ - Use an option when there is one. A flag given here as well as through its option reaches the CLI twice.
197
+
198
+ ## Callbacks and observers
199
+
200
+ Guides: [Observability](observability.md), [Rails Integration](rails.md).
201
+
202
+ | Option | Type | Default | Sent to the CLI as |
203
+ |--------|------|---------|--------------------|
204
+ | `observers` | `Array[untyped]` | `[]` | Nothing (SDK only): observer instances, or callables that return a fresh one per query or session |
205
+ | `callback_scheduling` | `:thread \| :inline \| "thread" \| "inline"` | `:thread` | Nothing (SDK only): `:thread` runs each callback on a plain thread, `:inline` on the reactor fiber. A String is stored as its Symbol (`callback_scheduling: 'inline'` reads back as `:inline`). A value that does not convert (through `to_sym`) to `:thread` or `:inline` raises `ArgumentError`; `nil` means the default |
206
+ | `callback_wrapper` | `_CallbackWrapper` | `nil` | Nothing (SDK only): a callable wrapped around every callback dispatch |
207
+
208
+ ## Environment variables
209
+
210
+ Variables that change what the SDK or a session does. "SDK process" means your Ruby process's own environment; `env` means `ClaudeAgentOptions#env`, which reaches only the CLI.
211
+
212
+ | Variable | Set it in | Effect |
213
+ |----------|-----------|--------|
214
+ | `ANTHROPIC_API_KEY`, `CLAUDE_CODE_OAUTH_TOKEN` | SDK process (the CLI inherits it) or `env` | Credentials the CLI can authenticate with |
215
+ | `CLAUDE_CLI_PATH` | SDK process | The CLI executable, ahead of the vendored binary and `PATH`, when `cli_path` is `nil`. A relative path is resolved against the SDK process's working directory. A value that does not name an executable file is skipped without a warning, and discovery goes on to the vendored binary, `PATH` and the common install locations |
216
+ | `CLAUDE_CONFIG_DIR` | SDK process, and `env` for the CLI | Where Claude Code keeps its configuration and transcripts, instead of `~/.claude`. The session functions read it from the SDK process ([Sessions](sessions.md)) |
217
+ | `CLAUDE_AGENT_SDK_CONTROL_REQUEST_TIMEOUT_SECONDS` | SDK process | How long the SDK waits for the CLI's answer to a control request. Default 1200 ([Error Handling](errors.md#configuring-timeout)) |
218
+ | `CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS` | `env`, or the SDK process | See below |
219
+ | `CLAUDE_CODE_DISABLE_AUTO_MEMORY` | `env` | `'1'` turns the CLI's auto-memory off ([Session Isolation](configuration.md#session-isolation)) |
220
+ | `CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS` | `env` | `'1'` makes `SessionStateChangedMessage` reach your message block |
221
+ | `CLAUDE_CODE_DISABLE_ADVISOR_TOOL` | `env` | `'1'` disables the advisor tool ([Advisor Model](configuration.md#advisor-model)) |
222
+
223
+ ### `CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS`
224
+
225
+ A one-shot `query()` or `ask` that has hooks, a `can_use_tool` callback or SDK MCP servers keeps the CLI's stdin open after a turn's result, because background work can still wake the session for another turn whose hook, permission and tool requests are answered over stdin. It closes stdin when the CLI reports that it is idle. This variable bounds that wait: if the CLI still reports work this many milliseconds after a result and no new turn has started, the run ends anyway.
226
+
227
+ - The default is 600000 (10 minutes). `0` means no limit.
228
+ - It is read from `env` first and from the SDK process's environment otherwise. Only a plain non-negative integer counts; anything else gives the default.
229
+ - The clock runs only between turns. A turn in progress, a request the SDK is still answering and a background agent that is still running stop it.
230
+ - The CLI reads the same variable for its own wait for background work once stdin is closed.
231
+ - A CLI that reports no session state (2.1.282 and earlier) is not waited for: stdin closes at the first result with no tracked background task still running.
232
+ - A `Client` session driven with `Client#query` is not affected: its stdin stays open until `disconnect`.