Fixed
- 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-generateduseId(_r_ld_), and the row'stextContentincludes 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) sentset_model "label"instead ofset_model "label::GPT-5.5 High". Fixed by (a) cascading the trigger selector through new + legacy classes, (b) resolving the menu viadata-testid→ trigger'saria-controls→ first open[role="menu"], (c) filtering out nested Edit buttons when collecting rows, (d) stripping descendant<button>text from the label, (e) detecting ReactuseIdids and round-tripping through synthesizedlabel::<text>ids, (f) substring fallback (length-gated to avoidGPT-5matchingGPT-5.5) when the live row has extra spans beyond the captured label. One sharedMODEL_ITEM_HELPERS_JS(collectModelItems+pickModelById) is now the single source of truth for bothget_model_optionsandset_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 thelabel::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-awareparseCallbackData()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-buttonetc.) 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 currentbutton.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 (triggeraria-controls, menu element, sample of menu items). Useful when Cursor reskins the picker again.