Skip to content

fix(adopt): detect Cursor agent sessions and fix adopt-session uptime/liveness reporting#99

Merged
deepcoldy merged 4 commits into
deepcoldy:masterfrom
LucasIcarus:feat/adopt-cursor-agent
Jun 4, 2026
Merged

fix(adopt): detect Cursor agent sessions and fix adopt-session uptime/liveness reporting#99
deepcoldy merged 4 commits into
deepcoldy:masterfrom
LucasIcarus:feat/adopt-cursor-agent

Conversation

@LucasIcarus
Copy link
Copy Markdown
Contributor

@LucasIcarus LucasIcarus commented Jun 3, 2026

Summary

Make /adopt work end-to-end for Cursor Agent sessions, and fix two adopt-session bugs surfaced along the way.

Cursor detection

  • Recognize cursor-agent processes as Cursor sessions during /adopt discovery.
  • Treat the generic agent binary as Cursor only when a Cursor bot is filtering candidates, so broad scans don't adopt unrelated agent processes.
  • Inspect argv (not just /proc/<pid>/comm) when walking a pane's process tree — Cursor Agent can report comm as MainThread while the real executable lives in argv[0]. Detection now lives in the shared session-discovery module and is reused by the tmux, zellij, and Herdr discovery paths.

Adopt-session uptime & liveness

  • Populate startedAt for every CLI, not just Claude: add readProcessStartTime (Linux /proc/<pid>/stat starttime + /proc/stat btime, ps -o lstart= fallback) so the adopt picker stops rendering "unknown" uptime for cursor/codex/coco/gemini.
  • Persist the bridge worker pid in forkAdoptWorker, mirroring forkWorker. Adopt sessions kept pid=null, so botmux list/killStalePids judged them by "process dead AND no bmx-* tmux" and pruned them right after /adopt. The stored pid is botmux's bridge worker, never the user's CLI.

botmux list output

  • Don't report an unconfirmed /adopt scratch placeholder as a crashed session. cmdList now distinguishes sessions that ever ran a real CLI (cliId / lastCliInput / adoptedFrom, matching isRelayableRealSession) from never-activated scratches: scratches are closed silently, genuinely dead sessions keep the original warning.

Validation

  • pnpm vitest run test/session-discovery.test.ts test/zellij-cli-detection.test.ts test/herdr-session-discovery.test.ts test/session-store.test.ts
  • pnpm build

Notes

  • No lockfile changes.

@LucasIcarus LucasIcarus requested a review from deepcoldy as a code owner June 3, 2026 07:50
@LucasIcarus
Copy link
Copy Markdown
Contributor Author

Note on the MainThread fallback:

Cursor Agent's agent launcher is a bash wrapper around the bundled Cursor Node runtime. The wrapper uses exec -a "$0" node --use-system-ca index.js ..., so argv[0] still points at agent/cursor-agent, but the Linux comm value for the resulting process is consistently MainThread.

I verified this with fresh launches for agent, agent --model gpt-5.5-extra-high, and agent --model claude-opus-4-8-xhigh; all reported comm=MainThread while cmdline contained /cursor-agent/versions/.../index.js. Running the bundled Cursor node directly also reports MainThread, while the system Node reports node, so this appears to be a Cursor runtime quirk rather than a transient session state.

The implementation treats MainThread only as a signal to inspect argv; it does not map MainThread to Cursor by itself. The generic agent basename is only considered Cursor when discovery is already filtered to cursor, which keeps unfiltered adoption from matching unrelated agent tools.

@LucasIcarus LucasIcarus force-pushed the feat/adopt-cursor-agent branch from a18f98a to e472465 Compare June 3, 2026 08:49
LucasIcarus and others added 2 commits June 3, 2026 17:00
Allow /adopt discovery to recognize cursor-agent processes as Cursor sessions and treat the generic agent binary as Cursor only when a Cursor bot is filtering candidates. This keeps broad scans from adopting unrelated agent processes while enabling Cursor-backed bots to attach to locally started Cursor Agent sessions.

Tests cover tmux, zellij, and herdr discovery paths for the new aliases.

Co-authored-by: Cursor <cursoragent@cursor.com>
Tmux adoption previously checked only /proc/<pid>/comm when walking a pane's process tree. Cursor Agent can report comm as MainThread while the real executable lives in argv[0], so Cursor-backed /adopt still missed locally started agent processes.

Move comm+argv CLI detection into the shared session discovery module and reuse it from zellij discovery to keep both multiplexer paths consistent.

