Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions docs/guides/session-persistence.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,16 +325,16 @@ async function cleanupExpiredSessions(maxAgeMs: number) {
await cleanupExpiredSessions(24 * 60 * 60 * 1000);
```

### Explicit Session Destruction
### Closing a Session (`destroy`)

When a task completes, destroy the session explicitly rather than waiting for timeouts:
When a task completes, close the session explicitly rather than waiting for timeouts. This releases in-memory resources but **preserves session data on disk**, so the session can still be resumed later:

```typescript
try {
// Do work...
await session.sendAndWait({ prompt: "Complete the task" });

// Task complete - clean up
// Task complete — release in-memory resources (session can be resumed later)
await session.destroy();
} catch (error) {
// Clean up even on error
Expand All @@ -343,6 +343,17 @@ try {
}
```

### Permanently Deleting a Session (`deleteSession`)

To permanently remove a session and all its data from disk (conversation history, planning state, artifacts), use `deleteSession`. This is irreversible — the session **cannot** be resumed after deletion:

```typescript
// Permanently remove session data
await client.deleteSession("user-123-task-456");
```

> **`destroy()` vs `deleteSession()`:** `destroy()` releases in-memory resources but keeps session data on disk for later resumption. `deleteSession()` permanently removes everything, including files on disk.

## Automatic Cleanup: Idle Timeout

The CLI has a built-in 30-minute idle timeout. Sessions without activity are automatically cleaned up:
Expand Down Expand Up @@ -526,8 +537,8 @@ await withSessionLock("user-123-task-456", async () => {
| **Resume session** | `client.resumeSession(sessionId)` |
| **BYOK resume** | Re-provide `provider` config |
| **List sessions** | `client.listSessions(filter?)` |
| **Delete session** | `client.deleteSession(sessionId)` |
| **Destroy active session** | `session.destroy()` |
| **Close active session** | `session.destroy()` — releases in-memory resources; session data on disk is preserved for resumption |
| **Delete session permanently** | `client.deleteSession(sessionId)` — permanently removes all session data from disk; cannot be resumed |
| **Containerized deployment** | Mount `~/.copilot/session-state/` to persistent storage |

## Next Steps
Expand Down
17 changes: 12 additions & 5 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,23 @@ async Task<Connection> StartCoreAsync(CancellationToken ct)
}

/// <summary>
/// Disconnects from the Copilot server and stops all active sessions.
/// Disconnects from the Copilot server and closes all active sessions.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method performs graceful cleanup:
/// <list type="number">
/// <item>Destroys all active sessions</item>
/// <item>Closes all active sessions (releases in-memory resources)</item>
/// <item>Closes the JSON-RPC connection</item>
/// <item>Terminates the CLI server process (if spawned by this client)</item>
/// </list>
/// </para>
/// <para>
/// Note: session data on disk is preserved, so sessions can be resumed later.
/// To permanently remove session data before stopping, call
/// <see cref="DeleteSessionAsync"/> for each session first.
/// </para>
/// </remarks>
/// <exception cref="AggregateException">Thrown when multiple errors occur during cleanup.</exception>
/// <example>
Expand Down Expand Up @@ -655,15 +660,17 @@ public async Task<List<ModelInfo>> ListModelsAsync(CancellationToken cancellatio
}

/// <summary>
/// Deletes a Copilot session by its ID.
/// Permanently deletes a session and all its data from disk, including
/// conversation history, planning state, and artifacts.
/// </summary>
/// <param name="sessionId">The ID of the session to delete.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous delete operation.</returns>
/// <exception cref="InvalidOperationException">Thrown when the session does not exist or deletion fails.</exception>
/// <remarks>
/// This permanently removes the session and all its conversation history.
/// The session cannot be resumed after deletion.
/// Unlike <see cref="CopilotSession.DisposeAsync"/>, which only releases in-memory
/// resources and preserves session data for later resumption, this method is
/// irreversible. The session cannot be resumed after deletion.
/// </remarks>
/// <example>
/// <code>
Expand Down
15 changes: 9 additions & 6 deletions dotnet/src/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -541,22 +541,25 @@ await InvokeRpcAsync<object>(
}

/// <summary>
/// Disposes the <see cref="CopilotSession"/> and releases all associated resources.
/// Closes this session and releases all in-memory resources (event handlers,
/// tool handlers, permission handlers).
/// </summary>
/// <returns>A task representing the dispose operation.</returns>
/// <remarks>
/// <para>
/// After calling this method, the session can no longer be used. All event handlers
/// and tool handlers are cleared.
/// Session state on disk (conversation history, planning state, artifacts) is
/// preserved, so the conversation can be resumed later by calling
/// <see cref="CopilotClient.ResumeSessionAsync"/> with the session ID. To
/// permanently remove all session data including files on disk, use
/// <see cref="CopilotClient.DeleteSessionAsync"/> instead.
/// </para>
/// <para>
/// To continue the conversation, use <see cref="CopilotClient.ResumeSessionAsync"/>
/// with the session ID.
/// After calling this method, the session object can no longer be used.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Using 'await using' for automatic disposal
/// // Using 'await using' for automatic disposal — session can still be resumed later
/// await using var session = await client.CreateSessionAsync(new() { OnPermissionRequest = PermissionHandler.ApproveAll });
///
/// // Or manually dispose
Expand Down
11 changes: 9 additions & 2 deletions go/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,14 @@ func (c *Client) Start(ctx context.Context) error {
// Stop stops the CLI server and closes all active sessions.
//
// This method performs graceful cleanup:
// 1. Destroys all active sessions
// 1. Closes all active sessions (releases in-memory resources)
// 2. Closes the JSON-RPC connection
// 3. Terminates the CLI server process (if spawned by this client)
//
// Note: session data on disk is preserved, so sessions can be resumed later.
// To permanently remove session data before stopping, call [Client.DeleteSession]
// for each session first.
//
// Returns an error that aggregates all errors encountered during cleanup.
//
// Example:
Expand Down Expand Up @@ -685,8 +689,11 @@ func (c *Client) ListSessions(ctx context.Context, filter *SessionListFilter) ([
return response.Sessions, nil
}

// DeleteSession permanently deletes a session and all its conversation history.
// DeleteSession permanently deletes a session and all its data from disk,
// including conversation history, planning state, and artifacts.
//
// Unlike [Session.Destroy], which only releases in-memory resources and
// preserves session data for later resumption, DeleteSession is irreversible.
// The session cannot be resumed after deletion. If the session is in the local
// sessions map, it will be removed.
//
Expand Down
14 changes: 9 additions & 5 deletions go/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,17 +511,21 @@ func (s *Session) GetMessages(ctx context.Context) ([]SessionEvent, error) {
return response.Events, nil
}

// Destroy destroys this session and releases all associated resources.
// Destroy closes this session and releases all in-memory resources (event
// handlers, tool handlers, permission handlers).
//
// After calling this method, the session can no longer be used. All event
// handlers and tool handlers are cleared. To continue the conversation,
// use [Client.ResumeSession] with the session ID.
// Session state on disk (conversation history, planning state, artifacts) is
// preserved, so the conversation can be resumed later by calling
// [Client.ResumeSession] with the session ID. To permanently remove all
// session data including files on disk, use [Client.DeleteSession] instead.
//
// After calling this method, the session object can no longer be used.
//
// Returns an error if the connection fails.
//
// Example:
//
// // Clean up when done
// // Clean up when done — session can still be resumed later
// if err := session.Destroy(); err != nil {
// log.Printf("Failed to destroy session: %v", err)
// }
Expand Down
14 changes: 10 additions & 4 deletions nodejs/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,14 @@ export class CopilotClient {
* Stops the CLI server and closes all active sessions.
*
* This method performs graceful cleanup:
* 1. Destroys all active sessions with retry logic
* 1. Closes all active sessions (releases in-memory resources)
* 2. Closes the JSON-RPC connection
* 3. Terminates the CLI server process (if spawned by this client)
*
* Note: session data on disk is preserved, so sessions can be resumed later.
* To permanently remove session data before stopping, call
* {@link deleteSession} for each session first.
*
* @returns A promise that resolves with an array of errors encountered during cleanup.
* An empty array indicates all cleanup succeeded.
*
Expand Down Expand Up @@ -823,10 +827,12 @@ export class CopilotClient {
}

/**
* Deletes a session and its data from disk.
* Permanently deletes a session and all its data from disk, including
* conversation history, planning state, and artifacts.
*
* This permanently removes the session and all its conversation history.
* The session cannot be resumed after deletion.
* Unlike {@link CopilotSession.destroy}, which only releases in-memory
* resources and preserves session data for later resumption, this method
* is irreversible. The session cannot be resumed after deletion.
*
* @param sessionId - The ID of the session to delete
* @returns A promise that resolves when the session is deleted
Expand Down
17 changes: 11 additions & 6 deletions nodejs/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,18 +499,23 @@ export class CopilotSession {
}

/**
* Destroys this session and releases all associated resources.
* Closes this session and releases all in-memory resources (event handlers,
* tool handlers, permission handlers).
*
* After calling this method, the session can no longer be used. All event
* handlers and tool handlers are cleared. To continue the conversation,
* use {@link CopilotClient.resumeSession} with the session ID.
* Session state on disk (conversation history, planning state, artifacts) is
* preserved, so the conversation can be resumed later by calling
* {@link CopilotClient.resumeSession} with the session ID. To permanently
* remove all session data including files on disk, use
* {@link CopilotClient.deleteSession} instead.
*
* @returns A promise that resolves when the session is destroyed
* After calling this method, the session object can no longer be used.
*
* @returns A promise that resolves when the session is closed
* @throws Error if the connection fails
*
* @example
* ```typescript
* // Clean up when done
* // Clean up when done — session can still be resumed later
* await session.destroy();
* ```
*/
Expand Down
14 changes: 10 additions & 4 deletions python/copilot/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,10 +320,14 @@ async def stop(self) -> list["StopError"]:
Stop the CLI server and close all active sessions.

This method performs graceful cleanup:
1. Destroys all active sessions
1. Closes all active sessions (releases in-memory resources)
2. Closes the JSON-RPC connection
3. Terminates the CLI server process (if spawned by this client)

Note: session data on disk is preserved, so sessions can be resumed
later. To permanently remove session data before stopping, call
:meth:`delete_session` for each session first.

Returns:
A list of StopError objects containing error messages that occurred
during cleanup. An empty list indicates all cleanup succeeded.
Expand Down Expand Up @@ -928,10 +932,12 @@ async def list_sessions(

async def delete_session(self, session_id: str) -> None:
"""
Delete a session permanently.
Permanently delete a session and all its data from disk, including
conversation history, planning state, and artifacts.

This permanently removes the session and all its conversation history.
The session cannot be resumed after deletion.
Unlike :meth:`CopilotSession.destroy`, which only releases in-memory
resources and preserves session data for later resumption, this method
is irreversible. The session cannot be resumed after deletion.

Args:
session_id: The ID of the session to delete.
Expand Down
15 changes: 10 additions & 5 deletions python/copilot/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,17 +476,22 @@ async def get_messages(self) -> list[SessionEvent]:

async def destroy(self) -> None:
"""
Destroy this session and release all associated resources.
Close this session and release all in-memory resources (event handlers,
tool handlers, permission handlers).

After calling this method, the session can no longer be used. All event
handlers and tool handlers are cleared. To continue the conversation,
use :meth:`CopilotClient.resume_session` with the session ID.
Session state on disk (conversation history, planning state, artifacts)
is preserved, so the conversation can be resumed later by calling
:meth:`CopilotClient.resume_session` with the session ID. To
permanently remove all session data including files on disk, use
:meth:`CopilotClient.delete_session` instead.

After calling this method, the session object can no longer be used.

Raises:
Exception: If the connection fails.

Example:
>>> # Clean up when done
>>> # Clean up when done — session can still be resumed later
>>> await session.destroy()
"""
await self._client.request("session.destroy", {"sessionId": self.session_id})
Expand Down
Loading