Skip to content

Commit d8b8fe0

Browse files
szabta89Copilot
andcommitted
feat: add session limits support
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 6189f84 commit d8b8fe0

92 files changed

Lines changed: 4343 additions & 405 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/features/streaming-events.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,29 @@ Ephemeral. Context window utilization snapshot.
472472
| `currentTokens` | `number` || Current tokens in the context window |
473473
| `messagesLength` | `number` || Current message count in the conversation |
474474

475+
### `session.usage_checkpoint`
476+
477+
Durable usage checkpoint for reconstructing aggregate session accounting on resume.
478+
479+
| Data Field | Type | Required | Description |
480+
|------------|------|----------|-------------|
481+
| `totalNanoAiu` | `number` || Session-wide accumulated nano-AI units cost at checkpoint time |
482+
| `totalPremiumRequests` | `number` | | Total premium API requests used at checkpoint time |
483+
484+
### `session.session_limits_changed`
485+
486+
Emitted when session limits are set, changed, or cleared.
487+
488+
| Data Field | Type | Required | Description |
489+
|------------|------|----------|-------------|
490+
| `sessionLimits` | `SessionLimitsConfig \| null` || Current session limits, or `null` when no limits are active |
491+
492+
**`SessionLimitsConfig` fields:**
493+
494+
| Field | Type | Required | Description |
495+
|-------|------|----------|-------------|
496+
| `maxAiCredits` | `number` | | Maximum AI Credits allowed across the session's current accounting window |
497+
475498
### `session.task_complete`
476499

477500
The agent has completed its assigned task.
@@ -550,6 +573,33 @@ Ephemeral. A user input request was resolved.
550573
|------------|------|----------|-------------|
551574
| `requestId` | `string` || Matches the corresponding `user_input.requested` |
552575

576+
### `session_limits_exhausted.requested`
577+
578+
Ephemeral. The session's AI Credits budget is exhausted and the runtime needs a decision before continuing the blocked model request.
579+
580+
| Data Field | Type | Required | Description |
581+
|------------|------|----------|-------------|
582+
| `requestId` | `string` || Use this to respond via `session.ui.handlePendingSessionLimitsExhausted()` |
583+
| `maxAiCredits` | `number` || Configured max AI Credits for the current accounting window |
584+
| `usedAiCredits` | `number` || AI Credits already consumed in the current accounting window |
585+
586+
### `session_limits_exhausted.completed`
587+
588+
Ephemeral. A session limit exhaustion request was resolved.
589+
590+
| Data Field | Type | Required | Description |
591+
|------------|------|----------|-------------|
592+
| `requestId` | `string` || Matches the corresponding `session_limits_exhausted.requested` |
593+
| `response` | `SessionLimitsExhaustedResponse` || User-selected action for the exhausted limit |
594+
595+
**`SessionLimitsExhaustedResponse` fields:**
596+
597+
| Field | Type | Required | Description |
598+
|-------|------|----------|-------------|
599+
| `action` | `"add" \| "set" \| "unset" \| "cancel"` || How to handle the exhausted session limit |
600+
| `additionalAiCredits` | `number` | | AI Credits to add to the current max when `action` is `"add"` |
601+
| `maxAiCredits` | `number` | | New absolute max AI Credits when `action` is `"set"` |
602+
553603
### `elicitation.requested`
554604

555605
Ephemeral. The agent needs structured form input from the user (MCP elicitation protocol).
@@ -773,12 +823,16 @@ session.idle → Ready for next message (ephemeral)
773823
| `session.title_changed` || Session | `title` |
774824
| `session.context_changed` | | Session | `cwd`, `gitRoot?`, `repository?`, `branch?` |
775825
| `session.usage_info` || Session | `tokenLimit`, `currentTokens`, `messagesLength` |
826+
| `session.usage_checkpoint` | | Session | `totalNanoAiu`, `totalPremiumRequests?` |
827+
| `session.session_limits_changed` | | Session | `sessionLimits` |
776828
| `session.task_complete` | | Session | `summary?` |
777829
| `session.shutdown` | | Session | `shutdownType`, `codeChanges`, `modelMetrics` |
778830
| `permission.requested` || Permission | `requestId`, `permissionRequest` |
779831
| `permission.completed` || Permission | `requestId`, `result.kind` |
780832
| `user_input.requested` || User Input | `requestId`, `question`, `choices?` |
781833
| `user_input.completed` || User Input | `requestId` |
834+
| `session_limits_exhausted.requested` || User Input | `requestId`, `maxAiCredits`, `usedAiCredits` |
835+
| `session_limits_exhausted.completed` || User Input | `requestId`, `response` |
782836
| `elicitation.requested` || User Input | `requestId`, `message`, `requestedSchema` |
783837
| `elicitation.completed` || User Input | `requestId` |
784838
| `subagent.started` | | Sub-Agent | `toolCallId`, `agentName`, `agentDisplayName` |

