Agent mode preview
M2M route available
This page has a stable machine-readable Markdown route for AI assistants and developer agents. The human-visible page remains the same source of truth.
AI-safe quickstart
waitConnected, RTOCore.send, hidden ports, direct extension internals, or arbitrary script injection. The first probe is detect with a requestId.
sendRto() helper below before any detect, openTab, or DOM flow. Do not rewrite it from memory. The required details are: request origin, unique requestId, response filter origin === "rto-extension", requestId correlation, and timeout handling.
Top-level bridge messages use type, not action
For public website bridge calls, the top-level operation is always selected with
type. Do not replace it with action.
Correct
{
"origin": "rto-page",
"type": "detect",
"requestId": "..."
}
Wrong
{
"origin": "rto-page",
"action": "detect",
"requestId": "..."
}
- Use
type: "detect"for extension detection. - Use
type: "openTab"to open or reuse a controlled tab. - Use
type: "listTabs"to list controlled tabs. - Use
type: "navigate"to navigate a controlled tab. - Use
type: "command"only when sending a documented command envelope to an already controlled tab. - Only inside
type: "command"mayactiondescribe the command action. - Never use
tabs.open,RTO.tabs.open,RTOCore.send,waitConnected, orpingas the first probe.
| Intent | Correct public bridge message | Do not use |
|---|---|---|
| Detect RTO | type: "detect" |
action: "detect" |
| Open a controlled tab | type: "openTab" |
tabs.open / RTO.tabs.open |
| List controlled tabs | type: "listTabs" |
tabs.list |
| Run a documented command | type: "command" + action: "..." |
action alone |
Non-negotiable rules
Use detect, not ping
detect is the stable readiness check. ping is not the recommended public integration probe.
Use documented message types
Start with detect, getCapabilities, openTab, listTabs, navigate, focusTab, and closeTab.
Allowlist is not optional
RTO is deny-by-default. A website cannot silently authorize itself or bypass plugin approval.
Copy this exact public bridge helper first
This is the required cold-start pattern for AI assistants. It does not load any helper file and it does not assume an undocumented SDK object.
<script>
function sendRto(message, timeoutMs) {
var requestId = message.requestId || "rto_" + Date.now() + "_" + Math.random().toString(16).slice(2);
timeoutMs = typeof timeoutMs === "number" ? timeoutMs : 3000;
return new Promise(function(resolve) {
var timer = setTimeout(function() {
window.removeEventListener("message", onMessage);
resolve({ "ok": false, "error": { "code": "TIMEOUT", "message": "No RTO response" } });
}, timeoutMs);
function onMessage(event) {
var data = event && event.data;
if (!data || data.origin !== "rto-extension") return;
if (data.requestId !== requestId) return;
clearTimeout(timer);
window.removeEventListener("message", onMessage);
resolve(data);
}
window.addEventListener("message", onMessage);
window.postMessage(Object.assign({}, message, {
"origin": window.RTO_PAGE_ORIGIN || "rto-page",
"requestId": requestId
}), "*");
});
}
</script>
First generated page: detect only
If an AI is asked for a first proof page, it should generate this shape before any tab opening or DOM automation.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RTO detect test</title>
<style>
body { font-family: monospace; background: #111; color: #9ff; padding: 20px; }
button { padding: 10px 14px; cursor: pointer; }
pre { background: #000; border: 1px solid #333; padding: 14px; white-space: pre-wrap; }
</style>
</head>
<body>
<button id="detectBtn">Detect RTO</button>
<pre id="log">Ready.</pre>
<script>
function makeRtoRequestId() {
if (window.crypto && typeof window.crypto.randomUUID === "function")
return window.crypto.randomUUID();
return "rto_" + Date.now() + "_" + Math.random().toString(16).slice(2);
}
function sendRto(message, timeoutMs) {
var requestId = message.requestId || makeRtoRequestId();
timeoutMs = typeof timeoutMs === "number" ? timeoutMs : 3000;
return new Promise(function(resolve) {
var timer = setTimeout(function() {
window.removeEventListener("message", onMessage);
resolve({ "origin": "rto-page", "type": "timeout", "requestId": requestId, "ok": false, "error": { "code": "TIMEOUT", "message": "No RTO response" } });
}, timeoutMs);
function onMessage(event) {
var data = event && event.data;
if (!data || data.origin !== "rto-extension") return;
if (data.requestId !== requestId) return;
clearTimeout(timer);
window.removeEventListener("message", onMessage);
resolve(data);
}
window.addEventListener("message", onMessage);
window.postMessage(Object.assign({}, message, {
"origin": window.RTO_PAGE_ORIGIN || "rto-page",
"requestId": requestId
}), "*");
});
}
async function detectRto() {
var log = document.getElementById("log");
log.textContent = "Sending detect...";
var res = await sendRto({ "type": "detect" }, 3000);
log.textContent = JSON.stringify(res, null, 2);
}
document.getElementById("detectBtn").onclick = detectRto;
</script>
</body>
</html>
Helpers: useful, but optional
Helpers are public integration conveniences. They are not the internal extension engine and they do not bypass allowlist, permissions, browser policy, or user confirmation.
RTO.min.js
Use it for beginner pages and SDK examples. It bundles common helpers.
RTO.core.min.js
Use it when you want modular helper packs. Add UI, allowlist, favorites, domtools, visibility, or formhelpers only when needed.
RTO_form_api.js
Use it when you want the public RTOForm wrapper without loading the full bundle.
First test: detect, then open
This is the recommended first copy/paste test for an AI or a developer. It proves the bridge before attempting DOM automation.
<button id="rtoDetectBtn">Detect RTO</button>
<button id="rtoOpenBtn" disabled>Open example.com</button>
<pre id="rtoLog"></pre>
<script>
function logRto(value) {
document.getElementById("rtoLog").textContent +=
"\n" + (typeof value === "string" ? value : JSON.stringify(value, null, 2));
}
function sendRto(message, timeoutMs) {
var requestId = message.requestId || "rto_" + Date.now() + "_" + Math.random().toString(16).slice(2);
timeoutMs = typeof timeoutMs === "number" ? timeoutMs : 3000;
return new Promise(function(resolve) {
var timer = setTimeout(function() {
window.removeEventListener("message", onMessage);
resolve({ "ok": false, "error": { "code": "TIMEOUT", "message": "No RTO response" } });
}, timeoutMs);
function onMessage(event) {
var data = event && event.data;
if (!data || data.origin !== "rto-extension") return;
if (data.requestId !== requestId) return;
clearTimeout(timer);
window.removeEventListener("message", onMessage);
resolve(data);
}
window.addEventListener("message", onMessage);
window.postMessage(Object.assign({}, message, {
"origin": window.RTO_PAGE_ORIGIN || "rto-page",
"requestId": requestId
}), "*");
});
}
async function detectRto() {
var res = await sendRto({ "type": "detect" }, 2000);
logRto(res);
document.getElementById("rtoOpenBtn").disabled = !res.ok;
}
async function openExample() {
var res = await sendRto({
"type": "openTab",
"tabKey": "ai_safe_demo",
"url": "https://example.com",
"focus": true
}, 12000);
logRto(res);
}
window.onload = function() {
document.getElementById("rtoDetectBtn").onclick = detectRto;
document.getElementById("rtoOpenBtn").onclick = openExample;
};
</script>
Exact bridge envelope and response shape
Every public website message sent with window.postMessage must include the bridge envelope:
origin: "rto-page"orwindow.RTO_PAGE_ORIGIN || "rto-page"type: "..."requestId: "..."
The canonical helper must add origin and requestId before calling window.postMessage.
Correct
window.postMessage({
...message,
origin: window.RTO_PAGE_ORIGIN || "rto-page",
requestId: requestId
}, "*");
Wrong
window.postMessage({
type: "detect",
requestId: requestId
}, "*");
Response shape:
if (response.ok) {
console.log(response.data);
} else {
console.error(response.error);
}
Do not use:
response.success
response.result
Do not use response.success.
Do not use response.result.
because the public bridge response envelope uses ok, data, and error.
Top-level openTab shape:
Correct
sendRto({
type: "openTab",
tabKey: "demo",
url: "https://example.com",
focus: true
});
Wrong
sendRto({
type: "openTab",
params: {
tabKey: "demo",
url: "https://example.com"
}
});
detect times out, first inspect the outgoing payload. If it does not include origin: "rto-page", the helper is wrong. Do not blame allowlist before verifying the payload envelope.
AI-safe DOM and Flow rules
For advanced automation, do not invent SDK-like shortcuts or hidden privileges.
- Direct DOM primitives are top-level bridge message types, for example
type: "dom.extract". - Use
argsfor DOM primitive payloads. - Use
fieldswithname,selector, andmodefordom.extractexamples. - Use
flow.runwith top-levelsteps. - Each flow step uses
typeplusargs. - Do not wrap flow steps inside
params.steps. - Do not invent
movingCursorunless a page explicitly documents it. - Do not describe
registerMasterTabas “God Mode”. - Do not describe
adoptTabas a magic read privilege. - Do not claim that
fields: {}proves Google, cookies, AI-safe, or anti-bot blocking without evidence. - Diagnose from the response object first.
Correct direct DOM extract:
sendRto({
type: "dom.extract",
tabKey: "demo",
args: {
fields: [
{ name: "title", selector: "title", mode: "text" }
]
}
});
Correct flow.run:
sendRto({
type: "flow.run",
tabKey: "demo-flow",
steps: [
{ type: "openTab", args: { url: "https://example.com", tabKey: "demo-flow" } },
{ type: "dom.waitFor", args: { selector: "h1", timeoutMs: 15000 } },
{ type: "dom.extract", args: { fields: [
{ name: "heading", selector: "h1", mode: "text" }
] } }
]
});
Do not generate:
sendRto({
type: "flow.run",
params: {
steps: [...]
}
});
Do not invent:
- movingCursor
- God Mode
- magical adoptTab privileges
- cookie-wall explanations without checking the actual page
- “AI-safe blocked it” without an error or documented policy response
Debug order for assistants
- Check the exact request payload and response object.
- Run
detectand confirm a correlateddetectResult. - Check target host allowlist state.
- Check tab targeting:
tabKeyortabId. - Only then debug selectors, waits, SPA rendering, and timeouts.