diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index 32a2d4804..d3c83aca0 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -157,7 +157,7 @@ class TerminalController { case failure(path: String, stage: String, errnoCode: Int32) } - private nonisolated static let focusIntentV2Methods: Set = [ + private static let focusIntentV2Methods: Set = [ "window.focus", "workspace.select", "review.open", @@ -578,7 +578,7 @@ class TerminalController { } } - private nonisolated static func socketCommandAllowsInAppFocusMutations(commandKey: String, isV2: Bool) -> Bool { + private static func socketCommandAllowsInAppFocusMutations(commandKey: String, isV2: Bool) -> Bool { // The v1 line protocol is gone; only v2 JSON-RPC methods can carry focus intent. guard isV2 else { return false } return focusIntentV2Methods.contains(commandKey) @@ -601,7 +601,7 @@ class TerminalController { return body() } - private nonisolated func withSocketCommandPolicy(commandKey: String, isV2: Bool, _ body: () -> T) -> T { + private func withSocketCommandPolicy(commandKey: String, isV2: Bool, _ body: () -> T) -> T { let allowsFocusMutation = Self.socketCommandAllowsInAppFocusMutations(commandKey: commandKey, isV2: isV2) Self.socketCommandPolicyLock.lock() Self.socketCommandPolicyDepth += 1 @@ -940,7 +940,7 @@ class TerminalController { } /// Check if `pid` is a descendant of this process by walking the process tree. - nonisolated func isDescendant(_ pid: pid_t) -> Bool { + func isDescendant(_ pid: pid_t) -> Bool { var current = pid // Walk up to 128 levels to avoid infinite loops from kernel bugs for _ in 0..<128 { @@ -960,7 +960,7 @@ class TerminalController { } /// Get the parent PID of a process using sysctl. - private nonisolated func parentPid(of pid: pid_t) -> pid_t { + private func parentPid(of pid: pid_t) -> pid_t { var info = kinfo_proc() var size = MemoryLayout.size var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid] @@ -1488,7 +1488,7 @@ class TerminalController { } } - private nonisolated func passwordAuthRequiredResponse(for command: String) -> String { + private func passwordAuthRequiredResponse(for command: String) -> String { let message = "Authentication required. Send auth first." guard command.hasPrefix("{"), let data = command.data(using: .utf8), @@ -1499,7 +1499,7 @@ class TerminalController { return v2Error(id: id, code: "auth_required", message: message) } - private nonisolated func passwordLoginV2ResponseIfNeeded(for command: String, authenticated: inout Bool) -> String? { + private func passwordLoginV2ResponseIfNeeded(for command: String, authenticated: inout Bool) -> String? { guard command.hasPrefix("{"), let data = command.data(using: .utf8), let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else { @@ -1535,7 +1535,7 @@ class TerminalController { return v2Ok(id: id, result: ["authenticated": true]) } - private nonisolated func authResponseIfNeeded( + private func authResponseIfNeeded( for command: String, authenticated: inout Bool, requestPolicy: SocketRequestPolicy @@ -1759,7 +1759,7 @@ class TerminalController { /// Unix clients carry the listener policy captured when they were accepted. /// Mobile Bridge sessions are admitted independently by pairing and its method allow-list. - nonisolated func handleClient(_ socket: Int32, peerPid: pid_t? = nil, source: SocketConnectionSource) { + func handleClient(_ socket: Int32, peerPid: pid_t? = nil, source: SocketConnectionSource) { // Owns this connection's writes (both ordinary v2 responses and any #167 subscription // event pushes) and its subscription lifecycle. `teardown()` (which tears down any // attached subscription) must run before the fd is closed -- defers unwind LIFO, so the @@ -1888,7 +1888,7 @@ class TerminalController { } } - private nonisolated func processCommand( + private func processCommand( _ command: String, connection: SocketConnection, requestPolicy: SocketRequestPolicy @@ -1912,7 +1912,7 @@ class TerminalController { // MARK: - V2 JSON Socket Protocol - private nonisolated func processV2Command( + private func processV2Command( _ jsonLine: String, connection: SocketConnection, requestPolicy: SocketRequestPolicy @@ -1952,7 +1952,7 @@ class TerminalController { } return withSocketCommandPolicy(commandKey: method, isV2: true) { - if let result = AgentRPCDispatcher.dispatch(method: method, params: params, controller: self) { + if let result = browserRPCDispatcher.dispatch(method: method, params: params, controller: self) ?? AgentRPCDispatcher.dispatch(method: method, params: params, controller: self) { return v2Result(id: id, result) } switch method { @@ -2194,8 +2194,7 @@ class TerminalController { // Markdown case "markdown.open": - // `v2MarkdownOpen` is main actor-isolated; hop explicitly. - return v2Result(id: id, v2MainSync { self.v2MarkdownOpen(params: params) }) + return v2Result(id: id, self.v2MarkdownOpen(params: params)) // Review (agent diff review panel) case "review.open": @@ -2297,17 +2296,12 @@ class TerminalController { #endif default: - // BrowserRPCDispatcher is @MainActor; only methods that missed every - // nonisolated case above pay this hop, keeping telemetry off main. - if let result = v2MainSync({ browserRPCDispatcher.dispatch(method: method, params: params, controller: self) }) { - return v2Result(id: id, result) - } return v2Error(id: id, code: "method_not_found", message: "Unknown method") } } } - private nonisolated func v2Capabilities(requestPolicy: SocketRequestPolicy) -> [String: Any] { + private func v2Capabilities(requestPolicy: SocketRequestPolicy) -> [String: Any] { return [ "protocol": "cmux-socket", "version": 2, diff --git a/programaTests/TabManagerSessionSnapshotTests.swift b/programaTests/TabManagerSessionSnapshotTests.swift index 3615994a5..b663bdc8c 100644 --- a/programaTests/TabManagerSessionSnapshotTests.swift +++ b/programaTests/TabManagerSessionSnapshotTests.swift @@ -189,11 +189,13 @@ final class TabManagerSessionSnapshotTests: XCTestCase { ) grandchild.setCustomTitle("Grandchild") - // Select a folder child so the collapsed-parent selection remap has something to prove. - manager.selectWorkspace(folderChildA) - let encoded = try JSONEncoder().encode(manager.sessionSnapshot(includeScrollback: false)) - let snapshot = try JSONDecoder().decode(SessionTabManagerSnapshot.self, from: encoded) + var snapshot = try JSONDecoder().decode(SessionTabManagerSnapshot.self, from: encoded) + // Selecting a child in the live app expands its folder, so a saved session with a + // collapsed folder and a selected child is built directly to exercise the restore remap. + snapshot.selectedWorkspaceIndex = try XCTUnwrap( + snapshot.workspaces.firstIndex { $0.customTitle == "FolderChildA" } + ) let restored = TabManager() restored.restoreSessionSnapshot(snapshot)