fix: use literal mode for tmux send-keys, prune stale lifecycle trackers

- Add -l flag to tmux send-keys for short messages so key-like payloads
  (e.g. "Enter", "Escape") are sent as text, not interpreted as keypresses
- Prune states and reactionTrackers maps in pollAll() for sessions that
  no longer appear in the session list, preventing unbounded memory growth
  and stale retry counters from applying to reused session IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Prateek 2026-02-13 21:09:49 +05:30
parent 07b3a553f7
commit 4c47a42b6e
3 changed files with 19 additions and 2 deletions

View File

@ -190,7 +190,7 @@ describe("sendKeys", () => {
expect(escapeArgs).toEqual(["send-keys", "-t", "app-1", "Escape"]);
// Call 1: text
const textArgs = mockExecFile.mock.calls[1][1] as string[];
expect(textArgs).toEqual(["send-keys", "-t", "app-1", "hello world"]);
expect(textArgs).toEqual(["send-keys", "-t", "app-1", "-l", "hello world"]);
// Call 2: Enter
const enterArgs = mockExecFile.mock.calls[2][1] as string[];
expect(enterArgs).toEqual(["send-keys", "-t", "app-1", "Enter"]);

View File

@ -453,6 +453,21 @@ export function createLifecycleManager(deps: LifecycleManagerDeps): LifecycleMan
// Poll all sessions concurrently
await Promise.allSettled(sessionsToCheck.map((s) => checkSession(s)));
// Prune stale entries from states and reactionTrackers for sessions
// that no longer appear in the session list (e.g., after kill/cleanup)
const currentSessionIds = new Set(sessions.map((s) => s.id));
for (const trackedId of states.keys()) {
if (!currentSessionIds.has(trackedId)) {
states.delete(trackedId);
}
}
for (const trackerKey of reactionTrackers.keys()) {
const sessionId = trackerKey.split(":")[0];
if (sessionId && !currentSessionIds.has(sessionId)) {
reactionTrackers.delete(trackerKey);
}
}
// Check if all sessions are complete (emit only once)
const activeSessions = sessions.filter((s) => s.status !== "merged" && s.status !== "killed");
if (sessions.length > 0 && activeSessions.length === 0 && !allCompleteEmitted) {

View File

@ -152,7 +152,9 @@ export async function sendKeys(
}
}
} else {
await tmux("send-keys", "-t", sessionName, text);
// Use -l (literal) to prevent tmux from interpreting text as key names
// (e.g. "Enter", "Escape", "C-c" would be treated as keypresses without -l)
await tmux("send-keys", "-t", sessionName, "-l", text);
}
if (pressEnter) {