Is Coupert Pure: AI-Powered Coupons & Price Compare safe?

Medium risk

Coupert Pure: AI-Powered Coupons & Price Compare is medium risk. When the visual shopping or image-search flow runs, the extension screenshots the visible tab. It blocks internal pages but allows http/https, returns a PNG data URL, and can POST that image to pure.coupert.com after an AI quota check.…

Coupert.comv1.0.54Chrome Web Store
45Risk

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

Visual shopping can screenshot the active web page

When the visual shopping or image-search flow runs, the extension screenshots the visible tab.

It blocks internal pages but allows http/https, returns a PNG data URL, and can POST that image to pure.coupert.com after an AI quota check.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You use the visual shopping or image-search feature on a normal web page.

The extension did this

The extension captures the visible area of the active tab as an image.

The source blocks browser-internal pages but permits ordinary http and https pages.

02EvidenceFIELD TABLE
Data exposed by the screenshot and recommendation request
FieldValueWhy it matters
Visible page image
Checkout page showing item names, shipping options, and order total (illustrative)The image can include whatever text, images, account context, and page state are visible on your screen at that moment.
Captured image body field
image_base64: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII= (illustrative one-pixel PNG)When the captured value is a data URL, the code strips the prefix and places the remaining image data in a request field.
Recommendation type
type: productsThe request labels the image as product-related context for the visual shopping backend.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://pure.coupert.com/api/ai/image_recommend?token=<redacted>
Headers
Content-Typeapplication/json
04EvidenceCODE COMPARE
The code that does this

The shipped handler captures the tab and the recommendation path posts image data

What it actually does
Readable background screenshot handlerbackground.js
tt.bp().on("deals:captureVisibleTab", function() {
        var e = i(u().mark((function e(t, r) {
          var n, a, i, s, c, l;
          return u().wrap((function(e) {
            for (;;) switch (e.prev = e.next) {
              case 0:
                return e.prev = 0, e.next = 3, chrome.tabs.query({
                  active: !0,
                  currentWindow: !0
                });
              case 3:
                if (n = e.sent, a = o(n, 1), null == (i = a[0]) || i.id, null == i || i.url, null == i || i.windowId, null != i && i.windowId) {
                  e.next = 9;
                  break
                }
                throw new Error("No active window found");
              case 9:
                if (!((s = i.url || "").startsWith("chrome://") || s.startsWith("chrome-extension://") || s.startsWith("edge://") || s.startsWith("about:") || s.startsWith("file://"))) {
                  e.next = 12;
                  break
                }
                throw new Error("Cannot capture this page. Please try on a regular web page.");
              case 12:
                return e.next = 14, chrome.tabs.captureVisibleTab(i.windowId, {
                  format: "png"
                });
              case 14:
                c = e.sent, r({
                  success: !0,
                  dataUrl: c
                }), e.next = 25;
                break;
              case 19:
                e.prev = 19, e.t0 = e.catch(0), console.error("[Deals Background] Failed to capture tab:", e.t0), ((l = e.t0.message || String(e.t0)).includes("permission") || l.includes("activeTab") || l.includes("<all_urls>")) && (l = "Please use the screenshot button in the side panel."), r({
                  success: !1,
                  error: l
                });
              case 25:
              case "end":
                return e.stop()
            }
          }), e, null, [
            [0, 19]
          ])
        })));
        return function(t, r) {
          return e.apply(this, arguments)
        }}())
