diff --git a/apps/server/src/provider/ClaudeModelCatalog.test.ts b/apps/server/src/provider/ClaudeModelCatalog.test.ts index d5c9d8f53d1e..3aa8d2abfcd0 100644 --- a/apps/server/src/provider/ClaudeModelCatalog.test.ts +++ b/apps/server/src/provider/ClaudeModelCatalog.test.ts @@ -5,8 +5,10 @@ import { hasValidClaudeManifestAdapters } from "./ClaudeModelManifest.ts"; import type { ModelManifestData } from "./ModelManifest.ts"; import { formatClaudeVersionUpgradeMessage, + getClaudeCatalogModelCapabilities, normalizeClaudeCatalogEffort, resolveClaudeCatalogApiModelId, + resolveClaudeCatalogContextWindowTokens, resolveClaudeCatalogEffort, resolveClaudeModelCatalog, resolveClaudeModelsForVersion, @@ -80,7 +82,7 @@ describe("Claude model catalog", () => { ); }); - it("resolves aliases and declarative adapter mappings", () => { + it("resolves aliases without exposing provider-managed effort or context options", () => { const base = manifest(); const input: ModelManifestData = { ...base, @@ -107,12 +109,24 @@ describe("Claude model catalog", () => { "claude-synthetic-next", ); assert.strictEqual(normalizeClaudeCatalogEffort(catalog, "extreme", "synthetic"), "high"); + assert.deepStrictEqual( + getClaudeCatalogModelCapabilities(catalog, "synthetic").optionDescriptors, + [], + ); + assert.strictEqual(resolveClaudeCatalogEffort(catalog, "synthetic", "extreme"), undefined); assert.strictEqual( resolveClaudeCatalogApiModelId(catalog, { instanceId: ProviderInstanceId.make("claudeAgent"), model: "synthetic", }), - "claude-synthetic-next[large]", + "claude-synthetic-next", + ); + assert.strictEqual( + resolveClaudeCatalogContextWindowTokens(catalog, { + instanceId: ProviderInstanceId.make("claudeAgent"), + model: "synthetic", + }), + undefined, ); }); @@ -137,7 +151,7 @@ describe("Claude model catalog", () => { assert.isFalse(hasValidClaudeManifestAdapters(malformed)); }); - it("appends custom models with their own descriptors and keeps bare slugs opaque", () => { + it("hides provider-managed options on custom models and keeps bare slugs opaque", () => { const catalog = scopeClaudeModelCatalog(resolveClaudeModelCatalog(manifest()), [ "synthetic", { @@ -163,15 +177,13 @@ describe("Claude model catalog", () => { assert.strictEqual(resolveClaudeModelSlug(catalog, "synthetic"), "synthetic"); assert.strictEqual(resolveClaudeCatalogEffort(catalog, "synthetic", "extreme"), undefined); - // The entry with descriptors resolves user-defined effort ids and passes - // them through untouched (no effortMap, no model suffix). assert.strictEqual( resolveClaudeCatalogEffort(catalog, "claude-custom-tuned", "brutal"), - "brutal", + undefined, ); assert.strictEqual( resolveClaudeCatalogEffort(catalog, "claude-custom-tuned", "bogus"), - "gentle", + undefined, ); assert.strictEqual( normalizeClaudeCatalogEffort(catalog, "brutal", "claude-custom-tuned"), diff --git a/apps/server/src/provider/ClaudeModelCatalog.ts b/apps/server/src/provider/ClaudeModelCatalog.ts index e601e9dc9392..6203741a6af8 100644 --- a/apps/server/src/provider/ClaudeModelCatalog.ts +++ b/apps/server/src/provider/ClaudeModelCatalog.ts @@ -29,6 +29,18 @@ import { const CLAUDE = ProviderDriverKind.make("claudeAgent"); const EMPTY_CAPABILITIES: ModelCapabilities = { optionDescriptors: [] }; +function useProviderConfiguredOptions(model: ServerProviderModel): ServerProviderModel { + const descriptors = model.capabilities?.optionDescriptors; + if (!descriptors) return model; + return { + ...model, + capabilities: { + ...model.capabilities, + optionDescriptors: descriptors.filter(({ id }) => id !== "effort" && id !== "contextWindow"), + }, + }; +} + export interface ClaudeCatalogModel { readonly model: ServerProviderModel; readonly runtime: ClaudeCodeProfile; @@ -49,7 +61,7 @@ function tryResolveClaudeModelCatalog(manifest: ModelManifestData): ClaudeModelC const adapter = decodeClaudeModelAdapter(entry.adapter ?? {}); if (Option.isNone(profile) || Option.isNone(adapter)) return null; models.push({ - model: entry.model, + model: useProviderConfiguredOptions(entry.model), runtime: profile.value.claudeCode ?? {}, compatibility: adapter.value.claudeCode ?? {}, }); @@ -75,8 +87,8 @@ export const BUNDLED_CLAUDE_MODEL_CATALOG = resolveClaudeModelCatalog(BUNDLED_MO * Scope the catalog to one instance's settings: custom model slugs stay opaque * (a built-in alias they shadow is dropped, canonical slugs and capabilities * are preserved), and custom entries that declare their own capabilities are - * appended so the adapter resolves effort / fast mode / thinking against the - * user's descriptors instead of the empty default. Custom entries carry no + * appended so the adapter resolves fast mode / thinking against the user's + * descriptors instead of the empty default. Custom entries carry no * runtime profile, so option values pass through to Claude Code verbatim. */ export function scopeClaudeModelCatalog( @@ -104,12 +116,12 @@ export function scopeClaudeModelCatalog( for (const entry of customEntries) { if (!entry.capabilities || builtInSlugs.has(entry.slug)) continue; customCatalogModels.push({ - model: { + model: useProviderConfiguredOptions({ slug: entry.slug, name: entry.name, isCustom: true, capabilities: entry.capabilities, - }, + }), runtime: {}, compatibility: {}, }); diff --git a/apps/server/src/provider/CodexDeveloperInstructions.ts b/apps/server/src/provider/CodexDeveloperInstructions.ts index 6a7fee351bce..22010c3c9d89 100644 --- a/apps/server/src/provider/CodexDeveloperInstructions.ts +++ b/apps/server/src/provider/CodexDeveloperInstructions.ts @@ -194,7 +194,7 @@ ${browserToolInstructions(browserToolsAvailable)} export interface CodexRuntimeInfo { readonly model: string; - readonly reasoningEffort: string; + readonly reasoningEffort?: string; } export function buildCodexDeveloperInstructions( diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts index ca596e6501cf..b5ac965309c6 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts @@ -39,6 +39,7 @@ import * as TestClock from "effect/testing/TestClock"; import { attachmentRelativePath } from "../../attachmentStore.ts"; import { ServerConfig } from "../../config.ts"; import { ServerSettingsService } from "../../serverSettings.ts"; +import { BUNDLED_CLAUDE_MODEL_CATALOG, type ClaudeModelCatalog } from "../ClaudeModelCatalog.ts"; import { SYNTHETIC_CLAUDE_CAPABLE_MODEL, SYNTHETIC_CLAUDE_COLLIDING_ALIAS, @@ -167,6 +168,7 @@ function makeHarness(config?: { readonly cwd?: string; readonly baseDir?: string; readonly claudeConfig?: Partial; + readonly modelCatalog?: ClaudeModelCatalog; readonly instanceId?: ProviderInstanceId; readonly scopedLimitNames?: ClaudeAdapterLiveOptions["scopedLimitNames"]; readonly environment?: ClaudeAdapterLiveOptions["environment"]; @@ -186,7 +188,7 @@ function makeHarness(config?: { ...(config?.environment ? { environment: config.environment } : {}), ...(config?.instanceId ? { instanceId: config.instanceId } : {}), ...(config?.scopedLimitNames ? { scopedLimitNames: config.scopedLimitNames } : {}), - modelCatalog: Effect.succeed(SYNTHETIC_CLAUDE_MODEL_CATALOG), + modelCatalog: Effect.succeed(config?.modelCatalog ?? SYNTHETIC_CLAUDE_MODEL_CATALOG), ...(config?.getSessionMessages ? { getSessionMessages: config.getSessionMessages } : {}), ...(config?.forkSession ? { forkSession: config.forkSession } : {}), createQuery: (input) => { @@ -590,6 +592,37 @@ describe("ClaudeAdapterLive", () => { ); }); + it.effect("uses Claude settings for effort and context despite saved T3 choices", () => { + const harness = makeHarness({ + modelCatalog: BUNDLED_CLAUDE_MODEL_CATALOG, + environment: { ...process.env, CLAUDE_CODE_DISABLE_1M_CONTEXT: "1" }, + }); + return Effect.gen(function* () { + const adapter = yield* ClaudeAdapter; + yield* adapter.startSession({ + threadId: THREAD_ID, + provider: ProviderDriverKind.make("claudeAgent"), + modelSelection: createModelSelection( + ProviderInstanceId.make("claudeAgent"), + "claude-opus-5-5", + [ + { id: "effort", value: "max" }, + { id: "contextWindow", value: "1m" }, + ], + ), + runtimeMode: "full-access", + }); + + const options = harness.getLastCreateQueryInput()?.options; + assert.equal(options?.model, "claude-opus-5-5"); + assert.equal(options?.effort, undefined); + assert.equal(options?.env?.CLAUDE_CODE_DISABLE_1M_CONTEXT, "1"); + }).pipe( + Effect.provideService(Random.Random, makeDeterministicRandomService()), + Effect.provide(harness.layer), + ); + }); + it.effect("runs Claude SDK sessions with the configured CLAUDE_CONFIG_DIR", () => { const harness = makeHarness({ claudeConfig: { homePath: "~/.claude-work" } }); return Effect.gen(function* () { diff --git a/apps/server/src/provider/Layers/CodexAdapter.test.ts b/apps/server/src/provider/Layers/CodexAdapter.test.ts index 9f464bdaa177..19f15b7c1635 100644 --- a/apps/server/src/provider/Layers/CodexAdapter.test.ts +++ b/apps/server/src/provider/Layers/CodexAdapter.test.ts @@ -415,7 +415,7 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { }), ); - it.effect("maps codex model options before sending a turn", () => + it.effect("keeps Codex effort in config while mapping service tier", () => Effect.gen(function* () { const adapter = yield* CodexAdapter; yield* adapter.startSession({ @@ -442,7 +442,6 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { NodeAssert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], { input: "hello", model: "gpt-5.3-codex", - effort: "high", serviceTier: "priority", }); }), @@ -608,7 +607,6 @@ sessionErrorLayer("CodexAdapterLive session errors", (it) => { NodeAssert.deepStrictEqual(runtime.sendTurnImpl.mock.calls[0]?.[0], { input: "hello", model: "gpt-5.3-codex", - effort: "high", serviceTier: "flex", }); }).pipe(Effect.provide(customLayer)); diff --git a/apps/server/src/provider/Layers/CodexAdapter.ts b/apps/server/src/provider/Layers/CodexAdapter.ts index 0ecc9693ab04..551bd6e1b8e4 100644 --- a/apps/server/src/provider/Layers/CodexAdapter.ts +++ b/apps/server/src/provider/Layers/CodexAdapter.ts @@ -44,7 +44,6 @@ import { ChildProcessSpawner } from "effect/unstable/process"; import * as CodexErrors from "effect-codex-app-server/errors"; import * as EffectCodexSchema from "effect-codex-app-server/schema"; -import { getModelSelectionStringOptionValue } from "@t3tools/shared/model"; import { getCodexServiceTierOptionValue } from "../../codexModelOptions.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; @@ -2527,10 +2526,6 @@ export const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* ( ); const session = yield* requireSession(input.threadId); - const reasoningEffort = - input.modelSelection?.instanceId === boundInstanceId - ? getModelSelectionStringOptionValue(input.modelSelection, "reasoningEffort") - : undefined; const serviceTier = input.modelSelection?.instanceId === boundInstanceId ? getCodexServiceTierOptionValue(input.modelSelection) @@ -2541,11 +2536,6 @@ export const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* ( ...(input.modelSelection?.instanceId === boundInstanceId ? { model: input.modelSelection.model } : {}), - ...(reasoningEffort - ? { - effort: reasoningEffort as EffectCodexSchema.V2TurnStartParams__ReasoningEffort, - } - : {}), ...(serviceTier ? { serviceTier } : {}), ...(input.interactionMode !== undefined ? { interactionMode: input.interactionMode } : {}), ...(codexAttachments.length > 0 ? { attachments: codexAttachments } : {}), diff --git a/apps/server/src/provider/Layers/CodexCollabRuntime.integration.test.ts b/apps/server/src/provider/Layers/CodexCollabRuntime.integration.test.ts index 2a9fb56c186a..cdc3b4636947 100644 --- a/apps/server/src/provider/Layers/CodexCollabRuntime.integration.test.ts +++ b/apps/server/src/provider/Layers/CodexCollabRuntime.integration.test.ts @@ -14,7 +14,12 @@ import * as NodePath from "node:path"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; -import { type ProviderApprovalDecision, type ProviderEvent, ThreadId } from "@t3tools/contracts"; +import { + type ProviderApprovalDecision, + type ProviderEvent, + ThreadId, + TurnId, +} from "@t3tools/contracts"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; @@ -741,7 +746,7 @@ describe("CodexSessionRuntime collab integration", () => { yield* runtime.start(); yield* runtime.sendTurn({ input: "keep working" }); yield* runtime.sendTurn({ input: "queued follow-up" }); - yield* runtime.interruptTurn(); + yield* runtime.interruptTurn(TurnId.make(queuedTurnId)); const interrupts = NodeFS.readFileSync(interruptsPath, "utf8") .trim() diff --git a/apps/server/src/provider/Layers/CodexProvider.test.ts b/apps/server/src/provider/Layers/CodexProvider.test.ts index 0c7a40d9bd9e..f1c4266dfa10 100644 --- a/apps/server/src/provider/Layers/CodexProvider.test.ts +++ b/apps/server/src/provider/Layers/CodexProvider.test.ts @@ -2,7 +2,7 @@ import { assert, it } from "@effect/vitest"; import { applyPreferredCodexDefaultModel, mapCodexModelCapabilities } from "./CodexProvider.ts"; -it("maps current Codex model capability fields", () => { +it("exposes service tiers while leaving reasoning effort to Codex config", () => { const capabilities = mapCodexModelCapabilities({ additionalSpeedTiers: [], defaultReasoningEffort: "super-high", @@ -34,13 +34,6 @@ it("maps current Codex model capability fields", () => { }); assert.deepStrictEqual(capabilities.optionDescriptors, [ - { - id: "reasoningEffort", - label: "Reasoning", - type: "select", - options: [{ id: "super-high", label: "super-high", isDefault: true }], - currentValue: "super-high", - }, { id: "serviceTier", label: "Service Tier", diff --git a/apps/server/src/provider/Layers/CodexProvider.ts b/apps/server/src/provider/Layers/CodexProvider.ts index cdf40f73b1bd..6fa204c41c28 100644 --- a/apps/server/src/provider/Layers/CodexProvider.ts +++ b/apps/server/src/provider/Layers/CodexProvider.ts @@ -77,23 +77,8 @@ export interface CodexAppServerProviderSnapshot { readonly skills: ReadonlyArray; } -const REASONING_EFFORT_LABELS: Readonly> = { - none: "None", - minimal: "Minimal", - low: "Low", - medium: "Medium", - high: "High", - xhigh: "Extra High", - max: "Max", - ultra: "Ultra", -}; - const DEFAULT_SERVICE_TIER_ID = "default"; -function reasoningEffortLabel(reasoningEffort: string): string { - return REASONING_EFFORT_LABELS[reasoningEffort] ?? reasoningEffort; -} - function codexAccountAuthLabel(account: CodexSchema.V2GetAccountResponse["account"]) { if (!account) return undefined; if (account.type === "apiKey") return "OpenAI API Key"; @@ -145,20 +130,6 @@ function codexAccountEmail(account: CodexSchema.V2GetAccountResponse["account"]) export function mapCodexModelCapabilities( model: CodexSchema.V2ModelListResponse__Model, ): ModelCapabilities { - const reasoningOptions = model.supportedReasoningEfforts.map(({ reasoningEffort }) => - reasoningEffort === - (codexModelFamily(model.model) === "gpt-6-astra" ? "medium" : model.defaultReasoningEffort) - ? { - id: reasoningEffort, - label: reasoningEffortLabel(reasoningEffort), - isDefault: true, - } - : { - id: reasoningEffort, - label: reasoningEffortLabel(reasoningEffort), - }, - ); - const defaultReasoning = reasoningOptions.find((option) => option.isDefault)?.id; const serviceTiers = model.serviceTiers && model.serviceTiers.length > 0 ? model.serviceTiers @@ -175,15 +146,6 @@ export function mapCodexModelCapabilities( const defaultServiceTier = catalogDefaultServiceTier ?? DEFAULT_SERVICE_TIER_ID; const optionDescriptors: ProviderOptionDescriptor[] = []; - if (reasoningOptions.length > 0) { - optionDescriptors.push({ - id: "reasoningEffort", - label: "Reasoning", - type: "select", - options: reasoningOptions, - ...(defaultReasoning ? { currentValue: defaultReasoning } : {}), - }); - } if (serviceTiers.length > 0) { optionDescriptors.push({ id: "serviceTier", diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts index ec113ab7c521..75675494f147 100644 --- a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts +++ b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts @@ -277,17 +277,15 @@ describe("buildTurnStartParams", () => { mode: "default", settings: { model: "gpt-5.3-codex", - reasoning_effort: "medium", developer_instructions: buildCodexDeveloperInstructions("default", { model: "gpt-5.3-codex", - reasoningEffort: "medium", }), }, }, }); }); - it("reports the same fallback model and effort in settings and instructions", () => { + it("leaves effort to Codex config when no override is given", () => { const params = Effect.runSync( buildTurnStartParams({ threadId: "provider-thread-1", @@ -299,8 +297,9 @@ describe("buildTurnStartParams", () => { const settings = params.collaborationMode?.settings; NodeAssert.equal(settings?.model, DEFAULT_MODEL); - NodeAssert.equal(settings?.reasoning_effort, "medium"); - NodeAssert.ok(settings?.developer_instructions?.includes(`as ${DEFAULT_MODEL} with medium`)); + NodeAssert.equal(settings?.reasoning_effort, undefined); + NodeAssert.ok(settings?.developer_instructions?.includes(`as ${DEFAULT_MODEL}`)); + NodeAssert.ok(!settings?.developer_instructions?.includes("reasoning effort")); }); it.effect("routes approvals to the auto reviewer in auto mode", () => diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.ts index 674d23327b65..120164c3e2f5 100644 --- a/apps/server/src/provider/Layers/CodexSessionRuntime.ts +++ b/apps/server/src/provider/Layers/CodexSessionRuntime.ts @@ -590,15 +590,14 @@ function buildCodexCollaborationMode(input: { return undefined; } const model = normalizeCodexModelSlug(input.model) ?? DEFAULT_MODEL; - const reasoningEffort = input.effort ?? "medium"; return { mode: input.interactionMode, settings: { model, - reasoning_effort: reasoningEffort, + ...(input.effort ? { reasoning_effort: input.effort } : {}), developer_instructions: buildCodexDeveloperInstructions( input.interactionMode, - { model, reasoningEffort }, + { model, ...(input.effort ? { reasoningEffort: input.effort } : {}) }, input.browserToolsAvailable ?? true, ), }, @@ -2592,7 +2591,7 @@ export const makeCodexSessionRuntime = ( .pipe(Effect.timeoutOption("3 seconds"), Effect.ignore), { concurrency: 8, discard: true }, ).pipe(Effect.timeoutOption("10 seconds"), Effect.ignore); - const effectiveTurnId = turnId ?? session.activeTurnId; + const effectiveTurnId = session.activeTurnId ?? turnId; if (!effectiveTurnId) { return; } diff --git a/apps/server/src/textGeneration/CodexTextGeneration.test.ts b/apps/server/src/textGeneration/CodexTextGeneration.test.ts index 91c9cb94b5d5..5142e7cfb7e5 100644 --- a/apps/server/src/textGeneration/CodexTextGeneration.test.ts +++ b/apps/server/src/textGeneration/CodexTextGeneration.test.ts @@ -32,7 +32,6 @@ interface FakeCodexInput { stderr?: string; requireImage?: boolean; requireServiceTier?: string; - requireReasoningEffort?: string; forbidReasoningEffort?: boolean; requireArg?: string; forbidArg?: string; @@ -48,7 +47,6 @@ function makeFakeCodexBinary(dir: string, input: FakeCodexInput) { const check = JSON.stringify({ requireImage: input.requireImage ?? false, requireServiceTier: input.requireServiceTier ?? null, - requireReasoningEffort: input.requireReasoningEffort ?? null, forbidReasoningEffort: input.forbidReasoningEffort ?? false, requireArg: input.requireArg ?? null, forbidArg: input.forbidArg ?? null, @@ -106,12 +104,6 @@ function makeFakeCodexBinary(dir: string, input: FakeCodexInput) { ") {", ' fail("unexpected service tier config: " + seenServiceTier, 5);', "}", - "if (", - " check.requireReasoningEffort !== null &&", - ' seenReasoningEffort !== `model_reasoning_effort="${check.requireReasoningEffort}"`', - ") {", - ' fail("unexpected reasoning effort config: " + seenReasoningEffort, 6);', - "}", "if (check.forbidReasoningEffort && seenReasoningEffort.length > 0) {", ' fail("reasoning effort config should be omitted: " + seenReasoningEffort, 7);', "}", @@ -209,31 +201,29 @@ it.layer(CodexTextGenerationTestLayer)("CodexTextGeneration", (it) => { ), ); - it.effect( - "forwards codex service tier and non-default reasoning effort into codex exec config", - () => - withFakeCodexEnv( - { - output: JSON.stringify({ - subject: "Add important change", - body: "", - }), - requireServiceTier: "priority", - requireReasoningEffort: "xhigh", - stdinMustNotContain: "branch must be a short semantic git branch fragment", - }, - (textGeneration) => - textGeneration.generateCommitMessage({ - cwd: process.cwd(), - branch: "feature/codex-effect", - stagedSummary: "M README.md", - stagedPatch: "diff --git a/README.md b/README.md", - modelSelection: createModelSelection(ProviderInstanceId.make("codex"), "gpt-5.4", [ - { id: "reasoningEffort", value: "xhigh" }, - { id: "serviceTier", value: "priority" }, - ]), - }), - ), + it.effect("forwards Codex service tier but leaves effort to config", () => + withFakeCodexEnv( + { + output: JSON.stringify({ + subject: "Add important change", + body: "", + }), + requireServiceTier: "priority", + forbidReasoningEffort: true, + stdinMustNotContain: "branch must be a short semantic git branch fragment", + }, + (textGeneration) => + textGeneration.generateCommitMessage({ + cwd: process.cwd(), + branch: "feature/codex-effect", + stagedSummary: "M README.md", + stagedPatch: "diff --git a/README.md b/README.md", + modelSelection: createModelSelection(ProviderInstanceId.make("codex"), "gpt-5.4", [ + { id: "reasoningEffort", value: "xhigh" }, + { id: "serviceTier", value: "priority" }, + ]), + }), + ), ); it.effect("passes exec-safe launch args into codex exec", () => @@ -281,14 +271,14 @@ it.layer(CodexTextGenerationTestLayer)("CodexTextGeneration", (it) => { ), ); - it.effect("defaults git text generation codex effort to low", () => + it.effect("leaves Git text generation effort to Codex config", () => withFakeCodexEnv( { output: JSON.stringify({ subject: "Add important change", body: "", }), - requireReasoningEffort: "low", + forbidReasoningEffort: true, }, (textGeneration) => textGeneration.generateCommitMessage({ diff --git a/apps/server/src/textGeneration/CodexTextGeneration.ts b/apps/server/src/textGeneration/CodexTextGeneration.ts index 4c9ac59d8422..5f3991b371ad 100644 --- a/apps/server/src/textGeneration/CodexTextGeneration.ts +++ b/apps/server/src/textGeneration/CodexTextGeneration.ts @@ -9,7 +9,6 @@ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import { type CodexSettings, - DEFAULT_TEXT_GENERATION_REASONING_EFFORT, type ModelSelection, type ServerProviderModel, TextGenerationError, @@ -35,7 +34,7 @@ import { sanitizeThreadTitle, toJsonSchemaObject, } from "./TextGenerationUtils.ts"; -import { codexModelFamily, getModelSelectionStringOptionValue } from "@t3tools/shared/model"; +import { codexModelFamily } from "@t3tools/shared/model"; import { getCodexServiceTierOptionValue } from "../codexModelOptions.ts"; const CODEX_TIMEOUT_MS = 180_000; @@ -189,9 +188,6 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func )?.slug ?? requestedModel; const launchArgs = resolveCodexLaunchArgs(codexConfig.launchArgs, resolvedEnvironment); - const reasoningEffort = - getModelSelectionStringOptionValue(modelSelection, "reasoningEffort") ?? - DEFAULT_TEXT_GENERATION_REASONING_EFFORT; const serviceTier = getCodexServiceTierOptionValue(modelSelection); const spawnCommand = yield* resolveSpawnCommand( codexConfig.binaryPath || "codex", @@ -204,8 +200,6 @@ export const makeCodexTextGeneration = Effect.fn("makeCodexTextGeneration")(func "read-only", "--model", model, - "--config", - `model_reasoning_effort="${reasoningEffort}"`, ...(serviceTier ? ["--config", `service_tier="${serviceTier}"`] : []), "--output-schema", schemaPath, diff --git a/docs/user/composer.md b/docs/user/composer.md index 6e699d0669c1..ad2aa94fffea 100644 --- a/docs/user/composer.md +++ b/docs/user/composer.md @@ -69,7 +69,10 @@ T3 Code remembers your provider, model, and model options for new threads. A project's configured model takes precedence; resetting that project setting returns to the remembered selection. -Leaving reasoning level or service tier unset uses the provider's own configuration. +For Codex, set reasoning effort and context length in `~/.codex/config.toml`. For +Claude Code, use `~/.claude/settings.json` for effort and its `env` settings for +1M context support. T3 Code shows the context window reported by the provider. +Leaving service tier unset uses the provider's configuration. ## Quote an assistant response diff --git a/docs/user/keybindings.md b/docs/user/keybindings.md index ad1b157bbb75..7f827cc06694 100644 --- a/docs/user/keybindings.md +++ b/docs/user/keybindings.md @@ -15,7 +15,7 @@ use `mod+Shift+Enter` for the opposite action. In a new thread, `mod+Enter` keep starting the thread in the background. Use `mod+shift+m` to choose a model and `mod+shift+h` to choose a host. -Use `mod+shift+e` for effort, `mod+shift+a` for access mode, `mod+shift+x` for the +Use `mod+shift+e` for available model traits, `mod+shift+a` for access mode, `mod+shift+x` for the workspace, and `mod+shift+g` for the Git branch. The workspace menu includes the current checkout, a new worktree, and the previous worktree when available. Use `mod+shift+l` to reuse the previous worktree directly. diff --git a/docs/user/providers-claude.md b/docs/user/providers-claude.md index cce194dbd2b4..dc748ab7873b 100644 --- a/docs/user/providers-claude.md +++ b/docs/user/providers-claude.md @@ -93,7 +93,7 @@ list, open that Claude instance in **Settings > Providers** and add its full mod ID with **Add custom model**. Then select it in the chat model picker. `ANTHROPIC_DEFAULT_*_MODEL` variables map Claude Code aliases such as `sonnet`; they do not replace the explicit model ID selected in T3 Code. Custom models may have -fewer effort, thinking, or context controls than built-in models. +fewer T3 Code controls than built-in models. Verify the model used in OpenRouter's activity dashboard. For current compatibility requirements, use the diff --git a/packages/contracts/src/model.ts b/packages/contracts/src/model.ts index 31e5f9d63305..b480e343e373 100644 --- a/packages/contracts/src/model.ts +++ b/packages/contracts/src/model.ts @@ -164,7 +164,6 @@ export const PREFERRED_DEFAULT_CODEX_MODELS: ReadonlyArray = [ export const DEFAULT_TEXT_GENERATION_MODEL = "gpt-6-luna"; /** Keep the official Antigravity session's current model. Never send this ID to ACP. */ export const ANTIGRAVITY_DEFAULT_MODEL = "antigravity-default"; -export const DEFAULT_TEXT_GENERATION_REASONING_EFFORT = "low"; export const DEFAULT_MODEL_BY_PROVIDER: Partial> = { [CODEX_DRIVER_KIND]: DEFAULT_MODEL, diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index 3e301201910c..95a67712e82c 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -17,7 +17,6 @@ import { KeybindingShortcut } from "./keybindings.ts"; import { CustomModelSetting, DEFAULT_TEXT_GENERATION_MODEL, - DEFAULT_TEXT_GENERATION_REASONING_EFFORT, ProviderOptionSelections, } from "./model.ts"; import { @@ -1227,12 +1226,7 @@ export const ServerSettings = Schema.Struct({ Effect.succeed({ instanceId: ProviderInstanceId.make("codex"), model: DEFAULT_TEXT_GENERATION_MODEL, - options: [ - { - id: "reasoningEffort", - value: DEFAULT_TEXT_GENERATION_REASONING_EFFORT, - }, - ], + options: [], }), ), ),