diff --git a/src/api/apiMachine.ts b/src/api/apiMachine.ts index b8e2b570..fa4e800f 100644 --- a/src/api/apiMachine.ts +++ b/src/api/apiMachine.ts @@ -102,14 +102,14 @@ export class ApiMachineClient { }: MachineRpcHandlers) { // Register spawn session handler this.rpcHandlerManager.registerHandler('spawn-happy-session', async (params: any) => { - const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, environmentVariables } = params || {}; + const { directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, model, environmentVariables } = params || {}; logger.debug(`[API MACHINE] Spawning session with params: ${JSON.stringify(params)}`); if (!directory) { throw new Error('Directory is required'); } - const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, environmentVariables }); + const result = await spawnSession({ directory, sessionId, machineId, approvedNewDirectoryCreation, agent, token, model, environmentVariables }); switch (result.type) { case 'success': diff --git a/src/daemon/run.ts b/src/daemon/run.ts index 75889d14..9cf4ccc8 100644 --- a/src/daemon/run.ts +++ b/src/daemon/run.ts @@ -219,7 +219,7 @@ export async function startDaemon(): Promise { const spawnSession = async (options: SpawnSessionOptions): Promise => { logger.debugLargeJson('[DAEMON RUN] Spawning session', options); - const { directory, sessionId, machineId, approvedNewDirectoryCreation = true } = options; + const { directory, sessionId, machineId, approvedNewDirectoryCreation = true, model } = options; let directoryCreated = false; try { @@ -325,6 +325,13 @@ export async function startDaemon(): Promise { // Final merge: Profile vars first, then auth (auth takes precedence to protect authentication) let extraEnv = { ...profileEnv, ...authEnv }; + + // If model is provided via spawn options, set ANTHROPIC_MODEL (takes precedence over profile) + if (model) { + extraEnv.ANTHROPIC_MODEL = model; + logger.debug(`[DAEMON RUN] Model set from spawn options: ${model}`); + } + logger.debug(`[DAEMON RUN] Final environment variable keys (before expansion) (${Object.keys(extraEnv).length}): ${Object.keys(extraEnv).join(', ')}`); // Expand ${VAR} references from daemon's process.env @@ -388,7 +395,9 @@ export async function startDaemon(): Promise { const cliPath = join(projectPath(), 'dist', 'index.mjs'); // Determine agent command - support claude, codex, and gemini const agent = options.agent === 'gemini' ? 'gemini' : (options.agent === 'codex' ? 'codex' : 'claude'); - const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${agent} --happy-starting-mode remote --started-by daemon`; + // Add --model flag if specified (for Claude agent) + const modelArg = model && (options.agent === 'claude' || options.agent === undefined) ? ` --model ${model}` : ''; + const fullCommand = `node --no-warnings --no-deprecation ${cliPath} ${agent} --happy-starting-mode remote --started-by daemon${modelArg}`; // Spawn in tmux with environment variables // IMPORTANT: Pass complete environment (process.env + extraEnv) because: @@ -495,6 +504,12 @@ export async function startDaemon(): Promise { '--started-by', 'daemon' ]; + // Add --model flag if model was specified (for Claude agent) + if (model && (options.agent === 'claude' || options.agent === undefined)) { + args.push('--model', model); + logger.debug(`[DAEMON RUN] Added --model ${model} to CLI args`); + } + // TODO: In future, sessionId could be used with --resume to continue existing sessions // For now, we ignore it - each spawn creates a new session const happyProcess = spawnHappyCLI(args, { diff --git a/src/modules/common/registerCommonHandlers.ts b/src/modules/common/registerCommonHandlers.ts index bd4e07a5..03e5dddb 100644 --- a/src/modules/common/registerCommonHandlers.ts +++ b/src/modules/common/registerCommonHandlers.ts @@ -122,6 +122,8 @@ export interface SpawnSessionOptions { approvedNewDirectoryCreation?: boolean; agent?: 'claude' | 'codex' | 'gemini'; token?: string; + /** Model alias (e.g., 'sonnet', 'opus') or full name. Takes precedence over environmentVariables.ANTHROPIC_MODEL */ + model?: string; environmentVariables?: { // Anthropic Claude API configuration ANTHROPIC_BASE_URL?: string; // Custom API endpoint (overrides default)