dotnet/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ Create a new conversation session.
125125
- `Provider` - Custom API provider configuration (BYOK)
126126
- `Streaming` - Enable streaming of response chunks (default: false)
127127
- `InfiniteSessions` - Configure automatic context compaction (see below)
128+
- `SessionLimits` - Configure an AI credit budget for the session (see below)
128129
- `OnPermissionRequest` - Optional handler called before each tool execution to approve or deny it. When omitted, permission requests are emitted as events and left pending for manual resolution. Use `PermissionHandler.ApproveAll` to allow everything, or provide a custom function for fine-grained control. See [Permission Handling](#permission-handling) section.
129130
- `OnUserInputRequest` - Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section.
130131
- `Hooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section.
@@ -136,6 +137,7 @@ Resume an existing session. Returns the session with `WorkspacePath` populated i
136137
**ResumeSessionConfig:**
137138

138139
- `OnPermissionRequest` - Optional handler called before each tool execution to approve or deny it. See [Permission Handling](#permission-handling) section.
140+
- `SessionLimits` - Configure an AI credit budget for the resumed session.
139141

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

@@ -431,6 +433,23 @@ var session = await client.CreateSessionAsync(new SessionConfig
431433

432434
When `Memory` is left unset, no memory configuration is sent and the runtime default applies. In the default `CopilotClientMode.CopilotCli` the SDK leaves `Memory` unset so the runtime applies its own default, while `CopilotClientMode.Empty` defaults `Memory` to disabled unless you set it explicitly.
433435

436+
## Session Limits
437+
438+
Set an AI credit budget when creating or resuming a session, then update it later through the session limits API. Pass `null` to clear the current limit.
439+
440+
```csharp
441+
var session = await client.CreateSessionAsync(new SessionConfig
442+
{
443+
Model = "gpt-5",
444+
SessionLimits = new SessionLimitsConfig { MaxAiCredits = 10 }
445+
});
446+
447+
await session.Limits.SetLimitsAsync(new SessionLimitsConfig { MaxAiCredits = 20 });
448+
await session.Limits.SetLimitsAsync(null);
449+
```
450+
451+
The runtime emits limit-related events such as `SessionUsageCheckpointEvent`, `SessionSessionLimitsChangedEvent`, `SessionLimitsExhaustedRequestedEvent`, and `SessionLimitsExhaustedCompletedEvent`.
452+
434453
## Advanced Usage
435454

436455
### Manual Server Control

dotnet/src/Client.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
10261026
PluginDirectories: config.PluginDirectories,
10271027
LargeOutput: config.LargeOutput,
10281028
Memory: config.Memory,
1029+
SessionLimits: config.SessionLimits,
10291030
Canvases: config.Canvases,
10301031
RequestCanvasRenderer: config.RequestCanvasRenderer,
10311032
RequestExtensions: config.RequestExtensions,
@@ -1236,6 +1237,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
12361237
PluginDirectories: config.PluginDirectories,
12371238
LargeOutput: config.LargeOutput,
12381239
Memory: config.Memory,
1240+
SessionLimits: config.SessionLimits,
12391241
Canvases: config.Canvases,
12401242
RequestCanvasRenderer: config.RequestCanvasRenderer,
12411243
RequestExtensions: config.RequestExtensions,
@@ -2477,6 +2479,7 @@ internal record CreateSessionRequest(
24772479
IList<string>? PluginDirectories = null,
24782480
LargeToolOutputConfig? LargeOutput = null,
24792481
MemoryConfiguration? Memory = null,
2482+
SessionLimitsConfig? SessionLimits = null,
24802483
#pragma warning disable GHCP001
24812484
IList<CanvasDeclaration>? Canvases = null,
24822485
bool? RequestCanvasRenderer = null,
@@ -2572,6 +2575,7 @@ internal record ResumeSessionRequest(
25722575
IList<string>? PluginDirectories = null,
25732576
LargeToolOutputConfig? LargeOutput = null,
25742577
MemoryConfiguration? Memory = null,
2578+
SessionLimitsConfig? SessionLimits = null,
25752579
#pragma warning disable GHCP001
25762580
IList<CanvasDeclaration>? Canvases = null,
25772581
bool? RequestCanvasRenderer = null,
@@ -2658,6 +2662,7 @@ internal record HooksInvokeResponse(
26582662
[JsonSerializable(typeof(ModelCapabilitiesOverride))]
26592663
[JsonSerializable(typeof(ProviderConfig))]
26602664
[JsonSerializable(typeof(CapiSessionOptions))]
2665+
[JsonSerializable(typeof(SessionLimitsConfig))]
26612666
[JsonSerializable(typeof(NamedProviderConfig))]
26622667
[JsonSerializable(typeof(ProviderModelConfig))]
26632668
[JsonSerializable(typeof(ResumeSessionRequest))]

0 commit comments

Comments
 (0)