feat(claude): expose the headless stream-json transport, with real streaming

`ClaudeCodeOptions.transport="stream_json"` runs the agent on `claude -p`
with stream-json on both pipes instead of driving the TUI through a
pseudo-tty. Opt-in: the default stays `pty`, so nothing moves until a
config asks for it.

The reason to ask for it is that the PTY path pays a multi-second
readiness wait before every spawn and can silently lose a prompt to a
swallowed paste; the headless path writes to a pipe and gets a native
`result` record back. `--remote-control` and friends are dropped by the
transport when set, since they'd take claude out of print mode.

`include_partial_messages` then gives clients genuine token-level SSE.
Blocks are emitted from the `StreamEvent`s rather than forwarded raw:
the payload is already Anthropic-shaped but arrives as a dict, so it
would need validating against the SDK union anyway, and rebuilding it
through our own builders drops an unrecognized block or delta type the
same way `_emit_block` already does instead of raising mid-stream on a
claude release that adds one. When streaming, the whole-block records
are kept for TurnCapture but not re-emitted — that would duplicate every
block on the wire.

Verified end-to-end against a live claude: both paths accumulate to the
identical Message (same blocks, same stop_reason, same usage), the
envelope stays 1:1, and block indices stay collision-free across a turn
that spans several API requests.
This commit is contained in:
hh
2026-07-28 03:00:10 +02:00
parent 99b5c69c1e
commit 823337409a
3 changed files with 146 additions and 20 deletions
Generated
+2 -2
View File
@@ -287,7 +287,7 @@ local = [
{ name = "raycast-api", version = "0.1.0", source = { editable = "../raycast-api" } },
]
prod = [
{ name = "claude-code-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#aa7beea1e014ba0dd8cd4980fee4b9302c57b212" } },
{ name = "claude-code-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#76799179d9437dcd133a40ffcf5eecc524063c2f" } },
{ name = "raycast-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/raycast-api.git#e73894c8e435da5c0709f92f69f11bcd0dab9afe" } },
]
@@ -419,7 +419,7 @@ wheels = [
[[package]]
name = "claude-code-api"
version = "0.1.0"
source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#aa7beea1e014ba0dd8cd4980fee4b9302c57b212" }
source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#76799179d9437dcd133a40ffcf5eecc524063c2f" }
resolution-markers = [
"python_full_version >= '3.14'",
"python_full_version < '3.14'",