fix: reloading the page or closing the terminal shell drops two tools and breaks the prompt cache

With an Open Terminal connected to a chat, the tools for reading from and typing into the user's shell were only offered while the Shell tab was open in the browser. Reloading the page or closing the shell removed them from the next request, which changed the start of the tool list and made the provider's prompt cache miss for the whole chat. The two tools now stay offered in the chat for as long as the terminal is selected, and calling them with no shell open returns the existing "No connected user shell" result. Automations and sub-agents, which never have a shell, still do not get them.

Fixes #31590
This commit is contained in:
Classic298 2026-09-29 13:15:20 +02:00
parent 176d31d1db
commit ab0d7e904c
4 changed files with 2 additions and 53 deletions

View file

@ -3102,7 +3102,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
if name not in tools_dict:
tools_dict[name] = tool_dict
# Only advertise user-shell tools when the originating browser has a connected shell.
# Automations and sub-agents never have a user's browser shell, so they never get these tools.
shell_tools = {
name: tool
for name, tool in tools_dict.items()
@ -3118,32 +3118,8 @@ async def process_chat_payload(request, form_data, user, metadata, model):
or (tool.get('direct') and tool.get('server', {}).get('url') == terminal_id)
)
}
connected = False
if (
selected
and event_caller
and metadata.get('session_id')
and metadata.get('chat_id')
and not metadata.get('automation_id')
and not metadata.get('internal')
):
try:
state = await asyncio.wait_for(
event_caller(
{
'type': 'request:terminal:state',
'data': {'terminal_id': terminal_id, 'session_id': metadata['session_id']},
}
),
timeout=2,
)
connected = isinstance(state, dict) and state.get('connected') is True
except Exception:
# Old/disconnected browsers cannot confirm availability; other tools still work.
pass
for name in shell_tools:
if not connected or name not in selected:
if name not in selected or metadata.get('automation_id') or metadata.get('internal'):
tools_dict.pop(name)
if tools_dict:

View file

@ -5,7 +5,6 @@
import { WebLinksAddon } from '@xterm/addon-web-links';
import '@xterm/xterm/css/xterm.css';
import { terminalRequest, type TerminalConnection } from '$lib/apis/terminal';
import { connectedUserTerminals } from '$lib/stores';
export let connection: TerminalConnection;
export let chatId: string | null = null;
@ -23,13 +22,8 @@
let pingInterval: ReturnType<typeof setInterval>;
let destroyed = false;
let sessionId = '';
const terminalOwner = Symbol();
function disconnect() {
connectedUserTerminals.update((entries) => {
entries.delete(terminalOwner);
return entries;
});
connected = false;
connecting = false;
clearInterval(pingInterval);
@ -75,10 +69,6 @@
ws?.send(
JSON.stringify({ type: 'auth', token: connection.key.trim(), chat_id: chatId ?? '' })
);
if (connection.selector && chatId) {
const shell = { terminalId: connection.selector, chatId };
connectedUserTerminals.update((entries) => entries.set(terminalOwner, shell));
}
connected = true;
connecting = false;
fit();

View file

@ -146,9 +146,6 @@ export type FileNavOpenRequest = string | { path: string; page?: number | null }
export const showFileNavPath: Writable<FileNavOpenRequest | null> = writable(null);
export const showFileNavDir: Writable<string | null> = writable(null);
export const selectedTerminalId: Writable<string | null> = writable(null);
export const connectedUserTerminals = writable(
new Map<symbol, { terminalId: string; chatId: string }>()
);
export const artifactCode = writable(null);
export const artifactContents = writable(null);

View file

@ -31,7 +31,6 @@
channels,
channelId,
terminalServers,
connectedUserTerminals,
showControls,
showFileNavPath,
showFileNavDir,
@ -562,19 +561,6 @@
};
const chatEventHandler = async (event, cb) => {
// Answer this session's availability check even when another chat is active.
if (
event?.data?.type === 'request:terminal:state' &&
event.data.data?.session_id === $socket?.id
) {
cb?.({
connected: [...$connectedUserTerminals.values()].some(
(shell) =>
shell.terminalId === event.data.data?.terminal_id && shell.chatId === event.chat_id
)
});
return;
}
const chat = $page.url.pathname.includes(`/c/${event.chat_id}`);
// Skip events from temporary chats that are not the current chat.