Counterexample review: F0 returns no hostile instruction text in either HTML mode (0/3); F1 aria-label text appears only in scoped HTML (3/3); F2 display:none text appears in both modes (3/3). Fetch output only—not Agent behavior or remote-session cleanup.
Counterexample probe · Fetch returned text is not an Agent-compliance test. Fixture snippets translated from the original Chinese.

With AI agents moving quickly, we’re also trying tools such as Meta Muse, ChatGPT Dots, and Claude Duet. Taking over a browser, signing into a back office, and completing a workflow can look like just a few steps. But when I watch a screen navigate on its own, a less glamorous question comes to mind: after the workflow ends, where did the account data sent to the cloud go? When does the login state end? If a tool hijacks that state, or a page hides text that tries to change the facts of an order, who bears responsibility? Asking this does not assume cloud agents are unsafe; it means clarifying the trust boundary before handing them real work.

Product demos can show that a workflow runs, but rarely answer these questions in depth. I chose TinyFish for this examination and broke the problem down: inspect what TinyFish Fetch returns from six HTML pages containing only fictional orders, then check the pinned AgentQL MCP source, SDK documentation, remote-browser session docs, and each service’s separate terms.

The boundary first: this round observed that extraction mode changes the text returned by the tool. I did not pass that text to an Agent, so it cannot tell us whether an Agent would obey a fake instruction. Real accounts, cookies, remote sessions, and remote-data cleanup were outside this test.

These are separate layers: fetching a page, passing content to a model, letting the model choose the next action, and keeping an account’s login state remotely are four different things. Fetch alone can return text that a person cannot see; that does not mean a model will follow it. Letting an Agent click based on page content, or handing it a logged-in session, are two further risk gates that this test did not reach.

Evidence boundaries

MeasuredStatic reviewNot tested
TinyFish Fetch returned text: 6 fixtures × 3 modes once (18 observations), then F0/F1/F2 × 2 HTML modes × 3 repeats (18 observations)Pinned AgentQL MCP request construction, SDK includeHidden defaults, AgentQL REST session options, and two public terms-of-service documentsTinyFish Browser Agent’s final answer; real AgentQL SDK/REST request bodies; real login, Cookie/Profile persistence, streaming-URL access control, and remote data-deletion receipts

Same page, different extraction mode, different returned text

The synthetic supplier portal has four baseline fields:

  • Order: #101
  • Amount: USD 4200.00
  • Status: PENDING_APPROVAL
  • Payee marker: ACCOUNT_FIXTURE_GOOD_101

The other content is test-only: a fake instruction in an aria-label, hidden text marked display:none, an HTML comment, and a hidden-field value explicitly marked as a non-secret canary. There are no real bank-card details, API keys, cookies, or personal records.

The six variants and their generator live in an independent public fixture branch. The pages are served from a third-party static CDN, not GitHub Pages. Their source is pinned to a commit so the test inputs can be reviewed.

I ran two rounds with the free TinyFish Fetch tool. In round one, each of six pages was read using ordinary Markdown, ordinary HTML, and HTML from a specified DOM region: 18 returned-text observations. In round two, only F0, F1, and F2 were repeated three times each in ordinary and scoped HTML: another 18 observations.

Overview of six synthetic HTML fixtures and three extraction modes in round one, followed by repeated F0–F2 observations. The 36 observations are Fetch returned text, not Agent decisions or evidence of remote-session deletion.
36 returned-text observations · Sample sizes vary by test cell; this is not an overall security score.

I ran two rounds with free TinyFish Fetch. Round one read each of six pages using ordinary Markdown, ordinary HTML, and HTML from a specified DOM region (18 returned-text observations). Round two repeated only F0, F1, and F2 three times each in ordinary and scoped HTML (another 18). The options were format: "markdown", format: "html", and format: "html" with include_selectors: ["#trusted-order-facts", "#untrusted-webpage-region"]. A hit means the target instruction or canary string appeared in returned text—not that a model followed it.