Readable image recommendation request builderbackground.js
var lO = "products";

    function pO(e) {
      return "string" == typeof e && e.startsWith("data:image/")
    }

    function fO(e) {
      var t = e.indexOf("base64,");
      if (-1 === t) throw new Error("Invalid base64 data URL");
      return e.substring(t + 7)
    }

    function dO() {
      return hO.apply(this, arguments)
    }

    function hO() {
      return (hO = i(u().mark((function e() {
        var t, r, n, o, a;
        return u().wrap((function(e) {
          for (;;) switch (e.prev = e.next) {
            case 0:
              return e.next = 2, bx();
            case 2:
              if (t = e.sent, r = cO({}, t), n = r.data, o = cO({}, n), a = o.token, o.can_use) {
                e.next = 7;
                break
              }
              throw new Error("AI token unavailable: can_use = false");
            case 7:
              if (a) {
                e.next = 9;
                break
              }
              throw new Error("AI token missing from /api/ai/chance");
            case 9:
              return e.abrupt("return", a);
            case 10:
            case "end":
              return e.stop()
          }
        }), e)
      })))).apply(this, arguments)
    }

    function vO(e) {
      return mO.apply(this, arguments)
    }

    function mO() {
      return mO = i(u().mark((function e(t) {
        var r, n, o, a, i, s, c, l, p, f, d, h, v = arguments;
        return u().wrap((function(e) {
          for (;;) switch (e.prev = e.next) {
            case 0:
              return n = v.length > 1 && void 0 !== v[1] ? v[1] : lO, o = v.length > 2 && void 0 !== v[2] ? v[2] : "", e.next = 4, Lo();
            case 4:
              return a = e.sent, e.next = 7, dO();
            case 7:
              return i = e.sent, s = t || "", c = o || "", pO(t) && (c = fO(t), s = ""), l = {
                image_url: s || "",
                image_base64: c || "",
                type: n
              }, p = "".concat(a, "/api/ai/image_recommend?token=").concat(encodeURIComponent(i)), (s || "").length, (c || "").length, e.next = 16, fetch(p, {
                method: "POST",
                headers: {
                  "Content-Type": "application/json"
                },
                body: JSON.stringify(l)
              });
            case 16:
              if ((f = e.sent).ok) {
                e.next = 23;
                break
              }
              return e.next = 20, f.text();
            case 20:
              throw d = e.sent, console.error("[Deals Internal] Error response:", d), new Error("image_recommend failed: ".concat(f.status, " ").concat(f.statusText));
            case 23:
              return e.next = 25, f.json();
            case 25:
              return h = e.sent, Array.isArray(null == h || null === (r = h.data) || void 0 === r ? void 0 : r.recommendations) && h.data.recommendations.length, e.abrupt("return", h);
            case 29:
            case "end":
              return e.stop()
          }
        }), e)
      }))), mO.apply(this, arguments)
    }
05EvidenceTHIRD PARTY LIST
External service receiving the image recommendation request
  • pure.coupert.com

    Coupert API host used for the AI quota check and image recommendation POST.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

AI agent sends page content to Coupert for actions

When the AI web-automation agent runs, the content script collects the page URL, title, selected/body text, signals, and history.

The background embeds that in a request to pure.coupert.com/api/ai/gem, then runs returned click/type actions.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You start the AI web-automation agent on an active page.

The extension did this

The extension sends a page snapshot to Coupert and can apply returned browser actions.

The path is feature-triggered; passive analysis did not observe the endpoint without invoking the agent.

02EvidenceFIELD TABLE
Fields included in the AI agent request path
FieldValueWhy it matters
Page URL
https://shop.example.com/checkoutThe URL identifies the site and exact page where the agent is operating.
Page title
Checkout - Example StoreThe page title gives the remote service context about the workflow open in your browser.
Page content
Order summary Shipping options Promo code Apply Total $84.17 (illustrative)The page text can include checkout details, account text, product details, or other visible information used to decide the next action.
Signals and metrics
interactiveRefCount: 18; visibleCanvasCount: 0The request includes page-derived context such as interactive element counts and page state used by the agent.
Recent agent history
Step 3: [click] {"ref":"ref_12"} -> OKRecent actions and visited pages let the remote service keep context across multiple automation steps.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://pure.coupert.com/api/ai/gem?m=6&token=<redacted>
Headers
Content-Typeapplication/json
04EvidenceCODE COMPARE
The code that does this

The agent collects page content, builds the remote prompt, and posts it

What it actually does
Readable page text extractorcontent.js
function BR(e) {
          if (!e) return LR;
          for (var t = 0, r = Object.keys(RR); t < r.length; t++) {
            var n = r[t];
            if (e === n || e.endsWith("." + n)) return [].concat((0, Zi.Z)(RR[n]), LR)
          }
          return LR
        }

        function FR(e) {
          return e ? "META" === e.tagName ? (e.getAttribute("content") || "").trim() : (e.innerText || e.textContent || "").trim() : ""
        }

        function UR() {
          var e, t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {},
            r = t.hostname || ("undefined" != typeof window ? null === (e = window.location) || void 0 === e ? void 0 : e.hostname : "") || "",
            n = t.maxLength || 2e3;
          if ("undefined" == typeof document || !document.body) return {
            text: "",
            source: "empty",
            selectorsHit: [],
            hostname: r
          };
          var o, i = BR(r),
            a = [],
            c = new Set,
            s = [],
            u = ZR(i);
          try {
            for (u.s(); !(o = u.n()).done;) {
              var l = o.value;
              try {
                var p = document.querySelectorAll(l),
                  d = !1;
                if (p.forEach((function(e) {
                    var t = FR(e);
                    t.length < 3 || t.length > 500 || c.has(t) || (c.add(t), a.push(t), d = !0)
                  })), d && s.push(l), a.join(" ").length >= n) break
              } catch (e) {}
            }
          } catch (e) {
            u.e(e)
          } finally {
            u.f()
          }
          if (a.length > 0) return {
            text: a.join("\n").slice(0, n),
            source: "selectors",
            selectorsHit: s,
            hostname: r
          };
          var f = (document.body.innerText || "").slice(0, n);
          return console.warn("[ML-EXTRACT] no selector hit on hostname=".concat(r, ", fallback to body.innerText (len=").concat(f.length, ")")), {
            text: f,
            source: "fallback",
            selectorsHit: [],
            hostname: r
          }
        }
Readable ai.agent.think sendercontent.js
ut = function() {
              var e = (0, l.Z)(d().mark((function e(t) {
                var n, o, i, a;
                return d().wrap((function(e) {
                  for (;;) switch (e.prev = e.next) {
                    case 0:
                      return n = (0, Hr.Qc)(window.location.href) || {}, o = n.domain, i = void 0 === o ? "" : o, e.next = 3, x.ZP.cs().send("ai.agent.think", {
                        task: r,
                        observation: {
                          url: t.url,
                          title: t.title,
                          pageContent: t.pageContent,
                          signals: t.signals,
                          viewport: t.viewport,
                          metrics: t.metrics
                        },
                        history: j.slice(-20),
                        visitedPages: S.slice(-30),
                        stepCount: T,
                        maxSteps: Ky,
                        domain: i || ""
                      }, {
                        to: x.ZP.Env.BACKGROUND_PAGE
                      });
                    case 3:
                      return a = e.sent, e.abrupt("return", a);
                    case 5:
                    case "end":
                      return e.stop()
                  }
                }), e)
              })));
              return function(t) {
                return e.apply(this, arguments)
              }
            }(),
