mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* fix(daemon): classify Claude mid-stream socket drops as retryable
When the Claude Code CLI's connection to the Anthropic API (or a custom
ANTHROPIC_BASE_URL relay) drops mid-response, the SDK surfaces 'The socket
connection was closed unexpectedly...'. The daemon relayed this raw SDK
string verbatim into the error bubble because diagnoseClaudeCliFailure had
no branch for an established-then-dropped connection (distinct from the
existing connection-refused case).
Add a connection-drop branch that classifies socket-close / ECONNRESET /
socket hang up / fetch failed / premature close into a human-readable,
retryable diagnostic, pointing at the proxy when a custom endpoint is set
and at the network otherwise. Most visible on long, large generations.
The fake-agent CLI gains a 'Return a daemon socket-drop failure' mode whose
claude shape was captured from the real Claude Code CLI 2.1.168 driven at a
socket-dropping endpoint (error on stdout as a synthetic assistant block +
result{is_error:true}, exit 1, empty stderr), so the e2e path exercises the
real failure shape rather than a guessed one.
* fix(daemon): use OpenClaude socket-drop diagnostics
Generated-By: looper 0.9.7 (runner=fixer, agent=opencode)
* fix(daemon): harden Claude connection-drop diagnostic from real repro
Reproduced end-to-end against the real Claude Code CLI (2.1.168, official
auth, no ANTHROPIC_BASE_URL) by tunneling through a local proxy that resets
the TLS stream mid-response. Two real error faces observed: 'The socket
connection was closed unexpectedly' (SSE cut) and 'Unable to connect to API
(ECONNRESET)' (tunnel reset); the CLI retries internally for minutes before
surfacing either, which is why long runs appear to abort after a while.
Broaden the connection-drop matcher with 'unable to connect to api' and
ETIMEDOUT, and lock the real ECONNRESET transcript into the test suite.
* fix(daemon): narrow Claude API drop matcher
Generated-By: looper 0.9.7 (runner=fixer, agent=opencode)
* feat(daemon,web): structured AGENT_CONNECTION_DROPPED code + localized message
Promote the Claude connection-drop diagnostic from a raw English string to a
stable, machine-readable failure class so the web can localize it and triage
can count it by code.
- contracts: add AGENT_CONNECTION_DROPPED to API_ERROR_CODES.
- daemon: ClaudeCliDiagnostic carries an optional code; the connection-drop
branches stamp AGENT_CONNECTION_DROPPED; server.ts prefers diagnostic.code
over the generic service-failure sniff so the run records the specific class
(flows into run_finished error_code + routine_runs.error_code for triage).
- web: resolveRunFailureUi maps AGENT_CONNECTION_DROPPED (any agent) to the
localized chat.connectionDropped message, plain retry, no AMR promotion.
- i18n: add chat.connectionDropped to types.ts and all 19 locale bundles.
* fix(daemon): classify Claude in-stream connection-drop error frame
End-to-end repro on a real daemon + fake Claude CLI revealed the diagnostic
never fired: Claude surfaces a connection drop as an in-stream `error` frame
(assistant error:"unknown" + the raw SDK string), which claude-stream emits
as a type:'error' event. The daemon's stream-error handler sent that raw
string verbatim as a non-retryable AGENT_EXECUTION_FAILED and set
agentStreamError, so the child-exit diagnoseClaudeCliFailure path was skipped
entirely.
Run the same per-agent diagnostic in the stream-error handler so a connection
drop emits AGENT_CONNECTION_DROPPED (retryable, with localizable copy +
triage-countable code) instead of the raw SDK string. Verified end-to-end via
the real-daemon-run Playwright test, which now asserts the localized card copy.