import assert from "node:assert/strict"; import test from "node:test"; import { makeFreshTab } from "../src/features/agent/messages/helpers"; import type { Project } from "../src/features/agent/projects/types"; import { terminalOwnerFor, terminalKeysMatch } from "../src/features/agent/terminal-owners"; import { collectLeaves } from "../src/features/agent/workspace/layout"; import { restorePersistedPaneState } from "../src/features/agent/workspace/store"; function persistedWorkspace( panes: Record, layout: unknown, focusedPaneId: string, ) { return JSON.stringify({ version: 1, panes, layout, focusedPaneId }); } test("legacy terminal panes are removed while restoring chat tasks", () => { const restored = restorePersistedPaneState( persistedWorkspace( { "p-chat": { activeTabId: "session-1", tabs: [{ id: "session-1", title: "Task", cwd: "/repo" }], }, "p-terminal": { kind: "terminal", mountKey: "project:repo", cwd: "/repo", }, }, { kind: "split", direction: "vertical", ratio: 0.5, a: { kind: "leaf", paneId: "p-chat" }, b: { kind: "leaf", paneId: "p-terminal" }, }, "p-terminal", ), ); assert.ok(restored); assert.deepEqual(collectLeaves(restored.layout), ["p-chat"]); assert.deepEqual([...restored.panesById.keys()], ["p-chat"]); assert.equal(restored.focusedPaneId, "p-chat"); }); test("a legacy terminal-only workspace falls back to a fresh task", () => { const restored = restorePersistedPaneState( persistedWorkspace( { "p-terminal": { kind: "terminal", mountKey: "project:repo", cwd: "/repo", }, }, { kind: "leaf", paneId: "p-terminal" }, "p-terminal", ), ); assert.equal(restored, null); }); test("a task owns one stable terminal across runtime adoption", () => { const project: Project = { id: "project-1", name: "Studio", path: "/repo/studio", addedAt: "2026-07-10T00:00:00.000Z", exists: true, hasGit: true, branch: "sol", }; const session = { ...makeFreshTab(), id: "task-1", title: "Session cleanup", projectId: project.id, cwd: project.path, }; const before = terminalOwnerFor(project, session); const after = terminalOwnerFor(project, { ...session, piSessionId: "pi-1" }); assert.equal(before?.mountKey, "session:task-1"); assert.equal(after?.mountKey, before?.mountKey); assert.deepEqual(after?.matchKeys, ["session:task-1", "pi:pi-1"]); assert.equal(after?.cwd, project.path); }); test("terminal owners keep old session terminals addressable by their keys", () => { const sessionA = { ...makeFreshTab(), id: "task-a", title: "Chat A", projectId: "project-a", cwd: "/repo/a", piSessionId: "pi-a", }; const sessionB = { ...makeFreshTab(), id: "task-b", title: "Chat B", projectId: "project-b", cwd: "/repo/b", piSessionId: "pi-b", }; const ownerA = terminalOwnerFor(null, sessionA); const ownerB = terminalOwnerFor(null, sessionB); assert.ok(ownerA); assert.ok(ownerB); assert.equal(ownerA.mountKey, "session:task-a"); assert.equal(ownerB.mountKey, "session:task-b"); assert.equal(terminalKeysMatch(ownerA.matchKeys, ownerB.matchKeys), false); });