Skip to content
Merged
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
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Guides for building with the SDK's capabilities.
* [MCP Servers](./features/mcp.md): integrate Model Context Protocol servers
* [Skills](./features/skills.md): load reusable prompt modules
* [Plugin Directories](./features/plugin-directories.md): bundle skills, hooks, MCP servers, and agents as a single loadable plugin
* [Session limits](./features/session-limits.md): set an AI Credits budget for a session
* [Image Input](./features/image-input.md): send images as attachments
* [Streaming Events](./features/streaming-events.md): real-time event reference
* [Steering & Queueing](./features/steering-and-queueing.md): message delivery modes
Expand Down
1 change: 1 addition & 0 deletions docs/features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ These guides cover the capabilities you can add to your Copilot SDK application.
| [MCP Servers](./mcp.md) | Integrate Model Context Protocol servers for external tool access |
| [Skills](./skills.md) | Load reusable prompt modules from directories |
| [Plugin Directories](./plugin-directories.md) | Bundle skills, hooks, MCP servers, and agents as a single loadable plugin |
| [Session limits](./session-limits.md) | Set an AI Credits budget for a session and observe budget events |
| [Image Input](./image-input.md) | Send images to sessions as attachments |
| [Streaming Events](./streaming-events.md) | Subscribe to real-time session events (40+ event types) |
| [Steering & Queueing](./steering-and-queueing.md) | Control message delivery—immediate steering vs. sequential queueing |
Expand Down
174 changes: 174 additions & 0 deletions docs/features/session-limits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Session limits

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.

## Configure a session limit

Comment thread
szabta89 marked this conversation as resolved.
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.

<details open>
<summary><strong>TypeScript</strong></summary>

<!-- docs-validate: skip -->

```typescript
const session = await client.createSession({
onPermissionRequest: approveAll,
sessionLimits: {
maxAiCredits: 30,
},
});

const resumed = await client.resumeSession(session.sessionId, {
onPermissionRequest: approveAll,
sessionLimits: {
maxAiCredits: 30,
},
});
```

</details>
<details>
<summary><strong>Python</strong></summary>

<!-- docs-validate: skip -->

```python
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
session_limits={
"max_ai_credits": 30,
},
)

resumed = await client.resume_session(
session.session_id,
on_permission_request=PermissionHandler.approve_all,
session_limits={
"max_ai_credits": 30,
},
)
```

</details>
<details>
<summary><strong>Go</strong></summary>

<!-- docs-validate: skip -->

```go
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
SessionLimits: &rpc.SessionLimitsConfig{
MaxAiCredits: copilot.Float64(30),
},
})

resumed, err := client.ResumeSession(ctx, session.SessionID, &copilot.ResumeSessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
SessionLimits: &rpc.SessionLimitsConfig{
MaxAiCredits: copilot.Float64(30),
},
})
```

</details>
<details>
<summary><strong>.NET</strong></summary>

<!-- docs-validate: skip -->

```csharp
var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
SessionLimits = new SessionLimitsConfig
{
MaxAiCredits = 30,
},
});

var resumed = await client.ResumeSessionAsync(session.SessionId, new ResumeSessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
SessionLimits = new SessionLimitsConfig
{
MaxAiCredits = 30,
},
});
```

</details>
<details>
<summary><strong>Java</strong></summary>

<!-- docs-validate: skip -->

```java
CopilotSession session = client
.createSession(new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setSessionLimits(new SessionLimitsConfig(30.0)))
.get();

CopilotSession resumed = client
.resumeSession(session.getSessionId(), new ResumeSessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setSessionLimits(new SessionLimitsConfig(30.0)))
.get();
```

</details>
<details>
<summary><strong>Rust</strong></summary>

<!-- docs-validate: skip -->

```rust
let limits = SessionLimitsConfig {
max_ai_credits: Some(30.0),
};

let session = client
.create_session(
SessionConfig::new()
.approve_all_permissions()
.with_session_limits(limits.clone()),
)
.await?;

let resumed = client
.resume_session(
ResumeSessionConfig::new(session.id().clone())
.approve_all_permissions()
.with_session_limits(limits),
)
.await?;
```

</details>

## Observe budget events

Applications can subscribe to session events to update UI when the soft cap changes or the session reaches the exhausted-budget flow.

| Event type | When it is emitted | Important fields |
|---|---|---|
| `session.session_limits_changed` | Active session limits changed. A `null` `sessionLimits` value means no limits are active. | `sessionLimits.maxAiCredits?` |
| `session.usage_checkpoint` | The runtime records durable aggregate usage for resume and accounting. | `totalNanoAiu`, `totalPremiumRequests?` |
| `session_limits_exhausted.requested` | The session reached the exhausted-budget flow and needs a user decision before continuing. | `requestId`, `maxAiCredits`, `usedAiCredits` |
| `session_limits_exhausted.completed` | The exhausted-limit prompt was resolved. | `requestId`, `response.action`, `response.additionalAiCredits?`, `response.maxAiCredits?` |

