Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions apps/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
});

Expand All @@ -205,7 +224,7 @@ wss.on('connection', (ws: WebSocket) => {
});

ws.on('error', (error) => {
console.error('[WebSocket] Error:', error);
console.error('[WebSocket] ERROR:', error);
unsubscribe();
});
});
Expand Down
27 changes: 17 additions & 10 deletions apps/server/src/lib/sdk-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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 {
Expand All @@ -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 }),
};
Expand All @@ -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 {
Expand All @@ -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 }),
};
Expand Down
28 changes: 28 additions & 0 deletions apps/server/src/lib/settings-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean> {
if (!settingsService) {
console.log(`${logPrefix} SettingsService not available, sandbox mode disabled`);
return false;
}

try {
const globalSettings = await settingsService.getGlobalSettings();
const result = globalSettings.enableSandboxMode ?? true;
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.
Expand Down
11 changes: 5 additions & 6 deletions apps/server/src/providers/claude-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,16 @@ 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
? { resume: sdkSessionId }
: {}),
// Forward settingSources for CLAUDE.md file loading
...(options.settingSources && { settingSources: options.settingSources }),
// Forward sandbox configuration
...(options.sandbox && { sandbox: options.sandbox }),
};

// Build prompt payload
Expand Down Expand Up @@ -90,7 +88,8 @@ export class ClaudeProvider extends BaseProvider {
yield msg as ProviderMessage;
}
} 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;
}
}
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/providers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/**
Expand Down
15 changes: 15 additions & 0 deletions apps/server/src/routes/agent/routes/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,25 @@ 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',
});
return;
}

console.log('[Send Handler] Validation passed, calling agentService.sendMessage()');

// Start the message processing (don't await - it streams via WebSocket)
agentService
.sendMessage({
Expand All @@ -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) });
}
Expand Down
23 changes: 15 additions & 8 deletions apps/server/src/services/agent-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -142,10 +146,12 @@ export class AgentService {
}) {
const session = this.sessions.get(sessionId);
if (!session) {
console.error('[AgentService] ERROR: Session not found:', sessionId);
throw new Error(`Session ${sessionId} not found`);
}

if (session.isRunning) {
console.error('[AgentService] ERROR: Agent already running for session:', sessionId);
throw new Error('Agent is already processing a message');
}

Expand Down Expand Up @@ -215,6 +221,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,
Expand All @@ -239,6 +251,7 @@ export class AgentService {
systemPrompt: combinedSystemPrompt,
abortController: session.abortController!,
autoLoadClaudeMd,
enableSandboxMode,
});

// Extract model, maxTurns, and allowedTools from SDK options
Expand All @@ -249,10 +262,6 @@ export class AgentService {
// Get provider for this model
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
Expand All @@ -264,6 +273,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
};

Expand All @@ -289,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 });
}
Expand Down Expand Up @@ -737,8 +746,6 @@ export class AgentService {
queue: session.promptQueue,
});

console.log(`[AgentService] Processing next queued prompt for session ${sessionId}`);

try {
await this.sendMessage({
sessionId,
Expand Down
12 changes: 11 additions & 1 deletion apps/server/src/services/auto-mode-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -1149,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);
Expand Down Expand Up @@ -1833,12 +1838,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
Expand Down Expand Up @@ -1879,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
Expand Down
Loading