Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
100ca24
feat(nodejs): add infinite sessions configuration
jmoseley Jan 21, 2026
30b6a6f
feat(python): add infinite sessions configuration
jmoseley Jan 21, 2026
24fe4d7
feat(go): add infinite sessions configuration
jmoseley Jan 21, 2026
d10701b
feat(dotnet): add infinite sessions configuration
jmoseley Jan 21, 2026
8a85bc3
docs: document infinite sessions feature in all SDKs
jmoseley Jan 21, 2026
63e2f1a
docs: fix missing copilot. prefix in Go README example
jmoseley Jan 21, 2026
c3f3366
fix: wrap long line in Python docstring
jmoseley Jan 21, 2026
bfa9b72
chore: remove accidentally committed node_modules, add .gitignore
jmoseley Jan 21, 2026
128a95b
Merge main into jm/infinite
jmoseley Jan 23, 2026
f823071
test(nodejs): add e2e tests for compaction and usage events
jmoseley Jan 23, 2026
21dcf41
test: skip compaction trigger test in CI
jmoseley Jan 23, 2026
8d147eb
fix(dotnet): fix syntax errors from merge conflict resolution
jmoseley Jan 23, 2026
a75c1f4
test: remove usage event tests (not part of this PR)
jmoseley Jan 23, 2026
b6fa81c
test: enable compaction test in CI
jmoseley Jan 23, 2026
c334859
fix(dotnet): change records to internal for JSON serializer
jmoseley Jan 23, 2026
b49dcc6
test: skip compaction trigger test in CI until CLI is released
jmoseley Jan 23, 2026
a318f06
test: revert skip - will update after CLI release
jmoseley Jan 23, 2026
5694a85
test: add compaction e2e tests for all SDK flavors
jmoseley Jan 23, 2026
701e463
Merge origin/main into jm/infinite
jmoseley Jan 24, 2026
e1b198a
fix: use %v format for float64 tokensRemoved in Go test
jmoseley Jan 24, 2026
0910f53
chore: update @github/copilot CLI to 0.0.394
jmoseley Jan 24, 2026
501fe8d
style: format compaction tests for all SDKs
jmoseley Jan 24, 2026
d4da3dc
fix: use dict instead of Dict in Python SDK
jmoseley Jan 24, 2026
e924316
fix: correct SessionEventType import in Python compaction test
jmoseley Jan 24, 2026
b6a2edd
feat(go): add Float64 helper function for pointer values
jmoseley Jan 24, 2026
814a40f
test: skip .NET compaction tests due to Windows CI proxy timing issues
jmoseley Jan 24, 2026
bc261a3
test: remove .NET compaction tests causing Windows CI fixture issues
jmoseley Jan 24, 2026
c02ec51
Revert "test: remove .NET compaction tests causing Windows CI fixture…
jmoseley Jan 24, 2026
1650316
test: enable .NET compaction tests (remove Skip attributes)
jmoseley Jan 24, 2026
0771de0
fix: increase proxy timeout to 30s on Windows
jmoseley Jan 24, 2026
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
44 changes: 43 additions & 1 deletion dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,11 @@ Create a new conversation session.
- `ExcludedTools` - List of tool names to disable
- `Provider` - Custom API provider configuration (BYOK)
- `Streaming` - Enable streaming of response chunks (default: false)
- `InfiniteSessions` - Configure automatic context compaction (see below)

##### `ResumeSessionAsync(string sessionId, ResumeSessionConfig? config = null): Task<CopilotSession>`

Resume an existing session.
Resume an existing session. Returns the session with `WorkspacePath` populated if infinite sessions were enabled.

##### `PingAsync(string? message = null): Task<PingResponse>`

Expand All @@ -127,6 +128,7 @@ Represents a single conversation session.
#### Properties

- `SessionId` - The unique identifier for this session
- `WorkspacePath` - Path to the session workspace directory when infinite sessions are enabled. Contains `checkpoints/`, `plan.md`, and `files/` subdirectories. Null if infinite sessions are disabled.

#### Methods

Expand Down Expand Up @@ -256,6 +258,46 @@ When `Streaming = true`:

