From 831cb3f5d3cd16e2df50af44c05e8cae0747ae29 Mon Sep 17 00:00:00 2001 From: murashit Date: Sat, 23 May 2026 08:17:29 +0900 Subject: [PATCH] Project runtime config into typed accessors --- src/panel/view.ts | 19 ++-- src/runtime/config.ts | 118 +++++++++++++++++++++++++ src/runtime/state.ts | 108 +++++++--------------- src/runtime/view.ts | 63 ++++++------- tests/runtime/runtime-settings.test.ts | 13 +-- 5 files changed, 194 insertions(+), 127 deletions(-) create mode 100644 src/runtime/config.ts diff --git a/src/panel/view.ts b/src/panel/view.ts index c85ff35b..2a0fadbd 100644 --- a/src/panel/view.ts +++ b/src/panel/view.ts @@ -26,13 +26,11 @@ import { rollbackCandidateFromItems } from "./rollback"; import { contextSummary, effectiveConfigSections, rateLimitSummary } from "../runtime/view"; import { autoReviewActive, - configRecord, currentModel, currentReasoningEffort, currentServiceTier, defaultRuntimeOverride, requestedTurnRuntimeSettings, - resolvedConfigValue, resetRuntimeOverride, runtimeOverridePayload, runtimeOverrideLabel, @@ -42,6 +40,7 @@ import { supportedReasoningEfforts, type RuntimeSnapshot, } from "../runtime/state"; +import { readRuntimeConfig } from "../runtime/config"; import { sortedAvailableModels } from "../runtime/model"; import { compactContextLabel, modelOverrideMessage, reasoningEffortOverrideMessage } from "../runtime/settings"; import { executeSlashCommand as runSlashCommand, type SlashCommandExecutionResult } from "./slash-commands"; @@ -676,7 +675,7 @@ export class CodexPanelView extends ItemView { } private async toggleFastMode(): Promise { - const current = currentServiceTier(this.runtimeSnapshot(), configRecord(this.state.effectiveConfig)); + const current = currentServiceTier(this.runtimeSnapshot(), readRuntimeConfig(this.state.effectiveConfig)); const next: ServiceTier = current === "fast" ? "standard" : "fast"; this.state.requestedServiceTier = next; this.state.activeServiceTier = next; @@ -694,7 +693,7 @@ export class CodexPanelView extends ItemView { } private async toggleAutoReview(): Promise { - const next: ApprovalsReviewer = autoReviewActive(this.runtimeSnapshot(), configRecord(this.state.effectiveConfig)) + const next: ApprovalsReviewer = autoReviewActive(this.runtimeSnapshot(), readRuntimeConfig(this.state.effectiveConfig)) ? "user" : "auto_review"; this.state.requestedApprovalsReviewer = next; @@ -867,7 +866,7 @@ export class CodexPanelView extends ItemView { private toolbarViewModel(): ToolbarViewModel { const snapshot = this.runtimeSnapshot(); - const config = configRecord(this.state.effectiveConfig); + const config = readRuntimeConfig(this.state.effectiveConfig); const context = contextSummary(snapshot); const limit = rateLimitSummary(snapshot); const historyOpen = this.state.openDetails.has("history"); @@ -938,7 +937,7 @@ export class CodexPanelView extends ItemView { private modelToolbarChoices(): ToolbarChoice[] { const snapshot = this.runtimeSnapshot(); - const config = configRecord(this.state.effectiveConfig); + const config = readRuntimeConfig(this.state.effectiveConfig); const activeModel = currentModel(snapshot, config); const models = sortedAvailableModels(this.state.availableModels); const choices: ToolbarChoice[] = [ @@ -960,7 +959,7 @@ export class CodexPanelView extends ItemView { private effortToolbarChoices(): ToolbarChoice[] { const snapshot = this.runtimeSnapshot(); - const config = configRecord(this.state.effectiveConfig); + const config = readRuntimeConfig(this.state.effectiveConfig); const activeEffort = currentReasoningEffort(snapshot, config); return supportedReasoningEfforts(snapshot).map((effort) => ({ label: effort, @@ -1049,11 +1048,11 @@ export class CodexPanelView extends ItemView { private modelStatusLines(): string[] { const snapshot = this.runtimeSnapshot(); - const config = configRecord(this.state.effectiveConfig); + const config = readRuntimeConfig(this.state.effectiveConfig); return [ `Model: ${currentModel(snapshot, config) ?? "(from default)"}`, `Override: ${runtimeOverrideLabel(this.state.requestedModel)}`, - `Provider: ${statusValue(resolvedConfigValue(config, "model_provider"), "(from default)")}`, + `Provider: ${statusValue(config.modelProvider, "(from default)")}`, `Effort: ${currentReasoningEffort(snapshot, config) ?? "(from default)"}`, `Mode: ${this.collaborationModeLabel()}`, `Service tier: ${serviceTierLabel(snapshot, config)}`, @@ -1062,7 +1061,7 @@ export class CodexPanelView extends ItemView { private effortStatusLines(): string[] { const snapshot = this.runtimeSnapshot(); - const config = configRecord(this.state.effectiveConfig); + const config = readRuntimeConfig(this.state.effectiveConfig); return [ `Effort: ${currentReasoningEffort(snapshot, config) ?? "(from default)"}`, `Override: ${runtimeOverrideLabel(this.state.requestedReasoningEffort)}`, diff --git a/src/runtime/config.ts b/src/runtime/config.ts new file mode 100644 index 00000000..a76b4531 --- /dev/null +++ b/src/runtime/config.ts @@ -0,0 +1,118 @@ +import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; +import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse"; +import type { ApprovalsReviewer } from "../generated/app-server/v2/ApprovalsReviewer"; +import { parseServiceTier, type ServiceTier } from "../app-server/service-tier"; +import { isReasoningEffort } from "./model"; + +export interface RuntimeConfigProjection { + profile: string | null; + model: string | null; + modelProvider: unknown; + reasoningEffort: ReasoningEffort | null; + rawReasoningEffort: string | null; + reasoningSummary: unknown; + verbosity: unknown; + serviceTier: ServiceTier | null; + approvalsReviewer: ApprovalsReviewer | null; + approvalPolicy: unknown; + webSearch: unknown; + modelContextWindow: number | null; + autoCompactTokenLimit: number | null; + sandboxMode: unknown; + workspaceNetworkAccess: unknown; + writableRoots: unknown; + hooksEnabled: unknown; + applyPatchFreeformEnabled: unknown; + toolWebSearch: unknown; + apps: Record; +} + +export function readRuntimeConfig(effectiveConfig: ConfigReadResponse | null): RuntimeConfigProjection { + const config = configRecord(effectiveConfig); + const features = asRecord(config["features"]); + const tools = asRecord(resolvedConfigValue(config, "tools")); + const workspaceWrite = asRecord(config["sandbox_workspace_write"]); + const effort = resolvedConfigValue(config, "model_reasoning_effort"); + return { + profile: stringOrNull(config["profile"]), + model: nonEmptyStringOrNull(resolvedConfigValue(config, "model")), + modelProvider: resolvedConfigValue(config, "model_provider"), + reasoningEffort: isReasoningEffort(effort) ? effort : null, + rawReasoningEffort: nonEmptyStringOrNull(effort), + reasoningSummary: resolvedConfigValue(config, "model_reasoning_summary"), + verbosity: resolvedConfigValue(config, "model_verbosity"), + serviceTier: parseServiceTier(resolvedConfigValue(config, "service_tier")), + approvalsReviewer: approvalsReviewerOrNull(resolvedConfigValue(config, "approvals_reviewer")), + approvalPolicy: resolvedConfigValue(config, "approval_policy"), + webSearch: resolvedConfigValue(config, "web_search"), + modelContextWindow: numberOrNull(config["model_context_window"]), + autoCompactTokenLimit: numberOrNull(config["model_auto_compact_token_limit"]), + sandboxMode: config["sandbox_mode"], + workspaceNetworkAccess: workspaceWrite["network_access"], + writableRoots: workspaceWrite["writable_roots"], + hooksEnabled: features["hooks"], + applyPatchFreeformEnabled: features["apply_patch_freeform"], + toolWebSearch: tools["web_search"], + apps: asRecord(config["apps"]), + }; +} + +function configRecord(effectiveConfig: ConfigReadResponse | null): Record { + return fillMissingConfigValues(asRecord(effectiveConfig?.config), effectiveConfig?.layers ?? null); +} + +function selectedProfileConfig(config: Record): Record { + const profile = config["profile"]; + const profileName = typeof profile === "string" && profile.length > 0 ? profile : null; + return profileName ? asRecord(asRecord(config["profiles"])[profileName]) : {}; +} + +function resolvedConfigValue(config: Record, key: string): unknown { + const profileValue = selectedProfileConfig(config)[key]; + return profileValue ?? config[key]; +} + +function approvalsReviewerOrNull(value: unknown): ApprovalsReviewer | null { + return value === "user" || value === "auto_review" || value === "guardian_subagent" ? value : null; +} + +function asRecord(value: unknown): Record { + return value && typeof value === "object" ? (value as Record) : {}; +} + +function fillMissingConfigValues(config: Record, layers: ConfigReadResponse["layers"]): Record { + if (!layers || layers.length === 0) return config; + const fallback = rawLayerConfigRecord(layers); + if (Object.keys(fallback).length === 0) return config; + + const merged = { ...config }; + for (const [key, value] of Object.entries(fallback)) { + merged[key] ??= value; + } + return merged; +} + +function rawLayerConfigRecord(layers: NonNullable): Record { + const merged: Record = {}; + for (const layer of layers) { + const config = asRecord(layer.config); + for (const [key, value] of Object.entries(config)) { + if (value !== undefined && value !== null) merged[key] = value; + } + } + return merged; +} + +function nonEmptyStringOrNull(value: unknown): string | null { + return typeof value === "string" && value.length > 0 ? value : null; +} + +function stringOrNull(value: unknown): string | null { + return typeof value === "string" ? value : null; +} + +function numberOrNull(value: unknown): number | null { + if (typeof value === "number") return Number.isFinite(value) ? value : null; + if (typeof value === "bigint") return Number(value); + return null; +} diff --git a/src/runtime/state.ts b/src/runtime/state.ts index ee00ab03..745f1d45 100644 --- a/src/runtime/state.ts +++ b/src/runtime/state.ts @@ -9,8 +9,9 @@ import type { ThreadTokenUsage } from "../generated/app-server/v2/ThreadTokenUsa import type { DisplayItem } from "../display/types"; import { parseServiceTier, serviceTierRequestValue, type ServiceTier, type ServiceTierRequest } from "../app-server/service-tier"; import { defaultCollaborationMode, planCollaborationMode } from "./collaboration-mode"; -import { findModelByIdOrName, isReasoningEffort, supportedEffortsForModel } from "./model"; +import { findModelByIdOrName, supportedEffortsForModel } from "./model"; import { compactModelLabel, compactReasoningEffortLabel } from "./settings"; +import { readRuntimeConfig, type RuntimeConfigProjection } from "./config"; export type RuntimeOverride = { kind: "default" } | { kind: "set"; value: T } | { kind: "resetPending" }; @@ -38,68 +39,56 @@ export interface TurnRuntimeSettings { warning: string | null; } -export function configRecord(effectiveConfig: ConfigReadResponse | null): Record { - return fillMissingConfigValues(asRecord(effectiveConfig?.config), effectiveConfig?.layers ?? null); -} - -export function selectedProfileConfig(config: Record): Record { - const profile = config["profile"]; - const profileName = typeof profile === "string" && profile.length > 0 ? profile : null; - return profileName ? asRecord(asRecord(config["profiles"])[profileName]) : {}; -} - -export function resolvedConfigValue(config: Record, key: string): unknown { - const profileValue = selectedProfileConfig(config)[key]; - return profileValue ?? config[key]; -} - -export function currentServiceTier(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string | null { +export function currentServiceTier( + snapshot: RuntimeSnapshot, + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), +): string | null { const active = parseServiceTier(snapshot.activeServiceTier) ?? snapshot.activeServiceTier; - const configured = configuredServiceTier(config); if (snapshot.requestedServiceTier !== null) return snapshot.requestedServiceTier; - return active ?? configured; + return active ?? config.serviceTier; } export function requestedOrConfiguredServiceTier( snapshot: RuntimeSnapshot, - config = configRecord(snapshot.effectiveConfig), + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), ): ServiceTierRequest { - return serviceTierRequestValue(snapshot.requestedServiceTier ?? configuredServiceTier(config)); + return serviceTierRequestValue(snapshot.requestedServiceTier ?? config.serviceTier); } -export function currentModel(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string | null { - const model = resolvedConfigValue(config, "model"); - const configModel = typeof model === "string" && model.length > 0 ? model : null; +export function currentModel( + snapshot: RuntimeSnapshot, + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), +): string | null { + const configModel = config.model; if (snapshot.requestedModel.kind === "set") return snapshot.requestedModel.value; if (snapshot.requestedModel.kind === "resetPending" && configModel) return configModel; return snapshot.activeModel ?? configModel; } -export function currentReasoningEffort(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): ReasoningEffort | null { +export function currentReasoningEffort( + snapshot: RuntimeSnapshot, + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), +): ReasoningEffort | null { if (snapshot.requestedReasoningEffort.kind === "set") return snapshot.requestedReasoningEffort.value; - const effort = resolvedConfigValue(config, "model_reasoning_effort"); - if (snapshot.requestedReasoningEffort.kind === "resetPending") return isReasoningEffort(effort) ? effort : null; - return snapshot.activeReasoningEffort ?? (isReasoningEffort(effort) ? effort : null); + if (snapshot.requestedReasoningEffort.kind === "resetPending") return config.reasoningEffort; + return snapshot.activeReasoningEffort ?? config.reasoningEffort; } export function currentApprovalsReviewer( snapshot: RuntimeSnapshot, - config = configRecord(snapshot.effectiveConfig), + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), ): ApprovalsReviewer | null { - const configured = configuredApprovalsReviewer(config); if (snapshot.requestedApprovalsReviewer !== null) return snapshot.requestedApprovalsReviewer; - return snapshot.activeApprovalsReviewer ?? configured; + return snapshot.activeApprovalsReviewer ?? config.approvalsReviewer; } -export function autoReviewActive(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): boolean { +export function autoReviewActive( + snapshot: RuntimeSnapshot, + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), +): boolean { return isAutoReviewReviewer(currentApprovalsReviewer(snapshot, config)); } -export function configuredApprovalsReviewer(config: Record): ApprovalsReviewer | null { - const value = resolvedConfigValue(config, "approvals_reviewer"); - return isApprovalsReviewer(value) ? value : null; -} - export function requestedTurnRuntimeSettings(snapshot: RuntimeSnapshot): TurnRuntimeSettings { const model = currentModel(snapshot); const effort = currentReasoningEffort(snapshot); @@ -125,11 +114,17 @@ export function runtimeSummaryLabel(model: string | null, effort: ReasoningEffor return `${modelLabel} ${compactReasoningEffortLabel(effort)}`; } -export function serviceTierLabel(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string { +export function serviceTierLabel( + snapshot: RuntimeSnapshot, + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), +): string { return currentServiceTier(snapshot, config) ?? "(not reported)"; } -export function fastModeLabel(snapshot: RuntimeSnapshot, config = configRecord(snapshot.effectiveConfig)): string { +export function fastModeLabel( + snapshot: RuntimeSnapshot, + config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig), +): string { const serviceTier = currentServiceTier(snapshot, config); if (serviceTier === "fast") return "on"; if (serviceTier === "standard") return "off"; @@ -161,41 +156,6 @@ export function runtimeOverrideLabel(override: RuntimeOverride): string { return "(none)"; } -export function configuredServiceTier(config: Record): ServiceTier | null { - return parseServiceTier(resolvedConfigValue(config, "service_tier")); -} - -function isApprovalsReviewer(value: unknown): value is ApprovalsReviewer { - return value === "user" || value === "auto_review" || value === "guardian_subagent"; -} - function isAutoReviewReviewer(value: ApprovalsReviewer | null): boolean { return value === "auto_review" || value === "guardian_subagent"; } - -function asRecord(value: unknown): Record { - return value && typeof value === "object" ? (value as Record) : {}; -} - -function fillMissingConfigValues(config: Record, layers: ConfigReadResponse["layers"]): Record { - if (!layers || layers.length === 0) return config; - const fallback = rawLayerConfigRecord(layers); - if (Object.keys(fallback).length === 0) return config; - - const merged = { ...config }; - for (const [key, value] of Object.entries(fallback)) { - merged[key] ??= value; - } - return merged; -} - -function rawLayerConfigRecord(layers: NonNullable): Record { - const merged: Record = {}; - for (const layer of layers) { - const config = asRecord(layer.config); - for (const [key, value] of Object.entries(config)) { - if (value !== undefined && value !== null) merged[key] = value; - } - } - return merged; -} diff --git a/src/runtime/view.ts b/src/runtime/view.ts index 51e0801c..18418e36 100644 --- a/src/runtime/view.ts +++ b/src/runtime/view.ts @@ -2,16 +2,13 @@ import type { RateLimitWindow } from "../generated/app-server/v2/RateLimitWindow import type { ThreadTokenUsage } from "../generated/app-server/v2/ThreadTokenUsage"; import { jsonPreview } from "../utils"; import { sortedAvailableModels } from "./model"; +import { readRuntimeConfig, type RuntimeConfigProjection } from "./config"; import { - configRecord, - configuredApprovalsReviewer, - configuredServiceTier, currentApprovalsReviewer, currentModel, currentReasoningEffort, autoReviewActive, fastModeLabel, - resolvedConfigValue, runtimeOverrideLabel, serviceTierLabel, type RuntimeSnapshot, @@ -46,8 +43,8 @@ export interface EffectiveConfigSection { export function contextSummary(snapshot: RuntimeSnapshot): ContextSummary | null { const usage = snapshot.tokenUsage; - const config = configRecord(snapshot.effectiveConfig); - const contextWindow = usage?.modelContextWindow ?? toNumber(config["model_context_window"]); + const config = readRuntimeConfig(snapshot.effectiveConfig); + const contextWindow = usage?.modelContextWindow ?? config.modelContextWindow; if (!usage) { if (!snapshot.activeThreadId) return null; if (!snapshot.displayItems.some((item) => item.turnId)) { @@ -105,17 +102,14 @@ export function rateLimitSummary(snapshot: RuntimeSnapshot, nowMs = Date.now()): } export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: string): EffectiveConfigSection[] { - const config = configRecord(snapshot.effectiveConfig); - const features = asRecord(config["features"]); - const tools = asRecord(resolvedConfigValue(config, "tools")); - const workspaceWrite = asRecord(config["sandbox_workspace_write"]); + const config = readRuntimeConfig(snapshot.effectiveConfig); return [ { title: "Scope", rows: [ { key: "cwd", value: vaultPath }, - { key: "profile", value: stringValue(config["profile"], "(default)") }, - { key: "model provider", value: stringValue(resolvedConfigValue(config, "model_provider"), "(from default)") }, + { key: "profile", value: config.profile ?? "(default)" }, + { key: "model provider", value: stringValue(config.modelProvider, "(from default)") }, ], }, { @@ -127,8 +121,8 @@ export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: st { key: "effort", value: currentReasoningEffort(snapshot, config) ?? "(from default)" }, { key: "config effort", value: configuredReasoningEffort(snapshot, config) ?? "(not reported)" }, { key: "effort change", value: runtimeOverrideLabel(snapshot.requestedReasoningEffort) }, - { key: "reasoning summary", value: stringValue(resolvedConfigValue(config, "model_reasoning_summary"), "(from default)") }, - { key: "verbosity", value: stringValue(resolvedConfigValue(config, "model_verbosity"), "(from default)") }, + { key: "reasoning summary", value: stringValue(config.reasoningSummary, "(from default)") }, + { key: "verbosity", value: stringValue(config.verbosity, "(from default)") }, { key: "mode", value: snapshot.activeCollaborationMode === "plan" ? "Plan" : "Default" }, { key: "mode change", @@ -138,37 +132,37 @@ export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: st : modeLabel(snapshot.requestedCollaborationMode), }, { key: "service tier", value: serviceTierLabel(snapshot, config) }, - { key: "config service tier", value: configuredServiceTier(config) ?? "(not reported)" }, + { key: "config service tier", value: config.serviceTier ?? "(not reported)" }, { key: "active service tier", value: activeRuntimeValueLabel(snapshot.activeServiceTier) }, { key: "fast mode", value: fastModeLabel(snapshot, config) }, - { key: "context window", value: tokenLimitLabel(config["model_context_window"]) }, - { key: "auto compact limit", value: tokenLimitLabel(config["model_auto_compact_token_limit"]) }, + { key: "context window", value: tokenLimitLabel(config.modelContextWindow) }, + { key: "auto compact limit", value: tokenLimitLabel(config.autoCompactTokenLimit) }, ], }, { title: "Policy", rows: [ - { key: "approval", value: stringValue(resolvedConfigValue(config, "approval_policy"), "(from default)") }, + { key: "approval", value: stringValue(config.approvalPolicy, "(from default)") }, { key: "reviewer", value: currentApprovalsReviewer(snapshot, config) ?? "(not reported)" }, { key: "auto-review", value: autoReviewActive(snapshot, config) ? "on" : "off" }, - { key: "config reviewer", value: configuredApprovalsReviewer(config) ?? "(from default)" }, + { key: "config reviewer", value: config.approvalsReviewer ?? "(from default)" }, { key: "active reviewer", value: activeRuntimeValueLabel(snapshot.activeApprovalsReviewer), }, - { key: "sandbox", value: stringValue(config["sandbox_mode"], "(from default)") }, - { key: "workspace network", value: stringValue(workspaceWrite["network_access"], "(from default)") }, - { key: "writable roots", value: writableRootsLabel(workspaceWrite["writable_roots"]) }, - { key: "web search", value: stringValue(resolvedConfigValue(config, "web_search"), "(from default)") }, + { key: "sandbox", value: stringValue(config.sandboxMode, "(from default)") }, + { key: "workspace network", value: stringValue(config.workspaceNetworkAccess, "(from default)") }, + { key: "writable roots", value: writableRootsLabel(config.writableRoots) }, + { key: "web search", value: stringValue(config.webSearch, "(from default)") }, ], }, { title: "Features", rows: [ - { key: "hooks", value: stringValue(features["hooks"], "(from default)") }, - { key: "apply patch freeform", value: stringValue(features["apply_patch_freeform"], "(from default)") }, - { key: "tool web search", value: stringValue(tools["web_search"], "(from default)") }, - { key: "apps", value: enabledAppsLabel(config["apps"]) }, + { key: "hooks", value: stringValue(config.hooksEnabled, "(from default)") }, + { key: "apply patch freeform", value: stringValue(config.applyPatchFreeformEnabled, "(from default)") }, + { key: "tool web search", value: stringValue(config.toolWebSearch, "(from default)") }, + { key: "apps", value: enabledAppsLabel(config.apps) }, ], }, ]; @@ -178,17 +172,13 @@ function contextUsageTokens(usage: ThreadTokenUsage): number { return usage.last.inputTokens > 0 ? usage.last.inputTokens : usage.last.totalTokens; } -function configuredModel(snapshot: RuntimeSnapshot, config: Record): string | null { - const model = resolvedConfigValue(config, "model"); - if (typeof model === "string" && model.length > 0) return model; +function configuredModel(snapshot: RuntimeSnapshot, config: RuntimeConfigProjection): string | null { + if (config.model) return config.model; return sortedAvailableModels(snapshot.availableModels).find((model) => model.isDefault)?.model ?? null; } -function configuredReasoningEffort(snapshot: RuntimeSnapshot, config: Record): string | null { - const effort = resolvedConfigValue(config, "model_reasoning_effort"); - if (typeof effort === "string" && effort.length > 0) { - return effort; - } +function configuredReasoningEffort(snapshot: RuntimeSnapshot, config: RuntimeConfigProjection): string | null { + if (config.rawReasoningEffort) return config.rawReasoningEffort; const model = configuredModel(snapshot, config); return ( sortedAvailableModels(snapshot.availableModels).find((availableModel) => availableModel.model === model)?.defaultReasoningEffort ?? null @@ -272,8 +262,7 @@ function writableRootsLabel(value: unknown): string { return `${String(value.length)} roots`; } -function enabledAppsLabel(value: unknown): string { - const apps = asRecord(value); +function enabledAppsLabel(apps: Record): string { const enabled = Object.entries(apps) .filter(([key, app]) => key !== "_default" && asRecord(app)["enabled"] === true) .map(([key]) => key) diff --git a/tests/runtime/runtime-settings.test.ts b/tests/runtime/runtime-settings.test.ts index 3e889a6e..959e204d 100644 --- a/tests/runtime/runtime-settings.test.ts +++ b/tests/runtime/runtime-settings.test.ts @@ -11,7 +11,6 @@ import { } from "../../src/runtime/settings"; import { autoReviewActive, - configRecord, currentApprovalsReviewer, currentModel, currentReasoningEffort, @@ -25,6 +24,7 @@ import { serviceTierLabel, type RuntimeSnapshot, } from "../../src/runtime/state"; +import { readRuntimeConfig } from "../../src/runtime/config"; import { contextSummary, effectiveConfigSections, rateLimitSummary } from "../../src/runtime/view"; describe("runtime settings", () => { @@ -131,7 +131,7 @@ describe("runtime settings", () => { }); it("uses raw config layers when typed config omits approval reviewer", () => { - const config = configRecord({ + const effectiveConfig = { config: { approvals_reviewer: null, }, @@ -143,14 +143,15 @@ describe("runtime settings", () => { disabledReason: null, }, ], - } as unknown as RuntimeSnapshot["effectiveConfig"]); + } as unknown as RuntimeSnapshot["effectiveConfig"]; + const projection = readRuntimeConfig(effectiveConfig); const snapshot = runtimeSnapshot({ activeApprovalsReviewer: "user", - effectiveConfig: { config } as unknown as RuntimeSnapshot["effectiveConfig"], + effectiveConfig, }); - expect(config["approvals_reviewer"]).toBe("auto_review"); - expect(autoReviewActive(snapshot, config)).toBe(false); + expect(projection.approvalsReviewer).toBe("auto_review"); + expect(autoReviewActive(snapshot, projection)).toBe(false); }); it("treats guardian subagent reviewer as active auto-review", () => {