关于 add-policy
面向 VS Code 开发者的技能指南,说明如何添加、修改和验证配置策略,涵盖操作系统策略与账户级策略。
主要功能
- 01添加并注册配置策略
- 02接入账户级策略数据
- 03导出策略目录与平台文件
- 04覆盖扩展配置策略
- 05运行类型检查与策略测试
适用场景
- 01为配置属性新增策略字段
- 02评审策略注册相关改动
- 03为扩展设置添加企业策略
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ vscode --skill add-policy安装后在 Claude Code 中输入/add-policy确认已加载。
附带文件github-managed-settings.mdlocal-testing.md
详细介绍
添加配置策略
管理员可以通过操作系统级机制(Windows 组策略、macOS 托管偏好设置、Linux 配置文件)或 Copilot 账户级策略数据锁定配置设置。本指南介绍完整流程。
使用场景
- 向任何配置属性添加新的
policy:字段 - 修改现有策略(重命名、更改类别等)
- 审查涉及策略注册的 PR
- 通过
IPolicyData添加基于账户的策略支持 - 接入企业托管设置(原生 MDM / GitHub 服务端)——参见 github-managed-settings.md
- 通过
policyReference让一个策略管理多个设置 - 在本地测试账户/托管设置策略,无需真实后端——参见 local-testing.md
架构概览
策略来源(分层,后写入者优先)
| 来源 | 实现 | 策略读取方式 |
|---|---|---|
| 操作系统级(Windows 注册表、macOS plist) | NativePolicyService,通过 @vscode/policy-watcher | 监视 Software\Policies\Microsoft\{productName}(Windows)或包标识符偏好设置(macOS) |
| Linux 文件 | FilePolicyService | 读取 /etc/vscode/policy.json |
| 账户/GitHub | AccountPolicyService | 从 IDefaultAccountService.policyData 读取 IPolicyData,并应用 value() 函数。服务端下发的托管设置位于 policyData.managedSettings;原生 MDM(INativeManagedSettingsService)和磁盘上的文件(IFileManagedSettingsService)是独立输入,AccountPolicyService 会在 getPolicyData() 中通过 pickManagedSettings(nativeMdm, server, file) 合并它们(按键优先级为原生 MDM > 服务端 > 文件;高优先级渠道锁定的键不能被覆盖,未设置的键则会沿用低优先级渠道的值) |
| Copilot 托管设置(原生 MDM) | NativeManagedSettingsService,通过 @vscode/policy-watcher | 监视 SOFTWARE\Policies\GitHubCopilot(Windows)/ com.github.copilot 偏好设置(macOS);为规范的 managedSettings 集合提供数据——参见 github-managed-settings.md |
| Copilot 托管设置(文件) | FileManagedSettingsService | 在主进程中,从各操作系统对应的已知路径读取并监视 managed-settings.json,再通过 IPC 暴露给渲染进程;优先级最低的托管设置渠道——参见 github-managed-settings.md |
| 多路复用 | MultiplexPolicyService | 在主进程中组合多个操作系统/文件策略读取器;在桌面端和 Agents 窗口渲染进程中,组合主进程的 PolicyChannelClient 与 AccountPolicyService |
关键文件
| 文件 | 用途 |
|---|---|
src/vs/base/common/policy.ts | PolicyCategory 枚举、IPolicy 接口、IPolicyReference、ManagedSettingsData、IManagedSettingsPolicyDefinitions |
src/vs/platform/policy/common/policy.ts | IPolicyService、AbstractPolicyService、PolicyDefinition、toSerializablePolicyDefinition(为 IPC 移除不可克隆的 value())、getRestrictedPolicyValue |
src/vs/platform/policy/common/copilotManagedSettings.ts | Managed settings 键常量和已知文件路径、collectManagedSettingsDefinitions、projectManagedSettings、供所有通道共用的 normalizeManagedSettings(统一规范化器)、pickManagedSettings(按键确定通道优先级)、INativeManagedSettingsService / IFileManagedSettingsService |
src/vs/platform/policy/node/nativeManagedSettingsService.ts | 用于 Copilot managed settings 的原生 MDM 监视器(@vscode/policy-watcher) |
src/vs/platform/policy/common/fileManagedSettingsService.ts | 基于文件的通道:从每个操作系统的已知路径读取并监视 managed-settings.json,通过 normalizeManagedSettings 进行规范化 |
src/vs/platform/configuration/common/configurations.ts | PolicyConfiguration — 将策略映射到配置值;将 JSON 字符串形式的 managed settings 解析回类型化值;将值应用到 policyReference 设置 |
src/vs/platform/configuration/common/configurationRegistry.ts | policy / policyReference 注册;getPolicyReferenceConfigurations()(名称 → 从属设置) |
src/vs/workbench/services/policies/common/accountPolicyService.ts | 基于帐户/GitHub 的策略评估;选择并投影 managed settings(原生 MDM 优先于服务端;仅使用一个权威层) |
src/vs/workbench/services/accounts/browser/managedSettings.ts | adaptManagedSettings — 将服务端 managed_settings 响应规范化为标准数据集合 |
src/vs/workbench/services/policies/common/multiplexPolicyService.ts | 组合多个策略服务 |
src/vs/workbench/contrib/policyExport/electron-browser/policyExport.contribution.ts | --export-policy-data 命令行处理程序 |
src/vs/base/common/defaultAccount.ts | IPolicyData 接口(包括 managedSettings),用于帐户级策略字段 |
build/lib/policies/policyData.jsonc | 自动生成的策略目录,包括 referencedSettings(请勿手动编辑) |
build/lib/policies/policyGenerator.ts | 生成 ADMX/ADML(Windows)、plist(macOS)、JSON(Linux) |
build/lib/test/policyConversion.test.ts | 策略制品生成测试 |
流程
步骤 1 — 将 policy 字段添加到配置属性中
找到配置注册代码(通常位于 *.contribution.ts 文件中),并在属性架构中添加一个 policy 对象。
必填字段:
确定 minimumVersion: 始终从根目录的 package.json 中读取 version,并使用 major.minor 部分。例如,如果 package.json 中有 "version": "1.112.0",则使用 minimumVersion: '1.112'。切勿硬编码 '1.99' 之类的旧版本。
policy: {
name: 'MyPolicyName', // PascalCase, unique across all policies
category: PolicyCategory.InteractiveSession, // From PolicyCategory enum
minimumVersion: '1.112', // Use major.minor from package.json version
localization: {
description: {
key: 'my.config.key', // NLS key for the description
value: nls.localize('my.config.key', "Human-readable description."),
}
}
}
可选:用于基于帐户策略的 value 函数:
如果此策略还应能通过 Copilot 帐户策略数据(来自 IPolicyData)进行控制,请添加 value 函数:
policy: {
name: 'MyPolicyName',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.112', // Use major.minor from package.json version
value: (policyData) => policyData.my_field === false ? false : undefined,
localization: { /* ... */ }
}
value 函数接收 IPolicyData(来自 src/vs/base/common/defaultAccount.ts),并应:
- 返回一个具体值,以覆盖用户设置
- 返回
undefined,表示不应用任何帐户级覆盖(继续采用 OS 策略或用户设置)
如果需要在 IPolicyData 中添加新字段,请将其添加到 src/vs/base/common/defaultAccount.ts 中的接口。
可选:用于枚举/字符串策略的 enumDescriptions:
重要: 如果配置属性的类型为 type: 'string' 且包含 enum 数组,则必须在 localization 块中包含 enumDescriptions,且条目数量必须与 enum 数组相同。否则,npm run export-policy-data 将失败,并显示错误:enumDescriptions must exist and have the same length as enum for policy "..."。
localization: {
description: { key: '...', value: nls.localize('...', "...") },
enumDescriptions: [
{ key: 'opt.none', value: nls.localize('opt.none', "No access.") },
{ key: 'opt.all', value: nls.localize('opt.all', "Full access.") },
]
}
步骤 2 — 确保已导入 PolicyCategory
import { PolicyCategory } from '../../../../base/common/policy.js';
PolicyCategory 枚举中的现有类别:
ExtensionsIntegratedTerminalInteractiveSession(用于所有聊天/Copilot 策略)TelemetryUpdate
如果需要新类别,请将其添加到 src/vs/base/common/policy.ts 中的 PolicyCategory,并添加相应的 PolicyCategoryData 本地化内容。
步骤 3 — 验证 TypeScript 编译
检查 VS Code - Build 监视任务的输出,或运行:
npm run typecheck-client
步骤 4 — 导出策略数据
重新生成自动生成的策略目录:
npm run export-policy-data
此脚本会处理转译、设置 GITHUB_TOKEN(通过 gh CLI 或 GitHub OAuth 设备流),并运行 --export-policy-data。导出命令通过 GitHub API 从发行版的 product.json 中读取扩展配置策略,并将其合并到输出中。
此操作会更新 build/lib/policies/policyData.jsonc。**切勿手动编辑此文件。**请确认新策略出现在输出中。要将更改合并到 main,你需要获得代码所有者的代码审查。
扩展提供的设置策略
扩展作者不能直接添加 policy: 字段——他们的设置定义在扩展的 package.json 中,而不是 VS Code 核心中。因此,扩展设置的策略定义在 vscode-distro 的 product.json 中,位于 extensionConfigurationPolicy 键下。
工作原理
- 真实来源:
extensionConfigurationPolicy映射位于vscode-distro的mixin/{quality}/product.json中(stable、insider、exploration)。 - 运行时:VS Code 启动时,如果使用了混入发行版的
product.json,configurationExtensionPoint.ts会读取extensionConfigurationPolicy,并将匹配的policy对象附加到扩展贡献的配置属性上。 - 导出/构建:
--export-policy-data命令会从package.json中固定的提交获取发行版的product.json,并将扩展策略合并到输出中。请使用会自动设置身份验证的npm run export-policy-data。
发行版格式
extensionConfigurationPolicy 中的每个条目都必须包含:
"extensionConfigurationPolicy": {
"publisher.extension.settingName": {
"name": "PolicyName",
"category": "InteractiveSession",
"minimumVersion": "1.99",
"description": "Human-readable description."
}
}
name:PascalCase 格式的策略名称,在所有策略中必须唯一category:必须是有效的PolicyCategory枚举值(例如InteractiveSession、Extensions)minimumVersion:首次推出此策略的 VS Code 版本description:面向用户的描述字符串,用于为 ADMX/ADML/macOS/Linux 策略工件生成本地化键值对
添加新的扩展策略
- 在
vscode-distro中全部三个质量版本的product.json文件(mixin/stable/、mixin/insider/、mixin/exploration/)中的extensionConfigurationPolicy添加条目 - 更新
package.json中的distro提交哈希,使其指向包含新条目的 distro 提交 — 导出命令会从固定的 distro 提交中获取扩展策略 - 运行
npm run export-policy-data重新生成policyData.jsonc(参见上文第 4 步) - 在
src/vs/workbench/contrib/policyExport/test/node/extensionPolicyFixture.json中更新测试夹具,添加新条目
测试夹具
文件 src/vs/workbench/contrib/policyExport/test/node/extensionPolicyFixture.json 是一个测试夹具,必须与已签入的 policyData.jsonc 中的扩展策略保持同步。当 distro 中的扩展策略添加或更改时,必须相应更新此夹具,否则集成测试会失败,因为测试输出(根据夹具生成)将与已签入的文件(根据真实 distro 生成)不匹配。
下游使用方
| 使用方 | 读取内容 | 输出 |
|---|---|---|
policyGenerator.ts | policyData.jsonc | ADMX/ADML(Windows GP)、.mobileconfig(macOS)、policy.json(Linux) |
vscode-website(gulpfile.policies.js) | policyData.jsonc | code.visualstudio.com/docs/enterprise/policies 上的企业策略参考表 |
vscode-docs | 根据网站构建生成 | docs/enterprise/policies.md |
GitHub 预览功能
如果你的设置属于 GitHub 预览功能 — 即 Copilot/聊天功能,组织可以通过 GitHub 账户级策略将其禁用 — 你必须添加一个 value 函数,用于检查 policyData.chat_preview_features_enabled。
何时添加此标志
在以下条件全部满足时,添加 chat_preview_features_enabled 检查:
- 此设置控制 Copilot 或聊天功能(例如,Agent 工具、钩子、MCP、自动批准)
- 此功能处于预览或实验状态(通常标记为
'preview'或'experimental') - 组织管理员应能通过 GitHub 账户策略为其组织中的所有用户禁用此功能
工作原理
IPolicyData 上的 chat_preview_features_enabled 字段(定义于 src/vs/base/common/defaultAccount.ts)从用户的 GitHub Copilot 令牌权限信息中填充。组织管理员禁用预览功能后,chat_preview_features_enabled 会被设为 false。
模式
为策略添加一个 value 函数:当 chat_preview_features_enabled === false 时返回禁用值,否则返回 undefined(以采用用户自己的设置):
policy: {
name: 'MyPreviewFeaturePolicy',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.xx', // Must match the first VS Code release that ships this policy.
value: (policyData) => policyData.chat_preview_features_enabled === false ? false : undefined,
localization: {
description: {
key: 'my.setting.description',
value: nls.localize('my.setting.description', "Description of the setting."),
}
}
}
要点:
- 始终使用
=== false进行比较,不要使用!policyData.chat_preview_features_enabled— 此字段是可选的,undefined表示“没有可用的策略数据”,此时不应禁用该功能。 - 当此标志不为
false时,返回undefined,以避免账户级策略覆盖用户的设置。 - 根据设置的类型返回禁用值:布尔值返回
false,其他类型则返回具有限制性的字符串/枚举值。
实际示例
请参阅 src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts 中的 chat.tools.global.autoApprove 和 chat.useHooks,了解使用此模式的现有设置。
企业托管设置(原生 MDM / GitHub 服务)
GitHub Copilot 企业管理员可以通过 managed-settings 集合锁定设置。
VS Code 从 两个渠道获取该集合:原生 MDM(Windows 注册表 / macOS plist)
和 GitHub /copilot_internal/managed_settings 端点。(外部
managed-settings-schema.json 也描述了 managed-settings.json 文件渠道,但
VS Code 不会读取此类文件。)VS Code 的这两个渠道都会汇入
IPolicyData.managedSettings(一个扁平的点路径集合),并由现有的
policy.value(policyData) 回调使用——不会新增 IPolicyService。
若要通过托管设置驱动策略,请在策略上声明 managedSettings,并在 value 中读取
policyData.managedSettings?.[KEY](实际的 ChatToolsAutoApprove 还会对
chat_preview_features_enabled === false 的情况进行 OR 运算):
// Existing policy shown verbatim; `minimumVersion: '1.99'` is its historical value —
// a NEW policy derives minimumVersion from package.json major.minor (see Step 1).
policy: {
name: 'ChatToolsAutoApprove',
category: PolicyCategory.InteractiveSession,
minimumVersion: '1.99',
value: (policyData) =>
policyData.managedSettings?.[COPILOT_DISABLE_BYPASS_PERMISSIONS_MODE_KEY] === 'disable'
|| policyData.chat_preview_features_enabled === false ? false : undefined,
managedSettings: {
[COPILOT_DISABLE_BYPASS_PERMISSIONS_MODE_KEY]: { type: 'string' },
},
localization: { /* ... */ }
}
这是独立的模态——详细信息、权威 Schema 来源、辅助函数、接线方式和 新增键检查清单,请参阅 github-managed-settings.md。 添加或审核任何托管设置键之前,请先阅读该文档。
**本地测试:**若要在没有真实 GitHub 后端的情况下测试账户/托管设置流程,请使用模拟策略服务端——参阅 local-testing.md。
一个策略对应多个设置(policyReference)
一个策略可以控制多个设置(例如,在编辑器窗口和 Agents 窗口中同时控制某个 Agent)。
所有者声明完整的 policy: { name, … };其他设置则声明
policyReference: { name },指向所有者的策略名称。
// Owner setting (existing policy; `minimumVersion: '1.126'` is its historical value —
// a NEW policy uses package.json major.minor, see Step 1)
policy: { name: 'Codex3PIntegration', category: PolicyCategory.InteractiveSession, minimumVersion: '1.126', /* ... */ }
// Subordinate setting (no type/value/localization of its own)
policyReference: { name: 'Codex3PIntegration' }
policyReference 并非托管设置专用:当一个企业策略需要将多个设置锁定为相同值时,都可以使用它。该引用是一个纯指针。它本身不提供类型、value、managedSettings、restrictedValue 或本地化内容;所有者仍是策略元数据和运行时行为的唯一事实来源。
关键规则和内部机制:
- 设置不得同时声明
policy和policyReference(在配置注册期间会被拒绝)。 - 一个策略名称只能由一个设置通过
policy拥有;其他设置通过policyReference关联。 - 引用设置的类型必须与所有者的类型一致;
npm run export-policy-data会强制执行此要求,因为同一个解析后的策略值会原样应用于所有者及其引用项。 ConfigurationRegistry.getPolicyReferenceConfigurations()会跟踪policyName → Set<settingKey>;PolicyConfiguration会在策略值变化时更新所有者设置及所有已注册的引用项。AbstractPolicyService.serialize()使用toSerializablePolicyDefinition(),在通过 IPC 发送策略定义前,剔除无法克隆的value()回调。AbstractPolicyService.updatePolicyDefinitions()会按策略名称替换定义,因此较晚注册的所有者会取代较早的引用回退项;如果所有者被移除,引用项仍可提供仅包含类型的回退项。- 导出的策略数据包含导出期间已注册引用项的
referencedSettings;Developer: Policy Diagnostics 会在同一策略名称下列出已注册的所有者/引用设置。
关于将 policyReference 与 Copilot 托管设置结合使用的示例,请参阅 github-managed-settings.md。
示例
在代码库中搜索 policy:,即可找到不同策略配置的所有示例。
经验总结
- 切勿手动编辑
build/lib/policies/policyData.jsonc(其文件头明确禁止这样做)。如果npm run export-policy-data运行失败,请修复脚本,而不是修改 JSON。常见原因:在错误的工作目录中运行(例如在主仓库而不是工作树中),导致导出时静默地使用了错误的源代码树。 - 请在干净的环境中重新生成
policyData.jsonc,否则PolicyExport集成测试会在 CI 中失败。referencedSettings仅会记录导出时已加载的引用。直接运行npm run export-policy-data会加载你的开发配置文件扩展(例如 Copilot 扩展),从而将referencedSettings注入核心策略;而测试基于夹具进行导出(干净配置文件,不含用户扩展),不会产生这些内容,因此检入的文件会包含多余的referencedSettings,导致 CI 失败。此问题无法在本地复现,因为测试会重复使用你的默认扩展目录。请按测试的导出方式重新生成:DISTRO_PRODUCT_JSON=<extensionPolicyFixture.json> ./scripts/code.sh --export-policy-data="$PWD/build/lib/policies/policyData.jsonc" --user-data-dir="$(mktemp -d)" --extensions-dir="$(mktemp -d)"(并设置VSCODE_SKIP_PRELAUNCH=1)。 - 应记录行为和业务逻辑预期,而不是照搬实现。将内部代码(例如
getPolicyData()的合并逻辑)复制到技能文档中,一旦源代码更改,内容就会过时,且并未提供源代码之外的信息。请用文字说明约定(例如“原生 MDM 管理的设置优先于服务器下发的渠道;这两层绝不会合并”),并指向实现所在的源代码。代码块应留给面向作者的 API 约定,说明贡献者必须遵循的内容——如何声明policy/managedSettings/value回调,而不是重复说明运行时的内部流程。