Note: `AssistantMessageEvent` and `AssistantReasoningEvent` (final events) are always sent regardless of streaming setting.

## Infinite Sessions

By default, sessions use **infinite sessions** which automatically manage context window limits through background compaction and persist state to a workspace directory.

```csharp
// Default: infinite sessions enabled with default thresholds
var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-5"
});

// Access the workspace path for checkpoints and files
Console.WriteLine(session.WorkspacePath);
// => ~/.copilot/session-state/{sessionId}/

// Custom thresholds
var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-5",
InfiniteSessions = new InfiniteSessionConfig
{
Enabled = true,
BackgroundCompactionThreshold = 0.80, // Start compacting at 80% context usage
BufferExhaustionThreshold = 0.95 // Block at 95% until compaction completes
}
});

// Disable infinite sessions
var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-5",
InfiniteSessions = new InfiniteSessionConfig { Enabled = false }
});
```

When enabled, sessions emit compaction events:

- `SessionCompactionStartEvent` - Background compaction started
- `SessionCompactionCompleteEvent` - Compaction finished (includes token counts)

## Advanced Usage

### Manual Server Control
Expand Down
16 changes: 10 additions & 6 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,12 +344,13 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig? config = nul
config?.CustomAgents,
config?.ConfigDir,
config?.SkillDirectories,
config?.DisabledSkills);
config?.DisabledSkills,
config?.InfiniteSessions);

var response = await connection.Rpc.InvokeWithCancellationAsync<CreateSessionResponse>(
"session.create", [request], cancellationToken);

var session = new CopilotSession(response.SessionId, connection.Rpc);
var session = new CopilotSession(response.SessionId, connection.Rpc, response.WorkspacePath);
session.RegisterTools(config?.Tools ?? []);
if (config?.OnPermissionRequest != null)
{
Expand Down Expand Up @@ -406,7 +407,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
var response = await connection.Rpc.InvokeWithCancellationAsync<ResumeSessionResponse>(
"session.resume", [request], cancellationToken);

var session = new CopilotSession(response.SessionId, connection.Rpc);
var session = new CopilotSession(response.SessionId, connection.Rpc, response.WorkspacePath);
session.RegisterTools(config?.Tools ?? []);
if (config?.OnPermissionRequest != null)
{
Expand Down Expand Up @@ -929,7 +930,8 @@ private record CreateSessionRequest(
List<CustomAgentConfig>? CustomAgents,
string? ConfigDir,
List<string>? SkillDirectories,
List<string>? DisabledSkills);
List<string>? DisabledSkills,
InfiniteSessionConfig? InfiniteSessions);

private record ToolDefinition(
string Name,
Expand All @@ -941,7 +943,8 @@ public static ToolDefinition FromAIFunction(AIFunction function)
}

private record CreateSessionResponse(
string SessionId);
string SessionId,
string? WorkspacePath);

private record ResumeSessionRequest(
string SessionId,
Expand All @@ -955,7 +958,8 @@ private record ResumeSessionRequest(
List<string>? DisabledSkills);

private record ResumeSessionResponse(
string SessionId);
string SessionId,
string? WorkspacePath);

private record GetLastSessionIdResponse(
string? SessionId);
Expand Down
13 changes: 12 additions & 1 deletion dotnet/src/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,29 @@ public class CopilotSession : IAsyncDisposable
/// <value>A string that uniquely identifies this session.</value>
public string SessionId { get; }

/// <summary>
/// Gets the path to the session workspace directory when infinite sessions are enabled.
/// </summary>
/// <value>
/// The path to the workspace containing checkpoints/, plan.md, and files/ subdirectories,
/// or null if infinite sessions are disabled.
/// </value>
public string? WorkspacePath { get; }

/// <summary>
/// Initializes a new instance of the <see cref="CopilotSession"/> class.
/// </summary>
/// <param name="sessionId">The unique identifier for this session.</param>
/// <param name="rpc">The JSON-RPC connection to the Copilot CLI.</param>
/// <param name="workspacePath">The workspace path if infinite sessions are enabled.</param>
/// <remarks>
/// This constructor is internal. Use <see cref="CopilotClient.CreateSessionAsync"/> to create sessions.
/// </remarks>
internal CopilotSession(string sessionId, JsonRpc rpc)
internal CopilotSession(string sessionId, JsonRpc rpc, string? workspacePath = null)
{
SessionId = sessionId;
_rpc = rpc;
WorkspacePath = workspacePath;
}

