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

> Easily resume OpenCode sessions in jcode using the --resume command. Learn how to reconnect to existing sessions or list all available ones with this technical guide.

- Repository: [Jeremy Huang/jcode](https://github.com/1jehuang/jcode)
- Tags: how-to-guide
- Published: 2026-04-30

---

**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`](https://github.com/1jehuang/jcode/blob/main/src/cli/args.rs) that accepts **zero or one** value:

```rust
/// 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/main/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`](https://github.com/1jehuang/jcode/blob/main/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`](https://github.com/1jehuang/jcode/blob/main/src/cli/startup.rs) (lines 151-169) handles this branching:

```rust
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`](https://github.com/1jehuang/jcode/blob/main/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`](https://github.com/1jehuang/jcode/blob/main/src/server/client_session.rs) (lines 96-99):

```rust
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`](https://github.com/1jehuang/jcode/blob/main/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:

```rust
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/main/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`](https://github.com/1jehuang/jcode/blob/main/src/tui/app/remote/server_events.rs) (lines 1217-1237), you will see:

```

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

No terminal found. Resume manually:

```

```text
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`](https://github.com/1jehuang/jcode/blob/main/src/tui/session_picker.rs) (line 1480) provides a fallback message when running in a non-interactive environment:

```rust
"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:

```bash
$ 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`](https://github.com/1jehuang/jcode/blob/main/src/cli/startup.rs).

### Resume a Specific Session

```bash
$ 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

```bash
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):

```bash
$ jcode --resume session_abc123 --fresh-spawn

```

## Summary

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

- **CLI parsing** in [`src/cli/args.rs`](https://github.com/1jehuang/jcode/blob/main/src/cli/args.rs) handles the `--resume` flag with optional session IDs.
- **Startup logic** in [`src/cli/startup.rs`](https://github.com/1jehuang/jcode/blob/main/src/cli/startup.rs) either lists sessions or initiates the resume RPC.
- **Server-side** logic in [`src/server/client_session.rs`](https://github.com/1jehuang/jcode/blob/main/src/server/client_session.rs) validates sessions, handles takeovers, and replays history.
- **UI feedback** in [`src/tui/app/remote/server_events.rs`](https://github.com/1jehuang/jcode/blob/main/src/tui/app/remote/server_events.rs) provides manual copy-paste instructions when automatic window creation fails.

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`](https://github.com/1jehuang/jcode/blob/main/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`](https://github.com/1jehuang/jcode/blob/main/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`](https://github.com/1jehuang/jcode/blob/main/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.