# How the Goose Fork Feature Works for Duplicating and Modifying Sessions

> Learn how the Goose fork feature duplicates and modifies sessions using copy_session and truncate_conversation methods via CLI and API. Understand session management in blockgoose.

- Repository: [Block Open Source/goose](https://github.com/block/goose)
- Tags: internals
- Published: 2026-04-05

---

**The Goose fork feature creates a copy of an existing session and optionally truncates its conversation history using the `copy_session` and `truncate_conversation` methods exposed through both the CLI and server API.**

The **goose fork feature** in the `block/goose` repository provides a robust mechanism for duplicating AI sessions and selectively modifying their conversation history. Implemented across the server API and CLI client, this feature enables users to branch existing sessions without losing prior context. Whether working locally or remotely, the fork functionality relies on the same core `SessionManager` methods to ensure consistent behavior.

## Understanding the Goose Fork Feature Architecture

The fork feature operates through a unified architecture that separates user-facing interfaces from core session management logic. According to the `block/goose` source code, both the HTTP API and CLI leverage the `SessionManager` struct located in [`crates/goose/src/session/session_manager.rs`](https://github.com/block/goose/blob/main/crates/goose/src/session/session_manager.rs) to handle session duplication and history truncation. This design ensures that **remote API calls** and **local CLI commands** execute identical underlying operations.

The workflow centers on three key operations: validating fork parameters, optionally copying the session via `SessionManager::copy_session`, and optionally truncating messages via `SessionManager::truncate_conversation`. These methods delegate to the storage implementation layer, making the feature agnostic to the specific storage backend.

## Server-Side Session Forking Implementation

The server implementation exposes the fork functionality through the `POST /sessions/{session_id}/fork` endpoint defined in [`crates/goose-server/src/routes/session.rs`](https://github.com/block/goose/blob/main/crates/goose-server/src/routes/session.rs). This endpoint accepts a structured payload and orchestrates the duplication and modification workflow.

### The ForkRequest Payload Structure

Clients send a JSON body matching the `ForkRequest` struct defined at lines 55-59, which specifies three critical fields:

- `copy`: Boolean indicating whether to create a new session before truncation
- `truncate`: Boolean enabling history trimming  
- `timestamp`: Optional Unix millisecond timestamp marking the truncation point

```json
{
  "copy": true,
  "truncate": true,
  "timestamp": 1700000000000
}

```

### Session Duplication with copy_session

When `copy=true`, the server invokes `session_manager.copy_session` after fetching the original session. As implemented in [`crates/goose/src/session/session_manager.rs`](https://github.com/block/goose/blob/main/crates/goose/src/session/session_manager.rs) at lines 30-33, this method forwards to `SessionStorage::copy_session` and returns a new `Session` object with a fresh UUID while preserving the original session name. If `copy=false`, the operation targets the existing `session_id` without generating a duplicate.

### Conversation Truncation with truncate_conversation

If `truncate=true` and a valid `timestamp` is provided, the server calls `session_manager.truncate_conversation(target_id, timestamp)` as shown in [`crates/goose-server/src/routes/session.rs`](https://github.com/block/goose/blob/main/crates/goose-server/src/routes/session.rs) at lines 140-152. This operation removes all messages occurring **after** the specified Unix millisecond timestamp, retaining only the conversation history up to that point. 

Attempting to truncate without providing a timestamp results in a **400 Bad Request** error, as validated at lines 91-96. The endpoint returns a `ForkResponse` (lines 61-66) containing the `session_id` of the target session—either the newly created copy or the original modified session.

## CLI-Based Session Forking

The CLI implementation in [`crates/goose-cli/src/cli.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/cli.rs) provides local access to session forking without requiring HTTP requests. This workflow mirrors the server semantics but operates directly against the local `SessionManager`.

### Configuring the Fork Flag

The CLI exposes the fork functionality through the `--fork` flag added to `SessionBuilderConfig` in [`crates/goose-cli/src/session/builder.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/session/builder.rs) at lines 84-86. When combined with `--resume`, this flag triggers the duplication workflow before launching the interactive session.

### Local Session Duplication Flow

When processing a command with `--fork` enabled, the CLI executes the following steps as implemented in [`crates/goose-cli/src/cli.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/cli.rs) at lines 1199-1205:

1. Retrieves the current session ID via `get_or_create_session_id`
2. Fetches the original session using `SessionManager::instance().get_session`  
3. Creates a copy using `SessionManager::instance().copy_session`
4. Replaces the internal `session_id` with the new copied session's ID

Notably, the CLI does **not** expose the `truncate` parameter directly—truncation remains available only through the server API endpoint.

## Code Examples

### Forking via CLI

To duplicate your most recent session and continue working on the copy locally:

```bash
goose run --resume --fork --text "Explain the fork feature"

```

This command detects the `--fork` flag, copies the current session via `SessionManager::copy_session`, and continues the interaction using the new session ID.

### Forking via HTTP API

To fork a session remotely while keeping the full conversation history:

```bash
curl -X POST "https://goose.example.com/sessions/abcd1234/fork" \
     -H "Content-Type: application/json" \
     -H "x-api-key: <YOUR_KEY>" \
     -d '{
           "copy": true,
           "truncate": false,
           "timestamp": null
         }'

```

To fork and truncate to a specific point in time:

```bash
curl -X POST "https://goose.example.com/sessions/abcd1234/fork" \
     -H "Content-Type: application/json" \
     -H "x-api-key: <YOUR_KEY>" \
     -d '{
           "copy": true,
           "truncate": true,
           "timestamp": 1700000000000
         }'

```

The response returns the new session ID:

```json
{ "session_id": "new-id-5678" }

```

### Programmatic Forking with Rust SDK

When using the Rust SDK to fork and truncate programmatically:

```rust
use goose_sdk::client::GooseClient;
use goose_sdk::model::{ForkRequest, ForkResponse};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = GooseClient::new("https://goose.example.com", "my-api-key");

    let req = ForkRequest {
        copy: true,
        truncate: true,
        timestamp: Some(1700000000000),
    };
    
    let resp: ForkResponse = client
        .post("/sessions/abcd1234/fork", &req)
        .await?;

    println!("New forked session ID: {}", resp.session_id);
    Ok(())
}

```

## Summary

- The **goose fork feature** enables session duplication through `SessionManager::copy_session` and optional history trimming via `SessionManager::truncate_conversation`.
- **Server API** clients use `POST /sessions/{session_id}/fork` with a `ForkRequest` payload containing `copy`, `truncate`, and `timestamp` parameters.
- **CLI users** leverage the `--fork` flag to create local session copies without HTTP round-trips, though truncation requires direct API access.
- Both implementations rely on the same core logic in [`crates/goose/src/session/session_manager.rs`](https://github.com/block/goose/blob/main/crates/goose/src/session/session_manager.rs), ensuring consistent behavior across local and remote workflows.

## Frequently Asked Questions

### What is the difference between copy and truncate in the Goose fork feature?

**Copy** creates a new session with a fresh UUID while preserving the original name and conversation history, whereas **truncate** removes messages after a specified timestamp. You can use `copy=true` with `truncate=false` to duplicate a session completely, or combine both to create a trimmed copy. The `copy` parameter determines whether a new session ID is generated before any truncation occurs.

### Why does the CLI not support the truncate parameter for forking?

The CLI implementation focuses on the common use case of duplicating sessions for branching workflows, while truncation is considered an advanced operation typically performed programmatically. According to the source code in [`crates/goose-cli/src/cli.rs`](https://github.com/block/goose/blob/main/crates/goose-cli/src/cli.rs), the `--fork` flag only triggers `copy_session` without calling `truncate_conversation`. Users requiring truncation must use the HTTP API endpoint directly or make manual requests to `POST /sessions/{session_id}/fork`.

### How does SessionManager handle session duplication internally?

The `SessionManager` struct delegates to the storage layer through `SessionStorage::copy_session`, as defined in [`crates/goose/src/session/session_manager.rs`](https://github.com/block/goose/blob/main/crates/goose/src/session/session_manager.rs) at lines 30-33. This abstraction allows the fork feature to work across different storage backends while maintaining consistent UUID generation and metadata preservation. The manager handles the orchestration between fetching the original session and persisting the copy.

### What happens if I request truncation without providing a timestamp?

The server returns a **400 Bad Request** error. As implemented in [`crates/goose-server/src/routes/session.rs`](https://github.com/block/goose/blob/main/crates/goose-server/src/routes/session.rs) at lines 91-96, the validation logic explicitly checks that `truncate=true` requires a valid `timestamp` parameter. This prevents accidental deletion of conversation history and ensures the truncation boundary is explicitly defined.