Fix MCP terminal capture scrollback
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
|||||||
import type { CreatePaseoWorktreeWorkflowFn } from "../worktree-session.js";
|
import type { CreatePaseoWorktreeWorkflowFn } from "../worktree-session.js";
|
||||||
import { WorkspaceGitServiceImpl } from "../workspace-git-service.js";
|
import { WorkspaceGitServiceImpl } from "../workspace-git-service.js";
|
||||||
import type { GitHubService } from "../../services/github-service.js";
|
import type { GitHubService } from "../../services/github-service.js";
|
||||||
|
import type { TerminalManager } from "../../terminal/terminal-manager.js";
|
||||||
|
|
||||||
interface LooseSafeParseResult {
|
interface LooseSafeParseResult {
|
||||||
success: boolean;
|
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 {
|
function createProviderDefinition(overrides: Partial<ProviderDefinition>): ProviderDefinition {
|
||||||
const provider = overrides.id ?? "claude";
|
const provider = overrides.id ?? "claude";
|
||||||
return {
|
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", () => {
|
describe("create_agent MCP tool", () => {
|
||||||
const logger = createTestLogger();
|
const logger = createTestLogger();
|
||||||
const existingCwd = process.cwd();
|
const existingCwd = process.cwd();
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import { scheduleAgentMetadataGeneration } from "./agent-metadata-generator.js";
|
|||||||
import type { VoiceCallerContext, VoiceSpeakHandler } from "../voice-types.js";
|
import type { VoiceCallerContext, VoiceSpeakHandler } from "../voice-types.js";
|
||||||
import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../path-utils.js";
|
import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../path-utils.js";
|
||||||
import type { TerminalManager } from "../../terminal/terminal-manager.js";
|
import type { TerminalManager } from "../../terminal/terminal-manager.js";
|
||||||
import { captureTerminalLines } from "../../terminal/terminal-capture.js";
|
|
||||||
import type {
|
import type {
|
||||||
AgentWorktreeSetupContinuation,
|
AgentWorktreeSetupContinuation,
|
||||||
CreatePaseoWorktreeSetupContinuationInput,
|
CreatePaseoWorktreeSetupContinuationInput,
|
||||||
@@ -1433,12 +1432,11 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
throw new Error("Terminal manager is not configured");
|
throw new Error("Terminal manager is not configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
const terminal = terminalManager.getTerminal(terminalId);
|
if (!terminalManager.getTerminal(terminalId)) {
|
||||||
if (!terminal) {
|
|
||||||
throw new Error(`Terminal ${terminalId} not found`);
|
throw new Error(`Terminal ${terminalId} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const capture = captureTerminalLines(terminal, {
|
const capture = await terminalManager.captureTerminal(terminalId, {
|
||||||
start: scrollback ? 0 : start,
|
start: scrollback ? 0 : start,
|
||||||
end,
|
end,
|
||||||
stripAnsi,
|
stripAnsi,
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import {
|
import { createTerminal, type TerminalSession, type TerminalStateSnapshot } from "./terminal.js";
|
||||||
captureTerminalLines,
|
import { captureTerminalLines, type CaptureTerminalLinesResult } from "./terminal-capture.js";
|
||||||
createTerminal,
|
|
||||||
type CaptureTerminalLinesResult,
|
|
||||||
type TerminalSession,
|
|
||||||
type TerminalStateSnapshot,
|
|
||||||
} from "./terminal.js";
|
|
||||||
import { resolve, sep, win32, posix } from "node:path";
|
import { resolve, sep, win32, posix } from "node:path";
|
||||||
|
|
||||||
export interface TerminalListItem {
|
export interface TerminalListItem {
|
||||||
|
|||||||
@@ -8,11 +8,6 @@ import { createRequire } from "node:module";
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { createExternalProcessEnv } from "../server/paseo-env.js";
|
import { createExternalProcessEnv } from "../server/paseo-env.js";
|
||||||
import type { TerminalCell, TerminalState } from "../shared/messages.js";
|
import type { TerminalCell, TerminalState } from "../shared/messages.js";
|
||||||
export {
|
|
||||||
captureTerminalLines,
|
|
||||||
type CaptureTerminalLinesOptions,
|
|
||||||
type CaptureTerminalLinesResult,
|
|
||||||
} from "./terminal-capture.js";
|
|
||||||
|
|
||||||
const { Terminal } = xterm;
|
const { Terminal } = xterm;
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import { randomUUID } from "node:crypto";
|
|||||||
import type { TerminalState } from "../shared/messages.js";
|
import type { TerminalState } from "../shared/messages.js";
|
||||||
import type {
|
import type {
|
||||||
ClientMessage,
|
ClientMessage,
|
||||||
CaptureTerminalLinesResult,
|
|
||||||
ServerMessage,
|
ServerMessage,
|
||||||
TerminalCommandFinishedInfo,
|
TerminalCommandFinishedInfo,
|
||||||
TerminalExitInfo,
|
TerminalExitInfo,
|
||||||
TerminalSession,
|
TerminalSession,
|
||||||
TerminalStateSnapshot,
|
TerminalStateSnapshot,
|
||||||
} from "./terminal.js";
|
} from "./terminal.js";
|
||||||
|
import type { CaptureTerminalLinesResult } from "./terminal-capture.js";
|
||||||
import type {
|
import type {
|
||||||
TerminalListItem,
|
TerminalListItem,
|
||||||
TerminalManager,
|
TerminalManager,
|
||||||
|
|||||||
Reference in New Issue
Block a user