Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
39 changes: 39 additions & 0 deletions apps/desktop/src/main/services/chat/agentChatService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

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);
Expand Down
37 changes: 25 additions & 12 deletions apps/desktop/src/main/services/chat/agentChatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return {
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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(
<EditorGroup
{...baseProps}
tabScope="lane"
group={{
...baseProps.group,
activeTabId: otherLaneTabId,
tabs: [
...baseProps.group.tabs,
{
id: otherLaneTabId,
workspaceId: "workspace-2",
laneId: "lane-2",
path: "src/other.ts",
title: "other.ts",
viewerKind: "code",
languageId: "typescript",
preview: false,
pinned: false,
},
],
}}
/>,
);

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(<EditorGroup {...baseProps} />);
const input = screen.getByTestId("viewer-input");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export function EditorGroup(props: EditorGroupProps) {
<TabButton
key={tab.id}
tab={tab}
active={tab.id === group.activeTabId}
active={tab.id === activeTab?.id}
dirty={dirtyTabIds.has(tab.id)}
laneAccent={props.tabScope === "all" ? laneAccentForTab(tab, props.lanes) : undefined}
showLaneDivider={props.tabScope === "all" && isLaneGroupBoundary(displayTabs, index)}
Expand Down