Skip to content

Commit 2427ddf

Browse files
authored
fix(codex): preserve boundaries between reasoning summary parts (#291)
1 parent d9f23e8 commit 2427ddf

3 files changed

Lines changed: 48 additions & 2 deletions

File tree

src/supervisor/agents/codex/canonicalMapping.test.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1343,12 +1343,43 @@ describe("mapCodexNotification — streaming deltas", () => {
13431343
expect(text[0]).toMatchObject({ type: "content.delta", stream: "reasoning_text" });
13441344
const summary = mapCodexNotification(
13451345
"item/reasoning/summaryTextDelta",
1346-
{ threadId: "x", itemId: "rs-1", delta: "summary" },
1346+
{ threadId: "x", itemId: "rs-1", delta: "summary", summaryIndex: 0 },
13471347
state,
13481348
);
13491349
expect(summary[0]).toMatchObject({ type: "content.delta", stream: "reasoning_text" });
13501350
});
13511351

1352+
it("preserves boundaries between indexed reasoning summary parts", () => {
1353+
const state = createCodexMapperState("t-codex");
1354+
mapCodexNotification(
1355+
"item/started",
1356+
{ threadId: "x", itemId: "rs-1", item: { id: "rs-1", type: "reasoning" } },
1357+
state,
1358+
);
1359+
1360+
const events = [
1361+
...mapCodexNotification(
1362+
"item/reasoning/summaryTextDelta",
1363+
{ threadId: "x", itemId: "rs-1", delta: "**Planning sidebar**", summaryIndex: 0 },
1364+
state,
1365+
),
1366+
...mapCodexNotification(
1367+
"item/reasoning/summaryTextDelta",
1368+
{ threadId: "x", itemId: "rs-1", delta: "**Refining", summaryIndex: 1 },
1369+
state,
1370+
),
1371+
...mapCodexNotification(
1372+
"item/reasoning/summaryTextDelta",
1373+
{ threadId: "x", itemId: "rs-1", delta: " removal**", summaryIndex: 1 },
1374+
state,
1375+
),
1376+
];
1377+
1378+
expect(
1379+
events.flatMap((event) => (event.type === "content.delta" ? [event.delta] : [])).join(""),
1380+
).toBe("**Planning sidebar**\n\n**Refining removal**");
1381+
});
1382+
13521383
it("maps MCP tool progress into the existing tool payload", () => {
13531384
const state = createCodexMapperState("t-codex");
13541385
mapCodexNotification(

src/supervisor/agents/codex/canonicalMapping/dispatch.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export function mapCodexNotification(
9191
state.commandOutputSeenSet.clear();
9292
state.fileChangeOutputMap.clear();
9393
state.fileChangePathMap.clear();
94+
state.reasoningSummaryIndexMap.clear();
9495
return events;
9596
}
9697

@@ -260,6 +261,7 @@ export function mapCodexNotification(
260261
state.commandOutputSeenSet.delete(codexItemId);
261262
state.fileChangeOutputMap.delete(codexItemId);
262263
state.fileChangePathMap.delete(codexItemId);
264+
state.reasoningSummaryIndexMap.delete(codexItemId);
263265
return events;
264266
}
265267
const itemType = state.itemTypeMap.get(codexItemId) ?? canonicalTypeFor(item.type ?? item.kind);
@@ -300,6 +302,7 @@ export function mapCodexNotification(
300302
state.commandOutputSeenSet.delete(codexItemId);
301303
state.fileChangeOutputMap.delete(codexItemId);
302304
state.fileChangePathMap.delete(codexItemId);
305+
state.reasoningSummaryIndexMap.delete(codexItemId);
303306
return events;
304307
}
305308

@@ -310,6 +313,15 @@ export function mapCodexNotification(
310313
if (!delta) return [];
311314
const codexItemId = readItemId(params);
312315
if (!codexItemId) return [];
316+
let contentDelta = delta;
317+
const summaryIndex = params?.summaryIndex;
318+
if (method === "item/reasoning/summaryTextDelta" && typeof summaryIndex === "number") {
319+
const previousIndex = state.reasoningSummaryIndexMap.get(codexItemId);
320+
if (previousIndex !== summaryIndex) {
321+
if (previousIndex !== undefined) contentDelta = `\n\n${delta}`;
322+
state.reasoningSummaryIndexMap.set(codexItemId, summaryIndex);
323+
}
324+
}
313325
let internalId = state.itemIdMap.get(codexItemId);
314326
const opened: RuntimeEvent[] = [];
315327
if (!internalId) {
@@ -349,7 +361,7 @@ export function mapCodexNotification(
349361
threadId,
350362
itemId: internalId,
351363
stream,
352-
delta,
364+
delta: contentDelta,
353365
},
354366
];
355367
}

src/supervisor/agents/codex/canonicalMappingState.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface CodexMapperState {
1616
fileChangeOutputMap: Map<string, string>;
1717
/** Last path emitted for a file-change item, to avoid duplicate updates. */
1818
fileChangePathMap: Map<string, string>;
19+
/** Last streamed reasoning summary index, used to preserve summary-part boundaries. */
20+
reasoningSummaryIndexMap: Map<string, number>;
1921
/** Current chat item that mirrors the provider's active goal state. */
2022
goalItemId?: string;
2123
/** Provider-created timestamp for the current goal, when reported. */
@@ -34,6 +36,7 @@ export function createCodexMapperState(threadId: string): CodexMapperState {
3436
commandOutputSeenSet: new Set(),
3537
fileChangeOutputMap: new Map(),
3638
fileChangePathMap: new Map(),
39+
reasoningSummaryIndexMap: new Map(),
3740
};
3841
}
3942

0 commit comments

Comments
 (0)