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
10 changes: 9 additions & 1 deletion docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: `<modelString>` • Thinking: `<thinkingLevel>`_
<!-- mux-attribution: model=<modelString> thinking=<thinkingLevel> -->
```

Prefer sourcing values from `$MUX_MODEL_STRING` and `$MUX_THINKING_LEVEL` (bash tool env).

## PR + Release Workflow

Expand Down
35 changes: 34 additions & 1 deletion src/node/runtime/initHook.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down Expand Up @@ -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[] = [];
Expand Down Expand Up @@ -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");
});
});
26 changes: 24 additions & 2 deletions src/node/runtime/initHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
Expand Down Expand Up @@ -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<string, string> {
return {
if (!projectPath) {
throw new Error("getMuxEnv: projectPath is required");
}
if (!workspaceName) {
throw new Error("getMuxEnv: workspaceName is required");
}

const env: Record<string, string> = {
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;
}

/**
Expand Down
6 changes: 5 additions & 1 deletion src/node/services/aiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading