# 云会话

云会话通过 Mission Control 在 GitHub 托管的计算上运行 Copilot 任务。 当应用应创建远程执行的会话，而不是在用户的计算机或服务器上启动本地 Copilot CLI 会话时，请使用它们。

<!-- markdownlint-disable GHD046 GHD005 -->

<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

## 先决条件

在创建云会话之前，请确保：

* 用户拥有 Copilot 访问权限，并享有云代理使用权益。
* 会话可以使用用户令牌，也可以使用已登录的 Copilot CLI 身份向 GitHub 进行身份验证。
* 可以将会话与GitHub存储库相关联。 这在 SDK 类型定义中是可选的，但建议提供，以便 Mission Control 和云代理能够获得仓库上下文。
* 组织策略允许从云图面进行远程控制和查看会话。

## 创建云会话

设置创建会话 `cloud` 选项以创建云会话。 可以包含存储库元数据，以便将云会话与GitHub存储库相关联。

<!-- tabs:start -->

### TypeScript

```typescript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session = await client.createSession({
  onPermissionRequest: async () => ({ kind: "approve-once" }),
  cloud: {
    repository: {
      owner: "github",
      name: "copilot-sdk",
      branch: "main",
    },
  },
});
```

### Python

```python
from copilot import (
    CloudSessionOptions,
    CloudSessionRepository,
    CopilotClient,
    PermissionHandler,
)

client = CopilotClient()
await client.start()

session = await client.create_session(
    on_permission_request=PermissionHandler.approve_all,
    cloud=CloudSessionOptions(
        repository=CloudSessionRepository(
            owner="github",
            name="copilot-sdk",
            branch="main",
        )
    ),
)
```

### Go

```golang
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
    return err
}

session, err := client.CreateSession(ctx, &copilot.SessionConfig{
    Cloud: &copilot.CloudSessionOptions{
        Repository: &copilot.CloudSessionRepository{
            Owner:  "github",
            Name:   "copilot-sdk",
            Branch: "main",
        },
    },
    OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
        return &rpc.PermissionDecisionApproveOnce{}, nil
    },
})
_ = session
```

### .NET

```csharp
await using var client = new CopilotClient();

var session = await client.CreateSessionAsync(new SessionConfig
{
    Cloud = new CloudSessionOptions
    {
        Repository = new CloudSessionRepository
        {
            Owner = "github",
            Name = "copilot-sdk",
            Branch = "main",
        },
    },
    OnPermissionRequest = (req, inv) =>
        Task.FromResult(PermissionDecision.ApproveOnce()),
});
```

### Java

```java
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;

try (var client = new CopilotClient()) {
    client.start().get();

    var session = client.createSession(
        new SessionConfig()
            .setCloud(new CloudSessionOptions()
                .setRepository(new CloudSessionRepository()
                    .setOwner("github")
                    .setName("copilot-sdk")
                    .setBranch("main")))
            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
    ).get();
}
```

### Rust

```rust
use std::sync::Arc;
use github_copilot_sdk::{CloudSessionOptions, CloudSessionRepository, SessionConfig};
use github_copilot_sdk::handler::ApproveAllHandler;

let session = client.create_session(
    SessionConfig::default()
        .with_cloud(CloudSessionOptions::with_repository(
            CloudSessionRepository::new("github", "copilot-sdk").with_branch("main"),
        ))
        .with_permission_handler(Arc::new(ApproveAllHandler)),
).await?;
```

<!-- tabs:end -->

## 发送第一个提示词

云会话分两个阶段完成初始化：任务管控中心预留任务资源后，`createSession` 即完成解析，但远程 `copilot-agent` 工作进程还需耗时一两秒建立连接并发出 `session.start`。 若在此之前调用 `session.send`，运行时 `RemoteSession.send` 会抛出 `"Remote session is still starting"`；不过架构封装层采用即发即弃机制，会**静默捕获异常**，同时仍向代码返回全新的 `messageId` 实例。 提示在服务器端被丢弃，永远不会到达工作进程。

为了确保可靠发送，请在**发送前**订阅事件，并等待第一个 `session.start` 为 `producer` 的 `"copilot-agent"` 事件：

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

