pub struct DuplexOptions { /* private fields */ }Expand description
Configuration for DuplexSession::spawn.
Builder methods cover the spawn-time options shared with
QueryCommand; the flag emission lives on a
common internal SharedSpawnArgs, so the oneshot and duplex paths
cannot drift on how a knob is rendered. The spawn call always
includes
--print --verbose --input-format stream-json --output-format stream-json
regardless of these options.
A few QueryCommand knobs are intentionally not surfaced here
because they only make sense for a oneshot run or are owned by the
duplex transport itself:
- Transport is fixed:
output_format,input_format,verbose, andprompt_via_stdinare pinned by the duplex spawn and not configurable. Partial message chunks are off by default; opt in withSelf::include_partial_messages. retry_policyreruns a whole oneshot invocation; a duplex session holds one child open across turns, so there is nothing to retry at this layer.briefandfrom_prshape a single oneshot run (SendUserMessage for one-turn agent-to-user replies, resume-from-PR startup); a duplex host drives turns and session selection itself.prompt_suggestionsandreplay_user_messagesshape stdin/stream echoing; the duplex layer owns its own stream plumbing.
Use Self::arg if you need one of these on a duplex spawn anyway.
Resource and time policies (Self::max_frame_bytes,
Self::max_turn_bytes, Self::max_turn_events,
Self::turn_timeout, Self::control_timeout) are host-side and
emit no CLI flags. All are unlimited by default. See the
module docs.
Implementations§
Source§impl DuplexOptions
impl DuplexOptions
Sourcepub fn system_prompt(self, prompt: impl Into<String>) -> Self
pub fn system_prompt(self, prompt: impl Into<String>) -> Self
Set the system prompt for this session (--system-prompt).
Sourcepub fn append_system_prompt(self, prompt: impl Into<String>) -> Self
pub fn append_system_prompt(self, prompt: impl Into<String>) -> Self
Append to the default system prompt (--append-system-prompt).
Sourcepub fn resume(self, session_id: impl Into<String>) -> Self
pub fn resume(self, session_id: impl Into<String>) -> Self
Resume a prior session by id (--resume <session_id>).
Mirrors QueryCommand::resume
for the duplex path. The spawned claude process picks up the
conversation that produced session_id and continues it; turns
sent through DuplexSession::send append to the existing
history rather than starting fresh.
Use case: a host (IDE, MCP server, agent backend) wants to
upgrade a passive on-disk session to a live duplex one –
pulls the session_id out of the existing JSONL log, opens a
duplex session here, and the next turn extends the same
conversation.
resume and Self::continue_session are mutually exclusive
at the CLI; passing both lets the CLI decide (it errors today).
Sourcepub fn continue_session(self) -> Self
pub fn continue_session(self) -> Self
Continue the most recent session in the current working
directory (--continue).
Mirrors QueryCommand::continue_session
for the duplex path. Use Self::resume to pick a specific
session id; use this when “the last one” is what you want.
Sourcepub fn worktree(self, name: Option<impl Into<String>>) -> Self
pub fn worktree(self, name: Option<impl Into<String>>) -> Self
Run this session in a fresh git worktree (--worktree [name]).
name is the optional worktree name (the CLI auto-generates
one if omitted). Calling this method always enables the
worktree flag, with or without a name.
Use case: an agent host wants the chat’s writes isolated from the current working tree – the chat opens with a fresh worktree, mutations land there, and the host can inspect or merge later.
Sourcepub fn agent(self, name: impl Into<String>) -> Self
pub fn agent(self, name: impl Into<String>) -> Self
Pin the session to a named subagent (--agent <name>).
name is resolved by the CLI in this order: inline
definitions from Self::agents_json, then user-level
~/.claude/agents/<name>.md files, then project-level dirs
loaded by the active --setting-sources.
Caveat: as of Claude Code 2.1.143, the CLI silently
ignores an unknown name and falls back to the default
behavior – no warning, no error. Callers that want a hard
“agent must exist” semantics should validate the name out of
band (e.g. via crate::artifacts::AgentsRoot::get) before
passing it here.
Sourcepub fn agents_json(self, json: impl Into<String>) -> Self
pub fn agents_json(self, json: impl Into<String>) -> Self
Inline subagent definitions for this session
(--agents <json>).
json is a JSON object keyed by agent name, with each value
carrying at least description and prompt. Inline
definitions take precedence over on-disk
~/.claude/agents/*.md of the same name. Pass Self::agent
to select which one to use as the session’s persona.
Example: {"reviewer": {"description": "Reviews code", "prompt": "You are a code reviewer"}}.
Sourcepub fn permission_mode(self, mode: PermissionMode) -> Self
pub fn permission_mode(self, mode: PermissionMode) -> Self
Set the permission mode for this session
(--permission-mode <mode>).
Mirrors QueryCommand::permission_mode
for the duplex path. The default mode (when this method isn’t
called) drops to the CLI’s interactive prompt for every
tool-use approval, which is broken for non-interactive duplex
sessions – nothing answers the prompts and the session stalls
or fails. Call this with PermissionMode::AcceptEdits for
the “edit files autonomously” pattern, PermissionMode::Plan
for read-only planning, etc.
Bypass mode is a footgun; reach for Self::dangerously_skip_permissions
(or, for stricter discipline, crate::dangerous::DangerousClient)
when you really need it.
Sourcepub fn forward_subagent_text(self) -> Self
pub fn forward_subagent_text(self) -> Self
Forward nested subagent text and thinking as stream-json messages
(--forward-subagent-text). Parent tool-use IDs remain in the raw events.
Only meaningful with stream-json output; duplex sessions already use it.
Sourcepub fn allow_dangerously_skip_permissions(self) -> Self
pub fn allow_dangerously_skip_permissions(self) -> Self
Make bypass mode available without activating it
(--allow-dangerously-skip-permissions). This does not select a bypass
permission mode or disable permission checks by itself.
Sourcepub fn dangerously_skip_permissions(self) -> Self
pub fn dangerously_skip_permissions(self) -> Self
Pass --dangerously-skip-permissions to the spawned session.
Bypasses ALL permission checks – file edits, bash, network,
the lot. Use only when you know the session runs in a trusted
sandbox (a fresh worktree, a container, etc.). For most “run
autonomously” cases you want Self::permission_mode with
PermissionMode::AcceptEdits instead.
Sourcepub fn session_id(self, id: impl Into<String>) -> Self
pub fn session_id(self, id: impl Into<String>) -> Self
Start a new session under a caller-chosen id
(--session-id <uuid>).
Mirrors QueryCommand::session_id
for the duplex path. Unlike Self::resume (pick up an
existing session) or Self::continue_session (pick up the
most recent one), this mints a fresh session whose id the host
knows up front – useful when the host indexes sessions
externally before the first turn completes.
Sourcepub fn json_schema(self, schema: impl Into<String>) -> Self
pub fn json_schema(self, schema: impl Into<String>) -> Self
Set a JSON schema for structured output validation
(--json-schema <schema>).
Mirrors QueryCommand::json_schema
for the duplex path. schema is the inline JSON of the schema;
the turn’s closing result message carries the validated
structured_output.
Sourcepub fn allowed_tools<I, T>(self, tools: I) -> Self
pub fn allowed_tools<I, T>(self, tools: I) -> Self
Add allowed tool patterns (--allowed-tools).
Mirrors QueryCommand::allowed_tools
for the duplex path: accepts anything convertible into
ToolPattern, including bare strings (e.g. "Bash",
"Bash(git log:*)", "mcp__my-server__*"), and joins them
into the comma-separated form the CLI expects.
Sourcepub fn allowed_tool(self, tool: impl Into<ToolPattern>) -> Self
pub fn allowed_tool(self, tool: impl Into<ToolPattern>) -> Self
Add a single allowed tool pattern.
Sourcepub fn disallowed_tools<I, T>(self, tools: I) -> Self
pub fn disallowed_tools<I, T>(self, tools: I) -> Self
Add disallowed tool patterns (--disallowed-tools).
Sourcepub fn disallowed_tool(self, tool: impl Into<ToolPattern>) -> Self
pub fn disallowed_tool(self, tool: impl Into<ToolPattern>) -> Self
Add a single disallowed tool pattern.
Sourcepub fn max_turns(self, turns: u32) -> Self
pub fn max_turns(self, turns: u32) -> Self
Cap the number of agentic turns (--max-turns <n>).
The cap applies per turn sent through DuplexSession::send;
a turn that exhausts it closes with an error_max_turns
result rather than an assistant reply.
Sourcepub fn max_budget_usd(self, budget: f64) -> Self
pub fn max_budget_usd(self, budget: f64) -> Self
Cap claude’s own spend for the session
(--max-budget-usd <usd>).
This is the CLI’s cap, checked post-hoc after each API call,
so a session can overspend before tripping. It is distinct
from the wrapper’s BudgetTracker
ceiling, which gates dispatch host-side – attach one via
Conversation::with_budget
to stop a duplex conversation before the next turn is sent.
Sourcepub fn fallback_model(self, model: impl Into<String>) -> Self
pub fn fallback_model(self, model: impl Into<String>) -> Self
Set a fallback model for when the primary is overloaded
(--fallback-model <model>).
Sourcepub fn add_dir(self, dir: impl Into<String>) -> Self
pub fn add_dir(self, dir: impl Into<String>) -> Self
Add an additional directory for tool access
(--add-dir <dir>, repeatable).
Sourcepub fn mcp_config(self, path: impl Into<String>) -> Self
pub fn mcp_config(self, path: impl Into<String>) -> Self
Add an MCP config file path (--mcp-config <path>,
repeatable).
Pair with crate::McpConfigBuilder to generate the file.
Sourcepub fn strict_mcp_config(self) -> Self
pub fn strict_mcp_config(self) -> Self
Only use MCP servers from --mcp-config files, ignoring the
user- and project-level MCP configuration
(--strict-mcp-config).
Sourcepub fn setting_sources(self, sources: impl Into<String>) -> Self
pub fn setting_sources(self, sources: impl Into<String>) -> Self
Comma-separated list of setting sources the CLI loads, for example
"user,project,local" (--setting-sources). Pass an empty string to
load none, sealing the session’s promptspace against ambient project
config (agents, skills, CLAUDE.md). Mirrors
QueryCommand::setting_sources.
Sourcepub fn hermetic(self) -> Self
pub fn hermetic(self) -> Self
Seal the ambient ~/.claude config for a reproducible session
(HermeticScope::Full).
Sets --setting-sources "", --strict-mcp-config, and
--exclude-dynamic-system-prompt-sections. This gives a warm
duplex session a clean seal without the Self::arg escape
hatch. Mirrors
QueryCommand::hermetic.
This is not Self::bare: a hermetic seal leaves OAuth and
keychain auth working, whereas --bare forces API-key billing.
A later Self::setting_sources call overrides the seal scope.
Sourcepub fn hermetic_scoped(self, scope: HermeticScope) -> Self
pub fn hermetic_scoped(self, scope: HermeticScope) -> Self
Seal the ambient ~/.claude config at an explicit
HermeticScope.
See Self::hermetic for the flag set. Mirrors
QueryCommand::hermetic_scoped.
Sourcepub fn no_session_persistence(self) -> Self
pub fn no_session_persistence(self) -> Self
Do not persist the session to on-disk history
(--no-session-persistence).
Sourcepub fn tools(self, tools: impl IntoIterator<Item = impl Into<String>>) -> Self
pub fn tools(self, tools: impl IntoIterator<Item = impl Into<String>>) -> Self
Set the list of available built-in tools (--tools).
Use "" to disable all tools, "default" for all tools, or
specific tool names like ["Bash", "Edit", "Read"]. This is
distinct from Self::allowed_tools, which controls tool
permissions rather than which built-ins load. Mirrors
QueryCommand::tools.
Sourcepub fn file(self, spec: impl Into<String>) -> Self
pub fn file(self, spec: impl Into<String>) -> Self
Add a file resource to download at startup (--file).
Format: file_id:relative_path (e.g. file_abc:doc.txt).
Repeatable. Mirrors QueryCommand::file.
Sourcepub fn settings(self, settings: impl Into<String>) -> Self
pub fn settings(self, settings: impl Into<String>) -> Self
Path to a settings JSON file or a JSON string (--settings).
Mirrors QueryCommand::settings.
Sourcepub fn fork_session(self) -> Self
pub fn fork_session(self) -> Self
When resuming, create a new session id instead of reusing the
original (--fork-session).
Only meaningful alongside Self::resume or
Self::continue_session. Mirrors
QueryCommand::fork_session.
Sourcepub fn debug_filter(self, filter: impl Into<String>) -> Self
pub fn debug_filter(self, filter: impl Into<String>) -> Self
Enable debug logging with an optional filter, e.g. "api,hooks"
(--debug). Mirrors
QueryCommand::debug_filter.
Sourcepub fn debug_file(self, path: impl Into<String>) -> Self
pub fn debug_file(self, path: impl Into<String>) -> Self
Write debug logs to the given file path (--debug-file).
Mirrors QueryCommand::debug_file.
Sourcepub fn betas(self, betas: impl Into<String>) -> Self
pub fn betas(self, betas: impl Into<String>) -> Self
Beta feature headers for API key authentication (--betas).
Mirrors QueryCommand::betas.
Sourcepub fn plugin_dir(self, dir: impl Into<String>) -> Self
pub fn plugin_dir(self, dir: impl Into<String>) -> Self
Load plugins from the given directory for this session
(--plugin-dir). Repeatable. Mirrors
QueryCommand::plugin_dir.
Sourcepub fn plugin_url(self, url: impl Into<String>) -> Self
pub fn plugin_url(self, url: impl Into<String>) -> Self
Fetch a plugin .zip from a URL for this session only
(--plugin-url). Repeatable. Mirrors
QueryCommand::plugin_url.
Sourcepub fn tmux(self) -> Self
pub fn tmux(self) -> Self
Create a tmux session for the worktree (--tmux).
Mirrors QueryCommand::tmux.
Sourcepub fn bare(self) -> Self
pub fn bare(self) -> Self
Run in minimal mode (--bare).
Skips hooks, LSP, plugin sync, attribution, auto-memory,
background prefetches, keychain reads, and CLAUDE.md
auto-discovery. Anthropic auth is restricted to
ANTHROPIC_API_KEY or apiKeyHelper; OAuth and keychain are
never read. Mirrors QueryCommand::bare.
Sourcepub fn safe_mode(self) -> Self
pub fn safe_mode(self) -> Self
Start with all customizations disabled (--safe-mode).
Disables CLAUDE.md, skills, plugins, hooks, MCP servers,
custom commands and agents, and output styles for
troubleshooting. Mirrors
QueryCommand::safe_mode.
Sourcepub fn disable_slash_commands(self) -> Self
pub fn disable_slash_commands(self) -> Self
Disable all slash-command skills (--disable-slash-commands).
Mirrors
QueryCommand::disable_slash_commands.
Sourcepub fn include_hook_events(self) -> Self
pub fn include_hook_events(self) -> Self
Include every hook lifecycle event in the stream-json output
(--include-hook-events).
Duplex sessions always run in stream-json, so this takes effect
without extra configuration. Mirrors
QueryCommand::include_hook_events.
Sourcepub fn exclude_dynamic_system_prompt_sections(self) -> Self
pub fn exclude_dynamic_system_prompt_sections(self) -> Self
Move per-machine sections (cwd, env info, memory paths, git
status) out of the system prompt and into the first user
message (--exclude-dynamic-system-prompt-sections).
Improves cross-user prompt-cache reuse. Only applies with the
default system prompt; ignored with Self::system_prompt.
Mirrors
QueryCommand::exclude_dynamic_system_prompt_sections.
Sourcepub fn name(self, name: impl Into<String>) -> Self
pub fn name(self, name: impl Into<String>) -> Self
Set a display name for this session (--name). Shown in the
/resume picker and terminal title. Mirrors
QueryCommand::name.
Sourcepub fn arg(self, arg: impl Into<String>) -> Self
pub fn arg(self, arg: impl Into<String>) -> Self
Add a raw argument to the spawn command line.
Escape hatch for flags not covered by the dedicated builder methods.
Sourcepub fn subscriber_capacity(self, capacity: usize) -> Self
pub fn subscriber_capacity(self, capacity: usize) -> Self
Set the per-session broadcast::Sender capacity backing
DuplexSession::subscribe.
Defaults to DEFAULT_SUBSCRIBER_CAPACITY (256). Larger
values give slow subscribers more room before they
Lagged;
smaller values reclaim memory if you do not subscribe.
Sourcepub fn on_permission(self, handler: PermissionHandler) -> Self
pub fn on_permission(self, handler: PermissionHandler) -> Self
Register a PermissionHandler to answer the CLI’s tool-use
permission prompts in-flight.
When set, the spawn command line includes
--permission-prompt-tool stdio, which configures the CLI to
emit control_request messages for tool use over the duplex
channel rather than blocking on a TUI prompt.
Without a handler, the session does not pass
--permission-prompt-tool and the CLI applies its default
permission policy (driven by --permission-mode).
Runs only when the CLI’s permission rules request a decision. Allowed
tools or permissive modes may resolve an action without calling it.
Configure an explicit ask rule through Self::settings for actions
that should reach the callback. The resident transport keeps stdin open;
native denial and deferred-denial are tested at both supported endpoints.
Sourcepub fn include_partial_messages(self) -> Self
pub fn include_partial_messages(self) -> Self
Emit partial message chunks as they arrive
(--include-partial-messages). Off by default.
The CLI then writes stream_event messages while a turn is in
progress. They are broadcast to DuplexSession::subscribe
receivers as InboundEvent::StreamEvent and retained in
TurnResult::events like any other event, so a long reply can
produce many events. Pair this with Self::max_turn_events or
Self::max_turn_bytes when per-turn memory must be bounded.
Sourcepub fn max_frame_bytes(self, bytes: usize) -> Self
pub fn max_frame_bytes(self, bytes: usize) -> Self
Cap the size of one stdout frame (one NDJSON line), in bytes, including its newline terminator. Default: unlimited.
The check runs before an unterminated line is extended, so a child
that writes an endless line without a newline cannot grow the
retained raw frame past this size. Applies whether or not a turn is in
flight. Exceeding it closes the session with
Error::DuplexLimitExceeded (see the
module docs).
ClaudeBuilder::output_limit
does not apply to duplex sessions.
Sourcepub fn max_turn_bytes(self, bytes: usize) -> Self
pub fn max_turn_bytes(self, bytes: usize) -> Self
Cap the stdout bytes consumed during one turn. Default: unlimited.
Counts every byte read while a turn is pending, from the moment the
session accepts the DuplexSession::send through the closing
result line: newline terminators, blank lines, and lines that fail
to parse all count. The counter resets for each turn; bytes read
while no turn is pending do not count, except for a partial frame
carried into the new turn: its buffered prefix counts too. Exceeding
the limit closes the session with Error::DuplexLimitExceeded.
Sourcepub fn max_turn_events(self, events: usize) -> Self
pub fn max_turn_events(self, events: usize) -> Self
Cap the number of events retained in one turn’s
TurnResult::events. Default: unlimited.
The count resets for each turn. Exceeding it closes the session with
Error::DuplexLimitExceeded instead of returning a truncated
turn. This is separate from Self::subscriber_capacity: a slow
subscriber sees
RecvError::Lagged
and the session continues, while a retained-event overflow ends the
session.
Sourcepub fn turn_timeout(self, timeout: Duration) -> Self
pub fn turn_timeout(self, timeout: Duration) -> Self
Bound each turn with a deadline. Default: none.
The clock starts when the session task accepts a
DuplexSession::send, before the prompt is written to stdin, and
stops when the closing result line is read. The deadline covers
the stdin write, every PermissionHandler call made during the
turn, and any wait for DuplexSession::respond_to_permission
after a PermissionDecision::Defer. Expiry closes the session
with Error::DuplexDeadlineExceeded.
Independent of the client’s
ClaudeBuilder::timeout, which does
not apply to duplex sessions. An idle session with no pending turn
has no lifetime limit. Cleanup can extend the wait beyond the deadline
by the configured kill grace and process settlement time. Zero expires
immediately; durations too large for the clock are treated as unlimited.
An expired deadline takes precedence over a result still awaiting
dispatch, even if that result is already buffered.
Sourcepub fn control_timeout(self, timeout: Duration) -> Self
pub fn control_timeout(self, timeout: Duration) -> Self
Bound each outbound control request, such as
DuplexSession::interrupt, with a deadline. Default: none.
The clock starts when the session task accepts the request, before
it is written to stdin, and stops when the matching
control_response is read. The acknowledgment is independent of
any turn result: an interrupted turn’s result does not satisfy this
deadline, and an acknowledgment does not end the turn. Expiry closes
the session with Error::DuplexDeadlineExceeded. Cleanup can extend
the wait beyond the deadline; zero and unrepresentable durations have
the same behavior as Self::turn_timeout.
Sourcepub fn to_command_string(&self, claude: &Claude) -> String
pub fn to_command_string(&self, claude: &Claude) -> String
Return the full spawn command as a string that could be run in a shell.
The duplex analog of
QueryCommand::to_command_string:
the binary path from the Claude client plus the exact
arguments DuplexSession::spawn would pass for these
options, including the client’s global args. Both share one
args-assembly path, so this preview always matches the real
spawn. Arguments containing spaces or special shell characters
are shell-quoted.
§Example
use claude_wrapper::{Claude, DuplexOptions};
let claude = Claude::builder().build()?;
let opts = DuplexOptions::default()
.agent("reviewer")
.setting_sources("project");
println!("Would spawn: {}", opts.to_command_string(&claude));