Readable Gemini POST helperbackground.js
var hx = "".concat(Vt, "/api/ai/gem");

    function vx(e, t) {
      return mx.apply(this, arguments)
    }

    function mx() {
      return mx = i(u().mark((function e(t, r) {
        var n, o, a, i, s, c, l, p = arguments;
        return u().wrap((function(e) {
          for (;;) switch (e.prev = e.next) {
            case 0:
              return n = p.length > 2 && void 0 !== p[2] ? p[2] : null, o = p.length > 3 && void 0 !== p[3] ? p[3] : "", a = "".concat(hx, "?m=").concat(t, "&token=").concat(o), e.next = 5, fetch(a, {
                method: "POST",
                headers: {
                  "Content-Type": "application/json"
                },
                body: JSON.stringify(r),
                signal: n,
                mode: "no-cors"
              });
            case 5:
              if ((i = e.sent).ok) {
                e.next = 12;
                break
              }
              return e.next = 9, i.text();
            case 9:
              c = e.sent;
              try {
                l = JSON.parse(c)
              } catch (e) {
                l = {
                  error: {
                    message: c
                  }
                }
              }
              throw {
                status: i.status, message: (null === (s = l.error) || void 0 === s ? void 0 : s.message) || "API error: ".concat(i.status)
              };
            case 12:
              return e.abrupt("return", i);
            case 13:
            case "end":
              return e.stop()
          }
        }), e)
      }))), mx.apply(this, arguments)
    }

    function gx(e, t) {
      return yx.apply(this, arguments)
    }

    function yx() {
      return yx = i(u().mark((function e(t, r) {
        var n, o, a, i = arguments;
        return u().wrap((function(e) {
          for (;;) switch (e.prev = e.next) {
            case 0:
              return n = i.length > 2 && void 0 !== i[2] ? i[2] : null, o = i.length > 3 && void 0 !== i[3] ? i[3] : "", e.next = 4, vx(t, r, n, o);
            case 4:
              return a = e.sent, e.abrupt("return", a.json());
            case 6:
            case "end":
              return e.stop()
          }
        }), e)
      }))), yx.apply(this, arguments)
    }

    function bx() {
      return zo("/api/ai/chance")
    }
Readable prompt builderbackground.js
var aO = function(e) {
      var t = e.task,
        r = e.url,
        n = e.title,
        a = e.signals,
        i = e.metrics,
        s = e.pageContent,
        u = e.history,
        c = e.visitedPages,
        l = e.stepCount,
        p = (l || 0) + 1,
        f = e.maxSteps || 50,
        d = Math.max(0, f - p + 1),
        h = function(e) {
          return Array.isArray(e) && 0 !== e.length ? e.map((function(e, t) {
            var r = e || {},
              n = r.action,
              o = r.params,
              a = r.result,
              i = r.error,
              s = r.targetUrl,
              u = i ? "FAILED: ".concat(i) : a || "OK",
              c = o ? JSON.stringify(o) : "{}",
              l = s ? " => ".concat(s) : "";
            return "  Step ".concat(t + 1, ": [").concat(n, "] ").concat(c).concat(l, " -> ").concat(u)
          })).join("\n") : "(No actions taken yet - this is the first step)"
        }(u),
        v = function(e) {
          return Array.isArray(e) && 0 !== e.length ? e.map((function(e, t) {
            return "  ".concat(t + 1, ". [").concat(e.title || "Untitled", "](").concat(e.url, ")")
          })).join("\n") : "(none yet)"
        }(c),
        m = a ? Object.entries(a).map((function(e) {
          var t = o(e, 2),
            r = t[0],
            n = t[1];
          return "  - ".concat(r, ": ").concat(n)
        })).join("\n") : "(none)",
        g = i ? Object.entries(i).map((function(e) {
          var t = o(e, 2),
            r = t[0],
            n = t[1];
          return "  - ".concat(r, ": ").concat(n)
        })).join("\n") : "(none)",
        y = "";
      return d <= 3 ? y = "\n⚠️ CRITICAL: You have almost NO steps left!\nYou MUST return `done` NOW with a comprehensive summary of everything you have gathered.\nDo NOT scroll, navigate, or explore further." : d <= 7 ? y = "\n⚠️ WARNING: Very few steps remaining (" + d + " left).\nWrap up immediately. Return `done` with a detailed summary." : d <= 15 ? y = "\n⏰ ATTENTION: Steps running low (" + d + " left).\nStart wrapping up. Collect final info and prepare summary." : d <= 25 && (y = "\n📊 Budget check: " + d + " steps remaining. Plan your remaining actions carefully."), ["You are a goal-oriented web automation assistant.", "You have a STRICT step budget. Every action costs 1 step.", "Your job: complete the task WITHIN budget, then return `done` with a useful summary.", "Return ONLY JSON: {action, params, reasoning}.", 0 === l ? "- Also include `taskName`: a short (≤8 words) title summarizing the user goal." : "", "", "## Task", t || "(No task specified)", "", "## Current Page (Step ".concat(p, " of ").concat(f, ")"), "- URL: ".concat(r || "(unknown)"), "- Title: ".concat(n || "(unknown)"), "- Steps Used: ".concat(p), "- Steps Remaining: ".concat(d), "- Signals:", m, "- Metrics:", g, y, "", "## Pages Already Visited (DO NOT revisit unless necessary)", v, "", "## Action History (last steps)", h, "", "## Available Actions", "| Action | Params |", "|--------|--------|", '| click | { "ref": "ref_N" } |', '| type | { "ref": "ref_N", "text": "v" } |', '| select | { "ref": "ref_N", "value": "v" } |', '| scroll | { "ref": "ref_N" } or', '|  | { "direction": "up/down", "amount": px } |', '| hover | { "ref": "ref_N" } |', '| navigate | { "url": "<full_url>" } |', "| back |  |", "| reload |  |", '| wait | { "ms": num } |', '| extract | { "ref": "ref_N" } |', '| done | { "message": "markdown summary", "data": any } |', '| fail | { "reason": "why" } |', '| ask_user | { "question": "what" } |', "", "## Rules", "- LANGUAGE: You MUST respond in the SAME language as the user's task.", "  If the task is in Chinese, ALL your output (reasoning, message, taskName) MUST be in Chinese.", "  If the task is in English, respond in English. Match the user's language exactly.", "- Use ref IDs from the element list.", '  e.g. [ref_3] means ref: "ref_3".', "- Refs are stable within the SAME page. After navigation, new refs are assigned.", "- Only use refs in the current list.", "- NEVER click elements marked disabled=true.", '- NEVER click elements marked aria-disabled="true".', "- For comment/reply/search tasks, use `type` on textbox or", "  contenteditable first.", "- For search tasks: after one `type`, submit via click/navigate; do NOT keep typing the same query repeatedly on the same URL.", "- NEVER invent a city/location (e.g. 无锡/上海/北京) unless explicitly mentioned in the user task or clearly shown on page.", "- If the task is location-ambiguous, search with original keywords first; ask_user for city only if needed.", "- Click submit only when it is enabled.", "- If target not listed, scroll to reveal.", "- If metrics suggest canvas-heavy page with few actionable refs,", "  prefer `ask_user` quickly instead of blind retries.", "- Use `reload` ONLY as a last resort for obvious temporary page lockups.", "- Never use repeated `reload` as a loop strategy.", "- Return ONE action. No markdown/fences.", "- If done, return done. If stuck, ask_user.", "- When returning `done`:", "  - `message` is shown to the user as the final answer.", "  - Use Markdown formatting (headings, lists, bold).", "  - ALWAYS include relevant URLs/links from the page.", "  - For recommendations: include title, URL, and reason.", "  - For data collection: include all gathered details.", "  - Prefer structured JSON in `data` for programmatic use.", "  - Recommended `data` shape:", "    {", '      "summary": {', '        "completed": string[],', '        "pendingOrUnknown": string[],', '        "currentUrl": string,', '        "nextUserAction": string', "      }", "    }", '  - If history has action=skill and params.skill="auto_coupon_test":', "    1) message should mention coupon test outcome explicitly.", "    2) data should include `couponTest`:", "       { outcome, source, summaryText, hasValidCoupons,", "         bestCode, savedAmount, testedCount }", "    3) If message already includes coupon outcome, set", "       data.couponTestIncludedInMessage=true, else false.", "- If previous action failed, try differently.", "- If you return to a page you have seen before, treat links you already opened from that same page as excluded candidates.", "- Do NOT click the same navigation target twice from the same source page unless the user explicitly asks for a retry.", "- Do NOT guess random URLs. But you CAN navigate to", "  well-known sites (Google, Amazon, eBay, etc.) to search.", "- NEVER call `navigate` to the current page URL.", "", "## Multi-Channel Strategy", "- If current page lacks useful info after 2-3 attempts:", "  1. Navigate to a search engine result page with refined query (do not inject new location keywords unless provided).", "  2. Try a different website (e.g. Amazon, eBay, official site).", "  3. Use `done` to return partial results rather than looping.", "- Do NOT keep scrolling/clicking the same page hoping for new content.", "- Prefer navigating to a new source over repeating failed actions.", "- For product/price tasks: try multiple retailers, not just one.", "", "## Execution Strategy (CRITICAL)", "", "### Three-Phase Approach", "- Phase 1 (first 40% of steps): EXPLORE — navigate to relevant pages, search for info.", "- Phase 2 (next 40% of steps): COLLECT — read page content, extract key data.", "- Phase 3 (last 20% of steps): SUMMARIZE — return `done` with comprehensive results.", "- Calculate your phase boundaries NOW:", "  Phase 1 ends at step ~".concat(Math.round(.4 * f)), "  Phase 2 ends at step ~".concat(Math.round(.8 * f)), "  Phase 3 starts at step ~".concat(Math.round(.8 * f), " — you MUST start wrapping up"), "", "### Step Budget Rules", "- Total budget: ".concat(f, " steps. Used: ").concat(p, ". Remaining: ").concat(d, "."), "- EVERY action (click, scroll, navigate) costs 1 step.", "- Reading page content is FREE — it is included in each observation.", "- Prefer READING the current page over clicking into sub-pages.", "- When remaining <= 7: STOP exploring, return `done` immediately.", "- When remaining <= 3: return `done` with whatever you have. No exceptions.", "", "### Efficiency Rules", "- On search results pages: READ the snippets directly. Only click 2-3 best links.", "- On content pages: READ the visible text. Use `extract` only for specific elements.", "- Do NOT click every link. Do NOT scroll endlessly.", "- If you have enough info to answer the task, return `done` IMMEDIATELY.", "- Each click should have a clear purpose stated in your reasoning.", "", "### Anti-Loop Rules", "- Never repeat the same action 3x in a row.", "- If URL changed after a click, the click WORKED. Read the new page, do NOT re-click.", '- If last 3+ actions are same type: STOP and ask yourself "am I making progress?"', "- If stuck on a page after 2 attempts, navigate elsewhere or return `done`.", "- If the last 6 actions are mostly `extract`/`scroll` on the SAME URL", "  and no new page is visited, you MUST stop looping and return `done`", "  with a best-effort summary.", "- Do NOT keep extracting the same checkout/payment section repeatedly.", "", "### No-Result Recovery (MANDATORY)", "- If you cannot find new actionable elements or new facts after 2-3 attempts,", "  return `done` immediately instead of continuing to loop.", "- `done` message must explicitly include:", "  1) Completed items,", "  2) Not completed / uncertain items,", "  3) Current page URL,", "  4) Recommended next user action (if any).", "- For checkout-oriented tasks:", "  if already on checkout/payment page and no better next action exists,", "  return `done` with current checkout status.", "", "## Page Content", s || "(empty)", "", "Return the next action as JSON now."].join("\n")
    };
