Flatten runtime slash command details

This commit is contained in:
murashit 2026-07-10 02:07:34 +09:00
parent b3a770d1b1
commit 37051838a8
5 changed files with 63 additions and 59 deletions

View file

@ -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(

View file

@ -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(

View file

@ -55,12 +55,12 @@ function context(overrides: Partial<SlashCommandExecutionContext> = {}): 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,

View file

@ -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)" },

View file

@ -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<Omit<RuntimeSnapshot, "active" | "pending">> {