From 7cbaf0d537b237109ad8594d78f5497b62854ef3 Mon Sep 17 00:00:00 2001 From: alectimison-maker Date: Mon, 3 Aug 2026 01:40:53 +0800 Subject: [PATCH] agent: add adapter for Traveloka --- src/chrome/src/agent/adapters.js | 14 +++++++++++ src/firefox/src/agent/adapters.js | 14 +++++++++++ test/run.js | 42 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/chrome/src/agent/adapters.js b/src/chrome/src/agent/adapters.js index 2f43cd6b4..af7568d49 100644 --- a/src/chrome/src/agent/adapters.js +++ b/src/chrome/src/agent/adapters.js @@ -16292,6 +16292,20 @@ const ADAPTERS = [ - Treat QR "扫码", password, SMS "短信", payment/real-name verification, a blank flights page, and HTTP 432 as blocking challenges. Do not retry or enter unprovided identity details; ask the user. For HTTP 432, pause and return through www.ctrip.com's "机票" entry. - On the review page, remove only unrequested chargeable extras; preserve beneficial coupons and check their restrictions. Re-read travelers, contact, itinerary, terms, currency, and total; get explicit confirmation before "提交订单". - Report success only with "订单号" plus "预订成功" or the applicable "出票成功" status. Pending confirmation, a payment redirect, or an itinerary held for payment is incomplete.`, + }, + { + name: 'traveloka', + category: 'general', + matches: (url) => /^https?:\/\/(?:www\.)?traveloka\.com\//.test(url), + notes: ` +- Observed 2026-08: an automated visit can return HTTP 405 with "Human Verification", "Let's confirm you are human", and a "Begin" control instead of inventory. Stop and ask the user to complete it manually; do not retry, bypass it, or report that no trips or rooms exist. +- Choose the requested product before entering details: "Flights" and "Hotels" have different search, traveler/guest, payment, and fulfillment rules. Re-read locale, language, and currency, then set exact cities or properties, local dates, rooms or travelers, and one-way/round-trip intent. +- For a flight, verify airports, local departure/arrival dates and times, operating and marketing carrier for a code-share, stops, connection requirements, cabin, and fare rules. A round trip built from two one-way tickets can create separate bookings and separate payments with independent change, cancellation, and disruption handling. +- Compare the selected offer's supplier and final payable amount, not a headline or crossed-out fare. Read included baggage, taxes, fees, currency, refund/change conditions, and all ancillaries such as seats, meals, insurance, or extra baggage; keep unrequested paid additions out of the order. +- For a hotel, verify the exact property and room, occupancy, bed, meals, inclusions, taxes, supplier charges, check-in/out, and policy. "Pay Now" and "Pay at Hotel" differ in payment timing and possible hotel-side charges; read the cancellation deadline, refundability, and no-show consequence for the selected room. +- On order review, re-read every traveler/guest name, required document data, contact, itinerary or stay, supplier, add-ons, policy, payment timing, currency, and total. Never invent identity details, and require explicit confirmation before creating the booking or making payment. +- After payment, obtain the voucher, e-ticket, or booking confirmation and verify the supplier's confirmation state. Use that booking's own policy and Help flow for a refund or reschedule; eligibility, fees, method, timing, and any Traveloka balance depend on the fare/room and supplier. +- Report success only from "Bookings" with a booking ID and explicit confirmed/ticketed status for every product or leg. Search results, a selected offer, payment redirect, pending supplier confirmation, or an unconfirmed itinerary are incomplete.`, }, { name: 'dianping', diff --git a/src/firefox/src/agent/adapters.js b/src/firefox/src/agent/adapters.js index 2e2a4f19f..1f7784525 100644 --- a/src/firefox/src/agent/adapters.js +++ b/src/firefox/src/agent/adapters.js @@ -16290,6 +16290,20 @@ const ADAPTERS = [ - Treat QR "扫码", password, SMS "短信", payment/real-name verification, a blank flights page, and HTTP 432 as blocking challenges. Do not retry or enter unprovided identity details; ask the user. For HTTP 432, pause and return through www.ctrip.com's "机票" entry. - On the review page, remove only unrequested chargeable extras; preserve beneficial coupons and check their restrictions. Re-read travelers, contact, itinerary, terms, currency, and total; get explicit confirmation before "提交订单". - Report success only with "订单号" plus "预订成功" or the applicable "出票成功" status. Pending confirmation, a payment redirect, or an itinerary held for payment is incomplete.`, + }, + { + name: 'traveloka', + category: 'general', + matches: (url) => /^https?:\/\/(?:www\.)?traveloka\.com\//.test(url), + notes: ` +- Observed 2026-08: an automated visit can return HTTP 405 with "Human Verification", "Let's confirm you are human", and a "Begin" control instead of inventory. Stop and ask the user to complete it manually; do not retry, bypass it, or report that no trips or rooms exist. +- Choose the requested product before entering details: "Flights" and "Hotels" have different search, traveler/guest, payment, and fulfillment rules. Re-read locale, language, and currency, then set exact cities or properties, local dates, rooms or travelers, and one-way/round-trip intent. +- For a flight, verify airports, local departure/arrival dates and times, operating and marketing carrier for a code-share, stops, connection requirements, cabin, and fare rules. A round trip built from two one-way tickets can create separate bookings and separate payments with independent change, cancellation, and disruption handling. +- Compare the selected offer's supplier and final payable amount, not a headline or crossed-out fare. Read included baggage, taxes, fees, currency, refund/change conditions, and all ancillaries such as seats, meals, insurance, or extra baggage; keep unrequested paid additions out of the order. +- For a hotel, verify the exact property and room, occupancy, bed, meals, inclusions, taxes, supplier charges, check-in/out, and policy. "Pay Now" and "Pay at Hotel" differ in payment timing and possible hotel-side charges; read the cancellation deadline, refundability, and no-show consequence for the selected room. +- On order review, re-read every traveler/guest name, required document data, contact, itinerary or stay, supplier, add-ons, policy, payment timing, currency, and total. Never invent identity details, and require explicit confirmation before creating the booking or making payment. +- After payment, obtain the voucher, e-ticket, or booking confirmation and verify the supplier's confirmation state. Use that booking's own policy and Help flow for a refund or reschedule; eligibility, fees, method, timing, and any Traveloka balance depend on the fare/room and supplier. +- Report success only from "Bookings" with a booking ID and explicit confirmed/ticketed status for every product or leg. Search results, a selected offer, payment redirect, pending supplier confirmation, or an unconfirmed itinerary are incomplete.`, }, { name: 'dianping', diff --git a/test/run.js b/test/run.js index f4f0159d7..93c3281cd 100644 --- a/test/run.js +++ b/test/run.js @@ -2866,6 +2866,48 @@ test('matches Ctrip travel surfaces and includes Chinese booking guidance', () = assert.equal(firefoxAdapter?.notes, adapter?.notes); }); +test('matches Traveloka consumer pages and distinguishes booking and fulfillment states', () => { + const trustedUrls = [ + 'https://traveloka.com/en-id/', + 'https://www.traveloka.com/en-id/flight', + 'https://www.traveloka.com/en-id/hotel', + 'https://www.traveloka.com/en-id/help', + ]; + for (const url of trustedUrls) { + assert.equal(getActiveAdapter(url)?.name, 'traveloka'); + assert.equal(getActiveAdapterFx(url)?.name, 'traveloka'); + } + + const rejectedUrls = [ + 'https://partner.traveloka.com/', + 'https://careers.traveloka.com/', + 'https://affiliate.traveloka.com/', + 'https://traveloka.com.phishing.example/en-id/flight', + 'https://example.com/?next=https://www.traveloka.com/en-id/hotel', + ]; + for (const url of rejectedUrls) { + assert.notEqual(getActiveAdapter(url)?.name, 'traveloka'); + assert.notEqual(getActiveAdapterFx(url)?.name, 'traveloka'); + } + + const adapter = getActiveAdapter('https://www.traveloka.com/en-id/flight'); + const firefoxAdapter = getActiveAdapterFx('https://traveloka.com/en-id/hotel'); + assert.match(adapter?.notes || '', /2026-08/); + assert.match(adapter?.notes || '', /Human Verification.*Let's confirm you are human.*Begin/s); + assert.match(adapter?.notes || '', /Flights.*Hotels/s); + assert.match(adapter?.notes || '', /code-share.*separate bookings.*separate payments/s); + assert.match(adapter?.notes || '', /baggage.*taxes.*fees/s); + assert.match(adapter?.notes || '', /Pay Now.*Pay at Hotel/s); + assert.match(adapter?.notes || '', /cancellation.*no-show/s); + assert.match(adapter?.notes || '', /ancillaries/); + assert.match(adapter?.notes || '', /explicit confirmation/); + assert.match(adapter?.notes || '', /voucher.*e-ticket.*booking confirmation/s); + assert.match(adapter?.notes || '', /refund.*reschedule/s); + assert.match(adapter?.notes || '', /Bookings.*booking ID.*status/s); + assert.equal((adapter?.notes || '').trim().split('\n').filter((line) => line.startsWith('- ')).length, 8); + assert.equal(firefoxAdapter?.notes, adapter?.notes); +}); + test('matches Qunar travel surfaces and distinguishes search, supplier, order, and ticket states', () => { const trustedUrls = [ 'https://qunar.com/', 'https://www.qunar.com/', 'https://flight.qunar.com/',