Skip to content

Allow Claude to rename its own session (programmatic session rename API) #33165

Open

Description

@dokipen

Problem

When using Claude Code in VS Code with agentic workflows, it's useful to name sessions by the issue/task number being worked on. Currently this requires manual renaming via the UI.

I investigated making Claude rename its own session programmatically by appending custom-title entries to the session .jsonl file (which is what the extension's renameSession() does internally). This partially works but has two issues:

1. Session list titles lost on long sessions

The session list (listSessions) reads titles by scanning only the last 64KB of each .jsonl file for "customTitle" entries. On long sessions (common with agentic workflows), the custom-title entry gets pushed outside this 64KB tail window by subsequent conversation messages, and the title is lost from the session list.

2. No way to update the live tab title

The editor tab title is set via an internal rename_tab webview message. There's no registered VS Code command to trigger this externally. Even if the file-based title is correct, the active tab title can't be updated without going through the extension's internal message channel.

Proposed Solution

Expose a VS Code command like claude-vscode.renameSession that:

  1. Appends the custom-title entry to the .jsonl file (already implemented internally)
  2. Updates the in-memory customTitles Map
  3. Sends a rename_tab message to update the live tab title
  4. Refreshes the session list sidebar

This would allow Claude itself (or other extensions/tools) to set meaningful session names programmatically.

Alternative for #1 alone

Store custom titles in a separate lightweight index (e.g., sessions-index.json or a small sidecar file) rather than relying on scanning the tail of potentially large .jsonl files.

Use Case

Agentic workflows where each session maps to a GitHub issue. Having Claude auto-name the session (e.g., "941-fix-supabase-release") at the start of a workflow would save manual effort and improve session organization.

Activity

  1. mgerasolo commented on Mar 16, 2026

    @mgerasolo

    Historically, I only saw the first 24 characters as tab titles. Now it's randomly updating it, and you can't tell which conversation is which anymore as they are changing, but its picking a random message, which makes it hard to pick the right conversation again, especially since lately when I reload, some of my tabs (I usually have 2-5 open panes each with multiple tabs) won't load likely becuase it is trying to load too many conversations at once on reload/refresh.

  2. interconnectedMe commented on Mar 16, 2026

    @interconnectedMe

    Session title corruption — root cause analysis and workaround

    This issue is part of a family of bugs that all stem from the same root cause. Collecting them here for visibility:

    Title lost/overwritten:

    Wrong title displayed / cross-session contamination:

    Sessions invisible / indexing failures:

    Feature requests (workarounds for the same root cause):

    Root cause

    The VS Code extension's listSessions reads titles by doing a raw string search for "customTitle" in the last 64KB of each .jsonl file. This has three failure modes:

    1. 64KB eviction: On long sessions (common with agentic workflows), the custom-title entry gets pushed outside the 64KB tail window. Title is lost.

    2. Cross-session contamination: The scanner doesn't distinguish between real custom-title JSONL entries and the string "customTitle" appearing inside tool results or conversation content. If a tool result contains "customTitle": "some other title" as text, the extension picks that up as the session's title.

    3. Overwrite on resume: When a session is resumed and new content is appended, any custom-title entry from /rename gets buried deeper in the file. The extension falls back to lastPrompt or picks up a stale match from tool output.

    Proposed fix

    Store titles in a separate lightweight index rather than scanning the tail of large JSONL files. A title-registry.json (or similar) that maps session_id → title would:

    • Survive session growth (no 64KB window)
    • Be immune to content contamination (not scanning conversation content)
    • Enable cross-client sync (single source of truth)
    • Allow programmatic rename without race conditions

    Workaround

    See #32150 for a working implementation (UserPromptSubmit hook + systemd timer + persistent title registry).

  3. interconnectedMe commented on Mar 16, 2026

    @interconnectedMe

    @hackyon-anthropic — you worked on the session title fix for #9668 (Warmup titles) and the v2.1.47/v2.1.71 patches. The root cause analysis above identifies why those fixes didn't fully resolve the problem: the 64KB tail scanner picks up "customTitle" strings from inside tool result content, not just from real JSONL entries. This causes cross-session title contamination that no amount of repositioning the custom-title entry can fix.

    A separate title index file (rather than scanning conversation content) would eliminate all three failure modes in one shot. Happy to discuss the implementation details — we've been running a working workaround (persistent registry + hook) for weeks.

  4. ElliotDrel commented on Mar 19, 2026

    @ElliotDrel

    +1 on this. My use case is from the CLI side rather than VS Code:

    I'd love to create a custom slash command (e.g., /suggest-rename) where Claude analyzes the conversation, suggests a few title options, and then applies the chosen one — all without me having to manually copy-paste into /rename. Right now the workflow is:

    1. Ask Claude for a good session title
    2. Read the suggestions
    3. Manually type /rename <title>

    It's a small friction, but it adds up across dozens of sessions. If Claude had access to a rename tool/API, this could be a single-step interaction.

    A programmatic rename capability would benefit both IDE and CLI users — anywhere Claude can suggest context-aware names but can't act on them.

  5. ElliotDrel commented on Mar 24, 2026

    @ElliotDrel

    I built a workaround for this: a /better-title skill that has Claude analyze the conversation, suggest 3 descriptive titles, and apply the chosen one by appending directly to the session JSONL file.

    Repo: https://github.com/ElliotDrel/elliots-skills/tree/main/better-title

    It works, but it's a hack — writing directly to the JSONL is fragile (see the 64KB tail-scan issue discussed upthread), and there's no way to update the live UI border until the next /resume. A proper rename API or tool would make this clean and reliable.

    My use case: at the end of every session I want Claude to give it a descriptive name so I can find it later in the /resume menu. Right now the skill handles it, but a first-class API would benefit both CLI and IDE users.

  6. ElliotDrel commented on Mar 24, 2026

    @ElliotDrel

    I'm personally affected by this — session titles I set via /rename (or my /better-title skill) regularly disappear from the /resume menu on longer sessions. It's disruptive when you're trying to find a specific session among dozens.

    My /better-title skill (repo) partially mitigates this since you can re-run it at the end of a long session to re-append the title within the 64KB window. But that's a band-aid — you shouldn't have to re-title a session just because the conversation got long.

    +1 for @interconnectedMe's proposal of a separate title registry/index. Scanning the tail of a growing JSONL file for metadata is fundamentally the wrong approach.

  7. interconnectedMe commented on Mar 24, 2026

    @interconnectedMe

    I built a workaround for this: a /better-title skill that has Claude analyze the conversation, suggest 3 descriptive titles, and apply the chosen one by appending directly to the session JSONL file.

    Repo: https://github.com/ElliotDrel/elliots-skills/tree/main/better-title

    It works, but it's a hack — writing directly to the JSONL is fragile (see the 64KB tail-scan issue discussed upthread), and there's no way to update the live UI border until the next /resume. A proper rename API or tool would make this clean and reliable.

    My use case: at the end of every session I want Claude to give it a descriptive name so I can find it later in the /resume menu. Right now the skill handles it, but a first-class API would benefit both CLI and IDE users.

    Same with mine - it works, but it's hacky, takes a couple of minutes for titles to show up, and sometimes even requires a dev reload....and will likely break on an upcoming extension version....

    But - anything to have Claude Code chat sessions actually match to my task card titles ;-)

  8. fthomas-team commented on Jul 28, 2026

    @fthomas-team

    +1 - I start chats all the time that either lead to or link to some external ticket reference. At that point I want Claude to immediately insert that ref into the session title. Why is that not possible? Cmon...

  9. BasedGPT commented on Jul 31, 2026

    @BasedGPT

    That long-session title loss is a cache problem rather than the session JSONL losing its content. The custom-title record is still in the file; the VS Code extension's 64 KB tail scan simply stops seeing it once later turns push it out.

    I built BasedGPT/claude-code-session-recovery for the related VS Code cache case. Run python tools/diagnose.py first; it checks the complete transcript files against the workspace cache and prints the exact recovery command for your diagnosis. If it reports dropped entries, the dry-run command is python tools/sessions/recover_vscode_sessions.py. That reads the complete JSONL rather than the tail and rebuilds the cache without touching the transcript. Keep VS Code closed before applying a cache repair.

    That won't provide a live rename command, but it should separate the title/API request from the existing recovery problem: the stored session is still there, while the list can no longer derive its title.

    Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)

  10. NubeBuster commented on Sep 14, 2026

    @NubeBuster

    Workaround: rename a live session programmatically via its messaging inbox socket (CLI)

    Verified on Claude Code 2.1.270, Linux.

    @dokipen Does this work for VS Code as well? It's basically the same as slash rename. But then via a script.

    This is the bare minimum test for it. Run

    ! printf '%s\n' '{"type":"control","action":"rename","name":"my-custom-session-name"}' | nc -N -U "$CLAUDE_CODE_MESSAGING_SOCKET" 

    Inside Claude Code. Does that update the VSCode tab?

    Or from another terminal:

    printf '%s\n' '{"type":"control","action":"rename","name":"my-custom-session-name"}' | nc -N -U /run/user/1000/cc-socks/XXXXXXXX.sock

    where the sock is the path that is shown in /status

    The frame runs the same internal function as a typed /rename (source "peer" instead of "user"), ~50 ms. This is basically the message that a remote control session would send for emitting a rename command.

    What it updates (all verified)

    Target Updated
    Live TUI label bar ✅ repaints by itself
    Transcript .jsonl (custom-title + agent-name records) ✅
    …/<session-id>/custom-title.json ✅
    ~/.claude/sessions/<pid>.json registry (name, nameSource:"peer", nameSince, formerNames) ✅
    Remote Control phone/web title ✅ syncs to remote
    /list-agents in other sessions, --resume later ✅

    Requirements — what is and isn't needed

    Variable Required? Verified
    Claude Code ≥ 2.1.224 (inbox socket exists) yes docs
    Session not started with --bare; /status Peer address not unavailable yes docs
    Remote Control enabled no — works with and without ✅
    Auth line {"type":"auth","token":…} (Linux/macOS) no — absent and bogus token both work; required on native Windows ✅
    session_id in the frame no — optional guard; if present and wrong, frame is dropped ✅
    Caller is the session's own child / hook no — any local process of the same user can send ✅
    crossSessionInbound = accept no — control frames bypass the inbound queue (hold/refuse, rate limit, dedupe, queue cap don't apply) code
    Any settings key or feature flag no code
    Terminal automation, tmux, binary patch, elevated permissions no ✅
    Sandboxed Bash: sandbox.network.allowUnixSockets only if the sender runs inside the sandbox; hooks aren't sandboxed docs
    Local terminal attached to a web session (claude --remote <id> / --teleport, the session itself runs in the cloud) must not be — the peer-rename callback isn't mounted in that mode; rename goes through the web session code, untested

    Name handling (verified)

    Input Result
    Same as current name no-op
    Empty / whitespace / missing name dropped
    Control characters replaced by spaces; spaces kept
    > 200 chars truncated to 200
    Held by another live session duplicate kept — yielding to <name>-<word>-<word> is behind server flag tengu_session_name_uniqueness (off for my account), identical for typed and socket

    Locating the socket

    ENVIRONMENT. CLAUDE_CODE_MESSAGING_SOCKET is available for hooks and in Bash commands run by Claude. docs

    Fallback, for a sender that is not a child of the session (another terminal, one session renaming another, external scripts): ~/.claude/sessions/*.json → match sessionId → messagingSocketPath.

    Hook example / Script

    #!/usr/bin/env python3
    """Rename a running Claude Code session via its messaging inbox socket.
    
      claude-rename-session.py <name>                   # own session (socket from the env var)
      claude-rename-session.py <name> --session <uuid>  # any live session on this machine
      claude-rename-session.py                          # as a UserPromptSubmit hook: reads the hook JSON
                                                        # from stdin, renames when the prompt is "rename: <name>"
    Exit 0 once the registry shows the rename (or nothing to do), 1 otherwise.
    """
    import json, os, socket, sys, time
    from pathlib import Path
    
    SESSIONS = Path(os.environ.get("CLAUDE_CONFIG_DIR", Path.home() / ".claude")) / "sessions"
    
    
    def record(session_id):
        for f in SESSIONS.glob("*.json"):
            try:
                d = json.loads(f.read_text())
            except (OSError, ValueError):
                continue
            if d.get("sessionId") == session_id:
                return d
        return None
    
    
    def rename(name, session_id=None, sock=None, token=None, timeout=3.0):
        rec = record(session_id) if session_id else None
        sock = (rec or {}).get("messagingSocketPath") or sock
        if not sock:
            return None
        frames = [{"type": "auth", "token": token}] if token else []  # required on Windows only
        frame = {"type": "control", "action": "rename", "name": name}
        if session_id:
            frame["session_id"] = session_id  # dropped by the session if the id doesn't match
        frames.append(frame)
        sent_at = time.time() * 1000
        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
            s.settimeout(1.0)
            s.connect(sock)
            s.sendall("".join(json.dumps(f) + "
    " for f in frames).encode())
        if not session_id:
            return name  # fire-and-forget; nothing to poll against
        deadline = time.monotonic() + timeout
        while time.monotonic() < deadline:
            rec = record(session_id) or {}
            if rec.get("name") == name or (rec.get("nameSince") or 0) >= sent_at:
                return rec.get("name")
            time.sleep(0.05)
        return None
    
    
    PROMPT_PREFIX = "rename:"
    
    
    def main():
        args = sys.argv[1:]
        session_id = None
        if "--session" in args:
            i = args.index("--session")
            session_id = args[i + 1]
            del args[i : i + 2]
        if len(args) > 1:
            sys.exit(__doc__)
        hook = {}
        if not session_id and not sys.stdin.isatty():  # hook input: {"session_id", "prompt", ...}
            try:
                hook = json.loads(sys.stdin.read() or "{}")
            except ValueError:
                pass
            session_id = hook.get("session_id")
        if args:
            name = args[0]
        else:
            prompt = (hook.get("prompt") or "").strip()
            if not prompt.startswith(PROMPT_PREFIX):
                return  # not a rename prompt; let it through untouched
            name = prompt[len(PROMPT_PREFIX):].strip()
            if not name:
                return
        applied = rename(name, session_id, os.environ.get("CLAUDE_CODE_MESSAGING_SOCKET"),
                         os.environ.get("CLAUDE_CODE_MESSAGING_TOKEN"))
        if applied is None:
            sys.exit("rename not confirmed (no inbox socket, or session did not apply it)")
        print(applied)
    
    
    if __name__ == "__main__":
        main()
    {
      "hooks": {
        "UserPromptSubmit": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "python3 ~/.claude/hooks/claude-rename-session.py"
              }
            ]
          }
        ]
      }
    }
  11. dkushnikov commented on Oct 20, 2026

    @dkushnikov

    Data point from the terminal CLI, Claude Code 2.1.296 on macOS: the inbox-socket control frame from the comment above ({"type":"control","action":"rename","name":…} + optional session_id) renames a live session end to end — TUI label, transcript custom-title record, custom-title.json sidecar, pid-file registry (nameSource: "peer", formerNames) and the Remote Control / phone list. The session_id guard works as described (mismatching id → frame dropped). We now call it from a wrap-up script so the agent names its own session without a manual /rename.

    Request: an official door for the same — claude session rename <id> <name> or a hook/SDK-visible call — the socket frame is undocumented and may change between releases.

  12. jacobyoby commented on Nov 1, 2026

    @jacobyoby

    Workaround: https://github.com/jacobrakaiFoundation/claude-mods/tree/main/mods/session-namer

    Gives Claude a rename_session tool (same as /rename), and names an unnamed session after its first answer with a short title from a small model. A name you set is never replaced unless your prompt asks for a new one. Interactive sessions only (it stays silent under claude -p). Install: /plugin install session-namer --marketplace jacobrakaiFoundation/claude-mods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:ideduplicateThis issue or pull request already existsenhancementNew feature or requestplatform:vscodeIssue specifically occurs in VS Code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions