[BUG] Remote Control sessions are hidden from the local /resume picker (filtered as entrypoint=sdk-cli)

Status Open
Reported on v2.1.274
Maintainer reply None cached
Activity 3 comments · opened Sep 17, 2026

Preflight Checklist

  • [x] I have searched existing issues and this is not a duplicate
  • [x] This is a bug report, not a feature request

What's Wrong?

Conversations started through claude remote-control (claude rc) run on the local machine and are persisted to the local project transcript directory, but the local claude --resume picker never lists them.

Root cause (from the bundled source of 2.1.274): claude rc spawns each conversation as a headless child

claude --print --sdk-url https://api.anthropic.com/v1/code/sessions/<id> --session-id <id> --input-format stream-json --output-format stream-json …

which sets CLAUDE_CODE_ENTRYPOINT=sdk-cli, so every transcript record carries "entrypoint":"sdk-cli", "promptSource":"sdk". The picker's exclusion predicate skips any session whose transcript head has an SDK entrypoint:

var fwt = new Set(["sdk-cli", "sdk-ts", "sdk-py"]);
function cto(e, n) {
  let t = ele(e, "entrypoint") ?? I1(n, "entrypoint");
  if (t && fwt.has(t)) return !0;           // ← Remote Control sessions hit this
  let r = e.split("\n").find(a => a.includes('"parentUuid":')) ?? e,
      i = ele(r, "sessionKind");
  return i === "daemon" || i === "daemon-worker";
}

The filter presumably targets scripted claude -p / SDK runs. Remote Control reuses the same headless transport, but these are human, interactive conversations the user reasonably expects to find locally.

What Should Happen?

The Remote Control conversation is listed in claude --resume (run from the same project directory) like any other local session — ideally with a "Remote Control" badge, matching how ListAgents already labels them.

Suggested fix: distinguish bridge-spawned sessions from generic SDK runs — record a distinct entrypoint (e.g. remote-control) or a sessionKind for them and exempt it in the filter, or tag the transcript with the bridge session id (a ~/.claude/bridge-spawn/<id> directory already exists per conversation).

Error Messages/Logs

Debug log string emitted by the picker: filtered from /resume: entrypoint=sdk-cli

Transcript head of the hidden session:

{"type":"ai-title","aiTitle":"Hello world","sessionId":"5981c850-…"}
… "type":"user","promptSource":"sdk","userType":"external","entrypoint":"sdk-cli","cwd":"/home/ivan/code/dough-tools","version":"2.1.274" …

Workaround: claude --resume <uuid> with the uuid taken from ~/.claude/projects/<slug>/ works, so the transcript is valid and resumable.

Steps to Reproduce

  1. In a project directory run claude rc, then start a conversation with that machine from claude.ai/code.
  2. Send a message so ~/.claude/projects/<slug>/<uuid>.jsonl is created.
  3. In another terminal, cd to the same project directory and run claude --resume.
  4. The conversation is absent from the list; claude --resume <uuid> opens it fine.

Claude Model

Opus 5

Is this a regression?

Not sure

Claude Code Version

2.1.274 (native build)

Platform

CLI (terminal)

Operating System

Linux (Ubuntu, kernel 6.8)

Terminal/Shell

bash

View original on GitHub ↗

3 Comments

h6dr-git · 28 days ago

Same on macOS — and with the Desktop app there is no workaround at all.

Setup: macOS (Apple silicon), Claude Code CLI 2.1.274 (npm), Claude Desktop 2.110.1 (bundled Claude Code 2.1.271). claude remote-control runs as a launchd agent; sessions are started from the mobile app and from the Desktop app on another machine.

Confirming the report, plus three additions.

1. The workaround works, so the transcript itself is fine.
claude --resume <uuid> --fork-session -p "…" on a session the picker hides returned normally. Only the listing hides it.

2. The Desktop app ships the same predicate, so GUI-only users have no path.
In Claude.app/Contents/Resources/app.asar:

var lq = new Set(["sdk-cli", "sdk-ts", "sdk-py"]);
function uq(e, t) {
  let n = mq(e, "entrypoint") ?? hq(t, "entrypoint");
  if (n && lq.has(n)) return !0;
  let r = mq(e.split("\n").find((e) => e.includes('"parentUuid":')) ?? e, "sessionKind");
  return r === "daemon" || r === "daemon-worker";
}