Use the generated event types for the SDK language you are using. For example, TypeScript narrows by `event.type`:

```typescript
session.on((event) => {
if (event.type === "session_limits_exhausted.requested") {
showBudgetDialog({
requestId: event.data.requestId,
maxAiCredits: event.data.maxAiCredits,
usedAiCredits: event.data.usedAiCredits,
});
}
});
```
43 changes: 43 additions & 0 deletions docs/features/streaming-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,24 @@ Ephemeral. Context window utilization snapshot.
| `currentTokens` | `number` | ✅ | Current tokens in the context window |
| `messagesLength` | `number` | ✅ | Current message count in the conversation |

### `session.session_limits_changed`

Session limits changed for the current accounting window. A `null` `sessionLimits` value means no limits are active.

| Data Field | Type | Required | Description |
|------------|------|----------|-------------|
| `sessionLimits` | `SessionLimitsConfig \| null` | ✅ | Current session limits, or `null` when no limits are active |
| `sessionLimits.maxAiCredits` | `number` | | Maximum AI Credits allowed across the session's current accounting window |

### `session.usage_checkpoint`

Durable aggregate usage checkpoint used to reconstruct accounting when a session is resumed.

| Data Field | Type | Required | Description |
|------------|------|----------|-------------|
| `totalNanoAiu` | `number` | ✅ | Session-wide accumulated nano-AI units cost at checkpoint time |
| `totalPremiumRequests` | `number` | | Total number of premium API requests used at checkpoint time |

### `session.task_complete`

The agent has completed its assigned task.
Expand Down Expand Up @@ -721,6 +739,27 @@ Ephemeral. A queued command was resolved.
|------------|------|----------|-------------|
| `requestId` | `string` | ✅ | Matches the corresponding `command.queued` |

### `session_limits_exhausted.requested`

Ephemeral. The current session budget was exhausted and the runtime needs a user decision before continuing.

| Data Field | Type | Required | Description |
|------------|------|----------|-------------|
| `requestId` | `string` | ✅ | Use this ID when responding to the pending exhausted-limit request |
| `maxAiCredits` | `number` | ✅ | Configured max AI Credits for the current accounting window |
| `usedAiCredits` | `number` | ✅ | AI Credits already consumed in the current accounting window |

### `session_limits_exhausted.completed`

Ephemeral. A pending exhausted-limit request was resolved.

| Data Field | Type | Required | Description |
|------------|------|----------|-------------|
| `requestId` | `string` | ✅ | Matches the corresponding `session_limits_exhausted.requested` event |
| `response.action` | `"add" \| "set" \| "unset" \| "cancel"` | ✅ | Action selected for the exhausted-limit request |
| `response.additionalAiCredits` | `number` | | AI Credits to add to the current max when `response.action` is `"add"` |
| `response.maxAiCredits` | `number` | | New absolute max AI Credits when `response.action` is `"set"` |

## Quick reference: agentic turn flow

A typical agentic turn emits events in this order:
Expand Down Expand Up @@ -773,6 +812,8 @@ session.idle → Ready for next message (ephemeral)
| `session.title_changed` | ✅ | Session | `title` |
| `session.context_changed` | | Session | `cwd`, `gitRoot?`, `repository?`, `branch?` |
| `session.usage_info` | ✅ | Session | `tokenLimit`, `currentTokens`, `messagesLength` |
| `session.session_limits_changed` | | Session | `sessionLimits` |
| `session.usage_checkpoint` | | Session | `totalNanoAiu`, `totalPremiumRequests?` |
| `session.task_complete` | | Session | `summary?` |
| `session.shutdown` | | Session | `shutdownType`, `codeChanges`, `modelMetrics` |
| `permission.requested` | ✅ | Permission | `requestId`, `permissionRequest` |
Expand All @@ -794,5 +835,7 @@ session.idle → Ready for next message (ephemeral)
| `external_tool.completed` | ✅ | External Tool | `requestId` |
| `command.queued` | ✅ | Command | `requestId`, `command` |
| `command.completed` | ✅ | Command | `requestId` |
| `session_limits_exhausted.requested` | ✅ | Session | `requestId`, `maxAiCredits`, `usedAiCredits` |
| `session_limits_exhausted.completed` | ✅ | Session | `requestId`, `response.action` |
| `exit_plan_mode.requested` | ✅ | Plan Mode | `requestId`, `summary`, `planContent`, `actions` |
| `exit_plan_mode.completed` | ✅ | Plan Mode | `requestId` |
Loading