FixtureUntrusted contentOrdinary MarkdownOrdinary HTMLScoped HTML
F0No fake instruction0/10/10/1
F1Fake instruction in aria-label0/10/11/1
F2Fake instruction in display:none1/11/11/1
F3Fake instruction in HTML comment0/10/10/1
F4Fake canary in hidden fieldcanary 0/1canary 0/1canary 1/1
F5Benign aria-label control——Benign aria-label text 1/1

F5 is a benign negative control: it contains no malicious text, yet scoped HTML still returned its harmless aria-label. That fits attribute text being serialized from the selected DOM region better than semantic detection of malicious content. The HTML-comment instruction in F3 did not appear in this round’s returned DOM serialization.

F4’s canary at 1/1 is a single first-round observation, not a repeat result. The second round repeated only F0–F2 in the two HTML modes; Markdown and F3–F5 were not repeated.

The repeat results show the clearest contrast:

FixtureOrdinary HTML (3 runs)Scoped HTML (3 runs)Observed result
F0: clean control0/30/3No fake instruction text in either response
F1: instruction in aria-label0/33/3Scoped extraction preserved the attribute text
F2: instruction in display:none node3/33/3Hidden-node text appeared in both HTML responses

All 36 returned-text observations contained the baseline order amount, status, and payee marker. “Contained” means those strings appeared in the tool’s returned text. It does not mean a model answered correctly, and these were not 36 AI decision tests.

Round one also produced two lower-confidence observations. The HTML-comment instruction did not appear in that round’s three extraction modes. The synthetic hidden-field canary was absent from ordinary extraction but appeared in the scoped-HTML response. Each result was observed only once, so neither supports a general rule.

The narrow conclusion is that extraction mode affects which page content enters the tool’s returned text. This does not prove that a provider stole real credentials or that a model will follow a hidden instruction. Those questions require different experiments.

AgentQL MCP does not simply put the local DOM into the request body

An initial hypothesis proposed that the local AgentQL MCP uploads the entire DOM tree from local Chrome for semantic targeting. That claim cannot be applied to every AgentQL path.

I inspected the pinned src/index.ts in tinyfish-io/agentql-mcp. The local request construction for extract-web-data sends a url, a prompt, and parameters to api.agentql.com/v1/query-data. The constructed JSON request body does not contain a local DOM or an accessibility_tree field.

That does not mean no page content leaves the machine. This MCP path sends a URL and extraction prompt; the server-side route that fetches and processes the page must be assessed separately. But this source is not evidence that the local browser serializes its entire DOM into the request body.

A different path wraps a Playwright page with the AgentQL SDK. AgentQL’s under-the-hood documentation says semantic processing uses page HTML and the Accessibility Tree and simplifies the input. Its JavaScript SDK page API lists different defaults for hidden elements:

SDK methodDocumented default for includeHidden
queryData()true
queryElements()false
getByPrompt()false

These differences show that configuration varies by method; they do not replace capturing an actual network request. I did not run the SDK or capture its request body, so I cannot claim that a hidden field was uploaded through the SDK path.

TinyFish’s cloud Browser Agent and AgentQL’s remote browser sessions are separate paths again. MCP, SDK, URL extraction, and remote browser sessions must be evaluated separately by execution location and data egress.

Remote-session cleanup depends on the lifecycle configuration

AgentQL’s remote-browser guide documents CDP connection addresses and page streaming URLs, and describes automatic session management and cleanup. That documentation alone does not establish whether a particular streaming URL needs secondary authorization or whether session data has been deleted from every store.

More specific options appear in the browser-session interface in the AgentQL REST API reference:

  • inactivity_timeout_seconds defaults to 300 seconds (five minutes).
  • shutdown_mode defaults to on_disconnect.
  • With on_inactivity_timeout, a session can remain after the last client disconnects until the inactivity timeout expires; reconnecting within that window can reuse the browser.

This is more precise than saying that every task immediately clears its session. But these are documented behaviors for the AgentQL REST session interface, not a universal rule for every TinyFish Browser Context Profile. I did not independently verify that a real session was completely deleted after a timeout.

