Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
66b37a0
Run the CLI in interactive mode over a PTY instead of headless pipes
claude Jun 6, 2026
957895f
Make the interactive PTY transport the SDK's only transport
claude Jun 6, 2026
015226b
Add comprehensive drop-in tests for the interactive PTY transport
claude Jun 7, 2026
44a4c72
Harden the interactive PTY transport against the review findings
claude Jun 7, 2026
e86808a
Correct the can_use_tool rejection rationale to the verified reason
claude Jun 7, 2026
fca773b
Add structured question detection/extraction for the PTY transport
claude Jun 7, 2026
8b38893
PTY: compute cost/usage/model_usage/stop_reason on result (C1, C2, H4…
claude Jun 7, 2026
cf43bff
PTY: populate init/server_info and mcp_status; consistent session_id …
claude Jun 7, 2026
80de1ae
PTY: inherit cwd, pass user, restore entrypoint, surface user content…
claude Jun 7, 2026
ebb8133
PTY: track live permission mode from transcript records (H1, L5)
claude Jun 7, 2026
0a3bdd5
PTY: answer tool-permission dialogs via the TUI detector (C5, C6)
claude Jun 7, 2026
ac75c98
PTY: honor options.max_buffer_size for the message buffer (M8)
claude Jun 7, 2026
3f0a4cd
PTY: parse structured_output from json_schema turns (H6)
claude Jun 7, 2026
85d8af6
Fix PTY drop-in parity: num_turns, assistant-snapshot dedup, calibrat…
claude Jun 8, 2026
31c71ae
PTY drop-in: fix model_usage key, cost rates, interrupt, server_info …
claude Jun 8, 2026
5ab3b36
PTY drop-in: fix can_use_tool deny path (RR1,RR2,RR4,RR5)
claude Jun 8, 2026
29607de
PTY drop-in: surface all assistant tool_use/thinking blocks (RV1)
claude Jun 8, 2026
cd4c2d4
PTY drop-in: fix empty-prompt hang, idle interrupt, resume context (R…
claude Jun 8, 2026
875ab78
Add always-on pure-relay API monitor to enrich PTY transport output
claude Jun 8, 2026
a1d4149
PTY relay: fix RV2 race, quota over-count, keep-alive, exact endpoint…
claude Jun 8, 2026
59551c9
PTY relay: traffic-derived enrichments RL9/RL10/RL11
claude Jun 8, 2026
1d3d93d
PTY relay: refresh get_server_info catalog, drop stale partial warnin…
claude Jun 8, 2026
0248265
PTY relay: map updated_permissions to TUI session-allow (RL12)
claude Jun 8, 2026
b50aff7
Tighten setMode over-grant guard in _should_persist_allow (RL13)
claude Jun 8, 2026
1fd84b1
Harden _should_persist_allow into strict positive allowlist (RL14)
claude Jun 8, 2026
0051759
Require unanimous session-broad list in _should_persist_allow (RL15)
claude Jun 8, 2026
00ef6a2
PTY transport: deliver child stderr via a dedicated pipe (H3)
claude Jun 9, 2026
2e1da10
PTY transport: isolate config writes + auto-accept bypass dialog (L4/…
claude Jun 9, 2026
2229f24
Add settings-hook IPC bridge + shim for PTY transport
claude Jun 9, 2026
fdeb242
Wire hooks + can_use_tool through the settings-hook IPC bridge (PTY)
claude Jun 9, 2026
23e14c8
Document hooks + can_use_tool support; live A/B verification
claude Jun 9, 2026
57f2c96
Fix hook-IPC review findings W1-W4 (PTY transport)
claude Jun 9, 2026
cb5829e
Fix hook-IPC permission merge: deny-wins + per-id concurrency (W5/W6)
claude Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions examples/pty_interactive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""Demonstrates the SDK driving the interactive Claude Code CLI over a PTY.

Since v-next, the SDK launches the *interactive* CLI attached to a
pseudo-terminal instead of the headless ``stream-json`` pipe. This is an
internal implementation detail -- the public ``query()`` / ``ClaudeSDKClient``
API is unchanged -- so this example looks like any other.

Run it on a machine where you are already logged in to Claude Code::

python examples/pty_interactive.py
"""

import anyio

from claude_agent_sdk import (
AssistantMessage,
ClaudeAgentOptions,
ClaudeSDKClient,
ResultMessage,
TextBlock,
query,
)


async def one_shot() -> None:
"""A single prompt via the one-shot ``query()`` helper."""
print("=== one-shot query() ===")
async for message in query(prompt="Reply with a single word: pong"):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text.strip()}")
elif isinstance(message, ResultMessage):
print(f"(done in {message.duration_ms} ms)")
print()


async def multi_turn() -> None:
"""A persistent interactive session with several turns."""
print("=== multi-turn ClaudeSDKClient ===")
options = ClaudeAgentOptions()
async with ClaudeSDKClient(options=options) as client:
for prompt in (
"Say the word 'apple' and nothing else.",
"Now say the word 'banana' and nothing else.",
):
await client.query(prompt)
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text.strip()}")
print()


async def main() -> None:
await one_shot()
await multi_turn()


if __name__ == "__main__":
anyio.run(main)
11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ dev = [
otel = [
"opentelemetry-api>=1.20.0",
]
pty-introspect = [
# Terminal emulator used by PtyCLITransport.detect_question() to
# reconstruct the rendered screen and parse blocking TUI dialogs.
"pyte>=0.8.1",
]
examples = [
# Backend clients for the SessionStore reference adapters under
# examples/session_stores/. The corresponding tests importorskip on
Expand Down Expand Up @@ -101,6 +106,12 @@ strict_equality = true
module = ["opentelemetry", "opentelemetry.*"]
ignore_missing_imports = true

[[tool.mypy.overrides]]
# pyte is an optional dependency (the [pty-introspect] extra) and ships no type
# stubs; the imports in pty_cli.py / pty_question.py are guarded by try/except.
module = ["pyte", "pyte.*"]
ignore_missing_imports = true

[tool.ruff]
target-version = "py310"
line-length = 88
Expand Down
4 changes: 2 additions & 2 deletions src/claude_agent_sdk/_internal/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
)
from .session_store_validation import validate_session_store_options
from .transport import Transport
from .transport.subprocess_cli import SubprocessCLITransport
from .transport.pty_cli import PtyCLITransport


class InternalClient:
Expand Down Expand Up @@ -124,7 +124,7 @@ async def _process_query_inner(
if transport is not None:
chosen_transport = transport
else:
chosen_transport = SubprocessCLITransport(
chosen_transport = PtyCLITransport(
prompt=prompt,
options=configured_options,
)
Expand Down
Loading