Clarify diagnostics and runtime config labels

This commit is contained in:
murashit 2026-05-27 14:48:26 +09:00
parent c222f33593
commit c9a6f87ddb
9 changed files with 125 additions and 75 deletions

View file

@ -96,7 +96,7 @@ export class ChatSessionController {
this.host.state.activeReasoningEffort = response.reasoningEffort;
this.host.state.activeServiceTier = reportedServiceTier(response.serviceTier);
this.host.state.activeApprovalsReviewer = response.approvalsReviewer;
this.host.state.activeThreadCliVersion = response.thread.cliVersion;
this.host.state.activeThreadCreationCliVersion = response.thread.cliVersion;
this.host.state.tokenUsage = null;
this.host.state.historyCursor = null;
this.host.state.turnDiffs.clear();

View file

@ -34,7 +34,7 @@ export interface ChatState {
activeCollaborationMode: ModeKind;
activeServiceTier: ReportedServiceTier | null;
activeApprovalsReviewer: ApprovalsReviewer | null;
activeThreadCliVersion: string | null;
activeThreadCreationCliVersion: string | null;
appServerDiagnostics: AppServerDiagnostics;
requestedModel: RuntimeOverride<string>;
requestedReasoningEffort: RuntimeOverride<ReasoningEffort>;
@ -79,7 +79,7 @@ export function createChatState(): ChatState {
activeCollaborationMode: "default",
activeServiceTier: null,
activeApprovalsReviewer: null,
activeThreadCliVersion: null,
activeThreadCreationCliVersion: null,
appServerDiagnostics: createAppServerDiagnostics(),
requestedModel: defaultRuntimeOverride(),
requestedReasoningEffort: defaultRuntimeOverride(),
@ -129,7 +129,7 @@ export function clearActiveThreadState(state: ChatState): void {
state.activeCollaborationMode = "default";
state.activeServiceTier = null;
state.activeApprovalsReviewer = null;
state.activeThreadCliVersion = null;
state.activeThreadCreationCliVersion = null;
state.tokenUsage = null;
state.historyCursor = null;
state.loadingHistory = false;
@ -146,7 +146,7 @@ export function clearConnectionScopedState(state: ChatState): void {
state.activeCollaborationMode = "default";
state.activeServiceTier = null;
state.activeApprovalsReviewer = null;
state.activeThreadCliVersion = null;
state.activeThreadCreationCliVersion = null;
state.rateLimit = null;
state.listedThreads = [];
state.threadsLoaded = false;

View file

@ -20,7 +20,7 @@ export interface ConnectionDiagnosticsInput {
connected: boolean;
configuredCommand: string;
initializeResponse: InitializeResponse | null;
activeThreadCliVersion: string | null;
activeThreadCreationCliVersion: string | null;
diagnostics: AppServerDiagnostics;
}
@ -36,7 +36,7 @@ export function connectionDiagnosticSections(input: ConnectionDiagnosticsInput):
{ label: "panel client", value: CLIENT_VERSION },
{ label: "platform", value: appServerPlatform(input.initializeResponse) },
{ label: "Codex home", value: input.initializeResponse?.codexHome ?? "(not connected)" },
{ label: "active thread CLI", value: input.activeThreadCliVersion ?? "(none)" },
{ label: "thread created by CLI", value: input.activeThreadCreationCliVersion ?? "(none)" },
],
},
{

View file

@ -611,7 +611,7 @@ export class CodexChatView extends ItemView {
this.state.activeReasoningEffort = response.reasoningEffort;
this.state.activeServiceTier = reportedServiceTier(response.serviceTier);
this.state.activeApprovalsReviewer = response.approvalsReviewer;
this.state.activeThreadCliVersion = response.thread.cliVersion;
this.state.activeThreadCreationCliVersion = response.thread.cliVersion;
this.state.tokenUsage = null;
this.state.displayItems = [this.systemItem("Loading thread...")];
this.state.turnDiffs.clear();
@ -1077,7 +1077,7 @@ export class CodexChatView extends ItemView {
this.state.activeCollaborationMode = "default";
this.state.activeServiceTier = null;
this.state.activeApprovalsReviewer = null;
this.state.activeThreadCliVersion = null;
this.state.activeThreadCreationCliVersion = null;
this.state.tokenUsage = null;
this.state.busy = false;
this.state.displayItems = [this.systemItem("Thread restored. Send a message to resume it.")];
@ -1280,7 +1280,7 @@ export class CodexChatView extends ItemView {
autoReviewActive: autoReviewActive(snapshot, config),
fastActive: currentServiceTier(snapshot, config) === "fast",
runtimeSummary: runtimeSummaryLabel(model, effort),
runtimeTitle: `Model: ${model ?? "(from default)"}; Effort: ${effort ?? "(from default)"}`,
runtimeTitle: `Model: ${model ?? "(Codex default)"}; Effort: ${effort ?? "(Codex default)"}`,
runtimeEmphasized: false,
context: context ? { ...context, label: compactContextLabel(context.percent, context.label) } : null,
rateLimit: limit,
@ -1506,10 +1506,10 @@ export class CodexChatView extends ItemView {
const snapshot = this.runtimeSnapshot();
const config = readRuntimeConfig(this.state.effectiveConfig);
return [
`Model: ${currentModel(snapshot, config) ?? "(from default)"}`,
`Model: ${currentModel(snapshot, config) ?? "(Codex default)"}`,
`Override: ${runtimeOverrideLabel(this.state.requestedModel)}`,
`Provider: ${statusValue(config.modelProvider, "(from default)")}`,
`Effort: ${currentReasoningEffort(snapshot, config) ?? "(from default)"}`,
`Provider: ${statusValue(config.modelProvider, "(Codex default)")}`,
`Effort: ${currentReasoningEffort(snapshot, config) ?? "(Codex default)"}`,
`Mode: ${this.collaborationModeLabel()}`,
`Service tier: ${serviceTierLabel(snapshot, config)}`,
];
@ -1519,7 +1519,7 @@ export class CodexChatView extends ItemView {
const snapshot = this.runtimeSnapshot();
const config = readRuntimeConfig(this.state.effectiveConfig);
return [
`Effort: ${currentReasoningEffort(snapshot, config) ?? "(from default)"}`,
`Effort: ${currentReasoningEffort(snapshot, config) ?? "(Codex default)"}`,
`Override: ${runtimeOverrideLabel(this.state.requestedReasoningEffort)}`,
`Supported: ${supportedReasoningEfforts(snapshot).join(", ")}`,
];
@ -1530,7 +1530,7 @@ export class CodexChatView extends ItemView {
connected: this.connection.isConnected(),
configuredCommand: this.plugin.settings.codexPath,
initializeResponse: this.state.initializeResponse,
activeThreadCliVersion: this.state.activeThreadCliVersion,
activeThreadCreationCliVersion: this.state.activeThreadCreationCliVersion,
diagnostics: this.state.appServerDiagnostics,
});
}

View file

@ -116,7 +116,7 @@ export function serviceTierLabel(
snapshot: RuntimeSnapshot,
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
): string {
return currentServiceTier(snapshot, config) ?? "(not reported)";
return currentServiceTier(snapshot, config) ?? "(Codex default)";
}
export function fastModeLabel(
@ -127,7 +127,7 @@ export function fastModeLabel(
if (serviceTier === "fast") return "on";
if (serviceTier === "standard") return "off";
if (serviceTier) return `unknown (${serviceTier})`;
return "not reported";
return "Codex default";
}
export function defaultRuntimeOverride<T>(): RuntimeOverride<T> {

View file

@ -41,6 +41,9 @@ export interface EffectiveConfigSection {
rows: { key: string; value: string }[];
}
const CODEX_DEFAULT_LABEL = "(Codex default)";
const NOT_REPORTED_LABEL = "(not reported)";
export function contextSummary(snapshot: RuntimeSnapshot): ContextSummary | null {
const usage = snapshot.tokenUsage;
const config = readRuntimeConfig(snapshot.effectiveConfig);
@ -109,31 +112,34 @@ export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: st
rows: [
{ key: "cwd", value: vaultPath },
{ key: "profile", value: config.profile ?? "(default)" },
{ key: "model provider", value: stringValue(config.modelProvider, "(from default)") },
{ key: "model provider", value: stringValue(config.modelProvider, CODEX_DEFAULT_LABEL) },
],
},
{
title: "Runtime",
rows: [
{ key: "model", value: currentModel(snapshot, config) ?? "(from default)" },
{ key: "config model", value: configuredModel(snapshot, config) ?? "(not reported)" },
{ key: "model change", value: runtimeOverrideLabel(snapshot.requestedModel) },
{ 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(config.reasoningSummary, "(from default)") },
{ key: "verbosity", value: stringValue(config.verbosity, "(from default)") },
{ key: "mode", value: snapshot.activeCollaborationMode === "plan" ? "Plan" : "Default" },
{ key: "effective model", value: currentModel(snapshot, config) ?? CODEX_DEFAULT_LABEL },
{ key: "configured model", value: configuredModel(snapshot, config) ?? NOT_REPORTED_LABEL },
{ key: "thread model", value: activeRuntimeValueLabel(snapshot.activeModel) },
{ key: "requested model", value: runtimeOverrideLabel(snapshot.requestedModel) },
{ key: "effective effort", value: currentReasoningEffort(snapshot, config) ?? CODEX_DEFAULT_LABEL },
{ key: "configured effort", value: configuredReasoningEffort(snapshot, config) ?? NOT_REPORTED_LABEL },
{ key: "thread effort", value: activeRuntimeValueLabel(snapshot.activeReasoningEffort) },
{ key: "requested effort", value: runtimeOverrideLabel(snapshot.requestedReasoningEffort) },
{ key: "reasoning summary", value: stringValue(config.reasoningSummary, CODEX_DEFAULT_LABEL) },
{ key: "verbosity", value: stringValue(config.verbosity, CODEX_DEFAULT_LABEL) },
{ key: "effective mode", value: snapshot.activeCollaborationMode === "plan" ? "Plan" : "Default" },
{
key: "mode change",
key: "requested mode",
value:
snapshot.requestedCollaborationMode === snapshot.activeCollaborationMode
? "(none)"
: modeLabel(snapshot.requestedCollaborationMode),
},
{ key: "service tier", value: serviceTierLabel(snapshot, config) },
{ key: "config service tier", value: config.serviceTier ?? "(not reported)" },
{ key: "active service tier", value: activeRuntimeValueLabel(snapshot.activeServiceTier) },
{ key: "effective service tier", value: serviceTierLabel(snapshot, config) },
{ key: "configured service tier", value: config.serviceTier ?? CODEX_DEFAULT_LABEL },
{ key: "thread service tier", value: activeRuntimeValueLabel(snapshot.activeServiceTier) },
{ key: "requested service tier", value: snapshot.requestedServiceTier ?? "(none)" },
{ key: "fast mode", value: fastModeLabel(snapshot, config) },
{ key: "context window", value: tokenLimitLabel(config.modelContextWindow) },
{ key: "auto compact limit", value: tokenLimitLabel(config.autoCompactTokenLimit) },
@ -142,26 +148,24 @@ export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: st
{
title: "Policy",
rows: [
{ key: "approval", value: stringValue(config.approvalPolicy, "(from default)") },
{ key: "reviewer", value: currentApprovalsReviewer(snapshot, config) ?? "(not reported)" },
{ key: "approval", value: stringValue(config.approvalPolicy, CODEX_DEFAULT_LABEL) },
{ key: "effective reviewer", value: currentApprovalsReviewer(snapshot, config) ?? NOT_REPORTED_LABEL },
{ key: "auto-review", value: autoReviewActive(snapshot, config) ? "on" : "off" },
{ key: "config reviewer", value: config.approvalsReviewer ?? "(from default)" },
{
key: "active reviewer",
value: activeRuntimeValueLabel(snapshot.activeApprovalsReviewer),
},
{ key: "sandbox", value: stringValue(config.sandboxMode, "(from default)") },
{ key: "workspace network", value: stringValue(config.workspaceNetworkAccess, "(from default)") },
{ key: "configured reviewer", value: config.approvalsReviewer ?? CODEX_DEFAULT_LABEL },
{ key: "thread reviewer", value: activeRuntimeValueLabel(snapshot.activeApprovalsReviewer) },
{ key: "requested reviewer", value: snapshot.requestedApprovalsReviewer ?? "(none)" },
{ key: "sandbox", value: stringValue(config.sandboxMode, CODEX_DEFAULT_LABEL) },
{ key: "workspace network", value: stringValue(config.workspaceNetworkAccess, CODEX_DEFAULT_LABEL) },
{ key: "writable roots", value: writableRootsLabel(config.writableRoots) },
{ key: "web search", value: stringValue(config.webSearch, "(from default)") },
{ key: "web search", value: stringValue(config.webSearch, CODEX_DEFAULT_LABEL) },
],
},
{
title: "Features",
rows: [
{ 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: "hooks", value: stringValue(config.hooksEnabled, CODEX_DEFAULT_LABEL) },
{ key: "apply_patch freeform", value: stringValue(config.applyPatchFreeformEnabled, CODEX_DEFAULT_LABEL) },
{ key: "tool web search", value: stringValue(config.toolWebSearch, CODEX_DEFAULT_LABEL) },
{ key: "apps", value: enabledAppsLabel(config.apps) },
],
},
@ -190,7 +194,7 @@ function modeLabel(mode: RuntimeSnapshot["requestedCollaborationMode"]): string
}
function activeRuntimeValueLabel(value: string | null): string {
return value ?? "(not reported)";
return value ?? NOT_REPORTED_LABEL;
}
function rateLimitWindowSummary(
@ -252,11 +256,11 @@ function capitalize(value: string): string {
function tokenLimitLabel(value: unknown): string {
const tokens = toNumber(value);
return tokens === null ? "(from default)" : formatTokenCount(tokens);
return tokens === null ? CODEX_DEFAULT_LABEL : formatTokenCount(tokens);
}
function writableRootsLabel(value: unknown): string {
if (!Array.isArray(value)) return "(from default)";
if (!Array.isArray(value)) return CODEX_DEFAULT_LABEL;
if (value.length === 0) return "none";
if (value.length === 1) return String(value[0]);
return `${String(value.length)} roots`;
@ -270,7 +274,7 @@ function enabledAppsLabel(apps: unknown): string {
.sort();
if (enabled.length > 0) return enabled.join(", ");
const defaultConfig = asRecord(appConfig["_default"]);
return stringValue(defaultConfig["enabled"], "(from default)");
return stringValue(defaultConfig["enabled"], CODEX_DEFAULT_LABEL);
}
function asRecord(value: unknown): Record<string, unknown> {

View file

@ -845,7 +845,7 @@ describe("ChatController", () => {
state.activeTurnId = "turn-active";
state.activeModel = "gpt-5.5";
state.activeServiceTier = "fast";
state.activeThreadCliVersion = "codex-cli 1.0.0";
state.activeThreadCreationCliVersion = "codex-cli 1.0.0";
state.tokenUsage = {
last: { inputTokens: 1, cachedInputTokens: 0, outputTokens: 2, reasoningOutputTokens: 0, totalTokens: 3 },
total: { inputTokens: 1, cachedInputTokens: 0, outputTokens: 2, reasoningOutputTokens: 0, totalTokens: 3 },
@ -892,7 +892,7 @@ describe("ChatController", () => {
expect(state.activeTurnId).toBeNull();
expect(state.activeModel).toBeNull();
expect(state.activeServiceTier).toBeNull();
expect(state.activeThreadCliVersion).toBeNull();
expect(state.activeThreadCreationCliVersion).toBeNull();
expect(state.tokenUsage).toBeNull();
expect(state.historyCursor).toBeNull();
expect(state.loadingHistory).toBe(false);

View file

@ -37,7 +37,7 @@ describe("connection diagnostics", () => {
platformFamily: "unix",
platformOs: "macos",
},
activeThreadCliVersion: "0.130.0",
activeThreadCreationCliVersion: "0.130.0",
diagnostics,
});
@ -46,6 +46,7 @@ describe("connection diagnostics", () => {
expect(rows.map((row) => `${row.label}: ${row.value}`)).toEqual(
expect.arrayContaining([
"connection: connected",
"thread created by CLI: 0.130.0",
"model/list: ok (12 models)",
"skills/list: failed - unknown method skills/list",
"mcp docs: ready - auth notLoggedIn",

View file

@ -252,16 +252,19 @@ describe("runtime settings", () => {
);
expect(runtimeRows).toMatchObject({
model: "gpt-5-pending",
"config model": "gpt-5.5",
"model change": "gpt-5-pending",
effort: "low",
"config effort": "high",
"effort change": "(none)",
mode: "Plan",
"mode change": "(none)",
"config service tier": "standard",
"active service tier": "(not reported)",
"effective model": "gpt-5-pending",
"configured model": "gpt-5.5",
"thread model": "gpt-5-active",
"requested model": "gpt-5-pending",
"effective effort": "low",
"configured effort": "high",
"thread effort": "low",
"requested effort": "(none)",
"effective mode": "Plan",
"requested mode": "(none)",
"configured service tier": "standard",
"thread service tier": "(not reported)",
"requested service tier": "(none)",
});
const resetSections = effectiveConfigSections(
@ -273,7 +276,43 @@ describe("runtime settings", () => {
const resetRuntimeRows = Object.fromEntries(
resetSections.find((section) => section.title === "Runtime")?.rows.map((row) => [row.key, row.value]) ?? [],
);
expect(resetRuntimeRows["effort change"]).toBe("(reset to config)");
expect(resetRuntimeRows["requested effort"]).toBe("(reset to config)");
});
it("labels unset config values as Codex defaults when no concrete value is reported", () => {
const sections = effectiveConfigSections(
runtimeSnapshot({
effectiveConfig: effectiveConfigFixture({}),
}),
"/vault",
);
const scopeRows = Object.fromEntries(
sections.find((section) => section.title === "Scope")?.rows.map((row) => [row.key, row.value]) ?? [],
);
const runtimeRows = Object.fromEntries(
sections.find((section) => section.title === "Runtime")?.rows.map((row) => [row.key, row.value]) ?? [],
);
const policyRows = Object.fromEntries(
sections.find((section) => section.title === "Policy")?.rows.map((row) => [row.key, row.value]) ?? [],
);
expect(scopeRows["model provider"]).toBe("(Codex default)");
expect(runtimeRows).toMatchObject({
"effective model": "(Codex default)",
"configured model": "(not reported)",
"thread model": "(not reported)",
"requested model": "(none)",
"configured service tier": "(Codex default)",
"thread service tier": "(not reported)",
"requested service tier": "(none)",
"fast mode": "Codex default",
});
expect(policyRows).toMatchObject({
approval: "(Codex default)",
"configured reviewer": "(Codex default)",
sandbox: "(Codex default)",
"web search": "(Codex default)",
});
});
it("shows effective profile runtime and policy values in status details", () => {
@ -307,22 +346,28 @@ describe("runtime settings", () => {
expect(scopeRows["profile"]).toBe("auto");
expect(scopeRows["model provider"]).toBe("profile-provider");
expect(runtimeRows).toMatchObject({
model: "gpt-profile",
"config model": "gpt-profile",
effort: "high",
"config effort": "high",
"effective model": "gpt-profile",
"configured model": "gpt-profile",
"thread model": "(not reported)",
"requested model": "(none)",
"effective effort": "high",
"configured effort": "high",
"thread effort": "(not reported)",
"requested effort": "(none)",
"reasoning summary": "detailed",
verbosity: "high",
"service tier": "fast",
"config service tier": "fast",
"active service tier": "(not reported)",
"effective service tier": "fast",
"configured service tier": "fast",
"thread service tier": "(not reported)",
"requested service tier": "(none)",
"fast mode": "on",
});
expect(policyRows["approval"]).toBe("never");
expect(policyRows["reviewer"]).toBe("auto_review");
expect(policyRows["effective reviewer"]).toBe("auto_review");
expect(policyRows["auto-review"]).toBe("on");
expect(policyRows["config reviewer"]).toBe("auto_review");
expect(policyRows["active reviewer"]).toBe("(not reported)");
expect(policyRows["configured reviewer"]).toBe("auto_review");
expect(policyRows["thread reviewer"]).toBe("(not reported)");
expect(policyRows["requested reviewer"]).toBe("(none)");
expect(policyRows["web search"]).toBe("live");
});
@ -339,10 +384,10 @@ describe("runtime settings", () => {
);
expect(policyRows).toMatchObject({
reviewer: "guardian_subagent",
"effective reviewer": "guardian_subagent",
"auto-review": "on",
"config reviewer": "auto_review",
"active reviewer": "guardian_subagent",
"configured reviewer": "auto_review",
"thread reviewer": "guardian_subagent",
});
});