mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
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:
@@ -436,6 +436,7 @@ function AgentFlowModal({
|
||||
(value: string) => {
|
||||
setWorkingDirFromUser(value);
|
||||
setErrorMessage("");
|
||||
shouldSyncBaseBranchRef.current = true;
|
||||
},
|
||||
[setWorkingDirFromUser]
|
||||
);
|
||||
|
||||
@@ -119,6 +119,7 @@ function DiffLineView({ line }: { line: DiffLine }) {
|
||||
function DiffFileSection({ file, defaultExpanded = true, testID }: DiffFileSectionProps) {
|
||||
const { theme } = useUnistyles();
|
||||
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
||||
const [scrollViewWidth, setScrollViewWidth] = useState(0);
|
||||
const horizontalScroll = useHorizontalScrollOptional();
|
||||
const scrollId = useId();
|
||||
|
||||
@@ -190,8 +191,9 @@ function DiffFileSection({ file, defaultExpanded = true, testID }: DiffFileSecti
|
||||
contentContainerStyle={styles.diffContentInner}
|
||||
onScroll={handleScroll}
|
||||
scrollEventThrottle={16}
|
||||
onLayout={(e) => setScrollViewWidth(e.nativeEvent.layout.width)}
|
||||
>
|
||||
<View style={styles.linesContainer}>
|
||||
<View style={[styles.linesContainer, scrollViewWidth > 0 && { minWidth: scrollViewWidth }]}>
|
||||
{file.hunks.map((hunk, hunkIndex) =>
|
||||
hunk.lines.map((line, lineIndex) => (
|
||||
<DiffLineView key={`${hunkIndex}-${lineIndex}`} line={line} />
|
||||
@@ -388,8 +390,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
flexDirection: "column",
|
||||
},
|
||||
linesContainer: {
|
||||
alignSelf: "flex-start",
|
||||
minWidth: "100%",
|
||||
backgroundColor: "#0d1117",
|
||||
},
|
||||
diffLineContainer: {
|
||||
|
||||
@@ -8,7 +8,6 @@ const LEGACY_SETTINGS_KEY = "@paseo:settings";
|
||||
const FALLBACK_DAEMON_URL = "ws://localhost:6767/ws";
|
||||
const DEFAULT_DAEMONS: Array<{ label: string; wsUrl: string; restUrl?: string | null }> = [
|
||||
{ label: "localhost", wsUrl: "ws://localhost:6767/ws" },
|
||||
{ label: "macbook", wsUrl: "ws://mohameds-macbook-pro:6767/ws" },
|
||||
];
|
||||
const DAEMON_REGISTRY_QUERY_KEY = ["daemon-registry"];
|
||||
|
||||
|
||||
@@ -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