From e886e2271685e7c2a051d701d6201f118a777a50 Mon Sep 17 00:00:00 2001
From: Arul Sharma <31745423+arul28@users.noreply.github.com>
Date: Tue, 30 Jun 2026 01:55:40 -0400
Subject: [PATCH 1/3] Fix Codex handoff goal ordering
---
.../services/chat/agentChatService.test.ts | 39 +++++++++++++++++++
.../main/services/chat/agentChatService.ts | 37 ++++++++++++------
.../components/files/v2/EditorGroup.test.tsx | 31 +++++++++++++++
.../components/files/v2/EditorGroup.tsx | 2 +-
4 files changed, 96 insertions(+), 13 deletions(-)
diff --git a/apps/desktop/src/main/services/chat/agentChatService.test.ts b/apps/desktop/src/main/services/chat/agentChatService.test.ts
index 0ce548b80..5c4a3b7d1 100644
--- a/apps/desktop/src/main/services/chat/agentChatService.test.ts
+++ b/apps/desktop/src/main/services/chat/agentChatService.test.ts
@@ -3181,6 +3181,45 @@ describe("createAgentChatService", () => {
}, { timeout: 2000, interval: 50 });
});
+ it("sends Codex brief handoff text before syncing the inherited goal", async () => {
+ const { service, sessionService } = createService();
+ const source = await service.createSession({
+ laneId: "lane-1",
+ provider: "codex",
+ model: "gpt-5.5",
+ modelId: "openai/gpt-5.5",
+ });
+ sessionService.updateMeta({
+ sessionId: source.id,
+ goal: "No Machine State Polish",
+ });
+ const sourceRow = mockState.sessions.get(source.id);
+ if (sourceRow) {
+ sourceRow.summary = "Fix the iPhone 17 simulator chat layout handoff.";
+ }
+
+ const result = await service.handoffSession({
+ sourceSessionId: source.id,
+ targetModelId: "openai/gpt-5.5",
+ });
+
+ expect(result.session.provider).toBe("codex");
+ expect(mockState.sessions.get(result.session.id)?.goal).toBe("No Machine State Polish");
+
+ const requestMethods = mockState.codexRequestPayloads.map((payload) => String(payload.method ?? ""));
+ const turnStartIndex = requestMethods.indexOf("turn/start");
+ const goalSetIndex = requestMethods.indexOf("thread/goal/set");
+ expect(turnStartIndex).toBeGreaterThanOrEqual(0);
+ expect(goalSetIndex === -1 || goalSetIndex > turnStartIndex).toBe(true);
+
+ const turnStartRequest = mockState.codexRequestPayloads[turnStartIndex] as {
+ params?: { input?: Array<{ text?: unknown }> };
+ };
+ const inputText = turnStartRequest.params?.input?.map((entry) => String(entry.text ?? "")).join("\n") ?? "";
+ expect(inputText).toContain("This message was injected automatically by ADE during a chat handoff.");
+ expect(inputText).toContain("No Machine State Polish");
+ });
+
it("uses the selected Claude handoff permission instead of the source interaction mode", async () => {
const send = vi.fn().mockResolvedValue(undefined);
const setPermissionMode = vi.fn().mockResolvedValue(undefined);
diff --git a/apps/desktop/src/main/services/chat/agentChatService.ts b/apps/desktop/src/main/services/chat/agentChatService.ts
index eb33ec304..1842a43d5 100644
--- a/apps/desktop/src/main/services/chat/agentChatService.ts
+++ b/apps/desktop/src/main/services/chat/agentChatService.ts
@@ -19970,27 +19970,40 @@ export function createAgentChatService(args: {
const inheritedGoal = trimLine(sourceSession.goal)
?? trimLine(sourceSession.summary)
?? trimLine(sourceSession.title);
- if (inheritedGoal) {
+ const applyInheritedGoal = (): void => {
+ if (!inheritedGoal) return;
createdManaged.session.goal = inheritedGoal;
sessionService.updateMeta({
sessionId: created.id,
goal: inheritedGoal,
});
+ };
+ const deferInheritedGoalUntilHandoffDispatch =
+ handoffMode === "brief" && createdManaged.session.provider === "codex";
+ if (!deferInheritedGoalUntilHandoffDispatch) {
+ applyInheritedGoal();
}
persistChatState(createdManaged);
if (handoffMode === "brief") {
- await sendMessage({
- sessionId: created.id,
- text: buildHandoffPrompt(brief),
- displayText: "Chat handoff from previous session",
- metadata: { kind: "handoff", hideFullPrompt: true },
- reasoningEffort: targetReasoningEffort,
- executionMode: createdManaged.session.executionMode ?? null,
- interactionMode: createdManaged.session.interactionMode ?? null,
- }, {
- awaitDispatch: true,
- });
+ try {
+ await sendMessage({
+ sessionId: created.id,
+ text: buildHandoffPrompt(brief),
+ displayText: "Chat handoff from previous session",
+ metadata: { kind: "handoff", hideFullPrompt: true },
+ reasoningEffort: targetReasoningEffort,
+ executionMode: createdManaged.session.executionMode ?? null,
+ interactionMode: createdManaged.session.interactionMode ?? null,
+ }, {
+ awaitDispatch: true,
+ });
+ } finally {
+ if (deferInheritedGoalUntilHandoffDispatch) {
+ applyInheritedGoal();
+ persistChatState(createdManaged);
+ }
+ }
}
return {
diff --git a/apps/desktop/src/renderer/components/files/v2/EditorGroup.test.tsx b/apps/desktop/src/renderer/components/files/v2/EditorGroup.test.tsx
index a86cc5f55..cee341de4 100644
--- a/apps/desktop/src/renderer/components/files/v2/EditorGroup.test.tsx
+++ b/apps/desktop/src/renderer/components/files/v2/EditorGroup.test.tsx
@@ -27,6 +27,7 @@ const registry = {
} as unknown as MonacoModelRegistry;
const tabId = editorTabId("workspace-1", "src/file.ts");
+const otherLaneTabId = editorTabId("workspace-2", "src/other.ts");
const baseProps: EditorGroupProps = {
group: {
@@ -108,6 +109,36 @@ describe("EditorGroup", () => {
expect(screen.getByTestId("viewer-button")).toBeTruthy();
});
+ it("marks the visible fallback tab active when lane scope hides the stored active tab", () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByRole("tab", { name: /file\.ts/i }).getAttribute("aria-selected")).toBe("true");
+ expect(screen.queryByRole("tab", { name: /other\.ts/i })).toBeNull();
+ });
+
it("does not steal Cmd+S from focused text inputs", () => {
render();
const input = screen.getByTestId("viewer-input");
diff --git a/apps/desktop/src/renderer/components/files/v2/EditorGroup.tsx b/apps/desktop/src/renderer/components/files/v2/EditorGroup.tsx
index c019748eb..27a933e1a 100644
--- a/apps/desktop/src/renderer/components/files/v2/EditorGroup.tsx
+++ b/apps/desktop/src/renderer/components/files/v2/EditorGroup.tsx
@@ -178,7 +178,7 @@ export function EditorGroup(props: EditorGroupProps) {
Date: Tue, 30 Jun 2026 02:13:21 -0400
Subject: [PATCH 2/3] Address Codex handoff review feedback
---
.../src/main/services/chat/agentChatService.test.ts | 13 ++++++++++---
.../src/main/services/chat/agentChatService.ts | 3 +++
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/apps/desktop/src/main/services/chat/agentChatService.test.ts b/apps/desktop/src/main/services/chat/agentChatService.test.ts
index 5c4a3b7d1..df26d0c08 100644
--- a/apps/desktop/src/main/services/chat/agentChatService.test.ts
+++ b/apps/desktop/src/main/services/chat/agentChatService.test.ts
@@ -3198,6 +3198,7 @@ describe("createAgentChatService", () => {
sourceRow.summary = "Fix the iPhone 17 simulator chat layout handoff.";
}
+ const handoffStart = mockState.codexRequestPayloads.length;
const result = await service.handoffSession({
sourceSessionId: source.id,
targetModelId: "openai/gpt-5.5",
@@ -3206,18 +3207,24 @@ describe("createAgentChatService", () => {
expect(result.session.provider).toBe("codex");
expect(mockState.sessions.get(result.session.id)?.goal).toBe("No Machine State Polish");
- const requestMethods = mockState.codexRequestPayloads.map((payload) => String(payload.method ?? ""));
+ const handoffPayloads = mockState.codexRequestPayloads.slice(handoffStart);
+ const requestMethods = handoffPayloads.map((payload) => String(payload.method ?? ""));
const turnStartIndex = requestMethods.indexOf("turn/start");
const goalSetIndex = requestMethods.indexOf("thread/goal/set");
expect(turnStartIndex).toBeGreaterThanOrEqual(0);
- expect(goalSetIndex === -1 || goalSetIndex > turnStartIndex).toBe(true);
+ expect(goalSetIndex).toBeGreaterThan(turnStartIndex);
- const turnStartRequest = mockState.codexRequestPayloads[turnStartIndex] as {
+ const turnStartRequest = handoffPayloads[turnStartIndex] as {
params?: { input?: Array<{ text?: unknown }> };
};
const inputText = turnStartRequest.params?.input?.map((entry) => String(entry.text ?? "")).join("\n") ?? "";
expect(inputText).toContain("This message was injected automatically by ADE during a chat handoff.");
expect(inputText).toContain("No Machine State Polish");
+
+ const goalSetRequest = handoffPayloads[goalSetIndex] as {
+ params?: { objective?: unknown };
+ };
+ expect(goalSetRequest.params?.objective).toBe("No Machine State Polish");
});
it("uses the selected Claude handoff permission instead of the source interaction mode", async () => {
diff --git a/apps/desktop/src/main/services/chat/agentChatService.ts b/apps/desktop/src/main/services/chat/agentChatService.ts
index 1842a43d5..67fffe2ea 100644
--- a/apps/desktop/src/main/services/chat/agentChatService.ts
+++ b/apps/desktop/src/main/services/chat/agentChatService.ts
@@ -20001,6 +20001,9 @@ export function createAgentChatService(args: {
} finally {
if (deferInheritedGoalUntilHandoffDispatch) {
applyInheritedGoal();
+ if (createdManaged.runtime?.kind === "codex") {
+ await seedCodexThreadGoalFromSessionGoal(createdManaged, createdManaged.runtime);
+ }
persistChatState(createdManaged);
}
}
From c735d71f2239a2ac2865e5dd955c38ab2f33ef43 Mon Sep 17 00:00:00 2001
From: Arul Sharma <31745423+arul28@users.noreply.github.com>
Date: Tue, 30 Jun 2026 02:27:24 -0400
Subject: [PATCH 3/3] Keep deferred Codex goal seeding best effort
---
.../services/chat/agentChatService.test.ts | 31 +++++++++++++++++++
.../main/services/chat/agentChatService.ts | 12 +++++--
2 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/apps/desktop/src/main/services/chat/agentChatService.test.ts b/apps/desktop/src/main/services/chat/agentChatService.test.ts
index df26d0c08..e92bc8922 100644
--- a/apps/desktop/src/main/services/chat/agentChatService.test.ts
+++ b/apps/desktop/src/main/services/chat/agentChatService.test.ts
@@ -3227,6 +3227,37 @@ describe("createAgentChatService", () => {
expect(goalSetRequest.params?.objective).toBe("No Machine State Polish");
});
+ it("keeps Codex brief handoff successful when deferred goal seeding throws", async () => {
+ const { service, sessionService } = createService();
+ const source = await service.createSession({
+ laneId: "lane-1",
+ provider: "codex",
+ model: "gpt-5.5",
+ modelId: "openai/gpt-5.5",
+ });
+ sessionService.updateMeta({
+ sessionId: source.id,
+ goal: "No Machine State Polish",
+ });
+ mockState.codexResponseOverrides.set("thread/goal/set", () => {
+ throw new Error("goal seed unavailable");
+ });
+
+ const handoffStart = mockState.codexRequestPayloads.length;
+ const result = await service.handoffSession({
+ sourceSessionId: source.id,
+ targetModelId: "openai/gpt-5.5",
+ });
+
+ expect(result.session.provider).toBe("codex");
+ expect(mockState.sessions.get(result.session.id)?.goal).toBe("No Machine State Polish");
+ const handoffMethods = mockState.codexRequestPayloads
+ .slice(handoffStart)
+ .map((payload) => String(payload.method ?? ""));
+ expect(handoffMethods).toContain("turn/start");
+ expect(handoffMethods).toContain("thread/goal/set");
+ });
+
it("uses the selected Claude handoff permission instead of the source interaction mode", async () => {
const send = vi.fn().mockResolvedValue(undefined);
const setPermissionMode = vi.fn().mockResolvedValue(undefined);
diff --git a/apps/desktop/src/main/services/chat/agentChatService.ts b/apps/desktop/src/main/services/chat/agentChatService.ts
index 67fffe2ea..4be2e56c8 100644
--- a/apps/desktop/src/main/services/chat/agentChatService.ts
+++ b/apps/desktop/src/main/services/chat/agentChatService.ts
@@ -20001,10 +20001,18 @@ export function createAgentChatService(args: {
} finally {
if (deferInheritedGoalUntilHandoffDispatch) {
applyInheritedGoal();
+ persistChatState(createdManaged);
if (createdManaged.runtime?.kind === "codex") {
- await seedCodexThreadGoalFromSessionGoal(createdManaged, createdManaged.runtime);
+ try {
+ await seedCodexThreadGoalFromSessionGoal(createdManaged, createdManaged.runtime);
+ } catch (error) {
+ logger.warn("agent_chat.codex_goal_seed_after_handoff_failed", {
+ sessionId: createdManaged.session.id,
+ error: error instanceof Error ? error.message : String(error),
+ });
+ persistChatState(createdManaged);
+ }
}
- persistChatState(createdManaged);
}
}
}