```typescript
import { CopilotClient, type CopilotSession } from "@github/copilot-sdk";

const client = new CopilotClient();
await client.start();

const session: CopilotSession = await client.createSession({
  streaming: true, // required for assistant.message_delta to fire
  cloud: { repository: { owner: "github", name: "copilot-sdk" } },
  onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Subscribe BEFORE sending so you don't miss the start event.
const ready = new Promise<void>((resolve) => {
  const off = session.on("session.start", (event) => {
    if (event.data?.producer === "copilot-agent") {
      off();
      resolve();
    }
  });
});

await ready;
await session.send({ prompt: "Summarize the README" });
```

一些说明：

* 在 `streaming: true` 上设置 `createSession`，从而使运行时发出 `assistant.message_delta` 事件。 如果没有它，你获得的唯一助手信号是最终 `assistant.message` 信号 -- 适合批量使用，但如果呈现实时 UI，聊天会呈现卡死状态。 请参阅“[流式处理会话事件](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/streaming-events)”。
* 仅**首次**`session.send`调用会受此竞态问题影响。 在同一会话中的后续发送都会正常进行，因为运行时会使 `hasSessionStarted` 在整个会话期间保持已设置状态。
* 为 `ready` Promise 配置超时（例如 60 秒），防止任务管控中心资源配置卡死造成你的应用永久阻塞。
* 同一模式适用于每个 SDK 语言 - 订阅 `session.start`、检查 `producer === "copilot-agent"`、然后调用 `send`。

## 访问 Mission Control 的 URL

云会话天然是远程的：一旦工作器连接，Mission Control 就会在 `https://github.com/copilot/tasks/{sessionId}` 发布该会话，而运行时会发出一个 `session.info` 事件，并附带该 URL。
**无需调用**`remote.enable()` — 该 API 仅用于将本地会话提升到任务控制。

通过订阅 `session.info` 并按 `infoType: "remote"` 筛选来捕获 URL：

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

```typescript
session.on("session.info", (event) => {
  if (event.data?.infoType === "remote" && event.data.url) {
    console.log("Open from web or mobile:", event.data.url);
    // For example, surface in your UI as a shareable link or QR code.
  }
});
```

`session.start` 完成后很快就会触发该事件。 如果你的渲染组件在事件触发完成后才完成挂载，请将 URL 和会话记录一同保存在应用状态中，并在重新挂载时恢复数据——运行时不会自行重新发出 `session.info`。

有关通过 `remote: true` 提升的本地会话的相同连接逻辑，请参阅 [远程会话](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/remote-sessions)。

## 存储库关联

`cloud.repository` 对象将云会话与GitHub存储库相关联：

| 领域       | 必选 | Description                             |
| -------- | -- | --------------------------------------- |
| `owner`  | 是的 | 存储库所有者或组织。                              |
| `name`   | 是的 | 存储库名称。                                  |
| `branch` | 否  | 要用于存储库上下文的分支。 省略它以允许运行时选择默认分支或当前存储库上下文。 |

存储库关联在 SDK 类型中是可选的，但只要应用知道目标存储库，就将其包含在内。 它有助于任务控制在正确的上下文中显示会话，并为云代理提供更清晰的起点。

当工作应从特定分支开始时使用 `branch` 。 如果你的应用依托拉取请求、工单分类流程或部署工作流创建会话，请传入和用户可见任务对应的分支。

## 恢复云会话

此选项 `cloud` 仅适用于创建新会话时。 若要恢复现有云会话，请使用 SDK 语言的标准恢复 API：

```typescript
const session = await client.resumeSession("session-id", {
  onPermissionRequest: async () => ({ kind: "approve-once" }),
});
```

请勿在恢复时再次传递 `cloud`。 保存的会话元数据表明该会话由云端支持，并按正常的会话恢复流程进行恢复。

## 组织策略和权限

当用户或组织无权执行云代理或组织级策略阻止流时，云会话创建可能会失败。 具体而言，云沙盒的策略可以防止客户端创建云任务。

发生这种情况时，运行时会报告 `"policy_blocked"` 云任务创建失败原因。 将此视为授权或策略结果，而不是暂时性基础结构故障。

在 TypeScript 中，在重试之前检查原因：

```typescript
try {
  await client.createSession({ cloud: { repository } });
} catch (error) {
  if ((error as { reason?: string }).reason === "policy_blocked") {
    // Show an admin-facing message or link to org policy settings.
  }
  throw error;
}
```

