Commit Graph

9 Commits

Author SHA1 Message Date
Jinwoo Hong da98a93089
Fix cursor-agent notification spam on shell tool hooks (#4835) 2026-06-07 15:24:15 -07:00
Neil d1b9392b49
perf: cap generated hook posts (#4166)
Generated agent hook POSTs now use short curl and PowerShell request timeouts so best-effort status hooks cannot hold agent subprocesses open on a stalled local listener.
2026-05-31 06:40:02 -07:00
Neil 675b1b0cb6
Fix Windows agent hook Unicode prompts (#2100) 2026-05-16 13:32:23 -07:00
Neil 6a960a855e
Add Cursor Agent hook installer coverage 2026-05-15 21:31:43 -07:00
Brennan Benson 2cadcbc4e1
Support agent status hooks over SSH (#1865)
Co-authored-by: Orca <help@stably.ai>
2026-05-14 16:06:23 -07:00
Brennan Benson 727226fed9
fix(agent-hooks): write managed hook script to shared ~/.orca path (#1546)
Co-authored-by: Orca <help@stably.ai>
2026-05-14 11:57:48 -07:00
Brennan Benson c375f403ef
fix(agent-hooks): silence exit-127 spam from stale managed hook entries (#1536)
Co-authored-by: Orca <help@stably.ai>
2026-05-07 13:05:30 -07:00
Brennan Benson 4279fc8075
feat(agent-hooks): on-disk endpoint discovery for surviving PTYs (v2) (#1196)
Co-authored-by: Orca <help@stably.ai>
2026-04-27 21:49:18 -07:00
Neil ae9ddeab5c
feat(cursor): first-class Cursor CLI agent status via hooks.json (#1108)
* feat(cursor): first-class Cursor CLI agent status via ~/.cursor/hooks.json

Give cursor-agent the same hook-driven status pipeline Claude/Codex/Gemini/
OpenCode already use so working/done/permission transitions show the proper
sidebar spinner instead of falling back to title heuristics — cursor-agent
only sets its OSC title to the literal string "Cursor Agent" during a turn,
so title-based detection cannot see working→done transitions on its own.

- New CursorHookService installs a managed shell script and registers it
  under ~/.cursor/hooks.json for beforeSubmitPrompt, preToolUse, postToolUse,
  postToolUseFailure, beforeShellExecution, beforeMCPExecution, stop, and
  afterAgentResponse (the subset that marks turn boundaries and surfaces
  in-flight tool context).
- AgentHookServer gains a /hook/cursor route and a normalizeCursorEvent
  mapping the camelCase cursor events to working/done/waiting, with tool
  previews for preToolUse/shell/MCP and lastAssistantMessage from
  afterAgentResponse. stop with status != "completed" surfaces as
  interrupted (matches Claude's is_interrupt behavior).
- cursorHookService joins the startup install loop alongside Claude/Codex/
  Gemini, and the IPC status handler is exposed via preload.
- 'cursor' added to WellKnownAgentType and to the renderer's
  WELL_KNOWN_LABELS so the dashboard prints "Cursor" rather than the raw
  'cursor' id.

Verified end-to-end against a real cursor-agent 2026.04.17-787b533 binary:
a mock hook receiver pointed at by ~/.cursor/hooks.json observes
beforeSubmitPrompt → stop for a live turn.

Co-authored-by: Orca <help@stably.ai>

* feat(cursor): wire hook events into sidebar spinner + unread pipeline

The initial hook wiring landed behind AGENT_DASHBOARD_ENABLED, which is
still false. That meant cursor-agent panes lit up no spinner and no
unread indicator — cursor's native OSC title stays literally "Cursor
Agent" across a turn, so title-based detection cannot transition.

Plumb cursor's hook stream into the existing, shipped title-tracker
pipeline (the one Claude/Codex/Pi drive working/idle/unread off) by
synthesizing OSC title sequences in the main-process hook listener:

  - `working`  → `\x1b]0;⠋ Cursor Agent\x07`   (braille prefix → working)
  - `waiting`  → `\x1b]0;Cursor - action required\x07\x07`
  - `done`     → `\x1b]0;Cursor ready\x07\x07`

The two trailing BELs on done/waiting are load-bearing: the unread badge
keys off BEL (0x07 outside any OSC), and cursor-agent emits none on its
own. The first BEL is consumed as the OSC terminator; the second fires
the bell detector.

Also treats the bare native "Cursor Agent" title as a no-op in
`detectAgentStatusFromTitle` so cursor's own per-turn re-emissions cannot
stomp our synthesized working state back to idle. `isClaudeAgent`
excludes cursor-bearing braille titles so the Claude prompt-cache timer
doesn't fire for cursor panes.

Scope: the hook server + cursor install run unconditionally now, but
Claude/Codex/Gemini installs stay gated behind AGENT_DASHBOARD_ENABLED,
so only cursor events flow through the pipeline. No dashboard surface
is turned on.

Verified end-to-end in Electron (dev build): launched cursor-agent via
the Cursor menu item, submitted three prompts (including a tool-use
turn reading package.json). Observed tab title flip to "Cursor ready"
on done, and the tab + worktree both transitioned to unread
(unreadTerminalTabs[tabId]=true, worktree.isUnread=true). Parallel
Claude Code pane untouched.

Co-authored-by: Orca <help@stably.ai>

* fix(cursor): animate spinner frames + filter bare native title so the spinner doesn't go solid mid-turn

cursor-agent re-emits its bare "Cursor Agent" OSC title on every internal
redraw, which was stomping the single synthesized "⠋ Cursor Agent" frame
in runtimePaneTitlesByTabId within milliseconds and flipping the sidebar
dot back to solid. Two-part fix:

- Main: drive an 80ms Pi-style braille spinner from the cursor hook
  channel, keyed by paneKey, torn down on pty exit via a new
  registerPaneKeyTeardownListener hook in ipc/pty.
- Renderer: drop bare "Cursor Agent" titles in pty-transport so cursor's
  native re-emissions cannot overwrite the synthesized working/idle
  titles.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-04-25 23:14:31 -07:00