[FEATURE] Allow stream-json session binding after process start

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 5, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

In --input-format stream-json mode, session identity can only be selected when the process starts, using --resume. If a CLI starts without --resume, a session_id on the first user message does not load that session's transcript; the CLI creates a new session instead.

This prevents a pre-initialized CLI from being reused for different sessions. For example, a generic VM snapshot can contain an initialized CLI waiting on stdin, but it cannot contain a specific session identity. Resuming a session therefore requires discarding the restored process and spawning another CLI with --resume.

sequenceDiagram
    participant Host
    participant VM
    participant CLI as Restored CLI
    participant CLI2 as New CLI
    Host->>VM: restore generic snapshot
    Host->>VM: hydrate transcript and workspace
    Note over CLI: initialized, but cannot bind session
    Host->>CLI2: spawn with --resume
    CLI2->>CLI2: initialize again
    Host->>CLI2: send first message

Proposed Solution

Allow an unbound stream-json process to bind to an existing session from its input stream. Either:

  1. Honor session_id on the first incoming user message.
  2. Add a resume control_request accepting a session id or transcript path.

Expected behavior:

  • A first user message with session_id loads that transcript and continues the session.
  • A first user message without session_id starts a fresh session.
  • A leading control_request is handled normally.
  • Explicit --resume and --continue flags remain authoritative.
  • A missing transcript fails using the existing --resume behavior.
  • Binding only the initial session is sufficient; mid-stream switching is not required.
sequenceDiagram
    participant Host
    participant CLI as Restored, initialized CLI
    Host->>CLI: hydrate transcript and workspace
    Host->>CLI: first message with session_id
    CLI->>CLI: load transcript and bind session
    CLI-->>Host: continue prior conversation

Alternative Solutions

The supported workaround is to spawn another CLI with --resume, which loses the benefit of restoring an initialized process.

We currently patch the vendored Bun executable instead:

  1. Extract the embedded cli.js.
  2. Inject logic immediately before loadInitialMessages.
  3. Read the first structured input message.
  4. Use its session_id as the resume target when applicable.
  5. Requeue the consumed message on every path.

Requeueing is important: otherwise a message without session_id, or a leading control_request, is silently consumed. The patch works, but it is tied to minified identifiers and must preserve the embedded bundle's exact byte length, making it brittle across releases.

Comparable protocols support late binding directly:

  • Codex app-server: thread/resume
  • pi RPC mode: switch_session

Priority

Medium - Would be very helpful

Feature Category

CLI commands and flags

Use Case Example

  1. Build one generic VM snapshot containing a CLI initialized and waiting on stream-json stdin.
  2. Restore that snapshot when a session resumes.
  3. Hydrate the session transcript and workspace into the VM.
  4. Send the first user message with the existing session_id.
  5. Continue the prior conversation without spawning another CLI.

Additional Context

Measurements on Claude Code 2.1.220:

| Operation | Initialization time |
|---|---:|
| Spawn, then send first message | ~0.74s |
| Send first message to initialized CLI | ~0.07s |

An initialized CLI used about 344 MiB RSS in the same test. The primary benefit is avoiding a new process and initialization cycle for every resumed session.

Patch:
https://github.com/jomcgi/homelab/tree/f45b6e6f70bc738e5586939a31cd4baf9def977b/tools/claude-code-patch

View original on GitHub ↗