Skip to content

Commit 764ff45

Browse files
author
Brendan Gray
committed
S7-10 re-apply: tool budget fix, tool toggles panel, header spacer, titlebar height
1 parent 1da37a5 commit 764ff45

5 files changed

Lines changed: 167 additions & 104 deletions

File tree

electron-main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ function createWindow() {
287287
titleBarOverlay: {
288288
color: '#1e1e1e',
289289
symbolColor: '#cccccc',
290-
height: 34,
290+
height: 32,
291291
},
292292
backgroundColor: '#1e1e1e',
293293
show: false,

main/pipeline/agenticLoop.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,19 @@ async function handleLocalChat(ctx, message, context, helpers) {
8989

9090
// Calculate budget splits
9191
const maxResponseTokens = Math.min(Math.floor(totalCtx * 0.25), 4096);
92-
const toolCount = typeof mcpToolServer.getToolDefinitions === 'function' ? mcpToolServer.getToolDefinitions().length : 20;
93-
const sysPromptReserve = Math.max(500, toolCount * 55);
94-
let maxPromptTokens = Math.max(totalCtx - sysPromptReserve - maxResponseTokens, 256);
95-
96-
console.log(`[AgenticLoop] Context budget: total=${totalCtx}, sysReserve=${sysPromptReserve}, maxPrompt=${maxPromptTokens}, maxResponse=${maxResponseTokens}`);
9792

98-
// Get compact tool hint
93+
// Get compact tool hint FIRST so we can measure its actual size for budget
9994
const toolHint = typeof mcpToolServer.getCompactToolHint === 'function'
10095
? mcpToolServer.getCompactToolHint('general')
10196
: '';
10297

98+
// Compute sysPromptReserve from ACTUAL measured sizes (not per-tool estimate)
99+
const toolHintTokens = estimateTokens(toolHint);
100+
const sysPromptReserve = Math.max(500, toolHintTokens + 600); // +600 for preamble + project context + buffer
101+
let maxPromptTokens = Math.max(totalCtx - sysPromptReserve - maxResponseTokens, 256);
102+
103+
console.log(`[AgenticLoop] Context budget: total=${totalCtx}, sysReserve=${sysPromptReserve} (toolHint=${toolHintTokens}tok), maxPrompt=${maxPromptTokens}, maxResponse=${maxResponseTokens}`);
104+
103105
// Select preamble based on model size/context
104106
const contextIsConstrained = totalCtx < 8192;
105107
const useCompact = isSmallModel || contextIsConstrained;

src/components/Chat/ChatPanel.tsx

Lines changed: 5 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,16 +2452,18 @@ ${e.message}`,
24522452
/>
24532453
) : (
24542454
<>
2455-
{/* Header — justify-between puts title left, buttons right */}
2455+
{/* Header — spacer pushes buttons to right edge */}
24562456
<div
2457-
className={`h-[32px] flex items-center justify-between px-2 ${navigator.userAgent.includes('Electron') ? 'pr-[140px]' : 'pr-2'} flex-shrink-0`}
2457+
className={`h-[32px] flex items-center px-2 ${navigator.userAgent.includes('Electron') ? 'pr-[140px]' : 'pr-2'} flex-shrink-0`}
24582458
style={{ backgroundColor: 'var(--theme-bg-secondary)', borderBottom: '1px solid var(--theme-border)' }}
24592459
>
24602460
<div className="flex items-center flex-shrink-0">
24612461
<Sparkles size={14} className="mr-2 flex-shrink-0" style={{ color: 'var(--theme-accent)' }} />
24622462
<span className="text-[12px] font-semibold whitespace-nowrap brand-font" style={{ color: 'var(--theme-foreground)' }}>gu<span style={{ color: 'var(--theme-accent)' }}>IDE</span></span>
24632463
</div>
2464-
{/* Action buttons — justify-between places these at right edge */}
2464+
{/* Spacer — pushes action buttons to the right */}
2465+
<div className="flex-1 min-w-0" />
2466+
{/* Action buttons — pinned right by spacer */}
24652467
<div className="flex items-center gap-[1px] flex-shrink-0 chat-header-buttons">
24662468

24672469
<button
@@ -2835,98 +2837,6 @@ ${e.message}`,
28352837
{licenseMessage}
28362838
</p>
28372839
)}
2838-
2839-
{/* Tool Toggles */}
2840-
<div className="border-t border-[#3c3c3c] my-2" />
2841-
<p className="text-[10px] text-[#858585] mb-1 uppercase tracking-wider flex items-center gap-1.5">
2842-
<Settings size={10} /> Tools
2843-
<span className="ml-auto text-[9px] text-[#585858] normal-case tracking-normal">
2844-
{66 - disabledTools.length}/{66} enabled
2845-
</span>
2846-
</p>
2847-
<p className="text-[10px] text-[#585858] mb-2">Toggle tools the AI can use. Disabled tools are hidden from the model and rejected at execution.</p>
2848-
{(() => {
2849-
const toolCategories: Record<string, string[]> = {
2850-
'File Operations': ['read_file', 'write_file', 'edit_file', 'append_to_file', 'delete_file', 'rename_file', 'copy_file', 'list_directory', 'find_files', 'create_directory', 'get_project_structure', 'get_file_info', 'open_file_in_editor', 'diff_files'],
2851-
'Search': ['grep_search', 'search_in_file', 'search_codebase', 'replace_in_files'],
2852-
'Terminal': ['run_command', 'check_port', 'install_packages'],
2853-
'Web': ['web_search', 'fetch_webpage', 'http_request'],
2854-
'Browser': ['browser_navigate', 'browser_snapshot', 'browser_click', 'browser_type', 'browser_fill_form', 'browser_select_option', 'browser_evaluate', 'browser_scroll', 'browser_back', 'browser_press_key', 'browser_hover', 'browser_drag', 'browser_screenshot', 'browser_get_content', 'browser_get_url', 'browser_get_links', 'browser_tabs', 'browser_handle_dialog', 'browser_console_messages', 'browser_file_upload', 'browser_resize', 'browser_wait', 'browser_wait_for', 'browser_close'],
2855-
'Git': ['git_status', 'git_commit', 'git_diff', 'git_log', 'git_branch', 'git_stash', 'git_reset'],
2856-
'Code Analysis': ['analyze_error'],
2857-
'Undo': ['undo_edit', 'list_undoable'],
2858-
'Memory': ['save_memory', 'get_memory', 'list_memories'],
2859-
'Planning': ['write_todos', 'update_todo'],
2860-
'Scratchpad': ['write_scratchpad', 'read_scratchpad'],
2861-
'Image Generation': ['generate_image'],
2862-
};
2863-
const allToolNames = Object.values(toolCategories).flat();
2864-
return (
2865-
<div className="space-y-0.5 max-h-[200px] overflow-auto">
2866-
{Object.entries(toolCategories).map(([category, tools]) => {
2867-
const enabledInCat = tools.filter(t => !disabledTools.includes(t)).length;
2868-
const allEnabled = enabledInCat === tools.length;
2869-
const noneEnabled = enabledInCat === 0;
2870-
return (
2871-
<details key={category} className="group">
2872-
<summary className="flex items-center gap-1.5 py-0.5 cursor-pointer select-none text-[10px] text-[#cccccc] hover:text-white list-none">
2873-
<ChevronDown size={10} className="transition-transform group-open:rotate-0 -rotate-90 text-[#585858]" />
2874-
<span className="flex-1">{category} <span className="text-[#585858]">({enabledInCat}/{tools.length})</span></span>
2875-
<button
2876-
onClick={(e) => {
2877-
e.preventDefault();
2878-
e.stopPropagation();
2879-
if (allEnabled) {
2880-
setDisabledTools(prev => [...new Set([...prev, ...tools])]);
2881-
} else {
2882-
setDisabledTools(prev => prev.filter(t => !tools.includes(t)));
2883-
}
2884-
}}
2885-
className="text-[9px] px-1.5 py-0 rounded transition-colors"
2886-
style={{
2887-
color: allEnabled ? '#585858' : '#3794ff',
2888-
backgroundColor: 'transparent',
2889-
}}
2890-
onMouseEnter={e => { e.currentTarget.style.backgroundColor = '#ffffff10'; }}
2891-
onMouseLeave={e => { e.currentTarget.style.backgroundColor = 'transparent'; }}
2892-
>
2893-
{allEnabled ? 'Disable all' : 'Enable all'}
2894-
</button>
2895-
</summary>
2896-
<div className="pl-4 pb-1 space-y-0">
2897-
{tools.map(toolName => {
2898-
const isEnabled = !disabledTools.includes(toolName);
2899-
return (
2900-
<label key={toolName} className="flex items-center gap-1.5 py-[1px] cursor-pointer text-[10px] hover:bg-[#ffffff06] rounded px-1 -mx-1">
2901-
<div
2902-
onClick={() => toggleTool(toolName)}
2903-
className="w-[26px] h-[13px] rounded-full relative transition-colors flex-shrink-0 cursor-pointer"
2904-
style={{ backgroundColor: isEnabled ? '#007acc' : '#3c3c3c' }}
2905-
>
2906-
<div
2907-
className="w-[9px] h-[9px] rounded-full bg-white absolute top-[2px] transition-all"
2908-
style={{ left: isEnabled ? '15px' : '2px' }}
2909-
/>
2910-
</div>
2911-
<span style={{ color: isEnabled ? '#cccccc' : '#585858' }}>{toolName}</span>
2912-
</label>
2913-
);
2914-
})}
2915-
</div>
2916-
</details>
2917-
);
2918-
})}
2919-
{disabledTools.length > 0 && (
2920-
<button
2921-
onClick={() => setDisabledTools([])}
2922-
className="text-[9px] text-[#3794ff] hover:text-[#4da6ff] hover:underline mt-1 cursor-pointer"
2923-
>
2924-
Enable all tools
2925-
</button>
2926-
)}
2927-
</div>
2928-
);
2929-
})()}
29302840
</div>
29312841
)}
29322842

src/components/Chat/hooks/useChatSettings.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,27 @@ export function useChatSettings(): ChatSettings & ChatSettingsActions {
6767
const [ttsEnabled, setTtsEnabled] = useState(false);
6868
const [autoMode, setAutoMode] = useState(false);
6969
const [planMode, setPlanMode] = useState(false);
70-
const [disabledTools, setDisabledTools] = useState<string[]>([]);
70+
const [disabledTools, setDisabledTools] = useState<string[]>([
71+
// Non-essential tools disabled by default — users can enable in Settings > Tools
72+
'delete_file', 'rename_file', 'copy_file', 'create_directory', 'get_file_info',
73+
'open_file_in_editor', 'diff_files',
74+
'search_in_file', 'replace_in_files',
75+
'check_port', 'install_packages',
76+
'http_request',
77+
'browser_fill_form', 'browser_select_option', 'browser_evaluate',
78+
'browser_scroll', 'browser_back', 'browser_press_key', 'browser_hover',
79+
'browser_drag', 'browser_screenshot', 'browser_get_content',
80+
'browser_get_url', 'browser_get_links', 'browser_tabs',
81+
'browser_handle_dialog', 'browser_console_messages', 'browser_file_upload',
82+
'browser_resize', 'browser_wait', 'browser_wait_for', 'browser_close',
83+
'git_status', 'git_commit', 'git_diff', 'git_log', 'git_branch',
84+
'git_stash', 'git_reset',
85+
'analyze_error',
86+
'undo_edit', 'list_undoable',
87+
'list_memories',
88+
'write_scratchpad', 'read_scratchpad',
89+
'generate_image',
90+
]);
7191
const [cloudProvider, setCloudProvider] = useState<string | null>(() => {
7292
try { return localStorage.getItem('guide-cloud-provider') || null; } catch { return null; }
7393
});
@@ -194,7 +214,26 @@ export function useChatSettings(): ChatSettings & ChatSettingsActions {
194214
setReasoningEffort('medium');
195215
setThinkingBudget(0);
196216
setMaxIterations(100);
197-
setDisabledTools([]);
217+
setDisabledTools([
218+
'delete_file', 'rename_file', 'copy_file', 'create_directory', 'get_file_info',
219+
'open_file_in_editor', 'diff_files',
220+
'search_in_file', 'replace_in_files',
221+
'check_port', 'install_packages',
222+
'http_request',
223+
'browser_fill_form', 'browser_select_option', 'browser_evaluate',
224+
'browser_scroll', 'browser_back', 'browser_press_key', 'browser_hover',
225+
'browser_drag', 'browser_screenshot', 'browser_get_content',
226+
'browser_get_url', 'browser_get_links', 'browser_tabs',
227+
'browser_handle_dialog', 'browser_console_messages', 'browser_file_upload',
228+
'browser_resize', 'browser_wait', 'browser_wait_for', 'browser_close',
229+
'git_status', 'git_commit', 'git_diff', 'git_log', 'git_branch',
230+
'git_stash', 'git_reset',
231+
'analyze_error',
232+
'undo_edit', 'list_undoable',
233+
'list_memories',
234+
'write_scratchpad', 'read_scratchpad',
235+
'generate_image',
236+
]);
198237
(window as any).electronAPI?.llmSetContextSize?.(0);
199238
(window as any).electronAPI?.llmSetReasoningEffort?.('medium');
200239
}, []);

src/components/Settings/AdvancedSettingsPanel.tsx

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ interface SettingsState {
3838
cloudProvider: string;
3939
cloudApiKey: string;
4040
cloudModel: string;
41+
// Tools
42+
disabledTools: string[];
4143
}
4244

4345
const DEFAULTS: SettingsState = {
@@ -72,6 +74,26 @@ const DEFAULTS: SettingsState = {
7274
cloudProvider: 'none',
7375
cloudApiKey: '',
7476
cloudModel: '',
77+
disabledTools: [
78+
'delete_file', 'rename_file', 'copy_file', 'create_directory', 'get_file_info',
79+
'open_file_in_editor', 'diff_files',
80+
'search_in_file', 'replace_in_files',
81+
'check_port', 'install_packages',
82+
'http_request',
83+
'browser_fill_form', 'browser_select_option', 'browser_evaluate',
84+
'browser_scroll', 'browser_back', 'browser_press_key', 'browser_hover',
85+
'browser_drag', 'browser_screenshot', 'browser_get_content',
86+
'browser_get_url', 'browser_get_links', 'browser_tabs',
87+
'browser_handle_dialog', 'browser_console_messages', 'browser_file_upload',
88+
'browser_resize', 'browser_wait', 'browser_wait_for', 'browser_close',
89+
'git_status', 'git_commit', 'git_diff', 'git_log', 'git_branch',
90+
'git_stash', 'git_reset',
91+
'analyze_error',
92+
'undo_edit', 'list_undoable',
93+
'list_memories',
94+
'write_scratchpad', 'read_scratchpad',
95+
'generate_image',
96+
],
7597
};
7698

7799
const CLOUD_PROVIDERS = [
@@ -604,6 +626,96 @@ export const AdvancedSettingsPanel: React.FC = () => {
604626
<ToggleField label="Format on Type" value={settings.formatOnType} onChange={v => update('formatOnType', v)} />
605627
</Section>
606628

629+
{/* ── Tools ───────────────────────────── */}
630+
<Section title="Tools" defaultOpen={false}>
631+
<p className="text-[10px] mb-2" style={{ color: 'var(--theme-foreground-muted)' }}>
632+
Toggle which tools the AI can use. Disabled tools are hidden from the model. {66 - settings.disabledTools.length}/66 enabled.
633+
</p>
634+
{(() => {
635+
const toolCategories: Record<string, string[]> = {
636+
'File Operations': ['read_file', 'write_file', 'edit_file', 'append_to_file', 'delete_file', 'rename_file', 'copy_file', 'list_directory', 'find_files', 'create_directory', 'get_project_structure', 'get_file_info', 'open_file_in_editor', 'diff_files'],
637+
'Search': ['grep_search', 'search_in_file', 'search_codebase', 'replace_in_files'],
638+
'Terminal': ['run_command', 'check_port', 'install_packages'],
639+
'Web': ['web_search', 'fetch_webpage', 'http_request'],
640+
'Browser': ['browser_navigate', 'browser_snapshot', 'browser_click', 'browser_type', 'browser_fill_form', 'browser_select_option', 'browser_evaluate', 'browser_scroll', 'browser_back', 'browser_press_key', 'browser_hover', 'browser_drag', 'browser_screenshot', 'browser_get_content', 'browser_get_url', 'browser_get_links', 'browser_tabs', 'browser_handle_dialog', 'browser_console_messages', 'browser_file_upload', 'browser_resize', 'browser_wait', 'browser_wait_for', 'browser_close'],
641+
'Git': ['git_status', 'git_commit', 'git_diff', 'git_log', 'git_branch', 'git_stash', 'git_reset'],
642+
'Code Analysis': ['analyze_error'],
643+
'Undo': ['undo_edit', 'list_undoable'],
644+
'Memory': ['save_memory', 'get_memory', 'list_memories'],
645+
'Planning': ['write_todos', 'update_todo'],
646+
'Scratchpad': ['write_scratchpad', 'read_scratchpad'],
647+
'Image Generation': ['generate_image'],
648+
};
649+
return (
650+
<div className="space-y-0.5 max-h-[300px] overflow-auto">
651+
{Object.entries(toolCategories).map(([category, tools]) => {
652+
const enabledInCat = tools.filter(t => !settings.disabledTools.includes(t)).length;
653+
const allEnabled = enabledInCat === tools.length;
654+
return (
655+
<details key={category} className="group">
656+
<summary className="flex items-center gap-1.5 py-0.5 cursor-pointer select-none text-[10px] list-none" style={{ color: 'var(--theme-foreground)' }}>
657+
<span style={{ transition: 'transform 0.15s', display: 'inline-block', transform: 'rotate(-90deg)' }} className="group-open:!rotate-0">
658+
<ChevronDown size={10} />
659+
</span>
660+
<span className="flex-1">{category} <span style={{ color: 'var(--theme-foreground-muted)' }}>({enabledInCat}/{tools.length})</span></span>
661+
<button
662+
onClick={(e) => {
663+
e.preventDefault();
664+
e.stopPropagation();
665+
if (allEnabled) {
666+
update('disabledTools', [...new Set([...settings.disabledTools, ...tools])]);
667+
} else {
668+
update('disabledTools', settings.disabledTools.filter(t => !tools.includes(t)));
669+
}
670+
}}
671+
className="text-[9px] px-1.5 py-0 rounded transition-colors"
672+
style={{ color: allEnabled ? 'var(--theme-foreground-muted)' : 'var(--theme-accent)', backgroundColor: 'transparent' }}
673+
>
674+
{allEnabled ? 'Disable all' : 'Enable all'}
675+
</button>
676+
</summary>
677+
<div className="pl-4 pb-1 space-y-0">
678+
{tools.map(toolName => {
679+
const isEnabled = !settings.disabledTools.includes(toolName);
680+
return (
681+
<label key={toolName} className="flex items-center gap-1.5 py-[1px] cursor-pointer text-[10px] rounded px-1 -mx-1" style={{ color: isEnabled ? 'var(--theme-foreground)' : 'var(--theme-foreground-muted)' }}>
682+
<div
683+
onClick={() => {
684+
const newDisabled = isEnabled
685+
? [...settings.disabledTools, toolName]
686+
: settings.disabledTools.filter(t => t !== toolName);
687+
update('disabledTools', newDisabled);
688+
}}
689+
className="w-[26px] h-[13px] rounded-full relative transition-colors flex-shrink-0 cursor-pointer"
690+
style={{ backgroundColor: isEnabled ? 'var(--theme-accent)' : 'var(--theme-bg-tertiary, #3c3c3c)' }}
691+
>
692+
<div
693+
className="w-[9px] h-[9px] rounded-full bg-white absolute top-[2px] transition-all"
694+
style={{ left: isEnabled ? '15px' : '2px' }}
695+
/>
696+
</div>
697+
<span>{toolName}</span>
698+
</label>
699+
);
700+
})}
701+
</div>
702+
</details>
703+
);
704+
})}
705+
{settings.disabledTools.length > 0 && (
706+
<button
707+
onClick={() => update('disabledTools', [])}
708+
className="text-[9px] hover:underline mt-1 cursor-pointer"
709+
style={{ color: 'var(--theme-accent)' }}
710+
>
711+
Enable all tools
712+
</button>
713+
)}
714+
</div>
715+
);
716+
})()}
717+
</Section>
718+
607719
{/* Footer spacer */}
608720
<div className="h-4" />
609721
</div>

0 commit comments

Comments
 (0)