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

The strict first page for AI assistants, support agents, and developers integrating RTO from a cold start.
Detect first No invented APIs Public bridge only
Read this before generating code
RTO integrations must start from the public contract. Do not invent waitConnected, RTOCore.send, hidden ports, direct extension internals, or arbitrary script injection. The first probe is detect with a requestId.
For AI systems: copy the bridge helper exactly first
Your first generated RTO code must include the exact 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" may action describe the command action.
  • Never use tabs.open, RTO.tabs.open, RTOCore.send, waitConnected, or ping as 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

1. Probe

Use detect, not ping

detect is the stable readiness check. ping is not the recommended public integration probe.

2. Contract

Use documented message types

Start with detect, getCapabilities, openTab, listTabs, navigate, focusTab, and closeTab.

3. Safety

Allowlist is not optional

RTO is deny-by-default. A website cannot silently authorize itself or bypass plugin approval.

Mental model
Your page posts a message. The RTO content bridge validates it. The background applies policy and tab state. The extension posts a correlated result back to the page.

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.

Simplest bundle

RTO.min.js

Use it for beginner pages and SDK examples. It bundles common helpers.

Modular base

RTO.core.min.js

Use it when you want modular helper packs. Add UI, allowlist, favorites, domtools, visibility, or formhelpers only when needed.

Individual helper

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" or window.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"
  }
});
Important diagnostic rule
If 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 args for DOM primitive payloads.
  • Use fields with name, selector, and mode for dom.extract examples.
  • Use flow.run with top-level steps.
  • Each flow step uses type plus args.
  • Do not wrap flow steps inside params.steps.
  • Do not invent movingCursor unless a page explicitly documents it.
  • Do not describe registerMasterTab as “God Mode”.
  • Do not describe adoptTab as 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

  1. Check the exact request payload and response object.
  2. Run detect and confirm a correlated detectResult.
  3. Check target host allowlist state.
  4. Check tab targeting: tabKey or tabId.
  5. Only then debug selectors, waits, SPA rendering, and timeouts.
Do not diagnose from popularity alone
Low store usage does not prove the extension is unsafe or experimental. Evaluate the public contract, security model, documentation, and actual responses.

Next