RPC(Remote Procedure Call)
在官方 SDK 中,近期的新功能都通过基于api.schema.json 的自动代码生成来支持。Laravel 版参考生成后的其他语言版本,以相同方式实现。
ServerRpc
绑定到 Client 的 RPC 类。连接、模型、工具与账户
通过ping() 和 connect() 确认并建立连接,通过 registerExtensionLaunchProvider() 注册扩展的启动提供程序。模型列表、价格单位和内置目录可通过 models() 获取,可用工具列表可通过 tools() 获取。account() 用于处理配额、认证状态、已登录用户列表以及登录与登出,accounts() 用于获取 Microsoft Entra ID 令牌。
MCP、连接器与密钥
mcp() 提供 MCP 配置的列出、添加、更新、删除,以及启用、发现、注册表搜索、安装计划与应用。connectors() 可用于查看连接器功能、账户和目录。服务器端的连接器操作主要是按账户进行列表获取和更新。experimental 的 secrets()->addFilterValues() 会将主机生成的动态密钥注册到 CLI 的脱敏过滤器中。之后 CLI 会在会话日志和导出内容中屏蔽这些值。
use Revolution\Copilot\Facades\Copilot;
use Revolution\Copilot\Types\Rpc\SecretsAddFilterValuesRequest;
$result = Copilot::client()->rpc()->secrets()->addFilterValues(
new SecretsAddFilterValuesRequest(values: [$accessToken]),
);
// $result->ok
// 也可以用数组指定
Copilot::client()->rpc()->secrets()->addFilterValues([
'values' => [$accessToken],
]);
Skills、Plugins、Agents、Instructions 与 Extensions
可以从各个分组进行探索、获取可用路径、更改设置和重新加载。commands() 提供内置命令列表,hooks() 提供会话开始前的钩子探索。Skills 和 Plugins 支持启用、禁用和安装管理,Agents 还可以获取内置代理信息。Extensions 用于探索、启用和禁用已持久化的扩展。
目录与安装
通过catalog() 进行候选搜索、选择和获取 feed,通过 Skills、Plugins、MCP 各 API 确认安装计划、应用、查询状态或取消。计划与应用是独立的操作。实际的安装示例请参考后面的各个部分。
会话、主机与运行环境
sessions() 用于处理已持久化会话的搜索、事件读取、大小确认、删除与清理以及 fork。还可以使用 Workspace 的创建、加载和远程控制。host() 用于启动主机、发布会话、列出、取消发布、处理认证、更改会话接纳策略和销毁。这些主机 RPC 是 SDK 高级 AHP(Agent Host Protocol)主机 API 的基础。runtime() 用于处理运行时的生命周期。
Git、环境、沙箱与策略
可以通过git() 获取分支、工作目录和 Git remote 信息,通过 gitHubRepository() 和 gitHubOwners() 探索 GitHub 仓库和所有者。environments() 处理 Mission Control 环境,sandbox() 和 sandboxProxyCa() 处理沙箱支持状态和 Proxy CA,contentExclusion() 用于验证排除策略,managedSettings() 和 userSettings() 用于处理设置。
本地模型、HTTP 推理与文件系统
azureMl() 提供本地模型的目录、解析、下载,以及状态确认、取消和删除。llmInference() 是将模型层的 HTTP 请求中继到客户端的实验性 API。sessionFs() 用于注册会话的文件系统/SQLite 回调提供程序。
基本调用示例
use Revolution\Copilot\Facades\Copilot;
use Revolution\Copilot\Enums\TokenPriceUnit;
use Revolution\Copilot\Types\Rpc\ModelsListTokenPriceUnitsRequest;
use Revolution\Copilot\Types\Rpc\ModelList;
// 获取模型列表
// 返回值为 ModelList
$result = Copilot::client()->rpc()->models()->list();
// models 是 ModelInfo 的数组
$models = $result->models;
// 与最初就有的 listModels() 几乎相同
// 返回值为 ModelInfo 的数组
Copilot::client()->listModels();
按功能分类的使用示例
下面汇总了上述功能的代表性调用示例。use Illuminate\Http\Client\Response;
use Revolution\Copilot\Enums\CatalogCandidateKind;
use Revolution\Copilot\Enums\CatalogCapability;
use Revolution\Copilot\Enums\CatalogSelectionDecision;
use Revolution\Copilot\Enums\CatalogTrustTier;
use Revolution\Copilot\Enums\EventsReadDirection;
use Revolution\Copilot\Enums\McpInstallationSecretStorage;
use Revolution\Copilot\Enums\OptionsUpdateToolFilterPrecedence;
use Revolution\Copilot\Enums\SkillInstallationManagementOutcomeKind;
use Revolution\Copilot\Enums\SkillInstallationManagementResultKind;
use Revolution\Copilot\Enums\AzureMlRegistry;
use Revolution\Copilot\Types\Rpc\AzureMlAcquireFamilyRequest;
use Revolution\Copilot\Types\Rpc\AzureMlCancelRequest;
use Revolution\Copilot\Types\Rpc\AzureMlCatalogRequest;
use Revolution\Copilot\Types\Rpc\AzureMlDiscardRequest;
use Revolution\Copilot\Types\Rpc\AzureMlDownloadRequest;
use Revolution\Copilot\Types\Rpc\AzureMlLatestRequest;
use Revolution\Copilot\Types\Rpc\AzureMlReleaseFamilyRequest;
use Revolution\Copilot\Types\Rpc\AzureMlResolveRequest;
use Revolution\Copilot\Types\Rpc\AzureMlStatusRequest;
use Revolution\Copilot\Types\Rpc\AgentsDiscoverRequest;
use Revolution\Copilot\Types\Rpc\AgentsGetDiscoveryPathsRequest;
use Revolution\Copilot\Types\Rpc\CatalogClientContract;
use Revolution\Copilot\Types\Rpc\CatalogGetGeneratedFeedRequest;
use Revolution\Copilot\Types\Rpc\CatalogGetSavedFeedRequest;
use Revolution\Copilot\Types\Rpc\CatalogMaterializationReference;
use Revolution\Copilot\Types\Rpc\CatalogSearchRequest;
use Revolution\Copilot\Types\Rpc\CatalogSelectionRequest;
use Revolution\Copilot\Types\Rpc\ExtensionLaunchProfile;
use Revolution\Copilot\Types\Rpc\ExtensionLaunchProviderResolveRequest;
use Revolution\Copilot\Types\Rpc\ExtensionLaunchProviderResolveResult;
use Revolution\Copilot\Types\Rpc\HooksDiscoverRequest;
use Revolution\Copilot\Types\Rpc\InstructionsDiscoverRequest;
use Revolution\Copilot\Types\Rpc\InstructionsGetDiscoveryPathsRequest;
use Revolution\Copilot\Types\Rpc\McpApplyInstallRequest;
use Revolution\Copilot\Types\Rpc\McpApplyUninstallRequest;
use Revolution\Copilot\Types\Rpc\McpInstallationOperationRequest;
use Revolution\Copilot\Types\Rpc\McpInstallationsRequest;
use Revolution\Copilot\Types\Rpc\McpPlanUninstallRequest;
use Revolution\Copilot\Types\Rpc\McpPrepareInstallRequest;
use Revolution\Copilot\Types\Rpc\McpRegistryCancelRequest;
use Revolution\Copilot\Types\Rpc\McpRegistrySearchRequest;
use Revolution\Copilot\Types\Rpc\McpServerCardReference;
use Revolution\Copilot\Types\Rpc\McpShouldExcludeGitHubToolsRequest;
use Revolution\Copilot\Types\Rpc\PluginsBuiltinSetRequest;
use Revolution\Copilot\Types\Rpc\PluginsDisableRequest;
use Revolution\Copilot\Types\Rpc\PluginsEnableRequest;
use Revolution\Copilot\Types\Rpc\PluginsInstallRequest;
use Revolution\Copilot\Types\Rpc\PluginsMarketplacesAddRequest;
use Revolution\Copilot\Types\Rpc\PluginsMarketplacesBrowseRequest;
use Revolution\Copilot\Types\Rpc\PluginsMarketplacesRefreshRequest;
use Revolution\Copilot\Types\Rpc\PluginsMarketplacesRemoveRequest;
use Revolution\Copilot\Types\Rpc\PluginsUninstallRequest;
use Revolution\Copilot\Types\Rpc\PluginsUpdateRequest;
use Revolution\Copilot\Types\Rpc\SessionsListRequest;
use Revolution\Copilot\Types\Rpc\SessionsOpenCreateRequest;
use Revolution\Copilot\Enums\SessionsOpenStatus;
use Revolution\Copilot\Types\SessionConfig;
use Revolution\Copilot\Types\Rpc\SessionsPruneOldRequest;
use Revolution\Copilot\Types\Rpc\SessionsReadPersistedEventsRequest;
use Revolution\Copilot\Types\Rpc\SkillApplyInstallRequest;
use Revolution\Copilot\Types\Rpc\SkillApplyUninstallRequest;
use Revolution\Copilot\Types\Rpc\SkillInstallationOperationRequest;
use Revolution\Copilot\Types\Rpc\SkillInstallationsRequest;
use Revolution\Copilot\Types\Rpc\SkillPlanInstallRequest;
use Revolution\Copilot\Types\Rpc\SkillPlanInstallFromReferenceRequest;
use Revolution\Copilot\Types\Rpc\SkillPlanUninstallRequest;
use Revolution\Copilot\Types\Rpc\SkillSetEnabledRequest;
use Revolution\Copilot\Types\Rpc\SkillsGetDiscoveryPathsRequest;
use Revolution\Copilot\Types\Rpc\UIEphemeralQueryRequest;
use Revolution\Copilot\Types\SessionListFilter;
use Revolution\Copilot\Support\CopilotRequestContext;
use Revolution\Copilot\Support\CopilotRequestHandler;
Copilot::client()->rpc()->ping();
// models
Copilot::client()->rpc()->models()->list();
// 使用每个会话的 GitHub 令牌获取
Copilot::client()->rpc()->models()->list(new ModelsListRequest(gitHubToken: $token));
// 获取模型令牌价格的单位。传入与 models.list 相同的账户选择信息
$priceUnits = Copilot::client()->rpc()->models()->listTokenPriceUnits(
new ModelsListTokenPriceUnitsRequest(gitHubToken: $token),
);
// $priceUnits->units 包含 modelId 和 TokenPriceUnit|string 类型的 unit
foreach ($priceUnits->units as $priceUnit) {
if ($priceUnit->unit === TokenPriceUnit::NanoAiu) {
// 将 nano-AIU 换算为 AI Credits 时,除以 1,000,000,000
}
}
// 内置模型目录
Copilot::client()->rpc()->models()->getBuiltInCatalog();
// Azure ML 本地模型管理(experimental)
$azureMl = Copilot::client()->rpc()->azureMl();
$catalog = $azureMl->catalog(new AzureMlCatalogRequest(includeDev: false));
$latest = $azureMl->latest(new AzureMlLatestRequest(
registry: AzureMlRegistry::AzureMl,
name: 'phi',
));
$resolved = $azureMl->resolve(new AzureMlResolveRequest(
registry: AzureMlRegistry::AzureMl,
name: $latest->name,
version: $latest->version,
));
// requestId 和 leaseId 绑定到此 SDK 连接,不要在其他 Client 中复用
$lease = $azureMl->acquireFamily(new AzureMlAcquireFamilyRequest(
registry: AzureMlRegistry::AzureMl,
name: $latest->name,
));
try {
$download = $azureMl->download(new AzureMlDownloadRequest(
requestId: 'download-1',
registry: AzureMlRegistry::AzureMl,
name: $latest->name,
version: $latest->version,
destination: 'models/phi/'.$latest->version,
leaseId: $lease->leaseId,
));
} finally {
$azureMl->releaseFamily(new AzureMlReleaseFamilyRequest(
leaseId: $lease->leaseId,
registry: AzureMlRegistry::AzureMl,
name: $latest->name,
));
}
// download() 会等待直到完成。处理过程中的进度确认和取消,
// 请从使用同一连接 $azureMl 的其他 Fiber 等中执行
// $status = $azureMl->status(new AzureMlStatusRequest(requestId: 'download-1'));
// $cancelled = $azureMl->cancel(new AzureMlCancelRequest(requestId: 'download-1'));
// cancel() 的 bool 表示取消请求已被接受,清理是否完成请通过 download() 确认
// 删除非活动版本
$azureMl->discard(new AzureMlDiscardRequest(destination: 'models/phi/previous-version'));
// resolve() 和 download() 的类型中没有签名 URL 的专用字段
// download() 默认没有超时。如需指定秒数,请传入第 2 个参数
// 新会话中可用的内置斜杠命令列表(experimental)
Copilot::client()->rpc()->commands()->list();
// 会话开始前的钩子探索(experimental)
Copilot::client()->rpc()->hooks()->discover(new HooksDiscoverRequest(
projectPaths: ['/path/to/project'],
));
// 主机的沙箱支持状态(experimental)
$hostSupport = Copilot::client()->rpc()->sandbox()->getHostSupport();
// $hostSupport->supported, $hostSupport->reason, $hostSupport->capabilities
// tools
Copilot::client()->rpc()->tools()->list();
// account
Copilot::client()->rpc()->account()->getQuota();
// 使用每个会话的 GitHub 令牌获取
Copilot::client()->rpc()->account()->getQuota(new AccountGetQuotaRequest(gitHubToken: $token));
// mcp config(管理 MCP 服务器配置)
Copilot::client()->rpc()->mcp()->list();
Copilot::client()->rpc()->mcp()->add(new McpConfigAddRequest(
name: 'my-server',
config: new McpServerValue(type: 'local', command: 'php', args: ['artisan', 'mcp']),
));
// args 可以省略
Copilot::client()->rpc()->mcp()->update(new McpConfigUpdateRequest(
name: 'my-server',
config: new McpServerValue(type: 'http', url: 'https://mcp.example.com'),
));
Copilot::client()->rpc()->mcp()->remove(new McpConfigRemoveRequest(name: 'my-server'));
// 启用/禁用 MCP 服务器(全局设置)
Copilot::client()->rpc()->mcp()->enable(new McpConfigEnableRequest(names: ['my-server']));
Copilot::client()->rpc()->mcp()->disable(new McpConfigDisableRequest(names: ['my-server']));
// 清除内存缓存(下次加载时从磁盘重新获取)
Copilot::client()->rpc()->mcp()->reload();
// MCP 注册表搜索 (experimental/internal)
// $authInfo 是由主机端准备的 JSON 认证信息。SDK 不会解析其内容。
$registry = Copilot::client()->rpc()->mcp()->registry();
$requestId = $registry->allocateRequestId()->requestId;
$servers = $registry->search(new McpRegistrySearchRequest(
requestId: $requestId,
authInfo: $authInfo, // 由主机传入的认证信息。SDK 不会解析其内容。
query: 'database',
limit: 20,
));
// $servers->servers 是注册表的服务器数据(不解析其结构,原样保留)
// 未使用的预留 ID 可以释放。处理中的搜索可从其他处理中调用 cancel() 来取消。
$unusedRequestId = $registry->allocateRequestId()->requestId;
$cancelResult = $registry->cancel(new McpRegistryCancelRequest(requestId: $unusedRequestId));
// 释放未使用的 ID 时 canceled 为 false。仅在停止正在执行的搜索时为 true。
// 通过 MCP 目录安装 / 卸载 (experimental)
// 结果是带有 kind 的判别联合关联数组。请先确认 kind 再引用各字段。
$mcp = Copilot::client()->rpc()->mcp();
$contract = new CatalogClientContract(
protocolVersion: 1,
requiredCapabilities: ['mcp-server-card', 'mcp-install-planning'],
);
// $candidateHandle、$searchId、$cardReference 从目录搜索结果中获取。
// $sessionId 是已连接到此 Client 的会话 ID。
$planned = $mcp->planInstall([
'contract' => $contract->toArray(),
'source' => ['kind' => 'candidate', 'candidateHandle' => $candidateHandle, 'searchId' => $searchId],
]);
if (($planned['kind'] ?? null) === 'planned') {
$plan = $planned['plan'];
$prepared = $mcp->prepareInstall(new McpPrepareInstallRequest(
contract: $contract,
planHandle: $plan['planHandle'],
choiceId: $plan['transportChoices'][0]['choiceId'],
inputs: [],
policySessionId: $sessionId,
secrets: [],
secretStorage: McpInstallationSecretStorage::Keychain,
source: McpServerCardReference::fromArray($cardReference),
));
// 在 applyInstall 期间,会调用创建客户端时设置的 installation_confirmation_handler。
if (($prepared['kind'] ?? null) === 'outcome'
&& ($prepared['outcome']['kind'] ?? null) === 'install-prepared') {
$applied = $mcp->applyInstall(new McpApplyInstallRequest(
contract: $contract,
operationId: $prepared['outcome']['operation']['operationId'],
policySessionId: $sessionId,
));
}
}
// 列出所拥有的安装并恢复未完成的操作
$installations = $mcp->installations();
$installations->list(new McpInstallationsRequest(contract: $contract, policySessionId: $sessionId));
$installations->recover(new McpInstallationsRequest(contract: $contract, policySessionId: $sessionId));
$installations->status(new McpInstallationOperationRequest($contract, $operationId));
$installations->cancel(new McpInstallationOperationRequest($contract, $operationId));
// 卸载时指定拥有所有权的 installationId 进行计划,然后应用返回的 planHandle。
$uninstallPlan = $mcp->planUninstall(new McpPlanUninstallRequest(
contract: $contract,
installationId: $installationId,
policySessionId: $sessionId,
));
if (($uninstallPlan['kind'] ?? null) === 'outcome'
&& ($uninstallPlan['outcome']['kind'] ?? null) === 'uninstall-planned') {
$mcp->applyUninstall(new McpApplyUninstallRequest(
contract: $contract,
planHandle: $uninstallPlan['outcome']['plan']['planHandle'],
policySessionId: $sessionId,
));
}
// 判断是否可以排除 GitHub MCP 工具 (experimental/internal)
$exclude = Copilot::client()->rpc()->mcp()->shouldExcludeGitHubTools(
new McpShouldExcludeGitHubToolsRequest(
availableTools: ['builtin:bash'],
excludedTools: [],
toolFilterPrecedence: OptionsUpdateToolFilterPrecedence::Available,
),
)->excludeGhReplaceableTools;
// user settings(用户设置)
// 当前 CLI 中没有 user.settings.reload RPC。get() 的对象是 settings.json。
Copilot::client()->rpc()->userSettings()->set(['settings' => ['theme' => 'dark']]); // 无返回值
// mcp discover(自动发现 MCP 服务器)
Copilot::client()->rpc()->mcp()->discover(new McpDiscoverRequest(
workingDirectory: '/path/to/project',
));
// 可以不带参数执行
$result = Copilot::client()->rpc()->mcp()->discover();
// $result->servers 是 DiscoveredMcpServer 的数组
// llmInference(experimental:钩取发往模型层的 HTTP 请求)
$client = Copilot::client([
'request_handler' => new class extends CopilotRequestHandler
{
public function sendRequest(CopilotRequestContext $context, string $method, string $body): Response
{
$context->url = 'https://proxy.example.com/v1/chat/completions';
$context->headers['X-Request-Source'] = ['laravel'];
return parent::sendRequest($context, $method, $body);
}
},
]);
// 启动时会发送 llmInference.setProvider,并接收 httpRequestStart / httpRequestChunk。
// 未重写 sendRequest 的处理程序会通过 Laravel HTTP Client 透明转发 HTTP/SSE。
// 指定 Closure 时,以 fn (CopilotRequestContext $context, string $method, string $body): Response 的形式返回 Response。
// request_handler 是 experimental 功能。尚不支持 WebSocket transport,会返回相当于 501 的错误响应。
// sessionFs(注册会话文件系统提供程序)
Copilot::client()->rpc()->sessionFs()->setProvider(new SessionFsSetProviderRequest(
initialCwd: '/path/to/project',
sessionStatePath: '.copilot/sessions',
conventions: 'posix',
));
// 用于解析扩展启动方式的客户端选项(需在 15 秒内响应)
$client = Copilot::client([
'extensionLaunchProvider' => fn (ExtensionLaunchProviderResolveRequest $request) => new ExtensionLaunchProviderResolveResult(
launch: new ExtensionLaunchProfile(
executable: '/usr/bin/node',
args: [$request->modulePath],
),
),
]);
// 没有对应的启动方式时返回 null。
// fn (ExtensionLaunchProviderResolveRequest $request) => null
$sessions = Copilot::client()->rpc()->sessions();
// sessions(experimental:查询和管理已持久化的会话)
// sessions.open 通过 `kind` 选择新建、恢复、resumeLast、attach、远程/云连接或交接。
$opened = $sessions->open(new SessionsOpenCreateRequest(
options: new SessionConfig(sessionId: 'new-session', model: 'gpt-5'),
));
if ($opened->status === SessionsOpenStatus::Created) {
echo $opened->sessionId;
}
// 也可以用数组指定。恢复时,在 variant 的顶层指定 sessionId。
$resumed = $sessions->open(['kind' => 'resume', 'sessionId' => 'existing-session']);
// 返回 SessionsOpenResult。status 为 not_found 时没有 sessionId。
// sessionApi、onTaskCreated、onProgress、onConfirm 仅在进程内使用,不包含在 wire payload 中。
$listed = $sessions->list(new SessionsListRequest(
source: 'local',
metadataLimit: 50,
filter: new SessionListFilter(cwd: '/path/to/project'),
));
// 通过各 SessionMetadata 的 isRemote 区分 local / remote 会话
foreach ($listed->sessions as $session) {
echo $session->sessionId;
}
$sessions->findByTaskId(['taskId' => 'task-id']);
$sessions->findByPrefix(['prefix' => 'session-id-prefix']);
$sessions->getLastForContext(['context' => ['cwd' => '/path/to/project']]);
$sizes = $sessions->getSizes(); // $sizes->sizes 为 session ID => 字节数
$inUse = $sessions->checkInUse(['sessionIds' => ['session-id']]);
$deleted = $sessions->bulkDelete(['sessionIds' => ['session-id']]);
// 关闭活动会话(不删除历史记录)
$sessions->close(['sessionId' => 'session-id']);
// 将待处理事件保存到磁盘
$sessions->save(['sessionId' => 'session-id']);
// 显式释放此进程持有的会话 in-use lock
$sessions->releaseLock(['sessionId' => 'session-id']);
// reloadPluginHooks 会重新加载 user/plugin hooks。
// 在确认 folder trust 之前延迟 repo hooks,确认信任后再继续加载。
$sessions->reloadPluginHooks([
'sessionId' => 'session-id',
'deferRepoHooks' => true,
]);
$loadedHooks = $sessions->loadDeferredRepoHooks(['sessionId' => 'session-id']);
// $loadedHooks->startupPrompts 和 $loadedHooks->hookCount
// 替换 Manager-wide 的附加 plugin 设置(传入空数组则清除)
$sessions->setAdditionalPlugins(['plugins' => []]);
// 补全 local session metadata 的 summary / context
$enriched = $sessions->enrichMetadata([
'sessions' => [[
'sessionId' => 'session-id',
'startTime' => '2026-10-17T09:00:00Z',
'modifiedTime' => '2026-10-17T09:00:00Z',
'isRemote' => false,
]],
]);
// close() 会执行 shutdown、保存事件、释放锁并销毁 active session,
// 但不会删除 persisted history。$session->disconnect() 通过 session.detach 释放与此 SDK 的
// 连接以及 PHP 端的 in-memory resources。删除持久化数据请使用 bulkDelete()。
// 不打开会话,使用 cursor 对已持久化的事件进行分页
$cursor = null;
do {
$page = $sessions->readPersistedEvents(new SessionsReadPersistedEventsRequest(
sessionId: 'session-id',
cursor: $cursor,
max: 100,
direction: EventsReadDirection::FORWARD,
));
foreach ($page->events as $event) {
// 处理事件
}
$cursor = $page->cursor;
} while ($page->hasMore);
// dryRun 不会删除,而是返回候选项和估算结果。实际删除时请指定 dryRun: false
$preview = $sessions->pruneOld(new SessionsPruneOldRequest(
olderThanDays: 30,
dryRun: true,
));
// sessions.fork
$sessions->fork(new SessionsForkRequest(
sessionId: 'source-session-id',
toEventId: 'evt-boundary', // 可选:仅包含此 ID 之前的事件
));
// skills(服务器级别的技能管理)
// 探索技能
$result = Copilot::client()->rpc()->skills()->discover();
// $result->skills 是 ServerSkill 的数组
// 可选地指定项目路径
$result = Copilot::client()->rpc()->skills()->discover(new SkillsDiscoverRequest(
projectPaths: ['/path/to/project'],
skillDirectories: ['/custom/skills'],
));
// 设置要禁用的技能
Copilot::client()->rpc()->skills()->config()->setDisabledSkills(
new SkillsConfigSetDisabledSkillsRequest(disabledSkills: ['skill-name'])
);
// 单独启用或禁用技能
Copilot::client()->rpc()->skills()->config()->setSkillDisabled(
new SkillsConfigSetSkillDisabledRequest(name: 'skill-name', disabled: true)
);
// 通过 catalog.search → catalog.select 选择技能候选 (experimental)
$contract = new CatalogClientContract(1, ['catalog-selection', 'ai-skill-installation']);
$catalog = Copilot::client()->rpc()->catalog();
$search = $catalog->search(new CatalogSearchRequest(
contract: $contract,
query: 'code review skill',
kinds: [CatalogCandidateKind::AiSkill],
));
$selection = $catalog->select(new CatalogSelectionRequest(
contract: $contract,
sessionId: $sessionId,
selectionRef: $search['candidates'][0]['selectionRef'],
outcome: CatalogSelectionDecision::Selected,
));
// category filter 需要请求 catalog-search-categories
$categoryContract = new CatalogClientContract(3, [
CatalogCapability::CatalogSearchCategories->value,
]);
$categorySearch = $catalog->search(new CatalogSearchRequest(
contract: $categoryContract,
query: 'design tools',
categories: ['design', 'test'],
));
// 指定 policySessionId 时,还需在 contract 中请求 credential-required 和 session-bound,
// 并且只绑定到现有的 attached session。不会新建或恢复 session。
// 已保存和生成的 catalog feed (credential required)
$feedContract = new CatalogClientContract(3, [
CatalogCapability::CatalogFeeds->value,
CatalogCapability::CatalogSearchCredentialRequired->value,
]);
$savedFeed = $catalog->getSavedFeed(new CatalogGetSavedFeedRequest(
contract: $feedContract,
feedId: 'opaque-feed-id',
trustTiers: [CatalogTrustTier::T1],
));
// 绑定到现有 attached session 时,还需请求 session-bound capability
$sessionFeedContract = new CatalogClientContract(3, [
CatalogCapability::CatalogFeeds->value,
CatalogCapability::CatalogSearchCredentialRequired->value,
CatalogCapability::CatalogSearchSessionBound->value,
]);
$generatedFeed = $catalog->getGeneratedFeed(new CatalogGetGeneratedFeedRequest(
contract: $sessionFeedContract,
strategy: 'telemetry',
key: 'popular',
policySessionId: $sessionId,
trustTiers: [CatalogTrustTier::T1, CatalogTrustTier::T2],
));
// entries 保持 feed 顺序。不符合 trust tier 的条目也会作为 filtered 保留位置,
// 无法显示的 entry 会以 unavailable 和 reason 返回。
// 没有 feed 时返回 kind=unavailable、reason=feed-not-found。认证、协商、策略拒绝
// 也会作为各自的类型化结果返回,catalog feed RPC 不会执行安装或设置。
// 技能的安装计划与应用
$skills = Copilot::client()->rpc()->skills();
if ($selection['kind'] === 'selected') {
$planned = $skills->planInstall(new SkillPlanInstallRequest(
contract: $contract,
candidateHandle: $selection['candidateHandle'],
policySessionId: $sessionId,
));
if ($planned->kind === SkillInstallationManagementResultKind::Outcome
&& $planned->outcome?->kind === SkillInstallationManagementOutcomeKind::InstallPlanned) {
// applyInstall() 在通过 installations.confirm 确认后应用
$result = $skills->applyInstall(new SkillApplyInstallRequest(
contract: $contract,
planHandle: $planned->outcome->plan['planHandle'],
policySessionId: $sessionId,
));
}
}
// 从 Agent Finder 的 materialisation reference 计划技能安装
$referenceContract = new CatalogClientContract(3, [
'skill-confirmed-installation',
'skill-reference-installation',
]);
$referencePlan = $skills->planInstallFromReference(new SkillPlanInstallFromReferenceRequest(
contract: $referenceContract,
reference: new CatalogMaterializationReference(
resourceId: '12345',
revisionId: '67890',
schemaVersion: '1.0.0',
descriptorDigest: 'sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
bundleDigest: 'sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
),
policySessionId: $sessionId,
));
// 成功时 $referencePlan->kind === SkillInstallationManagementResultKind::Outcome,
// $referencePlan->outcome->kind === SkillInstallationManagementOutcomeKind::InstallPlanned。
// 计划本身不会产生副作用,它从固定的 Agent Finder authority 获取 descriptor,
// 并核对 descriptor 与 bundle 的 digest。请确认内容并完成现有的确认流程后再调用 applyInstall()。
// invalid-reference、descriptor-not-found、descriptor-withdrawn、digest-mismatch、
// unsupported-resource 会作为计划结果的拒绝原因返回。
// GitHub 的拒绝原因 source-rate-limited、no-credential、credential-rejected
// 通过 SkillInstallationFailureReason 区分,retryAfterSeconds 仅在 source-rate-limited 时返回。
// network-failure 的 message 是不包含 URL、搜索查询、句柄和 secret 的安全说明。
// 已安装的技能和进行中的操作
$installations = $skills->installations();
$owned = $installations->list(new SkillInstallationsRequest($contract, $sessionId));
$pending = $installations->recover(new SkillInstallationsRequest($contract, $sessionId));
$status = $installations->status(new SkillInstallationOperationRequest($contract, 'operation-id'));
$cancelled = $installations->cancel(new SkillInstallationOperationRequest($contract, 'operation-id'));
$enabled = $installations->setEnabled(new SkillSetEnabledRequest(
contract: $contract,
installationId: 'installation-id',
enabled: true,
policySessionId: $sessionId,
));
// 卸载同样需要先确认计划再应用
$removalPlan = $skills->planUninstall(new SkillPlanUninstallRequest(
contract: $contract,
installationId: 'installation-id',
policySessionId: $sessionId,
));
$removed = $skills->applyUninstall(new SkillApplyUninstallRequest(
contract: $contract,
planHandle: $removalPlan['planHandle'],
policySessionId: $sessionId,
));
// skills:获取可创建技能的目录 (experimental)
// 放置技能后运行时可识别的目录列表
$result = Copilot::client()->rpc()->skills()->getDiscoveryPaths();
// $result->paths 是 SkillDiscoveryPath 的数组
// $result->paths[0]->path - 目录路径
// $result->paths[0]->scope - 范围(user, project, plugin)
// $result->paths[0]->preferredForCreation - 是否为新建时的推荐目录
// 可选地筛选范围
$result = Copilot::client()->rpc()->skills()->getDiscoveryPaths(
new SkillsGetDiscoveryPathsRequest(scope: 'project')
);
// plugins(服务器范围的插件管理)
$plugins = Copilot::client()->rpc()->plugins();
$result = $plugins->list(); // 返回值为 PluginList
$installed = $plugins->install(new PluginsInstallRequest(source: 'eslint@official'));
$plugins->uninstall(new PluginsUninstallRequest(name: 'eslint@official'));
$plugins->update(new PluginsUpdateRequest(name: 'eslint@official'));
$plugins->updateAll();
$plugins->enable(new PluginsEnableRequest(names: ['eslint@official']));
$plugins->disable(new PluginsDisableRequest(names: ['eslint@official']));
// 内置插件的目录设置
$plugins->builtin()->set(new PluginsBuiltinSetRequest(paths: ['/opt/copilot/plugins']));
// 在所有会话中使用的目录,如果在客户端配置中指定,
// Client::start() 会在创建第一个会话之前自动注册
// config/copilot.php: 'builtin_plugin_directories' => ['/opt/copilot/plugins']
// 市场管理
$marketplaces = $plugins->marketplaces();
$marketplaces->list();
$marketplaces->add(new PluginsMarketplacesAddRequest(source: 'github/copilot-plugins'));
$marketplaces->remove(new PluginsMarketplacesRemoveRequest(name: 'official', force: true));
$marketplaces->browse(new PluginsMarketplacesBrowseRequest(name: 'official'));
$marketplaces->refresh(new PluginsMarketplacesRefreshRequest(name: 'official'));
// agents(experimental:自定义代理管理)
// 探索自定义代理
$result = Copilot::client()->rpc()->agents()->discover();
// $result->agents 是 AgentInfo 的数组
$result = Copilot::client()->rpc()->agents()->discover(new AgentsDiscoverRequest(
projectPaths: ['/path/to/project'],
excludeHostAgents: true,
));
// 获取可创建代理的目录 (experimental)
$result = Copilot::client()->rpc()->agents()->getDiscoveryPaths();
// $result->paths 是 AgentDiscoveryPath 的数组
// $result->paths[0]->path - 目录路径
// $result->paths[0]->scope - 范围(user, project, plugin)
// $result->paths[0]->preferredForCreation - 是否为新建时的推荐目录
$result = Copilot::client()->rpc()->agents()->getDiscoveryPaths(
new AgentsGetDiscoveryPathsRequest(scope: 'user')
);
// instructions(experimental:指令管理)
// 探索指令
$result = Copilot::client()->rpc()->instructions()->discover();
// $result->sources 是 InstructionSource 的数组
$result = Copilot::client()->rpc()->instructions()->discover(
new InstructionsDiscoverRequest(projectPaths: ['/path/to/project'])
);
// 获取可创建指令的文件/目录 (experimental)
$result = Copilot::client()->rpc()->instructions()->getDiscoveryPaths();
// $result->paths 是 InstructionDiscoveryPath 的数组
// $result->paths[0]->path - 文件/目录路径
// $result->paths[0]->location - 位置(user, repository, working-directory, plugin)
// $result->paths[0]->kind - 类型(file, directory)
// $result->paths[0]->preferredForCreation - 是否为新建时的推荐项
$result = Copilot::client()->rpc()->instructions()->getDiscoveryPaths(
new InstructionsGetDiscoveryPathsRequest(location: 'repository')
);
// extensions(experimental:管理已持久化的扩展)
$result = Copilot::client()->rpc()->extensions()->discover();
Copilot::client()->rpc()->extensions()->enable(['ids' => ['user:demo']]);
Copilot::client()->rpc()->extensions()->disable(['ids' => ['plugin:demo']]);
// environments(experimental:搜索、获取和删除 GitHub Mission Control 环境)
$result = Copilot::client()->rpc()->environments()->list(['kind' => 'user-local']);
// $result->environments 是 GitHubEnvironment 的数组
$env = Copilot::client()->rpc()->environments()->get(['environmentId' => 'env-id']);
Copilot::client()->rpc()->environments()->delete(['environmentId' => 'env-id']);
Agent Plugin catalog 候选
在catalog.search 中请求 agent-plugin-discovery,并在搜索对象的 kinds 中指定 plugin,即可获取 Agent Plugin 候选。若要接收标准格式 application/vnd.github.copilot-plugin 以外的 application/vnd.anthropic.claude-plugin+json 和 application/agent-plugin+json,除了 agent-plugin-discovery 之外,还需请求 agent-plugin-formats。
agent-plugin-confirmed-installation 是返回是否可安装以及用于审查的计划的 capability,同样需要与 agent-plugin-discovery 一起请求。允许后,候选中会包含 installability,其值为 installable、install-data-unavailable、feature-disabled、not-installable-kind 之一。installPlan 仅在 installability 为 installable 时包含。计划是用于确认的数据,此 catalog RPC 不会执行安装。
计划的 sources 按优先顺序包含 materialization、marketplace、repository 候选。Marketplace 会记录名称和仓库,直接的 Repository 会记录仓库和可选路径。仅有 materialization 的计划会省略 pluginName,且 components 为空。名称和实际引入的内容请通过 plugins.planInstall() 返回的审查结果确认。
对于已选择的候选,可以使用 plugins.planInstall() 创建无副作用且经过验证的计划。plugins.applyInstall() 会要求用户明确确认,获得许可后应用计划。应用后的插件以禁用状态注册。
审查中的 mcpServers 显示已验证文件声明的服务器名称、transport,以及远程情况下的 host。这并不是安全性保证,因此请让用户确认内容后再进行明确确认。已安装插件的 description 来自 manifest。verifiedSource 表示 GitHub 仓库和固定的 commit,仅在固定到 commit 的直接安装中存在。iconUrl 是外部的不受信任内容,获取时请勿发送认证信息或 GitHub 凭据。凭据本身不会作为拒绝原因返回。
在安装结果的 network-failure 中,可以查看 CatalogNetworkFailureReason、HTTP statusCode、可选的 retryAfterSeconds 以及可以安全显示的 message。
use Revolution\Copilot\Enums\CatalogReferenceInstallationResultKind;
use Revolution\Copilot\Enums\PluginInstallationManagementOutcomeKind;
use Revolution\Copilot\Types\Rpc\CatalogClientContract;
use Revolution\Copilot\Types\Rpc\PluginApplyInstallRequest;
use Revolution\Copilot\Types\Rpc\PluginPlanInstallRequest;
$contract = new CatalogClientContract(3, [
'agent-plugin-confirmed-installation',
]);
$plugins = Copilot::client()->rpc()->plugins();
$planned = $plugins->planInstall(new PluginPlanInstallRequest(
candidateHandle: $candidateHandle,
contract: $contract,
policySessionId: $sessionId,
));
if ($planned->kind === CatalogReferenceInstallationResultKind::Outcome
&& $planned->outcome?->kind === PluginInstallationManagementOutcomeKind::InstallPlanned) {
$applied = $plugins->applyInstall(new PluginApplyInstallRequest(
contract: $contract,
planHandle: $planned->outcome->plan->planHandle,
policySessionId: $sessionId,
));
}
catalog.planInstallFromReference()。结果的 outcome->kind 为 plugin-install-planned、skill-install-planned、refused 之一。请将插件的计划传给 plugins.applyInstall(),将技能的计划传给 skills.applyInstall()。
use Revolution\Copilot\Enums\CatalogCapability;
use Revolution\Copilot\Enums\CatalogReferenceInstallationOutcomeKind;
use Revolution\Copilot\Enums\CatalogReferenceInstallationResultKind;
use Revolution\Copilot\Types\Rpc\CatalogClientContract;
use Revolution\Copilot\Types\Rpc\CatalogPlanInstallFromReferenceRequest;
use Revolution\Copilot\Types\Rpc\PluginApplyInstallRequest;
$referenceContract = new CatalogClientContract(3, [
CatalogCapability::CatalogReferenceInstallation->value,
CatalogCapability::AgentPluginConfirmedInstallation->value,
]);
$planned = Copilot::client()->rpc()->catalog()->planInstallFromReference(
new CatalogPlanInstallFromReferenceRequest(
contract: $referenceContract,
policySessionId: $sessionId,
reference: $reference,
),
);
$plugins = Copilot::client()->rpc()->plugins();
if ($planned->kind === CatalogReferenceInstallationResultKind::Outcome
&& $planned->outcome?->kind === CatalogReferenceInstallationOutcomeKind::PluginInstallPlanned) {
$plugins->applyInstall(new PluginApplyInstallRequest(
contract: $referenceContract,
planHandle: $planned->outcome->plan->planHandle,
policySessionId: $sessionId,
));
}
contentExclusion
可以在创建会话之前验证内容排除策略的 JSON。use Revolution\Copilot\Types\Rpc\ContentExclusionValidatePoliciesRequest;
$result = Copilot::client()->rpc()->contentExclusion()->validatePolicies(
new ContentExclusionValidatePoliciesRequest(policiesJson: $policiesJson),
);
// $result->policies 是经过验证并规范化的策略
policiesJson 以未解析的 JSON 字符串形式直接传给 CLI。如果 JSON 解析或模式验证失败,会发生来自 CLI 的 RPC 错误(JsonRpcException)。
这是用于验证策略本身的服务器范围 RPC。若要根据实际的排除策略检查会话工作目录内的路径,请使用会话范围的 contentExclusion()->checkPaths()。
managedSettings(experimental:托管设置)
用于读取托管设置、解析策略、获取模式、验证、合成预览以及丢弃缓存。$managedSettings = $client->rpc()->managedSettings()->read();
$effective = $client->rpc()->managedSettings()->resolve([
'selectionId' => 'selection-id',
'workingDirectory' => '/path/to/project',
]);
$schema = $client->rpc()->managedSettings()->schema();
$validation = $client->rpc()->managedSettings()->validate(['content' => ['permissions' => []]]);
$preview = $client->rpc()->managedSettings()->compose(['layers' => [
['source' => 'device', 'settings' => ['permissions' => []]],
]]);
$client->rpc()->managedSettings()->clearCache();
Runtime 与 Host 的生命周期(experimental)
host.unpublishSession() 会从所拥有的运行中主机撤回 application publication。即使撤回发布,原始会话历史、worktree 和正在运行的工作也不会被删除或停止。如果已经未发布,unpublished 为 false,application 以外的 publication 会导致错误。
$result = Copilot::client()->rpc()->host()->unpublishSession([
'hostId' => 'host-1',
'sessionId' => 'session-1',
]);
// $result->unpublished
host.dispose() 通过指定 hostId 来销毁主机监听器。
Copilot::client()->rpc()->host()->dispose(['hostId' => 'host-1']);
host.setSessionCreationPolicy() 用于切换运行中的主机是否接受新的远程会话。此更改不会断开现有客户端,仅适用于之后的接纳。
use Revolution\Copilot\Types\Rpc\HostSetSessionCreationPolicyRequest;
Copilot::client()->rpc()->host()->setSessionCreationPolicy(
new HostSetSessionCreationPolicyRequest(
hostId: 'host-1',
allowCreatingNewSessions: false,
),
);
Client::stop() 在断开会话之后、关闭 JSON-RPC 连接和进程之前调用 runtime.shutdown。最多等待 10 秒以完成运行时清理,失败时会将错误包含在返回值中,并继续执行剩余的停止处理。对于 cli_url 等外部运行时连接,为避免终止共享运行时,不会调用此 RPC。
如果 CLI 无法识别对应的请求并返回 id: null 的 JSON-RPC 错误,所有进行中的请求都会以 JsonRpcException 失败。该异常包含已移除引号内的值并限制了长度的诊断消息。连接会保持打开。由于 CLI 可能已经处理了请求,在确认安全之前请勿自动重试。SDK 会在发送前拒绝 CLI 无法读取的、嵌套超过 127 层的发出请求,并将嵌套过深的处理程序响应替换为 JSON-RPC 错误。
SessionRpc
绑定到 Session 的 RPC 类。 还可以使用以前的 SDK 无法实现的计划模式等功能。use Revolution\Copilot\Contracts\CopilotSession;
use Revolution\Copilot\Facades\Copilot;
use Revolution\Copilot\Types\Rpc\ModeSetRequest;
use Revolution\Copilot\Types\Rpc\PlanReadResult;
Copilot::start(function (CopilotSession $session) {
$session->rpc()->mode()->set(new ModeSetRequest(mode: 'plan'));
$response = $session->sendAndWait(prompt: '制定〇〇的计划');
$result = $session->rpc()->plan()->read();
dump($result->content);
$session->rpc()->mode()->set(new ModeSetRequest(mode: 'autopilot'));
$response = $session->sendAndWait(prompt: '按照计划实现');
dump($response->content());
});
模型、模式、名称与计划
通过model() 可以确认或切换当前模型,并设置 reasoning effort、Auto tier 和允许的模型。mode() 处理模式,name() 处理会话名称,plan() 处理计划正文和 SQLite todo 的读写。
工作区、指令与 LSP
workspaces() 提供文件的列出、读取、写入、移动、删除以及目录操作。可以通过 instructions() 获取、重新加载和探索指令,通过 lsp() 初始化语言服务器配置。
Autopilot 目标
autopilotObjective()->getState() 以类型化对象返回会话当前的 Autopilot 目标状态。没有目标时,state 为 null。此 API 与确认工作区中是否保存了目标的 workspaces()->autopilotObjectiveExists() 不同。
$state = $session->rpc()->autopilotObjective()->getState()->state;
if ($state !== null) {
dump($state->objective, $state->status, $state->turnCount, $state->creditLimit);
}
代理、Fleet 与后台任务
通过agent() 列出、选择和重新加载代理,通过 fleet() 启动 Fleet。tasks() 处理后台代理和客户端所有任务的注册、进度获取与更新、迁移、发送消息、取消和删除。
MCP、Plugins、Extensions 与 Workflow
mcp() 处理服务器的列出、启动、停止、重新加载,工具、提示、资源,OAuth、sampling 以及动态标头的更新。plugins() 和 extensions() 管理会话内的状态,customizations() 用于重新加载自定义项。可以通过 workflow() 执行和查询工作流,通过 diagnostics() 和 connectors() 获取诊断信息和连接器。
Tools、Permissions、Commands 与 UI
tools() 和 permissions() 处理对通过事件到达的待处理请求的响应和设置。commands() 提供命令列表、执行和事件响应,ui() 提供对用户输入、elicitation、计划批准等请求的响应。completions() 用于获取输入候选。
Accounts、认证与 Providers
accounts() 处理通过 enumerate() 列举账户/提供程序/登录账户,get()、set(),以及通过 begin()、advance()、cancel() 进行的交互式登录。gitHubAuth() 处理会话的认证状态和凭据,provider() 和 providers() 处理模型连接目标和提供程序适配器,connectors() 处理会话的连接器连接。
历史、队列、日志与使用量
history() 提供对话的压缩、中断、交接摘要和截断,queue() 提供待处理项的插入、移动、编辑、删除和立即发送。通过 log()、eventLog()、usage()、quota()、limitPrediction() 处理日志、事件、使用量和上限信息。schedule() 可用于查看和停止已计划的提示。
生命周期、远程与主机集成
sendMessages() 在一个回合中执行多条消息,suspend() 和 shutdown() 用于停止会话。interruptMainTurn() 单独控制主回合,cancelAllBackgroundAgents() 单独控制后台代理。remote() 和 visibility() 处理远程发布和共享状态,sandbox() 处理会话的沙箱,managedSettings() 处理会话范围的托管设置,contentExclusion() 处理路径判定,metadata() 处理客户端所有的元数据,canvas() 处理 Canvas 集成。
debug (experimental)
通过debug()->collectLogs() 可以将会话的事件日志、进程日志和 shell 日志收集到 .tgz 归档或目录中。日志默认会对机密信息进行脱敏。可以通过 additionalEntries 添加文件或目录,redaction 可指定为 plain-text、events-jsonl、none。指定 none 时不会进行脱敏,因此请注意所添加的内容。
use Revolution\Copilot\Enums\DebugCollectLogsEntryKind;
use Revolution\Copilot\Enums\DebugCollectLogsRedaction;
use Revolution\Copilot\Types\Rpc\DebugCollectLogsDestination;
use Revolution\Copilot\Types\Rpc\DebugCollectLogsEntry;
use Revolution\Copilot\Types\Rpc\DebugCollectLogsRequest;
$result = $session->rpc()->debug()->collectLogs(new DebugCollectLogsRequest(
destination: DebugCollectLogsDestination::archive('/tmp/copilot-debug.tgz', noOverwrite: true),
additionalEntries: [
new DebugCollectLogsEntry(
kind: DebugCollectLogsEntryKind::File,
path: '/var/log/my-app.log',
bundlePath: 'application/my-app.log',
redaction: DebugCollectLogsRedaction::PlainText,
required: true,
),
],
));
DebugCollectLogsDestination::directory('/tmp/copilot-debug') 可以输出到暂存目录。include 可以单独指定要收集的日志和引用路径,并可从结果的 entries 和 skippedEntries 确认收集内容。目标位置和附加条目的路径是 CLI 服务器端的路径。在通过 stdio 启动 CLI 的标准配置中与应用位于同一主机,但如果通过 TCP 连接到其他主机上的 CLI 服务器,请指定服务器端的路径,而不是调用方机器上的路径。
contentExclusion (experimental)
可以确认会话工作目录内的本地绝对路径是否属于内容排除策略的对象。请在读取或附加文件内容之前调用。use Revolution\Copilot\Types\Rpc\ContentExclusionCheckPathsRequest;
$result = $session->rpc()->contentExclusion()->checkPaths(
new ContentExclusionCheckPathsRequest(paths: [
'/workspace/private.txt',
'/workspace/public.txt',
]),
);
if ($result->isExcluded('/workspace/private.txt')) {
// 属于排除对象,或无法安全判定。
}
checkPaths() 仅接受绝对路径,且路径必须位于会话的工作目录内。checks 按与输入相同的顺序返回,重复的路径也会原样保留。当路径、文件系统不受支持或策略评估不可用时,会返回 available: false 和空的 checks。此时 isExcluded() 始终返回 true,因此请将所有路径视为排除对象。即使 available: true,结果中不存在的路径也会按安全优先原则判定为排除对象。
MCP Apps (experimental)
可以通过$session->rpc()->mcp()->apps() 调用 MCP Apps 用的 RPC。在 listTools() 和 callTool() 中,调用方 MCP 服务器名称 originServerName 为必需。callTool() 的结果以自由格式的 MCP 结果返回。
use Revolution\Copilot\Types\Rpc\MCPAppsDiagnoseRequest;
use Revolution\Copilot\Types\Rpc\MCPAppsListToolsRequest;
use Revolution\Copilot\Types\Rpc\McpAppsCallToolRequest;
use Revolution\Copilot\Types\Rpc\McpAppsReadResourceRequest;
use Revolution\Copilot\Types\Rpc\McpAppsSetHostContextDetails;
use Revolution\Copilot\Types\Rpc\McpAppsSetHostContextRequest;
$apps = $session->rpc()->mcp()->apps();
$tools = $apps->listTools(new MCPAppsListToolsRequest(
serverName: 'my-server',
originServerName: 'my-server',
));
$result = $apps->callTool(new McpAppsCallToolRequest(
serverName: 'my-server',
toolName: 'lookup',
originServerName: 'my-server',
arguments: ['query' => 'hello'],
));
$resource = $apps->readResource(new McpAppsReadResourceRequest(
serverName: 'my-server',
uri: 'ui://my-app/index.html',
));
$context = $apps->getHostContext();
$apps->setHostContext(new McpAppsSetHostContextRequest(
context: new McpAppsSetHostContextDetails(theme: 'dark', locale: 'ja-JP'),
));
$diagnostics = $apps->diagnose(new MCPAppsDiagnoseRequest(serverName: 'my-server'));
readResource() 返回 HTML 等资源数据。SDK 不进行浏览器渲染或 iframe 操作,因此渲染所获取的内容是调用方应用程序的责任。
按功能分类的使用示例
下面汇总了上述功能的代表性调用示例。// model
$session->rpc()->model()->getCurrent();
// 获取当前模型的 reasoning summary 显示策略(experimental)
$policy = $session->rpc()->model()->getReasoningSummaries(new ModelGetReasoningSummariesRequest(
persistedShowReasoning: true, // 未设置时省略
summariesVisible: true, // 当前是否在主机 UI 中显示
));
// $policy->ready、$policy->sessionShowsSummaries、$policy->visibility 等
// 不更改所选模型,仅更改 reasoning effort(experimental)
$effort = $session->rpc()->model()->setReasoningEffort(
new ModelSetReasoningEffortRequest(reasoningEffort: ReasoningEffort::HIGH),
);
// $effort->reasoningEffort
$session->rpc()->model()->switchTo(new ModelSwitchToRequest(modelId: 'gpt-4'));
// 指定 reasoningEffort 时(仅限支持的模型)
$session->rpc()->model()->switchTo(new ModelSwitchToRequest(modelId: 'claude-opus-4.7', reasoningEffort: ReasoningEffort::HIGH));
// 指定 reasoningSummary 时
$session->rpc()->model()->switchTo(new ModelSwitchToRequest(modelId: 'claude-opus-4.7', reasoningSummary: 'concise'));
// 指定 contextTier 时(仅限支持的模型)
$session->rpc()->model()->switchTo(new ModelSwitchToRequest(modelId: 'gpt-4o', contextTier: 'long_context'));
// 覆盖 modelCapabilities 时
$session->rpc()->model()->switchTo(new ModelSwitchToRequest(
modelId: 'gpt-4',
modelCapabilities: new ModelCapabilitiesOverride(
supports: new ModelCapabilitiesOverrideSupports(vision: true),
),
));
// 获取会话中可用的模型列表(experimental)
$models = $session->rpc()->model()->list();
$models->auto?->tiers; // 账户中可用的 Auto routing preference
$session->rpc()->model()->list(new ModelListRequest(skipCache: true)); // 跳过缓存
// 更改 Auto routing preference(不更改 selected model,experimental)
$session->rpc()->model()->switchAutoTier(new ModelSwitchAutoTierRequest(autoTier: AutoTier::BALANCE));
$session->rpc()->model()->switchAutoTier(new ModelSwitchAutoTierRequest(autoTier: null)); // 恢复为 provider-default 的 Auto routing
// 在主机端设置或解除会话的模型允许列表(experimental)
$session->rpc()->model()->setAllowedModels(new ModelSetAllowedModelsRequest(allowedModels: ['gpt-5']));
$session->rpc()->model()->setAllowedModels(new ModelSetAllowedModelsRequest(allowedModels: null)); // 解除限制
// setModel() 辅助方法同样可以指定 reasoningEffort 和 modelCapabilities
$session->setModel('claude-opus-4.7', ReasoningEffort::HIGH);
$session->setModel('claude-opus-4.7', 'high'); // 也可以用字符串指定
$session->setModel('gpt-4', modelCapabilities: ['supports' => ['vision' => true]]); // 也可以用数组指定
// 指定 autoTier 时(仅在 modelId 为 'auto' 时有效)
$session->setModel('auto', autoTier: AutoTier::INTELLIGENCE);
// 使用 setAutoTier() 辅助方法,不更改 model,仅更改 Auto routing preference(experimental)
$session->setAutoTier(AutoTier::EFFICIENCY);
// mode
$session->rpc()->mode()->get();
$modeResult = $session->rpc()->mode()->set(new ModeSetRequest(mode: 'plan'));
// $modeResult->modeApplied、$modeResult->modelChanged、$modeResult->confirmation 等
// name
$session->rpc()->name()->get();
$session->rpc()->name()->set(new NameSetRequest(name: 'My Session'));
// 用户未设置名称时,将摘要作为会话名称应用(experimental)
$autoName = $session->rpc()->name()->setAuto(new NameSetAutoRequest(summary: 'Summary of the session'));
// $autoName->applied
// plan
$session->rpc()->plan()->read();
$session->rpc()->plan()->update(new PlanUpdateRequest(content: '...'));
$session->rpc()->plan()->delete();
// 读取 SQLite todo 列表(计划模式)
$result = $session->rpc()->plan()->readSqlTodos();
// $result->rows - PlanSqlTodosRow 的数组(id, title, description, status)
// 读取 SQLite todo 列表及依赖关系(experimental)
$result = $session->rpc()->plan()->readSqlTodosWithDependencies();
// $result->rows - PlanSqlTodosRow 的数组
// $result->dependencies - PlanSqlTodoDependency 的数组(todo_id, depends_on)
// workspaces
$session->rpc()->workspaces()->getWorkspace();
$session->rpc()->workspaces()->listFiles();
$session->rpc()->workspaces()->readFile(new WorkspacesReadFileRequest(path: 'file.txt'));
$session->rpc()->workspaces()->createFile(new WorkspacesCreateFileRequest(path: 'file.txt', content: '...'));
$session->rpc()->workspaces()->statFile(new WorkspacesStatFileRequest(path: 'file.txt'));
$session->rpc()->workspaces()->createDirectory(new WorkspacesCreateDirectoryRequest(path: 'new/dir', recursive: true));
$session->rpc()->workspaces()->removePath(new WorkspacesRemovePathRequest(path: 'old/dir', recursive: true, force: true));
$session->rpc()->workspaces()->renamePath(new WorkspacesRenamePathRequest(source: 'old.txt', destination: 'new.txt'));
// instructions(获取会话的指令来源)
$result = $session->rpc()->instructions()->getSources();
// $result->sources - InstructionsSources 的数组
$session->rpc()->instructions()->reload();
// 显式重新加载 LSP 配置(experimental)
$session->rpc()->lsp()->initialize([
'workingDirectory' => base_path(),
'gitRoot' => base_path(),
'force' => true,
]);
// instructions discover(检测指令文件)
use Revolution\Copilot\Types\Rpc\InstructionsDiscoverRequest;
$result = $session->rpc()->instructions()->discover();
// $result->sources - InstructionSource 的数组
$result = $session->rpc()->instructions()->discover(new InstructionsDiscoverRequest(
projectPaths: ['/path/to/project'],
excludeHostInstructions: true, // 排除主机级别的指令
));
// fleet
$session->rpc()->fleet()->start(new FleetStartRequest(prompt: '...'));
// 详情请参考 fleet-mode.md
// agent
$session->rpc()->agent()->list();
$session->rpc()->agent()->getCurrent();
$session->rpc()->agent()->select(new AgentSelectRequest(agentId: '...'));
$session->rpc()->agent()->deselect();
$session->rpc()->agent()->reload();
// skills(experimental:技能管理)
$session->rpc()->skills()->list();
$session->rpc()->skills()->enable(new SkillsEnableRequest(name: 'skill-name'));
$session->rpc()->skills()->disable(new SkillsDisableRequest(name: 'skill-name'));
$session->rpc()->skills()->reload();
$session->rpc()->skills()->ensureLoaded(); // 加载尚未加载的技能(不重新加载)
$invoked = $session->rpc()->skills()->getInvoked();
// $invoked->skills - 会话中调用过的技能历史
// mcp(experimental:MCP 服务器管理)
$servers = $session->rpc()->mcp()->list();
// HTTP/SSE 服务器的配置 URL:$servers->servers[0]->url
$session->rpc()->mcp()->enable(new McpEnableRequest(serverName: 'server-name'));
$session->rpc()->mcp()->disable(new McpDisableRequest(serverName: 'server-name'));
$session->rpc()->mcp()->reload();
// 将进行中的 MCP 加载移至后台(释放等待中的回合)
$session->rpc()->mcp()->moveLoadingToBackground();
// 启动或重启会话内的 MCP 服务器(不更改持久配置)
use Revolution\Copilot\Types\Rpc\McpRestartServerRequest;
use Revolution\Copilot\Types\Rpc\McpStartServerRequest;
$session->rpc()->mcp()->startServer(new McpStartServerRequest(
serverName: 'server-name',
config: ['type' => 'http', 'url' => 'https://mcp.example.com'],
));
$session->rpc()->mcp()->restartServer(new McpRestartServerRequest(
serverName: 'server-name',
expectedInstallationId: 'installation-id',
));
// 删除自动管理的 GitHub MCP 服务器,以及环境变量值的解析方式
$removed = $session->rpc()->mcp()->removeGitHub();
$mode = $session->rpc()->mcp()->setEnvValueMode(['mode' => 'indirect']);
// $removed->removed、$mode->mode
// 执行和取消 MCP sampling
use Revolution\Copilot\Types\Rpc\McpCancelSamplingExecutionParams;
use Revolution\Copilot\Types\Rpc\McpExecuteSamplingParams;
$sampling = $session->rpc()->mcp()->executeSampling(new McpExecuteSamplingParams(
requestId: 'sampling-1',
serverName: 'server-name',
mcpRequestId: 'mcp-request-1',
request: ['messages' => [], 'maxTokens' => 256],
));
$cancelled = $session->rpc()->mcp()->cancelSamplingExecution(
new McpCancelSamplingExecutionParams(requestId: 'sampling-1'),
);
// 确认 MCP 服务器是否正在运行
use Revolution\Copilot\Types\Rpc\McpIsServerRunningRequest;
$result = $session->rpc()->mcp()->isServerRunning(new McpIsServerRunningRequest(serverName: 'my-server'));
// $result->running - 服务器是否正在运行
// 获取 MCP 服务器的工具列表
use Revolution\Copilot\Types\Rpc\McpListToolsRequest;
$result = $session->rpc()->mcp()->listTools(new McpListToolsRequest(serverName: 'my-server'));
// $result->tools - McpTools 的数组(name, description)
// MCP 提示的列表与获取(experimental)
use Revolution\Copilot\Types\Rpc\McpPromptsGetRequest;
use Revolution\Copilot\Types\Rpc\McpPromptsListRequest;
$prompts = $session->rpc()->mcp()->listPrompts(new McpPromptsListRequest(serverName: 'my-server'));
$rendered = $session->rpc()->mcp()->getPrompt(new McpPromptsGetRequest(
serverName: 'my-server',
promptName: 'summarize',
arguments: ['document' => 'README.md'],
));
// $prompts->prompts - MCP 提示定义
// $rendered->messages - 已渲染的提示(不会发送给模型)
// 停止 MCP 服务器
use Revolution\Copilot\Types\Rpc\McpStopServerRequest;
$session->rpc()->mcp()->stopServer(new McpStopServerRequest(serverName: 'my-server'));
// 获取 MCP 资源列表(experimental)
use Revolution\Copilot\Types\Rpc\McpResourcesListRequest;
$result = $session->rpc()->mcp()->resources()->list(new McpResourcesListRequest(serverName: 'my-server'));
// $result->resources - McpResource 的数组(uri, name, title, mimeType 等)
// $result->nextCursor - 下一页的游标(如果存在)
// 获取 MCP 资源(experimental)
use Revolution\Copilot\Types\Rpc\McpResourcesReadRequest;
$result = $session->rpc()->mcp()->resources()->read(new McpResourcesReadRequest(serverName: 'my-server', uri: 'ui://example'));
// $result->contents - McpResourceContent 的数组
// 获取 MCP 资源模板列表(experimental)
use Revolution\Copilot\Types\Rpc\McpResourcesListTemplatesRequest;
$result = $session->rpc()->mcp()->resources()->listTemplates(new McpResourcesListTemplatesRequest(serverName: 'my-server'));
// MCP OAuth 登录(适用于需要认证的 MCP 服务器)
use Revolution\Copilot\Types\Rpc\McpOauthCompleteRequest;
use Revolution\Copilot\Types\Rpc\McpOauthLoginRequest;
use Revolution\Copilot\Types\Rpc\McpOauthProbeRequest;
use Revolution\Copilot\Types\Rpc\SessionMcpOauthCancelLoginRequest;
use Revolution\Copilot\Types\Rpc\SessionMcpOauthPrepareLoginRequest;
$result = $session->rpc()->mcp()->login(new McpOauthLoginRequest(serverName: 'my-server'));
// $result->authorizationUrl - OAuth 流程的 URL(需要认证时)
// 在主机管理的 OAuth 重定向流程中指定 redirectUri,并在 callback 后调用 complete()
$result = $session->rpc()->mcp()->login(new McpOauthLoginRequest(
serverName: 'my-server',
redirectUri: 'http://localhost:3000/oauth/callback',
));
$session->rpc()->mcp()->complete(new McpOauthCompleteRequest(
authorizationId: $result->authorizationId,
callbackUrl: $callbackUrl,
));
// 确认 OAuth 状态,并准备或取消与所拥有安装关联的登录
$probe = $session->rpc()->mcp()->oauth()->probe(new McpOauthProbeRequest(serverName: 'my-server'));
$prepared = $session->rpc()->mcp()->oauth()->prepareLogin(new SessionMcpOauthPrepareLoginRequest(
serverName: 'my-server',
expectedInstallationId: 'installation-id',
));
$cancelled = $session->rpc()->mcp()->oauth()->cancelLogin(new SessionMcpOauthCancelLoginRequest(
expectedInstallationId: 'installation-id',
loginId: $prepared->loginId,
));
// plugins(experimental:插件列表)
$session->rpc()->plugins()->list();
// 重新加载插件
$session->rpc()->plugins()->reload();
// 指定选项重新加载
use Revolution\Copilot\Types\Rpc\PluginsReloadRequest;
$session->rpc()->plugins()->reload(new PluginsReloadRequest(
reloadMcp: true, // 重新加载 MCP 服务器配置
reloadHooks: true, // 重新加载钩子
reloadCustomAgents: false, // 不重新加载自定义代理
));
// extensions(experimental:扩展管理)
$session->rpc()->extensions()->list();
$session->rpc()->extensions()->enable(new ExtensionsEnableRequest(id: 'project:my-ext'));
$session->rpc()->extensions()->disable(new ExtensionsDisableRequest(id: 'project:my-ext'));
$session->rpc()->extensions()->reconcile(); // host-only:重新同步有效 membership 并返回 settled list
$session->rpc()->extensions()->reload();
// compaction → 更名为 history
$session->rpc()->history()->compact();
// 中断手动压缩 / 取消后台压缩
$session->rpc()->history()->abortManualCompaction();
$session->rpc()->history()->cancelBackgroundCompaction();
// 获取用于交接到其他会话的摘要
$summary = $session->rpc()->history()->summarizeForHandoff()->summary;
// 截断特定事件之后的历史
$session->rpc()->history()->truncate(new HistoryTruncateRequest(
eventId: 'evt-123', // 此事件及之后的所有事件都会被删除
));
// tools(协议 v3+:响应 external_tool.requested 事件)
// 在发送前初始化并验证运行时工具集(experimental,用于子代理会话等)
$session->rpc()->tools()->initializeAndValidate();
// 返回空的 ToolsInitializeAndValidateResult
// 通过原生调用管道执行当前向会话公开的工具(experimental)
use Revolution\Copilot\Types\Rpc\ToolsExecuteRequest;
$result = $session->rpc()->tools()->execute(new ToolsExecuteRequest(
name: 'search',
arguments: ['query' => 'Laravel'],
toolCallId: 'call-123', // 可选。用于与调用关联
));
if (is_string($result)) {
// 简单的文本结果
echo $result;
} else {
// ToolResultExpanded:textResultForLlm、resultType、newMessages、
// mcpMeta、structuredContent、taskCompletionDecision 等
echo $result->textResultForLlm;
// resultType 保存 ToolResultType 或表示未知值的 string
}
// 也可以用数组指定。toolCallId 可以省略,sessionId 会被此会话的值覆盖
$session->rpc()->tools()->execute([
'name' => 'search',
'arguments' => ['query' => 'Copilot'],
]);
// arguments: [] 会作为 JSON 对象 {} 发送,scalar/null/非空数组保持其 JSON 值
// execute() 会经过常规的原生工具调用管道,因此不会绕过权限确认。
// 如果工具需要批准,会照常由 SessionConfig 的 onPermissionRequest 处理程序处理。
$session->rpc()->tools()->handlePendingToolCall(new HandlePendingToolCallRequest(
requestId: '...',
result: '工具的执行结果',
));
// 获取会话当前的工具元数据(experimental)
$session->rpc()->tools()->getCurrentMetadata();
// 完全替换此连接提供的外部工具(experimental)
$session->rpc()->tools()->set(['tools' => [
['name' => 'search', 'description' => 'Search documents', 'parameters' => ['type' => 'object']],
]]);
// 获取应用于当前会话的子代理设置(experimental)
$settings = $session->rpc()->tools()->getSubagentSettings();
// $settings->subagents 为 SubagentSettings,没有设置块时为 null
// 更新子代理设置(experimental)
use Revolution\Copilot\Enums\SubagentSettingsEntryContextTier;
use Revolution\Copilot\Types\Rpc\SubagentSettings;
use Revolution\Copilot\Types\Rpc\SubagentSettingsEntry;
use Revolution\Copilot\Types\Rpc\UpdateSubagentSettingsRequest;
$result = $session->rpc()->tools()->updateSubagentSettings(new UpdateSubagentSettingsRequest(
subagents: new SubagentSettings(agents: [
'my-agent' => new SubagentSettingsEntry(
contextTier: SubagentSettingsEntryContextTier::Default,
model: 'gpt-5',
),
]),
));
// 应用设置后返回空的 ToolsUpdateSubagentSettingsResult
// permissions(协议 v3+:响应 permission.requested 事件)
use Revolution\Copilot\Types\Rpc\FolderTrustAddParams;
use Revolution\Copilot\Types\Rpc\PermissionLocationAddToolApprovalParams;
use Revolution\Copilot\Types\Rpc\PermissionLocationApplyParams;
use Revolution\Copilot\Types\Rpc\PermissionLocationResolveParams;
use Revolution\Copilot\Types\Rpc\PermissionsLocationsAddToolApprovalDetailsCommands;
$session->rpc()->permissions()->handlePendingPermissionRequest(new PermissionDecisionRequest(
requestId: '...',
result: PermissionDecision::approveOnce(),
));
// 自动批准会话内的所有权限请求
$session->rpc()->permissions()->setApproveAll(new PermissionsSetApproveAllRequest(enabled: true));
// 设置权限模式 (manual / assisted / allow-all)
$session->rpc()->permissions()->setMode(new PermissionsSetModeRequest(mode: PermissionMode::AllowAll));
// 获取当前权限模式
$mode = $session->rpc()->permissions()->getMode();
// 重置会话范围的权限批准
$session->rpc()->permissions()->resetSessionApprovals();
// 获取当前 pending 中的权限请求列表
$pending = $session->rpc()->permissions()->pendingRequests();
// 启用/禁用权限请求的事件桥接
$session->rpc()->permissions()->setRequired(new PermissionsSetRequiredRequest(required: true));
// 添加/删除权限规则
$session->rpc()->permissions()->modifyRules(new PermissionsModifyRulesParams(
scope: PermissionsModifyRulesScope::SESSION,
add: [['tool' => 'bash', 'decision' => 'allow']],
));
// 通知已显示权限提示
$session->rpc()->permissions()->notifyPromptShown(new PermissionPromptShownNotification(message: 'tool permission prompt'));
// 路径权限
$paths = $session->rpc()->permissions()->paths()->list();
$session->rpc()->permissions()->paths()->add(new PermissionPathsAddParams(path: '/path/to/allow'));
$session->rpc()->permissions()->paths()->updatePrimary(new PermissionPathsUpdatePrimaryParams(path: '/path/to/allow'));
$session->rpc()->permissions()->paths()->isPathWithinAllowedDirectories(new PermissionPathsAllowedCheckParams(path: '/path/to/allow/file.txt'));
$session->rpc()->permissions()->paths()->isPathWithinWorkspace(new PermissionPathsWorkspaceCheckParams(path: '/path/to/allow/file.txt'));
// URL 权限
$session->rpc()->permissions()->urls()->setUnrestrictedMode(
new PermissionUrlsSetUnrestrictedModeParams(enabled: true)
);
// 解析工作目录的权限位置,并应用已保存的规则
$location = $session->rpc()->permissions()->locations()->resolve(
new PermissionLocationResolveParams(workingDirectory: '/path/to/repo')
);
$session->rpc()->permissions()->locations()->apply(
new PermissionLocationApplyParams(workingDirectory: '/path/to/repo')
);
// 将工具批准持久化到权限位置
$session->rpc()->permissions()->locations()->addToolApproval(
new PermissionLocationAddToolApprovalParams(
approval: new PermissionsLocationsAddToolApprovalDetailsCommands(commandIdentifiers: ['bash']),
locationKey: $location->locationKey,
)
);
// 确认和添加文件夹信任状态
$trusted = $session->rpc()->permissions()->folderTrust()->isTrusted(['path' => '/path/to/repo']);
$session->rpc()->permissions()->folderTrust()->addTrusted(new FolderTrustAddParams(path: '/path/to/repo'));
// commands:获取会话中可用的斜杠命令列表
use Revolution\Copilot\Types\Rpc\CommandsListRequest;
$list = $session->rpc()->commands()->list();
// $list->commands - SlashCommandInfo 的数组
// $list->commands[0]->name - 命令名称(不含斜杠)
// $list->commands[0]->description - 命令说明
// $list->commands[0]->kind - 命令类别(builtin, skill, client)
// $list->commands[0]->allowDuringAgentExecution - 代理执行期间是否可用
// 指定筛选条件获取列表
$list = $session->rpc()->commands()->list(new CommandsListRequest(
includeBuiltins: true,
includeSkills: false,
));
// commands:调用斜杠命令
use Revolution\Copilot\Types\Rpc\CommandsInvokeRequest;
$result = $session->rpc()->commands()->invoke(new CommandsInvokeRequest(
name: 'help',
input: 'optional input text',
));
// commands:响应命令调用事件
$session->rpc()->commands()->handlePendingCommand(new CommandsHandlePendingCommandRequest(
requestId: '...',
));
// commands:同步执行斜杠命令
use Revolution\Copilot\Types\Rpc\ExecuteCommandParams;
$result = $session->rpc()->commands()->execute(new ExecuteCommandParams(
commandName: 'review',
args: 'src/',
), timeout: 120.0); // 等待 CLI 响应的时间(秒)。省略时为 30 秒
// $result->error - 执行失败时的错误消息(成功时为 null)
// commands:响应已加入队列的命令
$session->rpc()->commands()->respondToQueuedCommand(new CommandsRespondToQueuedCommandRequest(
requestId: '...',
result: new QueuedCommandResult(handled: true),
));
// ui:响应 UI elicitation 请求
$session->rpc()->ui()->elicitation(new UIElicitationRequest(
message: '向用户提出的问题',
requestedSchema: ['type' => 'object', 'properties' => [...]],
));
// ui:不添加到对话历史的临时询问
$result = $session->rpc()->ui()->ephemeralQuery(new UIEphemeralQueryRequest(
question: '请告诉我到目前为止对话的要点。',
));
// $result->answer
// ui:响应待处理的 elicitation 请求(通过 elicitation.requested 事件)
$session->rpc()->ui()->handlePendingElicitation(new UIHandlePendingElicitationRequest(
requestId: '...',
result: ['action' => 'accept', 'content' => ['name' => 'John']],
));
// ui:通过广播事件响应待处理的请求
use Revolution\Copilot\Enums\UIAutoModeSwitchResponse;
use Revolution\Copilot\Types\Rpc\UIExitPlanModeResponse;
use Revolution\Copilot\Types\Rpc\UIHandlePendingAutoModeSwitchRequest;
use Revolution\Copilot\Types\Rpc\UIHandlePendingExitPlanModeRequest;
use Revolution\Copilot\Types\Rpc\UIHandlePendingSamplingRequest;
use Revolution\Copilot\Types\Rpc\UIHandlePendingUserInputRequest;
use Revolution\Copilot\Types\Rpc\UIUserInputResponse;
$result = $session->rpc()->ui()->handlePendingUserInput(new UIHandlePendingUserInputRequest(
requestId: '...',
response: new UIUserInputResponse(answer: 'blue', wasFreeform: false),
));
$result = $session->rpc()->ui()->handlePendingExitPlanMode(new UIHandlePendingExitPlanModeRequest(
requestId: '...',
response: new UIExitPlanModeResponse(approved: true, selectedAction: 'autopilot_fleet'),
));
$result = $session->rpc()->ui()->handlePendingAutoModeSwitch(new UIHandlePendingAutoModeSwitchRequest(
requestId: '...',
response: UIAutoModeSwitchResponse::Yes,
));
$result = $session->rpc()->ui()->handlePendingSampling(new UIHandlePendingSamplingRequest(
requestId: '...',
response: ['result' => '...'],
));
// 各方法返回 UIHandlePendingResult。可以通过 $result->success 确认响应是否被接受。
// SessionConfig 的 onExitPlanModeRequest / onAutoModeSwitchRequest 会通过 reverse-RPC 自动响应。
// 通过事件手动响应同一请求时,不要同时使用两种途径。
// log:将消息记录到会话时间线
$session->rpc()->log()->log(new LogRequest(message: '已开始处理'));
$session->rpc()->log()->log(new LogRequest(message: '磁盘使用量较高', level: LogLevel::WARNING));
$session->rpc()->log()->log(new LogRequest(message: '发生了错误', level: LogLevel::ERROR));
$session->rpc()->log()->log(new LogRequest(message: '调试信息', ephemeral: true));
// shell:在会话内执行 shell 命令
$result = $session->rpc()->shell()->exec(new ShellExecRequest(command: 'ls -la'));
// 通过 $result->processId 获取进程 ID,用于 kill 或跟踪输出
$session->rpc()->shell()->exec(new ShellExecRequest(
command: 'npm test',
cwd: '/path/to/project',
timeout: 60000, // 毫秒
));
// 停止正在运行的 shell 进程
$session->rpc()->shell()->kill(new ShellKillRequest(
processId: $result->processId,
signal: 'SIGTERM', // SIGTERM(默认), SIGKILL, SIGINT
));
// 取消用户请求的 shell 命令
use Revolution\Copilot\Types\Rpc\ShellExecuteUserRequestedRequest;
use Revolution\Copilot\Types\Rpc\ShellCancelUserRequestedRequest;
$execution = $session->rpc()->shell()->executeUserRequested(new ShellExecuteUserRequestedRequest(
requestId: 'req-123',
command: 'pwd',
), timeout: 120.0); // 等待 CLI 响应的时间(秒)。省略时为 30 秒
// $execution->toolCallId / success / output / exitCode / error
// 如需在执行中从其他处理取消,请指定相同的 requestId
$cancelled = $session->rpc()->shell()->cancelUserRequested(
new ShellCancelUserRequestedRequest(requestId: 'req-123')
);
// $cancelled->cancelled - 是否取消成功
// telemetry(experimental:会话遥测)
use Revolution\Copilot\Types\Rpc\TelemetrySetFeatureOverridesRequest;
$telemetry = $session->rpc()->telemetry();
$engagement = $telemetry->getEngagementId();
// $engagement->engagementId - 可用时为 engagement ID,否则为 null
$telemetry->setFeatureOverrides(new TelemetrySetFeatureOverridesRequest(
features: ['feature-key' => 'override-value'],
));
// 覆盖值会附加到之后的遥测事件中,并替换之前的覆盖值。
// 这与 SessionConfig::$enableSessionTelemetry(启用内部遥测)是分开的。
// usage(experimental:会话使用量指标)
$metrics = $session->rpc()->usage()->getMetrics();
// $metrics->totalPremiumRequestCost - 高级请求的总成本
// $metrics->totalUserRequests - 用户请求的总数
// $metrics->codeChanges - 代码变更指标(新增行数、删除行数、变更文件数)
// $metrics->modelMetrics - 每个模型的令牌使用量和请求数
// $metrics->currentModel - 当前模型标识符
// auth:获取会话的认证状态
$status = $session->rpc()->auth()->getStatus();
// $status->isAuthenticated - 是否已认证
// $status->authType - 认证类型(AuthInfoType enum:gh-cli, token, env 等)
// $status->login - GitHub 登录名
// $status->host - GitHub 主机
// $status->copilotPlan - Copilot 计划(individual, business 等)
// $status->statusMessage - 认证状态消息
// provider:获取会话所使用提供程序的端点信息 (experimental)
// 在 BYOK(Bring Your Own Key)配置中直接调用 LLM 后端时使用
use Revolution\Copilot\Types\Rpc\ProviderGetEndpointRequest;
$endpoint = $session->rpc()->provider()->getEndpoint();
// $endpoint->baseUrl - 传给 LLM 客户端库的基础 URL
// $endpoint->type - 提供程序类型(openai, anthropic 等)
// $endpoint->headers - 请求中包含的 HTTP 标头
// $endpoint->apiKey - 认证信息(nullable)
// $endpoint->sessionToken - 短期会话认证信息(nullable)
// $endpoint->wireApi - 提供程序特定的 wire API(nullable)
// 获取特定模型的端点
$endpoint = $session->rpc()->provider()->getEndpoint(
new ProviderGetEndpointRequest(model: 'gpt-5')
);
// provider:从 BYOK 注册表中撤下主机管理的模型 (experimental)
$result = $session->rpc()->provider()->withdraw(['models' => ['provider/model']]);
// $result->withdrawn, $result->providersRemoved, $result->modelDeselected
// queue:查看和管理队列中面向用户的项目
$pending = $session->rpc()->queue()->pendingItems();
// $pending->items - QueuePendingItems 的数组
// $pending->items[0]->kind - QueuePendingItemsKind(Command / Message)
$removed = $session->rpc()->queue()->removeMostRecent();
// $removed->removed - 是否成功删除最近的项目
$session->rpc()->queue()->clear();
// 在指定位置插入项目(position 从 0 开始)
use Revolution\Copilot\Types\Rpc\QueueInsertAtRequest;
use Revolution\Copilot\Types\Rpc\QueueInsertMessage;
$queue = $session->rpc()->queue();
$inserted = $queue->insertAt(new QueueInsertAtRequest(
position: 0,
message: new QueueInsertMessage(prompt: 'Review the changes'),
));
// $inserted->id - 插入项目的 ID
$queue->moveItem(['id' => $inserted->id, 'toPosition' => 1]);
$duplicated = $queue->duplicateAt(['id' => $inserted->id]);
$queue->updateText(['id' => $duplicated->id, 'prompt' => 'Review and test the changes']);
$removedAt = $queue->removeAt(['id' => $duplicated->id]);
// $removedAt->removed - 是否成功删除项目
$sent = $queue->sendNow(['id' => $inserted->id]);
// $sent->steered - 是否已发送到正在执行的回合
// 暂停排出队列。暂停不会自动解除,因此务必使用 false 恢复
$queue->setDrainPaused(['paused' => true]);
$queue->setDrainPaused(['paused' => false]);
// schedule(experimental:已计划提示的管理)
$schedule = $session->rpc()->schedule()->list();
// $schedule->entries - ScheduleEntry 的数组
use Revolution\Copilot\Types\Rpc\ScheduleStopRequest;
$stopped = $session->rpc()->schedule()->stop(new ScheduleStopRequest(id: 1));
// $stopped->stopped - 是否成功停止
// tasks(experimental:后台任务管理)
// 启动代理任务并获取 ID
use Revolution\Copilot\Types\Rpc\TasksStartAgentRequest;
$result = $session->rpc()->tasks()->startAgent(new TasksStartAgentRequest(
agentType: 'explore',
prompt: 'Search for all TODO comments in the codebase',
name: 'todo-search',
description: 'Find TODO items', // 可选
model: 'gpt-4o', // 可选
));
// $result->agentId - 后台任务的 ID
// 获取当前任务列表
$list = $session->rpc()->tasks()->list();
// $list->tasks - TaskAgentInfo、TaskShellInfo、TaskClientInfo 的数组
// 使用游标增量读取已分离 shell 的输出
use Revolution\Copilot\Types\Rpc\TasksReadOutputRequest;
$cursor = null;
do {
$output = $session->rpc()->tasks()->readOutput(new TasksReadOutputRequest(
id: 'shell-1',
cursor: $cursor,
waitMs: 1000,
));
echo $output->text;
$cursor = $output->cursor;
} while ($output->exit === null);
// $output->exit->status - 任务的结束状态
// $output->exit->exitCode - 进程的退出码(nullable)
// 单次读取最多 64 KiB。waitMs 默认值为 0,最大 30000ms。
// stdout 和 stderr 会合并。游标失效时,请不带游标重新读取。
// 将应用程序所有的作业注册为任务
use Revolution\Copilot\Types\Rpc\TasksRegisterRequest;
$registered = $session->rpc()->tasks()->register(new TasksRegisterRequest(
clientTaskId: 'import-123',
description: '导入客户数据',
cancellable: true,
displayName: '客户导入', // 可选
));
// $registered->task->id - CLI 分配的任务 ID
// $registered->created / $registered->reclaimed - 新建或重新获取孤立任务的状态
// 更新客户端所有任务的进度
use Revolution\Copilot\Enums\TaskClientActiveStatus;
use Revolution\Copilot\Enums\TaskClientUpdateKind;
use Revolution\Copilot\Types\Rpc\TaskClientUpdate;
use Revolution\Copilot\Types\Rpc\TasksUpdateRequest;
$updated = $session->rpc()->tasks()->update(new TasksUpdateRequest(
id: $registered->task->id,
sequence: 1,
update: new TaskClientUpdate(
kind: TaskClientUpdateKind::Progress,
status: TaskClientActiveStatus::Running,
phase: 'importing',
percentage: 25,
),
));
// sequence 每次更新时递增
// 报告完成
$completed = $session->rpc()->tasks()->update(new TasksUpdateRequest(
id: $registered->task->id,
sequence: 2,
update: new TaskClientUpdate(
kind: TaskClientUpdateKind::Completed,
result: ['imported' => 100],
),
));
// 获取任务进度(未跟踪时为 null)
$progress = $session->rpc()->tasks()->getProgress(['id' => $registered->task->id])->progress;
// TaskAgentProgress、TaskClientProgress、TaskShellProgress 或 null
// 更新已分离 shell 的状态,并等待后台工作完成
$session->rpc()->tasks()->refresh();
$session->rpc()->tasks()->waitForPending();
// 获取当前可以转入后台的任务,或立即将该任务转入后台
$promotable = $session->rpc()->tasks()->getCurrentPromotable()->task;
$promotedCurrent = $session->rpc()->tasks()->promoteCurrentToBackground()->task;
// 将任务移至后台模式(解除同步等待)
use Revolution\Copilot\Types\Rpc\TasksPromoteToBackgroundRequest;
$promoted = $session->rpc()->tasks()->promoteToBackground(
new TasksPromoteToBackgroundRequest(id: $result->agentId)
);
// $promoted->promoted - 是否移动成功
// 取消任务
use Revolution\Copilot\Types\Rpc\TasksCancelRequest;
$cancelled = $session->rpc()->tasks()->cancel(
new TasksCancelRequest(id: $result->agentId)
);
// $cancelled->cancelled - 是否取消成功
// 删除已完成或已取消的任务
use Revolution\Copilot\Types\Rpc\TasksRemoveRequest;
$removed = $session->rpc()->tasks()->remove(
new TasksRemoveRequest(id: $result->agentId)
);
// $removed->removed - 是否删除成功
// 向正在执行的代理任务发送消息(引导)
use Revolution\Copilot\Types\Rpc\TasksSendMessageRequest;
$sent = $session->rpc()->tasks()->sendMessage(
new TasksSendMessageRequest(id: $result->agentId, message: '请也搜索其他目录')
);
// $sent->sent - 消息是否发送成功
// $sent->error - 发送失败时的错误消息(nullable)
// 仅中断主回合,队列中的提示保留到下一回合
use Revolution\Copilot\Types\Rpc\InterruptMainTurnRequest;
$interrupted = $session->rpc()->interruptMainTurn(
new InterruptMainTurnRequest(flushQueued: true)
);
// $interrupted->interrupted - 是否成功中断正在执行的主回合
// 不中断主回合,取消所有正在执行的后台代理
$cancelledCount = $session->rpc()->cancelAllBackgroundAgents();
// 已取消的代理数量
remote(experimental:远程会话支持)
use Revolution\Copilot\Enums\RemoteSessionMode;
use Revolution\Copilot\Types\Rpc\RemoteEnableRequest;
use Revolution\Copilot\Types\Rpc\RemoteEnableResult;
// 启用远程会话
// 在 GitHub 仓库的工作目录中执行时,
// 会返回可以从 GitHub Web 或移动端访问会话的 URL
$result = $session->rpc()->remote()->enable();
// $result->remoteSteerable - 是否启用了远程引导
// $result->url - GitHub 前端 URL(可能为 null)
// 指定模式启用
// RemoteSessionMode::Off - 仅本地(默认)
// RemoteSessionMode::Export - 将会话事件导出到 GitHub(不支持远程引导)
// RemoteSessionMode::On - 同时启用导出和远程引导
$result = $session->rpc()->remote()->enable(new RemoteEnableRequest(
mode: RemoteSessionMode::Export,
));
// 禁用远程会话
$session->rpc()->remote()->disable();
sandbox(experimental:沙箱控制)
// 在创建会话之前确认主机的沙箱支持状态
$hostSupport = $client->rpc()->sandbox()->getHostSupport();
// $hostSupport->supported, $hostSupport->reason, $hostSupport->capabilities
// 确认是否需要 Proxy CA 及其安装支持状态
$proxyCa = $client->rpc()->sandboxProxyCa();
$relevant = $proxyCa->checkRelevance(['enabled' => true]);
$installable = $proxyCa->checkInstallationSupport();
use Revolution\Copilot\Types\Rpc\SandboxDisableForSessionRequest;
use Revolution\Copilot\Types\Rpc\SandboxGrantPathForRequestRequest;
// 获取沙箱强制状态
$status = $session->rpc()->sandbox()->getEnforcementStatus();
// 沙箱中新 shell 可能可用的认证信息(不返回值)
$credentials = $session->rpc()->sandbox()->getCredentialSuggestions();
// $credentials->suggestions 是 SandboxCredentialSuggestion 的数组
// sandbox 禁用或没有候选时为空数组。不会更改设置或网络权限。
// 批准待处理的沙箱绕过权限请求,并在整个会话中禁用沙箱
$result = $session->rpc()->sandbox()->disableForSession(new SandboxDisableForSessionRequest(requestId: $requestId));
// $result->success, $result->enabled
// 批准待处理的沙箱提权权限请求所提出的路径授权(sandboxPathGrant),
// 将该路径添加到会话的沙箱策略中,并让被阻止的操作
// 在沙箱内重新执行
$result = $session->rpc()->sandbox()->grantPathForRequest(new SandboxGrantPathForRequestRequest(requestId: $requestId));
// 也可以使用数组输入:->grantPathForRequest(['requestId' => $requestId])
// $result->success
// 如果目标请求已不再处于待处理状态、不带有 sandboxPathGrant,
// 或在当前托管策略下授权无效,则会失败。
// 这是仅允许特定路径的最小权限手段,比禁用整个会话的 disableForSession() 更推荐。
// 此路径不会被持久化(仅限本次会话,不会延续到之后的会话)。
// 保存沙箱设置的主机如有需要,请自行保存路径。
$request['sandboxPathGrant'] 时,使用目标待处理请求的 requestId 进行调用。由于此 RPC 本身会响应权限请求,成功时请返回 PermissionDecision::noResult(),以免发送常规响应。
if (isset($request['sandboxPathGrant'])) {
$result = $session->rpc()->sandbox()->grantPathForRequest([
'requestId' => $requestId,
]);
return $result->success
? PermissionDecision::noResult()
: PermissionDecision::reject();
}
metadata(experimental:会话信息)
use Revolution\Copilot\Types\Rpc\MetadataUpdateClientMetadataRequest;
// 获取会话当前的活动状态
$activity = $session->rpc()->metadata()->activity();
// $activity->abortable - 是否存在可中止的工作
// $activity->hasActiveWork - 回合或后台任务等工作是否正在运行
// 获取与此本地会话关联的客户端所有元数据
$metadata = $session->rpc()->metadata()->getClientMetadata();
// 原子地更新元数据(按 clear → remove → set 的顺序应用)
$metadata = $session->rpc()->metadata()->updateClientMetadata(new MetadataUpdateClientMetadataRequest(
set: ['my-app/key' => 'value'],
));
Session canvas
Canvas RPC 通过$session->rpc()->canvas() 调用。list() 在
CanvasList 的 canvases 中返回列表,listOpen() 在 CanvasListOpenResult 的
openCanvases 中返回列表。open() 返回 OpenCanvasInstance,
action 调用返回 CanvasActionInvokeResult。close()
没有返回值。
$canvases = $session->rpc()->canvas();
$available = $canvases->list()->canvases;
$openInstances = $canvases->listOpen()->openCanvases;
$instance = $canvases->open([
'canvasId' => 'editor',
'instanceId' => 'editor-1',
'input' => ['file' => 'README.md'],
]);
$result = $canvases->action()->invoke([
'instanceId' => $instance->instanceId,
'actionName' => 'format',
'input' => ['style' => 'compact'],
]);
$canvases->close(['instanceId' => $instance->instanceId]);
canvas.open / canvas.close /
canvas.action.invoke)由 SessionConfig / ResumeSessionConfig 的
canvasHandler 处理。Canvas 的渲染是主机 UI 的责任。
workflow
Copilot CLI 1.0.89 系列中新增的工作流、连接器、诊断等实验性 RPC API 也可以通过SessionRpc 使用。这些可变协议 payload 以 JSON 兼容的数组传递,响应也以数组形式返回。每次调用都会自动添加当前的 sessionId。
// 执行在 CLI 端注册的工作流,并确认执行状态
$run = $session->rpc()->workflow()->run([
'name' => 'weekly-summary',
'args' => ['team' => 'platform'],
]);
$current = $session->rpc()->workflow()->getRun(['runId' => $run['runId']]);
// 诊断信息和连接器列表
$diagnostics = $session->rpc()->diagnostics()->read(['sources' => ['mcp']]);
$connectors = $session->rpc()->connectors()->list();
$account = $session->rpc()->connectors()->getAccount(); // 如果有选中的账户则为 ConnectorSessionAccount
// 插件市场
$marketplaces = $session->rpc()->plugins()->marketplaces()->list();
// 结束活动会话,并在后台处理 sessionEnd hook
$session->rpc()->shutdown(['detachSessionEndHooks' => true]);
workflow() 调用 CLI 提供的低级 RPC。像 Node.js SDK 那样将 PHP 闭包注册为工作流并执行的高级 API,在此次同步中并未添加。这些新的 RPC 分组依赖于 CLI 版本且属于实验性功能,请仅在支持的 CLI 中使用。
SessionFS 回调类型
定义了用于会话范围文件系统操作的回调类型(Request/Result)。这些是 Copilot CLI 回调客户端时使用的请求/响应类型。| 类型类 | 用途 |
|---|---|
SessionFsReadFileRequest / SessionFsReadFileResult | 读取文件 |
SessionFsWriteFileRequest | 写入文件 |
SessionFsAppendFileRequest | 追加文件 |
SessionFsExistsRequest / SessionFsExistsResult | 文件存在性检查 |
SessionFsStatRequest / SessionFsStatResult | 获取文件元数据 |
SessionFsMkdirRequest | 创建目录 |
SessionFsReaddirRequest / SessionFsReaddirResult | 目录列表 |
SessionFsReaddirWithTypesRequest / SessionFsReaddirWithTypesResult | 带类型的目录列表 |
SessionFsRmRequest | 删除文件 / 目录 |
SessionFsRenameRequest | 重命名文件 / 目录 |
SessionFsStatResult / SessionFsReaddirResult / SessionFsReaddirWithTypesResult 带有失败时使用的可选 error(SessionFsError)。
SessionFs 处理程序
Client 除了sessionFs.readFile / readFileBytes / writeFile / writeFileBytes 之外,还会将 appendFile、exists、stat、mkdir、readdir、readdirWithTypes、rm、rename、sqliteQuery、sqliteTransaction、sqliteExists 各请求分派给 Session 的提供程序。文件操作失败时以 ENOENT(ENOENT 或 errno 2)/ UNKNOWN 的 error 返回。exists 在发生异常时返回 false,stat 在失败时返回零值加 error。提供程序不支持的操作会产生 UNKNOWN 错误(SQLite 相关为 JSON-RPC 错误)。sqliteQuery/sqliteExists 的异常为 JSON-RPC 错误,sqliteTransaction 的失败以结果中的 error(带 errorClass)返回。
提供程序端的接口(SessionFsFilesystemProvider、SessionFsSqliteCapableProvider、SessionFsSqliteProvider、SessionFsSqliteTransactionProvider)作为独立接口添加,以免破坏原有的 SessionFsProvider(2 个方法)。
SQLite 回调类型
| 类型类 | 用途 |
|---|---|
SessionFsSetProviderCapabilities | 声明提供程序支持的功能(SQLite 支持等) |
SessionFsSqliteExistsRequest / SessionFsSqliteExistsResult | 检查 SQLite 数据库是否存在 |
SessionFsSqliteQueryRequest / SessionFsSqliteQueryResult | 执行 SQLite 查询 |
SessionFsSqliteTransactionRequest / SessionFsSqliteTransactionStatement / SessionFsSqliteTransactionResult | SQLite 事务(原子地执行多条语句) |
SessionFsSqliteTransactionError | 事务失败的分类(errorClass:busyOrLocked / fatal / postCommitAmbiguous。enum SessionFSSqliteTransactionErrorClass) |
SessionFsSetProviderRequest 的 capabilities 中传入 SessionFsSetProviderCapabilities(sqlite: true)。
use Revolution\Copilot\Types\Rpc\SessionFsSetProviderCapabilities;
use Revolution\Copilot\Types\Rpc\SessionFsSetProviderRequest;
$client->rpc()->sessionFs()->setProvider(new SessionFsSetProviderRequest(
initialCwd: '/app',
sessionStatePath: '.copilot/sessions',
capabilities: new SessionFsSetProviderCapabilities(sqlite: true),
));
sessionFs(或 config/copilot.php 中的 session_fs),Client::start() 会仅发送一次 sessionFs.setProvider。会话需要 createSessionFsProvider(请参考 SessionConfig)。
SessionFsSqliteQueryType enum(src/Enums/)表示查询类型:
Exec:DDL/多条语句(无结果)Query:SELECT(返回行)Run:INSERT/UPDATE/DELETE(返回 rowsAffected)
src/Types/Rpc/。
以数组形式指定参数
参数和返回值都使用专用类,但参数也可以用数组指定。$session->rpc()->mode()->set(['mode' => 'plan']);
测试
由于无法使用Copilot::fake() 的 mock,请使用 Copilot::expects('client') 或 Copilot::expects('start') 进行 mock。
最新信息请参考 GitHub 仓库。