Readable ai.agent.think handlerbackground.js
tt.bp().on("ai.agent.think", function() {
      var e = i(u().mark((function e(t, r, n) {
        var o, a, i, s, c, l, p, f, d, h, v, m, g, y, b, w, x, k, _, O, j, S, A, P, E, T, D, C;
        return u().wrap((function(e) {
          for (;;) switch (e.prev = e.next) {
            case 0:
              return e.prev = 0, l = X_({}, t), p = l.content, f = p.task, d = void 0 === f ? "" : f, h = p.observation, v = void 0 === h ? {} : h, m = p.history, g = void 0 === m ? [] : m, y = p.visitedPages, b = void 0 === y ? [] : y, w = p.stepCount, x = void 0 === w ? 0 : w, k = p.maxSteps, _ = void 0 === k ? 50 : k, e.next = 5, bx();
            case 5:
              if (O = e.sent, j = (null == O ? void 0 : O.data) || {}, S = j.token, j.can_use) {
                e.next = 10;
                break
              }
              return r({
                action: "ask_user",
                params: {
                  question: "AI quota exhausted. Please try again later."
                },
                reasoning: "No AI credits available."
              }), e.abrupt("return");
            case 10:
              return A = v.pageContent || "", P = aO({
                task: d,
                url: v.url,
                title: v.title,
                signals: v.signals,
                metrics: v.metrics,
                pageContent: A,
                history: g,
                visitedPages: b,
                stepCount: x,
                maxSteps: _
              }), E = {
                contents: [{
                  parts: [{
                    text: P
                  }]
                }],
                generationConfig: {
                  temperature: .3
                }
              }, e.next = 15, gx(6, E, null, S);
            case 15:
              return T = e.sent, e.next = 18, oO(t, T);
            case 18:
              if (null == T || null === (o = T.candidates) || void 0 === o || null === (a = o[0]) || void 0 === a || null === (i = a.content) || void 0 === i || null === (s = i.parts) || void 0 === s || null === (c = s[0]) || void 0 === c || !c.text) {
                e.next = 22;
                break
              }
              D = T.candidates[0].content.parts[0].text;
              try {
                C = rO(D), r(C)
              } catch (e) {
                console.warn("[Agent Think] JSON parse failed:", D), r({
                  action: "ask_user",
                  params: {
                    question: "AI returned invalid response. Please help."
                  },
                  reasoning: "Failed to parse AI response."
                })
              }
              return e.abrupt("return");
            case 22:
              r({
                action: "ask_user",
                params: {
                  question: "AI returned empty response. Please help."
                },
                reasoning: "Empty AI response."
              }), e.next = 29;
              break;
            case 25:
              e.prev = 25, e.t0 = e.catch(0), console.error("[Agent Think] Error:", e.t0), n(e.t0);
            case 29:
            case "end":
              return e.stop()
          }
        }), e, null, [
          [0, 25]
        ])
      })));
      return function(t, r, n) {
        return e.apply(this, arguments)
      }
    }())
