Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions Sources/TerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class TerminalController {
case failure(path: String, stage: String, errnoCode: Int32)
}

private nonisolated static let focusIntentV2Methods: Set<String> = [
private static let focusIntentV2Methods: Set<String> = [
"window.focus",
"workspace.select",
"review.open",
Expand Down Expand Up @@ -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)
Expand All @@ -601,7 +601,7 @@ class TerminalController {
return body()
}

private nonisolated func withSocketCommandPolicy<T>(commandKey: String, isV2: Bool, _ body: () -> T) -> T {
private func withSocketCommandPolicy<T>(commandKey: String, isV2: Bool, _ body: () -> T) -> T {
let allowsFocusMutation = Self.socketCommandAllowsInAppFocusMutations(commandKey: commandKey, isV2: isV2)
Self.socketCommandPolicyLock.lock()
Self.socketCommandPolicyDepth += 1
Expand Down Expand Up @@ -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 {
Expand All @@ -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<kinfo_proc>.size
var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, pid]
Expand Down Expand Up @@ -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 <password> first."
guard command.hasPrefix("{"),
let data = command.data(using: .utf8),
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1888,7 +1888,7 @@ class TerminalController {
}
}

private nonisolated func processCommand(
private func processCommand(
_ command: String,
connection: SocketConnection,
requestPolicy: SocketRequestPolicy
Expand All @@ -1912,7 +1912,7 @@ class TerminalController {

// MARK: - V2 JSON Socket Protocol

private nonisolated func processV2Command(
private func processV2Command(
_ jsonLine: String,
connection: SocketConnection,
requestPolicy: SocketRequestPolicy
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 6 additions & 4 deletions programaTests/TabManagerSessionSnapshotTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading