This commit is contained in:
Timothy Jaeryang Baek 2026-10-09 00:43:01 +04:00
parent 61c114c158
commit a20b622ba8
3 changed files with 81 additions and 9 deletions

View file

@ -3125,7 +3125,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.
# Connections can keep shell tools advertised across browser disconnects for prompt caching.
shell_tools = {
name: tool
for name, tool in tools_dict.items()
@ -3133,7 +3133,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
and (tool.get('type') == 'terminal' or tool.get('server', {}).get('is_terminal') is True)
}
selected = {
name
name: tool
for name, tool in shell_tools.items()
if terminal_id
and (
@ -3141,9 +3141,21 @@ 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
connected = (
any(
tool.get('user_shell_tools') == 'always'
or (
tool.get('direct')
and (tool.get('server', {}).get('config') or {}).get('user_shell_tools') == 'always'
)
for tool in selected.values()
)
and not metadata.get('automation_id')
and not metadata.get('internal')
)
if (
selected
and not connected
and event_caller
and metadata.get('session_id')
and metadata.get('chat_id')

View file

@ -1507,10 +1507,15 @@ async def get_terminal_tools(
headers[TERMINAL_CONTEXT_HEADER] = context_id
# Fetch live with the user's credentials so prompt changes apply without a restart
terminal_cwd, system_prompt = await asyncio.gather(
get_terminal_cwd(server_data['url'], headers, cookies),
get_terminal_system_prompt(server_data['url'], headers, cookies),
)
connection_config = connection.get('config') or {}
terminal_cwd = None
if connection_config.get('working_directory_context', True):
terminal_cwd, system_prompt = await asyncio.gather(
get_terminal_cwd(server_data['url'], headers, cookies),
get_terminal_system_prompt(server_data['url'], headers, cookies),
)
else:
system_prompt = await get_terminal_system_prompt(server_data['url'], headers, cookies)
if not system_prompt:
system_prompt = server_data.get('system_prompt')
@ -1548,6 +1553,7 @@ async def get_terminal_tools(
'callable': callable,
'spec': tool_spec,
'type': 'terminal',
'user_shell_tools': connection_config.get('user_shell_tools', 'auto'),
}
return tools_dict, system_prompt

View file

@ -41,6 +41,8 @@
let path = '/openapi.json';
let enabled = false;
let chatUploads: 'default' | 'filesystem' = 'default';
let workingDirectoryContext = true;
let userShellTools: 'auto' | 'always' = 'auto';
let chatContextMode: 'default' | 'chat_id' | 'off' = 'default';
let automationContextMode: 'default' | 'automation_id' | 'off' = 'default';
let showAdvanced = false;
@ -78,6 +80,8 @@
const init = () => {
forwardCookies = connection?.forward_cookies ?? false;
workingDirectoryContext = connection?.config?.working_directory_context ?? true;
userShellTools = connection?.config?.user_shell_tools === 'always' ? 'always' : 'auto';
if (connection) {
id = connection?.id ?? '';
url = connection.url;
@ -402,6 +406,8 @@
else delete connectionConfig.contexts;
if (chatUploads === 'filesystem') connectionConfig.chat_uploads = 'filesystem';
else delete connectionConfig.chat_uploads;
if (!direct) connectionConfig.working_directory_context = workingDirectoryContext;
connectionConfig.user_shell_tools = userShellTools;
const result = {
...(!direct && id.trim() ? { id: id.trim() } : {}),
@ -898,7 +904,55 @@
{#if !direct}
<div class="flex items-center justify-between gap-3 mt-2">
<div>
<label for="forward-cookies" class="text-xs text-gray-500">
<label
for="working-directory-context"
class="text-xs font-medium text-gray-900 dark:text-gray-100"
>
{$i18n.t('Working Directory Context')}
</label>
<p class="text-xs text-gray-500">
{$i18n.t('Include the current directory in tool instructions.')}
</p>
</div>
<Switch
id="working-directory-context"
ariaLabel={$i18n.t('Working Directory Context')}
bind:state={workingDirectoryContext}
/>
</div>
{/if}
<div class="flex items-center justify-between gap-3 mt-2">
<div>
<label
for="user-shell-tools"
class="text-xs font-medium text-gray-900 dark:text-gray-100"
>
{$i18n.t('User Shell Tools')}
</label>
<p class="text-xs text-gray-500">
{#if userShellTools === 'auto'}
{$i18n.t('Include tools only while your shell is connected.')}
{:else}
{$i18n.t('Keep tools listed even while your shell is closed.')}
{/if}
</p>
</div>
<select
id="user-shell-tools"
class={`text-xs shrink-0 ${selectClass}`}
bind:value={userShellTools}
>
<option value="auto">{$i18n.t('Automatic')}</option>
<option value="always">{$i18n.t('Always Include')}</option>
</select>
</div>
{#if !direct}
<div class="flex items-center justify-between gap-3 mt-2">
<div>
<label
for="forward-cookies"
class="text-xs font-medium text-gray-900 dark:text-gray-100"
>
{$i18n.t('Forward cookies')}
</label>
<p class="text-xs text-gray-500">
@ -917,7 +971,7 @@
<div class="flex flex-col w-full">
<div class="flex justify-between items-center mb-0.5">
<div class="flex gap-2 items-center">
<div class={`text-xs text-gray-500`}>
<div class="text-xs font-medium text-gray-900 dark:text-gray-100">
{$i18n.t('OpenAPI Spec')}
</div>
</div>