在 SDK 错误以不同方式表示的语言中，检查显示的错误原因或代码并显式处理 `"policy_blocked"` 。 如果不进行策略更改，则重试不会成功。

## 集成 ID 和路由

云会话使用派生自 `Copilot-Integration-Id` 环境变量的 `GITHUB_COPILOT_INTEGRATION_ID` 标头进行标记。 此集成 ID 由任务控制用于路由、归因和集成专属行为。

有关多用户服务器指南和完整的集成 ID 详细信息，请参阅 [多租户与服务器部署](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/setup/multi-tenancy)。

任务控制将 SDK 创建的云会话路由到 `copilot-developer-sandbox` 智能体数据域。 该名称是云端代理的内部路由标识，并不意味着该会话使用本地 Windows 沙盒环境。

## 高级：`COPILOT_MC_BASE_URL`

默认情况下，运行时会根据已配置的 Copilot API URL 推导出 Mission Control 的基础 URL。 仅当需要覆盖该任务控制终结点时才设置 `COPILOT_MC_BASE_URL`。

GitHub Enterprise Server 部署可能需要此操作。 在将其用于生产环境之前，请先与您的 GitHub 代表确认正确的值和支持状态。

```shell
COPILOT_MC_BASE_URL="https://example.com/agents"
```

## 云会话与远程会话

| Capability              | 远程会话                       | 云会话         |
| ----------------------- | -------------------------- | ----------- |
| 执行位置                    | 本地计算机或服务器                  | GitHub托管的计算 |
| 任务控制角色                  | 与 GitHub Web/mobile 共享本地会话 | 创建和路由托管会话   |
| SDK 选项                  |                            |             |
| `remote: true` 在客户端或会话中 |                            |             |
| `cloud: { ... }` 创建会话时  |                            |             |
| 恢复路径                    | 标准简历                       | 标准简历        |
| Windows 沙盒关系            | 无关                         | 无关          |

当会话需要在已运行 SDK 运行时的环境中执行，并且还需要能够从 Mission Control 访问时，请使用远程会话。 在GitHub托管的计算上执行会话时，请使用云会话。

## 故障排除

| 症状                                                                     | 可能的原因                                                             | 需要检查的事项                                                                                                     |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 云会话创建返回 `"policy_blocked"`                                             | 组织策略阻止从云流进行远程控制或查看                                                | 检查组织的 Copilot 策略和用户授权资格                                                                                     |
| 会话是在没有存储库上下文的情况下创建的                                                    |                                                                   |                                                                                                             |
| `cloud.repository` 已省略                                                 | 传递 `owner`、`name`，以及可选的 `branch`                                  |                                                                                                             |
| 恢复将忽略新 `cloud` 选项                                                      |                                                                   |                                                                                                             |
| `cloud` 仅适用于新会话                                                        | 正常恢复现有会话                                                          |                                                                                                             |
| 沙箱设置相关疑难问题                                                             | Windows沙盒和云会话是分开的                                                 | 不要将 `SANDBOX=true` 用于云端执行                                                                                   |
| `session.send` 使用 `messageId` 进行解析，但没有触发 `assistant.*` 事件，任务控制也不显示命令提示 | session.send 早于来自远程工作进程的 `session.start` 执行；运行时吞掉了该提示             | 在发送之前，等待第一个带有 `session.start` 的 `producer === "copilot-agent"` 事件。 请参阅[发送第一个提示词](#sending-the-first-prompt) |
| 尽管云端工作进程正在处理，实时界面却始终不更新                                                | 未在 `streaming` 上设置 `createSession`，因此只会发出最后一个 `assistant.message` | 在`streaming: true`上设置`createSession`，然后重新启动                                                                 |
| 云会话有效，但 UI 中未显示可共享 URL                                                 | 应用从未针对该 URL 订阅 `session.info`                                     | 订阅 `session.info` 并筛选 `infoType === "remote"`。 请参阅 [访问任务控制 URL](#accessing-the-mission-control-url)         |

## 另请参阅

* [远程会话](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/remote-sessions)：通过任务控制共享本地托管会话
* [流式处理会话事件](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/streaming-events)：订阅 `assistant.*` 增量以进行实时 UI 渲染
* [多租户与服务器部署](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/setup/multi-tenancy)：集成 ID 和服务器部署模式
* [Authentication](/zh/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/auth)：为 SDK 会话配置GitHub身份验证