Skip to content

v0.16.8

Choose a tag to compare

@ya-luotao ya-luotao released this 15 May 15:49
· 677 commits to main since this release

Patch release: README restructure into docs/ topic subpages, plus a sweep of P0/P1 production-hardening fixes across transport, query, sessions, types, and SDK MCP (PR #34). All fixes target accidental behavior — no documented API contracts changed.

Added

  • ServerToolUseBlock + ServerToolResultBlock content blocks for the CLI's built-in server-side tools (web_search, advisor, code_execution). Parser now recognises server_tool_use / server_tool_result.
  • DeferredToolUse class and ResultMessage#deferred_tool_use field — populated when a PreToolUse hook returns permissionDecision: "defer".
  • ResultMessage#api_error_status — integer HTTP status (429, 500, 529) on the api_error subtype (CLI 2.1.110+).
  • ToolPermissionContext gains title, display_name, description, blocked_path, decision_reason so can_use_tool callbacks can render the same prompt UI the CLI would.
  • PostToolUseHookSpecificOutput#updated_tool_output — unified tool-output replacement that works for any tool (MCP or built-in).

Changed

  • README slimmed from 1857 lines to ~285; detail moved into nine topic subpages under docs/ (client.md, mcp-servers.md, hooks-and-permissions.md, configuration.md, sessions.md, observability.md, rails.md, types.md, errors.md). The gemspec now ships docs/**/* so bundle show claude-agent-sdk exposes the full reference offline.
  • Query#start raises CLIConnectionError if invoked outside Async{}. The previous implementation appeared to support synchronous callers but silently hung forever. All documented usage already wraps in Async{}, so this only surfaces a previously hidden failure mode.
  • CommandBuilder raises ArgumentError synchronously when both continue_conversation and resume are set, instead of producing an opaque non-zero CLI exit.
  • sdk_mcp_server's read_resource, get_prompt, dynamic Resource#read, and dynamic Prompt#get now hop through FiberBoundary.invoke (matching call_tool). User reader/generator blocks that touch Thread.current-keyed libraries (ActiveRecord, pg) no longer see the async-gem fiber scheduler.

Fixed

  • Transport @stdin race: write/close are now serialised by a mutex. User callbacks on FiberBoundary threads can no longer race close() and hit NoMethodError on a nilled @stdin. The mutex only guards the reference snapshot — blocking IO happens outside the lock so disconnect is never blocked by a full pipe buffer.
  • Transport stdout poisoning: stdout lines that do not start with { are skipped when the JSON buffer is empty. Previously CLI debug prefixes like [SandboxDebug] accumulated until the 1 MB cap raised CLIJSONDecodeError and killed the session.
  • Transport stderr resilience: each stderr callback invocation is wrapped in rescue StandardError. A transiently failing user logger no longer terminates the read loop and silently stops stderr capture (matches Python SDK v0.2.82 PR #932).
  • Transport process reaping: @process.value is &.-safe and rescues Errno::ECHILD, so a concurrent close() that already reaped the subprocess cannot crash the message loop.
  • Query read-loop cancellation: @task now holds the actual read_messages child task. The previous outer-Async-wrapper finished immediately, so close's @task.stop never reached the read fiber.
  • Query control-request leak: send_control_request cleans @pending_control_* in an ensure block and uses Async::Task.current.with_timeout. An Async::Stop propagating through .wait, or a late control_response after timeout, can no longer leak pending state.
  • Session forks dropped metadata: parse_fork_transcript filters transcript body by TRANSCRIPT_TYPES (user/assistant/attachment/system/progress). custom-title, tag, aiTitle no longer bleed into the forked transcript with the old sessionId.
  • Session forks lost content-replacement history: content-replacement entries accumulate across compaction rounds (concatenated, not overwritten), gated on matching sessionId. Forks emit content-replacement and custom-title entries with uuid+timestamp so a fork-of-a-fork can re-ingest them.
  • delete_session orphans: now FileUtils.rm_rfs the sibling <session-id>/ subagent transcript directory.
  • detect_worktrees hang: 5-second hard cap with SIGKILL fallback. Stdlib Timeout.timeout is unsafe under the Async fiber scheduler (raises across threads via Thread#raise), so the new implementation drains stdout/stderr on side threads and kills the child on deadline. A pipe-buffer-overrun edge case that previously silently lost every worktree path is also fixed.

What's changed

  • #34 Harden production paths: fix P0/P1 bugs across transport, query, sessions, and types — @ya-luotao

Full Changelog: v0.16.7...v0.16.8


gem install claude-agent-sdk -v 0.16.8