mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Project runtime config into typed accessors
This commit is contained in:
parent
d77b7bf526
commit
831cb3f5d3
5 changed files with 194 additions and 127 deletions
|
|
@ -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
118
src/runtime/config.ts
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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", () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue