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
12 changes: 7 additions & 5 deletions native2/Sources/DesktopMac/System.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ extension Mac {
NSWorkspace.shared.runningApplications.contains { $0.bundleIdentifier == "com.apple.ScreenSaver.Engine" }
}

var layout: JSON?
if let source = TISCopyCurrentKeyboardLayoutInputSource()?.takeRetainedValue(),
let id = tisString(source, kTISPropertyInputSourceID)
{
layout = .object(["id": JSON(id), "name": tisString(source, kTISPropertyLocalizedName).map(JSON.string)])
// Text Input Sources must be read on the main thread: off it, HIToolbox can trap on a queue assertion and take
// the host down mid-call, which agents saw as "host closed the connection" after a desktop_open (#244).
let layout = await MainActor.run { () -> JSON? in
guard let source = TISCopyCurrentKeyboardLayoutInputSource()?.takeRetainedValue(),
let id = tisString(source, kTISPropertyInputSourceID)
else { return nil }
return .object(["id": JSON(id), "name": tisString(source, kTISPropertyLocalizedName).map(JSON.string)])
}
let pointer = CGEvent(source: nil).map(\.location).map { point -> JSON in
.object(["x": JSON(Double(point.x)), "y": JSON(Double(point.y)), "display_index": WindowPlaces().pointerDisplay(point).map { JSON($0) }])
Expand Down
17 changes: 16 additions & 1 deletion native2/Sources/desktop-tools-mcp/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,22 @@ func call(_ key: String, tool: String, arguments: JSON) async -> JSON {
if case .system("connect", _) = error {
return errorResult("The Desktop Tools 2 host isn't serving at \(socketPath). Start it with: native2/.build/debug/desktop-tools-host")
}
return errorResult(error.description)
// The request may have reached the host, which can stop or restart mid-call. An action may then have taken
// effect, as a desktop_open whose host stopped after Xcode opened the file did (#244), so it is unknown, never
// a plain error an agent reads as "nothing happened".
if Contract.actions.contains(tool) {
let action: JSON = .object([("action", .object([
("outcome", "unknown"), ("action", JSON(tool)), ("retry_safety", "unsafe"),
("error", .object([("code", "HOST_CONNECTION_LOST"), ("message", JSON(
"\(error.description) The Desktop Tools host stopped while running \(tool), so it may have taken effect. "
+ "Observe the target (desktop_windows, desktop_inspect) before deciding whether to act again; don't repeat it blindly."))])),
]))])
return .object([
("content", [.object([("type", "text"), ("text", "Outcome: unknown.")]), .object([("type", "text"), ("text", JSON(action.text))])]),
("isError", true),
])
}
return errorResult("\(error.description) Nothing was changed by this read; call it again.")
} catch {
return errorResult(String(describing: error))
}
Expand Down
Loading