fix(tui): surface gateway stderr tail in start_timeout activity#17112
Conversation
There was a problem hiding this comment.
Pull request overview
Surfaces recent gateway stderr/log output in the TUI when gateway startup times out, so users can diagnose startup failures without leaving the UI to run /logs.
Changes:
- Extend
gateway.start_timeoutevent payload to optionally includestderr_tail. - Capture a log tail on startup timeout in
GatewayClientand attach it to the timeout event. - Render the tail inline as additional error-toned activity entries; add a regression test.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| ui-tui/src/gatewayTypes.ts | Extends GatewayEvent union for gateway.start_timeout to include optional stderr_tail. |
| ui-tui/src/gatewayClient.ts | Captures getLogTail(20) on startup timeout and publishes it in the timeout event payload. |
| ui-tui/src/app/createGatewayEventHandler.ts | Displays up to 8 lines from stderr_tail as error activity after the timeout banner. |
| ui-tui/src/tests/createGatewayEventHandler.test.ts | Adds test asserting stderr tail lines appear in activity after gateway.start_timeout. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
`gateway.start_timeout` previously published only `cwd` + `python`, which made TUI startup failures hard to disambiguate. The user saw `gateway startup timed out · /path/to/python /repo · /logs to inspect` with no signal whether the actual cause was a wrong python interpreter, a missing dependency, or a config parse failure. Plumb a 20-line stderr tail through the event so the most useful lines land directly in the TUI activity feed, capped to the last 8 non-empty lines for readability: * `gatewayClient.ts` — collect `getLogTail(20)` when the readyTimer fires and attach it as `payload.stderr_tail`. * `gatewayTypes.ts` — extend the `gateway.start_timeout` event union with the new optional field. * `createGatewayEventHandler.ts` — emit the trimmed lines after the existing `gateway startup timed out` activity entry, classified `error`. Tests: regression test in `createGatewayEventHandler.test.ts` checks that `ModuleNotFoundError` / `FileNotFoundError` lines from the tail land in `getTurnState().activity` so they show up in the UI immediately. Validation: `npm run type-check` clean, `npm test --run` 390/390.
3fe86a0 to
76a3ecc
Compare
There was a problem hiding this comment.
Pull request overview
Improves TUI startup-timeout diagnostics by attaching a tail of recent gateway stderr/log output to the gateway.start_timeout event and rendering the most relevant lines inline in the activity feed.
Changes:
- Extend
gateway.start_timeoutevent payload with optionalstderr_tail. - Capture
getLogTail(20)on startup timeout and include it in the published event. - Render the last 8 non-empty stderr tail lines (trimmed, capped to 120 chars) as error activity entries, and add a regression test.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| ui-tui/src/gatewayTypes.ts | Extends the GatewayEvent union to include optional stderr_tail for gateway.start_timeout. |
| ui-tui/src/gatewayClient.ts | Captures recent log tail on startup timeout and publishes it as part of the timeout event payload. |
| ui-tui/src/app/createGatewayEventHandler.ts | Displays trimmed, non-empty tail lines (max 8, 120-char cap) as inline error activity entries on startup timeout. |
| ui-tui/src/tests/createGatewayEventHandler.test.ts | Adds a regression test ensuring stderr tail lines are surfaced into activity on gateway.start_timeout. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
* fix(tui): append gateway stderr tail to start_timeout activity `gateway.start_timeout` previously published only `cwd` + `python`, which made TUI startup failures hard to disambiguate. The user saw `gateway startup timed out · /path/to/python /repo · /logs to inspect` with no signal whether the actual cause was a wrong python interpreter, a missing dependency, or a config parse failure. Plumb a 20-line stderr tail through the event so the most useful lines land directly in the TUI activity feed, capped to the last 8 non-empty lines for readability: * `gatewayClient.ts` — collect `getLogTail(20)` when the readyTimer fires and attach it as `payload.stderr_tail`. * `gatewayTypes.ts` — extend the `gateway.start_timeout` event union with the new optional field. * `createGatewayEventHandler.ts` — emit the trimmed lines after the existing `gateway startup timed out` activity entry, classified `error`. Tests: regression test in `createGatewayEventHandler.test.ts` checks that `ModuleNotFoundError` / `FileNotFoundError` lines from the tail land in `getTurnState().activity` so they show up in the UI immediately. Validation: `npm run type-check` clean, `npm test --run` 390/390. * review(copilot): filter blanks before slice and cap stderr tail at 120 chars
…Research#17112) * fix(tui): append gateway stderr tail to start_timeout activity `gateway.start_timeout` previously published only `cwd` + `python`, which made TUI startup failures hard to disambiguate. The user saw `gateway startup timed out · /path/to/python /repo · /logs to inspect` with no signal whether the actual cause was a wrong python interpreter, a missing dependency, or a config parse failure. Plumb a 20-line stderr tail through the event so the most useful lines land directly in the TUI activity feed, capped to the last 8 non-empty lines for readability: * `gatewayClient.ts` — collect `getLogTail(20)` when the readyTimer fires and attach it as `payload.stderr_tail`. * `gatewayTypes.ts` — extend the `gateway.start_timeout` event union with the new optional field. * `createGatewayEventHandler.ts` — emit the trimmed lines after the existing `gateway startup timed out` activity entry, classified `error`. Tests: regression test in `createGatewayEventHandler.test.ts` checks that `ModuleNotFoundError` / `FileNotFoundError` lines from the tail land in `getTurnState().activity` so they show up in the UI immediately. Validation: `npm run type-check` clean, `npm test --run` 390/390. * review(copilot): filter blanks before slice and cap stderr tail at 120 chars
…Research#17112) * fix(tui): append gateway stderr tail to start_timeout activity `gateway.start_timeout` previously published only `cwd` + `python`, which made TUI startup failures hard to disambiguate. The user saw `gateway startup timed out · /path/to/python /repo · /logs to inspect` with no signal whether the actual cause was a wrong python interpreter, a missing dependency, or a config parse failure. Plumb a 20-line stderr tail through the event so the most useful lines land directly in the TUI activity feed, capped to the last 8 non-empty lines for readability: * `gatewayClient.ts` — collect `getLogTail(20)` when the readyTimer fires and attach it as `payload.stderr_tail`. * `gatewayTypes.ts` — extend the `gateway.start_timeout` event union with the new optional field. * `createGatewayEventHandler.ts` — emit the trimmed lines after the existing `gateway startup timed out` activity entry, classified `error`. Tests: regression test in `createGatewayEventHandler.test.ts` checks that `ModuleNotFoundError` / `FileNotFoundError` lines from the tail land in `getTurnState().activity` so they show up in the UI immediately. Validation: `npm run type-check` clean, `npm test --run` 390/390. * review(copilot): filter blanks before slice and cap stderr tail at 120 chars
…Research#17112) * fix(tui): append gateway stderr tail to start_timeout activity `gateway.start_timeout` previously published only `cwd` + `python`, which made TUI startup failures hard to disambiguate. The user saw `gateway startup timed out · /path/to/python /repo · /logs to inspect` with no signal whether the actual cause was a wrong python interpreter, a missing dependency, or a config parse failure. Plumb a 20-line stderr tail through the event so the most useful lines land directly in the TUI activity feed, capped to the last 8 non-empty lines for readability: * `gatewayClient.ts` — collect `getLogTail(20)` when the readyTimer fires and attach it as `payload.stderr_tail`. * `gatewayTypes.ts` — extend the `gateway.start_timeout` event union with the new optional field. * `createGatewayEventHandler.ts` — emit the trimmed lines after the existing `gateway startup timed out` activity entry, classified `error`. Tests: regression test in `createGatewayEventHandler.test.ts` checks that `ModuleNotFoundError` / `FileNotFoundError` lines from the tail land in `getTurnState().activity` so they show up in the UI immediately. Validation: `npm run type-check` clean, `npm test --run` 390/390. * review(copilot): filter blanks before slice and cap stderr tail at 120 chars
…Research#17112) * fix(tui): append gateway stderr tail to start_timeout activity `gateway.start_timeout` previously published only `cwd` + `python`, which made TUI startup failures hard to disambiguate. The user saw `gateway startup timed out · /path/to/python /repo · /logs to inspect` with no signal whether the actual cause was a wrong python interpreter, a missing dependency, or a config parse failure. Plumb a 20-line stderr tail through the event so the most useful lines land directly in the TUI activity feed, capped to the last 8 non-empty lines for readability: * `gatewayClient.ts` — collect `getLogTail(20)` when the readyTimer fires and attach it as `payload.stderr_tail`. * `gatewayTypes.ts` — extend the `gateway.start_timeout` event union with the new optional field. * `createGatewayEventHandler.ts` — emit the trimmed lines after the existing `gateway startup timed out` activity entry, classified `error`. Tests: regression test in `createGatewayEventHandler.test.ts` checks that `ModuleNotFoundError` / `FileNotFoundError` lines from the tail land in `getTurnState().activity` so they show up in the UI immediately. Validation: `npm run type-check` clean, `npm test --run` 390/390. * review(copilot): filter blanks before slice and cap stderr tail at 120 chars
…Research#17112) * fix(tui): append gateway stderr tail to start_timeout activity `gateway.start_timeout` previously published only `cwd` + `python`, which made TUI startup failures hard to disambiguate. The user saw `gateway startup timed out · /path/to/python /repo · /logs to inspect` with no signal whether the actual cause was a wrong python interpreter, a missing dependency, or a config parse failure. Plumb a 20-line stderr tail through the event so the most useful lines land directly in the TUI activity feed, capped to the last 8 non-empty lines for readability: * `gatewayClient.ts` — collect `getLogTail(20)` when the readyTimer fires and attach it as `payload.stderr_tail`. * `gatewayTypes.ts` — extend the `gateway.start_timeout` event union with the new optional field. * `createGatewayEventHandler.ts` — emit the trimmed lines after the existing `gateway startup timed out` activity entry, classified `error`. Tests: regression test in `createGatewayEventHandler.test.ts` checks that `ModuleNotFoundError` / `FileNotFoundError` lines from the tail land in `getTurnState().activity` so they show up in the UI immediately. Validation: `npm run type-check` clean, `npm test --run` 390/390. * review(copilot): filter blanks before slice and cap stderr tail at 120 chars
Summary
gateway.start_timeoutpreviously carried onlycwd+python. When the TUI gateway failed to start withinHERMES_TUI_STARTUP_TIMEOUT_MS, users saw a generic banner with no clue whether the cause was a wrong Python interpreter, a missing dependency, or a config-parse failure — and had to leave the TUI to run `/logs`.This appends the most recent gateway stderr lines to the timeout event and renders them inline in the activity feed.
Changes
gatewayClient.ts— capturegetLogTail(20)when the readyTimer fires and attach it aspayload.stderr_tail.gatewayTypes.ts— extend thegateway.start_timeoutevent union with the new optional field.createGatewayEventHandler.ts— emit the trimmed last 8 non-empty lines aserror-toned activity entries, after the existinggateway startup timed outline.Why this matters
Most TUI startup failures are recoverable once the user sees the actual exception. ~ZERO cost when startup succeeds; bounded to the existing 200-line CircularBuffer when it doesn't.
HERMES_TUI_STARTUP_TIMEOUT_MSwas already configurable but undocumented — this fix doubles down on diagnostics so users rarely need to bump it.Test plan
npm run type-check— cleannpm test -- --run— 390/390 passcreateGatewayEventHandler.test.tsverifiesModuleNotFoundError/FileNotFoundErrorlines from the tail land ingetTurnState().activity.