How the Goose Fork Feature Works for Duplicating and Modifying Sessions

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 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. 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
{
  "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 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 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 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 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 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:

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:

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:

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:

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

Programmatic Forking with Rust SDK

When using the Rust SDK to fork and truncate programmatically:

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, 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, 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 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 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.

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 →