The Desktop app's /resume is declared with argumentHint: "[search]" and searches that already-filtered list, so passing the uuid doesn't help either. Net effect for someone who only uses the Desktop app and the mobile app: a conversation started from the phone can never be continued as a Desktop-hosted session. The Desktop sidebar does show it under the Remote Control device group, but the app is only a viewer there, so none of the app-hosted affordances (browser panel, Terminal, Open in, Continue in) are available for it.

3. sessionKind: daemon / daemon-worker hits the same predicate, so this is not only about the sdk-cli entrypoint.

Why promotion looks cheap: a Desktop local-session record (~/Library/Application Support/Claude/claude-code-sessions/<account>/<org>/local_<uuid>.json) is essentially {sessionId, cliSessionId, cwd, title, model, effort, permissionMode, …}, and the app resumes whatever cliSessionId points at. The transcript is already on the same machine, so ending the server-hosted process and re-opening that transcript as an app-hosted session appears to be all that is missing. Live tool/subagent state would be lost, the same tradeoff --teleport already makes.

Requests, in order of usefulness:

  1. Record a distinct entrypoint (e.g. remote-control) or a bridge marker, so these stop being classified as generic SDK runs.
  2. Surface them in the Desktop app's /resume list with a badge.
  3. A first-class "continue here" action for a session hosted by claude remote-control on this machine.

Related: #94369, #94546, #95073.

ivan-gentile · 23 days ago

Still present in 2.1.278 (native build, Linux/WSL2), and I can add one detail that I think matters for how this gets fixed: the filter is permanent, because it only reads the transcript head.

Reproduced today with two Remote Control sessions started from the mobile app (claude remote-control running locally, default same-dir spawn). Both are written to ~/.claude/projects/<slug>/ with "entrypoint":"sdk-cli" on the first record, and the picker's own debug log shows the exclusion:

$ claude --resume --debug-file /tmp/resume.log     # run from the project dir
/resume: loading sessions for cwd=<project-a>, worktrees=[]
/resume: found 2 session files on disk
Session <uuid-a> filtered from /resume: entrypoint=sdk-cli
/resume: enriched 2 sessions, 1 filtered out, 1 visible (0 remaining on disk)

Two things that do not help, verified:

  1. /rename from the remote client. On the second session I ran /rename <title> from the phone before closing. The {"type":"custom-title","customTitle":"…"} record is correctly persisted in the local .jsonl, but since the session never reaches the list, the title is never shown and claude --resume "<title>" finds nothing. So the "name your session so you can find it later" advice does not work for Remote Control sessions.
  1. Resuming it locally by UUID and continuing the conversation. I then did claude --resume <uuid> on that same session and kept working in it interactively from the terminal. The transcript now has 68 records with "entrypoint":"cli" appended after the 191 sdk-cli ones — it is, by any reasonable definition, a local interactive session. The picker still hides it:
/resume: loading sessions for cwd=<project-b>, worktrees=[]
/resume: found 3 session files on disk
Session <uuid-b> filtered from /resume: entrypoint=sdk-cli
/resume: enriched 3 sessions, 1 filtered out, 2 visible (0 remaining on disk)

This matches the predicate quoted above (ele(e, "entrypoint") on the head chunk): whatever entrypoint the session started with decides its visibility forever. A session that was born on the phone and lived the rest of its life in the terminal is never listed.

Practical consequence for the "start on mobile, continue at the desk" flow: the only way to get back to the conversation is to know the UUID, and nothing in the product prints it (claude.ai/code shows a different session_01… id; ~/.claude/sessions/<pid>.json is deleted on exit; the bridge-pointer.json in the project dir holds the bridge session_… id, not the local UUID).

Any of the fixes proposed above would do (distinct entrypoint/sessionKind for bridge-spawned sessions, or honoring a later cli entrypoint / custom-title as a signal that this is a human session). Happy to test a build.

Spenhouet · 15 days ago

Still reproduces on 2.1.285. The filter is unchanged (new Set(["sdk-cli","sdk-ts","sdk-py"])). My rc session's transcript starts with "entrypoint":"sdk-cli" records and does not appear in claude -r.