Fix MCP terminal capture scrollback

This commit is contained in:
Mohamed Boudra
2026-05-07 11:59:13 +07:00
parent 55ec13f153
commit 16944da45b
5 changed files with 70 additions and 17 deletions
@@ -22,6 +22,7 @@ import {
import type { CreatePaseoWorktreeWorkflowFn } from "../worktree-session.js";
import { WorkspaceGitServiceImpl } from "../workspace-git-service.js";
import type { GitHubService } from "../../services/github-service.js";
import type { TerminalManager } from "../../terminal/terminal-manager.js";
interface LooseSafeParseResult {
success: boolean;
@@ -136,6 +137,23 @@ function createTestDeps(): TestDeps {
};
}
function createTerminalManagerStub(overrides: Partial<TerminalManager> = {}): TerminalManager {
return {
getTerminals: vi.fn().mockResolvedValue([]),
createTerminal: vi.fn(),
registerCwdEnv: vi.fn(),
getTerminal: vi.fn(),
getTerminalState: vi.fn().mockResolvedValue(null),
killTerminal: vi.fn(),
killTerminalAndWait: vi.fn().mockResolvedValue(undefined),
captureTerminal: vi.fn().mockResolvedValue({ lines: [], totalLines: 0 }),
listDirectories: vi.fn().mockReturnValue([]),
killAll: vi.fn(),
subscribeTerminalsChanged: vi.fn().mockReturnValue(() => {}),
...overrides,
} as unknown as TerminalManager;
}
function createProviderDefinition(overrides: Partial<ProviderDefinition>): ProviderDefinition {
const provider = overrides.id ?? "claude";
return {
@@ -339,6 +357,53 @@ function createPaseoWorktreeForMcpTest(options: {
};
}
describe("terminal MCP tools", () => {
const logger = createTestLogger();
it("captures terminal output through the terminal manager authority", async () => {
const { agentManager, agentStorage } = createTestDeps();
const captureTerminal = vi.fn().mockResolvedValue({
lines: ["from worker scrollback"],
totalLines: 42,
});
const terminalManager = createTerminalManagerStub({
getTerminal: vi.fn().mockReturnValue({
id: "term-1",
name: "daemon",
cwd: process.cwd(),
getState: vi.fn().mockReturnValue({ scrollback: [], grid: [[]] }),
}),
captureTerminal,
});
const server = await createAgentMcpServer({
agentManager,
agentStorage,
terminalManager,
logger,
});
const tool = registeredTool(server, "capture_terminal");
const response = await tool.callback({
terminalId: "term-1",
scrollback: true,
stripAnsi: false,
start: -10,
end: -1,
});
expect(captureTerminal).toHaveBeenCalledWith("term-1", {
start: 0,
end: -1,
stripAnsi: false,
});
expect(response.structuredContent).toEqual({
terminalId: "term-1",
lines: ["from worker scrollback"],
totalLines: 42,
});
});
});
describe("create_agent MCP tool", () => {
const logger = createTestLogger();
const existingCwd = process.cwd();
@@ -39,7 +39,6 @@ import { scheduleAgentMetadataGeneration } from "./agent-metadata-generator.js";
import type { VoiceCallerContext, VoiceSpeakHandler } from "../voice-types.js";
import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../path-utils.js";
import type { TerminalManager } from "../../terminal/terminal-manager.js";
import { captureTerminalLines } from "../../terminal/terminal-capture.js";
import type {
AgentWorktreeSetupContinuation,
CreatePaseoWorktreeSetupContinuationInput,
@@ -1433,12 +1432,11 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
throw new Error("Terminal manager is not configured");
}
const terminal = terminalManager.getTerminal(terminalId);
if (!terminal) {
if (!terminalManager.getTerminal(terminalId)) {
throw new Error(`Terminal ${terminalId} not found`);
}
const capture = captureTerminalLines(terminal, {
const capture = await terminalManager.captureTerminal(terminalId, {
start: scrollback ? 0 : start,
end,
stripAnsi,
@@ -1,10 +1,5 @@
import {
captureTerminalLines,
createTerminal,
type CaptureTerminalLinesResult,
type TerminalSession,
type TerminalStateSnapshot,
} from "./terminal.js";
import { createTerminal, type TerminalSession, type TerminalStateSnapshot } from "./terminal.js";
import { captureTerminalLines, type CaptureTerminalLinesResult } from "./terminal-capture.js";
import { resolve, sep, win32, posix } from "node:path";
export interface TerminalListItem {
-5
View File
@@ -8,11 +8,6 @@ import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { createExternalProcessEnv } from "../server/paseo-env.js";
import type { TerminalCell, TerminalState } from "../shared/messages.js";
export {
captureTerminalLines,
type CaptureTerminalLinesOptions,
type CaptureTerminalLinesResult,
} from "./terminal-capture.js";
const { Terminal } = xterm;
const require = createRequire(import.meta.url);
@@ -4,13 +4,13 @@ import { randomUUID } from "node:crypto";
import type { TerminalState } from "../shared/messages.js";
import type {
ClientMessage,
CaptureTerminalLinesResult,
ServerMessage,
TerminalCommandFinishedInfo,
TerminalExitInfo,
TerminalSession,
TerminalStateSnapshot,
} from "./terminal.js";
import type { CaptureTerminalLinesResult } from "./terminal-capture.js";
import type {
TerminalListItem,
TerminalManager,