You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Model picker broken on Cursor 3.5+ (web client and Telegram): the model dropdown was reskinned — trigger renamed (.composer-unified-dropdown-model → .ui-model-picker__trigger), data-testid="model-picker-menu" removed, each row now contains an inner Edit button with a React-generated useId (_r_ld_), and the row's textContent includes that button's "Edit" text. Combined, this showed "Loading models…" → "No models available" in the web client, made selection silently no-op, and (with the bonus Telegram bug below) sent set_model "label" instead of set_model "label::GPT-5.5 High". Fixed by (a) cascading the trigger selector through new + legacy classes, (b) resolving the menu via data-testid → trigger's aria-controls → first open [role="menu"], (c) filtering out nested Edit buttons when collecting rows, (d) stripping descendant <button> text from the label, (e) detecting React useId ids and round-tripping through synthesized label::<text> ids, (f) substring fallback (length-gated to avoid GPT-5 matching GPT-5.5) when the live row has extra spans beyond the captured label. One shared MODEL_ITEM_HELPERS_JS (collectModelItems + pickModelById) is now the single source of truth for both get_model_options and set_model/set_plan_model, with a round-trip unit test that fails if the read and write sides ever drift. Closes public#22 and likely public#9; thanks @michaelandrewgamble for the CDP probe data.
Telegram callback parsing mangled payloads containing colons: the callback handler did data.split(':') which shredded model ids with the label:: prefix, broke the View Plan button (vpl:<short-id>:<hash> → "Plan not found in current state"), and could misroute any future action with a multi-colon payload. Replaced with action-aware parseCallbackData() that takes the entire rest as the id for no-hash actions (mode, model), splits hashed actions (apr/rej/all/run/skp/alw/bld/vpl/dif) on the last colon, and routes questionnaire callbacks (qan/qsk/qco) as hash-only. Unit-pinned.
Telegram Run/Skip/Allowlist buttons silently no-op'd or said "Action expired" on recent Cursor builds: the stable-selector table (ACTION_SELECTORS) still pointed at pre-0.1.45 button classes (.composer-tool-call-status-row .anysphere-button.composer-run-button etc.) that don't match the current per-card UI — so tapping Run threw "Element not found" in a Telegram tooltip easy to miss on mobile, while the web client worked. Server restarts and two-bot-instance setups also evicted the per-card hash from the in-memory tracker and bailed as "Action expired" even while the approval was still pending. Stable selectors refreshed to current button.ui-shell-tool-call__run-btn / __skip-btn / __allowlist-button (legacy .composer-* kept as a second fallback). Callback resolution tries the per-card hash first (preserves multi-card targeting), falls through to the stable selector when the hash is missing. Error message replaced with "Action no longer pending (already actioned, cleared, or restart)". Closes public#21, public#25.
Added
scripts/probe-model-picker.ts — one-shot CDP probe that clicks the model trigger and dumps the resulting menu structure (trigger aria-controls, menu element, sample of menu items). Useful when Cursor reskins the picker again.