fix(tui-agents): distinguish missing tmux from dead sessions in sweep and reconcile - #3169
fix(tui-agents): distinguish missing tmux from dead sessions in sweep and reconcile#3169Tyagiquamar wants to merge 2 commits into
Conversation
… and reconcile On machines without tmux, listTmuxSessions collapsed capability-unavailable (ENOENT, exit 127) into an empty activity map. The sweep then treated tmux-backed sessions as idle-dead and evicted them, and the reconcile gate suspended both tmux-backed and non-tmux active intents as process-lost, so reopened TUI conversations came back blank. Represent the distinction at the pty service boundary with TmuxUnavailableError/isTmuxMissingError: successful empty enumeration still means zero live sessions, while an unavailable executable is unknown liveness. The runtime keeps (sweep) or defers (reconcile gate) tmux-backed sessions while unknown, and non-tmux sessions resume without consulting tmux at all.
Greptile SummaryThis PR distinguishes an unavailable tmux executable from an empty tmux server and adds deferred lifecycle reconciliation for unknown liveness. However, the newly introduced typed error is not recognized at the runtime boundary, so missing tmux still globally vetoes reconciliation.
Confidence Score: 4/5This PR is not safe to merge until the runtime can recognize the wrapped unavailable-tmux error and resume non-tmux intents when tmux is missing. The central reconcile fix remains ineffective because the classifier rejects the Files Needing Attention: packages/core/src/services/pty/api/tmux-commands.ts, packages/core/src/runtimes/tui-agents/node/runtime/runtime.ts, packages/core/src/runtimes/tui-agents/node/runtime/runtime.test.ts
|
| Filename | Overview |
|---|---|
| packages/core/src/services/pty/api/tmux-commands.ts | Introduces the unavailable-tmux error contract, but its exported classifier cannot recognize the typed error produced by the same module. |
| packages/core/src/runtimes/tui-agents/node/runtime/runtime.ts | Adds unknown-liveness handling for sweep and reconcile, but the reconcile path cannot reach it after the typed error is wrapped. |
| packages/core/src/runtimes/tui-agents/node/runtime/runtime.test.ts | Adds relevant regression tests, but does not combine a missing tmux executable with a non-tmux active intent. |
| packages/core/src/services/session-lifecycle/node/session-lifecycle.ts | Correctly adds a gate verdict that leaves deferred intents untouched. |
| packages/core/src/services/pty/api/tmux.ts | Preserves creation and discovery behavior by treating the typed unavailable error as an empty inventory. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Execute tmux list-sessions] --> B[Raw ENOENT failure]
B --> C[listTmuxSessions wraps TmuxUnavailableError]
C --> D[Runtime calls isTmuxMissingError]
D -->|Wrapper is not recognized| E[Generic failure path]
E --> F[Reconcile vetoes all intents]
F --> G[Non-tmux intents do not resume]
D -.->|Intended behavior| H[Set tmuxUnknown]
H --> I[Defer tmux intents]
H --> J[Resume non-tmux intents]
Prompt To Fix All With AI
### Issue 1
packages/core/src/services/pty/api/tmux-commands.ts:27-32
**Wrapped error bypasses detection**
When the tmux executable is missing, `listTmuxSessions` wraps the spawn failure in `TmuxUnavailableError`. However, `isTmuxMissingError` only checks errors with an `exitCode` or `code`, so the runtime treats this wrapper as an ordinary failure and vetoes the entire reconciliation. As a result, non-tmux intents still do not resume on machines without tmux. The new tests cover non-tmux resumption and missing tmux separately, so they do not catch this combined failure path.
```suggestion
export function isTmuxMissingError(error: unknown): boolean {
if (error instanceof TmuxUnavailableError) return true;
const failure = readExecFailure(error);
if (!failure) return false;
if (failure.executableMissing) return true;
return failure.exitCode === 127;
}
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "fix(tui-agents): distinguish missing tmu..." | Re-trigger Greptile
| export function isTmuxMissingError(error: unknown): boolean { | ||
| const failure = readExecFailure(error); | ||
| if (!failure) return false; | ||
| if (failure.executableMissing) return true; | ||
| return failure.exitCode === 127; | ||
| } |
There was a problem hiding this comment.
Wrapped error bypasses detection
When the tmux executable is missing, listTmuxSessions wraps the spawn failure in TmuxUnavailableError. However, isTmuxMissingError only checks errors with an exitCode or code, so the runtime treats this wrapper as an ordinary failure and vetoes the entire reconciliation. As a result, non-tmux intents still do not resume on machines without tmux. The new tests cover non-tmux resumption and missing tmux separately, so they do not catch this combined failure path.
| export function isTmuxMissingError(error: unknown): boolean { | |
| const failure = readExecFailure(error); | |
| if (!failure) return false; | |
| if (failure.executableMissing) return true; | |
| return failure.exitCode === 127; | |
| } | |
| export function isTmuxMissingError(error: unknown): boolean { | |
| if (error instanceof TmuxUnavailableError) return true; | |
| const failure = readExecFailure(error); | |
| if (!failure) return false; | |
| if (failure.executableMissing) return true; | |
| return failure.exitCode === 127; | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/src/services/pty/api/tmux-commands.ts
Line: 27-32
Comment:
**Wrapped error bypasses detection**
When the tmux executable is missing, `listTmuxSessions` wraps the spawn failure in `TmuxUnavailableError`. However, `isTmuxMissingError` only checks errors with an `exitCode` or `code`, so the runtime treats this wrapper as an ordinary failure and vetoes the entire reconciliation. As a result, non-tmux intents still do not resume on machines without tmux. The new tests cover non-tmux resumption and missing tmux separately, so they do not catch this combined failure path.
```suggestion
export function isTmuxMissingError(error: unknown): boolean {
if (error instanceof TmuxUnavailableError) return true;
const failure = readExecFailure(error);
if (!failure) return false;
if (failure.executableMissing) return true;
return failure.exitCode === 127;
}
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
listTmuxSessionswraps missing-tmux failures inTmuxUnavailableError, andisTmuxMissingErrordid not recognize that wrapper, so reconcile vetoed the whole run.
Confirmed. isTmuxMissingError now treats TmuxUnavailableError as missing tmux. Added a combined regression: tmux unavailable + an active non-tmux intent still resumes, while a tmux-backed intent stays deferred/active instead of being suspended.
listTmuxSessions wraps spawn failures in TmuxUnavailableError, but isTmuxMissingError only inspected raw exec shapes. Recognize the wrapper so reconcile still resumes non-tmux intents instead of vetoing the whole run.
Fixes #3109.
Problem
On machines without tmux, \listTmuxSessions\ collapsed capability-unavailable (ENOENT, exit 127) into an empty activity map. Consequences in \TuiAgentsRuntime:
Change
esolveTmuxSession/\indTmuxSessionNamesByIdentity\ (creation/discovery): treat unknown as not-found, so spawn surfaces the honest failure downstream. Only \listTmuxSessionActivity\ propagates for sweep/reconcile judgments.
Verification
untime.test.ts\ 38/38; pty, session-lifecycle green); \pnpm run typecheck\ green for all 9 projects.