Diff
1diff --git a/README.md b/README.md
2index 191e2394e4d44c4220ea6d2d5aa88a8299660edf..3184a8c26fd2a27daf4ee187960acf690196a22c 100644
3--- a/README.md
4+++ b/README.md
5@@ -40,11 +40,14 @@ The default reviewer is OpenAI with model `gpt-5.4-nano`. `OPENAI_SMALL_FAST_MOD
6 {
7 "reviewer": {
8 "kind": "openai",
9- "model": "gpt-5.4-nano"
10+ "model": "gpt-5.6-luna",
11+ "reasoningEffort": "low"
12 }
13 }
14 ```
15
16+OpenAI reviewers accept `reasoningEffort`: `none`, `low`, `medium`, `high`, `xhigh`, or `max`. Support depends on the selected model.
17+
18 Use local llama.cpp:
19
20 ```json
21diff --git a/src/core/config.ts b/src/core/config.ts
22index 11df1dd0c872430571e9ab6a3a6f586500e0ef54..b7437a880941e5048add7bd7fc402850f518de99 100644
23--- a/src/core/config.ts
24+++ b/src/core/config.ts
25@@ -1,4 +1,4 @@
26-import type { LlamaReasoningFormat, ReviewerConfig } from "./types"
27+import type { LlamaReasoningFormat, OpenAIReasoningEffort, ReviewerConfig } from "./types"
28
29 const DEFAULT_OPENAI_MODEL = "gpt-5.4-nano"
30 const DEFAULT_LLAMA_BASE_URL = "http://127.0.0.1:9931"
31@@ -29,6 +29,14 @@ function llamaReasoningFormat(value: unknown): LlamaReasoningFormat {
32 throw new Error('reviewer.reasoningFormat must be "none", "deepseek", or "deepseek-legacy"')
33 }
34
35+function openAIReasoningEffort(value: unknown): OpenAIReasoningEffort | undefined {
36+ if (value === undefined) return undefined
37+ if (value === "none" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max") {
38+ return value
39+ }
40+ throw new Error('reviewer.reasoningEffort must be "none", "low", "medium", "high", "xhigh", or "max"')
41+}
42+
43 function booleanValue(value: unknown, field: string, fallback: boolean): boolean {
44 if (value === undefined) return fallback
45 if (typeof value === "boolean") return value
46@@ -39,6 +47,7 @@ function openAIConfig(value: Record<string, unknown>): ReviewerConfig {
47 return {
48 kind: "openai",
49 model: stringValue(value.model) ?? process.env.OPENAI_SMALL_FAST_MODEL ?? DEFAULT_OPENAI_MODEL,
50+ reasoningEffort: openAIReasoningEffort(value.reasoningEffort),
51 }
52 }
53
54diff --git a/src/core/providers.ts b/src/core/providers.ts
55index 63628af8619201321eb93ee45d6329ee8415746b..aecf623ff47a23d50dce4099355aaadbd37b7a76 100644
56--- a/src/core/providers.ts
57+++ b/src/core/providers.ts
58@@ -42,6 +42,7 @@ async function callOpenAI(
59 messages: ChatMessage[],
60 maxTokens: number,
61 apiKey?: string,
62+ reasoningEffort?: "none" | "low" | "medium" | "high" | "xhigh" | "max",
63 ): Promise<string> {
64 const response = await fetch(completionUrl(baseUrl), {
65 method: "POST",
66@@ -54,6 +55,7 @@ async function callOpenAI(
67 max_completion_tokens: maxTokens,
68 messages,
69 temperature: 0,
70+ ...(reasoningEffort && { reasoning_effort: reasoningEffort }),
71 }),
72 })
73 return responseText(response, "OpenAI")
74@@ -108,5 +110,12 @@ export async function callReviewer(
75 const apiKey = config.apiKeyEnv ? process.env[config.apiKeyEnv]?.trim() : undefined
76 return callOpenAI(config.baseUrl, config.model, messages, maxTokens, apiKey)
77 }
78- return callOpenAI("https://api.openai.com/v1", config.model, messages, maxTokens, resolveApiKey(sessionId))
79+ return callOpenAI(
80+ "https://api.openai.com/v1",
81+ config.model,
82+ messages,
83+ maxTokens,
84+ resolveApiKey(sessionId),
85+ config.reasoningEffort,
86+ )
87 }
88diff --git a/src/core/types.ts b/src/core/types.ts
89index 5d84d31b0ef779a8c584a61fee2e878fccae14c2..7acf2fe6c34f03073af6228d2e740b22c8548965 100644
90--- a/src/core/types.ts
91+++ b/src/core/types.ts
92@@ -64,9 +64,10 @@ export type PolicyReviewer = {
93 }
94
95 export type LlamaReasoningFormat = "none" | "deepseek" | "deepseek-legacy"
96+export type OpenAIReasoningEffort = "none" | "low" | "medium" | "high" | "xhigh" | "max"
97
98 export type ReviewerConfig =
99- | { kind: "openai"; model: string }
100+ | { kind: "openai"; model: string; reasoningEffort?: OpenAIReasoningEffort }
101 | {
102 kind: "llama.cpp"
103 baseUrl: string