fix(terminal): harden relay lifecycle and session cleanup

- bound relay input and detach saturated output transports
- drain PTY output after child exit
- make terminal close lifecycle race-safe
- prune expired CC sessions and audit closure reasons
- require matching agent hello/auth identities
- validate terminal IDs and PTY dimensions
This commit is contained in:
lda
2026-07-16 08:51:55 +07:00 Verified
parent 536b50220f
commit 3476892cd1
10 changed files with 408 additions and 115 deletions
+32 -22
View File
@@ -570,34 +570,44 @@ export function TerminalPage({
const fallback = remaining.find((item) => !item.operator_attached);
if (closesActiveSession) {
socketRef.current?.send(JSON.stringify({ type: "close" }));
if (socketRef.current?.readyState === WebSocket.OPEN) {
socketRef.current.send(JSON.stringify({ type: "close" }));
}
detachTransport();
}
try {
await closeTerminal(closingId);
} catch (error) {
toast.error("Terminal cleanup failed", { description: String(error) });
} finally {
setSessions(remaining);
setSessionTitles((current) => {
const next = { ...current };
delete next[closingId];
return next;
});
if (
window.sessionStorage.getItem(REMEMBERED_TERMINAL_KEY) === closingId
) {
window.sessionStorage.removeItem(REMEMBERED_TERMINAL_KEY);
}
if (closesActiveSession) {
setSession(null);
activeSessionRef.current = null;
setConnection("idle");
// xterm.clear() deliberately preserves the active cursor line. Closing
// a session should discard its complete screen and terminal modes.
terminalRef.current?.reset();
if (fallback) void activateSession(fallback);
}
void listTerminals()
.then((listed) =>
setSessions((current) => reconcileTerminalSessions(current, listed)),
)
.catch(() => undefined);
return;
}
setSessions((current) =>
current.filter((item) => item.terminal_id !== closingId),
);
setSessionTitles((current) => {
const next = { ...current };
delete next[closingId];
return next;
});
if (
window.sessionStorage.getItem(REMEMBERED_TERMINAL_KEY) === closingId
) {
window.sessionStorage.removeItem(REMEMBERED_TERMINAL_KEY);
}
if (closesActiveSession) {
setSession(null);
activeSessionRef.current = null;
setConnection("idle");
// xterm.clear() deliberately preserves the active cursor line. Closing
// a session should discard its complete screen and terminal modes.
terminalRef.current?.reset();
if (fallback) void activateSession(fallback);
}
}