Finalize strict ESLint fixes

This commit is contained in:
murashit 2026-05-23 05:01:29 +09:00
parent f172610277
commit 2918451eaf
9 changed files with 145 additions and 106 deletions

View file

@ -201,13 +201,7 @@ export class PanelController {
} else if (method === "hook/completed") {
this.upsertHookRun(params.run, params.turnId, params.run.status);
} else if (method === "item/mcpToolCall/progress") {
this.state.displayItems = appendToolOutput(
this.state.displayItems,
params.itemId,
params.turnId,
params.message,
"mcp progress",
);
this.state.displayItems = appendToolOutput(this.state.displayItems, params.itemId, params.turnId, params.message, "mcp progress");
} else if (method === "item/autoApprovalReview/started" || method === "item/autoApprovalReview/completed") {
const reviewItem = createAutoReviewResultItem(params);
this.state.displayItems = upsertDisplayItem(removeUnstructuredAutoReviewWarnings(this.state.displayItems), reviewItem);
@ -427,6 +421,7 @@ export class PanelController {
this.state.activeReasoningEffort = settings.effort;
this.state.activeCollaborationMode = settings.collaborationMode.mode;
this.state.requestedCollaborationMode = settings.collaborationMode.mode;
const nullableSettings: ThreadSettingsWithNullableReviewer = settings;
const serviceTierOverride = this.state.activeServiceTierOverride || this.state.requestedServiceTier !== null;
const approvalsReviewerOverride = this.state.activeApprovalsReviewerOverride || this.state.requestedApprovalsReviewer !== null;
this.state.activeServiceTier = settings.serviceTier ?? (serviceTierOverride ? this.state.activeServiceTier : null);
@ -437,6 +432,11 @@ export class PanelController {
}
}
type ThreadSettings = Extract<ServerNotification, { method: "thread/settings/updated" }>["params"]["threadSettings"];
type ThreadSettingsWithNullableReviewer = Omit<ThreadSettings, "approvalsReviewer"> & {
approvalsReviewer: ThreadSettings["approvalsReviewer"] | null;
};
function removeUnstructuredAutoReviewWarnings(items: DisplayItem[]): DisplayItem[] {
return items.filter((item) => !isUnstructuredAutoReviewWarning(item));
}

View file

@ -105,9 +105,12 @@ export async function generateThreadTitleWithCodex(
let client!: AppServerClient;
client = new AppServerClient(codexPath, cwd, {
onNotification: (notification) => { handleNamingNotification(notification); },
onServerRequest: (request) =>
{ client.rejectServerRequest(request.id, -32601, "Thread title generation does not handle server requests."); },
onNotification: (notification) => {
handleNamingNotification(notification);
},
onServerRequest: (request) => {
client.rejectServerRequest(request.id, -32601, "Thread title generation does not handle server requests.");
},
onLog: () => undefined,
onExit: () => {
if (completed) return;

View file

@ -930,6 +930,7 @@ export class CodexPanelView extends ItemView {
this.render();
await this.ensureConnected();
if (!threadId) return;
try {
await this.resumeThread(threadId);
} catch (error) {

View file

@ -212,8 +212,12 @@ function renderHookRow(list: HTMLElement, hook: HookMetadata, state: HookSection
});
}
function firstNonEmptyString(...values: string[]): string {
return values.find((value) => value.length > 0) ?? values.at(-1) ?? "";
function firstNonEmptyString(...values: (string | null | undefined)[]): string {
return (
values.find((value): value is string => typeof value === "string" && value.length > 0) ??
values.find((value): value is string => typeof value === "string") ??
""
);
}
function formatThreadDate(timestamp: number): string {

View file

@ -48,6 +48,12 @@ export interface MessageStreamContext {
renderPendingRequests?: () => HTMLElement | null;
}
type RenderableMessageItem = Extract<DisplayItem, { kind: "message" | "system" | "userInputResult" }>;
function isRenderableMessageItem(item: DisplayItem): item is RenderableMessageItem {
return item.kind === "message" || item.kind === "system" || item.kind === "userInputResult";
}
export function messageRenderBlocks(context: MessageStreamContext): MessageRenderBlock[] {
const blocks: MessageRenderBlock[] = [];
@ -212,6 +218,9 @@ function renderDisplayItem(parent: HTMLElement, item: DisplayItem, context: Mess
renderToolResult(parent, item, context);
return;
}
if (!isRenderableMessageItem(item)) {
return;
}
const messageEl = parent.createDiv({ cls: messageClass(item) });
applyExecutionStateClass(messageEl, executionState(item));
const role = messageEl.createDiv({ cls: "codex-panel__message-role" });

View file

@ -107,12 +107,16 @@ export function toolbarSignature(model: ToolbarViewModel): string {
openPanel: model.openPanel,
threads: model.threads.map(
(thread) =>
`${thread.threadId}:${thread.title}:${String(thread.selected)}:${String(thread.disabled)}:${String(thread.canArchive)}:${thread.rename?.draft ?? ""}:${
String(thread.rename?.generating ?? false)
}`,
`${thread.threadId}:${thread.title}:${String(thread.selected)}:${String(thread.disabled)}:${String(thread.canArchive)}:${thread.rename?.draft ?? ""}:${String(
thread.rename?.generating ?? false,
)}`,
),
modelChoices: model.modelChoices.map(
(choice) => `${choice.label}:${String(choice.selected)}:${String(choice.disabled)}:${choice.meta ?? ""}`,
),
effortChoices: model.effortChoices.map(
(choice) => `${choice.label}:${String(choice.selected)}:${String(choice.disabled)}:${choice.meta ?? ""}`,
),
modelChoices: model.modelChoices.map((choice) => `${choice.label}:${String(choice.selected)}:${String(choice.disabled)}:${choice.meta ?? ""}`),
effortChoices: model.effortChoices.map((choice) => `${choice.label}:${String(choice.selected)}:${String(choice.disabled)}:${choice.meta ?? ""}`),
connectLabel: model.connectLabel,
diagnostics: model.diagnostics.map((section) => ({
title: section.title,

View file

@ -95,47 +95,53 @@ describe("approval model", () => {
});
it("shows approval reasons first in pending request summaries", () => {
const command = expectPresent(toPendingApproval({
id: 20,
method: "item/commandExecution/requestApproval",
params: {
command: "npm run build",
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "command",
startedAtMs: 1,
reason: "Needs unsandboxed access",
commandActions: [],
proposedExecpolicyAmendment: null,
proposedNetworkPolicyAmendments: [],
},
}));
const fileChange = expectPresent(toPendingApproval({
id: 21,
method: "item/fileChange/requestApproval",
params: {
threadId: "thread",
turnId: "turn",
itemId: "file",
startedAtMs: 1,
reason: "Write outside workspace",
grantRoot: "/tmp/project",
},
}));
const permissions = expectPresent(toPendingApproval({
id: 22,
method: "item/permissions/requestApproval",
params: {
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "permissions",
startedAtMs: 1,
reason: "Need network",
permissions: { network: { enabled: true }, fileSystem: null },
},
}));
const command = expectPresent(
toPendingApproval({
id: 20,
method: "item/commandExecution/requestApproval",
params: {
command: "npm run build",
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "command",
startedAtMs: 1,
reason: "Needs unsandboxed access",
commandActions: [],
proposedExecpolicyAmendment: null,
proposedNetworkPolicyAmendments: [],
},
}),
);
const fileChange = expectPresent(
toPendingApproval({
id: 21,
method: "item/fileChange/requestApproval",
params: {
threadId: "thread",
turnId: "turn",
itemId: "file",
startedAtMs: 1,
reason: "Write outside workspace",
grantRoot: "/tmp/project",
},
}),
);
const permissions = expectPresent(
toPendingApproval({
id: 22,
method: "item/permissions/requestApproval",
params: {
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "permissions",
startedAtMs: 1,
reason: "Need network",
permissions: { network: { enabled: true }, fileSystem: null },
},
}),
);
expect(approvalSummary(command)).toBe("Needs unsandboxed access\nnpm run build");
expect(approvalSummary(fileChange)).toBe("Write outside workspace\ngrant root: /tmp/project");
@ -143,19 +149,21 @@ describe("approval model", () => {
});
it("keeps approval details semantic and omits raw payloads", () => {
const approval = expectPresent(toPendingApproval({
id: 23,
method: "item/permissions/requestApproval",
params: {
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "permissions",
startedAtMs: 1,
reason: "Need network",
permissions: { network: { enabled: true }, fileSystem: null },
},
}));
const approval = expectPresent(
toPendingApproval({
id: 23,
method: "item/permissions/requestApproval",
params: {
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "permissions",
startedAtMs: 1,
reason: "Need network",
permissions: { network: { enabled: true }, fileSystem: null },
},
}),
);
expect(approvalDetails(approval)).toEqual([
{ key: "reason", value: "Need network" },
@ -165,22 +173,24 @@ describe("approval model", () => {
});
it("omits empty approval detail rows", () => {
const approval = expectPresent(toPendingApproval({
id: 24,
method: "item/commandExecution/requestApproval",
params: {
command: "npm test",
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "command",
startedAtMs: 1,
reason: null,
commandActions: [],
proposedExecpolicyAmendment: null,
proposedNetworkPolicyAmendments: [],
},
}));
const approval = expectPresent(
toPendingApproval({
id: 24,
method: "item/commandExecution/requestApproval",
params: {
command: "npm test",
cwd: "/tmp/project",
threadId: "thread",
turnId: "turn",
itemId: "command",
startedAtMs: 1,
reason: null,
commandActions: [],
proposedExecpolicyAmendment: null,
proposedNetworkPolicyAmendments: [],
},
}),
);
expect(approvalDetails(approval)).toEqual([
{ key: "command", value: "npm test" },

View file

@ -310,6 +310,12 @@ function ensureElementHelpers(): void {
};
}
function nodeConstructor(): typeof Node {
const defaultView = document.defaultView;
if (defaultView === null) throw new Error("Expected document.defaultView to install Obsidian DOM helpers.");
return (globalThis as typeof globalThis & { Node?: typeof Node }).Node ?? defaultView.Node;
}
function applyOptions(element: HTMLElement, options: ElementOptions): void {
if (Array.isArray(options.cls)) {
element.classList.add(...options.cls.filter(Boolean));

View file

@ -40,25 +40,27 @@ describe("user input model", () => {
});
it("signs pending request content and drafts deterministically", () => {
const input = expectPresent(toPendingUserInput({
id: 7,
method: "item/tool/requestUserInput",
params: {
threadId: "thread",
turnId: "turn",
itemId: "item",
questions: [
{
id: "direction",
header: "Direction",
question: "Which way?",
isOther: true,
isSecret: false,
options: [{ label: "Recommended", description: "Use the default path" }],
},
],
},
}));
const input = expectPresent(
toPendingUserInput({
id: 7,
method: "item/tool/requestUserInput",
params: {
threadId: "thread",
turnId: "turn",
itemId: "item",
questions: [
{
id: "direction",
header: "Direction",
question: "Which way?",
isOther: true,
isSecret: false,
options: [{ label: "Recommended", description: "Use the default path" }],
},
],
},
}),
);
const drafts = new Map([
["z", "last"],
["a", "first"],