refactor: remove terminal agent concept entirely

Remove the "agent can be a terminal" branching from the entire codebase.
An agent is now always a session-backed chat agent. Standalone terminal
infrastructure (terminal component, ANSI handling, terminal-stream-protocol)
is preserved.

Server: delete ManagedTerminalAgent, AgentKind, TerminalExitDetails,
launchTerminalAgent, registerTerminalAgent, handleTerminalAgentExited,
supportsTerminalMode capability, buildTerminalCreate/ResumeCommand from
all providers, terminal agent persistence/projections.

App: delete terminal-agent-panel.tsx, terminal-agent-reopen-store.ts,
terminal/terminalExit fields on agent state, "Terminal Agents" launcher
section, terminal-agent workspace setup flow, terminal badge in agent list.

CLI: remove terminal column from ls, terminal-agent error from send.

60 files changed, -3592 lines
This commit is contained in:
Mohamed Boudra
2026-04-02 10:55:54 +07:00
parent 59cb4d7499
commit 060f3d457c
60 changed files with 60 additions and 3592 deletions
@@ -9,7 +9,6 @@ import {
createPersistedProjectRecord,
createPersistedWorkspaceRecord,
} from "./workspace-registry.js";
import type { StoredAgentRecord } from "./agent/agent-storage.js";
function makeAgent(input: {
id: string;
@@ -39,7 +38,6 @@ function makeAgent(input: {
supportsMcpServers: true,
supportsReasoningStream: true,
supportsToolInvocations: true,
supportsTerminalMode: false,
},
currentModeId: null,
availableModes: [],
@@ -222,50 +220,6 @@ function seedWorkspace(options: {
return record;
}
function createStoredTerminalAgentRecord(input: {
id: string;
cwd: string;
}): StoredAgentRecord {
return {
id: input.id,
provider: "codex",
cwd: input.cwd,
createdAt: "2026-03-01T12:00:00.000Z",
updatedAt: "2026-03-01T12:00:00.000Z",
lastActivityAt: "2026-03-01T12:00:00.000Z",
lastUserMessageAt: null,
title: null,
labels: {},
lastStatus: "closed",
lastModeId: null,
config: {
terminal: true,
},
runtimeInfo: {
provider: "codex",
sessionId: null,
},
persistence: {
provider: "codex",
sessionId: input.id,
nativeHandle: input.id,
},
lastError: null,
terminalExit: {
command: "codex",
message: "Terminal session ended",
exitCode: 0,
signal: null,
outputLines: [],
},
requiresAttention: false,
attentionReason: null,
attentionTimestamp: null,
internal: false,
archivedAt: null,
};
}
function createTempGitRepo(options?: {
remoteUrl?: string;
branchName?: string;
@@ -635,89 +589,6 @@ describe("workspace aggregation", () => {
expect(sessionLogger.warn).toHaveBeenCalled();
});
test("terminal agents reject timeline fetch without reloading as chat sessions", async () => {
const emitted: Array<{ type: string; payload: any }> = [];
const logger = {
child: () => logger,
trace: vi.fn(),
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
const resumeAgentFromPersistence = vi.fn();
const launchTerminalAgent = vi.fn();
const hydrateTimelineFromProvider = vi.fn();
const session = new Session({
clientId: "test-client",
onMessage: (message) => emitted.push(message as any),
logger: logger as any,
downloadTokenStore: {} as any,
pushTokenStore: {} as any,
paseoHome: "/tmp/paseo-test",
agentManager: {
subscribe: () => () => {},
listAgents: () => [],
getAgent: () => null,
resumeAgentFromPersistence,
launchTerminalAgent,
hydrateTimelineFromProvider,
} as any,
agentStorage: {
list: async () => [],
get: async (agentId: string) =>
agentId === "terminal-1"
? createStoredTerminalAgentRecord({ id: agentId, cwd: "/tmp/repo" })
: null,
} as any,
projectRegistry: {
initialize: async () => {},
existsOnDisk: async () => true,
list: async () => [],
get: async () => null,
upsert: async () => {},
archive: async () => {},
remove: async () => {},
} as any,
workspaceRegistry: {
initialize: async () => {},
existsOnDisk: async () => true,
list: async () => [],
get: async () => null,
upsert: async () => {},
archive: async () => {},
remove: async () => {},
} as any,
createAgentMcpTransport: async () => {
throw new Error("not used");
},
stt: null,
tts: null,
terminalManager: null,
}) as any;
await session.handleMessage({
type: "fetch_agent_timeline_request",
requestId: "req-terminal-timeline",
agentId: "terminal-1",
});
expect(resumeAgentFromPersistence).not.toHaveBeenCalled();
expect(launchTerminalAgent).not.toHaveBeenCalled();
expect(hydrateTimelineFromProvider).not.toHaveBeenCalled();
expect(emitted).toContainEqual(
expect.objectContaining({
type: "fetch_agent_timeline_response",
payload: expect.objectContaining({
requestId: "req-terminal-timeline",
agentId: "terminal-1",
error: "Agent terminal-1 is a terminal agent and has no timeline history",
}),
}),
);
});
test("uses persisted workspace names and stable status aggregation", async () => {
const { session, projects, workspaces } = createSessionForWorkspaceTests();
seedProject({