05EvidenceCODE COMPARE
The code that does this

Returned actions are dispatched back into the active page

What it actually does
function mw() {
          return mw = (0, l.Z)(d().mark((function e(t) {
            var r, n, o, i, a, c, s = arguments;
            return d().wrap((function(e) {
              for (;;) switch (e.prev = e.next) {
                case 0:
                  r = s.length > 1 && void 0 !== s[1] ? s[1] : {}, n = t.type, o = t.params, i = void 0 === o ? {} : o, a = r.shadowHosts, c = void 0 === a ? [] : a, e.t0 = n, e.next = e.t0 === By.CLICK ? 6 : e.t0 === By.TYPE ? 7 : e.t0 === By.SELECT ? 8 : e.t0 === By.SCROLL ? 9 : e.t0 === By.HOVER ? 10 : e.t0 === By.NAVIGATE ? 11 : e.t0 === By.BACK ? 12 : e.t0 === By.RELOAD ? 13 : e.t0 === By.WAIT ? 14 : e.t0 === By.EXTRACT ? 15 : 16;
                  break;
                case 6:
                  return e.abrupt("return", bw(c, i));
                case 7:
                  return e.abrupt("return", yw(c, i));
                case 8:
                  return e.abrupt("return", ww(c, i));
                case 9:
                  return e.abrupt("return", kw(c, i));
                case 10:
                  return e.abrupt("return", Pw(c, i));
                case 11:
                  return e.abrupt("return", Ew(i));
                case 12:
                  return e.abrupt("return", Sw());
                case 13:
                  return e.abrupt("return", jw());
                case 14:
                  return e.abrupt("return", Aw(c, i));
                case 15:
                  return e.abrupt("return", Zw(c, i));
                case 16:
                  return e.abrupt("return", {
                    success: !1,
                    error: "Unknown action: ".concat(n)
                  });
                case 17:
                case "end":
                  return e.stop()
              }
            }), e)
          }))), mw.apply(this, arguments)
        }
06EvidenceTHIRD PARTY LIST
Remote service used by the AI agent
  • pure.coupert.com

    Coupert API host used for the AI quota check and Gemini-format agent request.

Updated 17 September 2026gdhpobnkinppekiaabcndnleaejeddod