Skip to content

v0.21.0

Choose a tag to compare

@ya-luotao ya-luotao released this 03 Jul 17:03
· 589 commits to main since this release

Design-decision fix batch (Batch C) from the 2026-07-03 full-codebase audit (AUDIT-2026-07-03.md), plus three teardown-race hardenings from its adversarial review. Minor (not patch) because three fixes change observable behavior for previously-broken flows: a missing settings file with sandbox no longer raises, a raising initial prompt stream no longer notifies observers, and teardown can now preserve (instead of delete) the materialized resume dir.

Fixed

  • Query#close (and therefore Client#disconnect) is now safe to call from any thread. Calling it from a FiberBoundary worker — e.g. a tool handler, hook, or permission callback ending the session — crashed with NoMethodError (nil Fiber.scheduler) and left the read/child tasks running, hanging the enclosing reactor. A transient reactor-side watcher now serves closes marshaled from foreign threads with identical semantics; once the reactor is gone, close falls back to a direct (fiber-dead-safe) teardown. close is also idempotent now — concurrent or repeated closes no longer re-run teardown against half-torn-down state.
  • Eager-mode mirror failures during the last flush of a stream are reported before the 'end' sentinel: the MirrorErrorMessage for an in-flight dropped batch could previously be enqueued after 'end' and never delivered, violating the documented "failures surface as MirrorErrorMessage" guarantee.
  • Disk session listings no longer report tool-argument text as the session summary/title: the head/tail byte-scans for summary/customTitle/aiTitle/lastPrompt now verify each match against the top level of its containing JSONL line (the same keys the SessionStore fold reads), so keys nested inside tool_use inputs (subagent/teammate tool arguments) are ignored. Lines truncated at the 64KB window edge keep the raw-scan value.
  • Resume-from-store teardown no longer deletes the only copy of turns the mirror failed to persist: when the batcher dropped batches (tracked via TranscriptMirrorBatcher#batches_dropped? / Query#mirror_batches_dropped?, including drains cancelled mid-flight), the materialized temp CLAUDE_CONFIG_DIR is preserved — credential copies scrubbed, transcripts kept, warning with the path — instead of removed.
  • A missing settings file with sandbox set now warns and continues with sandbox-only settings (Python parity) instead of raising CLIConnectionError.
  • Client#connect no longer fires observer on_error for a raising initial Enumerator prompt: input-stream errors are swallowed-with-warn (documented Observer#on_error contract, query() and Python parity). Notifying them also marked still-live OTel traces as failed.
  • Postgres reference adapter (examples/session_stores/postgres_session_store.rb): all DB round-trips now serialize through an internal mutex — mirror appends run on fresh worker threads and can overlap after a send-timeout abandon, which pg's single-connection thread rules forbid. The "a single PG::Connection suffices" concurrency guidance was wrong and has been rewritten.

What's changed

  • #43 Apply audit Batch C: 6 design-decision fixes (M2, M4, M14, M16, L3, L7, M19b) — @ya-luotao

Full Changelog: v0.20.0...v0.21.0


gem install claude-agent-sdk -v 0.21.0