Co-authored-by: Cursor <cursoragent@cursor.com>
@LucasIcarus LucasIcarus force-pushed the feat/adopt-cursor-agent branch from e472465 to d0632e6 Compare June 3, 2026 09:01
LucasIcarus and others added 2 commits June 3, 2026 18:28
…de CLIs

The adopt picker card only showed an uptime for Claude (read from
~/.claude/sessions/<pid>.json); cursor/codex/coco/gemini etc. always rendered
"unknown". Add readProcessStartTime — Linux reads /proc/<pid>/stat starttime +
/proc/stat btime, other Unix falls back to `ps -o lstart=` — and use it as the
startedAt fallback on both the tmux and zellij discovery paths.

Also call sessionStore.updateSessionPid in forkAdoptWorker, mirroring forkWorker.
Adopt sessions previously kept pid=null, so `botmux list` and killStalePids
judged liveness by "process dead AND no bmx-* tmux"; adopt attaches to the user's
own pane, so the session was misjudged unrecoverable and auto-pruned right after
/adopt. The stored pid is botmux's bridge worker, never the user's CLI.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ession

Triggering /adopt from Lark without picking a target leaves a scratch placeholder
session (adopt_select needs it in memory to attach adoptedFrom). It never forked
a worker, yet `botmux list`'s prune reported it as "process dead and no tmux
session" and warned about it — misleading, since no CLI ever ran there.

cmdList now splits the prune by whether a real CLI ever ran (cliId / lastCliInput
/ adoptedFrom, matching isRelayableRealSession): scratch placeholders are closed
silently, genuinely dead sessions keep the original warning.

Co-authored-by: Cursor <cursoragent@cursor.com>
@LucasIcarus LucasIcarus force-pushed the feat/adopt-cursor-agent branch from 75cdfa9 to 4e625a5 Compare June 3, 2026 10:29
@LucasIcarus LucasIcarus changed the title fix(adopt): detect Cursor agent sessions fix(adopt): detect Cursor agent sessions and fix adopt-session uptime/liveness reporting Jun 3, 2026
@deepcoldy
Copy link
Copy Markdown
Owner

已审阅通过,正在合并,感谢贡献 🙏

合并时在你的改动之上追加了一处小修复(reviewer 提交),先同步给你:

问题:adopt 的「点击接管前二次校验」与「daemon 重启恢复确认」这两步之前重走了无 filter 的识别路径,而本 PR 新增的「以通用名 agent 安装的 Cursor」只有带 cursor filter 才会被认成 Cursor(cliIdForComm 里的 agent 特例)。结果:这类会话能被 /adopt 扫到、出现在候选卡片,但一点击接管(以及 daemon 重启恢复)就会被误判为「目标 CLI 已退出」、接管失败。也就是说 discovery 与 validation 的二次识别口径不一致。

修复:给 validateTmuxAdoptTarget / validateZellijAdoptTarget 补可选形参 filterCliId,从 target.cliId 透传给 findCliProcess / findAllClisUnder,使二次识别与 discovery 一致;并新增 generic-agent 回归测试。该问题与修复经 Codex 独立复审确认。

影响面filterCliId 为可选参,旧调用不传即保持原行为,所以对标准 cursor-agent 名字及其它所有 CLI 零影响,仅修好了通用名 agent 这一形态。

deepcoldy pushed a commit that referenced this pull request Jun 4, 2026
…/liveness

合入 #99(作者 LucasIcarus/凡辞):/adopt 识别 Cursor agent 会话、为非 Claude
CLI 补齐 adopt-session 运行时长与 worker pid、不再把未确认的 /adopt scratch
误报为崩溃会话。

reviewer 追加修复:adopt 二次校验与 daemon 重启恢复透传 cliId,修复以通用名
agent 安装的 Cursor 会话能被发现却在接管/恢复时误判已退出的问题(Codex 复审确认)。
@deepcoldy deepcoldy merged commit 4e625a5 into deepcoldy:master Jun 4, 2026
@LucasIcarus
Copy link
Copy Markdown
Contributor Author

Correction to my previous comment: no separate follow-up PR is needed. Upstream independently landed an equivalent fix in f4e2344 ("adopt 二次校验与重启恢复透传 cliId"), already on master via the #99 merge. Its production changes are byte-for-byte identical to my e476faf (threading cliId through validateTmuxAdoptTarget / validateZellijAdoptTarget / validateAdoptTargetState), so I've dropped my duplicate commit. No further action required here.

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.

2 participants