fix: branch reset on workdir change and Codex MCP server config merge
- Reset git branch to current when user changes working directory in create-agent modal - Fix Codex MCP servers not being passed from extra.codex.mcp_servers config - Remove macbook host from default daemons - Update test expectations and structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
66881511b6
commit
0d9a69d00f
@@ -53,12 +53,14 @@ describe("create_agent MCP tool", () => {
|
||||
|
||||
const missingTitle = await tool.inputSchema.safeParseAsync({
|
||||
cwd: "/tmp/repo",
|
||||
initialMode: "default",
|
||||
});
|
||||
expect(missingTitle.success).toBe(false);
|
||||
expect(missingTitle.error.issues[0].path).toEqual(["title"]);
|
||||
|
||||
const tooLong = await tool.inputSchema.safeParseAsync({
|
||||
cwd: "/tmp/repo",
|
||||
initialMode: "default",
|
||||
title: "x".repeat(41),
|
||||
});
|
||||
expect(tooLong.success).toBe(false);
|
||||
@@ -66,6 +68,7 @@ describe("create_agent MCP tool", () => {
|
||||
|
||||
const ok = await tool.inputSchema.safeParseAsync({
|
||||
cwd: "/tmp/repo",
|
||||
initialMode: "default",
|
||||
title: "Short title",
|
||||
});
|
||||
expect(ok.success).toBe(true);
|
||||
|
||||
@@ -509,7 +509,7 @@ describe("ClaudeAgentClient (SDK integration)", () => {
|
||||
timeline.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "granted"
|
||||
)
|
||||
).toBe(true);
|
||||
@@ -583,7 +583,7 @@ describe("ClaudeAgentClient (SDK integration)", () => {
|
||||
timeline.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "denied"
|
||||
)
|
||||
).toBe(true);
|
||||
@@ -662,7 +662,7 @@ describe("ClaudeAgentClient (SDK integration)", () => {
|
||||
timeline.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "denied"
|
||||
)
|
||||
).toBe(true);
|
||||
|
||||
@@ -530,7 +530,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
if (event.item.type === "reasoning") {
|
||||
sawReasoning = true;
|
||||
}
|
||||
if (event.item.type === "tool_call" && event.item.server !== "permission") {
|
||||
if (event.item.type === "tool_call" && event.item.name !== "permission") {
|
||||
toolCalls.push(event.item);
|
||||
}
|
||||
}
|
||||
@@ -569,7 +569,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
.reverse()
|
||||
.find(
|
||||
(item) =>
|
||||
item.server === "command" && item.status !== "running"
|
||||
item.name === "shell" && item.status !== "running"
|
||||
);
|
||||
expect(commandToolCall).toBeTruthy();
|
||||
|
||||
@@ -665,7 +665,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
|
||||
if (
|
||||
timelineItems.some(
|
||||
(item) => item.type === "tool_call" && item.server === "file_change"
|
||||
(item) => item.type === "tool_call" && item.name === "apply_patch"
|
||||
)
|
||||
) {
|
||||
rawItemTypes.add("file_change");
|
||||
@@ -674,8 +674,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "test" &&
|
||||
item.tool === "echo"
|
||||
item.name === "test.echo"
|
||||
)
|
||||
) {
|
||||
rawItemTypes.add("mcp_tool_call");
|
||||
@@ -684,8 +683,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "web_search" &&
|
||||
item.tool === "web_search"
|
||||
item.name === "web_search"
|
||||
)
|
||||
) {
|
||||
rawItemTypes.add("web_search");
|
||||
@@ -707,23 +705,21 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
|
||||
expect(
|
||||
timelineItems.some(
|
||||
(item) => item.type === "tool_call" && item.server === "file_change"
|
||||
(item) => item.type === "tool_call" && item.name === "apply_patch"
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "test" &&
|
||||
item.tool === "echo"
|
||||
item.name === "test.echo"
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "web_search" &&
|
||||
item.tool === "web_search"
|
||||
item.name === "web_search"
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
@@ -807,7 +803,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
permissionResolved = true;
|
||||
}
|
||||
if (event.type === "timeline" && providerFromEvent(event) === "codex") {
|
||||
if (event.item.type === "tool_call" && event.item.server !== "permission") {
|
||||
if (event.item.type === "tool_call" && event.item.name !== "permission") {
|
||||
toolCalls.push(event.item);
|
||||
}
|
||||
}
|
||||
@@ -820,7 +816,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
expect.soft(permissionResolved).toBe(true);
|
||||
|
||||
const commandCalls = toolCalls.filter(
|
||||
(item) => item.server === "command" && item.status === "completed"
|
||||
(item) => item.name === "shell" && item.status === "completed"
|
||||
);
|
||||
expect.soft(commandCalls.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
@@ -842,7 +838,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
expect.soft(extractExitCode(stderrCall?.output)).toBe(0);
|
||||
|
||||
const fileChangeCalls = toolCalls.filter(
|
||||
(item) => item.server === "file_change" && item.tool === "apply_patch"
|
||||
(item) => item.name === "apply_patch"
|
||||
);
|
||||
expect.soft(fileChangeCalls.length).toBeGreaterThanOrEqual(2);
|
||||
expect.soft(
|
||||
@@ -859,21 +855,21 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
).toBe(true);
|
||||
|
||||
const readCall = toolCalls.find(
|
||||
(item) => item.tool === "read_file" && item.status === "completed"
|
||||
(item) => item.name === "read_file" && item.status === "completed"
|
||||
);
|
||||
expect.soft(readCall).toBeTruthy();
|
||||
expect.soft(stringifyUnknown(readCall?.input)).toContain("tool-create.txt");
|
||||
expect.soft(stringifyUnknown(readCall?.output)).toContain("beta");
|
||||
|
||||
const mcpCall = toolCalls.find(
|
||||
(item) => item.server === "test" && item.tool === "echo"
|
||||
(item) => item.name === "test.echo"
|
||||
);
|
||||
expect.soft(mcpCall).toBeTruthy();
|
||||
expect.soft(stringifyUnknown(mcpCall?.input)).toContain("mcp-ok");
|
||||
expect.soft(stringifyUnknown(mcpCall?.output)).toContain("mcp-ok");
|
||||
|
||||
const webSearchCall = toolCalls.find(
|
||||
(item) => item.server === "web_search" && item.tool === "web_search"
|
||||
(item) => item.name === "web_search"
|
||||
);
|
||||
expect.soft(webSearchCall).toBeTruthy();
|
||||
expect.soft(stringifyUnknown(webSearchCall?.input)).toContain("OpenAI Codex MCP");
|
||||
@@ -894,11 +890,11 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
}
|
||||
}
|
||||
const commandCallIds = toolCalls
|
||||
.filter((item) => item.server === "command")
|
||||
.filter((item) => item.name === "shell")
|
||||
.map((item) => item.callId)
|
||||
.filter((callId): callId is string => typeof callId === "string");
|
||||
const fileChangeCallIds = toolCalls
|
||||
.filter((item) => item.server === "file_change")
|
||||
.filter((item) => item.name === "apply_patch")
|
||||
.map((item) => item.callId)
|
||||
.filter((callId): callId is string => typeof callId === "string");
|
||||
|
||||
@@ -1147,13 +1143,13 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "granted"
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
timelineItems.some(
|
||||
(item) => item.type === "tool_call" && item.server === "command"
|
||||
(item) => item.type === "tool_call" && item.name === "shell"
|
||||
)
|
||||
).toBe(true);
|
||||
expect(existsSync(filePath)).toBe(true);
|
||||
@@ -1211,7 +1207,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "requested"
|
||||
)
|
||||
).toBe(true);
|
||||
@@ -1282,7 +1278,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "denied"
|
||||
)
|
||||
).toBe(true);
|
||||
@@ -1363,7 +1359,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "denied"
|
||||
)
|
||||
).toBe(true);
|
||||
@@ -1419,7 +1415,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
event.type === "timeline" &&
|
||||
providerFromEvent(event) === "codex" &&
|
||||
event.item.type === "tool_call" &&
|
||||
event.item.server === "command" &&
|
||||
event.item.name === "shell" &&
|
||||
isSleepCommandToolCall(event.item)
|
||||
) {
|
||||
sawSleepCommand = true;
|
||||
@@ -1494,7 +1490,7 @@ describe("CodexMcpAgentClient (MCP integration)", () => {
|
||||
event.type === "timeline" &&
|
||||
providerFromEvent(event) === "codex" &&
|
||||
event.item.type === "tool_call" &&
|
||||
event.item.server === "command"
|
||||
event.item.name === "shell"
|
||||
) {
|
||||
const commandText = commandTextFromInput(event.item.input);
|
||||
if (commandText && commandText.includes(marker)) {
|
||||
|
||||
@@ -2523,7 +2523,17 @@ function buildCodexMcpConfig(
|
||||
args: ["@playwright/mcp", "--headless", "--isolated"],
|
||||
};
|
||||
|
||||
// Merge user-provided MCP servers (they take precedence)
|
||||
// Merge MCP servers from extra.codex.mcp_servers (legacy location)
|
||||
const extraCodex = config.extra?.codex as Record<string, unknown> | undefined;
|
||||
if (extraCodex?.mcp_servers && typeof extraCodex.mcp_servers === "object") {
|
||||
for (const [name, serverConfig] of Object.entries(extraCodex.mcp_servers as Record<string, unknown>)) {
|
||||
if (typeof serverConfig === "object" && serverConfig !== null) {
|
||||
mcpServers[name] = serverConfig as CodexMcpServerConfig;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge user-provided MCP servers (they take highest precedence)
|
||||
if (config.mcpServers) {
|
||||
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
||||
if (typeof serverConfig === "object" && serverConfig !== null) {
|
||||
@@ -3098,7 +3108,7 @@ class CodexMcpAgentSession implements AgentSession {
|
||||
type: "timeline",
|
||||
provider: CODEX_PROVIDER,
|
||||
item: createToolCallTimelineItem({
|
||||
name: pending.request.name,
|
||||
name: "permission",
|
||||
status,
|
||||
callId: pending.request.id,
|
||||
input: pending.request.input,
|
||||
@@ -3405,7 +3415,7 @@ class CodexMcpAgentSession implements AgentSession {
|
||||
type: "timeline",
|
||||
provider: CODEX_PROVIDER,
|
||||
item: createToolCallTimelineItem({
|
||||
name: request.name,
|
||||
name: "permission",
|
||||
status: "requested",
|
||||
callId: request.id,
|
||||
input: request.input,
|
||||
|
||||
@@ -172,7 +172,7 @@ describe("daemon E2E", () => {
|
||||
const hasGranted = timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "granted"
|
||||
);
|
||||
expect(hasGranted).toBe(true);
|
||||
@@ -256,7 +256,7 @@ describe("daemon E2E", () => {
|
||||
const hasDenied = timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "denied"
|
||||
);
|
||||
expect(hasDenied).toBe(true);
|
||||
@@ -799,8 +799,7 @@ describe("daemon E2E", () => {
|
||||
const hasCreateAgentCall = timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.tool === "create_agent" &&
|
||||
item.server === "agent-control"
|
||||
item.name === "agent-control.create_agent"
|
||||
);
|
||||
expect(hasCreateAgentCall).toBe(true);
|
||||
|
||||
@@ -827,8 +826,7 @@ describe("daemon E2E", () => {
|
||||
const createAgentCall = timelineItems.find(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.tool === "create_agent" &&
|
||||
item.server === "agent-control"
|
||||
item.name === "agent-control.create_agent"
|
||||
);
|
||||
|
||||
let childAgentId: string | null = null;
|
||||
@@ -1372,7 +1370,7 @@ describe("daemon E2E", () => {
|
||||
const hasGranted = timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "granted"
|
||||
);
|
||||
expect(hasGranted).toBe(true);
|
||||
@@ -1463,7 +1461,7 @@ describe("daemon E2E", () => {
|
||||
const hasDenied = timelineItems.some(
|
||||
(item) =>
|
||||
item.type === "tool_call" &&
|
||||
item.server === "permission" &&
|
||||
item.name === "permission" &&
|
||||
item.status === "denied"
|
||||
);
|
||||
expect(hasDenied).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user