fix: 实时上报 runner backend 进展
This commit is contained in:
@@ -135,8 +135,10 @@ async function executeCommand(api: RunnerManagerApi, options: RunnerOnceOptions,
|
||||
const acked = await api.getCommand(options.runId, command.id);
|
||||
if (acked.state === "cancelled") return await reportCancelled(api, options.runId, command.id, runner, attemptId, "command cancelled before backend start");
|
||||
await assertNotCancelled(api, options.runId, command.id);
|
||||
await api.appendEvent(options.runId, { type: "backend_status", payload: { phase: "backend-turn-started", commandId: command.id, attemptId, runnerId: runner.id, backendProfile: options.backendProfile ?? null, workspaceReady: Boolean(workspacePath) } });
|
||||
const abortController = new AbortController();
|
||||
const stopCancelWatch = watchCancellation(api, options.runId, command.id, abortController);
|
||||
const stopBackendProgress = startBackendProgress(api, options.runId, command.id, attemptId, runner.id, options.backendProfile ?? null);
|
||||
try {
|
||||
const latestRun = await api.getRun(options.runId);
|
||||
const backendOptions = { ...options, ...(workspacePath ? { workspacePath } : {}), abortSignal: abortController.signal };
|
||||
@@ -151,6 +153,8 @@ async function executeCommand(api: RunnerManagerApi, options: RunnerOnceOptions,
|
||||
const failure = { failureKind, terminalStatus: terminalStatusForFailure(failureKind), message: errorMessage(error) };
|
||||
return await reportCommandFailure(api, options.runId, command.id, runner, attemptId, failure, "runner:execute");
|
||||
} finally {
|
||||
stopBackendProgress();
|
||||
await appendBestEffort(api, options.runId, { type: "backend_status", payload: { phase: "backend-turn-finished", commandId: command.id, attemptId, runnerId: runner.id } });
|
||||
stopCancelWatch();
|
||||
}
|
||||
}
|
||||
@@ -200,6 +204,31 @@ function startHeartbeat(api: RunnerManagerApi, runId: string, runnerId: string,
|
||||
};
|
||||
}
|
||||
|
||||
function startBackendProgress(api: RunnerManagerApi, runId: string, commandId: string, attemptId: string, runnerId: string, backendProfile: string | null): () => void {
|
||||
let stopped = false;
|
||||
let ticks = 0;
|
||||
const startedAt = Date.now();
|
||||
const emit = async (): Promise<void> => {
|
||||
if (stopped) return;
|
||||
ticks += 1;
|
||||
await appendBestEffort(api, runId, { type: "backend_status", payload: { phase: "backend-turn-running", commandId, attemptId, runnerId, backendProfile, elapsedMs: Date.now() - startedAt, ticks } });
|
||||
};
|
||||
const timer = setInterval(() => { void emit(); }, 10_000);
|
||||
void emit();
|
||||
return () => {
|
||||
stopped = true;
|
||||
clearInterval(timer);
|
||||
};
|
||||
}
|
||||
|
||||
async function appendBestEffort(api: RunnerManagerApi, runId: string, event: BackendEvent): Promise<void> {
|
||||
try {
|
||||
await api.appendEvent(runId, event);
|
||||
} catch {
|
||||
// Visibility events are best effort; terminal command reporting remains authoritative.
|
||||
}
|
||||
}
|
||||
|
||||
function annotateCommandEvent(event: BackendEvent, commandId: string, attemptId: string, runnerId: string): BackendEvent {
|
||||
return { ...event, payload: { ...event.payload, commandId, attemptId, runnerId } };
|
||||
}
|
||||
|
||||
@@ -108,6 +108,11 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
|
||||
assert.equal(multiEvents.filter((event) => event.type === "backend_status" && event.payload?.phase === "codex-app-server-starting").length, 1);
|
||||
assert.equal(multiEvents.filter((event) => event.type === "backend_status" && event.payload?.phase === "codex-app-server:reused").length, 1);
|
||||
assert.equal(multiEvents.filter((event) => event.type === "backend_status" && event.payload?.phase === "resource-bundle-materialized").length, 1);
|
||||
for (const commandId of [multiTurn.commandId, secondCommand.id]) {
|
||||
assert.ok(multiEvents.some((event) => event.type === "backend_status" && event.payload?.phase === "backend-turn-started" && event.payload?.commandId === commandId), `command ${commandId} must emit backend-turn-started before waiting on Codex`);
|
||||
assert.ok(multiEvents.some((event) => event.type === "backend_status" && event.payload?.phase === "backend-turn-running" && event.payload?.commandId === commandId), `command ${commandId} must emit backend-turn-running while Codex is active`);
|
||||
assert.ok(multiEvents.some((event) => event.type === "backend_status" && event.payload?.phase === "backend-turn-finished" && event.payload?.commandId === commandId), `command ${commandId} must emit backend-turn-finished after Codex returns`);
|
||||
}
|
||||
assert.equal(multiEvents.filter((event) => event.type === "backend_status" && event.payload?.phase === "command-terminal").length, 2);
|
||||
const secondEnvelope = await client.get(`/api/v1/runs/${multiTurn.runId}/commands/${secondCommand.id}/result`) as JsonRecord;
|
||||
assert.equal(secondEnvelope.terminalStatus, "completed");
|
||||
|
||||
Reference in New Issue
Block a user