From 37051838a8ff6ac551ad2016e2ada83a436f11fd Mon Sep 17 00:00:00 2001 From: murashit Date: Fri, 10 Jul 2026 02:07:34 +0900 Subject: [PATCH] Flatten runtime slash command details --- .../chat/panel/runtime-status-projection.ts | 12 ++-- .../chat/presentation/runtime/status.ts | 58 ++++++------------- .../turns/slash-command-execution.test.ts | 18 +++--- .../panel/runtime-status-projection.test.ts | 8 +-- tests/runtime/runtime-settings.test.ts | 26 ++++++++- 5 files changed, 63 insertions(+), 59 deletions(-) diff --git a/src/features/chat/panel/runtime-status-projection.ts b/src/features/chat/panel/runtime-status-projection.ts index d7de1ed4..72775678 100644 --- a/src/features/chat/panel/runtime-status-projection.ts +++ b/src/features/chat/panel/runtime-status-projection.ts @@ -5,7 +5,7 @@ import { collaborationModeLabel as formatCollaborationModeLabel } from "../domai import { resolveRuntimeControls } from "../domain/runtime/resolution"; import type { RuntimeSnapshot } from "../domain/runtime/snapshot"; import type { ThreadStreamNoticeSection } from "../domain/thread-stream/items"; -import { appServerDiagnosticSections } from "../presentation/runtime/diagnostic-sections"; +import { appServerDiagnosticSections, type DiagnosticRow } from "../presentation/runtime/diagnostic-sections"; import { runtimePermissionSections } from "../presentation/runtime/permission-sections"; import { effortStatusDetails as buildEffortStatusDetails, @@ -44,7 +44,7 @@ export function createChatPanelRuntimeProjection(input: ChatPanelRuntimeProjecti function statusDetails(input: ChatPanelRuntimeProjectionInput): ThreadStreamNoticeSection[] { const state = input.state(); - return noticeSectionsFromDiagnostics( + return noticeSectionsFromRows( buildStatusDetails({ activeThreadId: state.activeThread.id, snapshot: runtimeSnapshot(state), @@ -55,7 +55,7 @@ function statusDetails(input: ChatPanelRuntimeProjectionInput): ThreadStreamNoti function modelStatusDetails(input: ChatPanelRuntimeProjectionInput): ThreadStreamNoticeSection[] { const state = input.state(); - return noticeSectionsFromDiagnostics( + return noticeSectionsFromRows( buildModelStatusDetails({ runtimeConfig: state.connection.runtimeConfig, pendingModel: state.runtime.pending.model, @@ -67,7 +67,7 @@ function modelStatusDetails(input: ChatPanelRuntimeProjectionInput): ThreadStrea function effortStatusDetails(input: ChatPanelRuntimeProjectionInput): ThreadStreamNoticeSection[] { const state = input.state(); - return noticeSectionsFromDiagnostics( + return noticeSectionsFromRows( buildEffortStatusDetails({ runtimeConfig: state.connection.runtimeConfig, pendingReasoningEffort: state.runtime.pending.reasoningEffort, @@ -110,6 +110,10 @@ function noticeSectionsFromDiagnostics( })); } +function noticeSectionsFromRows(rows: readonly DiagnosticRow[]): ThreadStreamNoticeSection[] { + return [{ auditFacts: rows.map((row) => ({ key: row.label, value: row.value })) }]; +} + function collaborationModeLabel(state: ChatState): string { const snapshot = runtimeSnapshot(state); return formatCollaborationModeLabel( diff --git a/src/features/chat/presentation/runtime/status.ts b/src/features/chat/presentation/runtime/status.ts index c9341a22..b3aab5e6 100644 --- a/src/features/chat/presentation/runtime/status.ts +++ b/src/features/chat/presentation/runtime/status.ts @@ -4,7 +4,7 @@ import type { RateLimitWindow, SpendControlLimitSnapshot, ThreadTokenUsage } fro import { serviceTierLabel as formatServiceTierLabel, pendingRuntimeSettingLabel } from "../../domain/runtime/labels"; import { resolveRuntimeControls } from "../../domain/runtime/resolution"; import type { RuntimeSnapshot } from "../../domain/runtime/snapshot"; -import type { DiagnosticSection } from "./diagnostic-sections"; +import type { DiagnosticRow } from "./diagnostic-sections"; export interface ContextSummary { label: string; @@ -121,54 +121,36 @@ export function rateLimitSummary(snapshot: RuntimeSnapshot, nowMs: number): Rate }; } -export function statusDetails(input: StatusDetailsInput): DiagnosticSection[] { +export function statusDetails(input: StatusDetailsInput): DiagnosticRow[] { const context = contextSummary(input.snapshot); const limit = rateLimitSummary(input.snapshot, input.nowMs); return [ - { - title: "Thread", - rows: [ - { label: "Thread", value: input.activeThreadId ?? "(none)" }, - { label: "Context", value: context ? context.detail : "not available" }, - ], - }, - { - title: "Usage Limits", - rows: limit ? usageLimitRows(limit) : [{ label: "Status", value: "not available" }], - }, + { label: "Thread", value: input.activeThreadId ?? "(none)" }, + { label: "Context", value: context ? context.detail : "not available" }, + { label: "Usage Limits", value: limit ? usageLimitValue(limit) : "not available" }, ]; } -export function modelStatusDetails(input: ModelStatusDetailsInput): DiagnosticSection[] { +export function modelStatusDetails(input: ModelStatusDetailsInput): DiagnosticRow[] { const config = runtimeConfigOrDefault(input.runtimeConfig); const resolution = resolveRuntimeControls(input.snapshot, config); return [ - { - title: "Model", - rows: [ - { label: "Model", value: resolution.model.effective ?? CODEX_DEFAULT_LABEL }, - { label: "Override", value: pendingRuntimeSettingLabel(input.pendingModel) }, - { label: "Provider", value: stringValue(config.modelProvider, CODEX_DEFAULT_LABEL) }, - { label: "Effort", value: resolution.reasoningEffort.effective ?? CODEX_DEFAULT_LABEL }, - { label: "Mode", value: input.collaborationModeLabel }, - { label: "Service tier", value: serviceTierLabel(input.snapshot, config) }, - ], - }, + { label: "Model", value: resolution.model.effective ?? CODEX_DEFAULT_LABEL }, + { label: "Override", value: pendingRuntimeSettingLabel(input.pendingModel) }, + { label: "Provider", value: stringValue(config.modelProvider, CODEX_DEFAULT_LABEL) }, + { label: "Effort", value: resolution.reasoningEffort.effective ?? CODEX_DEFAULT_LABEL }, + { label: "Mode", value: input.collaborationModeLabel }, + { label: "Service tier", value: serviceTierLabel(input.snapshot, config) }, ]; } -export function effortStatusDetails(input: EffortStatusDetailsInput): DiagnosticSection[] { +export function effortStatusDetails(input: EffortStatusDetailsInput): DiagnosticRow[] { const config = runtimeConfigOrDefault(input.runtimeConfig); const resolution = resolveRuntimeControls(input.snapshot, config); return [ - { - title: "Reasoning", - rows: [ - { label: "Effort", value: resolution.reasoningEffort.effective ?? CODEX_DEFAULT_LABEL }, - { label: "Override", value: pendingRuntimeSettingLabel(input.pendingReasoningEffort) }, - { label: "Supported", value: resolution.supportedReasoningEfforts.join(", ") }, - ], - }, + { label: "Effort", value: resolution.reasoningEffort.effective ?? CODEX_DEFAULT_LABEL }, + { label: "Override", value: pendingRuntimeSettingLabel(input.pendingReasoningEffort) }, + { label: "Supported", value: resolution.supportedReasoningEfforts.join(", ") }, ]; } @@ -176,12 +158,8 @@ function contextUsageTokens(usage: ThreadTokenUsage): number { return usage.last.inputTokens > 0 ? usage.last.inputTokens : usage.last.totalTokens; } -function usageLimitRows(limit: RateLimitSummary): DiagnosticSection["rows"] { - return limit.rows.map((row) => ({ - label: row.label, - value: `${row.value}${row.resetLabel ? ` (${row.resetLabel})` : ""}`, - level: row.level === "ok" ? "normal" : row.level === "warn" ? "warning" : "error", - })); +function usageLimitValue(limit: RateLimitSummary): string { + return limit.rows.map((row) => `${row.label} ${row.value}${row.resetLabel ? ` (${row.resetLabel})` : ""}`).join(", "); } function rateLimitWindowSummary( diff --git a/tests/features/chat/application/turns/slash-command-execution.test.ts b/tests/features/chat/application/turns/slash-command-execution.test.ts index 273ea592..b5965cbb 100644 --- a/tests/features/chat/application/turns/slash-command-execution.test.ts +++ b/tests/features/chat/application/turns/slash-command-execution.test.ts @@ -55,12 +55,12 @@ function context(overrides: Partial = {}): SlashCo setStatus: vi.fn().mockResolvedValue(true), clear: vi.fn().mockResolvedValue(true), }, - statusDetails: () => [{ title: "Thread", auditFacts: [{ key: "Thread", value: "thread-1" }] }], + statusDetails: () => [{ auditFacts: [{ key: "Thread", value: "thread-1" }] }], permissionDetails: () => [{ title: "Permissions", auditFacts: [{ key: "Profile", value: "read-only" }] }], connectionDiagnosticDetails: () => [{ title: "Process", rows: [{ key: "connection", value: "connected" }] }], toolInventoryDetails: vi.fn(() => [{ title: "Tool providers", auditFacts: [{ key: "codex_apps", value: "github" }] }]), - modelStatusDetails: () => [{ title: "Model", auditFacts: [{ key: "Model", value: "gpt-5.5" }] }], - effortStatusDetails: () => [{ title: "Reasoning", auditFacts: [{ key: "Effort", value: "high" }] }], + modelStatusDetails: () => [{ auditFacts: [{ key: "Model", value: "gpt-5.5" }] }], + effortStatusDetails: () => [{ auditFacts: [{ key: "Effort", value: "high" }] }], ...overrides, }; } @@ -621,8 +621,12 @@ describe("slash commands", () => { it("shows status as a structured system result", async () => { const details = [ - { title: "Thread", auditFacts: [{ key: "Thread", value: "thread-1" }] }, - { title: "Usage Limits", auditFacts: [{ key: "5h", value: "42%" }] }, + { + auditFacts: [ + { key: "Thread", value: "thread-1" }, + { key: "Usage Limits", value: "5h 42%" }, + ], + }, ]; const ctx = context({ statusDetails: () => details }); @@ -772,8 +776,8 @@ describe("slash commands", () => { }); it("shows model and reasoning status for empty runtime commands", async () => { - const modelDetails = [{ title: "Model", auditFacts: [{ key: "Model", value: "gpt-5.5" }] }]; - const effortDetails = [{ title: "Reasoning", auditFacts: [{ key: "Effort", value: "high" }] }]; + const modelDetails = [{ auditFacts: [{ key: "Model", value: "gpt-5.5" }] }]; + const effortDetails = [{ auditFacts: [{ key: "Effort", value: "high" }] }]; const ctx = context({ modelStatusDetails: () => modelDetails, effortStatusDetails: () => effortDetails, diff --git a/tests/features/chat/panel/runtime-status-projection.test.ts b/tests/features/chat/panel/runtime-status-projection.test.ts index 5f7b92cd..4ecceee3 100644 --- a/tests/features/chat/panel/runtime-status-projection.test.ts +++ b/tests/features/chat/panel/runtime-status-projection.test.ts @@ -31,20 +31,15 @@ describe("createChatPanelRuntimeProjection", () => { expect(projection.statusDetails()).toEqual([ { - title: "Thread", auditFacts: [ { key: "Thread", value: "thread-1" }, { key: "Context", value: "0 tokens. No turns in this thread yet." }, + { key: "Usage Limits", value: "not available" }, ], }, - { - title: "Usage Limits", - auditFacts: [{ key: "Status", value: "not available" }], - }, ]); expect(projection.modelStatusDetails()).toEqual([ { - title: "Model", auditFacts: [ { key: "Model", value: "gpt-5.5" }, { key: "Override", value: "(none)" }, @@ -57,7 +52,6 @@ describe("createChatPanelRuntimeProjection", () => { ]); expect(projection.effortStatusDetails()).toEqual([ { - title: "Reasoning", auditFacts: [ { key: "Effort", value: "high" }, { key: "Override", value: "(none)" }, diff --git a/tests/runtime/runtime-settings.test.ts b/tests/runtime/runtime-settings.test.ts index a8656154..0f152275 100644 --- a/tests/runtime/runtime-settings.test.ts +++ b/tests/runtime/runtime-settings.test.ts @@ -22,7 +22,7 @@ import { permissionProfileRequestForThreadStart, serviceTierRequestForThreadStart, } from "../../src/features/chat/domain/runtime/thread-settings-patch"; -import { contextSummary, rateLimitSummary } from "../../src/features/chat/presentation/runtime/status"; +import { contextSummary, rateLimitSummary, statusDetails } from "../../src/features/chat/presentation/runtime/status"; describe("runtime settings", () => { it("formats runtime override messages", () => { @@ -983,6 +983,30 @@ describe("runtime settings", () => { level: "warn", }); }); + + it("formats runtime status details as flat rows", () => { + expect( + statusDetails({ + activeThreadId: "thread", + snapshot: runtimeSnapshot({ + activeThreadId: "thread", + rateLimit: { + limitId: "codex", + limitName: "Codex", + primary: { usedPercent: 15, windowDurationMins: 300, resetsAt: null }, + secondary: { usedPercent: 38, windowDurationMins: 10_080, resetsAt: null }, + individualLimit: null, + rateLimitReachedType: null, + }, + }), + nowMs: 0, + }), + ).toEqual([ + { label: "Thread", value: "thread" }, + { label: "Context", value: "0 / 100,000 (0%). No turns in this thread yet." }, + { label: "Usage Limits", value: "5h 15%, 1w 38%" }, + ]); + }); }); interface RuntimeSnapshotPatch extends Partial> {