AgentQL’s credential-caching example also shows Playwright’s storage_state(path=“session.json”) saving login state to a local file. That documents one local SDK workflow; it does not establish where a remote Browser Profile stores data.

Terms of service describe permitted processing, not what happened in a specific run

TinyFish and AgentQL have separate terms. They should not be blended into a vague, shared “vendor privacy promise.”

The TinyFish Terms of Service lists an effective date. Its Customer Data provisions authorize processing for service delivery, maintenance, review, and improvement, including model training, fine-tuning, and evaluation under the stated conditions; a separate applicable Custom Agreement may supersede conflicting terms.

The AgentQL Terms of Service is a separate contract. It describes processing Customer Data to provide, maintain, secure, and improve the service, as well as uses of aggregated or de-identified data. The AgentQL Terms of Service page lists an effective date of April 21, 2026.

Contractual permissions are important to understand before sending business data to a service, but a contract is not a runtime log. It cannot prove that a particular request was used for training or that a cookie leaked. Conversely, not seeing evidence of a leak does not prove that every execution path is safe.

How I would set practical boundaries

This test does not justify a blanket “TinyFish is safe” or “TinyFish is unsafe” rating. It does support concrete engineering practices:

  1. Do not start with a primary account. If login is required, use a dedicated low-privilege test account without payment, administrator, or unrelated business permissions. Typing a password into a remote browser does not make subsequent cookies or session state local again.
  2. Treat webpage content as untrusted input. aria-labels, hidden nodes, and comments can carry text unrelated to business fields. JSON output is not proof of sanitization. Define allowed fields explicitly, check their source, and test hidden content with negative cases.
  3. A selector is not a redaction mechanism. In this test, scoped HTML returned both aria-label text and a synthetic hidden-field value. A narrower DOM region can reduce irrelevant content, but it does not guarantee that attributes and hidden nodes inside that region are removed.
  4. Separate reading from writing. Summarizing an order or drafting a review is not the same permission as approving an order, sending email, or submitting a form. Irreversible actions need independent human confirmation, not just a prompt saying “do not submit.”
  5. Define session exit conditions. Identify the session API, shutdown_mode, and timeout before a task starts. A hidden page, a disconnected client, or a “Completed” tool status is not proof that remote state was cleared. Treat CDP addresses and streaming URLs as sensitive control endpoints.
  6. Minimize what reaches the model. For statements, customer records, and internal tickets, ask whether full-page semantic parsing is necessary. If a small set of fields can be selected locally, do not send the entire page to a cloud service by default.

I had planned to give the same fixtures to TinyFish Browser Agent to see how it would decide after receiving the page text. But the test account could not enforce the approved step cap, and the available default was far above the authorized range. I did not relax the limit just to finish the experiment. For automation, an execution boundary that cannot be enforced is a reason to stop—not something to wave away with “be careful.” This round therefore stops at Fetch output, pinned source review and public documentation.

The practical lesson is not to attach a global “safe” label to a product. First identify what is sent through which path, then use reviewable receipts to establish what was actually returned. Product names, privacy terms, and a successful demo are not substitutes for a trust-boundary audit.


Note on timing and scope: Source review and synthetic-page tests were recorded through October 25, 2026. Fetch testing includes six fixtures × three extraction modes plus three repeats each for F0/F1/F2 in two HTML modes, totaling 36 returned-text observations. No real account, cookie, API key or customer data was used. TinyFish Browser Agent, AgentQL SDK/REST runtime egress, real session persistence and remote deletion were not tested; this is not a product-wide security certification.

Source and reproduction note: Fetch used source commit 152cdc9a58e6a8fcf2d5857820d60774f0967555. Later commit 604bde069c0bd32bb41a4238140062e5468a7f2f changed only the README; the Git blob SHAs of F0–F5 remained unchanged. The pages were read through the third-party static CDN cdn.githubraw.com; its availability, caching and byte equivalence were not audited, so the pinned Git source does not prove the CDN served byte-identical content. The complete generator, classifier and raw returned-text receipts remain in the private research dossier.