fix: kill session subprocess on /cancel to break stuck streams (#600)

This commit is contained in:
Cameron
2026-03-15 14:37:14 -07:00
committed by GitHub
parent f1f3540005
commit f04e56d165

View File

@@ -825,12 +825,18 @@ export class LettaBot implements AgentSession {
// Signal the stream loop to break
this.cancelledKeys.add(convKey);
// Abort client-side stream
// Abort client-side stream and kill the session subprocess.
// abort() sends an interrupt control_request, but the CLI may not
// handle it if blocked on a long-running tool (e.g., Task subagent).
// invalidateSession() calls session.close() which kills the subprocess,
// closes the transport pump, and resolves all stream waiters with null
// -- guaranteeing the for-await loop in processMessage breaks.
const session = this.sessionManager.getSession(convKey);
if (session) {
session.abort().catch(() => {});
log.info(`/cancel - aborted session stream (key=${convKey})`);
}
this.sessionManager.invalidateSession(convKey);
// Cancel server-side run (conversation-scoped)
const convId = convKey === 'shared'
@@ -1750,8 +1756,12 @@ export class LettaBot implements AgentSession {
}
lap('stream complete');
// If cancelled, clean up partial state and return early
// If cancelled, clean up partial state and return early.
// Invalidate defensively in case the cancel handler's invalidation
// didn't fire (e.g., race with command dispatch).
if (this.cancelledKeys.has(convKey)) {
this.sessionManager.invalidateSession(convKey);
session = null;
if (messageId) {
try {
await adapter.editMessage(msg.chatId, messageId, '(Run cancelled.)');