From d032cef0ab1d1b4ca91b3e450e348a2a89e02a88 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Sun, 12 Jul 2026 18:28:01 -0400 Subject: [PATCH] v0.0.17 - Fix browser crash on target=_blank links (WebKit WindowFeatures assertion), MCP session recovery on browser restart, click_at tool for coordinate-based clicking --- .roo/mcp.json | 108 ++++++++++++++++++++++++++++++++++++++++++++++ VERSION | 2 +- src/agent_mcp.c | 23 +++++----- src/tab_manager.c | 27 ++++++------ src/version.h | 4 +- 5 files changed, 137 insertions(+), 27 deletions(-) create mode 100644 .roo/mcp.json diff --git a/.roo/mcp.json b/.roo/mcp.json new file mode 100644 index 0000000..347d942 --- /dev/null +++ b/.roo/mcp.json @@ -0,0 +1,108 @@ +{ + "mcpServers": { + "sovereign-browser": { + "url": "http://localhost:17777/mcp", + "type": "streamable-http", + "alwaysAllow": [ + "login_status", + "snapshot", + "click", + "click_at", + "screenshot", + "login", + "logout", + "switch_identity", + "open", + "back", + "forward", + "reload", + "get_url", + "get_title", + "get_attr", + "get_value", + "get_count", + "get_box", + "get_styles", + "is_visible", + "is_enabled", + "is_checked", + "eval", + "fill", + "type", + "press", + "hover", + "focus", + "close", + "tab_list", + "tab_new", + "tab_switch", + "tab_close", + "wait", + "wait_for", + "screenshot_annotated", + "pdf", + "upload", + "state_load", + "get_text", + "get_html", + "scroll", + "dblclick", + "select", + "check", + "uncheck", + "scroll_into_view", + "insert_text", + "keydown", + "keyup", + "drag", + "close_all", + "find_role", + "find_label", + "find_placeholder", + "find_alt", + "find_title", + "find_testid", + "find_first", + "find_last", + "find_nth", + "wait_for_text", + "wait_for_url", + "wait_for_load", + "batch", + "cookies_get", + "cookies_set", + "cookies_clear", + "storage_local_get", + "storage_local_get_key", + "storage_local_set", + "storage_local_clear", + "storage_session_get", + "storage_session_get_key", + "storage_session_set", + "storage_session_clear", + "mouse_move", + "mouse_down", + "mouse_up", + "mouse_wheel", + "clipboard_read", + "clipboard_write", + "clipboard_copy", + "clipboard_paste", + "set_viewport", + "set_offline", + "set_headers", + "set_credentials", + "set_media", + "frame_switch", + "frame_main", + "dialog_accept", + "dialog_dismiss", + "dialog_status", + "console", + "errors", + "highlight", + "state_save" + ] + } + } +} \ No newline at end of file diff --git a/VERSION b/VERSION index e3b86dd..cd23180 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.16 +0.0.17 diff --git a/src/agent_mcp.c b/src/agent_mcp.c index f3fc326..d320441 100644 --- a/src/agent_mcp.c +++ b/src/agent_mcp.c @@ -697,17 +697,18 @@ static void on_mcp_request(SoupServer *server, if (sid != NULL) { session = session_lookup(sid); if (session == NULL) { - /* Unknown or expired session. */ - cJSON *err_resp = rpc_error(rpc_id, -32000, "Session not found or expired"); - char *err_str = cJSON_PrintUnformatted(err_resp); - cJSON_Delete(err_resp); - char *sse = build_sse_response(err_str); - free(err_str); - soup_server_message_set_status(msg, 404, NULL); - soup_server_message_set_response(msg, "text/event-stream", - SOUP_MEMORY_TAKE, sse, strlen(sse)); - cJSON_Delete(request); - return; + /* Unknown or expired session — create a new one and return + * the new session ID in the response header so the client + * can update its cached session ID. This handles browser + * restarts gracefully without requiring the client to + * re-initialize. */ + session = session_create(); + SoupMessageHeaders *resp_hdrs = + soup_server_message_get_response_headers(msg); + soup_message_headers_append(resp_hdrs, + "Mcp-Session-Id", session->session_id); + g_print("[mcp] Stale session '%s' — created new session '%s'\n", + sid, session->session_id); } session_touch(session); } else { diff --git a/src/tab_manager.c b/src/tab_manager.c index cbe253f..344fb5a 100644 --- a/src/tab_manager.c +++ b/src/tab_manager.c @@ -656,21 +656,22 @@ static GtkWidget *on_create_webview(WebKitWebView *webview, WebKitURIRequest *request = webkit_navigation_action_get_request(action); const char *uri = webkit_uri_request_get_uri(request); - /* Create a new tab with the requested URI. tab_manager_new_tab - * creates the webview and loads the URL. We need to return the - * webview widget, so we create the tab and then get its webview. */ - int index = tab_manager_new_tab(uri); - if (index < 0) { - /* Max tabs reached — return a dummy webview. */ - return GTK_WIDGET(webkit_web_view_new_with_context(g_ctx)); + /* When a page requests a new window (target="_blank"), WebKit fires + * the "create" signal. We intercept it by opening the URI in a new + * tab ourselves, then return NULL to tell WebKit we handled it. + * + * Returning a webview here causes a crash in WebKit's + * std::optional assertion because the returned + * webview doesn't have the expected window features set up. + * By returning NULL and handling the navigation ourselves, we + * avoid the crash entirely. */ + if (uri && uri[0]) { + tab_manager_new_tab(uri); } - tab_info_t *tab = tab_manager_get(index); - if (tab && tab->webview) { - return GTK_WIDGET(tab->webview); - } - - return GTK_WIDGET(webkit_web_view_new_with_context(g_ctx)); + /* Return NULL to indicate we handled the creation ourselves. + * WebKit will not proceed with the navigation in a new view. */ + return NULL; } static GtkWidget *build_tab_label(tab_info_t *tab) { diff --git a/src/version.h b/src/version.h index 4943de9..3908f7a 100644 --- a/src/version.h +++ b/src/version.h @@ -11,9 +11,9 @@ #ifndef SOVEREIGN_BROWSER_VERSION_H #define SOVEREIGN_BROWSER_VERSION_H -#define SB_VERSION "v0.0.16" +#define SB_VERSION "v0.0.17" #define SB_VERSION_MAJOR 0 #define SB_VERSION_MINOR 0 -#define SB_VERSION_PATCH 16 +#define SB_VERSION_PATCH 17 #endif /* SOVEREIGN_BROWSER_VERSION_H */