Skip to content

Commit 62ffcfe

Browse files
szabta89Copilot
andauthored
docs: add session limits guidance (#1856)
* docs: add session limits guidance Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: clarify session limits soft cap Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 1047dfb commit 62ffcfe

4 files changed

Lines changed: 219 additions & 0 deletions

File tree

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Guides for building with the SDK's capabilities.
4646
* [MCP Servers](./features/mcp.md): integrate Model Context Protocol servers
4747
* [Skills](./features/skills.md): load reusable prompt modules
4848
* [Plugin Directories](./features/plugin-directories.md): bundle skills, hooks, MCP servers, and agents as a single loadable plugin
49+
* [Session limits](./features/session-limits.md): set an AI Credits budget for a session
4950
* [Image Input](./features/image-input.md): send images as attachments
5051
* [Streaming Events](./features/streaming-events.md): real-time event reference
5152
* [Steering & Queueing](./features/steering-and-queueing.md): message delivery modes

docs/features/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ These guides cover the capabilities you can add to your Copilot SDK application.
1515
| [MCP Servers](./mcp.md) | Integrate Model Context Protocol servers for external tool access |
1616
| [Skills](./skills.md) | Load reusable prompt modules from directories |
1717
| [Plugin Directories](./plugin-directories.md) | Bundle skills, hooks, MCP servers, and agents as a single loadable plugin |
18+
| [Session limits](./session-limits.md) | Set an AI Credits budget for a session and observe budget events |
1819
| [Image Input](./image-input.md) | Send images to sessions as attachments |
1920
| [Streaming Events](./streaming-events.md) | Subscribe to real-time session events (40+ event types) |
2021
| [Steering & Queueing](./steering-and-queueing.md) | Control message delivery—immediate steering vs. sequential queueing |

docs/features/session-limits.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Session limits
2+
3+
Session limits let an application set an AI Credits budget for a Copilot session. Use `sessionLimits` when creating or resuming a session to set a soft cap for the current accounting window.
4+
5+
## Configure a session limit
6+
7+
Set `maxAiCredits` to the AI Credits soft cap for the session's current accounting window. Usage is checked after model calls return, so one response can exceed the configured value before the runtime blocks the next model call. The SDK forwards this value to the Copilot CLI when it creates or resumes the session.
8+
9+
<details open>
10+
<summary><strong>TypeScript</strong></summary>
11+
12+
<!-- docs-validate: skip -->
13+
14+
```typescript
15+
const session = await client.createSession({
16+
onPermissionRequest: approveAll,
17+
sessionLimits: {
18+
maxAiCredits: 30,
19+
},
20+
});
21+
22+
const resumed = await client.resumeSession(session.sessionId, {
23+
onPermissionRequest: approveAll,
24+
sessionLimits: {
25+
maxAiCredits: 30,
26+
},
27+
});
28+
```
29+
30+
</details>
31+
<details>
32+
<summary><strong>Python</strong></summary>
33+
34+
<!-- docs-validate: skip -->
35+
36+
```python
37+
session = await client.create_session(
38+
on_permission_request=PermissionHandler.approve_all,
39+
session_limits={
40+
"max_ai_credits": 30,
41+
},
42+
)
43+
44+
resumed = await client.resume_session(
45+
session.session_id,
46+
on_permission_request=PermissionHandler.approve_all,
47+
session_limits={
48+
"max_ai_credits": 30,
49+
},
50+
)
51+
```
52+
53+
</details>
54+
<details>
55+
<summary><strong>Go</strong></summary>
56+
57+
<!-- docs-validate: skip -->
58+
59+
```go
60+
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
61+
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
62+
SessionLimits: &rpc.SessionLimitsConfig{
63+
MaxAiCredits: copilot.Float64(30),
64+
},
65+
})
66+
67+
resumed, err := client.ResumeSession(ctx, session.SessionID, &copilot.ResumeSessionConfig{
68+
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
69+
SessionLimits: &rpc.SessionLimitsConfig{
70+
MaxAiCredits: copilot.Float64(30),
71+
},
72+
})
73+
```
74+
75+
</details>
76+
<details>
77+
<summary><strong>.NET</strong></summary>
78+
79+
<!-- docs-validate: skip -->
80+
81+
```csharp
82+
var session = await client.CreateSessionAsync(new SessionConfig
83+
{
84+
OnPermissionRequest = PermissionHandler.ApproveAll,
85+
SessionLimits = new SessionLimitsConfig
86+
{
87+
MaxAiCredits = 30,
88+
},
89+
});
90+
91+
var resumed = await client.ResumeSessionAsync(session.SessionId, new ResumeSessionConfig
92+
{
93+
OnPermissionRequest = PermissionHandler.ApproveAll,
94+
SessionLimits = new SessionLimitsConfig
95+
{
96+
MaxAiCredits = 30,
97+
},
98+
});
99+
```
100+
101+
</details>
102+
<details>
103+
<summary><strong>Java</strong></summary>
104+
105+
<!-- docs-validate: skip -->
106+
107+
```java
108+
CopilotSession session = client
109+
.createSession(new SessionConfig()
110+
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
111+
.setSessionLimits(new SessionLimitsConfig(30.0)))
112+
.get();
113+
114+
CopilotSession resumed = client
115+
.resumeSession(session.getSessionId(), new ResumeSessionConfig()
116+
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
117+
.setSessionLimits(new SessionLimitsConfig(30.0)))
118+
.get();
119+
```
120+
121+
</details>
122+
<details>
123+
<summary><strong>Rust</strong></summary>
124+
125+
<!-- docs-validate: skip -->
126+
127+
```rust
128+
let limits = SessionLimitsConfig {
129+
max_ai_credits: Some(30.0),
130+
};
131+
132+
let session = client
133+
.create_session(
134+
SessionConfig::new()
135+
.approve_all_permissions()
136+
.with_session_limits(limits.clone()),
137+
)
138+
.await?;
139+
140+
let resumed = client
141+
.resume_session(
142+
ResumeSessionConfig::new(session.id().clone())
143+
.approve_all_permissions()
144+
.with_session_limits(limits),
145+
)
146+
.await?;
147+
```
148+
149+
</details>
150+
151+
## Observe budget events
152+
153+
Applications can subscribe to session events to update UI when the soft cap changes or the session reaches the exhausted-budget flow.
154+
155+
| Event type | When it is emitted | Important fields |
156+
|---|---|---|
157+
| `session.session_limits_changed` | Active session limits changed. A `null` `sessionLimits` value means no limits are active. | `sessionLimits.maxAiCredits?` |
158+
| `session.usage_checkpoint` | The runtime records durable aggregate usage for resume and accounting. | `totalNanoAiu`, `totalPremiumRequests?` |
159+
| `session_limits_exhausted.requested` | The session reached the exhausted-budget flow and needs a user decision before continuing. | `requestId`, `maxAiCredits`, `usedAiCredits` |
160+
| `session_limits_exhausted.completed` | The exhausted-limit prompt was resolved. | `requestId`, `response.action`, `response.additionalAiCredits?`, `response.maxAiCredits?` |
161+
162+
Use the generated event types for the SDK language you are using. For example, TypeScript narrows by `event.type`:
163+
164+
```typescript
165+
session.on((event) => {
166+
if (event.type === "session_limits_exhausted.requested") {
167+
showBudgetDialog({
168+
requestId: event.data.requestId,
169+
maxAiCredits: event.data.maxAiCredits,
170+
usedAiCredits: event.data.usedAiCredits,
171+
});
172+
}
173+
});
174+
```

docs/features/streaming-events.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,24 @@ 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.session_limits_changed`
476+
477+
Session limits changed for the current accounting window. A `null` `sessionLimits` value means no limits are active.
478+
479+
| Data Field | Type | Required | Description |
480+
|------------|------|----------|-------------|
481+
| `sessionLimits` | `SessionLimitsConfig \| null` || Current session limits, or `null` when no limits are active |
482+
| `sessionLimits.maxAiCredits` | `number` | | Maximum AI Credits allowed across the session's current accounting window |
483+
484+
### `session.usage_checkpoint`
485+
486+
Durable aggregate usage checkpoint used to reconstruct accounting when a session is resumed.
487+
488+
| Data Field | Type | Required | Description |
489+
|------------|------|----------|-------------|
490+
| `totalNanoAiu` | `number` || Session-wide accumulated nano-AI units cost at checkpoint time |
491+
| `totalPremiumRequests` | `number` | | Total number of premium API requests used at checkpoint time |
492+
475493
### `session.task_complete`
476494

477495
The agent has completed its assigned task.
@@ -721,6 +739,27 @@ Ephemeral. A queued command was resolved.
721739
|------------|------|----------|-------------|
722740
| `requestId` | `string` || Matches the corresponding `command.queued` |
723741

742+
### `session_limits_exhausted.requested`
743+
744+
Ephemeral. The current session budget was exhausted and the runtime needs a user decision before continuing.
745+
746+
| Data Field | Type | Required | Description |
747+
|------------|------|----------|-------------|
748+
| `requestId` | `string` || Use this ID when responding to the pending exhausted-limit request |
749+
| `maxAiCredits` | `number` || Configured max AI Credits for the current accounting window |
750+
| `usedAiCredits` | `number` || AI Credits already consumed in the current accounting window |
751+
752+
### `session_limits_exhausted.completed`
753+
754+
Ephemeral. A pending exhausted-limit request was resolved.
755+
756+
| Data Field | Type | Required | Description |
757+
|------------|------|----------|-------------|
758+
| `requestId` | `string` || Matches the corresponding `session_limits_exhausted.requested` event |
759+
| `response.action` | `"add" \| "set" \| "unset" \| "cancel"` || Action selected for the exhausted-limit request |
760+
| `response.additionalAiCredits` | `number` | | AI Credits to add to the current max when `response.action` is `"add"` |
761+
| `response.maxAiCredits` | `number` | | New absolute max AI Credits when `response.action` is `"set"` |
762+
724763
## Quick reference: agentic turn flow
725764

726765
A typical agentic turn emits events in this order:
@@ -773,6 +812,8 @@ session.idle → Ready for next message (ephemeral)
773812
| `session.title_changed` || Session | `title` |
774813
| `session.context_changed` | | Session | `cwd`, `gitRoot?`, `repository?`, `branch?` |
775814
| `session.usage_info` || Session | `tokenLimit`, `currentTokens`, `messagesLength` |
815+
| `session.session_limits_changed` | | Session | `sessionLimits` |
816+
| `session.usage_checkpoint` | | Session | `totalNanoAiu`, `totalPremiumRequests?` |
776817
| `session.task_complete` | | Session | `summary?` |
777818
| `session.shutdown` | | Session | `shutdownType`, `codeChanges`, `modelMetrics` |
778819
| `permission.requested` || Permission | `requestId`, `permissionRequest` |
@@ -794,5 +835,7 @@ session.idle → Ready for next message (ephemeral)
794835
| `external_tool.completed` || External Tool | `requestId` |
795836
| `command.queued` || Command | `requestId`, `command` |
796837
| `command.completed` || Command | `requestId` |
838+
| `session_limits_exhausted.requested` || Session | `requestId`, `maxAiCredits`, `usedAiCredits` |
839+
| `session_limits_exhausted.completed` || Session | `requestId`, `response.action` |
797840
| `exit_plan_mode.requested` || Plan Mode | `requestId`, `summary`, `planContent`, `actions` |
798841
| `exit_plan_mode.completed` || Plan Mode | `requestId` |

0 commit comments

Comments
 (0)