fix: recover stale Code Agent browser threads
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user