diff --git a/test/e2e/live/launch-agent-turn.ts b/test/e2e/live/launch-agent-turn.ts index a364cd7c5d0..9519aea10e2 100644 --- a/test/e2e/live/launch-agent-turn.ts +++ b/test/e2e/live/launch-agent-turn.ts @@ -1264,8 +1264,18 @@ if ! printf '%s\r' "$NEMOCLAW_LAUNCH_SECOND_INPUT" >&3; then fi wait_for_turn_count 2 +exit_command_write_status=0 if [[ -n "$NEMOCLAW_LAUNCH_EXIT_COMMAND" ]]; then - printf '%s\r' "$NEMOCLAW_LAUNCH_EXIT_COMMAND" >&3 + # The TUI may finish cleanly immediately after publishing the two required + # structured turns. Preserve that successful child status even when its + # input reader wins the race with the best-effort exit command. + trap '' PIPE + if printf '%s\r' "$NEMOCLAW_LAUNCH_EXIT_COMMAND" >&3; then + : + else + exit_command_write_status=$? + fi + trap - PIPE else # Some TUIs have no exit command. They may close the FIFO after the first # interrupt, so ignore SIGPIPE while sending the second one. @@ -1285,6 +1295,9 @@ fi session_pid="" if [[ "$launch_status" != 0 ]]; then + if [[ "$exit_command_write_status" != 0 ]]; then + echo "launch PTY closed before the exit command was submitted (status $exit_command_write_status)" >&2 + fi echo "launch exited with status $launch_status" >&2 terminal_diagnostic exit "$launch_status" diff --git a/test/e2e/support/launch-agent-turn.test.ts b/test/e2e/support/launch-agent-turn.test.ts index a3e019fdd9b..53cb57400d8 100644 --- a/test/e2e/support/launch-agent-turn.test.ts +++ b/test/e2e/support/launch-agent-turn.test.ts @@ -522,6 +522,7 @@ if (process.argv[2] !== "tui") { const second = await ask(); append("user", second); append("assistant", "second response"); + if (mode === "delayed-input-attachment") process.exit(0); const exitCommand = await ask(); if (mode === "late-extra") append("user", firstInput); rl.close(); @@ -1043,7 +1044,7 @@ it.runIf(process.platform === "linux").each(["absent", "ansi", "reordered"] as c ); it.runIf(process.platform === "linux")( - "waits for the OpenClaw TUI input mode before submitting PTY input (#9160)", + "waits for OpenClaw input mode and accepts a clean exit after two turns (#9160, #9384)", () => { const { baselineRemoved, result, ttyObserved } = runLaunchSessionFixture( "delayed-input-attachment",