Skip to content

Commit 2c5d7bd

Browse files
committed
Load full thread history on session resume
Closes #206
1 parent 196152b commit 2c5d7bd

7 files changed

Lines changed: 1620 additions & 49 deletions

src/CodexAcpClient.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,18 @@ export class CodexAcpClient {
230230
threadId: request.sessionId,
231231
});
232232
onSubscribed?.();
233+
const historyResponse = await this.codexClient.threadRead({
234+
threadId: response.thread.id,
235+
includeTurns: true,
236+
});
233237
const codexModels = await this.fetchAvailableModels();
234238
const currentModelId = this.createModelId(codexModels, response.model, response.reasoningEffort).toString();
235239
return {
236240
sessionId: request.sessionId,
237241
currentModelId: currentModelId,
238242
models: codexModels,
239243
currentServiceTier: response.serviceTier as ServiceTier ?? null,
240-
thread: response.thread,
244+
thread: historyResponse.thread,
241245
};
242246
}
243247

src/CodexAcpServer.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {CodexCommands} from "./CodexCommands";
3333
import type {QuotaMeta} from "./QuotaMeta";
3434
import {logger} from "./Logger";
3535
import {sanitizeMcpServerName} from "./McpServerName";
36+
import {createResponseItemHistoryFallbackUpdates} from "./ResponseItemHistoryFallback";
3637
import {
3738
type LegacyLoadSessionResponse,
3839
type LegacyNewSessionResponse,
@@ -44,6 +45,7 @@ import {
4445
LEGACY_SET_SESSION_MODEL_METHOD,
4546
} from "./AcpExtensions";
4647
import {
48+
createCommandExecutionCompleteUpdate,
4749
createCommandExecutionUpdate,
4850
createDynamicToolCallUpdate,
4951
createFileChangeUpdate,
@@ -833,17 +835,29 @@ export class CodexAcpServer implements acp.Agent {
833835

834836
private async streamThreadHistory(sessionId: string, thread: Thread): Promise<void> {
835837
const session = new ACPSessionConnection(this.connection, sessionId);
838+
const sessionState = this.getSessionState(sessionId);
839+
const responseItemFallbackUpdates = await createResponseItemHistoryFallbackUpdates(
840+
thread,
841+
sessionState.terminalOutputMode,
842+
);
843+
if (responseItemFallbackUpdates) {
844+
for (const update of responseItemFallbackUpdates) {
845+
await session.update(update);
846+
}
847+
return;
848+
}
849+
836850
for (const turn of thread.turns) {
837851
for (const item of turn.items) {
838-
const updates = await this.createHistoryUpdates(item);
852+
const updates = await this.createHistoryUpdates(item, sessionState);
839853
for (const update of updates) {
840854
await session.update(update);
841855
}
842856
}
843857
}
844858
}
845859

846-
private async createHistoryUpdates(item: ThreadItem): Promise<UpdateSessionEvent[]> {
860+
private async createHistoryUpdates(item: ThreadItem, sessionState: SessionState): Promise<UpdateSessionEvent[]> {
847861
switch (item.type) {
848862
case "userMessage":
849863
return this.createUserMessageUpdates(item);
@@ -859,8 +873,14 @@ export class CodexAcpServer implements acp.Agent {
859873
return this.createReasoningUpdates(item);
860874
case "fileChange":
861875
return [await createFileChangeUpdate(item)];
862-
case "commandExecution":
863-
return [await createCommandExecutionUpdate(item)];
876+
case "commandExecution": {
877+
const updates = [await createCommandExecutionUpdate(item)];
878+
const completeUpdate = createCommandExecutionCompleteUpdate(item, sessionState.terminalOutputMode);
879+
if (completeUpdate) {
880+
updates.push(completeUpdate);
881+
}
882+
return updates;
883+
}
864884
case "mcpToolCall":
865885
return [await createMcpToolCallUpdate(item)];
866886
case "dynamicToolCall":

src/CodexToolCallMapper.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ import type {
2727
} from "./app-server/v2";
2828
import type { JsonValue } from "./app-server/serde_json/JsonValue";
2929
import {logger} from "./Logger";
30+
import {
31+
createTerminalOutputMeta,
32+
type TerminalOutputMode,
33+
} from "./TerminalOutputMode";
3034

3135
type CodexItemStatus = CommandExecutionStatus | PatchApplyStatus | McpToolCallStatus | DynamicToolCallStatus;
3236
type AcpToolCallStatus = "pending" | "in_progress" | "completed" | "failed";
@@ -87,6 +91,47 @@ export async function createCommandExecutionUpdate(item: CommandExecutionItem):
8791
}, item.id, item.cwd);
8892
}
8993

94+
export function createCommandExecutionCompleteUpdate(
95+
item: CommandExecutionItem,
96+
terminalOutputMode: TerminalOutputMode,
97+
): UpdateSessionEvent | null {
98+
if (item.status === "inProgress") {
99+
return null;
100+
}
101+
102+
const update: UpdateSessionEvent = {
103+
sessionUpdate: "tool_call_update",
104+
toolCallId: item.id,
105+
status: item.status === "completed" ? "completed" : "failed",
106+
rawOutput: {
107+
formatted_output: item.aggregatedOutput ?? "",
108+
exit_code: item.exitCode,
109+
},
110+
};
111+
112+
if (!commandExecutionUsesTerminalOutput(item)) {
113+
return update;
114+
}
115+
116+
const terminalMeta: Record<string, unknown> = {};
117+
if (item.aggregatedOutput) {
118+
Object.assign(
119+
terminalMeta,
120+
createTerminalOutputMeta(terminalOutputMode, item.id, item.aggregatedOutput),
121+
);
122+
}
123+
terminalMeta["terminal_exit"] = {
124+
exit_code: item.exitCode,
125+
signal: null,
126+
terminal_id: item.id,
127+
};
128+
129+
return {
130+
...update,
131+
_meta: terminalMeta,
132+
};
133+
}
134+
90135
export async function createMcpToolCallUpdate(
91136
item: ThreadItem & { type: "mcpToolCall" }
92137
): Promise<UpdateSessionEvent> {
@@ -334,12 +379,12 @@ export function formatWebSearchTitle(item: WebSearchItem): string {
334379
}
335380
}
336381

337-
function createCommandActionEvent(
382+
export function createCommandActionEvent(
338383
id: string,
339384
status: CommandExecutionStatus,
340385
cwd: string,
341386
commandAction: CommandAction
342-
): UpdateSessionEvent {
387+
): AcpToolCallEvent {
343388
const acpStatus = toAcpStatus(status);
344389
switch (commandAction.type) {
345390
case "read":
@@ -395,7 +440,7 @@ function createTerminalCommandEvent(
395440
event: AcpToolCallEvent,
396441
terminalId: string,
397442
cwd: string,
398-
): UpdateSessionEvent {
443+
): AcpToolCallEvent {
399444
const { rawInput, ...eventWithoutRawInput } = event;
400445
return {
401446
...eventWithoutRawInput,

0 commit comments

Comments
 (0)