diff --git a/src/chrome/src/agent/adapters.js b/src/chrome/src/agent/adapters.js index 2f43cd6b4..f4e4c160a 100644 --- a/src/chrome/src/agent/adapters.js +++ b/src/chrome/src/agent/adapters.js @@ -16639,6 +16639,20 @@ const ADAPTERS = [ - Restaurant-scoped cart: you order from ONE restaurant at a time. Pick a "Restoran", then add menu items — you cannot combine items from two restaurants in a single order. - Availability gates: a restaurant may be "Kapalı" (closed — can't order now) and each has a "minimum sepet tutarı" (minimum order total) plus a delivery fee. Check the open status and the minimum before promising delivery. - "Yemeksepeti Mahalle" (grocery/market quick delivery) is a separate flow from restaurant food ordering — don't expect restaurant menus there.`, + }, + { + name: 'zomato', + category: 'general', + matches: (url) => /^https?:\/\/(?:www\.)?zomato\.com\//.test(url), + notes: ` +- As of 2026-08, choose "Delivery" for food ordering rather than "Dining Out" or "Nightlife". Set the exact delivery location first with the location field or "Detect current location", then re-read the address; restaurants, availability, distance, fees, and ETA depend on it, and an inaccurate or unreachable address can cause cancellation charges. +- Search with "Search for restaurant, cuisine or a dish" and use the visible filters for rating, cuisine, cost, and delivery time. Results marked "Promoted" are paid placements; do not treat their position as independent evidence of quality, speed, or best value. +- Confirm the exact restaurant and branch, location, hours, current delivery availability, menu, and review count. "Dining Ratings" and "Delivery Ratings" measure different experiences, so use the delivery rating for an order and do not silently substitute one for the other. +- A visible restaurant or menu is not proof that web ordering is available. Stop when the page says "Online ordering is only supported on the mobile app"; when it says "Currently closed for online ordering", do not try to add items or promise that the restaurant will accept the order now. +- Open each item and select every required size, quantity, variant, add-on, preparation choice, and dietary option before adding it. Preserve explicit allergy or exclusion requests, but do not infer ingredient safety from category labels alone; surface unresolved questions instead of submitting them as order notes without review. +- The menu subtotal is not the final price. Read delivery fee, surge charges, taxes, packaging and handling charges, discounts, and tip on the review page; treat the displayed approximate delivery time as an estimate that can change with demand, weather, restaurant preparation, and courier assignment. +- Adding items is not ordering. Re-read restaurant, delivery address, items, customizations, quantities, contact details, charges, tip, payment method, and total before submit. Cancellation and refund eligibility is limited by the displayed state and terms, and a problem report may require proof such as photos; do not promise either outcome. +- Require the user's explicit confirmation immediately before placing or paying for the order. Report success only from "Orders" with an order ID and explicit accepted/preparing/out-for-delivery/delivered status; a restaurant page, cart, payment redirect, or approximate ETA alone is incomplete.`, }, { name: 'foodpanda', diff --git a/src/firefox/src/agent/adapters.js b/src/firefox/src/agent/adapters.js index 2e2a4f19f..1133ce472 100644 --- a/src/firefox/src/agent/adapters.js +++ b/src/firefox/src/agent/adapters.js @@ -16637,6 +16637,20 @@ const ADAPTERS = [ - Restaurant-scoped cart: you order from ONE restaurant at a time. Pick a "Restoran", then add menu items — you cannot combine items from two restaurants in a single order. - Availability gates: a restaurant may be "Kapalı" (closed — can't order now) and each has a "minimum sepet tutarı" (minimum order total) plus a delivery fee. Check the open status and the minimum before promising delivery. - "Yemeksepeti Mahalle" (grocery/market quick delivery) is a separate flow from restaurant food ordering — don't expect restaurant menus there.`, + }, + { + name: 'zomato', + category: 'general', + matches: (url) => /^https?:\/\/(?:www\.)?zomato\.com\//.test(url), + notes: ` +- As of 2026-08, choose "Delivery" for food ordering rather than "Dining Out" or "Nightlife". Set the exact delivery location first with the location field or "Detect current location", then re-read the address; restaurants, availability, distance, fees, and ETA depend on it, and an inaccurate or unreachable address can cause cancellation charges. +- Search with "Search for restaurant, cuisine or a dish" and use the visible filters for rating, cuisine, cost, and delivery time. Results marked "Promoted" are paid placements; do not treat their position as independent evidence of quality, speed, or best value. +- Confirm the exact restaurant and branch, location, hours, current delivery availability, menu, and review count. "Dining Ratings" and "Delivery Ratings" measure different experiences, so use the delivery rating for an order and do not silently substitute one for the other. +- A visible restaurant or menu is not proof that web ordering is available. Stop when the page says "Online ordering is only supported on the mobile app"; when it says "Currently closed for online ordering", do not try to add items or promise that the restaurant will accept the order now. +- Open each item and select every required size, quantity, variant, add-on, preparation choice, and dietary option before adding it. Preserve explicit allergy or exclusion requests, but do not infer ingredient safety from category labels alone; surface unresolved questions instead of submitting them as order notes without review. +- The menu subtotal is not the final price. Read delivery fee, surge charges, taxes, packaging and handling charges, discounts, and tip on the review page; treat the displayed approximate delivery time as an estimate that can change with demand, weather, restaurant preparation, and courier assignment. +- Adding items is not ordering. Re-read restaurant, delivery address, items, customizations, quantities, contact details, charges, tip, payment method, and total before submit. Cancellation and refund eligibility is limited by the displayed state and terms, and a problem report may require proof such as photos; do not promise either outcome. +- Require the user's explicit confirmation immediately before placing or paying for the order. Report success only from "Orders" with an order ID and explicit accepted/preparing/out-for-delivery/delivered status; a restaurant page, cart, payment redirect, or approximate ETA alone is incomplete.`, }, { name: 'foodpanda', diff --git a/test/run.js b/test/run.js index f4f0159d7..2e1ce1b1a 100644 --- a/test/run.js +++ b/test/run.js @@ -3301,6 +3301,46 @@ test('matches yemeksepeti.com and includes food-delivery guidance', () => { assert.match(a?.notes || '', /Restoran/); }); +test('matches Zomato consumer pages and distinguishes restaurant delivery states', () => { + const trustedUrls = [ + 'https://zomato.com/', + 'https://www.zomato.com/bangalore/delivery?page=1', + 'https://www.zomato.com/bangalore/example-restaurant/order', + 'https://www.zomato.com/policies/terms-of-service/', + ]; + for (const url of trustedUrls) { + assert.equal(getActiveAdapter(url)?.name, 'zomato'); + assert.equal(getActiveAdapterFx(url)?.name, 'zomato'); + } + + const rejectedUrls = [ + 'https://business.zomato.com/', + 'https://blog.zomato.com/', + 'https://zomato.com.phishing.example/bangalore/delivery', + 'https://example.com/?next=https://www.zomato.com/bangalore/delivery', + ]; + for (const url of rejectedUrls) { + assert.notEqual(getActiveAdapter(url)?.name, 'zomato'); + assert.notEqual(getActiveAdapterFx(url)?.name, 'zomato'); + } + + const adapter = getActiveAdapter('https://www.zomato.com/bangalore/example-restaurant/order'); + const firefoxAdapter = getActiveAdapterFx('https://zomato.com/bangalore/delivery'); + assert.match(adapter?.notes || '', /2026-08/); + assert.match(adapter?.notes || '', /Delivery.*Dining Out.*Nightlife/s); + assert.match(adapter?.notes || '', /Detect current location.*address/s); + assert.match(adapter?.notes || '', /Promoted/); + assert.match(adapter?.notes || '', /Dining Ratings.*Delivery Ratings/s); + assert.match(adapter?.notes || '', /Online ordering is only supported on the mobile app.*Currently closed for online ordering/s); + assert.match(adapter?.notes || '', /restaurant, cuisine or a dish/); + assert.match(adapter?.notes || '', /delivery fee.*surge.*packaging.*handling/s); + assert.match(adapter?.notes || '', /approximate delivery time/i); + assert.match(adapter?.notes || '', /cancellation.*refund.*proof/s); + assert.match(adapter?.notes || '', /Orders.*order ID.*status/s); + assert.equal((adapter?.notes || '').trim().split('\n').filter((line) => line.startsWith('- ')).length, 8); + assert.equal(firefoxAdapter?.notes, adapter?.notes); +}); + test('matches foodpanda.pk and includes food-delivery guidance', () => { assert.equal(getActiveAdapter('https://www.foodpanda.pk/')?.name, 'foodpanda'); assert.equal(getActiveAdapter('https://foodpanda.pk/restaurants')?.name, 'foodpanda');