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
Approval banners broken for current Cursor UI: recent Cursor builds render shell tool approvals with a per-card layout (Run / Skip / Allowlist '<cmd>' plus an Auto-Run in Sandbox mode-dropdown trigger). The relay was matching the dropdown trigger via a generic Run text-substring match, so clicking Approve in the web client or Telegram opened a settings menu in Cursor instead of approving — users couldn't get unstuck without going to the IDE. Approval extraction now identifies the real action buttons by class (.ui-shell-tool-call__run-btn, .ui-shell-tool-call__skip-btn, .ui-shell-tool-call__allowlist-button) and skips any element with aria-haspopup (always a menu, never an action). Closes public#12.
Approval description shows the actual command (e.g. curl -sI --max-time 5 https://example.com | head -n 3) instead of a generic button label like "Auto-Run in Sandbox". Useful in Telegram, where you previously had no way to see what you were approving without switching to Cursor.
Stuck approval banner in multi-agent / multi-composer setups: approve/reject button discovery used document.querySelectorAll, so the relay matched buttons from other composers and sticky workbench chrome. Once a composer's approval was clicked the state never cleared because stale buttons in other composers kept surfacing. Approval extraction is now scoped to the active chat container and de-duped, mirroring how the rest of the extractor already worked. Originally credited as a community fix; see public#15 (thanks @gavinc).
Multi-window approvals now reach Telegram: with multiple Cursor windows open, only the active CDP target's approvals were processed for Telegram — non-active windows were polled by window-monitor into per-window snapshots, but those approvals never reached the bot. Each window's approvals now route to its own Telegram thread via doProcessWindow. Per-id content-hash dedupe keeps the active window from being banner-spammed twice when both the global and per-window paths fire.
Cross-window approval routing: when the user switches to a Cursor window that doesn't own the currently-selected agent (Cursor's global agent rail surfaces the same selected tab in every workbench DOM), the strict (windowId, tab) topic lookup returned nothing and the banner was silently dropped. Now falls through to a tab-title-only mapping lookup so the banner still surfaces in the canonical topic.
Two different agents with the same tab title in different projects no longer share one Telegram topic. The cross-window routing fix above wrongly conflated them when they shared a name (e.g. "Shell command approval process" in two projects). Topics are now identified by data-composer-id (Cursor's stable per-agent ID) instead of just tab title, so the same agent shown via Cursor's global rail in another window still routes to one topic, while two genuinely different agents that coincidentally share a title get their own topics auto-created.
Same agent seen via the new 'Cursor Agents' window no longer mints a duplicate topic. When a user opens an agent in the global Cursor Agents window after already having a topic for it (created earlier from the project's own workbench window), the relay would create a fresh topic because the composite <group>/<agent> tab title doesn't match the original tab title key. autoCreateTopic now consults composerId across all existing mappings before minting, and reuses the existing topic if found. /dedupe also groups by composerId so any historical duplicates can be collapsed in one command.
Telegram approval banner deletion no longer flickers: previously each transient empty pendingApprovals poll deleted the banner, and the next poll re-sent it — visible re-creation every ~10s while an approval was outstanding. Now defers deletion by 30 seconds (3× window-monitor cycle) so DOM transients are absorbed.
No more duplicate approval banners from concurrent paths: the global state-patch path and the per-window doProcessWindow path could both call the approval send routine for the same approval id within milliseconds of each other; both saw an empty tracker (because neither had called track() yet), both called sendMessage, and the user got two banners (confirmed live as msgIds 10352 + 10353 for one approval). Guarded with an inflight Set keyed on ${threadId}:${trackId}.
Telegram approval tracking per-id (was: single approval key per thread that edited the same message in place). New approvals now appear as fresh banners at the bottom of the topic instead of silently rewriting an old banner far up in the chat. Multiple concurrent approvals get separate banners.
Duplicate Telegram topics on WSL/SSH reconnect: Cursor adds connection-context suffixes to window titles when projects are opened over WSL/SSH/Codespaces (myproj vs myproj [WSL: ubuntu-24.04]). The relay treated those as different windows and created a parallel topic tree per (project × connection mode). Window titles are now normalized for matching, so reopening a project under a different connection mode reuses the existing topic.
Cursor Agents window now lists all agents, not just the active one. Recent Cursor builds host a dedicated 'Cursor Agents' workbench window with a glass-sidebar rail showing every agent across every project, grouped by project. The relay's legacy .agent-sidebar-cell selector only matched the visible active row in that window, so the web client showed exactly one switchable agent there. Now extracts every .glass-sidebar-agent-list-container li.ui-sidebar-menu-item > div.glass-sidebar-agent-menu-btn row and builds composite <group> / <agent> titles so two agents with the same name in different projects don't collide. switchTab understands both the composite and the agent-only forms. Legacy .agent-sidebar-cell extraction is preserved as a fallback for older Cursor builds. Closes public#13; see public#14 (thanks @gavinc).
Web client mobile multi-line input: on touch devices Enter now inserts a newline instead of sending the message — previously you couldn't compose multi-line prompts on a phone because mobile keyboards have no Shift+Enter. Tap the Send button to send. Cmd/Ctrl+Enter always sends regardless of platform (handy for hardware keyboards on tablets, familiar on desktop). Closes public#5.
iOS questionnaire scrolling: when the agent presents 3+ questions on iOS Safari, the questionnaire panel no longer overflows off-screen. Caps .questionnaire-bar at max-height: 55vh with #questionnaire-questions independently scrollable (with iOS momentum); header and Skip/Continue stay pinned. Closes public#8; see public#7 (thanks @hfutrell-gss).
Stopped log spam: the Skipping ... already belongs to ... warning fired every 10s for every (non-owning window, tab) pair when Cursor's global agent rail is in use. Now logged once per pair.
Persisted message tracker now self-heals legacy approval keys (approval, approval-approval-<TS>) from prior code revisions that accumulated thousands of stale entries on disk.
Added
/dedupe Telegram command — preview and merge duplicate topic mappings created across WSL/SSH variants of the same project. /dedupe shows a preview with KEEP / DROP markers; /dedupe yes deletes the orphan topics and removes their mappings. Companion to the title-normalization fix above for cleaning up topics from before this release.
/resync Telegram command — run inside a topic to rebind it to whatever Cursor has active right now. Useful when Cursor's global agent rail has surfaced an agent in a window other than the one that originally created the topic, and the topic's window prefix no longer matches where you actually work. Also renames the Telegram topic if the bot has Manage Topics permission. Refuses if the new target is already bound to a different topic (suggests /dedupe in that case).
/debug/state HTTP endpoint (auth-gated) returning current activeWindowId, agentStatus, pendingApprovals, chatTabs (with active flags), windows, and recent message types. Indispensable when debugging which window/tab a state field is coming from without parsing socket.io traffic.
[telegram-api] send thread=N msgId=M text=... log line on every outbound Telegram API call, for tracing duplicate-send issues across multiple bot instances or transports.
scripts/probe-tabs.ts — connects to a given Cursor window via CDP and dumps tab/composer markers (active flags, data-composer-id, data-composer-status, ARIA attributes) for debugging selector regressions.
scripts/probe-tg-thread.ts — sends a one-off probe message to a specific (chat, thread) to verify whether a Telegram topic exists outside of our tracker, used to discover orphan topics created by other bot instances.
Known issues
Two bot instances writing to the same Telegram chat will both succeed (extension-spawned server + dev server using the same token). Both will appear to work but messages get duplicated across each instance's mapping set. Stop one before running the other; see "Dev mode" below.