Skip to content

Add Vivid Seats discovery, page-parity listings and exact ticket preparation - #52

Open
yummybomb wants to merge 7 commits into
mainfrom
hypeship/vividseats-events
Open

yummybomb wants to merge 7 commits into
mainfrom
hypeship/vividseats-events

Conversation

@yummybomb

@yummybomb yummybomb commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Public tools for the Vivid Seats marketplace. Listings now follow the event page itself, like the Google Flights tools (#131): a read-only tool reports what the page's listings panel shows, and separate tools press the page's own controls once each and confirm what the page then shows.

tool kind what it does
search_events page Event search by text, place, dates, category, performer, venue or index-price ceiling, through the site's own search API. On the bot-protected fallback page it keeps a requested category when every event that page holds is in it, instead of refusing.
get_event page Exact public event details from the event page's server-rendered record.
get_venue page Exact venue address, coordinates, time zone and catalog capacity.
open_event_listings cdp New. Opens an event page at the page's own ?quantity=N address (so no 'How many tickets?' prompt) and returns its listings panel in the same call; gets past the panel's bot-protection error the way a person does (mouse input, then a reload). cdp because it navigates and returns structured results from the destination (spec §2.1).
list_listings page New. Reads the event page's listings panel as shown: rows in page order (listing id, section, row, ticket count, price per ticket, Fees Incl., deal score, badges), the panel's own "N listings" count, and the page's quantity, price, deal score, perks, zone and sort settings. Never clicks, hovers, scrolls, filters or sorts.
set_ticket_quantity page New. Answers the page's "How many tickets?" prompt, or chooses in its quantity menu.
set_listing_sort cdp New. Chooses Lowest/Highest Price, Deal Score, Row Number or Section Number in the page's sort menu.
set_listing_filters cdp New. Sets zones, min/max price, deal score and perks through the page's controls, or presses Clear.
show_more_listings page New. Scrolls the listings panel to its end once so the page loads its next rows.
prepare_ticket_selection page Re-reads one exact listing and returns a quote and ordinary event-page handoff; never selects, holds or buys.

get_ticket_offers is removed: it filtered, sorted and grouped the listings response itself, which the authoring guide's "keep parity with the website" rules rule out when the site offers those controls.

Every listings result carries available_actions (whether the page offers each action now, whether the tool is registered, and an input valid for this exact state) and next_steps, as in #131. list_listings returns the page's price_group_id and the selected quantity, so its first row's action is a ready prepare_ticket_selection input.

How the page works (observed live)

  • The quantity, price, perks and sort menus open while the mouse is over their button and close when it leaves; the tools send the mouse events a person's mouse sends. Deal Score buttons answer a full pointer press, price boxes apply on Enter.
  • The page keeps its URL query in step with every control (quantity, minPrice, maxPrice, dealScore, selectedGroups, sort, perk flags such as superSeller=true), so the tools read state from the query and confirm each change by it plus a re-rendered panel. Clear also resets the quantity.
  • Rows carry data-listingid, data-price and friends; the panel loads about 20 rows and 16 more per scroll to its end marker.
  • Until the page has had user input, Vivid Seats' bot protection answers the panel's listings request with a 404 and the panel shows "Sorry, there was an error" (tickets-response-error) without asking again. list_listings reports that as not_ready with the recovery (mouse input, then reload the page); actions refuse without pressing.

CDP exception

set_listing_sort and set_listing_filters are kind: "cdp" with their whole body run in the page through js(). Every sort, zone or Clear change makes the site's analytics navigate cross-origin child iframes (Facebook pixel, DoubleClick) while the page only updates its URL in place, and the page runtime then drops a page tool's result with "Cannot return tool results after a cross-origin navigation" although the change was made (same precedent as bluebottlecoffee.com/add_to_cart). Quantity changes and loading more rows fire no such frame, so those tools stay page tools.

Safety / limitations

Checkout transitions may hold inventory. The runtime does not enforce a trusted exact-action approval path, so hold, checkout, purchase, transfer and seller execution tools are deliberately absent. Restrictions and other site text are treated as data, never instructions. The search index can lag inventory; the radius is approximate and the result window is capped at 1000.

Status and evidence

All ten tools are verified as of 2026-10-29. The QA below was run by the same Hypeship conversation that wrote the listings tools; the PR owner accepted it as the tools' QA, and the registry notes say so.

  • Author live check on 2026-10-29 in plain Kernel browsers through registered WebMCP invocations, Knicks at Madison Square Garden: list_listings returned wrong_page on the home page, not_ready on the panel's error, and after input the prompt with 1,037 listings; set_ticket_quantity 2 answered the prompt (713 listings, cheapest $178 with Fees Incl.) and a repeat was unchanged; set_listing_sort deal_score then lowest_price; set_listing_filters with 400 Level, max $250, deal score 8+ and Super Seller (10 listings), then min $180 with zones, perks and deal score reset (94), an unknown zone refused before any press, and Clear; show_more_listings loaded 16 more rows; prepare_ticket_selection returned prepared from the input in available_actions.
  • Author live check of all ten tools on ccdd9b6, 2026-10-29, one fresh plain browser per event, each call checked against the page read separately with Playwright (49 checks per event):
    • Events: Knicks vs Nets at Madison Square Garden (700 listings for 2, 14 zones) and Hamilton at the Richard Rodgers Theatre (27 listings, 3 zones). 48 of 49 checks passed on each.
    • search_events succeeded on its first call before any input (performer-page fallback); nonsense query no_results, reversed dates invalid_input. With dates, the site's own performer page also returned two Date TBD playoff games, marked date_tbd.
    • get_event and get_venue matched the search; unknown ids not_found.
    • list_listings and set_ticket_quantity returned wrong_page on the home page without pressing anything.
    • open_event_listings (quantity 2): the tab moved to ?quantity=2, and the returned rows and "N listings" count matched the page; without a quantity the page reused 2.
    • set_ticket_quantity 2 to 4 to 2, set_listing_sort deal score, highest price, section number and back to lowest price, set_listing_filters (zone + max price + deal score 7+, then min price + Super Seller, an unknown zone, Clear, Clear with another filter) and show_more_listings: every changed view matched the page's own rows, count and URL; repeats were unchanged without a press.
    • Hamilton's page has no Perks menu; a perks request returned unavailable and pressed nothing.
    • prepare_ticket_selection from the input in available_actions returned prepared at the row's price ($712 x 2 = $1,424, no reservation); a changed price stale_selection, an unknown listing listing_unavailable, an extra confirmed field invalid_input.
    • An earlier run of the same check on 0e09dc0 found the zone buttons had lost role="checkbox" and are rendered twice: view.zones came back empty, and one zone was pressed twice, undoing it while the tool reported it applied. Fixed in ccdd9b6; the simulated page now matches that markup, and the old selector fails its tests.
    • In that earlier run, the listings panel showed its bot-protection error again after a sort change mid-session; set_listing_sort returned not_ready, and open_event_listings recovered the view with the sort kept.
  • Bench, 2026-10-29, vividseats-cheapest-pair, plain browsers, low thinking, 5 pairs per run, treatment pinned to the source named:
    • 1aeda15 (page tools, no open_event_listings): Haiku 5.5 median 13 turns, 1:58 → 1:04 vs control; GPT-6 Luna 8 turns, treatment 4/5 vs control 1/5. Transcripts showed the turns going to a first search_events refused for its category, opening the event page and re-listing tools, clicking the quantity prompt by hand, and reloading after the bot-protection error.
    • bd07d12 (open_event_listings returning not_ready on the error): Haiku 5.5 median 7 turns, 0:47 → 0:35; GPT-6 Luna median 7 turns, 0:53 → 0:23, treatment 3/5 vs control 2/5, both misses stopping at not_ready.
    • 0e09dc0: GPT-6 Luna 5/5 vs control 1/5, every treatment trial exactly 4 turns (search_events, open_event_listings) answering $178, the cheapest pair then; median 0:28. Haiku 5.5 5/5 vs 4/5, all at $178 where control quoted the deal-score preview ($229) in 2; median 4 turns, 1:23 → 0:24 over both-correct pairs. Its two 9-turn trials opened the event page themselves instead of calling open_event_listings. open_event_listings took 4-17 s.
    • Live, 12 fresh plain browsers calling open_event_listings straight from the home page all returned listings (median about 12 s, at most 31 s; most first loads succeeded after the input sent before navigating).
    • With the earlier get_ticket_offers (API, local filtering) the same intent took Haiku 5 and Luna 4 turns.
  • Simulated-page tests (tests/vividseats-listings.test.mjs) cover reading without changing the page, opening an event for a quantity (and an unknown event, the error panel and bad input), a remembered quantity, the prompt and menu paths, sort, every filter control, Clear, loading more rows, the bot-protection error, wrong pages, invalid input and available actions; seven mutants (bare click() on Deal Score, an unclosed menu, reversed rows, no sort press, a wrong show-more offset, no unknown-zone refusal, an ignored error panel) each fail them.
  • Registry validator, node --test "tests/*.test.mjs" (4,769) and the bench tests pass.

Follow-up

  • bench/tasks/vividseats.com/domain.json lists open_event_listings and the four action tools as page actions, so bench treatment gets them.

Only vividseats.com domain files, its shared lib snippets, its tests, its bench domain settings and this namespace's sorted registry entries are changed. Do not auto-merge.

@yummybomb
yummybomb force-pushed the hypeship/vividseats-events branch from 8a2d46b to 7ba0925 Compare September 30, 2026 02:11
@yummybomb yummybomb changed the title Add vividseats.com search_events and get_ticket_offers Add Vivid Seats discovery and exact ticket preparation Sep 30, 2026
@yummybomb
yummybomb marked this pull request as ready for review September 30, 2026 14:52
@yummybomb

Copy link
Copy Markdown
Contributor Author

Pushed c03b671 and 2c7201c. For the first two to three minutes of a browser session, Vivid Seats answers its JSON search and listings requests with a generic 404. The event page's own requests get the same 404 and its listings panel shows "Sorry, there was an error". Some sessions never get listings.

  • search_events: when the JSON search gives that 404, a text search reads the site's server-rendered search page instead. A performer match redirects to the performer page, which also applies the date range. Warnings name the page and any filter it did not apply.
  • get_ticket_offers: repeats its read for up to 20 seconds while it gets that 404, then says to call again. The message also says the page's preview rows and "from" price are not filtered by quantity.
  • Both tools stay draft.

Bench (5 pairs). Correct counts are shape-only. The real cheapest all-in price for 2 tickets at that time was $184:

  • Haiku 5.5 low: 4/5 → 5/5 correct, 1:19 → 0:42 (1.9×). Reported $184 in 0/5 pairs without tools (it answered $230/$235 from the page's unfiltered preview) and in 5/5 with tools.
  • GPT-6 Luna low: 3/5 → 2/5 correct, 0:29 → 0:17 (1.7×). Reported $184 in 1/5 without tools and 2/5 with tools. With tools, Luna answered null when the site served no listings, instead of an unfiltered price.

@yummybomb

Copy link
Copy Markdown
Contributor Author

Pushed d83e223: get_ticket_offers now waits up to 45s per call, under the 60s invocation limit. If listings still haven't loaded, it returns status: "not_ready" with waited_seconds and retry_after_seconds instead of an error. A wait the caller cancels now reports canceled, which the output schema allows.

Bench (5 pairs). The real cheapest all-in price for 2 tickets was $186 during this run:

  • GPT-6 Luna low: 3/5 → 4/5 correct; reported $186 in 0/5 pairs without tools ($183 or null) and 4/5 with tools. Paired 1:11 → 0:54.
  • Haiku 5.5 low: 5/5 → 5/5 correct; reported $186 in 3/5 without tools and 4/5 with tools. Paired 1:04 → 1:14. One browser never got listings, and Haiku kept retrying for 8 minutes before answering from the preview ($230).

@yummybomb
yummybomb force-pushed the hypeship/vividseats-events branch from 3f49947 to 164fbd1 Compare October 29, 2026 17:24
…ontrols

get_ticket_offers filtered, sorted and grouped the listings response itself. Replace it
with list_listings, which reads what the event page's listings panel shows, and
set_ticket_quantity, set_listing_sort, set_listing_filters and show_more_listings,
which press the page's own quantity prompt and menu, sort menu, zone, price, deal
score and perks controls, Clear, and the panel's scroll-to-load once each and confirm
what the page then shows.
@yummybomb yummybomb changed the title Add Vivid Seats discovery and exact ticket preparation Add Vivid Seats discovery, page-parity listings and exact ticket preparation Oct 29, 2026
Add open_event_listings, which opens the event page at the page's own ?quantity=N
address and returns its listings panel, so an agent no longer navigates, lists the
tools again and answers the 'How many tickets?' prompt as separate steps. Read the
selected quantity from the page's quantity control, since the page restores a
remembered quantity without putting it in its URL. search_events keeps a requested
category on the search-page fallback when every event that page holds is in it.
…ings call

Point the bot-protection recovery at open_event_listings, which reloads and reads the
page in one call, and tell callers after it navigates that the new page's tools have
new tool_refs.
Vivid Seats' bot protection refuses the listings panel's request until the browser has
had user input. open_event_listings now moves the mouse through the browser's input
pipeline before and after opening the page and, while the panel shows its error, moves
it again, waits for the protection's cookie to settle, and reloads within its time
budget, as a person does.
The page's zone buttons lost role="checkbox" and are rendered twice, so
view.zones came back empty and set_listing_filters could press a zone twice,
undoing it. Match them by test id alone and keep one per zone. Also correct
open_event_listings' quantity description: without a quantity the page
reuses one picked earlier in the session.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant