From 920dcd105f32e9907f2004074ed99346abba7bc1 Mon Sep 17 00:00:00 2001 From: Stephan Rieche Date: Sat, 27 Dec 2025 12:24:28 +0100 Subject: [PATCH 1/7] feat: add configurable sandbox mode setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a global setting to enable/disable sandbox mode for Claude Agent SDK. This allows users to control sandbox behavior based on their authentication setup and system compatibility. Changes: - Add enableSandboxMode to GlobalSettings (default: true) - Add sandbox mode checkbox in Claude settings UI - Wire up setting through app store and settings service - Update createChatOptions and createAutoModeOptions to use setting - Add getEnableSandboxModeSetting helper function - Remove hardcoded sandbox configuration from ClaudeProvider - Add detailed logging throughout agent execution flow The sandbox mode requires API key or OAuth token authentication. Users experiencing issues with CLI-only auth can disable it in settings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- apps/server/src/index.ts | 25 +++- apps/server/src/lib/sdk-options.ts | 27 +++-- apps/server/src/lib/settings-helpers.ts | 28 +++++ apps/server/src/providers/claude-provider.ts | 111 ++++++++++++++++-- apps/server/src/routes/agent/routes/send.ts | 15 +++ apps/server/src/services/agent-service.ts | 69 ++++++++++- apps/server/src/services/auto-mode-service.ts | 10 +- .../ui/src/components/views/settings-view.tsx | 4 + .../claude/claude-md-settings.tsx | 32 ++++- apps/ui/src/store/app-store.ts | 10 ++ libs/types/src/settings.ts | 3 + 11 files changed, 308 insertions(+), 26 deletions(-) diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 6b63ffa8..188e2883 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -190,12 +190,31 @@ server.on('upgrade', (request, socket, head) => { // Events WebSocket connection handler wss.on('connection', (ws: WebSocket) => { - console.log('[WebSocket] Client connected'); + console.log('[WebSocket] Client connected, ready state:', ws.readyState); // Subscribe to all events and forward to this client const unsubscribe = events.subscribe((type, payload) => { + console.log('[WebSocket] Event received:', { + type, + hasPayload: !!payload, + payloadKeys: payload ? Object.keys(payload) : [], + wsReadyState: ws.readyState, + wsOpen: ws.readyState === WebSocket.OPEN, + }); + if (ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify({ type, payload })); + const message = JSON.stringify({ type, payload }); + console.log('[WebSocket] Sending event to client:', { + type, + messageLength: message.length, + sessionId: (payload as any)?.sessionId, + }); + ws.send(message); + } else { + console.log( + '[WebSocket] WARNING: Cannot send event, WebSocket not open. ReadyState:', + ws.readyState + ); } }); @@ -205,7 +224,7 @@ wss.on('connection', (ws: WebSocket) => { }); ws.on('error', (error) => { - console.error('[WebSocket] Error:', error); + console.error('[WebSocket] ERROR:', error); unsubscribe(); }); }); diff --git a/apps/server/src/lib/sdk-options.ts b/apps/server/src/lib/sdk-options.ts index e7fc3578..80433f5b 100644 --- a/apps/server/src/lib/sdk-options.ts +++ b/apps/server/src/lib/sdk-options.ts @@ -216,6 +216,9 @@ export interface CreateSdkOptionsConfig { /** Enable auto-loading of CLAUDE.md files via SDK's settingSources */ autoLoadClaudeMd?: boolean; + + /** Enable sandbox mode for bash command isolation */ + enableSandboxMode?: boolean; } /** @@ -314,7 +317,7 @@ export function createSuggestionsOptions(config: CreateSdkOptionsConfig): Option * - Full tool access for code modification * - Standard turns for interactive sessions * - Model priority: explicit model > session model > chat default - * - Sandbox enabled for bash safety + * - Sandbox mode controlled by enableSandboxMode setting * - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createChatOptions(config: CreateSdkOptionsConfig): Options { @@ -333,10 +336,12 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options { maxTurns: MAX_TURNS.standard, cwd: config.cwd, allowedTools: [...TOOL_PRESETS.chat], - sandbox: { - enabled: true, - autoAllowBashIfSandboxed: true, - }, + ...(config.enableSandboxMode && { + sandbox: { + enabled: true, + autoAllowBashIfSandboxed: true, + }, + }), ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), }; @@ -349,7 +354,7 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options { * - Full tool access for code modification and implementation * - Extended turns for thorough feature implementation * - Uses default model (can be overridden) - * - Sandbox enabled for bash safety + * - Sandbox mode controlled by enableSandboxMode setting * - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options { @@ -365,10 +370,12 @@ export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options { maxTurns: MAX_TURNS.maximum, cwd: config.cwd, allowedTools: [...TOOL_PRESETS.fullAccess], - sandbox: { - enabled: true, - autoAllowBashIfSandboxed: true, - }, + ...(config.enableSandboxMode && { + sandbox: { + enabled: true, + autoAllowBashIfSandboxed: true, + }, + }), ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), }; diff --git a/apps/server/src/lib/settings-helpers.ts b/apps/server/src/lib/settings-helpers.ts index 9c4456ff..8c6e3073 100644 --- a/apps/server/src/lib/settings-helpers.ts +++ b/apps/server/src/lib/settings-helpers.ts @@ -45,6 +45,34 @@ export async function getAutoLoadClaudeMdSetting( } } +/** + * Get the enableSandboxMode setting from global settings. + * Returns false if settings service is not available. + * + * @param settingsService - Optional settings service instance + * @param logPrefix - Prefix for log messages (e.g., '[AgentService]') + * @returns Promise resolving to the enableSandboxMode setting value + */ +export async function getEnableSandboxModeSetting( + settingsService?: SettingsService | null, + logPrefix = '[SettingsHelper]' +): Promise { + if (!settingsService) { + console.log(`${logPrefix} SettingsService not available, sandbox mode disabled`); + return false; + } + + try { + const globalSettings = await settingsService.getGlobalSettings(); + const result = globalSettings.enableSandboxMode ?? false; + console.log(`${logPrefix} enableSandboxMode from global settings: ${result}`); + return result; + } catch (error) { + console.error(`${logPrefix} Failed to load enableSandboxMode setting:`, error); + throw error; + } +} + /** * Filters out CLAUDE.md from context files when autoLoadClaudeMd is enabled * and rebuilds the formatted prompt without it. diff --git a/apps/server/src/providers/claude-provider.ts b/apps/server/src/providers/claude-provider.ts index 9237cdf6..563dd70c 100644 --- a/apps/server/src/providers/claude-provider.ts +++ b/apps/server/src/providers/claude-provider.ts @@ -23,6 +23,8 @@ export class ClaudeProvider extends BaseProvider { * Execute a query using Claude Agent SDK */ async *executeQuery(options: ExecuteOptions): AsyncGenerator { + console.log('[ClaudeProvider] executeQuery() called'); + const { prompt, model, @@ -35,6 +37,20 @@ export class ClaudeProvider extends BaseProvider { sdkSessionId, } = options; + console.log('[ClaudeProvider] Options:', { + model, + cwd, + maxTurns, + promptType: typeof prompt, + promptLength: typeof prompt === 'string' ? prompt.length : 'array', + hasSystemPrompt: !!systemPrompt, + systemPromptLength: systemPrompt?.length, + hasConversationHistory: !!conversationHistory?.length, + conversationHistoryLength: conversationHistory?.length || 0, + sdkSessionId, + allowedToolsCount: allowedTools?.length, + }); + // Build Claude SDK options const defaultTools = ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch']; const toolsToUse = allowedTools || defaultTools; @@ -45,11 +61,7 @@ export class ClaudeProvider extends BaseProvider { maxTurns, cwd, allowedTools: toolsToUse, - permissionMode: 'acceptEdits', - sandbox: { - enabled: true, - autoAllowBashIfSandboxed: true, - }, + permissionMode: 'default', abortController, // Resume existing SDK session if we have a session ID ...(sdkSessionId && conversationHistory && conversationHistory.length > 0 @@ -59,6 +71,15 @@ export class ClaudeProvider extends BaseProvider { ...(options.settingSources && { settingSources: options.settingSources }), }; + console.log('[ClaudeProvider] SDK options prepared:', { + model: sdkOptions.model, + maxTurns: sdkOptions.maxTurns, + permissionMode: sdkOptions.permissionMode, + sandboxEnabled: sdkOptions.sandbox?.enabled || false, + hasResume: !!(sdkOptions as any).resume, + toolsCount: sdkOptions.allowedTools?.length, + }); + // Build prompt payload let promptPayload: string | AsyncIterable; @@ -83,14 +104,84 @@ export class ClaudeProvider extends BaseProvider { // Execute via Claude Agent SDK try { - const stream = query({ prompt: promptPayload, options: sdkOptions }); + console.log('[ClaudeProvider] ANTHROPIC_API_KEY exists:', !!process.env.ANTHROPIC_API_KEY); + console.log( + '[ClaudeProvider] ANTHROPIC_API_KEY length:', + process.env.ANTHROPIC_API_KEY?.length || 0 + ); + console.log('[ClaudeProvider] HOME directory:', process.env.HOME); + console.log('[ClaudeProvider] User:', process.env.USER); + console.log('[ClaudeProvider] Current working directory:', process.cwd()); - // Stream messages directly - they're already in the correct format - for await (const msg of stream) { - yield msg as ProviderMessage; + // CRITICAL DEBUG: Log exact SDK options being passed + console.log('[ClaudeProvider] EXACT sdkOptions being passed to query():'); + console.log( + JSON.stringify( + { + model: sdkOptions.model, + maxTurns: sdkOptions.maxTurns, + cwd: sdkOptions.cwd, + allowedTools: sdkOptions.allowedTools, + permissionMode: sdkOptions.permissionMode, + hasSandbox: !!sdkOptions.sandbox, + hasAbortController: !!sdkOptions.abortController, + hasResume: !!(sdkOptions as any).resume, + hasSettingSources: !!sdkOptions.settingSources, + settingSources: sdkOptions.settingSources, + }, + null, + 2 + ) + ); + + console.log('[ClaudeProvider] Calling Claude Agent SDK query()...'); + console.log( + '[ClaudeProvider] About to call query() with prompt payload type:', + typeof promptPayload + ); + + const stream = query({ prompt: promptPayload, options: sdkOptions }); + console.log('[ClaudeProvider] query() call returned, stream object type:', typeof stream); + + console.log('[ClaudeProvider] SDK query() returned stream, starting iteration...'); + let streamMessageCount = 0; + + // Add a watchdog timer to detect if stream is hanging + let lastMessageTime = Date.now(); + const watchdogInterval = setInterval(() => { + const timeSinceLastMessage = Date.now() - lastMessageTime; + if (timeSinceLastMessage > 10000) { + console.log( + `[ClaudeProvider] WARNING: No messages received for ${Math.floor(timeSinceLastMessage / 1000)}s` + ); + } + }, 5000); + + try { + // Stream messages directly - they're already in the correct format + for await (const msg of stream) { + lastMessageTime = Date.now(); + streamMessageCount++; + console.log(`[ClaudeProvider] Stream message #${streamMessageCount}:`, { + type: msg.type, + subtype: (msg as any).subtype, + hasMessage: !!(msg as any).message, + hasResult: !!(msg as any).result, + session_id: msg.session_id, + }); + yield msg as ProviderMessage; + } + } finally { + clearInterval(watchdogInterval); } + + console.log( + '[ClaudeProvider] Stream iteration completed, total messages:', + streamMessageCount + ); } catch (error) { - console.error('[ClaudeProvider] executeQuery() error during execution:', error); + console.error('[ClaudeProvider] ERROR: executeQuery() error during execution:', error); + console.error('[ClaudeProvider] ERROR stack:', (error as Error).stack); throw error; } } diff --git a/apps/server/src/routes/agent/routes/send.ts b/apps/server/src/routes/agent/routes/send.ts index 0dd2f424..35c1e88a 100644 --- a/apps/server/src/routes/agent/routes/send.ts +++ b/apps/server/src/routes/agent/routes/send.ts @@ -19,7 +19,16 @@ export function createSendHandler(agentService: AgentService) { model?: string; }; + console.log('[Send Handler] Received request:', { + sessionId, + messageLength: message?.length, + workingDirectory, + imageCount: imagePaths?.length || 0, + model, + }); + if (!sessionId || !message) { + console.log('[Send Handler] ERROR: Validation failed - missing sessionId or message'); res.status(400).json({ success: false, error: 'sessionId and message are required', @@ -27,6 +36,8 @@ export function createSendHandler(agentService: AgentService) { return; } + console.log('[Send Handler] Validation passed, calling agentService.sendMessage()'); + // Start the message processing (don't await - it streams via WebSocket) agentService .sendMessage({ @@ -37,12 +48,16 @@ export function createSendHandler(agentService: AgentService) { model, }) .catch((error) => { + console.error('[Send Handler] ERROR: Background error in sendMessage():', error); logError(error, 'Send message failed (background)'); }); + console.log('[Send Handler] Returning immediate response to client'); + // Return immediately - responses come via WebSocket res.json({ success: true, message: 'Message sent' }); } catch (error) { + console.error('[Send Handler] ERROR: Synchronous error:', error); logError(error, 'Send message failed'); res.status(500).json({ success: false, error: getErrorMessage(error) }); } diff --git a/apps/server/src/services/agent-service.ts b/apps/server/src/services/agent-service.ts index 5afddcd9..966519d4 100644 --- a/apps/server/src/services/agent-service.ts +++ b/apps/server/src/services/agent-service.ts @@ -17,7 +17,11 @@ import { ProviderFactory } from '../providers/provider-factory.js'; import { createChatOptions, validateWorkingDirectory } from '../lib/sdk-options.js'; import { PathNotAllowedError } from '@automaker/platform'; import type { SettingsService } from './settings-service.js'; -import { getAutoLoadClaudeMdSetting, filterClaudeMdFromContext } from '../lib/settings-helpers.js'; +import { + getAutoLoadClaudeMdSetting, + getEnableSandboxModeSetting, + filterClaudeMdFromContext, +} from '../lib/settings-helpers.js'; interface Message { id: string; @@ -140,12 +144,29 @@ export class AgentService { imagePaths?: string[]; model?: string; }) { + console.log('[AgentService] sendMessage() called:', { + sessionId, + messageLength: message?.length, + workingDirectory, + imageCount: imagePaths?.length || 0, + model, + }); + const session = this.sessions.get(sessionId); if (!session) { + console.error('[AgentService] ERROR: Session not found:', sessionId); throw new Error(`Session ${sessionId} not found`); } + console.log('[AgentService] Session found:', { + sessionId, + messageCount: session.messages.length, + isRunning: session.isRunning, + workingDirectory: session.workingDirectory, + }); + if (session.isRunning) { + console.error('[AgentService] ERROR: Agent already running for session:', sessionId); throw new Error('Agent is already processing a message'); } @@ -192,16 +213,19 @@ export class AgentService { session.abortController = new AbortController(); // Emit started event so UI can show thinking indicator + console.log('[AgentService] Emitting "started" event for session:', sessionId); this.emitAgentEvent(sessionId, { type: 'started', }); // Emit user message event + console.log('[AgentService] Emitting "message" event for session:', sessionId); this.emitAgentEvent(sessionId, { type: 'message', message: userMessage, }); + console.log('[AgentService] Saving session messages'); await this.saveSession(sessionId, session.messages); try { @@ -215,6 +239,12 @@ export class AgentService { '[AgentService]' ); + // Load enableSandboxMode setting (global setting only) + const enableSandboxMode = await getEnableSandboxModeSetting( + this.settingsService, + '[AgentService]' + ); + // Load project context files (CLAUDE.md, CODE_QUALITY.md, etc.) const contextResult = await loadContextFiles({ projectPath: effectiveWorkDir, @@ -239,6 +269,7 @@ export class AgentService { systemPrompt: combinedSystemPrompt, abortController: session.abortController!, autoLoadClaudeMd, + enableSandboxMode, }); // Extract model, maxTurns, and allowedTools from SDK options @@ -247,6 +278,7 @@ export class AgentService { const allowedTools = sdkOptions.allowedTools as string[] | undefined; // Get provider for this model + console.log('[AgentService] Getting provider for model:', effectiveModel); const provider = ProviderFactory.getProviderForModel(effectiveModel); console.log( @@ -267,6 +299,7 @@ export class AgentService { sdkSessionId: session.sdkSessionId, // Pass SDK session ID for resuming }; + console.log('[AgentService] Building prompt with images...'); // Build prompt content with images const { content: promptContent } = await buildPromptWithImages( message, @@ -278,14 +311,32 @@ export class AgentService { // Set the prompt in options options.prompt = promptContent; + console.log('[AgentService] Executing query via provider:', { + model: effectiveModel, + promptLength: typeof promptContent === 'string' ? promptContent.length : 'array', + hasConversationHistory: !!conversationHistory.length, + sdkSessionId: session.sdkSessionId, + }); + // Execute via provider const stream = provider.executeQuery(options); + console.log('[AgentService] Stream created, starting to iterate...'); let currentAssistantMessage: Message | null = null; let responseText = ''; const toolUses: Array<{ name: string; input: unknown }> = []; + let messageCount = 0; + console.log('[AgentService] Entering stream loop...'); for await (const msg of stream) { + messageCount++; + console.log(`[AgentService] Stream message #${messageCount}:`, { + type: msg.type, + subtype: (msg as any).subtype, + hasContent: !!(msg as any).message?.content, + session_id: msg.session_id, + }); + // Capture SDK session ID from any message and persist it if (msg.session_id && !session.sdkSessionId) { session.sdkSessionId = msg.session_id; @@ -295,6 +346,7 @@ export class AgentService { } if (msg.type === 'assistant') { + console.log('[AgentService] Processing assistant message...'); if (msg.message?.content) { for (const block of msg.message.content) { if (block.type === 'text') { @@ -312,6 +364,10 @@ export class AgentService { currentAssistantMessage.content = responseText; } + console.log( + '[AgentService] Emitting "stream" event, text length:', + responseText.length + ); this.emitAgentEvent(sessionId, { type: 'stream', messageId: currentAssistantMessage.id, @@ -325,6 +381,7 @@ export class AgentService { }; toolUses.push(toolUse); + console.log('[AgentService] Tool use detected:', toolUse.name); this.emitAgentEvent(sessionId, { type: 'tool_use', tool: toolUse, @@ -333,6 +390,7 @@ export class AgentService { } } } else if (msg.type === 'result') { + console.log('[AgentService] Result message received, subtype:', (msg as any).subtype); if (msg.subtype === 'success' && msg.result) { if (currentAssistantMessage) { currentAssistantMessage.content = msg.result; @@ -340,6 +398,7 @@ export class AgentService { } } + console.log('[AgentService] Emitting "complete" event'); this.emitAgentEvent(sessionId, { type: 'complete', messageId: currentAssistantMessage?.id, @@ -349,6 +408,8 @@ export class AgentService { } } + console.log('[AgentService] Stream loop completed, total messages:', messageCount); + await this.saveSession(sessionId, session.messages); session.isRunning = false; @@ -757,7 +818,13 @@ export class AgentService { } private emitAgentEvent(sessionId: string, data: Record): void { + console.log('[AgentService] emitAgentEvent() called:', { + sessionId, + eventType: data.type, + dataKeys: Object.keys(data), + }); this.events.emit('agent:stream', { sessionId, ...data }); + console.log('[AgentService] Event emitted to EventEmitter'); } private getSystemPrompt(): string { diff --git a/apps/server/src/services/auto-mode-service.ts b/apps/server/src/services/auto-mode-service.ts index bcdb92a8..c0ba7bfb 100644 --- a/apps/server/src/services/auto-mode-service.ts +++ b/apps/server/src/services/auto-mode-service.ts @@ -32,7 +32,11 @@ import { } from '../lib/sdk-options.js'; import { FeatureLoader } from './feature-loader.js'; import type { SettingsService } from './settings-service.js'; -import { getAutoLoadClaudeMdSetting, filterClaudeMdFromContext } from '../lib/settings-helpers.js'; +import { + getAutoLoadClaudeMdSetting, + getEnableSandboxModeSetting, + filterClaudeMdFromContext, +} from '../lib/settings-helpers.js'; const execAsync = promisify(exec); @@ -1833,12 +1837,16 @@ This mock response was generated because AUTOMAKER_MOCK_AGENT=true was set. ? options.autoLoadClaudeMd : await getAutoLoadClaudeMdSetting(finalProjectPath, this.settingsService, '[AutoMode]'); + // Load enableSandboxMode setting (global setting only) + const enableSandboxMode = await getEnableSandboxModeSetting(this.settingsService, '[AutoMode]'); + // Build SDK options using centralized configuration for feature implementation const sdkOptions = createAutoModeOptions({ cwd: workDir, model: model, abortController, autoLoadClaudeMd, + enableSandboxMode, }); // Extract model, maxTurns, and allowedTools from SDK options diff --git a/apps/ui/src/components/views/settings-view.tsx b/apps/ui/src/components/views/settings-view.tsx index b888c9b6..6ea52add 100644 --- a/apps/ui/src/components/views/settings-view.tsx +++ b/apps/ui/src/components/views/settings-view.tsx @@ -50,6 +50,8 @@ export function SettingsView() { setValidationModel, autoLoadClaudeMd, setAutoLoadClaudeMd, + enableSandboxMode, + setEnableSandboxMode, } = useAppStore(); // Hide usage tracking when using API key (only show for Claude Code CLI users) @@ -108,6 +110,8 @@ export function SettingsView() { {showUsageTracking && } diff --git a/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx b/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx index 920984be..c2a6a3db 100644 --- a/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx +++ b/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx @@ -1,11 +1,13 @@ import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; -import { FileCode } from 'lucide-react'; +import { FileCode, Shield } from 'lucide-react'; import { cn } from '@/lib/utils'; interface ClaudeMdSettingsProps { autoLoadClaudeMd: boolean; onAutoLoadClaudeMdChange: (enabled: boolean) => void; + enableSandboxMode: boolean; + onEnableSandboxModeChange: (enabled: boolean) => void; } /** @@ -25,6 +27,8 @@ interface ClaudeMdSettingsProps { export function ClaudeMdSettings({ autoLoadClaudeMd, onAutoLoadClaudeMdChange, + enableSandboxMode, + onEnableSandboxModeChange, }: ClaudeMdSettingsProps) { return (
+ +
+ onEnableSandboxModeChange(checked === true)} + className="mt-1" + data-testid="enable-sandbox-mode-checkbox" + /> +
+ +

+ Run bash commands in an isolated sandbox environment for additional security. + + Note: On some systems, enabling sandbox mode may cause the agent to hang without + responding. If you experience issues, try disabling this option. + +

+
+
); diff --git a/apps/ui/src/store/app-store.ts b/apps/ui/src/store/app-store.ts index 874e1a6d..c4d7e2ca 100644 --- a/apps/ui/src/store/app-store.ts +++ b/apps/ui/src/store/app-store.ts @@ -480,6 +480,7 @@ export interface AppState { // Claude Agent SDK Settings autoLoadClaudeMd: boolean; // Auto-load CLAUDE.md files using SDK's settingSources option + enableSandboxMode: boolean; // Enable sandbox mode for bash commands (may cause issues on some systems) // Project Analysis projectAnalysis: ProjectAnalysis | null; @@ -756,6 +757,7 @@ export interface AppActions { // Claude Agent SDK Settings actions setAutoLoadClaudeMd: (enabled: boolean) => Promise; + setEnableSandboxMode: (enabled: boolean) => Promise; // AI Profile actions addAIProfile: (profile: Omit) => void; @@ -929,6 +931,7 @@ const initialState: AppState = { enhancementModel: 'sonnet', // Default to sonnet for feature enhancement validationModel: 'opus', // Default to opus for GitHub issue validation autoLoadClaudeMd: false, // Default to disabled (user must opt-in) + enableSandboxMode: true, // Default to enabled for security (can be disabled if issues occur) aiProfiles: DEFAULT_AI_PROFILES, projectAnalysis: null, isAnalyzing: false, @@ -1561,6 +1564,12 @@ export const useAppStore = create()( const { syncSettingsToServer } = await import('@/hooks/use-settings-migration'); await syncSettingsToServer(); }, + setEnableSandboxMode: async (enabled) => { + set({ enableSandboxMode: enabled }); + // Sync to server settings file + const { syncSettingsToServer } = await import('@/hooks/use-settings-migration'); + await syncSettingsToServer(); + }, // AI Profile actions addAIProfile: (profile) => { @@ -2706,6 +2715,7 @@ export const useAppStore = create()( enhancementModel: state.enhancementModel, validationModel: state.validationModel, autoLoadClaudeMd: state.autoLoadClaudeMd, + enableSandboxMode: state.enableSandboxMode, // Profiles and sessions aiProfiles: state.aiProfiles, chatSessions: state.chatSessions, diff --git a/libs/types/src/settings.ts b/libs/types/src/settings.ts index e73e7269..a0a58e21 100644 --- a/libs/types/src/settings.ts +++ b/libs/types/src/settings.ts @@ -301,6 +301,8 @@ export interface GlobalSettings { // Claude Agent SDK Settings /** Auto-load CLAUDE.md files using SDK's settingSources option */ autoLoadClaudeMd?: boolean; + /** Enable sandbox mode for bash commands (default: true, disable if issues occur) */ + enableSandboxMode?: boolean; } /** @@ -459,6 +461,7 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = { worktreePanelCollapsed: false, lastSelectedSessionByProject: {}, autoLoadClaudeMd: false, + enableSandboxMode: true, }; /** Default credentials (empty strings - user must provide API keys) */ From 94e166636b09e431187c98f873967deb30b331ef Mon Sep 17 00:00:00 2001 From: Stephan Rieche Date: Sat, 27 Dec 2025 12:55:43 +0100 Subject: [PATCH 2/7] fix: set consistent default for enableSandboxMode to true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default value should be 'true' to match the defaults in libs/types/src/settings.ts and apps/ui/src/store/app-store.ts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- apps/server/src/lib/settings-helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/lib/settings-helpers.ts b/apps/server/src/lib/settings-helpers.ts index 8c6e3073..dc057873 100644 --- a/apps/server/src/lib/settings-helpers.ts +++ b/apps/server/src/lib/settings-helpers.ts @@ -64,7 +64,7 @@ export async function getEnableSandboxModeSetting( try { const globalSettings = await settingsService.getGlobalSettings(); - const result = globalSettings.enableSandboxMode ?? false; + const result = globalSettings.enableSandboxMode ?? true; console.log(`${logPrefix} enableSandboxMode from global settings: ${result}`); return result; } catch (error) { From 348a4d95e920cdab55d867a6e7f3a83367f7ef59 Mon Sep 17 00:00:00 2001 From: Stephan Rieche Date: Sat, 27 Dec 2025 13:06:22 +0100 Subject: [PATCH 3/7] fix: pass sandbox configuration through ExecuteOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sandbox configuration was set in createChatOptions() and createAutoModeOptions(), but was never passed to the ClaudeProvider. This caused the sandbox to never actually be enabled. Changes: - Add sandbox field to ExecuteOptions interface - Pass sandbox config from AgentService to provider - Pass sandbox config from AutoModeService to provider - Forward sandbox config in ClaudeProvider to SDK options Now the sandbox configuration from settings is properly used. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- apps/server/src/providers/claude-provider.ts | 2 + apps/server/src/providers/types.ts | 1 + apps/server/src/services/agent-service.ts | 1 + apps/server/src/services/auto-mode-service.ts | 2 + libs/types/src/provider.ts | 1 + package-lock.json | 88 +++++++------------ 6 files changed, 37 insertions(+), 58 deletions(-) diff --git a/apps/server/src/providers/claude-provider.ts b/apps/server/src/providers/claude-provider.ts index 563dd70c..919eeb30 100644 --- a/apps/server/src/providers/claude-provider.ts +++ b/apps/server/src/providers/claude-provider.ts @@ -69,6 +69,8 @@ export class ClaudeProvider extends BaseProvider { : {}), // Forward settingSources for CLAUDE.md file loading ...(options.settingSources && { settingSources: options.settingSources }), + // Forward sandbox configuration + ...(options.sandbox && { sandbox: options.sandbox }), }; console.log('[ClaudeProvider] SDK options prepared:', { diff --git a/apps/server/src/providers/types.ts b/apps/server/src/providers/types.ts index 5a594361..17b45066 100644 --- a/apps/server/src/providers/types.ts +++ b/apps/server/src/providers/types.ts @@ -34,6 +34,7 @@ export interface ExecuteOptions { conversationHistory?: ConversationMessage[]; // Previous messages for context sdkSessionId?: string; // Claude SDK session ID for resuming conversations settingSources?: Array<'user' | 'project' | 'local'>; // Claude filesystem settings to load + sandbox?: { enabled: boolean; autoAllowBashIfSandboxed?: boolean }; // Sandbox configuration } /** diff --git a/apps/server/src/services/agent-service.ts b/apps/server/src/services/agent-service.ts index 966519d4..c072803d 100644 --- a/apps/server/src/services/agent-service.ts +++ b/apps/server/src/services/agent-service.ts @@ -296,6 +296,7 @@ export class AgentService { abortController: session.abortController!, conversationHistory: conversationHistory.length > 0 ? conversationHistory : undefined, settingSources: sdkOptions.settingSources, + sandbox: sdkOptions.sandbox, // Pass sandbox configuration sdkSessionId: session.sdkSessionId, // Pass SDK session ID for resuming }; diff --git a/apps/server/src/services/auto-mode-service.ts b/apps/server/src/services/auto-mode-service.ts index c0ba7bfb..987554f2 100644 --- a/apps/server/src/services/auto-mode-service.ts +++ b/apps/server/src/services/auto-mode-service.ts @@ -1153,6 +1153,7 @@ Format your response as a structured markdown document.`; allowedTools: sdkOptions.allowedTools as string[], abortController, settingSources: sdkOptions.settingSources, + sandbox: sdkOptions.sandbox, // Pass sandbox configuration }; const stream = provider.executeQuery(options); @@ -1887,6 +1888,7 @@ This mock response was generated because AUTOMAKER_MOCK_AGENT=true was set. abortController, systemPrompt: sdkOptions.systemPrompt, settingSources: sdkOptions.settingSources, + sandbox: sdkOptions.sandbox, // Pass sandbox configuration }; // Execute via provider diff --git a/libs/types/src/provider.ts b/libs/types/src/provider.ts index 53c92717..3dca3db5 100644 --- a/libs/types/src/provider.ts +++ b/libs/types/src/provider.ts @@ -43,6 +43,7 @@ export interface ExecuteOptions { conversationHistory?: ConversationMessage[]; // Previous messages for context sdkSessionId?: string; // Claude SDK session ID for resuming conversations settingSources?: Array<'user' | 'project' | 'local'>; // Sources for CLAUDE.md loading + sandbox?: { enabled: boolean; autoAllowBashIfSandboxed?: boolean }; // Sandbox configuration } /** diff --git a/package-lock.json b/package-lock.json index b2e61ce0..e51794c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -423,6 +423,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -1006,6 +1007,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz", "integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==", "license": "MIT", + "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "crelt": "^1.0.6", @@ -1048,6 +1050,7 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -1868,7 +1871,6 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, - "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -1890,7 +1892,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1907,7 +1908,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -1922,7 +1922,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 10.0.0" } @@ -2678,7 +2677,6 @@ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=18" } @@ -2803,7 +2801,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2820,7 +2817,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2837,7 +2833,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -2946,7 +2941,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2969,7 +2963,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -2992,7 +2985,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -3078,7 +3070,6 @@ ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, - "peer": true, "dependencies": { "@emnapi/runtime": "^1.7.0" }, @@ -3101,7 +3092,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -3121,7 +3111,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -3460,8 +3449,7 @@ "version": "16.0.10", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { "version": "16.0.10", @@ -3475,7 +3463,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3492,7 +3479,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3509,7 +3495,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3526,7 +3511,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3543,7 +3527,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3560,7 +3543,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3577,7 +3559,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3594,7 +3575,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -3685,6 +3665,7 @@ "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.57.0" }, @@ -5125,7 +5106,6 @@ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -5459,6 +5439,7 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.6.tgz", "integrity": "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg==", "license": "MIT", + "peer": true, "dependencies": { "@tanstack/history": "1.141.0", "@tanstack/react-store": "^0.8.0", @@ -6010,6 +5991,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -6020,6 +6002,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -6125,6 +6108,7 @@ "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", @@ -6618,7 +6602,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@xyflow/react": { "version": "12.10.0", @@ -6716,6 +6701,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6776,6 +6762,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7335,6 +7322,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7866,8 +7854,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", @@ -8153,8 +8140,7 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/cross-env": { "version": "10.1.0", @@ -8251,6 +8237,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -8552,6 +8539,7 @@ "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "app-builder-lib": "26.0.12", "builder-util": "26.0.11", @@ -8878,7 +8866,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -8899,7 +8886,6 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -9150,6 +9136,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -11055,7 +11042,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11117,7 +11103,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13536,7 +13521,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -13553,7 +13537,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -13571,7 +13554,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -13760,6 +13742,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13769,6 +13752,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -14118,7 +14102,6 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -14307,6 +14290,7 @@ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz", "integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10" } @@ -14355,7 +14339,6 @@ "hasInstallScript": true, "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -14406,7 +14389,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -14429,7 +14411,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -14452,7 +14433,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -14469,7 +14449,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -14486,7 +14465,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -14503,7 +14481,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -14520,7 +14497,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -14537,7 +14513,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -14554,7 +14529,6 @@ "os": [ "linux" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -14571,7 +14545,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -14594,7 +14567,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -14617,7 +14589,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -14640,7 +14611,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -14663,7 +14633,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -14686,7 +14655,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -15155,7 +15123,6 @@ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "license": "MIT", - "peer": true, "dependencies": { "client-only": "0.0.1" }, @@ -15325,7 +15292,6 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -15389,7 +15355,6 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -15487,6 +15452,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -15691,6 +15657,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16062,6 +16029,7 @@ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -16151,7 +16119,8 @@ "resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz", "integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/vite/node_modules/fdir": { "version": "6.5.0", @@ -16177,6 +16146,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -16219,6 +16189,7 @@ "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.0.16", "@vitest/mocker": "4.0.16", @@ -16476,6 +16447,7 @@ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, From 01e6b7fa527c36bb07b5350366a7f0ab66b98836 Mon Sep 17 00:00:00 2001 From: Stephan Rieche Date: Sat, 27 Dec 2025 13:13:17 +0100 Subject: [PATCH 4/7] chore: address code review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address suggestions from gemini-code-assist and coderabbit-ai: Logging Improvements: - Remove excessive debug logging from ClaudeProvider - Remove sensitive environment variable logging (API key length, HOME, USER) - Remove verbose per-message stream logging from AgentService - Remove redundant SDK options logging - Remove watchdog timer logging (diagnostic tool) Documentation: - Update JSDoc example in ClaudeMdSettings to include sandbox props Persistence Fix: - Add enableSandboxMode to syncSettingsToServer updates object - Ensures sandbox setting is properly persisted to server storage This reduces log volume significantly while maintaining important error and state transition logging. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- apps/server/src/providers/claude-provider.ts | 100 +----------------- apps/server/src/services/agent-service.ts | 17 --- .../claude/claude-md-settings.tsx | 7 +- apps/ui/src/hooks/use-settings-migration.ts | 1 + 4 files changed, 9 insertions(+), 116 deletions(-) diff --git a/apps/server/src/providers/claude-provider.ts b/apps/server/src/providers/claude-provider.ts index 919eeb30..716e94ca 100644 --- a/apps/server/src/providers/claude-provider.ts +++ b/apps/server/src/providers/claude-provider.ts @@ -23,8 +23,6 @@ export class ClaudeProvider extends BaseProvider { * Execute a query using Claude Agent SDK */ async *executeQuery(options: ExecuteOptions): AsyncGenerator { - console.log('[ClaudeProvider] executeQuery() called'); - const { prompt, model, @@ -37,20 +35,6 @@ export class ClaudeProvider extends BaseProvider { sdkSessionId, } = options; - console.log('[ClaudeProvider] Options:', { - model, - cwd, - maxTurns, - promptType: typeof prompt, - promptLength: typeof prompt === 'string' ? prompt.length : 'array', - hasSystemPrompt: !!systemPrompt, - systemPromptLength: systemPrompt?.length, - hasConversationHistory: !!conversationHistory?.length, - conversationHistoryLength: conversationHistory?.length || 0, - sdkSessionId, - allowedToolsCount: allowedTools?.length, - }); - // Build Claude SDK options const defaultTools = ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch']; const toolsToUse = allowedTools || defaultTools; @@ -73,15 +57,6 @@ export class ClaudeProvider extends BaseProvider { ...(options.sandbox && { sandbox: options.sandbox }), }; - console.log('[ClaudeProvider] SDK options prepared:', { - model: sdkOptions.model, - maxTurns: sdkOptions.maxTurns, - permissionMode: sdkOptions.permissionMode, - sandboxEnabled: sdkOptions.sandbox?.enabled || false, - hasResume: !!(sdkOptions as any).resume, - toolsCount: sdkOptions.allowedTools?.length, - }); - // Build prompt payload let promptPayload: string | AsyncIterable; @@ -106,81 +81,12 @@ export class ClaudeProvider extends BaseProvider { // Execute via Claude Agent SDK try { - console.log('[ClaudeProvider] ANTHROPIC_API_KEY exists:', !!process.env.ANTHROPIC_API_KEY); - console.log( - '[ClaudeProvider] ANTHROPIC_API_KEY length:', - process.env.ANTHROPIC_API_KEY?.length || 0 - ); - console.log('[ClaudeProvider] HOME directory:', process.env.HOME); - console.log('[ClaudeProvider] User:', process.env.USER); - console.log('[ClaudeProvider] Current working directory:', process.cwd()); - - // CRITICAL DEBUG: Log exact SDK options being passed - console.log('[ClaudeProvider] EXACT sdkOptions being passed to query():'); - console.log( - JSON.stringify( - { - model: sdkOptions.model, - maxTurns: sdkOptions.maxTurns, - cwd: sdkOptions.cwd, - allowedTools: sdkOptions.allowedTools, - permissionMode: sdkOptions.permissionMode, - hasSandbox: !!sdkOptions.sandbox, - hasAbortController: !!sdkOptions.abortController, - hasResume: !!(sdkOptions as any).resume, - hasSettingSources: !!sdkOptions.settingSources, - settingSources: sdkOptions.settingSources, - }, - null, - 2 - ) - ); - - console.log('[ClaudeProvider] Calling Claude Agent SDK query()...'); - console.log( - '[ClaudeProvider] About to call query() with prompt payload type:', - typeof promptPayload - ); - const stream = query({ prompt: promptPayload, options: sdkOptions }); - console.log('[ClaudeProvider] query() call returned, stream object type:', typeof stream); - console.log('[ClaudeProvider] SDK query() returned stream, starting iteration...'); - let streamMessageCount = 0; - - // Add a watchdog timer to detect if stream is hanging - let lastMessageTime = Date.now(); - const watchdogInterval = setInterval(() => { - const timeSinceLastMessage = Date.now() - lastMessageTime; - if (timeSinceLastMessage > 10000) { - console.log( - `[ClaudeProvider] WARNING: No messages received for ${Math.floor(timeSinceLastMessage / 1000)}s` - ); - } - }, 5000); - - try { - // Stream messages directly - they're already in the correct format - for await (const msg of stream) { - lastMessageTime = Date.now(); - streamMessageCount++; - console.log(`[ClaudeProvider] Stream message #${streamMessageCount}:`, { - type: msg.type, - subtype: (msg as any).subtype, - hasMessage: !!(msg as any).message, - hasResult: !!(msg as any).result, - session_id: msg.session_id, - }); - yield msg as ProviderMessage; - } - } finally { - clearInterval(watchdogInterval); + // Stream messages directly - they're already in the correct format + for await (const msg of stream) { + yield msg as ProviderMessage; } - - console.log( - '[ClaudeProvider] Stream iteration completed, total messages:', - streamMessageCount - ); } catch (error) { console.error('[ClaudeProvider] ERROR: executeQuery() error during execution:', error); console.error('[ClaudeProvider] ERROR stack:', (error as Error).stack); diff --git a/apps/server/src/services/agent-service.ts b/apps/server/src/services/agent-service.ts index c072803d..b76e9c76 100644 --- a/apps/server/src/services/agent-service.ts +++ b/apps/server/src/services/agent-service.ts @@ -300,7 +300,6 @@ export class AgentService { sdkSessionId: session.sdkSessionId, // Pass SDK session ID for resuming }; - console.log('[AgentService] Building prompt with images...'); // Build prompt content with images const { content: promptContent } = await buildPromptWithImages( message, @@ -321,23 +320,12 @@ export class AgentService { // Execute via provider const stream = provider.executeQuery(options); - console.log('[AgentService] Stream created, starting to iterate...'); let currentAssistantMessage: Message | null = null; let responseText = ''; const toolUses: Array<{ name: string; input: unknown }> = []; - let messageCount = 0; - console.log('[AgentService] Entering stream loop...'); for await (const msg of stream) { - messageCount++; - console.log(`[AgentService] Stream message #${messageCount}:`, { - type: msg.type, - subtype: (msg as any).subtype, - hasContent: !!(msg as any).message?.content, - session_id: msg.session_id, - }); - // Capture SDK session ID from any message and persist it if (msg.session_id && !session.sdkSessionId) { session.sdkSessionId = msg.session_id; @@ -347,7 +335,6 @@ export class AgentService { } if (msg.type === 'assistant') { - console.log('[AgentService] Processing assistant message...'); if (msg.message?.content) { for (const block of msg.message.content) { if (block.type === 'text') { @@ -391,7 +378,6 @@ export class AgentService { } } } else if (msg.type === 'result') { - console.log('[AgentService] Result message received, subtype:', (msg as any).subtype); if (msg.subtype === 'success' && msg.result) { if (currentAssistantMessage) { currentAssistantMessage.content = msg.result; @@ -409,8 +395,6 @@ export class AgentService { } } - console.log('[AgentService] Stream loop completed, total messages:', messageCount); - await this.saveSession(sessionId, session.messages); session.isRunning = false; @@ -825,7 +809,6 @@ export class AgentService { dataKeys: Object.keys(data), }); this.events.emit('agent:stream', { sessionId, ...data }); - console.log('[AgentService] Event emitted to EventEmitter'); } private getSystemPrompt(): string { diff --git a/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx b/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx index c2a6a3db..ae5a67e4 100644 --- a/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx +++ b/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx @@ -13,14 +13,17 @@ interface ClaudeMdSettingsProps { /** * ClaudeMdSettings Component * - * UI control for the autoLoadClaudeMd setting which enables automatic loading - * of project instructions from .claude/CLAUDE.md files via the Claude Agent SDK. + * UI controls for Claude Agent SDK settings including: + * - Auto-loading of project instructions from .claude/CLAUDE.md files + * - Sandbox mode for isolated bash command execution * * Usage: * ```tsx * * ``` */ diff --git a/apps/ui/src/hooks/use-settings-migration.ts b/apps/ui/src/hooks/use-settings-migration.ts index 2bca750b..1e989060 100644 --- a/apps/ui/src/hooks/use-settings-migration.ts +++ b/apps/ui/src/hooks/use-settings-migration.ts @@ -224,6 +224,7 @@ export async function syncSettingsToServer(): Promise { enhancementModel: state.enhancementModel, validationModel: state.validationModel, autoLoadClaudeMd: state.autoLoadClaudeMd, + enableSandboxMode: state.enableSandboxMode, keyboardShortcuts: state.keyboardShortcuts, aiProfiles: state.aiProfiles, projects: state.projects, From 23d6756f0370284377cda080036ed4dd456d9a9d Mon Sep 17 00:00:00 2001 From: Stephan Rieche Date: Sat, 27 Dec 2025 13:20:39 +0100 Subject: [PATCH 5/7] test: fix sandbox mode test assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive test coverage for sandbox mode configuration: - Added tests for enableSandboxMode=false for both createChatOptions and createAutoModeOptions - Added tests for enableSandboxMode not provided for both functions - Updated existing tests to pass enableSandboxMode=true where sandbox assertions exist This addresses the broken test assertions identified by coderabbit-ai review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../server/tests/unit/lib/sdk-options.test.ts | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/apps/server/tests/unit/lib/sdk-options.test.ts b/apps/server/tests/unit/lib/sdk-options.test.ts index c7324d6c..e5d4c7c0 100644 --- a/apps/server/tests/unit/lib/sdk-options.test.ts +++ b/apps/server/tests/unit/lib/sdk-options.test.ts @@ -179,7 +179,7 @@ describe('sdk-options.ts', () => { it('should create options with chat settings', async () => { const { createChatOptions, TOOL_PRESETS, MAX_TURNS } = await import('@/lib/sdk-options.js'); - const options = createChatOptions({ cwd: '/test/path' }); + const options = createChatOptions({ cwd: '/test/path', enableSandboxMode: true }); expect(options.cwd).toBe('/test/path'); expect(options.maxTurns).toBe(MAX_TURNS.standard); @@ -212,6 +212,27 @@ describe('sdk-options.ts', () => { expect(options.model).toBe('claude-sonnet-4-20250514'); }); + + it('should not set sandbox when enableSandboxMode is false', async () => { + const { createChatOptions } = await import('@/lib/sdk-options.js'); + + const options = createChatOptions({ + cwd: '/test/path', + enableSandboxMode: false, + }); + + expect(options.sandbox).toBeUndefined(); + }); + + it('should not set sandbox when enableSandboxMode is not provided', async () => { + const { createChatOptions } = await import('@/lib/sdk-options.js'); + + const options = createChatOptions({ + cwd: '/test/path', + }); + + expect(options.sandbox).toBeUndefined(); + }); }); describe('createAutoModeOptions', () => { @@ -219,7 +240,7 @@ describe('sdk-options.ts', () => { const { createAutoModeOptions, TOOL_PRESETS, MAX_TURNS } = await import('@/lib/sdk-options.js'); - const options = createAutoModeOptions({ cwd: '/test/path' }); + const options = createAutoModeOptions({ cwd: '/test/path', enableSandboxMode: true }); expect(options.cwd).toBe('/test/path'); expect(options.maxTurns).toBe(MAX_TURNS.maximum); @@ -252,6 +273,27 @@ describe('sdk-options.ts', () => { expect(options.abortController).toBe(abortController); }); + + it('should not set sandbox when enableSandboxMode is false', async () => { + const { createAutoModeOptions } = await import('@/lib/sdk-options.js'); + + const options = createAutoModeOptions({ + cwd: '/test/path', + enableSandboxMode: false, + }); + + expect(options.sandbox).toBeUndefined(); + }); + + it('should not set sandbox when enableSandboxMode is not provided', async () => { + const { createAutoModeOptions } = await import('@/lib/sdk-options.js'); + + const options = createAutoModeOptions({ + cwd: '/test/path', + }); + + expect(options.sandbox).toBeUndefined(); + }); }); describe('createCustomOptions', () => { From 296ef20ef7f1cde627cdfbca3a900366a40f38dd Mon Sep 17 00:00:00 2001 From: Stephan Rieche Date: Sat, 27 Dec 2025 13:37:19 +0100 Subject: [PATCH 6/7] test: update claude-provider tests for sandbox changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated tests to reflect changes made to sandbox mode implementation: 1. Changed permissionMode expectation from 'acceptEdits' to 'default' - ClaudeProvider now uses 'default' permission mode 2. Renamed test "should enable sandbox by default" to "should pass sandbox configuration when provided" - Sandbox is no longer enabled by default in the provider - Provider now forwards sandbox config only when explicitly provided via ExecuteOptions 3. Updated error handling test expectations - Now expects two console.error calls with new format - First call: '[ClaudeProvider] ERROR: executeQuery() error during execution:' - Second call: '[ClaudeProvider] ERROR stack:' with stack trace All 32 tests in claude-provider.test.ts now pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../unit/providers/claude-provider.test.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/apps/server/tests/unit/providers/claude-provider.test.ts b/apps/server/tests/unit/providers/claude-provider.test.ts index 45deb0d1..b06642e9 100644 --- a/apps/server/tests/unit/providers/claude-provider.test.ts +++ b/apps/server/tests/unit/providers/claude-provider.test.ts @@ -73,7 +73,7 @@ describe('claude-provider.ts', () => { maxTurns: 10, cwd: '/test/dir', allowedTools: ['Read', 'Write'], - permissionMode: 'acceptEdits', + permissionMode: 'default', }), }); }); @@ -100,7 +100,7 @@ describe('claude-provider.ts', () => { }); }); - it('should enable sandbox by default', async () => { + it('should pass sandbox configuration when provided', async () => { vi.mocked(sdk.query).mockReturnValue( (async function* () { yield { type: 'text', text: 'test' }; @@ -110,6 +110,10 @@ describe('claude-provider.ts', () => { const generator = provider.executeQuery({ prompt: 'Test', cwd: '/test', + sandbox: { + enabled: true, + autoAllowBashIfSandboxed: true, + }, }); await collectAsyncGenerator(generator); @@ -242,11 +246,21 @@ describe('claude-provider.ts', () => { }); await expect(collectAsyncGenerator(generator)).rejects.toThrow('SDK execution failed'); - expect(consoleErrorSpy).toHaveBeenCalledWith( - '[ClaudeProvider] executeQuery() error during execution:', + + // Should log error message + expect(consoleErrorSpy).toHaveBeenNthCalledWith( + 1, + '[ClaudeProvider] ERROR: executeQuery() error during execution:', testError ); + // Should log stack trace + expect(consoleErrorSpy).toHaveBeenNthCalledWith( + 2, + '[ClaudeProvider] ERROR stack:', + testError.stack + ); + consoleErrorSpy.mockRestore(); }); }); From 71c17e1fbb093a6ebce32050fc8b2054537ab082 Mon Sep 17 00:00:00 2001 From: Stephan Rieche Date: Sat, 27 Dec 2025 13:45:34 +0100 Subject: [PATCH 7/7] chore: remove debug logging from agent-service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed all debug console.log statements from agent-service.ts to avoid polluting production logs. This addresses code review feedback from gemini-code-assist. Removed debug logs for: - sendMessage() entry and session state - Event emissions (started, message, stream, complete) - Provider execution - SDK session ID capture - Tool use detection - Queue processing - emitAgentEvent() calls Kept console.error logs for actual errors (session not found, execution errors, etc.) as they are useful for troubleshooting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- apps/server/src/services/agent-service.ts | 44 ----------------------- 1 file changed, 44 deletions(-) diff --git a/apps/server/src/services/agent-service.ts b/apps/server/src/services/agent-service.ts index b76e9c76..dc6d4594 100644 --- a/apps/server/src/services/agent-service.ts +++ b/apps/server/src/services/agent-service.ts @@ -144,27 +144,12 @@ export class AgentService { imagePaths?: string[]; model?: string; }) { - console.log('[AgentService] sendMessage() called:', { - sessionId, - messageLength: message?.length, - workingDirectory, - imageCount: imagePaths?.length || 0, - model, - }); - const session = this.sessions.get(sessionId); if (!session) { console.error('[AgentService] ERROR: Session not found:', sessionId); throw new Error(`Session ${sessionId} not found`); } - console.log('[AgentService] Session found:', { - sessionId, - messageCount: session.messages.length, - isRunning: session.isRunning, - workingDirectory: session.workingDirectory, - }); - if (session.isRunning) { console.error('[AgentService] ERROR: Agent already running for session:', sessionId); throw new Error('Agent is already processing a message'); @@ -213,19 +198,16 @@ export class AgentService { session.abortController = new AbortController(); // Emit started event so UI can show thinking indicator - console.log('[AgentService] Emitting "started" event for session:', sessionId); this.emitAgentEvent(sessionId, { type: 'started', }); // Emit user message event - console.log('[AgentService] Emitting "message" event for session:', sessionId); this.emitAgentEvent(sessionId, { type: 'message', message: userMessage, }); - console.log('[AgentService] Saving session messages'); await this.saveSession(sessionId, session.messages); try { @@ -278,13 +260,8 @@ export class AgentService { const allowedTools = sdkOptions.allowedTools as string[] | undefined; // Get provider for this model - console.log('[AgentService] Getting provider for model:', effectiveModel); const provider = ProviderFactory.getProviderForModel(effectiveModel); - console.log( - `[AgentService] Using provider "${provider.getName()}" for model "${effectiveModel}"` - ); - // Build options for provider const options: ExecuteOptions = { prompt: '', // Will be set below based on images @@ -311,13 +288,6 @@ export class AgentService { // Set the prompt in options options.prompt = promptContent; - console.log('[AgentService] Executing query via provider:', { - model: effectiveModel, - promptLength: typeof promptContent === 'string' ? promptContent.length : 'array', - hasConversationHistory: !!conversationHistory.length, - sdkSessionId: session.sdkSessionId, - }); - // Execute via provider const stream = provider.executeQuery(options); @@ -329,7 +299,6 @@ export class AgentService { // Capture SDK session ID from any message and persist it if (msg.session_id && !session.sdkSessionId) { session.sdkSessionId = msg.session_id; - console.log(`[AgentService] Captured SDK session ID: ${msg.session_id}`); // Persist the SDK session ID to ensure conversation continuity across server restarts await this.updateSession(sessionId, { sdkSessionId: msg.session_id }); } @@ -352,10 +321,6 @@ export class AgentService { currentAssistantMessage.content = responseText; } - console.log( - '[AgentService] Emitting "stream" event, text length:', - responseText.length - ); this.emitAgentEvent(sessionId, { type: 'stream', messageId: currentAssistantMessage.id, @@ -369,7 +334,6 @@ export class AgentService { }; toolUses.push(toolUse); - console.log('[AgentService] Tool use detected:', toolUse.name); this.emitAgentEvent(sessionId, { type: 'tool_use', tool: toolUse, @@ -385,7 +349,6 @@ export class AgentService { } } - console.log('[AgentService] Emitting "complete" event'); this.emitAgentEvent(sessionId, { type: 'complete', messageId: currentAssistantMessage?.id, @@ -783,8 +746,6 @@ export class AgentService { queue: session.promptQueue, }); - console.log(`[AgentService] Processing next queued prompt for session ${sessionId}`); - try { await this.sendMessage({ sessionId, @@ -803,11 +764,6 @@ export class AgentService { } private emitAgentEvent(sessionId: string, data: Record): void { - console.log('[AgentService] emitAgentEvent() called:', { - sessionId, - eventType: data.type, - dataKeys: Object.keys(data), - }); this.events.emit('agent:stream', { sessionId, ...data }); }