mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 17:30:31 +00:00
Model app-server runtime config snapshots
This commit is contained in:
parent
0df65090ca
commit
01d1a855fc
27 changed files with 359 additions and 231 deletions
|
|
@ -112,9 +112,6 @@ const generatedAppServerImportLegacyFiles = [
|
|||
"src/features/chat/inbound/routing.ts",
|
||||
"src/features/chat/requests/approval.ts",
|
||||
"src/features/chat/requests/user-input.ts",
|
||||
"src/features/chat/runtime/config.ts",
|
||||
"src/features/chat/runtime/effective-settings.ts",
|
||||
"src/features/chat/runtime/state.ts",
|
||||
"src/features/chat/session/connection-controller.ts",
|
||||
"src/features/chat/threads/thread-goal-actions.ts",
|
||||
"src/features/chat/threads/thread-history-controller.ts",
|
||||
|
|
|
|||
200
src/app-server/runtime-config.ts
Normal file
200
src/app-server/runtime-config.ts
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
import type { ConfigReadResponse as AppServerConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse";
|
||||
import type { Config as AppServerConfig } from "../generated/app-server/v2/Config";
|
||||
import {
|
||||
appServerApprovalsReviewerOrNull,
|
||||
parseServiceTier,
|
||||
type ApprovalPolicy,
|
||||
type ApprovalsReviewer,
|
||||
type ServiceTier,
|
||||
} from "./thread-settings";
|
||||
import { isReasoningEffort, type ReasoningEffort } from "../domain/catalog/metadata";
|
||||
|
||||
type ReasoningSummary = "auto" | "concise" | "detailed" | "none";
|
||||
type Verbosity = "low" | "medium" | "high";
|
||||
type WebSearchMode = "disabled" | "cached" | "live";
|
||||
type SandboxMode = "read-only" | "workspace-write" | "danger-full-access";
|
||||
|
||||
export interface ActivePermissionProfile {
|
||||
id: string;
|
||||
extends: string | null;
|
||||
}
|
||||
|
||||
export interface RuntimeConfigSnapshot {
|
||||
profile: string | null;
|
||||
model: string | null;
|
||||
modelProvider: string | null;
|
||||
reasoningEffort: ReasoningEffort | null;
|
||||
rawReasoningEffort: string | null;
|
||||
reasoningSummary: ReasoningSummary | null;
|
||||
verbosity: Verbosity | null;
|
||||
serviceTier: ServiceTier | null;
|
||||
approvalsReviewer: ApprovalsReviewer | null;
|
||||
approvalPolicy: ApprovalPolicy | null;
|
||||
webSearch: WebSearchMode | null;
|
||||
modelContextWindow: number | null;
|
||||
autoCompactTokenLimit: number | null;
|
||||
sandboxMode: SandboxMode | null;
|
||||
workspaceNetworkAccess: boolean | null;
|
||||
writableRoots: readonly string[] | null;
|
||||
toolWebSearch: unknown;
|
||||
apps: unknown;
|
||||
}
|
||||
|
||||
export function emptyRuntimeConfigSnapshot(): RuntimeConfigSnapshot {
|
||||
return {
|
||||
profile: null,
|
||||
model: null,
|
||||
modelProvider: null,
|
||||
reasoningEffort: null,
|
||||
rawReasoningEffort: null,
|
||||
reasoningSummary: null,
|
||||
verbosity: null,
|
||||
serviceTier: null,
|
||||
approvalsReviewer: null,
|
||||
approvalPolicy: null,
|
||||
webSearch: null,
|
||||
modelContextWindow: null,
|
||||
autoCompactTokenLimit: null,
|
||||
sandboxMode: null,
|
||||
workspaceNetworkAccess: null,
|
||||
writableRoots: null,
|
||||
toolWebSearch: null,
|
||||
apps: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function runtimeConfigSnapshotFromAppServerConfig(response: AppServerConfigReadResponse): RuntimeConfigSnapshot {
|
||||
const config = asConfigRecord(response.config);
|
||||
const tools = asRecordOrNull(config.tools);
|
||||
const workspaceWrite = asRecordOrNull(config.sandbox_workspace_write);
|
||||
const effort = config.model_reasoning_effort;
|
||||
return {
|
||||
profile: selectedConfigProfile(response.layers),
|
||||
model: nonEmptyStringOrNull(config.model),
|
||||
modelProvider: nonEmptyStringOrNull(config.model_provider),
|
||||
reasoningEffort: isReasoningEffort(effort) ? effort : null,
|
||||
rawReasoningEffort: nonEmptyStringOrNull(effort),
|
||||
reasoningSummary: reasoningSummaryOrNull(config.model_reasoning_summary),
|
||||
verbosity: verbosityOrNull(config.model_verbosity),
|
||||
serviceTier: parseServiceTier(config.service_tier),
|
||||
approvalsReviewer: appServerApprovalsReviewerOrNull(config.approvals_reviewer),
|
||||
approvalPolicy: approvalPolicyOrNull(config.approval_policy),
|
||||
webSearch: webSearchModeOrNull(config.web_search),
|
||||
modelContextWindow: numberOrNull(config.model_context_window),
|
||||
autoCompactTokenLimit: numberOrNull(config.model_auto_compact_token_limit),
|
||||
sandboxMode: sandboxModeOrNull(config.sandbox_mode),
|
||||
workspaceNetworkAccess: booleanOrNull(workspaceWrite?.["network_access"]),
|
||||
writableRoots: stringArrayOrNull(workspaceWrite?.["writable_roots"]),
|
||||
toolWebSearch: cloneJsonLike(asRecordOrNull(tools?.["web_search"])),
|
||||
apps: cloneJsonLike(asRecordOrNull(config.apps)),
|
||||
};
|
||||
}
|
||||
|
||||
export function cloneRuntimeConfigSnapshot(config: RuntimeConfigSnapshot): RuntimeConfigSnapshot {
|
||||
return {
|
||||
...config,
|
||||
approvalPolicy: cloneApprovalPolicy(config.approvalPolicy),
|
||||
writableRoots: config.writableRoots ? [...config.writableRoots] : null,
|
||||
toolWebSearch: cloneJsonLike(config.toolWebSearch),
|
||||
apps: cloneJsonLike(config.apps),
|
||||
};
|
||||
}
|
||||
|
||||
type ConfigProjectionRecord = Partial<AppServerConfig> & Record<string, unknown>;
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
|
||||
}
|
||||
|
||||
function asRecordOrNull(value: unknown): Record<string, unknown> | null {
|
||||
return value && typeof value === "object" ? (value as Record<string, unknown>) : null;
|
||||
}
|
||||
|
||||
function asConfigRecord(value: unknown): ConfigProjectionRecord {
|
||||
return asRecord(value) as ConfigProjectionRecord;
|
||||
}
|
||||
|
||||
function selectedConfigProfile(layers: AppServerConfigReadResponse["layers"]): string | null {
|
||||
let selected: string | null = null;
|
||||
if (!layers) return null;
|
||||
for (const layer of layers) {
|
||||
const name = layer.name;
|
||||
if (name.type === "user" && typeof name.profile === "string" && name.profile.length > 0) {
|
||||
selected = name.profile;
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
function nonEmptyStringOrNull(value: unknown): string | null {
|
||||
return typeof value === "string" && value.length > 0 ? 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;
|
||||
}
|
||||
|
||||
function booleanOrNull(value: unknown): boolean | null {
|
||||
return typeof value === "boolean" ? value : null;
|
||||
}
|
||||
|
||||
function stringArrayOrNull(value: unknown): string[] | null {
|
||||
return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : null;
|
||||
}
|
||||
|
||||
function reasoningSummaryOrNull(value: unknown): ReasoningSummary | null {
|
||||
return value === "auto" || value === "concise" || value === "detailed" || value === "none" ? value : null;
|
||||
}
|
||||
|
||||
function verbosityOrNull(value: unknown): Verbosity | null {
|
||||
return value === "low" || value === "medium" || value === "high" ? value : null;
|
||||
}
|
||||
|
||||
function webSearchModeOrNull(value: unknown): WebSearchMode | null {
|
||||
return value === "disabled" || value === "cached" || value === "live" ? value : null;
|
||||
}
|
||||
|
||||
function sandboxModeOrNull(value: unknown): SandboxMode | null {
|
||||
return value === "read-only" || value === "workspace-write" || value === "danger-full-access" ? value : null;
|
||||
}
|
||||
|
||||
function approvalPolicyOrNull(value: unknown): ApprovalPolicy | null {
|
||||
if (value === "untrusted" || value === "on-failure" || value === "on-request" || value === "never") return value;
|
||||
const granular = asRecordOrNull(asRecordOrNull(value)?.["granular"]);
|
||||
if (!granular) return null;
|
||||
const sandboxApproval = granular["sandbox_approval"];
|
||||
const rules = granular["rules"];
|
||||
const skillApproval = granular["skill_approval"];
|
||||
const requestPermissions = granular["request_permissions"];
|
||||
const mcpElicitations = granular["mcp_elicitations"];
|
||||
if (
|
||||
typeof sandboxApproval !== "boolean" ||
|
||||
typeof rules !== "boolean" ||
|
||||
typeof skillApproval !== "boolean" ||
|
||||
typeof requestPermissions !== "boolean" ||
|
||||
typeof mcpElicitations !== "boolean"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
granular: {
|
||||
sandbox_approval: sandboxApproval,
|
||||
rules,
|
||||
skill_approval: skillApproval,
|
||||
request_permissions: requestPermissions,
|
||||
mcp_elicitations: mcpElicitations,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function cloneApprovalPolicy(value: ApprovalPolicy | null): ApprovalPolicy | null {
|
||||
return value && typeof value === "object" ? { granular: { ...value.granular } } : value;
|
||||
}
|
||||
|
||||
function cloneJsonLike(value: unknown): unknown {
|
||||
if (Array.isArray(value)) return value.map(cloneJsonLike);
|
||||
if (!value || typeof value !== "object") return value;
|
||||
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneJsonLike(item)]));
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import type { Diagnostics } from "./diagnostics";
|
||||
import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse";
|
||||
import type { RateLimitSnapshot } from "./runtime-metrics";
|
||||
import { cloneRuntimeConfigSnapshot, type RuntimeConfigSnapshot } from "./runtime-config";
|
||||
import type { Thread } from "../domain/threads/model";
|
||||
import type { ModelMetadata, SkillMetadata } from "../domain/catalog/metadata";
|
||||
|
||||
export interface SharedAppServerMetadata {
|
||||
effectiveConfig: ConfigReadResponse | null;
|
||||
runtimeConfig: RuntimeConfigSnapshot | null;
|
||||
availableModels: readonly ModelMetadata[];
|
||||
availableSkills: readonly SkillMetadata[];
|
||||
rateLimit: RateLimitSnapshot | null;
|
||||
|
|
@ -72,6 +72,7 @@ export function cachedSharedModels(state: SharedAppServerState): ModelMetadata[]
|
|||
function cloneSharedAppServerMetadata(metadata: SharedAppServerMetadata): SharedAppServerMetadata {
|
||||
return {
|
||||
...metadata,
|
||||
runtimeConfig: metadata.runtimeConfig ? cloneRuntimeConfigSnapshot(metadata.runtimeConfig) : null,
|
||||
availableModels: cloneModelMetadata(metadata.availableModels),
|
||||
availableSkills: cloneSkillMetadata(metadata.availableSkills),
|
||||
appServerDiagnostics: {
|
||||
|
|
|
|||
|
|
@ -3,16 +3,29 @@ import type { ModeKind } from "../generated/app-server/ModeKind";
|
|||
import type { Personality } from "../generated/app-server/Personality";
|
||||
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
|
||||
import type { ReasoningSummary } from "../generated/app-server/ReasoningSummary";
|
||||
import type { AskForApproval } from "../generated/app-server/v2/AskForApproval";
|
||||
import type { SandboxPolicy } from "../generated/app-server/v2/SandboxPolicy";
|
||||
|
||||
export type ApprovalsReviewer = "user" | "auto_review" | "guardian_subagent";
|
||||
export type ApprovalPolicy =
|
||||
| "untrusted"
|
||||
| "on-failure"
|
||||
| "on-request"
|
||||
| {
|
||||
granular: {
|
||||
sandbox_approval: boolean;
|
||||
rules: boolean;
|
||||
skill_approval: boolean;
|
||||
request_permissions: boolean;
|
||||
mcp_elicitations: boolean;
|
||||
};
|
||||
}
|
||||
| "never";
|
||||
export type ServiceTier = string;
|
||||
export type ServiceTierRequest = string | null | undefined;
|
||||
|
||||
export interface ThreadSettingsUpdate {
|
||||
cwd?: string | null;
|
||||
approvalPolicy?: AskForApproval | null;
|
||||
approvalPolicy?: ApprovalPolicy | null;
|
||||
approvalsReviewer?: ApprovalsReviewer | null;
|
||||
sandboxPolicy?: SandboxPolicy | null;
|
||||
permissions?: string | null;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import type { InitializeResponse } from "../../generated/app-server/InitializeResponse";
|
||||
import type { ReasoningEffort } from "../../domain/catalog/metadata";
|
||||
import type { ConfigReadResponse } from "../../generated/app-server/v2/ConfigReadResponse";
|
||||
import type { Thread } from "../../domain/threads/model";
|
||||
import type { ModelMetadata, SkillMetadata } from "../../domain/catalog/metadata";
|
||||
import type { ThreadGoal } from "../../generated/app-server/v2/ThreadGoal";
|
||||
import type { ThreadSettingsUpdate } from "../../app-server/thread-settings";
|
||||
import type { Diagnostics } from "../../app-server/diagnostics";
|
||||
import { createAppServerDiagnostics } from "../../app-server/diagnostics";
|
||||
import type { RuntimeConfigSnapshot } from "../../app-server/runtime-config";
|
||||
import type { RateLimitSnapshot, ThreadTokenUsage } from "../../app-server/runtime-metrics";
|
||||
import type { ApprovalsReviewer } from "./runtime/approvals";
|
||||
import type { CollaborationMode } from "./runtime/collaboration";
|
||||
|
|
@ -68,7 +68,7 @@ export type { ChatTranscriptState } from "./transcript-state";
|
|||
|
||||
interface ChatConnectionState {
|
||||
status: string;
|
||||
effectiveConfig: ConfigReadResponse | null;
|
||||
runtimeConfig: RuntimeConfigSnapshot | null;
|
||||
initializeResponse: InitializeResponse | null;
|
||||
rateLimit: RateLimitSnapshot | null;
|
||||
appServerDiagnostics: Diagnostics;
|
||||
|
|
@ -125,7 +125,7 @@ type ConnectionAction =
|
|||
| ConnectionInitializedAction
|
||||
| {
|
||||
type: "connection/metadata-applied";
|
||||
effectiveConfig?: ConfigReadResponse | null;
|
||||
runtimeConfig?: RuntimeConfigSnapshot | null;
|
||||
availableModels?: readonly ModelMetadata[];
|
||||
availableSkills?: readonly SkillMetadata[];
|
||||
rateLimit?: RateLimitSnapshot | null;
|
||||
|
|
@ -512,7 +512,7 @@ function reduceConnectionSlice(state: ChatConnectionState, action: ChatSliceActi
|
|||
return patchObject(state, { initializeResponse: action.initializeResponse });
|
||||
case "connection/metadata-applied":
|
||||
return patchObject(state, {
|
||||
...definedPatch("effectiveConfig", action.effectiveConfig),
|
||||
...definedPatch("runtimeConfig", action.runtimeConfig),
|
||||
...definedPatch("availableModels", action.availableModels),
|
||||
...definedPatch("availableSkills", action.availableSkills),
|
||||
...definedPatch("rateLimit", action.rateLimit),
|
||||
|
|
@ -643,7 +643,7 @@ function clearDisconnectedConnectionState(state: ChatState): ChatState {
|
|||
function initialConnectionState(): ChatConnectionState {
|
||||
return {
|
||||
status: "Idle",
|
||||
effectiveConfig: null,
|
||||
runtimeConfig: null,
|
||||
initializeResponse: null,
|
||||
appServerDiagnostics: createAppServerDiagnostics(),
|
||||
rateLimit: null,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { readRuntimeConfig } from "../../runtime/config";
|
||||
import { runtimeConfigOrDefault } from "../../runtime/config";
|
||||
import { autoReviewActive, currentModel, currentReasoningEffort, fastModeActive } from "../../runtime/effective-settings";
|
||||
import { compactReasoningEffortLabel } from "../../runtime/override-commands";
|
||||
import { contextSummary } from "../../runtime/status-summary";
|
||||
|
|
@ -24,7 +24,7 @@ export function composerMetaViewModel(state: ChatState, snapshot: ReturnType<typ
|
|||
};
|
||||
}
|
||||
|
||||
const config = readRuntimeConfig(state.connection.effectiveConfig);
|
||||
const config = runtimeConfigOrDefault(state.connection.runtimeConfig);
|
||||
const context = contextSummary(snapshot);
|
||||
const model = currentModel(snapshot, config);
|
||||
const effort = currentReasoningEffort(snapshot, config);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { readRuntimeConfig } from "../../runtime/config";
|
||||
import { runtimeConfigOrDefault } from "../../runtime/config";
|
||||
import {
|
||||
currentModel,
|
||||
currentReasoningEffort,
|
||||
|
|
@ -19,7 +19,7 @@ import type {
|
|||
|
||||
export function runtimeSnapshotForChatSlices(input: RuntimeSnapshotInput) {
|
||||
return {
|
||||
effectiveConfig: input.effectiveConfig,
|
||||
runtimeConfig: input.runtimeConfig,
|
||||
activeThreadId: input.activeThread.id,
|
||||
activeModel: input.runtime.activeModel,
|
||||
activeReasoningEffort: input.runtime.activeReasoningEffort,
|
||||
|
|
@ -44,7 +44,7 @@ export function runtimeComposerChoices(input: RuntimeComposerChoicesInput): {
|
|||
modelChoices: RuntimeChoice[];
|
||||
effortChoices: RuntimeChoice[];
|
||||
} {
|
||||
const config = readRuntimeConfig(input.state.connection.effectiveConfig);
|
||||
const config = runtimeConfigOrDefault(input.state.connection.runtimeConfig);
|
||||
const activeModel = currentModel(input.snapshot, config);
|
||||
const models = sortedModelMetadata(input.state.connection.availableModels);
|
||||
const modelChoices: RuntimeChoice[] = models.slice(0, 12).map((model) => ({
|
||||
|
|
@ -86,7 +86,7 @@ export function statusSummaryLines(input: StatusSummaryLinesInput): string[] {
|
|||
}
|
||||
|
||||
export function modelStatusLines(input: ModelStatusLinesInput): string[] {
|
||||
const config = readRuntimeConfig(input.effectiveConfig);
|
||||
const config = runtimeConfigOrDefault(input.runtimeConfig);
|
||||
return [
|
||||
`Model: ${currentModel(input.snapshot, config) ?? "(Codex default)"}`,
|
||||
`Override: ${pendingRuntimeSettingLabel(input.requestedModel)}`,
|
||||
|
|
@ -98,7 +98,7 @@ export function modelStatusLines(input: ModelStatusLinesInput): string[] {
|
|||
}
|
||||
|
||||
export function effortStatusLines(input: EffortStatusLinesInput): string[] {
|
||||
const config = readRuntimeConfig(input.effectiveConfig);
|
||||
const config = runtimeConfigOrDefault(input.runtimeConfig);
|
||||
return [
|
||||
`Effort: ${currentReasoningEffort(input.snapshot, config) ?? "(Codex default)"}`,
|
||||
`Override: ${pendingRuntimeSettingLabel(input.requestedReasoningEffort)}`,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { Thread } from "../../../../domain/threads/model";
|
||||
import { getThreadTitle } from "../../../../domain/threads/model";
|
||||
import { effectiveConfigSections, rateLimitSummary } from "../../runtime/status-summary";
|
||||
import { runtimeConfigSections, rateLimitSummary } from "../../runtime/status-summary";
|
||||
import { connectionDiagnosticSections } from "../../diagnostics";
|
||||
import type { ConnectionDiagnosticsModelInput, ToolbarThreadRow, ToolbarViewModel, ToolbarViewModelInput } from "./types";
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ export function toolbarViewModel(input: ToolbarViewModelInput): ToolbarViewModel
|
|||
historyOpen,
|
||||
statusPanelOpen,
|
||||
rateLimit: limit,
|
||||
configSections: effectiveConfigSections(snapshot, input.vaultPath),
|
||||
configSections: runtimeConfigSections(snapshot, input.vaultPath),
|
||||
openPanel: historyOpen ? "history" : chatActionsOpen ? "chat-actions" : statusPanelOpen ? "status" : null,
|
||||
threads: toolbarThreadRows({
|
||||
threads: state.threadList.listedThreads,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { ReasoningEffort } from "../../../../domain/catalog/metadata";
|
||||
import type { RuntimeSnapshot } from "../../runtime/effective-settings";
|
||||
import type { EffectiveConfigSection, RateLimitSummary } from "../../runtime/status-summary";
|
||||
import type { RuntimeConfigSection, RateLimitSummary } from "../../runtime/status-summary";
|
||||
import type { ChatState } from "../../chat-state";
|
||||
|
||||
export interface RuntimeSnapshotInput {
|
||||
effectiveConfig: ChatState["connection"]["effectiveConfig"];
|
||||
runtimeConfig: ChatState["connection"]["runtimeConfig"];
|
||||
activeThread: Pick<ChatState["activeThread"], "id" | "tokenUsage">;
|
||||
runtime: ChatState["runtime"];
|
||||
rateLimit: ChatState["connection"]["rateLimit"];
|
||||
|
|
@ -73,7 +73,7 @@ export interface ToolbarViewModel {
|
|||
historyOpen: boolean;
|
||||
statusPanelOpen: boolean;
|
||||
rateLimit: RateLimitSummary | null;
|
||||
configSections: EffectiveConfigSection[];
|
||||
configSections: RuntimeConfigSection[];
|
||||
openPanel: "history" | "chat-actions" | "status" | null;
|
||||
threads: ToolbarThreadRow[];
|
||||
connectLabel: string;
|
||||
|
|
@ -111,14 +111,14 @@ export interface StatusSummaryLinesInput {
|
|||
}
|
||||
|
||||
export interface ModelStatusLinesInput {
|
||||
effectiveConfig: ChatState["connection"]["effectiveConfig"];
|
||||
runtimeConfig: ChatState["connection"]["runtimeConfig"];
|
||||
requestedModel: ChatState["runtime"]["requestedModel"];
|
||||
snapshot: RuntimeSnapshot;
|
||||
collaborationModeLabel: string;
|
||||
}
|
||||
|
||||
export interface EffortStatusLinesInput {
|
||||
effectiveConfig: ChatState["connection"]["effectiveConfig"];
|
||||
runtimeConfig: ChatState["connection"]["runtimeConfig"];
|
||||
requestedReasoningEffort: ChatState["runtime"]["requestedReasoningEffort"];
|
||||
snapshot: RuntimeSnapshot;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { Thread } from "../../../domain/threads/model";
|
||||
import type { OpenCodexPanelSnapshot } from "../../../workspace/open-panel-snapshot";
|
||||
import { readRuntimeConfig } from "../runtime/config";
|
||||
import { runtimeConfigOrDefault } from "../runtime/config";
|
||||
import { currentModel } from "../runtime/effective-settings";
|
||||
import { activeTurnId, chatTurnBusy, type ChatState } from "../chat-state";
|
||||
import type { DisplayItem } from "../display/types";
|
||||
|
|
@ -41,7 +41,7 @@ export function toolbarSlotSnapshot(state: ChatState, connected: boolean): ChatP
|
|||
state.threadList.threadsLoaded,
|
||||
threadListSignature(state.threadList.listedThreads),
|
||||
modelsSignature(state.connection.availableModels),
|
||||
state.connection.effectiveConfig,
|
||||
state.connection.runtimeConfig,
|
||||
state.connection.rateLimit,
|
||||
state.activeThread.tokenUsage,
|
||||
state.connection.appServerDiagnostics,
|
||||
|
|
@ -87,17 +87,17 @@ export function composerSlotSnapshot(state: ChatState, activeComposerThreadName:
|
|||
state.runtime.requestedModel,
|
||||
state.runtime.requestedReasoningEffort,
|
||||
state.activeThread.tokenUsage,
|
||||
state.connection.effectiveConfig,
|
||||
state.connection.runtimeConfig,
|
||||
currentModel(
|
||||
runtimeSnapshotForChatSlices({
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
activeThread: state.activeThread,
|
||||
runtime: state.runtime,
|
||||
rateLimit: state.connection.rateLimit,
|
||||
displayItems: state.transcript.displayItems,
|
||||
availableModels: state.connection.availableModels,
|
||||
}),
|
||||
readRuntimeConfig(state.connection.effectiveConfig),
|
||||
runtimeConfigOrDefault(state.connection.runtimeConfig),
|
||||
),
|
||||
state.connection.availableSkills.length,
|
||||
skillsSignature(state.connection.availableSkills),
|
||||
|
|
|
|||
|
|
@ -1,93 +1,7 @@
|
|||
import type { ReasoningSummary } from "../../../generated/app-server/ReasoningSummary";
|
||||
import type { Verbosity } from "../../../generated/app-server/Verbosity";
|
||||
import type { WebSearchMode } from "../../../generated/app-server/WebSearchMode";
|
||||
import type { ConfigReadResponse } from "../../../generated/app-server/v2/ConfigReadResponse";
|
||||
import type { AppsConfig } from "../../../generated/app-server/v2/AppsConfig";
|
||||
import type { AskForApproval } from "../../../generated/app-server/v2/AskForApproval";
|
||||
import type { Config } from "../../../generated/app-server/v2/Config";
|
||||
import type { SandboxMode } from "../../../generated/app-server/v2/SandboxMode";
|
||||
import type { SandboxWorkspaceWrite } from "../../../generated/app-server/v2/SandboxWorkspaceWrite";
|
||||
import type { ToolsV2 } from "../../../generated/app-server/v2/ToolsV2";
|
||||
import { parseServiceTier, type ServiceTier } from "../../../app-server/thread-settings";
|
||||
import { isReasoningEffort, type ReasoningEffort } from "../../../domain/catalog/metadata";
|
||||
import { approvalsReviewerOrNull, type ApprovalsReviewer } from "./approvals";
|
||||
import { cloneRuntimeConfigSnapshot, emptyRuntimeConfigSnapshot, type RuntimeConfigSnapshot } from "../../../app-server/runtime-config";
|
||||
|
||||
export interface RuntimeConfigProjection {
|
||||
profile: string | null;
|
||||
model: string | null;
|
||||
modelProvider: string | null;
|
||||
reasoningEffort: ReasoningEffort | null;
|
||||
rawReasoningEffort: string | null;
|
||||
reasoningSummary: ReasoningSummary | null;
|
||||
verbosity: Verbosity | null;
|
||||
serviceTier: ServiceTier | null;
|
||||
approvalsReviewer: ApprovalsReviewer | null;
|
||||
approvalPolicy: AskForApproval | null;
|
||||
webSearch: WebSearchMode | null;
|
||||
modelContextWindow: number | null;
|
||||
autoCompactTokenLimit: number | null;
|
||||
sandboxMode: SandboxMode | null;
|
||||
workspaceNetworkAccess: SandboxWorkspaceWrite["network_access"] | null;
|
||||
writableRoots: SandboxWorkspaceWrite["writable_roots"] | null;
|
||||
toolWebSearch: ToolsV2["web_search"] | null;
|
||||
apps: AppsConfig | null;
|
||||
}
|
||||
|
||||
export function readRuntimeConfig(effectiveConfig: ConfigReadResponse | null): RuntimeConfigProjection {
|
||||
const config = asConfigRecord(effectiveConfig?.config);
|
||||
const tools = config.tools ?? null;
|
||||
const workspaceWrite = config.sandbox_workspace_write ?? null;
|
||||
const effort = config.model_reasoning_effort;
|
||||
return {
|
||||
profile: selectedConfigProfile(effectiveConfig?.layers ?? null),
|
||||
model: nonEmptyStringOrNull(config.model),
|
||||
modelProvider: config.model_provider ?? null,
|
||||
reasoningEffort: isReasoningEffort(effort) ? effort : null,
|
||||
rawReasoningEffort: nonEmptyStringOrNull(effort),
|
||||
reasoningSummary: config.model_reasoning_summary ?? null,
|
||||
verbosity: config.model_verbosity ?? null,
|
||||
serviceTier: parseServiceTier(config.service_tier),
|
||||
approvalsReviewer: approvalsReviewerOrNull(config.approvals_reviewer),
|
||||
approvalPolicy: config.approval_policy ?? null,
|
||||
webSearch: config.web_search ?? null,
|
||||
modelContextWindow: numberOrNull(config.model_context_window),
|
||||
autoCompactTokenLimit: numberOrNull(config.model_auto_compact_token_limit),
|
||||
sandboxMode: config.sandbox_mode ?? null,
|
||||
workspaceNetworkAccess: workspaceWrite?.network_access ?? null,
|
||||
writableRoots: workspaceWrite?.writable_roots ?? null,
|
||||
toolWebSearch: tools?.web_search ?? null,
|
||||
apps: config.apps ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
type ConfigProjectionRecord = Partial<Config> & Record<string, unknown>;
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
|
||||
}
|
||||
|
||||
function asConfigRecord(value: unknown): ConfigProjectionRecord {
|
||||
return asRecord(value) as ConfigProjectionRecord;
|
||||
}
|
||||
|
||||
function selectedConfigProfile(layers: ConfigReadResponse["layers"]): string | null {
|
||||
let selected: string | null = null;
|
||||
if (!layers) return null;
|
||||
for (const layer of layers) {
|
||||
const name = layer.name;
|
||||
if (name.type === "user" && typeof name.profile === "string" && name.profile.length > 0) {
|
||||
selected = name.profile;
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
function nonEmptyStringOrNull(value: unknown): string | null {
|
||||
return typeof value === "string" && value.length > 0 ? 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;
|
||||
export type RuntimeConfigProjection = RuntimeConfigSnapshot;
|
||||
|
||||
export function runtimeConfigOrDefault(runtimeConfig: RuntimeConfigSnapshot | null): RuntimeConfigProjection {
|
||||
return runtimeConfig ? cloneRuntimeConfigSnapshot(runtimeConfig) : emptyRuntimeConfigSnapshot();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import type { ActivePermissionProfile } from "../../../generated/app-server/v2/ActivePermissionProfile";
|
||||
import type { AskForApproval } from "../../../generated/app-server/v2/AskForApproval";
|
||||
import type { ConfigReadResponse } from "../../../generated/app-server/v2/ConfigReadResponse";
|
||||
import type { ActivePermissionProfile, RuntimeConfigSnapshot } from "../../../app-server/runtime-config";
|
||||
import type { RateLimitSnapshot, ThreadTokenUsage } from "../../../app-server/runtime-metrics";
|
||||
import { findModelMetadataByIdOrName, type ModelMetadata } from "../../../domain/catalog/metadata";
|
||||
import {
|
||||
|
|
@ -9,9 +7,10 @@ import {
|
|||
serviceTierRequestValue,
|
||||
type ServiceTier,
|
||||
type ServiceTierRequest,
|
||||
type ApprovalPolicy,
|
||||
} from "../../../app-server/thread-settings";
|
||||
import { supportedEffortsForModelMetadata, type ReasoningEffort } from "../../../domain/catalog/metadata";
|
||||
import { readRuntimeConfig, type RuntimeConfigProjection } from "./config";
|
||||
import { runtimeConfigOrDefault, type RuntimeConfigProjection } from "./config";
|
||||
import type { CollaborationMode } from "./collaboration";
|
||||
import { isAutoReviewReviewer, type ApprovalsReviewer } from "./approvals";
|
||||
import { isFastServiceTier, type RequestedServiceTier } from "./service-tier-state";
|
||||
|
|
@ -19,13 +18,13 @@ import { isFastServiceTier, type RequestedServiceTier } from "./service-tier-sta
|
|||
export type PendingRuntimeSetting<T> = { kind: "unchanged" } | { kind: "set"; value: T } | { kind: "resetToConfig" };
|
||||
|
||||
export interface RuntimeSnapshot {
|
||||
effectiveConfig: ConfigReadResponse | null;
|
||||
runtimeConfig: RuntimeConfigSnapshot | null;
|
||||
activeThreadId: string | null;
|
||||
activeModel: string | null;
|
||||
activeReasoningEffort: ReasoningEffort | null;
|
||||
activeCollaborationMode: CollaborationMode;
|
||||
activeServiceTier: ServiceTier | null;
|
||||
activeApprovalPolicy: AskForApproval | null;
|
||||
activeApprovalPolicy: ApprovalPolicy | null;
|
||||
activeApprovalsReviewer: ApprovalsReviewer | null;
|
||||
activePermissionProfile: ActivePermissionProfile | null;
|
||||
requestedModel: PendingRuntimeSetting<string>;
|
||||
|
|
@ -41,7 +40,7 @@ export interface RuntimeSnapshot {
|
|||
|
||||
export function currentServiceTier(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): string | null {
|
||||
if (snapshot.requestedServiceTier.kind === "set" && snapshot.requestedServiceTier.value === "fast") return "fast";
|
||||
if (snapshot.requestedServiceTier.kind === "set" && snapshot.requestedServiceTier.value === "off") return null;
|
||||
|
|
@ -51,7 +50,7 @@ export function currentServiceTier(
|
|||
|
||||
export function requestedOrConfiguredServiceTier(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): ServiceTierRequest {
|
||||
if (snapshot.requestedServiceTier.kind === "set") {
|
||||
return snapshot.requestedServiceTier.value === "fast"
|
||||
|
|
@ -66,7 +65,7 @@ export function requestedOrConfiguredServiceTier(
|
|||
|
||||
export function currentModel(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): string | null {
|
||||
const configModel = config.model;
|
||||
if (snapshot.requestedModel.kind === "set") return snapshot.requestedModel.value;
|
||||
|
|
@ -76,7 +75,7 @@ export function currentModel(
|
|||
|
||||
export function currentReasoningEffort(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): ReasoningEffort | null {
|
||||
if (snapshot.requestedReasoningEffort.kind === "set") return snapshot.requestedReasoningEffort.value;
|
||||
if (snapshot.requestedReasoningEffort.kind === "resetToConfig") return config.reasoningEffort;
|
||||
|
|
@ -85,7 +84,7 @@ export function currentReasoningEffort(
|
|||
|
||||
export function currentApprovalsReviewer(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): ApprovalsReviewer | null {
|
||||
if (snapshot.requestedApprovalsReviewer.kind === "set") return snapshot.requestedApprovalsReviewer.value;
|
||||
if (snapshot.requestedApprovalsReviewer.kind === "resetToConfig") return config.approvalsReviewer;
|
||||
|
|
@ -94,14 +93,14 @@ export function currentApprovalsReviewer(
|
|||
|
||||
export function currentApprovalPolicy(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
): AskForApproval | null {
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): ApprovalPolicy | null {
|
||||
return snapshot.activeApprovalPolicy ?? config.approvalPolicy;
|
||||
}
|
||||
|
||||
export function autoReviewActive(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): boolean {
|
||||
return isAutoReviewReviewer(currentApprovalsReviewer(snapshot, config));
|
||||
}
|
||||
|
|
@ -113,21 +112,21 @@ export function supportedReasoningEfforts(snapshot: RuntimeSnapshot): ReasoningE
|
|||
|
||||
export function serviceTierLabel(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): string {
|
||||
return currentServiceTier(snapshot, config) ?? "(Codex default)";
|
||||
}
|
||||
|
||||
export function fastModeActive(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): boolean {
|
||||
return isFastServiceTier(currentServiceTier(snapshot, config), currentModelServiceTiers(snapshot, config));
|
||||
}
|
||||
|
||||
export function fastModeLabel(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): string {
|
||||
if (snapshot.requestedServiceTier.kind === "set" && snapshot.requestedServiceTier.value === "off") return "off";
|
||||
if (fastModeActive(snapshot, config)) return "on";
|
||||
|
|
@ -137,7 +136,7 @@ export function fastModeLabel(
|
|||
|
||||
export function fastServiceTierRequestValue(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): string {
|
||||
return currentModelServiceTiers(snapshot, config).find((tier) => tier.name.trim().toLowerCase() === "fast")?.id ?? "fast";
|
||||
}
|
||||
|
|
@ -168,7 +167,7 @@ export function pendingRuntimeSettingLabel<T>(setting: PendingRuntimeSetting<T>)
|
|||
|
||||
function currentModelServiceTiers(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): ModelMetadata["serviceTiers"] {
|
||||
return findModelMetadataByIdOrName(snapshot.availableModels, currentModel(snapshot, config))?.serviceTiers ?? [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { ReasoningEffort } from "../../../domain/catalog/metadata";
|
|||
import { autoReviewReviewerForState, autoReviewToggleMessage, nextAutoReviewState } from "./approvals";
|
||||
import type { CollaborationMode } from "./collaboration";
|
||||
import { collaborationModeToggleMessage, nextCollaborationMode } from "./collaboration";
|
||||
import { readRuntimeConfig } from "./config";
|
||||
import { runtimeConfigOrDefault } from "./config";
|
||||
import { autoReviewActive, fastModeActive, type RuntimeSnapshot } from "./effective-settings";
|
||||
import { pendingThreadSettingsUpdate as buildPendingThreadSettingsUpdate, type TurnCollaborationModeWarning } from "./turn-settings";
|
||||
import type { ThreadSettingsUpdate } from "../../../app-server/thread-settings";
|
||||
|
|
@ -106,7 +106,7 @@ async function setRequestedReasoningEffortFromUi(host: RuntimeSettingsActionsHos
|
|||
|
||||
async function toggleFastMode(host: RuntimeSettingsActionsHost): Promise<void> {
|
||||
const snapshot = host.runtimeSnapshot();
|
||||
const config = readRuntimeConfig(state(host).connection.effectiveConfig);
|
||||
const config = runtimeConfigOrDefault(state(host).connection.runtimeConfig);
|
||||
const next: RequestedServiceTier = fastModeActive(snapshot, config) ? "off" : "fast";
|
||||
dispatch(host, { type: "runtime/requested-service-tier-set", serviceTier: next });
|
||||
dispatch(host, { type: "ui/panel-set", panel: null });
|
||||
|
|
@ -129,7 +129,7 @@ async function setCollaborationMode(host: RuntimeSettingsActionsHost, collaborat
|
|||
|
||||
async function toggleAutoReview(host: RuntimeSettingsActionsHost): Promise<void> {
|
||||
const nextState = nextAutoReviewState(
|
||||
autoReviewActive(host.runtimeSnapshot(), readRuntimeConfig(state(host).connection.effectiveConfig)),
|
||||
autoReviewActive(host.runtimeSnapshot(), runtimeConfigOrDefault(state(host).connection.runtimeConfig)),
|
||||
);
|
||||
dispatch(host, { type: "runtime/requested-approvals-reviewer-set", approvalsReviewer: autoReviewReviewerForState(nextState) });
|
||||
dispatch(host, { type: "ui/panel-set", panel: null });
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import type { ActivePermissionProfile } from "../../../generated/app-server/v2/ActivePermissionProfile";
|
||||
import type { AskForApproval } from "../../../generated/app-server/v2/AskForApproval";
|
||||
import { parseServiceTier, type ServiceTier, type ThreadSettingsUpdate } from "../../../app-server/thread-settings";
|
||||
import type { ActivePermissionProfile } from "../../../app-server/runtime-config";
|
||||
import { parseServiceTier, type ApprovalPolicy, type ServiceTier, type ThreadSettingsUpdate } from "../../../app-server/thread-settings";
|
||||
import type { CollaborationMode } from "./collaboration";
|
||||
import type { ReasoningEffort } from "../../../domain/catalog/metadata";
|
||||
import type { ApprovalsReviewer } from "./approvals";
|
||||
|
|
@ -17,7 +16,7 @@ export interface ChatRuntimeState {
|
|||
activeReasoningEffort: ReasoningEffort | null;
|
||||
activeCollaborationMode: CollaborationMode;
|
||||
activeServiceTier: ServiceTier | null;
|
||||
activeApprovalPolicy: AskForApproval | null;
|
||||
activeApprovalPolicy: ApprovalPolicy | null;
|
||||
activeApprovalsReviewer: ApprovalsReviewer | null;
|
||||
activePermissionProfile: ActivePermissionProfile | null;
|
||||
requestedModel: PendingRuntimeSetting<string>;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { RateLimitWindow, SpendControlLimitSnapshot, ThreadTokenUsage } fro
|
|||
import { jsonPreview } from "../../../utils";
|
||||
import { sortedModelMetadata } from "../../../domain/catalog/metadata";
|
||||
import { defaultEffortForModelMetadata } from "../../../domain/catalog/metadata";
|
||||
import { readRuntimeConfig, type RuntimeConfigProjection } from "./config";
|
||||
import { runtimeConfigOrDefault, type RuntimeConfigProjection } from "./config";
|
||||
import {
|
||||
currentApprovalsReviewer,
|
||||
currentApprovalPolicy,
|
||||
|
|
@ -38,7 +38,7 @@ interface RateLimitSummaryRow {
|
|||
level: "ok" | "warn" | "danger";
|
||||
}
|
||||
|
||||
export interface EffectiveConfigSection {
|
||||
export interface RuntimeConfigSection {
|
||||
title: string;
|
||||
rows: { key: string; value: string }[];
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ const NOT_REPORTED_LABEL = "(not reported)";
|
|||
|
||||
export function contextSummary(snapshot: RuntimeSnapshot): ContextSummary | null {
|
||||
const usage = snapshot.tokenUsage;
|
||||
const config = readRuntimeConfig(snapshot.effectiveConfig);
|
||||
const config = runtimeConfigOrDefault(snapshot.runtimeConfig);
|
||||
const contextWindow = usage?.modelContextWindow ?? config.modelContextWindow;
|
||||
if (!usage) {
|
||||
if (!snapshot.activeThreadId) return null;
|
||||
|
|
@ -107,8 +107,8 @@ export function rateLimitSummary(snapshot: RuntimeSnapshot, nowMs = Date.now()):
|
|||
};
|
||||
}
|
||||
|
||||
export function effectiveConfigSections(snapshot: RuntimeSnapshot, vaultPath: string): EffectiveConfigSection[] {
|
||||
const config = readRuntimeConfig(snapshot.effectiveConfig);
|
||||
export function runtimeConfigSections(snapshot: RuntimeSnapshot, vaultPath: string): RuntimeConfigSection[] {
|
||||
const config = runtimeConfigOrDefault(snapshot.runtimeConfig);
|
||||
return [
|
||||
{
|
||||
title: "Scope",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { applyThreadSettingsValue, appServerCollaborationMode, type ThreadSettingsUpdate } from "../../../app-server/thread-settings";
|
||||
import { readRuntimeConfig, type RuntimeConfigProjection } from "./config";
|
||||
import { runtimeConfigOrDefault, type RuntimeConfigProjection } from "./config";
|
||||
import {
|
||||
currentModel,
|
||||
currentReasoningEffort,
|
||||
|
|
@ -33,7 +33,7 @@ export function requestedTurnCollaborationModeSettings(snapshot: RuntimeSnapshot
|
|||
|
||||
export function pendingThreadSettingsUpdate(
|
||||
snapshot: RuntimeSnapshot,
|
||||
config: RuntimeConfigProjection = readRuntimeConfig(snapshot.effectiveConfig),
|
||||
config: RuntimeConfigProjection = runtimeConfigOrDefault(snapshot.runtimeConfig),
|
||||
): PendingThreadSettingsUpdate {
|
||||
const update: ThreadSettingsUpdate = {};
|
||||
const collaborationModeSettings = requestedTurnCollaborationModeSettings(snapshot);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { type Diagnostics, diagnosticProbeError, diagnosticProbeOk } from "../../../app-server/diagnostics";
|
||||
import { listModelMetadata, listSkillCatalog } from "../../../app-server/resource-operations";
|
||||
import { runtimeConfigSnapshotFromAppServerConfig } from "../../../app-server/runtime-config";
|
||||
import { rateLimitSnapshotFromAppServerSnapshot } from "../../../app-server/runtime-metrics";
|
||||
import type { SharedAppServerMetadata } from "../../../app-server/shared-cache-state";
|
||||
import type { ModelMetadata, SkillMetadata } from "../../../domain/catalog/metadata";
|
||||
|
|
@ -57,7 +58,7 @@ export function createChatServerMetadataActions(host: ChatServerMetadataActionsH
|
|||
function serverMetadataSnapshot(host: ChatServerMetadataActionsHost): SharedAppServerMetadata {
|
||||
const state = host.stateStore.getState();
|
||||
return {
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
availableModels: state.connection.availableModels,
|
||||
availableSkills: state.connection.availableSkills,
|
||||
rateLimit: state.connection.rateLimit,
|
||||
|
|
@ -68,7 +69,7 @@ function serverMetadataSnapshot(host: ChatServerMetadataActionsHost): SharedAppS
|
|||
function applyAppServerMetadata(host: ChatServerMetadataActionsHost, metadata: SharedAppServerMetadata): void {
|
||||
host.stateStore.dispatch({
|
||||
type: "connection/metadata-applied",
|
||||
effectiveConfig: metadata.effectiveConfig,
|
||||
runtimeConfig: metadata.runtimeConfig,
|
||||
availableModels: metadata.availableModels,
|
||||
availableSkills: metadata.availableSkills,
|
||||
rateLimit: metadata.rateLimit,
|
||||
|
|
@ -79,14 +80,14 @@ function applyAppServerMetadata(host: ChatServerMetadataActionsHost, metadata: S
|
|||
async function loadAppServerMetadata(host: ChatServerMetadataActionsHost): Promise<SharedAppServerMetadata | null> {
|
||||
const client = host.currentClient();
|
||||
if (!client) return null;
|
||||
const effectiveConfig = await client.readEffectiveConfig(host.vaultPath);
|
||||
const runtimeConfig = runtimeConfigSnapshotFromAppServerConfig(await client.readEffectiveConfig(host.vaultPath));
|
||||
const [models, skills, rateLimit] = await Promise.all([loadModels(host), loadSkills(host), loadRateLimit(host)]);
|
||||
const diagnostics = cloneAppServerDiagnostics(host.stateStore.getState().connection.appServerDiagnostics);
|
||||
diagnostics.probes["model/list"] = models.probe;
|
||||
diagnostics.probes["skills/list"] = skills.probe;
|
||||
diagnostics.probes["account/rateLimits/read"] = rateLimit.probe;
|
||||
return {
|
||||
effectiveConfig,
|
||||
runtimeConfig,
|
||||
availableModels: models.data,
|
||||
availableSkills: skills.data,
|
||||
rateLimit: rateLimit.data,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { parseServiceTier } from "../../../app-server/thread-settings";
|
||||
import { parseServiceTier, type ApprovalPolicy } from "../../../app-server/thread-settings";
|
||||
import { upsertThread } from "../../../domain/threads/model";
|
||||
import { threadFromAppServerThread } from "../../../app-server/thread-model";
|
||||
import type { ReasoningEffort } from "../../../domain/catalog/metadata";
|
||||
import type { AskForApproval } from "../../../generated/app-server/v2/AskForApproval";
|
||||
import type { Thread } from "../../../domain/threads/model";
|
||||
import type { ThreadStartResponse } from "../../../generated/app-server/v2/ThreadStartResponse";
|
||||
import type { ThreadResumeResponse } from "../../../generated/app-server/v2/ThreadResumeResponse";
|
||||
|
|
@ -15,7 +14,7 @@ interface ThreadActivationResponse {
|
|||
cwd: string;
|
||||
model: string | null;
|
||||
serviceTier: string | null;
|
||||
approvalPolicy: AskForApproval | null;
|
||||
approvalPolicy: ApprovalPolicy | null;
|
||||
approvalsReviewer: ChatRuntimeState["activeApprovalsReviewer"];
|
||||
activePermissionProfile: ChatRuntimeState["activePermissionProfile"];
|
||||
reasoningEffort: ReasoningEffort | null;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ButtonHTMLAttributes, ComponentChild as UiNode, TargetedKeyboardEvent } from "preact";
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import type { EffectiveConfigSection, RateLimitSummary } from "../runtime/status-summary";
|
||||
import type { RuntimeConfigSection, RateLimitSummary } from "../runtime/status-summary";
|
||||
import { IconButton } from "../../../shared/ui/components";
|
||||
import { renderUiRoot } from "../../../shared/ui/ui-root";
|
||||
import type { ToolbarDiagnosticSection, ToolbarThreadRow, ToolbarViewModel } from "../panel/model/types";
|
||||
|
|
@ -139,7 +139,7 @@ function StatusPanel({ model, actions }: { model: ToolbarViewModel; actions: Too
|
|||
</div>
|
||||
<RateLimitPanel rateLimit={model.rateLimit} />
|
||||
<ConnectionDiagnostics sections={model.diagnostics} />
|
||||
<EffectiveConfigPanel sections={model.configSections} />
|
||||
<RuntimeConfigPanel sections={model.configSections} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -208,7 +208,7 @@ function DiagnosticSection({ section }: { section: ToolbarDiagnosticSection }):
|
|||
);
|
||||
}
|
||||
|
||||
function EffectiveConfigPanel({ sections }: { sections: EffectiveConfigSection[] }): UiNode {
|
||||
function RuntimeConfigPanel({ sections }: { sections: RuntimeConfigSection[] }): UiNode {
|
||||
return (
|
||||
<div className="codex-panel__config">
|
||||
<div className="codex-panel__config-title">Effective Codex config</div>
|
||||
|
|
@ -221,7 +221,7 @@ function EffectiveConfigPanel({ sections }: { sections: EffectiveConfigSection[]
|
|||
);
|
||||
}
|
||||
|
||||
function FragmentedConfigSection({ section }: { section: EffectiveConfigSection }): UiNode {
|
||||
function FragmentedConfigSection({ section }: { section: RuntimeConfigSection }): UiNode {
|
||||
return (
|
||||
<>
|
||||
<div className="codex-panel__config-section">{section.title}</div>
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ export class CodexChatView extends ItemView {
|
|||
|
||||
private modelStatusLines(): string[] {
|
||||
return buildModelStatusLines({
|
||||
effectiveConfig: this.state.connection.effectiveConfig,
|
||||
runtimeConfig: this.state.connection.runtimeConfig,
|
||||
requestedModel: this.state.runtime.requestedModel,
|
||||
snapshot: this.runtimeSnapshot(),
|
||||
collaborationModeLabel: this.collaborationModeLabel(),
|
||||
|
|
@ -617,7 +617,7 @@ export class CodexChatView extends ItemView {
|
|||
|
||||
private effortStatusLines(): string[] {
|
||||
return buildEffortStatusLines({
|
||||
effectiveConfig: this.state.connection.effectiveConfig,
|
||||
runtimeConfig: this.state.connection.runtimeConfig,
|
||||
requestedReasoningEffort: this.state.runtime.requestedReasoningEffort,
|
||||
snapshot: this.runtimeSnapshot(),
|
||||
});
|
||||
|
|
@ -648,7 +648,7 @@ export class CodexChatView extends ItemView {
|
|||
|
||||
private runtimeSnapshotForState(state: ChatState): RuntimeSnapshot {
|
||||
return runtimeSnapshotForChatSlices({
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
activeThread: state.activeThread,
|
||||
runtime: state.runtime,
|
||||
rateLimit: state.connection.rateLimit,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createAppServerDiagnostics } from "../../src/app-server/diagnostics";
|
||||
import { emptyRuntimeConfigSnapshot } from "../../src/app-server/runtime-config";
|
||||
import {
|
||||
applySharedAppServerMetadata,
|
||||
applySharedModels,
|
||||
|
|
@ -35,7 +36,7 @@ describe("shared app-server cache state", () => {
|
|||
expect(cachedSharedModels(modelState)[0]?.supportedReasoningEfforts).toEqual([]);
|
||||
|
||||
const metadataState = applySharedAppServerMetadata(createSharedAppServerState(), {
|
||||
effectiveConfig: null,
|
||||
runtimeConfig: emptyRuntimeConfigSnapshot(),
|
||||
availableModels: sourceModels,
|
||||
availableSkills: [{ name: "skill", description: "", path: "/tmp/skill", enabled: true }],
|
||||
rateLimit: null,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ describe("chat server actions", () => {
|
|||
stateStore,
|
||||
vaultPath: "/vault",
|
||||
currentClient: () => client,
|
||||
runtimeSnapshot: () => ({ requestedServiceTier: { kind: "unchanged" }, effectiveConfig: null }) as never,
|
||||
runtimeSnapshot: () => ({ requestedServiceTier: { kind: "unchanged" }, runtimeConfig: null }) as never,
|
||||
publishThreadList,
|
||||
syncThreadGoal,
|
||||
});
|
||||
|
|
@ -73,7 +73,7 @@ describe("chat server actions", () => {
|
|||
stateStore,
|
||||
vaultPath: "/vault",
|
||||
currentClient: () => client,
|
||||
runtimeSnapshot: () => ({ requestedServiceTier: { kind: "unchanged" }, effectiveConfig: null }) as never,
|
||||
runtimeSnapshot: () => ({ requestedServiceTier: { kind: "unchanged" }, runtimeConfig: null }) as never,
|
||||
publishThreadList: vi.fn(),
|
||||
syncThreadGoal,
|
||||
});
|
||||
|
|
@ -104,7 +104,7 @@ describe("chat server actions", () => {
|
|||
stateStore,
|
||||
vaultPath: "/vault",
|
||||
currentClient: () => client,
|
||||
runtimeSnapshot: () => ({ requestedServiceTier: { kind: "unchanged" }, effectiveConfig: null }) as never,
|
||||
runtimeSnapshot: () => ({ requestedServiceTier: { kind: "unchanged" }, runtimeConfig: null }) as never,
|
||||
publishThreadList,
|
||||
syncThreadGoal: () => undefined,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ function clientFixture(
|
|||
|
||||
function runtimeSnapshotFixture(state: ChatState) {
|
||||
return runtimeSnapshotForChatSlices({
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
activeThread: state.activeThread,
|
||||
runtime: state.runtime,
|
||||
rateLimit: state.connection.rateLimit,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import type { AppServerClient } from "../../../../src/app-server/client";
|
||||
import { emptyRuntimeConfigSnapshot } from "../../../../src/app-server/runtime-config";
|
||||
import { createChatState, createChatStateStore } from "../../../../src/features/chat/chat-state";
|
||||
import {
|
||||
ChatConnectionController,
|
||||
|
|
@ -99,7 +100,7 @@ describe("ChatConnectionController", () => {
|
|||
it("clears disconnected connection state on server exit while keeping last startup metadata", () => {
|
||||
const { controller, host, stateStore } = createController({ connected: true });
|
||||
const initializeResponse = { codexHome: "/codex", platformFamily: "unix", platformOs: "macos", userAgent: "test" } as const;
|
||||
const effectiveConfig = { config: { model: "gpt-5.1" }, layers: [] } as never;
|
||||
const runtimeConfig = { ...emptyRuntimeConfigSnapshot(), model: "gpt-5.1" };
|
||||
stateStore.dispatch({ type: "connection/initialized", initializeResponse });
|
||||
stateStore.dispatch({
|
||||
type: "thread-list/applied",
|
||||
|
|
@ -110,7 +111,7 @@ describe("ChatConnectionController", () => {
|
|||
type: "connection/metadata-applied",
|
||||
availableModels: [{ id: "model-1" } as never],
|
||||
availableSkills: [{ name: "skill-1" } as never],
|
||||
effectiveConfig,
|
||||
runtimeConfig,
|
||||
});
|
||||
|
||||
controller.handleExit();
|
||||
|
|
@ -129,7 +130,7 @@ describe("ChatConnectionController", () => {
|
|||
connection: {
|
||||
availableModels: [],
|
||||
availableSkills: [],
|
||||
effectiveConfig,
|
||||
runtimeConfig,
|
||||
initializeResponse,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|||
import { DEFAULT_SETTINGS } from "../../../src/settings/model";
|
||||
import type { CodexChatHost } from "../../../src/features/chat/chat-host";
|
||||
import { createAppServerDiagnostics } from "../../../src/app-server/diagnostics";
|
||||
import { emptyRuntimeConfigSnapshot } from "../../../src/app-server/runtime-config";
|
||||
import { threadFromAppServerThread } from "../../../src/app-server/thread-model";
|
||||
import { createChatState, type ChatState } from "../../../src/features/chat/chat-state";
|
||||
import { composerSlotSnapshot } from "../../../src/features/chat/panel/snapshot";
|
||||
|
|
@ -174,7 +175,7 @@ describe("CodexChatView connection lifecycle", () => {
|
|||
|
||||
expect(publishAppServerMetadata).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
effectiveConfig: {},
|
||||
runtimeConfig: expect.any(Object),
|
||||
availableModels: [],
|
||||
availableSkills: [],
|
||||
}),
|
||||
|
|
@ -405,7 +406,7 @@ describe("CodexChatView connection lifecycle", () => {
|
|||
cachedAppServerMetadata: vi.fn(
|
||||
() =>
|
||||
({
|
||||
effectiveConfig: { config: { model: "gpt-cached" }, origins: {}, layers: [] },
|
||||
runtimeConfig: { ...emptyRuntimeConfigSnapshot(), model: "gpt-cached" },
|
||||
availableModels: [],
|
||||
availableSkills: [{ name: "writer", enabled: true }],
|
||||
rateLimit: null,
|
||||
|
|
@ -421,12 +422,12 @@ describe("CodexChatView connection lifecycle", () => {
|
|||
view as unknown as {
|
||||
state: {
|
||||
threadList: { listedThreads: unknown[] };
|
||||
connection: { effectiveConfig: unknown; availableModels: unknown[]; availableSkills: unknown[] };
|
||||
connection: { runtimeConfig: unknown; availableModels: unknown[]; availableSkills: unknown[] };
|
||||
};
|
||||
}
|
||||
).state;
|
||||
expect(state.threadList.listedThreads).toEqual([cachedThread]);
|
||||
expect(state.connection.effectiveConfig).toEqual({ config: { model: "gpt-cached" }, origins: {}, layers: [] });
|
||||
expect(state.connection.runtimeConfig).toEqual({ ...emptyRuntimeConfigSnapshot(), model: "gpt-cached" });
|
||||
expect(state.connection.availableModels).toEqual([]);
|
||||
expect(state.connection.availableSkills).toEqual([{ name: "writer", enabled: true }]);
|
||||
});
|
||||
|
|
@ -434,13 +435,13 @@ describe("CodexChatView connection lifecycle", () => {
|
|||
it("tracks composer slot dependencies for model and skill suggestions", async () => {
|
||||
await chatView();
|
||||
const state = createChatState();
|
||||
state.connection.effectiveConfig = effectiveConfig("gpt-configured");
|
||||
state.connection.runtimeConfig = runtimeConfig("gpt-configured");
|
||||
state.connection.availableSkills = [skillFixture("writer")];
|
||||
|
||||
const base = composerSlotSnapshot(state, null);
|
||||
|
||||
expect(
|
||||
composerSlotSnapshot({ ...state, connection: { ...state.connection, effectiveConfig: effectiveConfig("gpt-updated") } }, null),
|
||||
composerSlotSnapshot({ ...state, connection: { ...state.connection, runtimeConfig: runtimeConfig("gpt-updated") } }, null),
|
||||
).not.toBe(base);
|
||||
expect(
|
||||
composerSlotSnapshot({ ...state, runtime: { ...state.runtime, requestedModel: { kind: "set", value: "gpt-requested" } } }, null),
|
||||
|
|
@ -1094,8 +1095,8 @@ function threadFixture(threadId: string): Thread {
|
|||
};
|
||||
}
|
||||
|
||||
function effectiveConfig(model: string): ChatState["connection"]["effectiveConfig"] {
|
||||
return { config: { model }, origins: {}, layers: [] } as never;
|
||||
function runtimeConfig(model: string): ChatState["connection"]["runtimeConfig"] {
|
||||
return { ...emptyRuntimeConfigSnapshot(), model };
|
||||
}
|
||||
|
||||
function skillFixture(name: string): ChatState["connection"]["availableSkills"][number] {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createAppServerDiagnostics } from "../../../src/app-server/diagnostics";
|
||||
import { runtimeConfigSnapshotFromAppServerConfig, type RuntimeConfigSnapshot } from "../../../src/app-server/runtime-config";
|
||||
import { createChatState } from "../../../src/features/chat/chat-state";
|
||||
import {
|
||||
activeComposerThreadName,
|
||||
|
|
@ -27,7 +28,7 @@ describe("chat view model", () => {
|
|||
state.threadList.listedThreads = [threadFixture("thread-1", "Active"), threadFixture("thread-2", "Other")];
|
||||
state.turn.lifecycle = { kind: "running", turnId: "turn" };
|
||||
state.ui.toolbarPanel = "history";
|
||||
state.connection.effectiveConfig = effectiveConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" });
|
||||
state.connection.runtimeConfig = runtimeConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" });
|
||||
state.connection.appServerDiagnostics = createAppServerDiagnostics();
|
||||
|
||||
const model = toolbarViewModel({
|
||||
|
|
@ -54,7 +55,7 @@ describe("chat view model", () => {
|
|||
const state = createChatState();
|
||||
state.activeThread.id = "thread-1";
|
||||
state.runtime.selectedCollaborationMode = "plan";
|
||||
state.connection.effectiveConfig = effectiveConfigFixture({
|
||||
state.connection.runtimeConfig = runtimeConfigFixture({
|
||||
model: "gpt-5.5",
|
||||
model_reasoning_effort: "high",
|
||||
approvals_reviewer: "auto_review",
|
||||
|
|
@ -100,7 +101,7 @@ describe("chat view model", () => {
|
|||
role: "assistant",
|
||||
},
|
||||
];
|
||||
state.connection.effectiveConfig = effectiveConfigFixture({ model: "gpt-5.5" });
|
||||
state.connection.runtimeConfig = runtimeConfigFixture({ model: "gpt-5.5" });
|
||||
|
||||
expect(composerMetaViewModel(state, runtimeSnapshotFixture(state))).toMatchObject({
|
||||
fatal: null,
|
||||
|
|
@ -169,7 +170,7 @@ describe("chat view model", () => {
|
|||
it("builds slash-command status lines from chat state", () => {
|
||||
const state = createChatState();
|
||||
state.activeThread.id = "thread-1";
|
||||
state.connection.effectiveConfig = effectiveConfigFixture({
|
||||
state.connection.runtimeConfig = runtimeConfigFixture({
|
||||
model: "gpt-5.5",
|
||||
model_provider: "openai",
|
||||
model_reasoning_effort: "high",
|
||||
|
|
@ -181,7 +182,7 @@ describe("chat view model", () => {
|
|||
expect(statusSummaryLines({ activeThreadId: state.activeThread.id, snapshot })[1]).toBe("Thread: thread-1");
|
||||
expect(
|
||||
modelStatusLines({
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
requestedModel: state.runtime.requestedModel,
|
||||
snapshot,
|
||||
collaborationModeLabel: "Default",
|
||||
|
|
@ -189,7 +190,7 @@ describe("chat view model", () => {
|
|||
).toContain("Model: gpt-5.5");
|
||||
expect(
|
||||
modelStatusLines({
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
requestedModel: state.runtime.requestedModel,
|
||||
snapshot,
|
||||
collaborationModeLabel: "Default",
|
||||
|
|
@ -197,7 +198,7 @@ describe("chat view model", () => {
|
|||
).toContain("Mode: Default");
|
||||
expect(
|
||||
effortStatusLines({
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
requestedReasoningEffort: state.runtime.requestedReasoningEffort,
|
||||
snapshot,
|
||||
}),
|
||||
|
|
@ -206,7 +207,7 @@ describe("chat view model", () => {
|
|||
|
||||
it("builds runtime composer choices from immutable chat state snapshots", () => {
|
||||
const state = createChatState();
|
||||
state.connection.effectiveConfig = effectiveConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" });
|
||||
state.connection.runtimeConfig = runtimeConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" });
|
||||
state.connection.availableModels = [modelFixture("gpt-5.5"), modelFixture("gpt-5-mini")];
|
||||
const selectedModels: (string | null)[] = [];
|
||||
const selectedEfforts: string[] = [];
|
||||
|
|
@ -250,17 +251,17 @@ describe("chat view model", () => {
|
|||
});
|
||||
});
|
||||
|
||||
function effectiveConfigFixture(config: Record<string, unknown>): ConfigReadResponse {
|
||||
return {
|
||||
function runtimeConfigFixture(config: Record<string, unknown>): RuntimeConfigSnapshot {
|
||||
return runtimeConfigSnapshotFromAppServerConfig({
|
||||
config: config as ConfigReadResponse["config"],
|
||||
origins: {},
|
||||
layers: null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function runtimeSnapshotFixture(state: ChatState) {
|
||||
return runtimeSnapshotForChatSlices({
|
||||
effectiveConfig: state.connection.effectiveConfig,
|
||||
runtimeConfig: state.connection.runtimeConfig,
|
||||
activeThread: state.activeThread,
|
||||
runtime: state.runtime,
|
||||
rateLimit: state.connection.rateLimit,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { runtimeConfigSnapshotFromAppServerConfig, type RuntimeConfigSnapshot } from "../../src/app-server/runtime-config";
|
||||
import type { ConfigReadResponse } from "../../src/generated/app-server/v2/ConfigReadResponse";
|
||||
import type { ModelMetadata } from "../../src/domain/catalog/metadata";
|
||||
import {
|
||||
|
|
@ -27,8 +28,8 @@ import {
|
|||
type RuntimeSnapshot,
|
||||
} from "../../src/features/chat/runtime/effective-settings";
|
||||
import { requestedTurnCollaborationModeSettings } from "../../src/features/chat/runtime/turn-settings";
|
||||
import { readRuntimeConfig } from "../../src/features/chat/runtime/config";
|
||||
import { contextSummary, effectiveConfigSections, rateLimitSummary } from "../../src/features/chat/runtime/status-summary";
|
||||
import { runtimeConfigOrDefault } from "../../src/features/chat/runtime/config";
|
||||
import { contextSummary, runtimeConfigSections, rateLimitSummary } from "../../src/features/chat/runtime/status-summary";
|
||||
|
||||
describe("runtime settings", () => {
|
||||
it("parses model overrides", () => {
|
||||
|
|
@ -117,7 +118,7 @@ describe("runtime settings", () => {
|
|||
runtimeSnapshot({
|
||||
requestedApprovalsReviewer: setPendingRuntimeSetting("user"),
|
||||
activeApprovalsReviewer: "auto_review",
|
||||
effectiveConfig: effectiveConfigFixture({ approvals_reviewer: "guardian_subagent" }),
|
||||
runtimeConfig: runtimeConfigFixture({ approvals_reviewer: "guardian_subagent" }),
|
||||
}),
|
||||
),
|
||||
).toBe("user");
|
||||
|
|
@ -125,14 +126,14 @@ describe("runtime settings", () => {
|
|||
currentApprovalsReviewer(
|
||||
runtimeSnapshot({
|
||||
activeApprovalsReviewer: "auto_review",
|
||||
effectiveConfig: effectiveConfigFixture({ approvals_reviewer: "guardian_subagent" }),
|
||||
runtimeConfig: runtimeConfigFixture({ approvals_reviewer: "guardian_subagent" }),
|
||||
}),
|
||||
),
|
||||
).toBe("auto_review");
|
||||
expect(
|
||||
currentApprovalsReviewer(
|
||||
runtimeSnapshot({
|
||||
effectiveConfig: effectiveConfigFixture({ approvals_reviewer: "guardian_subagent" }),
|
||||
runtimeConfig: runtimeConfigFixture({ approvals_reviewer: "guardian_subagent" }),
|
||||
}),
|
||||
),
|
||||
).toBe("guardian_subagent");
|
||||
|
|
@ -141,7 +142,7 @@ describe("runtime settings", () => {
|
|||
it("uses the active reviewer before configured reviewer", () => {
|
||||
const snapshot = runtimeSnapshot({
|
||||
activeApprovalsReviewer: "user",
|
||||
effectiveConfig: effectiveConfigFixture({ approvals_reviewer: "auto_review" }),
|
||||
runtimeConfig: runtimeConfigFixture({ approvals_reviewer: "auto_review" }),
|
||||
});
|
||||
|
||||
expect(currentApprovalsReviewer(snapshot)).toBe("user");
|
||||
|
|
@ -161,7 +162,7 @@ describe("runtime settings", () => {
|
|||
const snapshot = runtimeSnapshot({
|
||||
requestedApprovalsReviewer: setPendingRuntimeSetting("user"),
|
||||
activeApprovalsReviewer: "user",
|
||||
effectiveConfig: effectiveConfigFixture({ approvals_reviewer: "auto_review" }),
|
||||
runtimeConfig: runtimeConfigFixture({ approvals_reviewer: "auto_review" }),
|
||||
});
|
||||
|
||||
expect(currentApprovalsReviewer(snapshot)).toBe("user");
|
||||
|
|
@ -169,22 +170,22 @@ describe("runtime settings", () => {
|
|||
});
|
||||
|
||||
it("uses effective approval reviewer values and reports selected profile metadata", () => {
|
||||
const effectiveConfig = effectiveConfigFixture({ approvals_reviewer: "auto_review" }, [
|
||||
const runtimeConfig = runtimeConfigFixture({ approvals_reviewer: "auto_review" }, [
|
||||
configLayer({}, null),
|
||||
configLayer({ approvals_reviewer: "auto_review" }, "auto"),
|
||||
]);
|
||||
const snapshot = runtimeSnapshot({
|
||||
effectiveConfig,
|
||||
runtimeConfig,
|
||||
});
|
||||
|
||||
expect(readRuntimeConfig(effectiveConfig).profile).toBe("auto");
|
||||
expect(runtimeConfigOrDefault(runtimeConfig).profile).toBe("auto");
|
||||
expect(currentApprovalsReviewer(snapshot)).toBe("auto_review");
|
||||
expect(autoReviewActive(snapshot)).toBe(true);
|
||||
});
|
||||
|
||||
it("uses effective model, effort, and fast mode config values", () => {
|
||||
const snapshot = runtimeSnapshot({
|
||||
effectiveConfig: effectiveConfigFixture(
|
||||
runtimeConfig: runtimeConfigFixture(
|
||||
{
|
||||
model: "gpt-profile",
|
||||
model_reasoning_effort: "high",
|
||||
|
|
@ -208,7 +209,7 @@ describe("runtime settings", () => {
|
|||
it("uses active service tier before configured service tier", () => {
|
||||
const snapshot = runtimeSnapshot({
|
||||
activeServiceTier: "flex",
|
||||
effectiveConfig: effectiveConfigFixture({ service_tier: "fast" }),
|
||||
runtimeConfig: runtimeConfigFixture({ service_tier: "fast" }),
|
||||
});
|
||||
|
||||
expect(currentServiceTier(snapshot)).toBe("flex");
|
||||
|
|
@ -222,7 +223,7 @@ describe("runtime settings", () => {
|
|||
const snapshot = runtimeSnapshot({
|
||||
activeModel: "gpt-5.5",
|
||||
activeServiceTier: "priority",
|
||||
effectiveConfig: effectiveConfigFixture({ model: "gpt-5.5" }),
|
||||
runtimeConfig: runtimeConfigFixture({ model: "gpt-5.5" }),
|
||||
availableModels: [model],
|
||||
});
|
||||
|
||||
|
|
@ -239,7 +240,7 @@ describe("runtime settings", () => {
|
|||
const snapshot = runtimeSnapshot({
|
||||
activeModel: "gpt-5.5",
|
||||
activeServiceTier: "default",
|
||||
effectiveConfig: effectiveConfigFixture({ model: "gpt-5.5" }),
|
||||
runtimeConfig: runtimeConfigFixture({ model: "gpt-5.5" }),
|
||||
availableModels: [model],
|
||||
});
|
||||
|
||||
|
|
@ -253,7 +254,7 @@ describe("runtime settings", () => {
|
|||
const snapshot = runtimeSnapshot({
|
||||
requestedServiceTier: setPendingRuntimeSetting("off"),
|
||||
activeServiceTier: "flex",
|
||||
effectiveConfig: effectiveConfigFixture({ service_tier: "fast" }),
|
||||
runtimeConfig: runtimeConfigFixture({ service_tier: "fast" }),
|
||||
});
|
||||
|
||||
expect(currentServiceTier(snapshot)).toBeNull();
|
||||
|
|
@ -272,7 +273,7 @@ describe("runtime settings", () => {
|
|||
const snapshot = runtimeSnapshot({
|
||||
activeModel: "gpt-5-active",
|
||||
activeServiceTier: "fast",
|
||||
effectiveConfig: effectiveConfigFixture({}),
|
||||
runtimeConfig: runtimeConfigFixture({}),
|
||||
});
|
||||
|
||||
expect(currentModel(snapshot)).toBe("gpt-5-active");
|
||||
|
|
@ -288,7 +289,7 @@ describe("runtime settings", () => {
|
|||
});
|
||||
|
||||
it("separates effective runtime, config defaults, and pending changes in status details", () => {
|
||||
const sections = effectiveConfigSections(
|
||||
const sections = runtimeConfigSections(
|
||||
runtimeSnapshot({
|
||||
activeModel: "gpt-5-active",
|
||||
activeReasoningEffort: "low",
|
||||
|
|
@ -318,7 +319,7 @@ describe("runtime settings", () => {
|
|||
"requested service tier": "(none)",
|
||||
});
|
||||
|
||||
const resetSections = effectiveConfigSections(
|
||||
const resetSections = runtimeConfigSections(
|
||||
runtimeSnapshot({
|
||||
requestedReasoningEffort: resetRuntimeSettingToConfig(),
|
||||
}),
|
||||
|
|
@ -331,9 +332,9 @@ describe("runtime settings", () => {
|
|||
});
|
||||
|
||||
it("labels unset config values as Codex defaults when no concrete value is reported", () => {
|
||||
const sections = effectiveConfigSections(
|
||||
const sections = runtimeConfigSections(
|
||||
runtimeSnapshot({
|
||||
effectiveConfig: effectiveConfigFixture({}),
|
||||
runtimeConfig: runtimeConfigFixture({}),
|
||||
}),
|
||||
"/vault",
|
||||
);
|
||||
|
|
@ -371,9 +372,9 @@ describe("runtime settings", () => {
|
|||
|
||||
it("does not treat unknown catalog default reasoning efforts as runtime settings", () => {
|
||||
const model = { ...modelFixture("gpt-catalog-default"), isDefault: true, defaultReasoningEffort: "extreme" };
|
||||
const sections = effectiveConfigSections(
|
||||
const sections = runtimeConfigSections(
|
||||
runtimeSnapshot({
|
||||
effectiveConfig: effectiveConfigFixture({}),
|
||||
runtimeConfig: runtimeConfigFixture({}),
|
||||
availableModels: [model],
|
||||
}),
|
||||
"/vault",
|
||||
|
|
@ -398,9 +399,9 @@ describe("runtime settings", () => {
|
|||
web_search: "live",
|
||||
service_tier: "fast",
|
||||
};
|
||||
const sections = effectiveConfigSections(
|
||||
const sections = runtimeConfigSections(
|
||||
runtimeSnapshot({
|
||||
effectiveConfig: effectiveConfigFixture(profileConfig, [configLayer({}, null), configLayer(profileConfig, "auto")]),
|
||||
runtimeConfig: runtimeConfigFixture(profileConfig, [configLayer({}, null), configLayer(profileConfig, "auto")]),
|
||||
}),
|
||||
"/vault",
|
||||
);
|
||||
|
|
@ -446,12 +447,12 @@ describe("runtime settings", () => {
|
|||
});
|
||||
|
||||
it("shows active and configured reviewer inputs in status details", () => {
|
||||
const sections = effectiveConfigSections(
|
||||
const sections = runtimeConfigSections(
|
||||
runtimeSnapshot({
|
||||
activeApprovalsReviewer: "guardian_subagent",
|
||||
activeApprovalPolicy: "never",
|
||||
activePermissionProfile: { id: ":workspace", extends: ":default" },
|
||||
effectiveConfig: effectiveConfigFixture({ approvals_reviewer: "auto_review" }),
|
||||
runtimeConfig: runtimeConfigFixture({ approvals_reviewer: "auto_review" }),
|
||||
}),
|
||||
"/vault",
|
||||
);
|
||||
|
|
@ -510,7 +511,7 @@ describe("runtime settings", () => {
|
|||
|
||||
it("serializes explicit fast off as a null service tier request", () => {
|
||||
const snapshot = runtimeSnapshot({
|
||||
effectiveConfig: effectiveConfigFixture({ service_tier: "fast" }),
|
||||
runtimeConfig: runtimeConfigFixture({ service_tier: "fast" }),
|
||||
requestedServiceTier: setPendingRuntimeSetting("off"),
|
||||
});
|
||||
|
||||
|
|
@ -524,7 +525,7 @@ describe("runtime settings", () => {
|
|||
model.serviceTiers = [{ id: "priority", name: "Fast" }];
|
||||
const snapshot = runtimeSnapshot({
|
||||
requestedServiceTier: setPendingRuntimeSetting("fast"),
|
||||
effectiveConfig: effectiveConfigFixture({ model: "gpt-5.5" }),
|
||||
runtimeConfig: runtimeConfigFixture({ model: "gpt-5.5" }),
|
||||
availableModels: [model],
|
||||
});
|
||||
|
||||
|
|
@ -533,11 +534,11 @@ describe("runtime settings", () => {
|
|||
});
|
||||
|
||||
it("omits service tier when neither config nor override selects one", () => {
|
||||
expect(requestedOrConfiguredServiceTier(runtimeSnapshot({ effectiveConfig: effectiveConfigFixture({}) }))).toBeUndefined();
|
||||
expect(requestedOrConfiguredServiceTier(runtimeSnapshot({ runtimeConfig: runtimeConfigFixture({}) }))).toBeUndefined();
|
||||
});
|
||||
|
||||
it("passes through configured non-fast service tier ids", () => {
|
||||
const snapshot = runtimeSnapshot({ effectiveConfig: effectiveConfigFixture({ service_tier: "flex" }) });
|
||||
const snapshot = runtimeSnapshot({ runtimeConfig: runtimeConfigFixture({ service_tier: "flex" }) });
|
||||
|
||||
expect(serviceTierLabel(snapshot)).toBe("flex");
|
||||
expect(fastModeLabel(snapshot)).toBe("off");
|
||||
|
|
@ -644,7 +645,7 @@ describe("runtime settings", () => {
|
|||
|
||||
function runtimeSnapshot(overrides: Partial<RuntimeSnapshot> = {}): RuntimeSnapshot {
|
||||
return {
|
||||
effectiveConfig: effectiveConfigFixture({
|
||||
runtimeConfig: runtimeConfigFixture({
|
||||
model: "gpt-5.5",
|
||||
model_reasoning_effort: "high",
|
||||
service_tier: "flex",
|
||||
|
|
@ -671,12 +672,12 @@ function runtimeSnapshot(overrides: Partial<RuntimeSnapshot> = {}): RuntimeSnaps
|
|||
};
|
||||
}
|
||||
|
||||
function effectiveConfigFixture(config: Record<string, unknown>, layers: ConfigReadResponse["layers"] = null): ConfigReadResponse {
|
||||
return {
|
||||
function runtimeConfigFixture(config: Record<string, unknown>, layers: ConfigReadResponse["layers"] = null): RuntimeConfigSnapshot {
|
||||
return runtimeConfigSnapshotFromAppServerConfig({
|
||||
config: config as ConfigReadResponse["config"],
|
||||
origins: {},
|
||||
layers,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function configLayer(config: Record<string, unknown>, profile: string | null): NonNullable<ConfigReadResponse["layers"]>[number] {
|
||||
|
|
|
|||
Loading…
Reference in a new issue