From c1af92c6e9c720fa4480a152b7f91f16da740480 Mon Sep 17 00:00:00 2001 From: murashit Date: Thu, 28 May 2026 17:40:53 +0900 Subject: [PATCH] Rename app-server metadata surfaces --- README.md | 2 +- docs/development.md | 2 +- ...session-client.ts => connection-client.ts} | 2 +- ...oller.ts => chat-app-server-controller.ts} | 24 +++--- src/features/chat/chat-controller.ts | 6 +- src/features/chat/view.ts | 84 +++++++++---------- src/main.ts | 14 ++-- src/runtime/shared-app-server-state.ts | 12 +-- src/settings/tab.ts | 16 ++-- ....ts => chat-app-server-controller.test.ts} | 12 +-- tests/features/chat/chat-controller.test.ts | 14 ++-- tests/features/chat/view-connection.test.ts | 16 ++-- tests/main.test.ts | 4 +- tests/settings/settings-tab.test.ts | 26 +++--- 14 files changed, 117 insertions(+), 117 deletions(-) rename src/app-server/{session-client.ts => connection-client.ts} (92%) rename src/features/chat/{chat-session-controller.ts => chat-app-server-controller.ts} (94%) rename tests/features/chat/{chat-session-controller.test.ts => chat-app-server-controller.test.ts} (85%) diff --git a/README.md b/README.md index 4238f3e1..9b963e1a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Codex Panel is a desktop-only Obsidian plugin that brings Codex into a panel in the right sidebar. It is built as an Obsidian frontend for `codex app-server`: the plugin starts Codex locally, communicates with it over stdio, and uses the current vault root as the Codex working directory. -Codex stays in charge of models, reasoning effort, sandbox modes, approval policies, MCP servers, hooks, skills, network access, providers, and thread history. Codex Panel adds the Obsidian surface around that Codex App Server session instead of replacing Codex configuration or policy. +Codex stays in charge of models, reasoning effort, sandbox modes, approval policies, MCP servers, hooks, skills, network access, providers, and thread history. Codex Panel adds the Obsidian surface around that Codex App Server connection instead of replacing Codex configuration or policy. That boundary is also the privacy and security boundary: Codex Panel does not store API keys or make its own network requests. Data sent from the panel is handled by the configured Codex CLI according to your Codex configuration. diff --git a/docs/development.md b/docs/development.md index 260e64aa..f7d78ba2 100644 --- a/docs/development.md +++ b/docs/development.md @@ -17,7 +17,7 @@ The source tree is organized by responsibility rather than by the original singl - `src/main.ts` registers Obsidian views, commands, settings, and lifecycle hooks. - `src/app-server/` owns app-server transport, connection lifecycle, compatibility helpers, and RPC facades. -- `src/features/chat/` owns the main Codex chat surface: Obsidian `ItemView` classes, panel orchestration, request/session controllers, composer behavior, display models, chat-only UI renderers, approvals, user input, thread actions, and panel-specific state. +- `src/features/chat/` owns the main Codex chat surface: Obsidian `ItemView` classes, panel orchestration, request/app-server controllers, composer behavior, display models, chat-only UI renderers, approvals, user input, thread actions, and panel-specific state. - `src/features/threads-view/` owns the dedicated Obsidian thread list view, including app-server thread list rendering and live open-panel snapshot aggregation. - `src/features/selection-rewrite/` owns the Markdown editor selection rewrite command, popover, prompt/output handling, and short-lived rewrite session runner. - `src/runtime/` owns Codex runtime configuration projection, model metadata, collaboration mode, and compact labels used by views. diff --git a/src/app-server/session-client.ts b/src/app-server/connection-client.ts similarity index 92% rename from src/app-server/session-client.ts rename to src/app-server/connection-client.ts index 5d595cfe..046dd6cf 100644 --- a/src/app-server/session-client.ts +++ b/src/app-server/connection-client.ts @@ -1,6 +1,6 @@ import { AppServerClient } from "./client"; -export async function withAppServerSession( +export async function withAppServerConnection( codexPath: string, cwd: string, operation: (client: AppServerClient) => Promise, diff --git a/src/features/chat/chat-session-controller.ts b/src/features/chat/chat-app-server-controller.ts similarity index 94% rename from src/features/chat/chat-session-controller.ts rename to src/features/chat/chat-app-server-controller.ts index eeb29e29..07d43d31 100644 --- a/src/features/chat/chat-session-controller.ts +++ b/src/features/chat/chat-app-server-controller.ts @@ -11,11 +11,11 @@ import type { McpServerStatus } from "../../generated/app-server/v2/McpServerSta import type { Model } from "../../generated/app-server/v2/Model"; import type { RateLimitSnapshot } from "../../generated/app-server/v2/RateLimitSnapshot"; import type { SkillMetadata } from "../../generated/app-server/v2/SkillMetadata"; -import type { SharedSessionMetadata } from "../../runtime/shared-app-server-state"; +import type { SharedAppServerMetadata } from "../../runtime/shared-app-server-state"; import { requestedOrConfiguredServiceTier, type RuntimeSnapshot } from "../../runtime/state"; import type { ChatAction, ChatState, ChatStateStore } from "./chat-state"; -export interface ChatSessionControllerHost { +export interface ChatAppServerControllerHost { stateStore: ChatStateStore; vaultPath: string; currentClient: () => AppServerClient | null; @@ -24,11 +24,11 @@ export interface ChatSessionControllerHost { } export interface RefreshCapabilityDiagnosticsOptions { - cachedSessionMetadata?: boolean; + cachedAppServerMetadata?: boolean; } -export class ChatSessionController { - constructor(private readonly host: ChatSessionControllerHost) {} +export class ChatAppServerController { + constructor(private readonly host: ChatAppServerControllerHost) {} private get state(): ChatState { return this.host.stateStore.getState(); @@ -49,7 +49,7 @@ export class ChatSessionController { return response.data; } - sessionMetadataSnapshot(): SharedSessionMetadata { + appServerMetadataSnapshot(): SharedAppServerMetadata { return { effectiveConfig: this.state.effectiveConfig, availableModels: this.state.availableModels, @@ -59,7 +59,7 @@ export class ChatSessionController { }; } - applySessionMetadata(metadata: SharedSessionMetadata): void { + applyAppServerMetadata(metadata: SharedAppServerMetadata): void { this.dispatch({ type: "thread/list-applied", effectiveConfig: metadata.effectiveConfig, @@ -70,7 +70,7 @@ export class ChatSessionController { }); } - async loadSessionMetadata(): Promise { + async loadAppServerMetadata(): Promise { const client = this.host.currentClient(); if (!client) return null; const effectiveConfig = await client.readEffectiveConfig(this.host.vaultPath); @@ -88,9 +88,9 @@ export class ChatSessionController { }; } - async refreshSessionMetadata(): Promise { - const metadata = await this.loadSessionMetadata(); - if (metadata) this.applySessionMetadata(metadata); + async refreshAppServerMetadata(): Promise { + const metadata = await this.loadAppServerMetadata(); + if (metadata) this.applyAppServerMetadata(metadata); return metadata; } @@ -187,7 +187,7 @@ export class ChatSessionController { if (!client) return; const probes: Promise[] = []; - if (!options.cachedSessionMetadata) { + if (!options.cachedAppServerMetadata) { probes.push( this.probeCapability( "model/list", diff --git a/src/features/chat/chat-controller.ts b/src/features/chat/chat-controller.ts index a5ab6646..1728761c 100644 --- a/src/features/chat/chat-controller.ts +++ b/src/features/chat/chat-controller.ts @@ -42,7 +42,7 @@ import { createApprovalResultItem, createUserInputResultItem } from "./request-s export interface ChatControllerActions { refreshThreads: () => void; refreshSkills: (forceReload?: boolean) => void; - publishSessionMetadata: () => void; + publishAppServerMetadata: () => void; maybeNameThread: (threadId: string, turn: Turn) => void; notifyThreadArchived: (threadId: string) => void; notifyThreadRenamed: (threadId: string, name: string | null) => void; @@ -297,12 +297,12 @@ export class ChatController { this.dispatch({ type: "thread/token-usage-set", tokenUsage: params.tokenUsage }); } else if (method === "account/rateLimits/updated") { this.dispatch({ type: "thread/list-applied", rateLimit: params.rateLimits }); - this.actions.publishSessionMetadata(); + this.actions.publishAppServerMetadata(); } else if (method === "skills/changed") { this.actions.refreshSkills(true); } else if (method === "mcpServer/startupStatus/updated") { this.handleMcpStartupStatus(params); - this.actions.publishSessionMetadata(); + this.actions.publishAppServerMetadata(); } } diff --git a/src/features/chat/view.ts b/src/features/chat/view.ts index 7553a221..798ed8d6 100644 --- a/src/features/chat/view.ts +++ b/src/features/chat/view.ts @@ -44,7 +44,7 @@ import { compactContextLabel, modelOverrideMessage, reasoningEffortOverrideMessa import { executeSlashCommand as runSlashCommand, type SlashCommandExecutionResult } from "./slash-commands"; import type { ThreadReferenceInput } from "./slash-commands"; import { mcpStatusLines } from "./mcp-status"; -import { ChatSessionController } from "./chat-session-controller"; +import { ChatAppServerController } from "./chat-app-server-controller"; import { statusValue, usageLimitStatusLines } from "./status-lines"; import { ThreadHistoryLoader } from "./thread-history"; import { ThreadRenameController } from "./thread-rename"; @@ -76,7 +76,7 @@ import { renderChatPanelShell, unmountChatPanelShell, type ChatPanelSlotSnapshot import type { ChatTurnDiffViewState } from "./ui/turn-diff"; import { ChatMessageRenderer, type ChatMessageScrollIntent } from "./chat-message-renderer"; import type { OpenCodexPanelSnapshot } from "../../runtime/open-panel-snapshot"; -import type { SharedSessionMetadata } from "../../runtime/shared-app-server-state"; +import type { SharedAppServerMetadata } from "../../runtime/shared-app-server-state"; import { ChatThreadActionController } from "./thread-actions"; import { unmountReactRoot } from "../../shared/ui/react-root"; @@ -93,8 +93,8 @@ export interface CodexChatHost { refreshSharedThreadListFromOpenSurface(): void; refreshThreadList(fetchThreads: () => Promise): Promise; cachedThreadList(): readonly Thread[] | null; - publishSessionMetadata(metadata: SharedSessionMetadata): void; - cachedSessionMetadata(): SharedSessionMetadata | null; + publishAppServerMetadata(metadata: SharedAppServerMetadata): void; + cachedAppServerMetadata(): SharedAppServerMetadata | null; } interface RestoredThreadState { @@ -107,7 +107,7 @@ export class CodexChatView extends ItemView { private client: AppServerClient | null = null; private readonly connection: ConnectionManager; private readonly controller: ChatController; - private readonly session: ChatSessionController; + private readonly appServer: ChatAppServerController; private readonly history: ThreadHistoryLoader; private readonly threadActions: ChatThreadActionController; private readonly threadRename: ThreadRenameController; @@ -130,7 +130,7 @@ export class CodexChatView extends ItemView { private closing = false; private nextMessageScrollIntent: ChatMessageScrollIntent = "auto"; private lastPendingRequestFocusSignature = ""; - private scheduledSessionWarmupTimer: number | null = null; + private scheduledAppServerWarmupTimer: number | null = null; constructor( leaf: WorkspaceLeaf, @@ -204,8 +204,8 @@ export class CodexChatView extends ItemView { void this.refreshThreads(); }, refreshSkills: (forceReload) => void this.refreshSkills(forceReload), - publishSessionMetadata: () => { - this.publishSessionMetadataSnapshot(); + publishAppServerMetadata: () => { + this.publishAppServerMetadataSnapshot(); }, maybeNameThread: (threadId, turn) => { this.threadRename.maybeAutoNameThread(threadId, turn); @@ -217,13 +217,13 @@ export class CodexChatView extends ItemView { this.plugin.notifyThreadRenamed(threadId, name); }, recordMcpStartupStatus: (name, status, message) => { - this.session.recordMcpStartupStatus(name, status, message); + this.appServer.recordMcpStartupStatus(name, status, message); this.scheduleRender(); }, respondToServerRequest: (requestId, result) => this.respondToServerRequest(requestId, result), rejectServerRequest: (requestId, code, message) => this.rejectServerRequest(requestId, code, message), }); - this.session = new ChatSessionController({ + this.appServer = new ChatAppServerController({ stateStore: this.chatState, vaultPath: this.plugin.vaultPath, currentClient: () => this.connection.currentClient(), @@ -353,7 +353,7 @@ export class CodexChatView extends ItemView { this.invalidateResumeWork(); this.restoredThread = null; this.clearDeferredRestoredThreadHydration(); - this.scheduleDeferredSessionWarmup(); + this.scheduleDeferredAppServerWarmup(); return; } @@ -369,13 +369,13 @@ export class CodexChatView extends ItemView { } applyThreadListSnapshot(threads: readonly Thread[]): void { - this.session.applyThreadList(threads); + this.appServer.applyThreadList(threads); this.refreshTabHeader(); this.render(); } - applySessionMetadataSnapshot(metadata: SharedSessionMetadata): void { - this.session.applySessionMetadata(metadata); + applyAppServerMetadataSnapshot(metadata: SharedAppServerMetadata): void { + this.appServer.applyAppServerMetadata(metadata); this.render(); } @@ -457,7 +457,7 @@ export class CodexChatView extends ItemView { ); this.applyCachedSharedAppServerState(); this.render(); - this.scheduleDeferredSessionWarmup(); + this.scheduleDeferredAppServerWarmup(); this.scheduleDeferredRestoredThreadHydration(); } @@ -468,7 +468,7 @@ export class CodexChatView extends ItemView { this.invalidateResumeWork(); this.connectingPromise = null; this.clearDeferredRestoredThreadHydration(); - this.clearDeferredSessionWarmup(); + this.clearDeferredAppServerWarmup(); if (this.scheduledRenderTimer !== null) { this.containerEl.win.clearTimeout(this.scheduledRenderTimer); this.scheduledRenderTimer = null; @@ -522,9 +522,9 @@ export class CodexChatView extends ItemView { if (this.isStaleConnectionGeneration(generation)) return; this.client = this.connection.currentClient(); if (!this.client) throw new Error("Codex app-server connection did not initialize."); - const metadata = await this.session.refreshSessionMetadata(); + const metadata = await this.appServer.refreshAppServerMetadata(); if (this.isStaleConnectionGeneration(generation)) return; - if (metadata) this.plugin.publishSessionMetadata(metadata); + if (metadata) this.plugin.publishAppServerMetadata(metadata); await this.loadSharedThreadList(); if (this.isStaleConnectionGeneration(generation)) return; this.scheduleDeferredDiagnostics(); @@ -568,8 +568,8 @@ export class CodexChatView extends ItemView { if (!this.client) return; try { await this.loadSharedThreadList(); - const metadata = await this.session.refreshSessionMetadata(); - if (metadata) this.plugin.publishSessionMetadata(metadata); + const metadata = await this.appServer.refreshAppServerMetadata(); + if (metadata) this.plugin.publishAppServerMetadata(metadata); this.refreshTabHeader(); this.render(); } catch (error) { @@ -582,16 +582,16 @@ export class CodexChatView extends ItemView { await this.ensureConnected(); if (!this.client) return; this.clearDeferredDiagnostics(); - await this.session.refreshCapabilityDiagnostics(); - this.publishSessionMetadataSnapshot(); + await this.appServer.refreshCapabilityDiagnostics(); + this.publishAppServerMetadataSnapshot(); this.render(); } private async refreshSkills(forceReload = false): Promise { this.client = this.connection.currentClient(); if (!this.client) return; - await this.session.refreshSkills(forceReload); - this.publishSessionMetadataSnapshot(); + await this.appServer.refreshSkills(forceReload); + this.publishAppServerMetadataSnapshot(); this.render(); } @@ -660,19 +660,19 @@ export class CodexChatView extends ItemView { } private async loadSharedThreadList(): Promise { - const threads = await this.plugin.refreshThreadList(() => this.session.loadThreadList()); - this.session.applyThreadList(threads); + const threads = await this.plugin.refreshThreadList(() => this.appServer.loadThreadList()); + this.appServer.applyThreadList(threads); } - private publishSessionMetadataSnapshot(): void { - this.plugin.publishSessionMetadata(this.session.sessionMetadataSnapshot()); + private publishAppServerMetadataSnapshot(): void { + this.plugin.publishAppServerMetadata(this.appServer.appServerMetadataSnapshot()); } private applyCachedSharedAppServerState(): void { const threads = this.plugin.cachedThreadList(); - if (threads) this.session.applyThreadList(threads); - const metadata = this.plugin.cachedSessionMetadata(); - if (metadata) this.session.applySessionMetadata(metadata); + if (threads) this.appServer.applyThreadList(threads); + const metadata = this.plugin.cachedAppServerMetadata(); + if (metadata) this.appServer.applyAppServerMetadata(metadata); } private requestWorkspaceLayoutSave(): void { @@ -712,7 +712,7 @@ export class CodexChatView extends ItemView { let optimisticUserId: string | null = null; try { if (!this.state.activeThreadId) { - const threadResponse = await this.session.startThread(); + const threadResponse = await this.appServer.startThread(); if (!threadResponse) return; this.notifyActiveThreadIdentityChanged(); this.threadRename.resetThreadTurnPresence(false); @@ -1151,19 +1151,19 @@ export class CodexChatView extends ItemView { this.scheduledRestoredThreadHydrationTimer = null; } - private scheduleDeferredSessionWarmup(): void { - if (!this.opened || this.connection.isConnected() || this.scheduledSessionWarmupTimer !== null) return; - this.scheduledSessionWarmupTimer = this.containerEl.win.setTimeout(() => { - this.scheduledSessionWarmupTimer = null; + private scheduleDeferredAppServerWarmup(): void { + if (!this.opened || this.connection.isConnected() || this.scheduledAppServerWarmupTimer !== null) return; + this.scheduledAppServerWarmupTimer = this.containerEl.win.setTimeout(() => { + this.scheduledAppServerWarmupTimer = null; if (!this.opened || this.closing || this.connection.isConnected()) return; void this.ensureConnected(); }, 0); } - private clearDeferredSessionWarmup(): void { - if (this.scheduledSessionWarmupTimer === null) return; - this.containerEl.win.clearTimeout(this.scheduledSessionWarmupTimer); - this.scheduledSessionWarmupTimer = null; + private clearDeferredAppServerWarmup(): void { + if (this.scheduledAppServerWarmupTimer === null) return; + this.containerEl.win.clearTimeout(this.scheduledAppServerWarmupTimer); + this.scheduledAppServerWarmupTimer = null; } private activeThreadTitle(): string | null { @@ -1523,8 +1523,8 @@ export class CodexChatView extends ItemView { private async refreshDeferredDiagnostics(): Promise { if (!this.connection.isConnected()) return; - await this.session.refreshCapabilityDiagnostics({ cachedSessionMetadata: true }); - this.publishSessionMetadataSnapshot(); + await this.appServer.refreshCapabilityDiagnostics({ cachedAppServerMetadata: true }); + this.publishAppServerMetadataSnapshot(); this.render(); } diff --git a/src/main.ts b/src/main.ts index 0e723e6e..ab744a6b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,11 +11,11 @@ import type { Thread } from "./generated/app-server/v2/Thread"; import type { Model } from "./generated/app-server/v2/Model"; import { applySharedModels, - applySharedSessionMetadata, + applySharedAppServerMetadata, applySharedThreadList, createSharedAppServerState, type SharedAppServerState, - type SharedSessionMetadata, + type SharedAppServerMetadata, } from "./runtime/shared-app-server-state"; import { DEFAULT_SETTINGS, getVaultPath, normalizeSettings, settingsMatchNormalizedData, type CodexPanelSettings } from "./settings/model"; import { CodexPanelSettingTab } from "./settings/tab"; @@ -262,11 +262,11 @@ export default class CodexPanelPlugin extends Plugin { return this.sharedAppServerState.threads; } - publishSessionMetadata(metadata: SharedSessionMetadata): void { - this.sharedAppServerState = applySharedSessionMetadata(this.sharedAppServerState, metadata); + publishAppServerMetadata(metadata: SharedAppServerMetadata): void { + this.sharedAppServerState = applySharedAppServerMetadata(this.sharedAppServerState, metadata); for (const leaf of this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL)) { if (leaf.view instanceof CodexChatView) { - leaf.view.applySessionMetadataSnapshot(metadata); + leaf.view.applyAppServerMetadataSnapshot(metadata); } } } @@ -280,8 +280,8 @@ export default class CodexPanelPlugin extends Plugin { } } - cachedSessionMetadata(): SharedSessionMetadata | null { - if (!this.sharedAppServerState.sessionMetadataLoaded) return null; + cachedAppServerMetadata(): SharedAppServerMetadata | null { + if (!this.sharedAppServerState.appServerMetadataLoaded) return null; return { effectiveConfig: this.sharedAppServerState.effectiveConfig, availableModels: this.sharedAppServerState.availableModels, diff --git a/src/runtime/shared-app-server-state.ts b/src/runtime/shared-app-server-state.ts index 6a524593..efe050c1 100644 --- a/src/runtime/shared-app-server-state.ts +++ b/src/runtime/shared-app-server-state.ts @@ -6,7 +6,7 @@ import type { RateLimitSnapshot } from "../generated/app-server/v2/RateLimitSnap import type { SkillMetadata } from "../generated/app-server/v2/SkillMetadata"; import type { Thread } from "../generated/app-server/v2/Thread"; -export interface SharedSessionMetadata { +export interface SharedAppServerMetadata { effectiveConfig: ConfigReadResponse | null; availableModels: readonly Model[]; availableSkills: readonly SkillMetadata[]; @@ -14,15 +14,15 @@ export interface SharedSessionMetadata { appServerDiagnostics: AppServerDiagnostics; } -export interface SharedAppServerState extends SharedSessionMetadata { +export interface SharedAppServerState extends SharedAppServerMetadata { threads: readonly Thread[] | null; - sessionMetadataLoaded: boolean; + appServerMetadataLoaded: boolean; } export function createSharedAppServerState(): SharedAppServerState { return { threads: null, - sessionMetadataLoaded: false, + appServerMetadataLoaded: false, effectiveConfig: null, availableModels: [], availableSkills: [], @@ -38,11 +38,11 @@ export function applySharedThreadList(state: SharedAppServerState, threads: read }; } -export function applySharedSessionMetadata(state: SharedAppServerState, metadata: SharedSessionMetadata): SharedAppServerState { +export function applySharedAppServerMetadata(state: SharedAppServerState, metadata: SharedAppServerMetadata): SharedAppServerState { return { ...state, ...metadata, - sessionMetadataLoaded: true, + appServerMetadataLoaded: true, }; } diff --git a/src/settings/tab.ts b/src/settings/tab.ts index 87232030..6d86aa36 100644 --- a/src/settings/tab.ts +++ b/src/settings/tab.ts @@ -1,7 +1,7 @@ import { type App, Notice, type Plugin, PluginSettingTab, Setting } from "obsidian"; import type { AppServerClient } from "../app-server/client"; -import { withAppServerSession } from "../app-server/session-client"; +import { withAppServerConnection } from "../app-server/connection-client"; import { DEFAULT_CODEX_PATH } from "../constants"; import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort"; import type { HookMetadata } from "../generated/app-server/v2/HookMetadata"; @@ -231,7 +231,7 @@ export class CodexPanelSettingTab extends PluginSettingTab { let failedCount = 0; try { - const result = await this.withSettingsSession((client) => loadSettingsData(client, this.plugin.vaultPath)); + const result = await this.withSettingsConnection((client) => loadSettingsData(client, this.plugin.vaultPath)); if (result.models.ok) { this.models = result.models.data; @@ -284,7 +284,7 @@ export class CodexPanelSettingTab extends PluginSettingTab { this.hooksStatus = ""; this.display(); try { - const hooks = await this.withSettingsSession((client) => loadHookData(client, this.plugin.vaultPath)); + const hooks = await this.withSettingsConnection((client) => loadHookData(client, this.plugin.vaultPath)); this.hooks = hooks.hooks; this.hookWarnings = hooks.warnings; this.hookErrors = hooks.errors; @@ -304,7 +304,7 @@ export class CodexPanelSettingTab extends PluginSettingTab { this.hooksStatus = ""; this.display(); try { - await this.withSettingsSession((client) => client.trustHook(hook)); + await this.withSettingsConnection((client) => client.trustHook(hook)); this.hooksStatus = "Trusted hook definition."; await this.loadHooks(); } catch (error) { @@ -320,7 +320,7 @@ export class CodexPanelSettingTab extends PluginSettingTab { this.hooksStatus = ""; this.display(); try { - await this.withSettingsSession((client) => client.setHookEnabled(hook, enabled)); + await this.withSettingsConnection((client) => client.setHookEnabled(hook, enabled)); this.hooksStatus = enabled ? "Enabled hook." : "Disabled hook."; await this.loadHooks(); } catch (error) { @@ -357,7 +357,7 @@ export class CodexPanelSettingTab extends PluginSettingTab { this.archivedThreadsStatus = ""; this.display(); try { - const response = await this.withSettingsSession((client) => client.unarchiveThread(threadId)); + const response = await this.withSettingsConnection((client) => client.unarchiveThread(threadId)); this.archivedThreads = this.archivedThreads.filter((thread) => thread.id !== threadId); this.archivedThreadsLoaded = true; this.archivedThreadsStatus = `Restored "${archivedThreadDisplayTitle(response.thread)}".`; @@ -371,8 +371,8 @@ export class CodexPanelSettingTab extends PluginSettingTab { } } - private async withSettingsSession(operation: (client: AppServerClient) => Promise): Promise { - return withAppServerSession(this.plugin.settings.codexPath, this.plugin.vaultPath, operation); + private async withSettingsConnection(operation: (client: AppServerClient) => Promise): Promise { + return withAppServerConnection(this.plugin.settings.codexPath, this.plugin.vaultPath, operation); } private modelOptions(): Model[] { diff --git a/tests/features/chat/chat-session-controller.test.ts b/tests/features/chat/chat-app-server-controller.test.ts similarity index 85% rename from tests/features/chat/chat-session-controller.test.ts rename to tests/features/chat/chat-app-server-controller.test.ts index 1f37ec6c..1b362e13 100644 --- a/tests/features/chat/chat-session-controller.test.ts +++ b/tests/features/chat/chat-app-server-controller.test.ts @@ -1,14 +1,14 @@ import { describe, expect, it, vi } from "vitest"; import type { AppServerClient } from "../../../src/app-server/client"; -import { ChatSessionController } from "../../../src/features/chat/chat-session-controller"; +import { ChatAppServerController } from "../../../src/features/chat/chat-app-server-controller"; import { createChatState, createChatStateStore } from "../../../src/features/chat/chat-state"; import type { Model } from "../../../src/generated/app-server/v2/Model"; import type { RateLimitSnapshot } from "../../../src/generated/app-server/v2/RateLimitSnapshot"; import type { SkillMetadata } from "../../../src/generated/app-server/v2/SkillMetadata"; -describe("ChatSessionController", () => { - it("reuses cached session metadata for deferred diagnostics", async () => { +describe("ChatAppServerController", () => { + it("reuses cached app-server metadata for deferred diagnostics", async () => { const state = createChatState(); const stateStore = createChatStateStore(state); @@ -27,7 +27,7 @@ describe("ChatSessionController", () => { readModelProviderCapabilities: vi.fn().mockResolvedValue({}), } as unknown as AppServerClient; - const controller = new ChatSessionController({ + const controller = new ChatAppServerController({ stateStore, vaultPath: "/vault", currentClient: () => client, @@ -35,12 +35,12 @@ describe("ChatSessionController", () => { forceMessagesToBottom: () => undefined, }); - await controller.refreshSessionMetadata(); + await controller.refreshAppServerMetadata(); listModels.mockClear(); listSkills.mockClear(); readAccountRateLimits.mockClear(); - await controller.refreshCapabilityDiagnostics({ cachedSessionMetadata: true }); + await controller.refreshCapabilityDiagnostics({ cachedAppServerMetadata: true }); expect(listModels).not.toHaveBeenCalled(); expect(listSkills).not.toHaveBeenCalled(); diff --git a/tests/features/chat/chat-controller.test.ts b/tests/features/chat/chat-controller.test.ts index 0df9ab23..6810f798 100644 --- a/tests/features/chat/chat-controller.test.ts +++ b/tests/features/chat/chat-controller.test.ts @@ -24,7 +24,7 @@ function controllerForState( return new ChatController(testStoreForState(state), { refreshThreads: vi.fn(), refreshSkills: vi.fn(), - publishSessionMetadata: vi.fn(), + publishAppServerMetadata: vi.fn(), maybeNameThread: vi.fn(), notifyThreadArchived: vi.fn(), notifyThreadRenamed: vi.fn(), @@ -601,8 +601,8 @@ describe("ChatController", () => { it("stores account rate limit updates outside thread scope", () => { const state = createChatState(); - const publishSessionMetadata = vi.fn(); - const controller = controllerForState(state, { publishSessionMetadata }); + const publishAppServerMetadata = vi.fn(); + const controller = controllerForState(state, { publishAppServerMetadata }); controller.handleNotification({ method: "account/rateLimits/updated", @@ -623,14 +623,14 @@ describe("ChatController", () => { limitId: "codex", primary: { usedPercent: 64 }, }); - expect(publishSessionMetadata).toHaveBeenCalledOnce(); + expect(publishAppServerMetadata).toHaveBeenCalledOnce(); }); it("records MCP startup status for diagnostics without a chat system message", () => { const state = createChatState(); const recordMcpStartupStatus = vi.fn(); - const publishSessionMetadata = vi.fn(); - const controller = controllerForState(state, { recordMcpStartupStatus, publishSessionMetadata }); + const publishAppServerMetadata = vi.fn(); + const controller = controllerForState(state, { recordMcpStartupStatus, publishAppServerMetadata }); controller.handleNotification({ method: "mcpServer/startupStatus/updated", @@ -642,7 +642,7 @@ describe("ChatController", () => { } satisfies Extract); expect(recordMcpStartupStatus).toHaveBeenCalledWith("github", "failed", "missing token"); - expect(publishSessionMetadata).toHaveBeenCalledOnce(); + expect(publishAppServerMetadata).toHaveBeenCalledOnce(); expect(state.displayItems).toEqual([]); }); }); diff --git a/tests/features/chat/view-connection.test.ts b/tests/features/chat/view-connection.test.ts index 3a4a69e2..56e2a107 100644 --- a/tests/features/chat/view-connection.test.ts +++ b/tests/features/chat/view-connection.test.ts @@ -103,16 +103,16 @@ describe("CodexChatView connection lifecycle", () => { expect((view as unknown as { state: { listedThreads: unknown[] } }).state.listedThreads).toEqual(threads); }); - it("publishes session metadata after connecting", async () => { - const publishSessionMetadata = vi.fn(); + it("publishes app-server metadata after connecting", async () => { + const publishAppServerMetadata = vi.fn(); connectionMock.state.client = connectedClient(); const view = await chatView({ - host: chatHost({ publishSessionMetadata }), + host: chatHost({ publishAppServerMetadata }), }); await view.connect(); - expect(publishSessionMetadata).toHaveBeenCalledWith( + expect(publishAppServerMetadata).toHaveBeenCalledWith( expect.objectContaining({ effectiveConfig: {}, availableModels: [], @@ -205,7 +205,7 @@ describe("CodexChatView connection lifecycle", () => { expect(client.threadTurnsList).toHaveBeenCalledWith("thread-1", null, 20); }); - it("warms session metadata for an empty restored panel after the shell is open", async () => { + it("warms app-server metadata for an empty restored panel after the shell is open", async () => { vi.useFakeTimers(); const client = connectedClient({ listThreads: vi.fn().mockResolvedValue({ data: [threadFixture("thread-1")] }), @@ -232,7 +232,7 @@ describe("CodexChatView connection lifecycle", () => { const view = await chatView({ host: chatHost({ cachedThreadList: vi.fn(() => [cachedThread] as never[]), - cachedSessionMetadata: vi.fn( + cachedAppServerMetadata: vi.fn( () => ({ effectiveConfig: { config: { model: "gpt-cached" }, origins: {}, layers: [] }, @@ -882,8 +882,8 @@ function chatHost(overrides: Partial = {}): CodexChatHost { (fetchThreads: () => Promise) => fetchThreads() as Promise, ) as CodexChatHost["refreshThreadList"], cachedThreadList: vi.fn(() => null), - publishSessionMetadata: vi.fn(), - cachedSessionMetadata: vi.fn(() => null), + publishAppServerMetadata: vi.fn(), + cachedAppServerMetadata: vi.fn(() => null), ...overrides, }; } diff --git a/tests/main.test.ts b/tests/main.test.ts index 93cc7401..b541d03b 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -412,8 +412,8 @@ function chatView(CodexChatViewCtor: typeof CodexChatView, leaf: TestLeaf) { refreshThreadsViewLiveState: vi.fn(), refreshThreadList: vi.fn((fetchThreads: () => Promise) => fetchThreads() as Promise), cachedThreadList: vi.fn(() => null), - publishSessionMetadata: vi.fn(), - cachedSessionMetadata: vi.fn(() => null), + publishAppServerMetadata: vi.fn(), + cachedAppServerMetadata: vi.fn(() => null), }, ); } diff --git a/tests/settings/settings-tab.test.ts b/tests/settings/settings-tab.test.ts index 523e20bd..a764a81e 100644 --- a/tests/settings/settings-tab.test.ts +++ b/tests/settings/settings-tab.test.ts @@ -15,12 +15,12 @@ const { JSDOM } = require("jsdom") as { JSDOM: new (html: string) => { window: Window & typeof globalThis }; }; -const { withAppServerSessionMock } = vi.hoisted(() => ({ - withAppServerSessionMock: vi.fn(), +const { withAppServerConnectionMock } = vi.hoisted(() => ({ + withAppServerConnectionMock: vi.fn(), })); -vi.mock("../../src/app-server/session-client", () => ({ - withAppServerSession: withAppServerSessionMock, +vi.mock("../../src/app-server/connection-client", () => ({ + withAppServerConnection: withAppServerConnectionMock, })); describe("settings tab", () => { @@ -33,7 +33,7 @@ describe("settings tab", () => { vi.stubGlobal("HTMLInputElement", dom.window.HTMLInputElement); vi.stubGlobal("HTMLSelectElement", dom.window.HTMLSelectElement); vi.stubGlobal("Event", dom.window.Event); - withAppServerSessionMock.mockReset(); + withAppServerConnectionMock.mockReset(); notices.length = 0; }); @@ -68,7 +68,7 @@ describe("settings tab", () => { it("auto-loads settings data once and keeps one global refresh button", async () => { const client = settingsClient(); - withAppServerSessionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + withAppServerConnectionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => operation(client), ); const tab = newSettingsTab(); @@ -76,7 +76,7 @@ describe("settings tab", () => { tab.display(); await flushPromises(); - expect(withAppServerSessionMock).toHaveBeenCalledTimes(1); + expect(withAppServerConnectionMock).toHaveBeenCalledTimes(1); expect(client.listModels).toHaveBeenCalledTimes(1); expect(client.listHooks).toHaveBeenCalledTimes(1); expect(client.listThreads).toHaveBeenCalledWith("/vault", true); @@ -84,7 +84,7 @@ describe("settings tab", () => { tab.display(); await flushPromises(); - expect(withAppServerSessionMock).toHaveBeenCalledTimes(1); + expect(withAppServerConnectionMock).toHaveBeenCalledTimes(1); expect(buttonTexts(tab)).toContain("Refresh Codex data"); expect(buttonTexts(tab)).not.toContain("Load models"); expect(buttonTexts(tab)).not.toContain("Load hooks"); @@ -154,7 +154,7 @@ describe("settings tab", () => { models: [model("gpt-5.5")], threads: [thread({ id: "thread-new", preview: "New" })], }); - withAppServerSessionMock + withAppServerConnectionMock .mockImplementationOnce((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => operation(firstClient), ) @@ -168,7 +168,7 @@ describe("settings tab", () => { clickButton(tab, "Refresh Codex data"); await flushPromises(); - expect(withAppServerSessionMock).toHaveBeenCalledTimes(2); + expect(withAppServerConnectionMock).toHaveBeenCalledTimes(2); expect(tab.containerEl.textContent).toContain("gpt-5.5"); expect(tab.containerEl.textContent).toContain("New"); expect(tab.containerEl.textContent).not.toContain("Old"); @@ -177,7 +177,7 @@ describe("settings tab", () => { it("uses cached models initially and publishes refreshed models", async () => { const publishModels = vi.fn(); const client = settingsClient({ models: [model("gpt-5.5")] }); - withAppServerSessionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + withAppServerConnectionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => operation(client), ); const tab = newSettingsTab({ cachedModels: [model("gpt-cached")], publishModels }); @@ -198,7 +198,7 @@ describe("settings tab", () => { hooksError: new Error("hooks unavailable"), threads: [thread({ preview: "Archived thread" })], }); - withAppServerSessionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + withAppServerConnectionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => operation(client), ); const tab = newSettingsTab(); @@ -217,7 +217,7 @@ describe("settings tab", () => { hooks: [hook({ key: "hook-1", command: "node hook.js", currentHash: "abc123", trustStatus: "untrusted" })], threads: [thread({ id: "thread-archived", preview: "Archived thread" })], }); - withAppServerSessionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => + withAppServerConnectionMock.mockImplementation((_codexPath: string, _cwd: string, operation: (client: unknown) => Promise) => operation(client), ); const tab = newSettingsTab();