Problem
When a wrapper adapter (e.g. grok) spawns a harness process that exits immediately with a fatal configuration error, the failure is currently only visible in the adapter's stderr log. The daemon session shows as awaiting input with no output, which looks like a hung agent rather than a launch error.
Example from #1204:
INFO adapter: stderr: Error: --allow "MultiEdit(/Users/moon/.local/share/construct/sessions/<sid>/widgets/**)": unknown tool prefix: MultiEdit
The session state stayed awaiting input / pending_input: true and produced zero transcript events until a human grepped the daemon log.
Where the gap is
- Headless mode already emits
SessionEvent::Error when command.spawn() itself fails, but not when the spawned process exits immediately on stderr.
spawn_stderr_log only calls emit.log(...), which routes to the daemon log, not to the session transcript.
- Interactive mode relies on
run_pty; it is unclear whether an immediate process exit there surfaces as a session error either.
Suggested scope
- Detect when a wrapper adapter's harness process exits with a non-zero status before producing any session output.
- In that case, emit a
SessionEvent::Error carrying the captured stderr (or a clear "harness exited during launch" message) so the UI can show it.
- Consider a per-adapter/tool-prefix validation/feature-detection step at startup so that an unsupported tool name degrades gracefully instead of bricking the harness.
This applies to grok, claude, codex, and any other wrapper adapters that pass tool names or CLI flags that the upstream harness may reject.
Problem
When a wrapper adapter (e.g. grok) spawns a harness process that exits immediately with a fatal configuration error, the failure is currently only visible in the adapter's stderr log. The daemon session shows as
awaiting inputwith no output, which looks like a hung agent rather than a launch error.Example from #1204:
The session state stayed
awaiting input/pending_input: trueand produced zero transcript events until a human grepped the daemon log.Where the gap is
SessionEvent::Errorwhencommand.spawn()itself fails, but not when the spawned process exits immediately on stderr.spawn_stderr_logonly callsemit.log(...), which routes to the daemon log, not to the session transcript.run_pty; it is unclear whether an immediate process exit there surfaces as a session error either.Suggested scope
SessionEvent::Errorcarrying the captured stderr (or a clear "harness exited during launch" message) so the UI can show it.This applies to grok, claude, codex, and any other wrapper adapters that pass tool names or CLI flags that the upstream harness may reject.