app
Grok Bot
Public Made by Adomby adom
Grok Bridge lets Adom talk to Grok Bot. Adom's AI environment is for electrical engineers: any provider, any desktop power tool, one place.
← Commit history
v1.3.4 (insiders): Grok Bot launches readable: grok_open adds the accessibility and no-occlusion switches so a background window paints and its page shows in UIA, and grok_open restart relaunches a Grok Bot its own auto-update started without them.
6 files changed
+87−13
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.3.3+1.3.4
CLAUDE.md+5@@ -64,6 +64,11 @@ Document once (8 s per UIA callback, 15 s budget) and report `computerState` / answer at once with `no_transcript` or `computer_unreachable`; focusedThread is never a caption button. readiness, webhook_fire (warning) and computer_screenshot (refuses) use it. +Launch switches (1.3.4): `HOST_FLAGS` in `handlers/windows.py` go on every launch. Without+them a background Grok Bot window never paints and UIA sees only caption buttons. Grok Bot's+auto-update relaunch drops them; `host_launch_flags_ok()` spots that and `grok_open+{restart:true, reason}` fixes it.+ Webhook bind file: `~/.adom/grok-webhook.json` `{url, key, header, boundAt}` (never log the key). `grok_webhook_ensure` reads UIA Edits Webhook URL / Webhook key (no OCR). ## The publish loop: insiders first, public on a human's words (wiki #2)
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "grok", "displayName": "Grok Bot",- "version": "1.3.3",+ "version": "1.3.4", "description": "Drive Grok Bot (standalone Electron app, Grok Bot.exe) from Adom Bridge: open/close/resize/screenshot/record the app, read thinking vs idle and the visible transcript of the focused thread, queue messages, bind the Grok Bridge webhook and create bots/channels. No public Grok HTTP API \u2014 the runtime talks back to ab over desktop_* callbacks. Cursor is last-resort fallback only if Grok Bot.exe is missing.", "homepage": "https://wiki.adom.inc/adom/grok-bridge", "author": "Adom Inc.",
handlers/chat.py+12−4@@ -11,11 +11,13 @@ import time from grok_detect import HOST_APP, detect_host from handlers.windows import (+ RESTART_HINT, _cb, _callback_ok, _title_of, flatten_callback, grok_hwnd,+ host_launch_flags_ok, ) # Proven UIA on Grok Bot.exe hwnd (AdomLapper): Document named "Grok Bot",@@ -353,13 +355,14 @@ UNREACHABLE_HINT = ( "events are accepted (HTTP 200) but no reply can come back. Ask the user to press " "Recover Grok Bot's Computer / Retry in Grok Bot, then re-check grok_chat_status." )-NO_PAGE_REASON = "no thread list on the page (window blank or still loading)"+NO_PAGE_REASON = "no page exposed to UIA (window blank or still loading)" def _no_page(hwnd: int, win, probe: dict, verb: str) -> dict: """Fast answer when the window has no Document, or shows the unreachable page.""" state = probe["state"] down = state in ("unreachable", "reconnecting")+ flags_ok = None if down else host_launch_flags_ok() out = { "success": False, "errorCode": "computer_unreachable" if down else "no_transcript",@@ -377,13 +380,18 @@ def _no_page(hwnd: int, win, probe: dict, verb: str) -> dict: "title": _title_of(win), "windowEtiquette": "background", "_hint": (UNREACHABLE_HINT if down else- "The Grok Bot window has no page yet (only caption buttons in UIA). After a "- "background grok_open the first paint can take minutes; retry in 30 s or "- "grok_screenshot to see it. Answered fast instead of waiting out the verb timeout."),+ "The Grok Bot window shows no page to UIA (only caption buttons). It may still be "+ "loading: retry in 30 s, or grok_screenshot to see it. Answered fast instead of "+ "waiting out the verb timeout."), "_next": ("grok_chat_status {} after the user presses Retry" if down else f"wait 30 s, then grok_{verb} {{}}"), } out.update(computer_fields(state, probe["banner"]))+ out["launchedWithAccessibility"] = flags_ok+ if flags_ok is False:+ out["focusedThreadReason"] = "Grok Bot's page is not exposed to UIA (started without the accessibility switches)"+ out["_hint"] = RESTART_HINT+ out["_next"] = "grok_open {\"restart\":true,\"reason\":\"Grok Bot page invisible to UIA\"}" return out
handlers/describe.py+1−1@@ -41,7 +41,7 @@ VERB_CATALOG = { "open": { "summary": "Launch Grok Bot in the BACKGROUND if installed. Polls process/window; Start-Process minimized fallback.", "long": True,- "hint": "Returns launched, opened, alreadyOpen, hwnd. Polls process_list/window after launch/desktop_open; Start-Process -WindowStyle Minimized if those do not take. Never steals focus.",+ "hint": "Returns launched, opened, alreadyOpen, hwnd. Polls process_list/window after launch/desktop_open; Start-Process -WindowStyle Minimized if those do not take. Never steals focus. Every launch carries --force-renderer-accessibility and the no-occlusion switches so a background window paints and is readable. {restart:true, reason} relaunches a Grok Bot that was started without them (launchedWithAccessibility:false, e.g. after its own auto-update).", "related": ["grok_list_windows", "grok_screenshot", "grok_close", "grok_readiness"], "pitfalls": [ "success:true requires a live process/window after poll — not just callback ok.",
handlers/windows.py+67−6@@ -21,6 +21,19 @@ from grok_detect import ( _grok_bot_running, ) +# Chromium switches every launch of the host carries. Without them a background+# (never-shown, occluded) window does not paint, and the page is invisible to UIA:+# grok_chat_read / grok_chat_status saw only the caption buttons (measured on+# AdomLapper 2026-10-14; also the "blank for minutes" of wiki #5). Grok Bot's own+# auto-update relaunch ("--updated") drops them, so grok_open {restart:true} exists.+HOST_FLAGS = [+ "--force-renderer-accessibility",+ "--disable-features=CalculateNativeWinOcclusion",+ "--disable-backgrounding-occluded-windows",+ "--disable-renderer-backgrounding",+]+_RESTART_REASON_MIN = 10+ def _cb(command: str, args: dict | None = None, timeout: int = 30) -> dict: """Best-effort ab callback. Never raises."""@@ -347,9 +360,8 @@ def launch_host(argv: list | None = None) -> dict: """ info = detect_host() exe = info.get("exe")- launch_args = {"background": True, "noActivate": True, "detached": True}- if argv:- launch_args["args"] = list(argv)+ launch_args = {"background": True, "noActivate": True, "detached": True,+ "args": HOST_FLAGS + list(argv or [])} if exe: launch_args["path"] = exe else:@@ -436,6 +448,31 @@ def _start_process_minimized(exe: str, extra_args: list | None = None) -> dict: return {"success": False, "via": "Start-Process", "error": str(e)} +def host_launch_flags_ok() -> bool | None:+ """True when the running Grok Bot main process carries HOST_FLAGS, False when it does+ not (it was started by its own updater or by hand), None when unknown."""+ try:+ procs = _as_processes(_cb("process_list", {"name": "Grok Bot"}, timeout=10))+ except Exception:+ return None+ mains = []+ for p in procs:+ cmd = str(p.get("commandLine") or p.get("cmdline") or p.get("CommandLine") or "")+ if "grok bot.exe" in cmd.lower() and "--type=" not in cmd:+ mains.append(cmd)+ if not mains:+ return None+ return any("--force-renderer-accessibility" in c for c in mains)+++RESTART_HINT = (+ "Grok Bot was started without the accessibility / no-occlusion switches (its own auto-update "+ "relaunch, or the user started it), so its page is invisible to UIA. Fix: grok_open "+ "{\"restart\":true,\"reason\":\"<why>\"} relaunches it in the background with them; its bots run "+ "in Grok's cloud, so nothing is lost."+)++ def handle_list_windows(args: dict) -> dict: info = detect_host() title = (args.get("titleContains") or "").strip()@@ -577,6 +614,20 @@ def handle_open(args: dict) -> dict: if fallback else "" ) + restarted = None+ if args.get("restart"):+ why = str(args.get("reason") or "").strip()+ if fallback:+ return {"success": False, "errorCode": "restart_refused_cursor",+ "_hint": "Grok Bot.exe is missing and the host is Cursor: never kill Cursor. Nothing restarted."}+ if len(why) < _RESTART_REASON_MIN:+ return {"success": False, "errorCode": "reason_required",+ "_hint": f"grok_open {{restart:true}} closes the user's Grok Bot window; pass reason "+ f"(at least {_RESTART_REASON_MIN} characters) saying why.",+ "_next": "grok_open {\"restart\":true,\"reason\":\"Grok Bot page invisible to UIA after its auto-update\"}"}+ restarted = _cb("process_kill", {"name": "Grok Bot.exe", "reason": why})+ time.sleep(3)+ hwnd0, win0, proc0 = _host_alive(info) listed = handle_list_windows(args) if (hwnd0 or proc0) else None @@ -617,7 +668,8 @@ def handle_open(args: dict) -> dict: if hwnd0 or proc0: if want_fg and hwnd0 and len(reason) >= _FG_REASON_MIN: _cb("desktop_set_window_state", {"hwnd": hwnd0, "state": "restore"})- return _envelope(+ flags_ok = None if fallback else host_launch_flags_ok()+ env = _envelope( success=True, launched=False, already_open=bool(hwnd0),@@ -631,11 +683,17 @@ def handle_open(args: dict) -> dict: "Grok Bot is already running — nothing launched, focus not stolen. " "hwnd/opened/alreadyOpen populated so callers can tell launched vs already up. " "To queue a message: grok_send. App control: grok_close / grok_resize / grok_screenshot."+ + (" " + RESTART_HINT if flags_ok is False else "") ), )+ env["launchedWithAccessibility"] = flags_ok+ if restarted is not None:+ env["restartKill"] = restarted+ env["_hint"] = "Restart asked, but Grok Bot was still running after the kill. " + env["_hint"]+ return env launch_name = fallback if fallback else HOST_APP- launch_args = {"background": True, "noActivate": True, "detached": True}+ launch_args = {"background": True, "noActivate": True, "detached": True, "args": list(HOST_FLAGS)} if exe: launch_args["path"] = exe else:@@ -656,7 +714,7 @@ def handle_open(args: dict) -> dict: start_fb = None if not hwnd and not proc:- start_fb = _start_process_minimized(exe or launch_name)+ start_fb = _start_process_minimized(exe or launch_name, HOST_FLAGS) via = start_fb.get("via") or "Start-Process -WindowStyle Minimized" launched_cb = start_fb hwnd, win, proc = _poll_host(info, timeout=20)@@ -713,6 +771,9 @@ def handle_open(args: dict) -> dict: host_running=really_up, extra_hint=hint, )+ out["launchedWithAccessibility"] = True if via != "desktop_open" else None+ if restarted is not None:+ out["restarted"] = True if not really_up: out["errorCode"] = "launch_did_not_start" out["_next"] = "Retry grok_open {}; confirm Grok Bot.exe path via grok_readiness."