Files
Alex Newman 608611b627 fix(sdk): stale Claude CLI can no longer silently kill every observation (#2911)
* fix(sdk): stale Claude CLI can no longer silently kill every observation

findClaudeExecutable validated candidates with --version only, so an
abandoned old install shadowing a current CLI in PATH (e.g. npm-global
2.0.42 next to the auto-updating native installer) passed validation,
then died at every Observer spawn: the SDK passes --permission-mode
dontAsk (hardened-options) which old CLIs reject with exit 1. Result:
healthy worker, zero observations, no visible error (#2782 family;
previously #1857/#2049/#1866/#2142 in the same class).

- Probe every candidate (which -a + known install paths) with
  `--permission-mode dontAsk --version`: one spawn proves both flag
  compatibility and version, no API call (~150ms)
- Prefer the newest capable version; PATH order only breaks ties
- Explicit CLAUDE_CODE_PATH still wins but fails loud with version and
  remedy when too old, instead of dying silently at spawn
- All-too-old throws an error naming each candidate, its version, and
  how to fix; resolution success logs at INFO with the chosen version
- Cache successful resolution 15 min (resolver runs per SDK query);
  never cache failure so a CLI update is picked up without restart
- SDK child keeps a 2KB stderr tail and includes it in the exit WARN,
  so "unknown option" deaths are diagnosable at default log level

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* build: regenerate plugin bundles with stale-CLI resolver fix

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(supervisor): log SDK exit stderr tail on 'close' so it is never truncated

'exit' can fire before piped stderr has drained, so the WARN log could
miss the tail end of the CLI's dying words — the exact text the tail
exists to capture. 'close' waits for all stdio to finish. Registry
unregistration stays on 'exit' since it doesn't read the streams.

Addresses greptile review on PR #2911.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(resolver): cover the broken-candidate path; correct probe-count comment

The broken branch (fails both the capability probe and plain --version)
had no coverage: desktop-app skip warnings, the generic failed-probe
warning, not-found fall-through, and both CLAUDE_CODE_PATH failure
messages are now asserted. Also rewrites the probeCandidate doc comment,
which claimed a single spawn — accurate only for capable CLIs; failing
candidates intentionally get a second plain --version spawn to split
'too old' from 'broken' without pattern-matching stderr.

Addresses greptile review on PR #2911.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 16:52:17 -07:00
..