fix: validate Cua window discovery filters and guide recovery
This commit is contained in:
1 parent
c89588a80d
commit
1c1d700e9b
12 files changed
+337
-31
No files matched your search
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Optional filters must remain optional across JSON-schema clients. In
|
||||
// particular, null means unspecified; zero is not a wildcard process ID.
|
||||
func windowQuery(args obj) (obj, error) {
|
||||
query := obj{}
|
||||
for key, value := range args {
|
||||
if value != nil {
|
||||
query[key] = value
|
||||
}
|
||||
}
|
||||
if value, ok := query["app"]; ok {
|
||||
app, valid := value.(string)
|
||||
if !valid {
|
||||
return nil, errors.New("app must be an application name or null")
|
||||
}
|
||||
if app = strings.TrimSpace(app); app == "" {
|
||||
delete(query, "app")
|
||||
} else {
|
||||
query["app"] = app
|
||||
}
|
||||
}
|
||||
if value, ok := query["pid"]; ok {
|
||||
valid := num(value) > 0 && num(value) <= math.MaxUint32
|
||||
switch n := value.(type) {
|
||||
case float64:
|
||||
valid = valid && n == math.Trunc(n)
|
||||
case int, int64:
|
||||
default:
|
||||
valid = false
|
||||
}
|
||||
if !valid {
|
||||
return nil, errors.New("pid must be a positive Windows process ID from a matching list_windows result. If unknown, omit pid or use null and search by app, e.g. {\"app\":\"Brave\",\"on_screen_only\":true}. Never use 0 or a different app's foreground PID")
|
||||
}
|
||||
}
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// Upstream's empty-result hint names the unrelated foreground process. Do not
|
||||
// present that PID as a recovery candidate. Keep the exact filters and provide
|
||||
// a separate suggested query; never silently widen the caller's PID scope.
|
||||
func windowListing(result obj, query obj) obj {
|
||||
if result["isError"] == true {
|
||||
return result
|
||||
}
|
||||
data := object(result["structuredContent"])
|
||||
windows, ok := data["windows"].([]any)
|
||||
if !ok {
|
||||
return failure("Driver returned no window list; this is an observation error, not evidence that the application is absent", "not_started", "none", "invalid_window_list")
|
||||
}
|
||||
lookup := obj{"status": "matched", "filters": query, "count": len(windows)}
|
||||
lines := []string{fmt.Sprintf("Found %d matching window(s).", len(windows))}
|
||||
for _, value := range windows {
|
||||
window := object(value)
|
||||
lines = append(lines, fmt.Sprintf("- %s (pid %d), window_id %d, title %q", str(window["app_name"]), num(window["pid"]), num(window["window_id"]), str(window["title"])))
|
||||
}
|
||||
if len(windows) == 0 {
|
||||
lookup["status"] = "no_match"
|
||||
lines = append(lines, "No windows match these filters. This alone does not prove the application is unavailable or screenshot capture is broken. Do not substitute another application's foreground PID.")
|
||||
if str(query["app"]) != "" && query["pid"] != nil {
|
||||
next := obj{}
|
||||
for key, value := range query {
|
||||
if key != "pid" {
|
||||
next[key] = value
|
||||
}
|
||||
}
|
||||
lookup["suggested_arguments"] = next
|
||||
lines = append(lines, "app and pid are combined filters. Retry list_windows for the same app with pid omitted; use only a PID returned for that app.")
|
||||
} else if query["on_screen_only"] == true {
|
||||
next := obj{}
|
||||
for key, value := range query {
|
||||
next[key] = value
|
||||
}
|
||||
next["on_screen_only"] = false
|
||||
lookup["suggested_arguments"] = next
|
||||
lines = append(lines, "Retry the same filters with on_screen_only=false to check for minimized/off-screen windows. A listed window still needs a fresh get_window_state before interaction.")
|
||||
} else {
|
||||
lines = append(lines, "Verify the requested application name or PID. When the PID is unknown, search by the known app name with pid omitted. Do not guess a PID or operate an unrelated window.")
|
||||
}
|
||||
}
|
||||
data["lookup"] = lookup
|
||||
result["structuredContent"] = data
|
||||
result["content"] = []any{textBlock(strings.Join(lines, "\n"))}
|
||||
return result
|
||||
}
|
||||
Reference in new issue
Block a user