feat: 支持运行中 steer command

This commit is contained in:
Codex
2026-06-02 10:04:36 +08:00
parent 1d45d272f1
commit d90e01a91c
15 changed files with 206 additions and 13 deletions
+23
View File
@@ -48,6 +48,14 @@ export interface CodexStdioTurnOptions {
codexHome?: string;
abortSignal?: AbortSignal;
onEvent?: (event: BackendEvent) => void | Promise<void>;
onActiveTurn?: (control: CodexActiveTurnControl) => void | (() => void);
}
export interface CodexActiveTurnControl {
threadId: string;
turnId: string;
steer(prompt: string): Promise<void>;
interrupt(): Promise<void>;
}
interface PendingRequest {
@@ -396,6 +404,7 @@ async function runCodexStdioTurnWithSession(options: CodexStdioTurnOptions, sess
const terminalPromise = new Promise<void>((resolve) => { terminalResolve = resolve; });
let client: CodexStdioClient | null = null;
const requestTimeoutMs = Math.min(positiveTimeout(options.timeoutMs), requestTimeoutCapMs);
let stopActiveTurn: (() => void) | undefined;
const abortTurn = (): void => {
if (terminal) return;
terminal = { status: "cancelled", failureKind: "cancelled", message: "cancel requested" };
@@ -463,6 +472,19 @@ async function runCodexStdioTurnWithSession(options: CodexStdioTurnOptions, sess
const turnResponse = requireResponseRecord(await client.request("turn/start", withOptionalModel({ threadId, input: textInput(options.prompt), cwd: options.cwd, approvalPolicy: options.approvalPolicy }, options.model), requestTimeoutMs), "turn/start");
turnId = requireNestedId(turnResponse, "turn/start", "turn");
emitEvent({ type: "backend_status", payload: { phase: "turn/start:completed", turnId } });
if (threadId && turnId && options.onActiveTurn) {
const maybeStop = options.onActiveTurn({
threadId,
turnId,
steer: async (prompt: string) => {
await client!.request("turn/steer", { threadId: threadId!, expectedTurnId: turnId!, input: textInput(prompt) }, requestTimeoutMs);
},
interrupt: async () => {
await client!.request("turn/interrupt", { threadId: threadId!, turnId: turnId! }, requestTimeoutMs);
},
});
if (typeof maybeStop === "function") stopActiveTurn = maybeStop;
}
const race = await Promise.race([
terminalPromise.then(() => ({ kind: "terminal" as const })),
@@ -480,6 +502,7 @@ async function runCodexStdioTurnWithSession(options: CodexStdioTurnOptions, sess
emitEvent({ type: "error", payload: { failureKind: failure.failureKind, message: failure.message, phase: failure.phase, details: failure.details } });
}
} finally {
stopActiveTurn?.();
stopNotifications();
options.abortSignal?.removeEventListener("abort", abortTurn);
clearTimeout(timeout);