Route unhandled app-server request policies to short-lived clients

This commit is contained in:
murashit 2026-06-17 14:43:20 +09:00
parent 71fbe05e34
commit 77b9071b8a
2 changed files with 39 additions and 0 deletions

View file

@ -229,6 +229,9 @@ export class CodexPanelRuntime implements AppServerClientAccess {
if (!appServerQueryContextIsComplete(context)) {
throw new Error("Codex app-server query context is incomplete.");
}
if (options.unhandledServerRequestMessage !== undefined) {
return withShortLivedAppServerClient(context.codexPath, context.vaultPath, operation, options);
}
const chatSurface = this.connectedClientSurface();
if (chatSurface) return chatSurface.runWithAppServerClient(operation);
return withShortLivedAppServerClient(context.codexPath, context.vaultPath, operation, options);

View file

@ -14,6 +14,14 @@ import { installObsidianDomShims } from "./support/dom";
installObsidianDomShims();
const { withShortLivedAppServerClientMock } = vi.hoisted(() => ({
withShortLivedAppServerClientMock: vi.fn(),
}));
vi.mock("../src/app-server/connection/short-lived-client", () => ({
withShortLivedAppServerClient: withShortLivedAppServerClientMock,
}));
function panels(plugin: CodexPanelPlugin) {
return new WorkspacePanelCoordinator({
app: plugin.app,
@ -28,6 +36,7 @@ function threadCatalog(plugin: CodexPanelPlugin) {
describe("CodexPanelPlugin boot restored panel loading", () => {
beforeEach(() => {
vi.useRealTimers();
withShortLivedAppServerClientMock.mockReset();
});
it("loads restored Codex panel leaves after startup without blocking onload", async () => {
@ -505,6 +514,33 @@ describe("CodexPanelPlugin boot restored panel loading", () => {
expect(threadCatalog(plugin).snapshot()).toEqual([thread("cached")]);
});
it("uses a short-lived client when the operation declares an unhandled server-request policy", async () => {
const { CodexChatView } = await import("../src/features/chat/host/view");
const connectedLeaf = leaf();
connectedLeaf.view = chatView(CodexChatView, connectedLeaf);
const connectedView = connectedLeaf.view as CodexChatView;
vi.spyOn(connectedView.surface, "openPanelSnapshot").mockReturnValue(panelSnapshot({ viewId: "connected", connected: true }));
const runWithAppServerClient = vi.spyOn(connectedView.surface, "runWithAppServerClient").mockResolvedValue("chat-result");
const shortLivedClient = { readEffectiveConfig: vi.fn().mockResolvedValue({}) };
withShortLivedAppServerClientMock.mockImplementation(
(_codexPath: string, _vaultPath: string, operation: (client: typeof shortLivedClient) => Promise<unknown>) =>
operation(shortLivedClient),
);
const plugin = await pluginWithLeaves([connectedLeaf]);
plugin.settings.codexPath = "codex";
const result = await plugin.runtime.withClient((client) => client.readEffectiveConfig("/vault") as Promise<unknown>, {
unhandledServerRequestMessage: "Settings refresh does not handle server requests.",
});
expect(result).toEqual({});
expect(runWithAppServerClient).not.toHaveBeenCalled();
expect(withShortLivedAppServerClientMock).toHaveBeenCalledWith("codex", "/vault", expect.any(Function), {
unhandledServerRequestMessage: "Settings refresh does not handle server requests.",
});
expect(shortLivedClient.readEffectiveConfig).toHaveBeenCalledWith("/vault");
});
it("refreshes shared thread lists from a remaining connected panel after the archived panel is detached", async () => {
const { CodexChatView } = await import("../src/features/chat/host/view");
const sourceLeaf = leaf();