/// <summary>
Expand Down
36 changes: 36 additions & 0 deletions dotnet/src/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,36 @@ public class CustomAgentConfig
public bool? Infer { get; set; }
}

/// <summary>
/// Configuration for infinite sessions with automatic context compaction and workspace persistence.
/// When enabled, sessions automatically manage context window limits through background compaction
/// and persist state to a workspace directory.
/// </summary>
public class InfiniteSessionConfig
{
/// <summary>
/// Whether infinite sessions are enabled. Default: true
/// </summary>
[JsonPropertyName("enabled")]
public bool? Enabled { get; set; }

/// <summary>
/// Context utilization threshold (0.0-1.0) at which background compaction starts.
/// Compaction runs asynchronously, allowing the session to continue processing.
/// Default: 0.80
/// </summary>
[JsonPropertyName("backgroundCompactionThreshold")]
public double? BackgroundCompactionThreshold { get; set; }

/// <summary>
/// Context utilization threshold (0.0-1.0) at which the session blocks until compaction completes.
/// This prevents context overflow when compaction hasn't finished in time.
/// Default: 0.95
/// </summary>
[JsonPropertyName("bufferExhaustionThreshold")]
public double? BufferExhaustionThreshold { get; set; }
}

public class SessionConfig
{
public string? SessionId { get; set; }
Expand Down Expand Up @@ -347,6 +377,12 @@ public class SessionConfig
/// List of skill names to disable.
/// </summary>
public List<string>? DisabledSkills { get; set; }

/// <summary>
/// Infinite session configuration for persistent workspaces and automatic compaction.
/// When enabled (default), sessions automatically manage context limits and persist state.
/// </summary>
public InfiniteSessionConfig? InfiniteSessions { get; set; }
}

public class ResumeSessionConfig
Expand Down
38 changes: 38 additions & 0 deletions go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,44 @@ When `Streaming: true`:

Note: `assistant.message` and `assistant.reasoning` (final events) are always sent regardless of streaming setting.

## Infinite Sessions

By default, sessions use **infinite sessions** which automatically manage context window limits through background compaction and persist state to a workspace directory.

```go
// Default: infinite sessions enabled with default thresholds
session, _ := client.CreateSession(&copilot.SessionConfig{
Model: "gpt-5",
})

// Access the workspace path for checkpoints and files
fmt.Println(session.WorkspacePath())
// => ~/.copilot/session-state/{sessionId}/

// Custom thresholds
session, _ := client.CreateSession(&copilot.SessionConfig{
Model: "gpt-5",
InfiniteSessions: &copilot.InfiniteSessionConfig{
Enabled: copilot.Bool(true),
BackgroundCompactionThreshold: Float64(0.80), // Start compacting at 80% context usage
BufferExhaustionThreshold: Float64(0.95), // Block at 95% until compaction completes
Comment thread
jmoseley marked this conversation as resolved.
Outdated
},
})

// Disable infinite sessions
session, _ := client.CreateSession(&copilot.SessionConfig{
Model: "gpt-5",
InfiniteSessions: &copilot.InfiniteSessionConfig{
Enabled: copilot.Bool(false),
},
})
```

When enabled, sessions emit compaction events:

- `session.compaction_start` - Background compaction started
- `session.compaction_complete` - Compaction finished (includes token counts)

## Transport Modes

### stdio (Default)
Expand Down
22 changes: 20 additions & 2 deletions go/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,20 @@ func (c *Client) CreateSession(config *SessionConfig) (*Session, error) {
if len(config.DisabledSkills) > 0 {
params["disabledSkills"] = config.DisabledSkills
}
// Add infinite sessions configuration
if config.InfiniteSessions != nil {
infiniteSessions := make(map[string]interface{})
if config.InfiniteSessions.Enabled != nil {
infiniteSessions["enabled"] = *config.InfiniteSessions.Enabled
}
if config.InfiniteSessions.BackgroundCompactionThreshold != nil {
infiniteSessions["backgroundCompactionThreshold"] = *config.InfiniteSessions.BackgroundCompactionThreshold
}
if config.InfiniteSessions.BufferExhaustionThreshold != nil {
infiniteSessions["bufferExhaustionThreshold"] = *config.InfiniteSessions.BufferExhaustionThreshold
Comment thread
jmoseley marked this conversation as resolved.
}
params["infiniteSessions"] = infiniteSessions
}
}

