diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 21f21e3a7b..b1a2667ed6 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -9,7 +9,15 @@ description: Agent instructions for AI assistants working on the mux codebase - `mux`: Electron + React desktop app for parallel agent workflows; UX must be fast, responsive, predictable. - Minor breaking changes are expected, but critical flows must allow upgrade↔downgrade without friction; skip migrations when breakage is tightly scoped. -- Public work (issues/PRs/commits) must use 🤖 in the title and include "_Generated with `mux`_" in the body when applicable. +- Public work (issues/PRs/commits) must use 🤖 in the title and include this footer in the body: + + ```md + --- + _Generated with `mux` • Model: `` • Thinking: ``_ + + ``` + + Prefer sourcing values from `$MUX_MODEL_STRING` and `$MUX_THINKING_LEVEL` (bash tool env). ## PR + Release Workflow diff --git a/src/node/runtime/initHook.test.ts b/src/node/runtime/initHook.test.ts index d591678d45..bdd68ff592 100644 --- a/src/node/runtime/initHook.test.ts +++ b/src/node/runtime/initHook.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "bun:test"; -import { LineBuffer, createLineBufferedLoggers } from "./initHook"; +import { LineBuffer, createLineBufferedLoggers, getMuxEnv } from "./initHook"; import type { InitLogger } from "./Runtime"; describe("LineBuffer", () => { @@ -53,6 +53,7 @@ describe("LineBuffer", () => { }); }); +// getMuxEnv tests are placed here because initHook.ts owns the implementation. describe("createLineBufferedLoggers", () => { it("should create separate buffers for stdout and stderr", () => { const stdoutLines: string[] = []; @@ -109,3 +110,35 @@ describe("createLineBufferedLoggers", () => { expect(stderrLines).toEqual(["also incomplete"]); }); }); + +describe("getMuxEnv", () => { + it("should include base MUX_ environment variables", () => { + const env = getMuxEnv("/path/to/project", "worktree", "feature-branch"); + + expect(env.MUX_PROJECT_PATH).toBe("/path/to/project"); + expect(env.MUX_RUNTIME).toBe("worktree"); + expect(env.MUX_WORKSPACE_NAME).toBe("feature-branch"); + expect(env.MUX_MODEL_STRING).toBeUndefined(); + expect(env.MUX_THINKING_LEVEL).toBeUndefined(); + }); + + it("should include model + thinking env vars when provided", () => { + const env = getMuxEnv("/path/to/project", "worktree", "feature-branch", { + modelString: "openai:gpt-5.2-pro", + thinkingLevel: "medium", + }); + + expect(env.MUX_MODEL_STRING).toBe("openai:gpt-5.2-pro"); + expect(env.MUX_THINKING_LEVEL).toBe("medium"); + }); + + it("should allow explicit thinkingLevel=off", () => { + const env = getMuxEnv("/path/to/project", "local", "main", { + modelString: "anthropic:claude-3-5-sonnet", + thinkingLevel: "off", + }); + + expect(env.MUX_MODEL_STRING).toBe("anthropic:claude-3-5-sonnet"); + expect(env.MUX_THINKING_LEVEL).toBe("off"); + }); +}); diff --git a/src/node/runtime/initHook.ts b/src/node/runtime/initHook.ts index f6bd9b6905..0ca80dace0 100644 --- a/src/node/runtime/initHook.ts +++ b/src/node/runtime/initHook.ts @@ -3,6 +3,7 @@ import * as fsPromises from "fs/promises"; import * as path from "path"; import type { InitLogger } from "./Runtime"; import type { RuntimeConfig } from "@/common/types/runtime"; +import type { ThinkingLevel } from "@/common/types/thinking"; import { isWorktreeRuntime, isSSHRuntime } from "@/common/types/runtime"; /** @@ -38,13 +39,34 @@ export function getInitHookPath(projectPath: string): string { export function getMuxEnv( projectPath: string, runtime: "local" | "worktree" | "ssh", - workspaceName: string + workspaceName: string, + options?: { + modelString?: string; + thinkingLevel?: ThinkingLevel; + } ): Record { - return { + if (!projectPath) { + throw new Error("getMuxEnv: projectPath is required"); + } + if (!workspaceName) { + throw new Error("getMuxEnv: workspaceName is required"); + } + + const env: Record = { MUX_PROJECT_PATH: projectPath, MUX_RUNTIME: runtime, MUX_WORKSPACE_NAME: workspaceName, }; + + if (options?.modelString) { + env.MUX_MODEL_STRING = options.modelString; + } + + if (options?.thinkingLevel !== undefined) { + env.MUX_THINKING_LEVEL = options.thinkingLevel; + } + + return env; } /** diff --git a/src/node/services/aiService.ts b/src/node/services/aiService.ts index 373346c32c..cd7b94c642 100644 --- a/src/node/services/aiService.ts +++ b/src/node/services/aiService.ts @@ -1150,7 +1150,11 @@ export class AIService extends EventEmitter { muxEnv: getMuxEnv( metadata.projectPath, getRuntimeType(metadata.runtimeConfig), - metadata.name + metadata.name, + { + modelString, + thinkingLevel: thinkingLevel ?? "off", + } ), runtimeTempDir, backgroundProcessManager: this.backgroundProcessManager,