mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Add chat app-server gateway wiring
This commit is contained in:
parent
1f95937dcf
commit
316806c069
10 changed files with 184 additions and 131 deletions
|
|
@ -62,7 +62,7 @@ Keep new code near the state or API it owns. A feature may import another featur
|
|||
|
||||
Generated app-server types should stay behind app-server connection and protocol adapter modules. Chat-local app-server integration modules may consume app-server protocol projections, but not raw generated bindings. If a domain, shared, settings, workspace, or UI module needs app-server payloads, add or reuse a panel-owned projection at the boundary instead of importing generated payload types directly.
|
||||
|
||||
Chat application workflows should not import root `src/app-server/` modules or receive `AppServerClient` access directly. Keep app-server client access, connection freshness checks, vault-path injection, and payload projection in `src/features/chat/app-server/` transports or host-owned wiring, then pass chat-owned workflow contracts into application modules.
|
||||
Chat application workflows should not import root `src/app-server/` modules or receive `AppServerClient` access directly. Keep app-server client access, connection freshness checks, vault-path injection, and payload projection in `src/features/chat/app-server/` transports or host-owned wiring, then pass chat-owned workflow contracts into application modules. Keep session-level adapter composition at the chat app-server boundary so host bundles consume a composed app-server adapter instead of importing each transport factory.
|
||||
|
||||
Chat panel-visible state belongs in `ChatStateStore` and should flow through named reducer actions and the shell-state adapter. Use Preact Signals only in `src/features/chat/panel/shell-state.tsx`; lint enforces this boundary. When a surface needs fewer dependencies, add or reuse a named shell-state projection instead of importing `@preact/signals` elsewhere.
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ private pattern js_module_reference() {
|
|||
}
|
||||
|
||||
js_module_reference() as $stmt where {
|
||||
$stmt <: contains `$source` where { $source <: r"^[\"'](?:(?:\./)?app-server|(?:\.\./)+app-server|src/app-server)/[^/\"']+[\"']$" },
|
||||
$stmt <: contains `$source` where {
|
||||
$source <: r"^[\"'](?:(?:\./)?app-server|(?:\.\./)+app-server|src/app-server)/[^/\"']+[\"']$",
|
||||
not { $filename <: r".*/src/features/chat/[^/]+/.*", $source <: r"^[\"']\.\./app-server/[^/\"']+[\"']$" }
|
||||
},
|
||||
register_diagnostic(span=$stmt, message="Import app-server boundary modules from responsibility subfolders, not src/app-server root modules.", severity="error")
|
||||
}
|
||||
|
|
|
|||
102
src/features/chat/app-server/session-gateway.ts
Normal file
102
src/features/chat/app-server/session-gateway.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import type { AppServerClient } from "../../../app-server/connection/client";
|
||||
import type { AppServerClientAccess } from "../../../app-server/connection/client-access";
|
||||
import { readFileBase64 as readAppServerFileBase64 } from "../../../app-server/services/files";
|
||||
import { renameThread as renameAppServerThread } from "../../../app-server/services/threads";
|
||||
import type { CodexInput } from "../../../domain/chat/input";
|
||||
import type { ChatTurnTransport } from "../application/conversation/turn-transport";
|
||||
import type { RuntimeSettingsTransport } from "../application/runtime/settings-transport";
|
||||
import type { ThreadGoalReadTransport, ThreadGoalTransport } from "../application/threads/goal-transport";
|
||||
import type { ThreadHistoryTransport, ThreadResumeTransport } from "../application/threads/thread-loading-transport";
|
||||
import type { ThreadMutationTransport } from "../application/threads/thread-mutation-transport";
|
||||
import { createChatThreadGoalReadTransport, createChatThreadGoalTransport } from "./goals/transport";
|
||||
import { createThreadReferenceResolver, type ThreadReferenceResolver } from "./references/thread-reference-resolver";
|
||||
import { createChatRuntimeSettingsTransport } from "./runtime/thread-settings-transport";
|
||||
import { createChatThreadHistoryTransport, createChatThreadResumeTransport } from "./threads/loading-transport";
|
||||
import { createChatThreadMutationTransport } from "./threads/transport";
|
||||
import { createChatTurnTransport } from "./turns/transport";
|
||||
|
||||
export interface ChatAppServerGatewayHost {
|
||||
vaultPath: string;
|
||||
currentClient(): AppServerClient | null;
|
||||
connectedClient(): Promise<AppServerClient | null>;
|
||||
}
|
||||
|
||||
interface ChatThreadReferenceResolverOptions {
|
||||
codexInput(text: string): CodexInput;
|
||||
addSystemMessage(text: string): void;
|
||||
setStatus(status: string): void;
|
||||
}
|
||||
|
||||
export interface ChatAppServerGateway {
|
||||
clientAccess: AppServerClientAccess;
|
||||
turn: ChatTurnTransport;
|
||||
runtimeSettings: RuntimeSettingsTransport;
|
||||
threadHistory: ThreadHistoryTransport;
|
||||
threadResume: ThreadResumeTransport;
|
||||
threadMutation: ThreadMutationTransport;
|
||||
threadGoalRead: ThreadGoalReadTransport;
|
||||
threadGoal: ThreadGoalTransport;
|
||||
connectionAvailable(): boolean;
|
||||
readFileBase64(path: string, options?: { timeoutMs?: number }): Promise<string>;
|
||||
renameThread(threadId: string, name: string): Promise<boolean>;
|
||||
threadReferences(options: ChatThreadReferenceResolverOptions): ThreadReferenceResolver;
|
||||
}
|
||||
|
||||
export function createChatAppServerGateway(host: ChatAppServerGatewayHost): ChatAppServerGateway {
|
||||
return {
|
||||
clientAccess: createCurrentClientAccess(() => host.currentClient()),
|
||||
turn: createChatTurnTransport(host),
|
||||
runtimeSettings: createChatRuntimeSettingsTransport(host),
|
||||
threadHistory: createChatThreadHistoryTransport(host),
|
||||
threadResume: createChatThreadResumeTransport(host),
|
||||
threadMutation: createChatThreadMutationTransport(host),
|
||||
threadGoalRead: createChatThreadGoalReadTransport(host),
|
||||
threadGoal: createChatThreadGoalTransport(host),
|
||||
connectionAvailable: () => host.currentClient() !== null,
|
||||
readFileBase64: (path, options) => readCurrentClientFileBase64(host, path, options),
|
||||
renameThread: (threadId, name) => renameCurrentClientThread(host, threadId, name),
|
||||
threadReferences: (options) =>
|
||||
createThreadReferenceResolver({
|
||||
currentClient: () => host.currentClient(),
|
||||
codexInput: (text) => options.codexInput(text),
|
||||
addSystemMessage: (text) => {
|
||||
options.addSystemMessage(text);
|
||||
},
|
||||
setStatus: (status) => {
|
||||
options.setStatus(status);
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function createCurrentClientAccess(currentClient: () => AppServerClient | null): AppServerClientAccess {
|
||||
return {
|
||||
withClient: async (operation) => {
|
||||
const client = currentClient();
|
||||
if (!client) throw new Error("Codex app-server is not connected.");
|
||||
const result = await operation(client);
|
||||
if (currentClient() !== client) {
|
||||
throw new Error("Codex app-server connection changed while running the operation.");
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function readCurrentClientFileBase64(
|
||||
host: ChatAppServerGatewayHost,
|
||||
path: string,
|
||||
options: { timeoutMs?: number } = {},
|
||||
): Promise<string> {
|
||||
const client = host.currentClient();
|
||||
if (!client) return "";
|
||||
const data = await readAppServerFileBase64(client, path, options);
|
||||
return host.currentClient() === client ? data : "";
|
||||
}
|
||||
|
||||
async function renameCurrentClientThread(host: ChatAppServerGatewayHost, threadId: string, name: string): Promise<boolean> {
|
||||
const client = host.currentClient();
|
||||
if (!client) return false;
|
||||
await renameAppServerThread(client, threadId, name);
|
||||
return host.currentClient() === client;
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ import type { createThreadGoalSyncActions } from "../application/threads/goal-ac
|
|||
import type { ChatPanelEnvironment } from "./contracts";
|
||||
import type { ChatViewDeferredTasks } from "./deferred-work";
|
||||
|
||||
export type CurrentAppServerClient = () => AppServerClient | null;
|
||||
type CurrentAppServerClient = () => AppServerClient | null;
|
||||
|
||||
type RespondRequestId = Parameters<AppServerClient["respondToServerRequest"]>[0];
|
||||
type RejectRequestId = Parameters<AppServerClient["rejectServerRequest"]>[0];
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import type { ConnectionManager } from "../../../app-server/connection/connection-manager";
|
||||
import { createChatRuntimeSettingsTransport } from "../app-server/runtime/thread-settings-transport";
|
||||
import type { ChatAppServerGateway } from "../app-server/session-gateway";
|
||||
import { type ChatRuntimeSettingsActions, createChatRuntimeSettingsActions } from "../application/runtime/settings-actions";
|
||||
import { runtimeSnapshotForChatState } from "../application/runtime/snapshot";
|
||||
import type { ChatStateStore } from "../application/state/store";
|
||||
import { collaborationModeLabel as formatCollaborationModeLabel } from "../domain/runtime/labels";
|
||||
import { type ChatPanelRuntimeProjection, createChatPanelRuntimeProjection } from "../panel/runtime-status-projection";
|
||||
import type { CurrentAppServerClient } from "./connection-bundle";
|
||||
import type { ChatPanelEnvironment } from "./contracts";
|
||||
|
||||
export type ChatPanelRuntimeSettingsActions = ChatRuntimeSettingsActions;
|
||||
|
|
@ -28,26 +27,24 @@ export function createRuntimeBundle(
|
|||
host: ChatPanelRuntimeHost,
|
||||
input: {
|
||||
connection: ConnectionManager;
|
||||
currentClient: CurrentAppServerClient;
|
||||
appServer: ChatAppServerGateway;
|
||||
status: ChatPanelRuntimeStatus;
|
||||
},
|
||||
): ChatPanelRuntimeBundle {
|
||||
return {
|
||||
settings: createSessionRuntimeSettingsActions(host, input.currentClient, input.status),
|
||||
settings: createSessionRuntimeSettingsActions(host, input.appServer, input.status),
|
||||
projection: createSessionRuntimeProjection(host, input.connection),
|
||||
};
|
||||
}
|
||||
|
||||
function createSessionRuntimeSettingsActions(
|
||||
host: ChatPanelRuntimeHost,
|
||||
currentClient: CurrentAppServerClient,
|
||||
appServer: ChatAppServerGateway,
|
||||
status: ChatPanelRuntimeStatus,
|
||||
): ChatPanelRuntimeSettingsActions {
|
||||
return createChatRuntimeSettingsActions({
|
||||
stateStore: host.stateStore,
|
||||
runtimeTransport: createChatRuntimeSettingsTransport({
|
||||
currentClient,
|
||||
}),
|
||||
runtimeTransport: appServer.runtimeSettings,
|
||||
runtimeSnapshotForState: runtimeSnapshotForChatState,
|
||||
collaborationModeLabel: () => collaborationModeLabel(host.stateStore),
|
||||
addSystemMessage: (text) => {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { ConnectionManager } from "../../../app-server/connection/connection-man
|
|||
import { isStaleAppServerSharedQueryContextError } from "../../../app-server/query/shared-queries";
|
||||
import { createLocalIdSource, type LocalIdSource } from "../../../shared/id/local-id";
|
||||
import type { ConnectionWorkTracker } from "../../../shared/lifecycle/connection-work";
|
||||
import { createChatAppServerGateway } from "../app-server/session-gateway";
|
||||
import type { ChatAction, ChatConnectionPhase } from "../application/state/root-reducer";
|
||||
import type { ChatStateStore } from "../application/state/store";
|
||||
import type { ActiveThreadIdentitySync } from "../application/threads/active-thread-identity-sync";
|
||||
|
|
@ -71,6 +72,18 @@ export function createChatPanelSessionGraph(host: ChatPanelSessionGraphHost): Ch
|
|||
const localItemIds = createLocalIdSource();
|
||||
const connection = createConnectionManager(environment);
|
||||
const currentClient = () => connection.currentClient();
|
||||
let ensureConnected: () => Promise<void> = async () => {
|
||||
throw new Error("Codex app-server connection controller is not initialized.");
|
||||
};
|
||||
const connectedClient = async () => {
|
||||
await ensureConnected();
|
||||
return currentClient();
|
||||
};
|
||||
const appServer = createChatAppServerGateway({
|
||||
vaultPath: environment.plugin.settingsRef.vaultPath,
|
||||
currentClient,
|
||||
connectedClient,
|
||||
});
|
||||
const status = createSessionStatus(stateStore, localItemIds);
|
||||
const refreshTabHeader = () => {
|
||||
host.environment.view.refreshTabHeader();
|
||||
|
|
@ -88,7 +101,7 @@ export function createChatPanelSessionGraph(host: ChatPanelSessionGraphHost): Ch
|
|||
};
|
||||
|
||||
const threadFoundation = createThreadFoundation(host, {
|
||||
currentClient,
|
||||
appServer,
|
||||
localItemIds,
|
||||
status,
|
||||
refreshLiveState,
|
||||
|
|
@ -120,21 +133,16 @@ export function createChatPanelSessionGraph(host: ChatPanelSessionGraphHost): Ch
|
|||
inboundHandler,
|
||||
} = connectionBundle;
|
||||
const { threads: serverThreads } = connectionBundle.serverActions;
|
||||
const ensureConnected = () => connectionController.ensureConnected();
|
||||
const connectedClient = async () => {
|
||||
await ensureConnected();
|
||||
return currentClient();
|
||||
};
|
||||
ensureConnected = () => connectionController.ensureConnected();
|
||||
const refreshActiveThreads = () => connectionController.refreshActiveThreads();
|
||||
const runtime = createRuntimeBundle(host, {
|
||||
connection,
|
||||
currentClient,
|
||||
appServer,
|
||||
status,
|
||||
});
|
||||
const threadLifecycle = createThreadLifecycleBundle(host, {
|
||||
currentClient,
|
||||
appServer,
|
||||
localItemIds,
|
||||
connectedClient,
|
||||
ensureConnected,
|
||||
status,
|
||||
serverThreads,
|
||||
|
|
@ -148,8 +156,7 @@ export function createChatPanelSessionGraph(host: ChatPanelSessionGraphHost): Ch
|
|||
runtimeSettings: runtime.settings,
|
||||
});
|
||||
const threadActions = createThreadActionBundle(host, {
|
||||
currentClient,
|
||||
connectedClient,
|
||||
appServer,
|
||||
status,
|
||||
composerController: composer.controller,
|
||||
foundation: threadFoundation,
|
||||
|
|
@ -161,8 +168,7 @@ export function createChatPanelSessionGraph(host: ChatPanelSessionGraphHost): Ch
|
|||
connection,
|
||||
localItemIds,
|
||||
ensureConnected,
|
||||
connectedClient,
|
||||
currentClient,
|
||||
appServer,
|
||||
status,
|
||||
inboundHandler,
|
||||
threadLifecycle: threadLifecycle.lifecycle,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,12 @@
|
|||
import { Notice } from "obsidian";
|
||||
|
||||
import type { AppServerClientAccess } from "../../../app-server/connection/client-access";
|
||||
import { readFileBase64 } from "../../../app-server/services/files";
|
||||
import { recoverRolloutTokenUsage } from "../../../app-server/services/rollout-token-usage";
|
||||
import { renameThread as renameAppServerThread } from "../../../app-server/services/threads";
|
||||
import { normalizeExplicitThreadName } from "../../../domain/threads/model";
|
||||
import type { LocalIdSource } from "../../../shared/id/local-id";
|
||||
import { createThreadOperations, type ThreadOperations } from "../../threads/workflows/thread-operations";
|
||||
import { createThreadTitleService, type ThreadTitleService } from "../../threads/workflows/thread-title-service";
|
||||
import type { ChatServerThreadActions } from "../app-server/actions/threads";
|
||||
import { createChatThreadGoalReadTransport, createChatThreadGoalTransport } from "../app-server/goals/transport";
|
||||
import { createChatThreadHistoryTransport, createChatThreadResumeTransport } from "../app-server/threads/loading-transport";
|
||||
import { createChatThreadMutationTransport } from "../app-server/threads/transport";
|
||||
import type { ChatAppServerGateway } from "../app-server/session-gateway";
|
||||
import { messageStreamItems } from "../application/state/message-stream";
|
||||
import type { ChatStateStore } from "../application/state/store";
|
||||
import type { ActiveThreadIdentitySync } from "../application/threads/active-thread-identity-sync";
|
||||
|
|
@ -32,7 +27,6 @@ import { createThreadNavigationActions } from "../application/threads/thread-nav
|
|||
import { threadTitleContextFromMessageStreamItems } from "../application/threads/title-context";
|
||||
import type { ChatComposerController } from "../panel/composer-controller";
|
||||
import { createToolbarPanelActions, type ToolbarPanelActions } from "../panel/toolbar-actions";
|
||||
import type { CurrentAppServerClient } from "./connection-bundle";
|
||||
import type { ChatPanelEnvironment } from "./contracts";
|
||||
|
||||
type ChatPanelGoalSyncActions = ReturnType<typeof createThreadGoalSyncActions>;
|
||||
|
|
@ -57,7 +51,7 @@ interface ChatPanelThreadHost {
|
|||
}
|
||||
|
||||
interface ChatPanelThreadFoundationInput {
|
||||
currentClient: CurrentAppServerClient;
|
||||
appServer: ChatAppServerGateway;
|
||||
localItemIds: LocalIdSource;
|
||||
status: ChatPanelThreadStatus;
|
||||
refreshLiveState: () => void;
|
||||
|
|
@ -73,9 +67,8 @@ interface ChatPanelThreadFoundation {
|
|||
}
|
||||
|
||||
interface ChatPanelThreadLifecycleInput {
|
||||
currentClient: CurrentAppServerClient;
|
||||
appServer: ChatAppServerGateway;
|
||||
localItemIds: LocalIdSource;
|
||||
connectedClient: () => Promise<ReturnType<CurrentAppServerClient>>;
|
||||
ensureConnected: () => Promise<void>;
|
||||
status: ChatPanelThreadStatus;
|
||||
serverThreads: ChatServerThreadActions;
|
||||
|
|
@ -95,8 +88,7 @@ interface ChatPanelThreadLifecycleBundle {
|
|||
}
|
||||
|
||||
interface ChatPanelThreadActionInput {
|
||||
currentClient: CurrentAppServerClient;
|
||||
connectedClient: () => Promise<ReturnType<CurrentAppServerClient>>;
|
||||
appServer: ChatAppServerGateway;
|
||||
status: ChatPanelThreadStatus;
|
||||
composerController: ChatComposerController;
|
||||
foundation: ChatPanelThreadFoundation;
|
||||
|
|
@ -112,16 +104,16 @@ interface ChatPanelThreadActionBundle {
|
|||
}
|
||||
|
||||
export function createThreadFoundation(host: ChatPanelThreadHost, input: ChatPanelThreadFoundationInput): ChatPanelThreadFoundation {
|
||||
const { currentClient, localItemIds, status, refreshLiveState } = input;
|
||||
const titleService = createSessionThreadTitleService(host, currentClient);
|
||||
const autoTitleCoordinator = createSessionAutoTitleCoordinator(host, currentClient, titleService);
|
||||
const history = createSessionHistoryController(host, currentClient, status, autoTitleCoordinator);
|
||||
const { appServer, localItemIds, status, refreshLiveState } = input;
|
||||
const titleService = createSessionThreadTitleService(host, appServer);
|
||||
const autoTitleCoordinator = createSessionAutoTitleCoordinator(host, appServer, titleService);
|
||||
const history = createSessionHistoryController(host, appServer, status, autoTitleCoordinator);
|
||||
const invalidateThreadWork = () => {
|
||||
host.resumeWork.invalidate();
|
||||
history.invalidate();
|
||||
};
|
||||
const goalSync = createSessionGoalSyncActions(host, currentClient, localItemIds, status, refreshLiveState);
|
||||
const threadOperations = createSessionThreadOperations(host.environment, currentClient);
|
||||
const goalSync = createSessionGoalSyncActions(host, appServer, localItemIds, status, refreshLiveState);
|
||||
const threadOperations = createSessionThreadOperations(host.environment, appServer);
|
||||
|
||||
return {
|
||||
titleService,
|
||||
|
|
@ -138,9 +130,8 @@ export function createThreadLifecycleBundle(
|
|||
input: ChatPanelThreadLifecycleInput,
|
||||
): ChatPanelThreadLifecycleBundle {
|
||||
const {
|
||||
currentClient,
|
||||
appServer,
|
||||
localItemIds,
|
||||
connectedClient,
|
||||
ensureConnected,
|
||||
status,
|
||||
serverThreads,
|
||||
|
|
@ -149,7 +140,7 @@ export function createThreadLifecycleBundle(
|
|||
refreshLiveState,
|
||||
notifyActiveThreadIdentityChanged,
|
||||
} = input;
|
||||
const goals = createSessionGoalActions(host, currentClient, localItemIds, connectedClient, status, serverThreads, refreshLiveState);
|
||||
const goals = createSessionGoalActions(host, appServer, localItemIds, status, serverThreads, refreshLiveState);
|
||||
const rename = createSessionThreadRenameEditorActions(
|
||||
host.stateStore,
|
||||
foundation.threadOperations,
|
||||
|
|
@ -158,8 +149,7 @@ export function createThreadLifecycleBundle(
|
|||
status,
|
||||
);
|
||||
const lifecycle = createSessionThreadLifecycle(host, {
|
||||
currentClient,
|
||||
connectedClient,
|
||||
appServer,
|
||||
status,
|
||||
goals,
|
||||
autoTitleCoordinator: foundation.autoTitleCoordinator,
|
||||
|
|
@ -184,16 +174,7 @@ export function createThreadLifecycleBundle(
|
|||
}
|
||||
|
||||
export function createThreadActionBundle(host: ChatPanelThreadHost, input: ChatPanelThreadActionInput): ChatPanelThreadActionBundle {
|
||||
const {
|
||||
currentClient,
|
||||
connectedClient,
|
||||
status,
|
||||
composerController,
|
||||
foundation,
|
||||
lifecycle,
|
||||
refreshActiveThreads,
|
||||
notifyActiveThreadIdentityChanged,
|
||||
} = input;
|
||||
const { appServer, status, composerController, foundation, lifecycle, refreshActiveThreads, notifyActiveThreadIdentityChanged } = input;
|
||||
const { environment, stateStore } = host;
|
||||
const threadManagementHost: ThreadManagementActionsHost = {
|
||||
stateStore,
|
||||
|
|
@ -201,11 +182,7 @@ export function createThreadActionBundle(host: ChatPanelThreadHost, input: ChatP
|
|||
renameThread: (threadId, value) => foundation.threadOperations.renameThread(threadId, value),
|
||||
archiveThread: async (threadId, options) => (await foundation.threadOperations.archiveThread(threadId, options)) !== null,
|
||||
},
|
||||
threadTransport: createChatThreadMutationTransport({
|
||||
vaultPath: environment.plugin.settingsRef.vaultPath,
|
||||
currentClient,
|
||||
connectedClient,
|
||||
}),
|
||||
threadTransport: appServer.threadMutation,
|
||||
addSystemMessage: status.addSystemMessage,
|
||||
setStatus: status.set,
|
||||
setComposerText: (text) => {
|
||||
|
|
@ -242,14 +219,14 @@ export function createThreadActionBundle(host: ChatPanelThreadHost, input: ChatP
|
|||
return { actions, toolbarPanelActions, navigation };
|
||||
}
|
||||
|
||||
function createSessionThreadTitleService(host: ChatPanelThreadHost, currentClient: CurrentAppServerClient): ThreadTitleService {
|
||||
function createSessionThreadTitleService(host: ChatPanelThreadHost, appServer: ChatAppServerGateway): ThreadTitleService {
|
||||
const { environment, stateStore } = host;
|
||||
return createThreadTitleService({
|
||||
codexPath: () => environment.plugin.settingsRef.settings.codexPath(),
|
||||
vaultPath: environment.plugin.settingsRef.vaultPath,
|
||||
threadNamingModel: () => environment.plugin.settingsRef.settings.threadNamingModel(),
|
||||
threadNamingEffort: () => environment.plugin.settingsRef.settings.threadNamingEffort(),
|
||||
clientAccess: createCurrentClientAccess(currentClient),
|
||||
clientAccess: appServer.clientAccess,
|
||||
visibleContext: (threadId) => activeThreadRenameTitleContext(stateStore.getState(), threadId),
|
||||
visibleCompletedTurnContext: (turnId) =>
|
||||
threadTitleContextFromMessageStreamItems(turnId, messageStreamItems(stateStore.getState().messageStream)),
|
||||
|
|
@ -258,7 +235,7 @@ function createSessionThreadTitleService(host: ChatPanelThreadHost, currentClien
|
|||
|
||||
function createSessionAutoTitleCoordinator(
|
||||
host: ChatPanelThreadHost,
|
||||
currentClient: CurrentAppServerClient,
|
||||
appServer: ChatAppServerGateway,
|
||||
titleService: ThreadTitleService,
|
||||
): AutoTitleCoordinator {
|
||||
return createAutoTitleCoordinator({
|
||||
|
|
@ -268,11 +245,7 @@ function createSessionAutoTitleCoordinator(
|
|||
renameGeneratedTitle: async (threadId, title, options) => {
|
||||
const name = normalizeExplicitThreadName(title);
|
||||
if (!name) return false;
|
||||
const client = currentClient();
|
||||
if (!client) return false;
|
||||
|
||||
await renameAppServerThread(client, threadId, name);
|
||||
if (currentClient() !== client) return false;
|
||||
if (!(await appServer.renameThread(threadId, name))) return false;
|
||||
if (options.shouldPublish()) {
|
||||
host.environment.plugin.threadCatalog.apply({ type: "thread-renamed", threadId, name });
|
||||
}
|
||||
|
|
@ -283,15 +256,13 @@ function createSessionAutoTitleCoordinator(
|
|||
|
||||
function createSessionHistoryController(
|
||||
host: ChatPanelThreadHost,
|
||||
currentClient: CurrentAppServerClient,
|
||||
appServer: ChatAppServerGateway,
|
||||
status: ChatPanelThreadStatus,
|
||||
autoTitleCoordinator: AutoTitleCoordinator,
|
||||
): HistoryController {
|
||||
return new HistoryController({
|
||||
stateStore: host.stateStore,
|
||||
historyTransport: createChatThreadHistoryTransport({
|
||||
currentClient,
|
||||
}),
|
||||
historyTransport: appServer.threadHistory,
|
||||
addSystemMessage: status.addSystemMessage,
|
||||
showLatestPageAtBottom: () => {
|
||||
host.messageScrollController.showLatest();
|
||||
|
|
@ -304,16 +275,14 @@ function createSessionHistoryController(
|
|||
|
||||
function createSessionGoalSyncActions(
|
||||
host: ChatPanelThreadHost,
|
||||
currentClient: CurrentAppServerClient,
|
||||
appServer: ChatAppServerGateway,
|
||||
localItemIds: LocalIdSource,
|
||||
status: ChatPanelThreadStatus,
|
||||
refreshLiveState: () => void,
|
||||
): ChatPanelGoalSyncActions {
|
||||
return createThreadGoalSyncActions({
|
||||
stateStore: host.stateStore,
|
||||
goalTransport: createChatThreadGoalReadTransport({
|
||||
currentClient,
|
||||
}),
|
||||
goalTransport: appServer.threadGoalRead,
|
||||
localItemIds,
|
||||
addSystemMessage: (text) => {
|
||||
status.addSystemMessage(text);
|
||||
|
|
@ -325,9 +294,9 @@ function createSessionGoalSyncActions(
|
|||
});
|
||||
}
|
||||
|
||||
function createSessionThreadOperations(environment: ChatPanelEnvironment, currentClient: CurrentAppServerClient): ThreadOperations {
|
||||
function createSessionThreadOperations(environment: ChatPanelEnvironment, appServer: ChatAppServerGateway): ThreadOperations {
|
||||
return createThreadOperations({
|
||||
clientAccess: createCurrentClientAccess(currentClient),
|
||||
clientAccess: appServer.clientAccess,
|
||||
archiveExport: {
|
||||
settings: () => environment.plugin.settingsRef.settings.archiveExportSettings(),
|
||||
enabled: () => environment.plugin.settingsRef.settings.archiveExportEnabled(),
|
||||
|
|
@ -342,35 +311,17 @@ function createSessionThreadOperations(environment: ChatPanelEnvironment, curren
|
|||
});
|
||||
}
|
||||
|
||||
function createCurrentClientAccess(currentClient: CurrentAppServerClient): AppServerClientAccess {
|
||||
return {
|
||||
withClient: async (operation) => {
|
||||
const client = currentClient();
|
||||
if (!client) throw new Error("Codex app-server is not connected.");
|
||||
const result = await operation(client);
|
||||
if (currentClient() !== client) {
|
||||
throw new Error("Codex app-server connection changed while running the operation.");
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createSessionGoalActions(
|
||||
host: ChatPanelThreadHost,
|
||||
currentClient: CurrentAppServerClient,
|
||||
appServer: ChatAppServerGateway,
|
||||
localItemIds: LocalIdSource,
|
||||
connectedClient: () => Promise<ReturnType<CurrentAppServerClient>>,
|
||||
status: ChatPanelThreadStatus,
|
||||
serverThreads: ChatServerThreadActions,
|
||||
refreshLiveState: () => void,
|
||||
): ChatPanelGoalActions {
|
||||
return createGoalActions({
|
||||
stateStore: host.stateStore,
|
||||
goalTransport: createChatThreadGoalTransport({
|
||||
currentClient,
|
||||
connectedClient,
|
||||
}),
|
||||
goalTransport: appServer.threadGoal,
|
||||
localItemIds,
|
||||
startThread: (preview, options) => serverThreads.startThread(preview, options),
|
||||
addSystemMessage: (text) => {
|
||||
|
|
@ -402,8 +353,7 @@ function createSessionThreadRenameEditorActions(
|
|||
function createSessionThreadLifecycle(
|
||||
host: ChatPanelThreadHost,
|
||||
input: {
|
||||
currentClient: CurrentAppServerClient;
|
||||
connectedClient: () => Promise<ReturnType<CurrentAppServerClient>>;
|
||||
appServer: ChatAppServerGateway;
|
||||
status: ChatPanelThreadStatus;
|
||||
goals: ChatPanelGoalActions;
|
||||
autoTitleCoordinator: AutoTitleCoordinator;
|
||||
|
|
@ -415,8 +365,7 @@ function createSessionThreadLifecycle(
|
|||
},
|
||||
): ChatPanelThreadLifecycle {
|
||||
const {
|
||||
currentClient,
|
||||
connectedClient,
|
||||
appServer,
|
||||
status,
|
||||
goals,
|
||||
autoTitleCoordinator,
|
||||
|
|
@ -428,21 +377,14 @@ function createSessionThreadLifecycle(
|
|||
} = input;
|
||||
return createThreadLifecycleParts({
|
||||
stateStore: host.stateStore,
|
||||
resumeTransport: createChatThreadResumeTransport({
|
||||
vaultPath: host.environment.plugin.settingsRef.vaultPath,
|
||||
currentClient,
|
||||
connectedClient,
|
||||
}),
|
||||
resumeTransport: appServer.threadResume,
|
||||
lifecycle: {
|
||||
resumeWork: host.resumeWork,
|
||||
history,
|
||||
invalidateThreadWork,
|
||||
getClosing: host.getClosing,
|
||||
recoverTokenUsageFromRollout: (path) =>
|
||||
recoverRolloutTokenUsage(path, async (filePath, options) => {
|
||||
const client = currentClient();
|
||||
return client ? readFileBase64(client, filePath, options) : "";
|
||||
}),
|
||||
recoverRolloutTokenUsage(path, (filePath, options) => appServer.readFileBase64(filePath, options)),
|
||||
},
|
||||
thread: {
|
||||
notifyIdentityChanged: notifyActiveThreadIdentityChanged,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ import type { LocalIdSource } from "../../../shared/id/local-id";
|
|||
import type { ConnectionWorkTracker } from "../../../shared/lifecycle/connection-work";
|
||||
import type { ChatServerThreadActions } from "../app-server/actions/threads";
|
||||
import type { ChatInboundHandler } from "../app-server/inbound/handler";
|
||||
import { createThreadReferenceResolver } from "../app-server/references/thread-reference-resolver";
|
||||
import { createChatTurnTransport } from "../app-server/turns/transport";
|
||||
import type { ChatAppServerGateway } from "../app-server/session-gateway";
|
||||
import { type ChatReconnectActionsHost, reconnectPanel } from "../application/connection/reconnect-actions";
|
||||
import {
|
||||
type ConversationTurnActions as ChatPanelConversationTurnActions,
|
||||
|
|
@ -17,7 +16,6 @@ import type { AutoTitleCoordinator } from "../application/threads/auto-title-coo
|
|||
import type { MessageStreamNoticeSection } from "../domain/message-stream/items";
|
||||
import type { ChatComposerController } from "../panel/composer-controller";
|
||||
import type { ChatPanelRuntimeProjection } from "../panel/runtime-status-projection";
|
||||
import type { CurrentAppServerClient } from "./connection-bundle";
|
||||
import type { ChatPanelEnvironment } from "./contracts";
|
||||
import type { ChatViewDeferredTasks } from "./deferred-work";
|
||||
import type { ChatPanelRuntimeSettingsActions } from "./runtime-bundle";
|
||||
|
|
@ -54,8 +52,7 @@ interface ChatPanelTurnInput {
|
|||
connection: ConnectionManager;
|
||||
localItemIds: LocalIdSource;
|
||||
ensureConnected: () => Promise<void>;
|
||||
connectedClient: () => Promise<ReturnType<CurrentAppServerClient>>;
|
||||
currentClient: CurrentAppServerClient;
|
||||
appServer: ChatAppServerGateway;
|
||||
status: ChatPanelTurnStatus;
|
||||
inboundHandler: ChatInboundHandler;
|
||||
threadLifecycle: ChatPanelThreadLifecycle;
|
||||
|
|
@ -78,8 +75,7 @@ export function createTurnBundle(host: ChatPanelTurnHost, input: ChatPanelTurnIn
|
|||
connection,
|
||||
localItemIds,
|
||||
ensureConnected,
|
||||
connectedClient,
|
||||
currentClient,
|
||||
appServer,
|
||||
status,
|
||||
inboundHandler,
|
||||
threadLifecycle,
|
||||
|
|
@ -129,13 +125,7 @@ export function createTurnBundle(host: ChatPanelTurnHost, input: ChatPanelTurnIn
|
|||
},
|
||||
};
|
||||
const reconnect = () => reconnectPanel(reconnectHost);
|
||||
const turnTransport = createChatTurnTransport({
|
||||
vaultPath: host.environment.plugin.settingsRef.vaultPath,
|
||||
currentClient,
|
||||
connectedClient,
|
||||
});
|
||||
const threadReferenceResolver = createThreadReferenceResolver({
|
||||
currentClient,
|
||||
const threadReferenceResolver = appServer.threadReferences({
|
||||
codexInput: (text) => composerController.codexInput(text),
|
||||
addSystemMessage: status.addSystemMessage,
|
||||
setStatus: status.set,
|
||||
|
|
@ -144,8 +134,8 @@ export function createTurnBundle(host: ChatPanelTurnHost, input: ChatPanelTurnIn
|
|||
{
|
||||
stateStore: host.stateStore,
|
||||
localItemIds,
|
||||
connectionAvailable: () => currentClient() !== null,
|
||||
turnTransport,
|
||||
connectionAvailable: () => appServer.connectionAvailable(),
|
||||
turnTransport: appServer.turn,
|
||||
referThread: (thread, message) => threadReferenceResolver.referThread(thread, message),
|
||||
status,
|
||||
runtime: {
|
||||
|
|
|
|||
|
|
@ -69,8 +69,11 @@ function turnBundleFixture(options: { stateStore?: ReturnType<typeof createChatS
|
|||
connection: { resetConnection: vi.fn() },
|
||||
localItemIds: { next: vi.fn(() => "local-id") },
|
||||
ensureConnected: vi.fn().mockResolvedValue(undefined),
|
||||
connectedClient: vi.fn().mockResolvedValue({}),
|
||||
currentClient: vi.fn(() => ({})),
|
||||
appServer: {
|
||||
connectionAvailable: vi.fn(() => true),
|
||||
threadReferences: vi.fn(() => ({ referThread: vi.fn() })),
|
||||
turn: { ensureConnected: vi.fn().mockResolvedValue(true) },
|
||||
},
|
||||
status,
|
||||
inboundHandler: {},
|
||||
threadLifecycle: {
|
||||
|
|
|
|||
|
|
@ -866,6 +866,14 @@ export type Escape = AppServerClient;
|
|||
import { listThreads } from "../../../../app-server/threads";
|
||||
|
||||
export const read = listThreads;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/chat/host/chat-app-server-root-import.ts"),
|
||||
`
|
||||
import { createChatAppServerGateway } from "../app-server/session-gateway";
|
||||
|
||||
export const gateway = createChatAppServerGateway;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
|
|
@ -891,6 +899,7 @@ export const read = listThreads;
|
|||
[
|
||||
"src/app-server/escape.ts",
|
||||
"src/features/chat/app-server/root-import.ts",
|
||||
"src/features/chat/host/chat-app-server-root-import.ts",
|
||||
"src/app-server/services/root-import.ts",
|
||||
"src/app-server/services/allowed.ts",
|
||||
],
|
||||
|
|
@ -899,6 +908,7 @@ export const read = listThreads;
|
|||
|
||||
expect(pluginMessages(report, "src/app-server/escape.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]);
|
||||
expect(pluginMessages(report, "src/features/chat/app-server/root-import.ts")).toEqual([APP_SERVER_ROOT_MODULE_IMPORT_MESSAGE]);
|
||||
expect(pluginDiagnostics(report, "src/features/chat/host/chat-app-server-root-import.ts")).toEqual([]);
|
||||
expect(pluginMessages(report, "src/app-server/services/root-import.ts")).toEqual([APP_SERVER_SUBFOLDER_ROOT_IMPORT_MESSAGE]);
|
||||
expect(pluginDiagnostics(report, "src/app-server/services/allowed.ts")).toEqual([]);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue