Extract pending request derivations

This commit is contained in:
murashit 2026-05-29 05:29:19 +09:00
parent 3f7c9e29ec
commit 172bacfe30
4 changed files with 43 additions and 14 deletions

View file

@ -40,6 +40,14 @@ export function pendingRequestsSignature(
});
}
export function pendingRequestFocusSignature(approvals: readonly PendingApproval[], inputs: readonly PendingUserInput[]): string {
if (approvals.length === 0 && inputs.length === 0) return "";
return JSON.stringify({
approvals: approvals.map((approval) => ({ id: approval.requestId, method: approval.method })),
inputs: inputs.map((input) => ({ id: input.requestId, method: input.method })),
});
}
export function clearUserInputDrafts(drafts: Map<string, string>, input: PendingUserInput): void {
for (const question of input.params.questions) {
drafts.delete(userInputDraftKey(input.requestId, question.id));

View file

@ -3,6 +3,7 @@ import type { ServerRequest } from "../../../generated/app-server/ServerRequest"
import type { ToolRequestUserInputParams } from "../../../generated/app-server/v2/ToolRequestUserInputParams";
import type { ToolRequestUserInputQuestion } from "../../../generated/app-server/v2/ToolRequestUserInputQuestion";
import type { ToolRequestUserInputResponse } from "../../../generated/app-server/v2/ToolRequestUserInputResponse";
import { userInputDraftKey } from "../request-state";
export type UserInputRequest = Extract<ServerRequest, { method: "item/tool/requestUserInput" }>;
@ -37,3 +38,12 @@ export function userInputResponse(input: PendingUserInput, answers: Record<strin
export function questionDefaultAnswer(question: ToolRequestUserInputQuestion): string {
return question.options?.[0]?.label ?? "";
}
export function answersForPendingUserInput(input: PendingUserInput, drafts: ReadonlyMap<string, string>): Record<string, string> {
return Object.fromEntries(
input.params.questions.map((question) => [
question.id,
drafts.get(userInputDraftKey(input.requestId, question.id)) ?? questionDefaultAnswer(question),
]),
);
}

View file

@ -24,9 +24,14 @@ import { mcpStatusLines } from "./mcp-status";
import { ChatAppServerController } from "./chat-app-server-controller";
import { ThreadHistoryLoader } from "./thread-history";
import { ThreadRenameController } from "./thread-rename";
import { pendingRequestsSignature as requestStateSignature, userInputDraftKey, userInputOtherDraftKey } from "./request-state";
import {
pendingRequestFocusSignature,
pendingRequestsSignature as requestStateSignature,
userInputDraftKey,
userInputOtherDraftKey,
} from "./request-state";
import type { CodexPanelSettings } from "../../settings/model";
import { questionDefaultAnswer, type PendingUserInput } from "./user-input/model";
import { answersForPendingUserInput, type PendingUserInput } from "./user-input/model";
import { ChatComposerController } from "./chat-composer-controller";
import {
activeTurnId,
@ -1382,19 +1387,11 @@ export class CodexChatView extends ItemView {
}
private pendingRequestFocusSignature(): string {
const approvals = this.state.approvals.map((approval) => ({ id: approval.requestId, method: approval.method }));
const inputs = this.state.pendingUserInputs.map((input) => ({ id: input.requestId, method: input.method }));
if (approvals.length === 0 && inputs.length === 0) return "";
return JSON.stringify({ approvals, inputs });
return pendingRequestFocusSignature(this.state.approvals, this.state.pendingUserInputs);
}
private answersForUserInput(input: PendingUserInput): Record<string, string> {
return Object.fromEntries(
input.params.questions.map((question) => [
question.id,
this.state.userInputDrafts.get(userInputDraftKey(input.requestId, question.id)) ?? questionDefaultAnswer(question),
]),
);
return answersForPendingUserInput(input, this.state.userInputDrafts);
}
private queueMessagesBottomScroll(): void {

View file

@ -1,7 +1,12 @@
import { describe, expect, it } from "vitest";
import { questionDefaultAnswer, toPendingUserInput, userInputResponse } from "../../../../src/features/chat/user-input/model";
import { pendingRequestsSignature } from "../../../../src/features/chat/request-state";
import {
answersForPendingUserInput,
questionDefaultAnswer,
toPendingUserInput,
userInputResponse,
} from "../../../../src/features/chat/user-input/model";
import { pendingRequestFocusSignature, pendingRequestsSignature } from "../../../../src/features/chat/request-state";
import type { ServerRequest } from "../../../../src/generated/app-server/ServerRequest";
function expectPresent<T>(value: T | null | undefined): T {
@ -34,6 +39,8 @@ describe("user input model", () => {
const input = expectPresent(toPendingUserInput(request));
expect(input).toMatchObject({ requestId: 7, method: "item/tool/requestUserInput" });
expect(questionDefaultAnswer(expectPresent(request.params.questions[0]))).toBe("Recommended");
expect(answersForPendingUserInput(input, new Map())).toEqual({ direction: "Recommended" });
expect(answersForPendingUserInput(input, new Map([["7:direction", "Left"]]))).toEqual({ direction: "Left" });
expect(userInputResponse(input, { direction: "Recommended" })).toEqual({
answers: { direction: { answers: ["Recommended"] } },
});
@ -67,6 +74,13 @@ describe("user input model", () => {
]);
expect(pendingRequestsSignature([], [], drafts)).toBe("");
expect(pendingRequestFocusSignature([], [])).toBe("");
expect(pendingRequestFocusSignature([], [input])).toBe(
JSON.stringify({
approvals: [],
inputs: [{ id: 7, method: "item/tool/requestUserInput" }],
}),
);
expect(pendingRequestsSignature([], [input], drafts)).toBe(
JSON.stringify({
approvals: [],