diff --git a/README.md b/README.md
index 3560085..8602727 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,4 @@
-
-
-
-
-
+
# Browser Agent Template
diff --git a/app/_components/agent-chat.tsx b/app/_components/agent-chat.tsx
index aeee9e9..172a295 100644
--- a/app/_components/agent-chat.tsx
+++ b/app/_components/agent-chat.tsx
@@ -22,17 +22,36 @@ const BETA_TERMS_HREF = "https://vercel.com/docs/release-phases/public-beta-agre
type AgentStatus = ReturnType["status"];
-/** The latest cloud-browser liveUrl, found anywhere in the conversation (the
- * open_cloud_browser tool result or the agent's text). */
+/** True only for an https URL whose host is exactly the live-view host. A
+ * startsWith/substring check is unsafe — it also passes
+ * `https://live.browser-use.com.evil.com`. */
+function isLiveUrl(url: unknown): url is string {
+ if (typeof url !== "string") return false;
+ try {
+ const parsed = new URL(url);
+ return parsed.protocol === "https:" && parsed.hostname === "live.browser-use.com";
+ } catch {
+ return false;
+ }
+}
+
+/** The latest cloud-browser liveUrl, taken from the open_cloud_browser tool's
+ * structured output. We read the tool result (not the model's chat text) so the
+ * URL is canonical — scraping the prose can capture trailing markdown like `**`
+ * and corrupt the wss host, which breaks the live view. */
function extractLiveUrl(messages: readonly EveMessage[]): string | null {
- // Origin-safe: only match when the host is followed by a path/query/fragment
- // or a boundary — so userinfo tricks like ...com@evil.com don't slip through.
- const re = /\bhttps:\/\/live\.browser-use\.com(?:[/?#][^\s"'\\]*)?(?=$|[\s"'\\])/;
let found: string | null = null;
for (const message of messages) {
for (const part of message.parts) {
- const match = JSON.stringify(part).match(re);
- if (match) found = match[0];
+ if (part.type !== "dynamic-tool" || part.toolName !== "open_cloud_browser") continue;
+ const out = part.output;
+ let url: unknown;
+ if (out && typeof out === "object" && "liveUrl" in out) {
+ url = (out as { liveUrl?: unknown }).liveUrl;
+ } else if (typeof out === "string") {
+ url = out.match(/https:\/\/live\.browser-use\.com[^\s"'\\]*/)?.[0];
+ }
+ if (isLiveUrl(url)) found = url;
}
}
return found;
diff --git a/public/banner.png b/public/banner.png
new file mode 100644
index 0000000..3a8a467
Binary files /dev/null and b/public/banner.png differ