fix: recover stale Code Agent browser threads

This commit is contained in:
Codex
2026-05-31 17:05:18 +08:00
parent 79f2694f0c
commit 0bfc5c39d7
3 changed files with 163 additions and 47 deletions
+94 -46
View File
@@ -1606,55 +1606,64 @@ async function runAppServerTurn({
threadId,
waitingFor: "thread/resume"
});
const resumed = await client.resumeThread({
threadId,
model,
cwd: workspace,
sandbox,
approvalPolicy: "never"
}, effectiveMs);
threadId = optionalId(resumed?.threadId) ?? threadId;
state.setThreadId(threadId);
state.appendTrace({
type: "thread",
status: "completed",
label: "thread:resume:completed",
sessionId: session.sessionId,
sessionStatus: session.status,
turn: session.turn,
threadId,
waitingFor: "turn/start"
});
try {
const resumed = await client.resumeThread({
threadId,
model,
cwd: workspace,
sandbox,
approvalPolicy: "never"
}, effectiveMs);
threadId = optionalId(resumed?.threadId) ?? threadId;
state.setThreadId(threadId);
state.appendTrace({
type: "thread",
status: "completed",
label: "thread:resume:completed",
sessionId: session.sessionId,
sessionStatus: session.status,
turn: session.turn,
threadId,
waitingFor: "turn/start"
});
} catch (error) {
if (!isStaleAppServerThreadError(error)) throw error;
state.appendTrace({
type: "thread",
status: "degraded",
label: "thread:resume:stale",
sessionId: session.sessionId,
sessionStatus: session.status,
turn: session.turn,
threadId,
message: "Persisted app-server thread is no longer resumable; starting a fresh thread for this turn.",
waitingFor: "thread/start"
});
session.threadId = null;
threadId = null;
state.setThreadId(null);
const started = await startFreshAppServerThread({
client,
state,
session,
workspace,
sandbox,
model,
effectiveMs
});
threadId = started.threadId;
}
} else {
state.appendTrace({
type: "thread",
status: "started",
label: "thread:start:started",
sessionId: session.sessionId,
sessionStatus: session.status,
turn: session.turn,
waitingFor: "thread/start"
});
const started = await client.startThread({
model,
cwd: workspace,
const started = await startFreshAppServerThread({
client,
state,
session,
workspace,
sandbox,
approvalPolicy: "never",
serviceName: "hwlab-cloud-api"
}, effectiveMs);
threadId = optionalId(started?.threadId);
if (!threadId) throw new Error("thread/start response did not include thread.id");
state.setThreadId(threadId);
state.appendTrace({
type: "thread",
status: "completed",
label: "thread:start:completed",
sessionId: session.sessionId,
sessionStatus: session.status,
turn: session.turn,
threadId,
waitingFor: "turn/start"
model,
effectiveMs
});
threadId = started.threadId;
}
state.appendTrace({
@@ -1698,3 +1707,42 @@ async function runAppServerTurn({
}
}
}
async function startFreshAppServerThread({ client, state, session, workspace, sandbox, model, effectiveMs }) {
state.appendTrace({
type: "thread",
status: "started",
label: "thread:start:started",
sessionId: session.sessionId,
sessionStatus: session.status,
turn: session.turn,
waitingFor: "thread/start"
});
const started = await client.startThread({
model,
cwd: workspace,
sandbox,
approvalPolicy: "never",
serviceName: "hwlab-cloud-api"
}, effectiveMs);
const threadId = optionalId(started?.threadId);
if (!threadId) throw new Error("thread/start response did not include thread.id");
state.setThreadId(threadId);
state.appendTrace({
type: "thread",
status: "completed",
label: "thread:start:completed",
sessionId: session.sessionId,
sessionStatus: session.status,
turn: session.turn,
threadId,
waitingFor: "turn/start"
});
return { threadId };
}
function isStaleAppServerThreadError(error) {
const message = String(error?.message ?? "");
return /\bno\s+rollout\s+found\s+for\s+thread\s+id\b/iu.test(message) ||
/\bthread\b.*\b(?:not\s+found|missing|expired|gone|unknown)\b/iu.test(message);
}