result, err := c.client.Request("session.create", params)
Expand All @@ -556,7 +570,9 @@ func (c *Client) CreateSession(config *SessionConfig) (*Session, error) {
return nil, fmt.Errorf("invalid response: missing sessionId")
}

session := NewSession(sessionID, c.client)
workspacePath, _ := result["workspacePath"].(string)

session := NewSession(sessionID, c.client, workspacePath)

if config != nil {
session.registerTools(config.Tools)
Expand Down Expand Up @@ -692,7 +708,9 @@ func (c *Client) ResumeSessionWithOptions(sessionID string, config *ResumeSessio
return nil, fmt.Errorf("invalid response: missing sessionId")
}

session := NewSession(resumedSessionID, c.client)
workspacePath, _ := result["workspacePath"].(string)

session := NewSession(resumedSessionID, c.client, workspacePath)
if config != nil {
session.registerTools(config.Tools)
if config.OnPermissionRequest != nil {
Expand Down
19 changes: 14 additions & 5 deletions go/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type sessionHandler struct {
type Session struct {
// SessionID is the unique identifier for this session.
SessionID string
workspacePath string
client *JSONRPCClient
handlers []sessionHandler
nextHandlerID uint64
Expand All @@ -57,16 +58,24 @@ type Session struct {
permissionMux sync.RWMutex
}

// WorkspacePath returns the path to the session workspace directory when infinite
// sessions are enabled. Contains checkpoints/, plan.md, and files/ subdirectories.
// Returns empty string if infinite sessions are disabled.
func (s *Session) WorkspacePath() string {
return s.workspacePath
}

// NewSession creates a new session wrapper with the given session ID and client.
//
// Note: This function is primarily for internal use. Use [Client.CreateSession]
// to create sessions with proper initialization.
func NewSession(sessionID string, client *JSONRPCClient) *Session {
func NewSession(sessionID string, client *JSONRPCClient, workspacePath string) *Session {
return &Session{
SessionID: sessionID,
client: client,
handlers: make([]sessionHandler, 0),
toolHandlers: make(map[string]ToolHandler),
SessionID: sessionID,
workspacePath: workspacePath,
client: client,
handlers: make([]sessionHandler, 0),
toolHandlers: make(map[string]ToolHandler),
}
}

Expand Down
17 changes: 17 additions & 0 deletions go/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,20 @@ type CustomAgentConfig struct {
Infer *bool `json:"infer,omitempty"`
}

// InfiniteSessionConfig configures infinite sessions with automatic context compaction
// and workspace persistence. When enabled, sessions automatically manage context window
// limits through background compaction and persist state to a workspace directory.
type InfiniteSessionConfig struct {
// Enabled controls whether infinite sessions are enabled (default: true)
Enabled *bool
// BackgroundCompactionThreshold is the context utilization (0.0-1.0) at which
// background compaction starts. Default: 0.80
BackgroundCompactionThreshold *float64
// BufferExhaustionThreshold is the context utilization (0.0-1.0) at which
// the session blocks until compaction completes. Default: 0.95
BufferExhaustionThreshold *float64
}

// SessionConfig configures a new session
type SessionConfig struct {
// SessionID is an optional custom session ID
Expand Down Expand Up @@ -167,6 +181,9 @@ type SessionConfig struct {
SkillDirectories []string
// DisabledSkills is a list of skill names to disable
DisabledSkills []string
// InfiniteSessions configures infinite sessions for persistent workspaces and automatic compaction.
// When enabled (default), sessions automatically manage context limits and persist state.
InfiniteSessions *InfiniteSessionConfig
}

// Tool describes a caller-implemented tool that can be invoked by Copilot
Expand Down
Loading
Loading