refactor(coding-agent): add runtime host for session switching closes #2024

This commit is contained in:
Mario Zechner
2026-03-31 13:49:57 +02:00
parent a3bf1eb399
commit d86122cbd3
32 changed files with 1328 additions and 692 deletions

View File

@@ -17,11 +17,15 @@ type FakeSession = {
bindExtensions: ReturnType<typeof vi.fn>;
subscribe: ReturnType<typeof vi.fn>;
prompt: ReturnType<typeof vi.fn>;
reload: ReturnType<typeof vi.fn>;
};
type FakeRuntimeHost = {
session: FakeSession;
newSession: ReturnType<typeof vi.fn>;
fork: ReturnType<typeof vi.fn>;
navigateTree: ReturnType<typeof vi.fn>;
switchSession: ReturnType<typeof vi.fn>;
reload: ReturnType<typeof vi.fn>;
dispose: ReturnType<typeof vi.fn>;
};
function createAssistantMessage(options?: {
@@ -49,7 +53,7 @@ function createAssistantMessage(options?: {
};
}
function createSession(assistantMessage: AssistantMessage): FakeSession {
function createRuntimeHost(assistantMessage: AssistantMessage): FakeRuntimeHost {
const extensionRunner: FakeExtensionRunner = {
hasHandlers: (eventType: string) => eventType === "session_shutdown",
emit: vi.fn(async () => {}),
@@ -57,7 +61,7 @@ function createSession(assistantMessage: AssistantMessage): FakeSession {
const state = { messages: [assistantMessage] };
return {
const session: FakeSession = {
sessionManager: { getHeader: () => undefined },
agent: { waitForIdle: async () => {} },
state,
@@ -65,12 +69,18 @@ function createSession(assistantMessage: AssistantMessage): FakeSession {
bindExtensions: vi.fn(async () => {}),
subscribe: vi.fn(() => () => {}),
prompt: vi.fn(async () => {}),
newSession: vi.fn(async () => true),
fork: vi.fn(async () => ({ cancelled: false })),
navigateTree: vi.fn(async () => ({ cancelled: false })),
switchSession: vi.fn(async () => true),
reload: vi.fn(async () => {}),
};
return {
session,
newSession: vi.fn(async () => undefined),
fork: vi.fn(async () => ({ selectedText: "" })),
switchSession: vi.fn(async () => undefined),
dispose: vi.fn(async () => {
await session.extensionRunner.emit({ type: "session_shutdown" });
}),
};
}
afterEach(() => {
@@ -79,10 +89,11 @@ afterEach(() => {
describe("runPrintMode", () => {
it("emits session_shutdown in text mode", async () => {
const session = createSession(createAssistantMessage({ text: "done" }));
const runtimeHost = createRuntimeHost(createAssistantMessage({ text: "done" }));
const { session } = runtimeHost;
const images: ImageContent[] = [{ type: "image", mimeType: "image/png", data: "abc" }];
const exitCode = await runPrintMode(session as unknown as Parameters<typeof runPrintMode>[0], {
const exitCode = await runPrintMode(runtimeHost as unknown as Parameters<typeof runPrintMode>[0], {
mode: "text",
initialMessage: "Say done",
initialImages: images,
@@ -95,9 +106,10 @@ describe("runPrintMode", () => {
});
it("emits session_shutdown in json mode", async () => {
const session = createSession(createAssistantMessage({ text: "done" }));
const runtimeHost = createRuntimeHost(createAssistantMessage({ text: "done" }));
const { session } = runtimeHost;
const exitCode = await runPrintMode(session as unknown as Parameters<typeof runPrintMode>[0], {
const exitCode = await runPrintMode(runtimeHost as unknown as Parameters<typeof runPrintMode>[0], {
mode: "json",
messages: ["hello"],
});
@@ -109,10 +121,13 @@ describe("runPrintMode", () => {
});
it("emits session_shutdown and returns non-zero on assistant error", async () => {
const session = createSession(createAssistantMessage({ stopReason: "error", errorMessage: "provider failure" }));
const runtimeHost = createRuntimeHost(
createAssistantMessage({ stopReason: "error", errorMessage: "provider failure" }),
);
const { session } = runtimeHost;
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const exitCode = await runPrintMode(session as unknown as Parameters<typeof runPrintMode>[0], {
const exitCode = await runPrintMode(runtimeHost as unknown as Parameters<typeof runPrintMode>[0], {
mode: "text",
});