@@ -347,12 +347,20 @@ export class TerminalService {
347347 return this . getAgentTabs ( )
348348 }
349349
350- switchTerminal ( terminalId : string ) : TerminalTabsState {
350+ /**
351+ * Shows a terminal. `claim` records it as the user's own; a switch that only
352+ * mirrors the renderer's resource-strip selection passes false so the agent
353+ * can still close or adopt the shell as its own.
354+ */
355+ switchTerminal (
356+ terminalId : string ,
357+ { claim = true } : { claim ?: boolean } = { }
358+ ) : TerminalTabsState {
351359 if ( ! this . sessions . has ( terminalId ) ) {
352360 throw new TerminalError ( 'NO_SUCH_TERMINAL' , unknownTerminal ( terminalId ) )
353361 }
354362 this . activeId = terminalId
355- this . activeTerminalUserSelected = true
363+ if ( claim ) this . activeTerminalUserSelected = true
356364 this . emitTabs ( )
357365 void this . sessions . get ( terminalId ) ?. refreshCwd ( )
358366 return this . getTabs ( )
@@ -387,19 +395,11 @@ export class TerminalService {
387395 }
388396
389397 /**
390- * Closes a terminal, or resets it when it is the only one left.
391- *
392- * Emptying the panel is not an option the close button should have: the
393- * resource IS a terminal, so a panel with no shell in it is a dead end the
394- * user has to close and reopen to escape. Replacing the last shell with a
395- * fresh one in the same directory gives the button a sensible meaning at
396- * every count — the same shape as closing a browser's last tab, which
397- * leaves you a tab rather than an empty window.
398- *
399- * A shell that ends by itself — `exit`, or Ctrl-D — goes the same way. It
400- * leaves behind a session that can no longer do anything, so it has to be
401- * reaped either way; treating it as a close means the last one is replaced
402- * rather than leaving a dead tab that cannot be typed into.
398+ * Closes a terminal. Each shell is its own resource tab in the renderer, so
399+ * closing the last one simply leaves none; the strip drops the tab and a new
400+ * shell comes back through `+ Terminal` or the agent. A shell that ends by
401+ * itself — `exit`, or Ctrl-D — goes the same way: it leaves behind a session
402+ * that can no longer do anything, so it is reaped like a close.
403403 */
404404 closeTerminal ( terminalId : string ) : TerminalTabsState {
405405 if ( ! this . sessions . has ( terminalId ) ) {
@@ -451,32 +451,24 @@ export class TerminalService {
451451 }
452452
453453 /**
454- * Drops a terminal and decides what replaces it. Closing and exiting share
455- * this so the two cannot drift into different answers for "what happens to
456- * the last one".
454+ * Drops a terminal and moves both cursors to a neighbour. Closing and
455+ * exiting share this so the two cannot drift into different answers.
457456 */
458457 private retire ( terminalId : string ) : TerminalTabsState {
459458 const session = this . sessions . get ( terminalId )
460459 if ( ! session ) return this . getTabs ( )
461460 const closedCwd = session . currentCwd
462- const cols = session . cols
463- const rows = session . rows
464461 const order = [ ...this . sessions . keys ( ) ]
465462 const index = order . indexOf ( terminalId )
466463 session . dispose ( )
467464 this . sessions . delete ( terminalId )
468465 this . tmuxCache . delete ( terminalId )
469466 this . releasePendingRuns ( terminalId )
470467
471- if ( this . sessions . size === 0 ) {
472- this . spawn ( this . resolveCwd ( closedCwd ) , cols , rows , {
473- activateVisible : true ,
474- activateAgent : true ,
475- } )
476- return this . getTabs ( )
477- }
478-
479468 this . rememberClosed ( closedCwd )
469+ // Nothing is left for the user to hold on to; the next shell the agent
470+ // opens must not inherit a claim on a terminal that no longer exists.
471+ if ( this . sessions . size === 0 ) this . activeTerminalUserSelected = false
480472 if ( this . activeId === terminalId ) {
481473 this . activeId = order [ index + 1 ] ?? order [ index - 1 ] ?? null
482474 }
@@ -641,9 +633,17 @@ export class TerminalService {
641633 )
642634 }
643635
644- /** Whether one renderer may close a tab in the terminal panel it displays. */
636+ /**
637+ * Whether one renderer may close a tab. The strip that lists shells sits
638+ * outside the terminal panel, so a renderer on the chat may close a shell
639+ * nobody is displaying; while a window does display the panel, only that
640+ * window may close, so a second window on the same chat cannot end a shell
641+ * someone is using.
642+ */
645643 acceptsUserClose ( owner : WebContents , terminalId : string ) : boolean {
646- return ! owner . isDestroyed ( ) && this . visibleOwner === owner && this . sessions . has ( terminalId )
644+ if ( owner . isDestroyed ( ) || ! this . sessions . has ( terminalId ) ) return false
645+ const shown = this . visibleOwner && ! this . visibleOwner . isDestroyed ( ) ? this . visibleOwner : null
646+ return shown === null || shown === owner
647647 }
648648
649649 /** Drops the claim and unsubscribes from the owner's lifecycle. */
0 commit comments