Project runtime config into typed accessors

This commit is contained in:
murashit 2026-05-23 08:17:29 +09:00
parent d77b7bf526
commit 831cb3f5d3
5 changed files with 194 additions and 127 deletions

View file

@ -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<void> {
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<void> {
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)}`,

118
src/runtime/config.ts Normal file
View file

@ -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<string, unknown>;
}
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<string, unknown> {
return fillMissingConfigValues(asRecord(effectiveConfig?.config), effectiveConfig?.layers ?? null);
}
function selectedProfileConfig(config: Record<string, unknown>): Record<string, unknown> {
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<string, unknown>, 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<string, unknown> {
return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
}
function fillMissingConfigValues(config: Record<string, unknown>, layers: ConfigReadResponse["layers"]): Record<string, unknown> {
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<ConfigReadResponse["layers"]>): Record<string, unknown> {
const merged: Record<string, unknown> = {};
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;
}

View file

@ -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<T> = { 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<string, unknown> {
return fillMissingConfigValues(asRecord(effectiveConfig?.config), effectiveConfig?.layers ?? null);
}
export function selectedProfileConfig(config: Record<string, unknown>): Record<string, unknown> {
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<string, unknown>, 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<string, unknown>): 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<T>(override: RuntimeOverride<T>): string {
return "(none)";
}
export function configuredServiceTier(config: Record<string, unknown>): 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<string, unknown> {
return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
}
function fillMissingConfigValues(config: Record<string, unknown>, layers: ConfigReadResponse["layers"]): Record<string, unknown> {
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<ConfigReadResponse["layers"]>): Record<string, unknown> {
const merged: Record<string, unknown> = {};
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;
}

View file

@ -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, unknown>): 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, unknown>): 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, unknown>): string {
const enabled = Object.entries(apps)
.filter(([key, app]) => key !== "_default" && asRecord(app)["enabled"] === true)
.map(([key]) => key)

View file

@ -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", () => {