How to Resume Sessions from OpenCode in jcode: A Complete Technical Guide

Use jcode --resume <session_id> to reconnect to an existing OpenCode session, or run jcode --resume without arguments to list all available sessions on the server.

The jcode CLI (from the 1jehuang/jcode repository) persists every interactive OpenCode session server-side, enabling you to resume sessions from OpenCode in jcode after terminal crashes, window closures, or when switching machines. This guide explains the complete resume flow—from CLI argument parsing through server-side session takeover logic—based on the actual implementation in the source code.

CLI Flag Definition

jcode uses clap to define its command-line interface. The resume option is declared as a global argument in src/cli/args.rs that accepts zero or one value:

/// Resume a session by ID, or list sessions if no ID provided
#[arg(long, global = true, num_args = 0..=1, default_missing_value = "")]
pub(crate) resume: Option<String>,

Source: [src/cli/args.rs](https://github.com/1jehuang/jcode/blob/master/src/cli/args.rs#L48-L51)

  • num_args = 0..=1 allows running jcode --resume (no value) to display a table of resumable sessions.
  • When a session ID is supplied, the resume field contains Some("<session_id>").

Startup Logic

When the binary starts, src/cli/startup.rs examines Args::resume and branches into three distinct paths:

  • No flag (None): Normal launch—starts a fresh session.
  • Flag without value (Some("")): Calls list_resumable_sessions() and prints a table of available sessions.
  • Flag with ID (Some(id)): Calls client.resume_session(id) to initiate the remote RPC.

The relevant implementation in src/cli/startup.rs (lines 151-169) handles this branching:

if let Some(resume_id) = args.resume.clone() {
    if resume_id.is_empty() {
        // List resumable sessions
        list_resumable_sessions(&client).await?;
    } else {
        // Perform real resume
        client.resume_session(&resume_id).await?;
    }
}

The client.resume_session() method sends a ResumeSession request to the server via the jcode-protocol crate's RPC module.

Server-Side Resume Handling

Entry Point and Validation

The server receives the RPC in handle_resume_session (defined in src/server/client_lifecycle.rs). The function evaluates several parameters:

  • session_id: The requested session identifier.
  • client_has_local_history: Indicates if the client already possesses a partial history buffer.
  • allow_session_takeover: Set when the client passes --force (used internally for forced takeovers).

Conflict Resolution and Session Takeover

If another live client is already attached to the same session, the server determines whether the new client can take over using the logic in src/server/client_session.rs (lines 96-99):

let can_take_over_live_session =
    allow_session_takeover && client_has_local_history && !distinct_client_instances;

When takeover is permitted, the server sends a disconnect signal to the old client, and the new client inherits the processing state—including any in-flight tool executions—guaranteeing exact continuation of the session.

History Replay

After conflict resolution, handle_get_history() streams the full session history (both user messages and assistant responses) to the newly attached client. The client receives a ServerEvent::Done { id } event to mark the completion of the resume operation.

Source: src/server/client_session.rs (around lines 720-730).

Tool and MCP Registration

If the resumed session is a self-dev (canary) session, the server registers extra developer tools:

if is_canary {
    *client_selfdev = true;
    registry.register_selfdev_tools().await;
}

The server also registers any MCP (Multi-Chat-Planner) tools so the UI can continue invoking them seamlessly.

Source: [src/server/client_session.rs](https://github.com/1jehuang/jcode/blob/master/src/server/client_session.rs#L65-L69)

UI Feedback for Manual Resumption

When resuming from a remote machine (e.g., via SSH) where the server cannot spawn a local terminal automatically, the TUI displays a specific instruction block. According to src/tui/app/remote/server_events.rs (lines 1217-1237), you will see:


🔍 <provider> session **<session_id>** created.

No terminal found. Resume manually:

jcode --resume <session_id>

This message appears in three scenarios:

  • When a new window creation fails for a resumed session.
  • When a split creation fails.
  • When a transfer operation fails.

Additionally, the session picker in src/tui/session_picker.rs (line 1480) provides a fallback message when running in a non-interactive environment:

"Session picker requires an interactive terminal. Use --resume <session_id> directly."

Practical Usage Examples

List All Resumable Sessions

Run the command without a session ID to see available sessions:

$ jcode --resume
┌─────────────────┬───────────────┬─────────────┐
 Session ID Provider Age (min)   │
├─────────────────┼───────────────┼─────────────┤
 session_abc123 claude 12
 session_xyz789 gpt-4o-mini 3
└─────────────────┴───────────────┴─────────────┘

The table is generated by list_resumable_sessions() in src/cli/startup.rs.

Resume a Specific Session

$ jcode --resume session_abc123

Internal flow:

  1. CLI parses the flag into resume: Some("session_abc123").
  2. client.resume_session("session_abc123") transmits the RPC.
  3. Server executes handle_resume_session(), resolves any conflicts, and streams history.
  4. TUI re-attaches and displays the prompt ready for new input.

Resume from a Remote Host

ssh user@remote
$ jcode --resume session_xyz789

If the remote side cannot launch a local terminal, use the "Resume manually" hint provided in the output, then run the same command on a machine capable of opening a UI.

Force Takeover of a Live Session (Advanced)

The allow_session_takeover flag is currently internal. To forcibly hijack a session already attached elsewhere, combine --fresh-spawn with --resume (primarily used by the desktop helper when launching resumed sessions in new windows):

$ jcode --resume session_abc123 --fresh-spawn

Summary

Resuming sessions from OpenCode in jcode provides persistent, interruption-proof coding conversations:

With these mechanisms, jcode treats sessions as persistent "chat documents" survivable across crashes and machine switches.

Frequently Asked Questions

What happens if I try to resume a session that is already active on another terminal?

According to src/server/client_session.rs, the server checks can_take_over_live_session using three conditions: allow_session_takeover, client_has_local_history, and distinct_client_instances. If takeover is allowed, the old client receives a disconnect signal and the new client inherits the exact processing state. If not allowed, the resume request is rejected or queued.

Can I resume a session from a different machine?

Yes. Since jcode stores sessions server-side, you can SSH into a remote host and run jcode --resume <session_id>. If the remote cannot open a graphical terminal, the CLI prints the exact command to copy-paste on a local machine that supports the TUI, as implemented in src/tui/app/remote/server_events.rs.

Why does jcode display "Resume manually" when I try to resume?

This message appears when the server cannot automatically spawn a terminal window for the resumed session—common in headless SSH sessions or when window creation fails. The UI prints jcode --resume <session_id> so you can run the command manually in an environment where the TUI can actually display.

How do I list all resumable sessions without specifying an ID?

Run jcode --resume with no additional arguments. The startup logic in src/cli/startup.rs detects the empty string value (Some("")) and calls list_resumable_sessions(), printing a formatted table containing Session ID, Provider, and Age.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →