@@ -3181,6 +3181,83 @@ describe("createAgentChatService", () => {
31813181 }, { timeout: 2000, interval: 50 });
31823182 });
31833183
3184+ it("sends Codex brief handoff text before syncing the inherited goal", async () => {
3185+ const { service, sessionService } = createService();
3186+ const source = await service.createSession({
3187+ laneId: "lane-1",
3188+ provider: "codex",
3189+ model: "gpt-5.5",
3190+ modelId: "openai/gpt-5.5",
3191+ });
3192+ sessionService.updateMeta({
3193+ sessionId: source.id,
3194+ goal: "No Machine State Polish",
3195+ });
3196+ const sourceRow = mockState.sessions.get(source.id);
3197+ if (sourceRow) {
3198+ sourceRow.summary = "Fix the iPhone 17 simulator chat layout handoff.";
3199+ }
3200+
3201+ const handoffStart = mockState.codexRequestPayloads.length;
3202+ const result = await service.handoffSession({
3203+ sourceSessionId: source.id,
3204+ targetModelId: "openai/gpt-5.5",
3205+ });
3206+
3207+ expect(result.session.provider).toBe("codex");
3208+ expect(mockState.sessions.get(result.session.id)?.goal).toBe("No Machine State Polish");
3209+
3210+ const handoffPayloads = mockState.codexRequestPayloads.slice(handoffStart);
3211+ const requestMethods = handoffPayloads.map((payload) => String(payload.method ?? ""));
3212+ const turnStartIndex = requestMethods.indexOf("turn/start");
3213+ const goalSetIndex = requestMethods.indexOf("thread/goal/set");
3214+ expect(turnStartIndex).toBeGreaterThanOrEqual(0);
3215+ expect(goalSetIndex).toBeGreaterThan(turnStartIndex);
3216+
3217+ const turnStartRequest = handoffPayloads[turnStartIndex] as {
3218+ params?: { input?: Array<{ text?: unknown }> };
3219+ };
3220+ const inputText = turnStartRequest.params?.input?.map((entry) => String(entry.text ?? "")).join("\n") ?? "";
3221+ expect(inputText).toContain("This message was injected automatically by ADE during a chat handoff.");
3222+ expect(inputText).toContain("No Machine State Polish");
3223+
3224+ const goalSetRequest = handoffPayloads[goalSetIndex] as {
3225+ params?: { objective?: unknown };
3226+ };
3227+ expect(goalSetRequest.params?.objective).toBe("No Machine State Polish");
3228+ });
3229+
3230+ it("keeps Codex brief handoff successful when deferred goal seeding throws", async () => {
3231+ const { service, sessionService } = createService();
3232+ const source = await service.createSession({
3233+ laneId: "lane-1",
3234+ provider: "codex",
3235+ model: "gpt-5.5",
3236+ modelId: "openai/gpt-5.5",
3237+ });
3238+ sessionService.updateMeta({
3239+ sessionId: source.id,
3240+ goal: "No Machine State Polish",
3241+ });
3242+ mockState.codexResponseOverrides.set("thread/goal/set", () => {
3243+ throw new Error("goal seed unavailable");
3244+ });
3245+
3246+ const handoffStart = mockState.codexRequestPayloads.length;
3247+ const result = await service.handoffSession({
3248+ sourceSessionId: source.id,
3249+ targetModelId: "openai/gpt-5.5",
3250+ });
3251+
3252+ expect(result.session.provider).toBe("codex");
3253+ expect(mockState.sessions.get(result.session.id)?.goal).toBe("No Machine State Polish");
3254+ const handoffMethods = mockState.codexRequestPayloads
3255+ .slice(handoffStart)
3256+ .map((payload) => String(payload.method ?? ""));
3257+ expect(handoffMethods).toContain("turn/start");
3258+ expect(handoffMethods).toContain("thread/goal/set");
3259+ });
3260+
31843261 it("uses the selected Claude handoff permission instead of the source interaction mode", async () => {
31853262 const send = vi.fn().mockResolvedValue(undefined);
31863263 const setPermissionMode = vi.fn().mockResolvedValue(undefined);
0 commit comments