---
url: /wave-agent/specs/enterprise/server-managed-config.md
description: 从服务端下载托管配置：校验和缓存、键级合并优先级与插件市场/启用列表下发
---

# 功能规格说明：服务器托管配置下载

**创建日期**：2026-05-25

## 用户场景与测试 *（必填）*

### 用户故事：启动时下载托管设置（优先级：P1）

作为组织管理员，我希望将托管设置（允许的工具、模型限制、环境变量）推送到团队的 Wave agent，以便在不要求每个用户手动配置设置的情况下执行组织策略。

**为什么是这个优先级**：这是核心价值主张——团队和企业的集中配置管理。

**独立测试**：使用在 Wave AI 上配置了托管设置的账户通过 SSO 认证，启动 Wave，并验证托管设置已应用。

**验收场景**：

1. **假设**用户通过 SSO 认证且服务器有托管设置，**当** Wave 初始化时，**则**它从 `WAVE_SERVER_URL/api/v1/managed-settings` 下载托管设置并应用。
2. **假设**用户未通过 SSO 认证，**当** Wave 初始化时，**则**不下载托管设置，使用仅本地设置。
3. **假设**托管设置包含 `model` 标量字段，**当**解析设置时，**则**托管模型优先于本地 `settings.json` 模型（管理员强制）。**假设**托管设置包含 `env.WAVE_MODEL` 但没有 `model` 字段，**当**用户在 `settings.json` 中有 `model` 字段时，**则**用户的模型优先（用户覆盖管理员的默认值）。

***

### 用户故事：基于校验和的缓存（优先级：P2）

作为慢速网络上的用户，我希望 Wave 在托管设置未更改时跳过重新下载，以便启动快速且节省带宽。

**为什么是这个优先级**：性能优化——大多数启动会发现设置未更改，因此避免下载是有意义的改进。

**独立测试**：在服务器上托管设置未更改的情况下启动 Wave 两次；验证第二次启动不进行下载请求。

**验收场景**：

1. **假设**托管设置之前使用校验和下载过，**当** Wave 初始化且服务器返回相同校验和时，**则**使用缓存的设置而无需重新解析。
2. **假设**托管设置之前已下载，**当**服务器返回不同的校验和时，**则**新设置被下载、解析和缓存。

***

### 用户故事：设置合并优先级（优先级：P1）

作为组织管理员，我希望我的托管设置对特定键覆盖本地用户设置，以便安全策略（例如，不允许的工具）不能被本地配置规避。

**为什么是这个优先级**：没有正确的合并优先级，托管设置可能被本地设置覆盖，使集中管理失去意义。

**独立测试**：配置不允许 Bash 工具的托管设置，验证本地 `allowedTools` 设置不能重新启用 Bash。

**验收场景**：

1. **假设**托管设置包含 `disallowedTools: ["Bash"]`，**当**设置被合并时，**则** Bash 工具被禁用，无论本地 `allowedTools` 设置。
2. **假设**托管设置不包含 `model` 字段，**当**设置被合并时，**则**使用 `settings.json` 中的本地 `model` 设置。
3. **假设**托管和本地设置都包含 `env` 字段，**当**设置被合并时，**则**托管环境变量覆盖同键的本地变量，非重叠的本地环境变量被保留。

***

### 用户故事：托管配置下发插件市场与启用列表（优先级：P1）

作为组织管理员，我希望把插件市场与要启用的插件随托管配置一起下发，使团队所有成员首次启动即自动装好并启用该插件、且成员无法卸载或禁用它，以便在「工具 / 模型 / 环境变量」之外把插件形态也纳入统一管控。

**为什么是这个优先级**：托管配置通道（下载、校验和缓存、合并优先级、未登录回退）已经具备，`marketplaces` 与 `enabledPlugins` 这两个键本来就在远端合并白名单里；但插件子系统的数据源只读本地三个 `settings.json`，远端值只活在内存中，不驱动任何安装或启用动作，且托管层此前对这两个键是整体替换语义（管理员只下发一个插件，会把成员本机已有的插件与市场一并挤掉）。不打通这一步，管理员下发的这两个键对插件等于零效果——值拉到了，插件既不装也不启用。这是集中管控从「会话行为」扩展到「能力分发」的关键一步，也是插件子系统第一次消费托管配置。

**独立测试**：清空本机该插件的安装与启用记录，在托管配置中写入 `marketplaces` 与 `enabledPlugins: {"<插件>@<市场>": true}`，启动 Wave，验证插件被自动安装并启用；随后在本机 `settings.json` 写入同一插件的 `false` 并重启，验证插件仍为启用；再执行一次卸载并重启，验证插件被重新安装并启用。

**验收场景**：

1. **假设**托管配置含某市场与 `enabledPlugins: {"<插件>@<市场>": true}`，且本机没有该插件的安装记录，**当** Wave 初始化时，**则**插件被自动安装并启用，全程不需要用户点「安装」（与本地配置驱动自动安装的行为一致）。
2. **假设**托管配置把某插件设为 `true`，**当**本机 `settings.json` 为同一插件写入 `false`、或删除该键时，**则**合并结果仍为启用、插件照常加载——本机值不能翻盘。
3. **假设**托管配置把某插件设为 `true`，**当**用户卸载该插件后重启 Wave（或收到一次托管配置同步）时，**则**该插件被重新安装并启用。
4. **假设**托管配置把某插件显式设为 `false`（强制禁用），**当**用户在本机安装或启用该插件时，**则**合并结果仍为禁用、插件不加载——管理员的禁用意图同样不可被本机操作翻盘。
5. **假设**托管配置与本机 `settings.json` 都配置了插件与市场，**当**配置合并时，**则**按**键**合并、托管层的键覆盖同名的本机键、本机独有的键照常保留（与 Claude Code 的 policy 层合并语义一致）——托管只下发一个插件，不得让成员本机已有的其它插件失效；**假设**托管配置与本机配置了同名市场但来源不同，**则**该市场取托管来源。
6. **假设**管理员从托管配置里去掉某插件的条目，**当**插件子系统重新读取配置（重启或插件重载）后，**则**托管意图消失，该插件回到用户可自行安装 / 卸载的普通状态。
7. **假设**托管配置引用的市场尚未在本机获取过，**当**首次启动自动安装该市场下的插件时，**则**先获取该市场清单再安装（与本地配置驱动自动安装的既有行为一致）。

***

### 边界情况

* **如果托管设置端点不可达会怎样？** Wave 必须在缓存可用时使用缓存设置，否则使用仅本地设置。记录警告但不阻止启动。
* **如果托管设置包含无效 JSON 会怎样？** Wave 必须记录错误并回退到仅本地设置。
* **如果用户退出 SSO 会怎样？** 后续启动不再下载托管设置，但之前缓存的托管设置保持有效，直到被本地更改覆盖。
* **如果托管设置与环境变量冲突会怎样？** 用户的 `settings.json` `model` 字段覆盖管理员的 `env.WAVE_MODEL` 默认值。如果管理员想要强制执行，他们使用 `model` 标量字段，在合并期间覆盖本地值。Shell 环境变量（在 Wave 启动前设置）是最低优先级的回退。
* **设置重新加载期间（文件监视器）会怎样？** 托管设置在文件更改时不会重新下载——它们仅在初始化期间下载。
* **如果 `WAVE_SERVER_URL` 配置在 `settings.json` 的 `env` 中而非 shell 环境变量会怎样？** 启动时本地 `settings.json` 的 `env` 在 `loadMergedConfiguration` 内才写入 `process.env`，而远程设置的网络请求通过 `getServerUrl()` 读取 `process.env.WAVE_SERVER_URL` 来决定目标端点。因此网络请求必须在 `loadMergedConfiguration` **之后**启动；否则 `WAVE_SERVER_URL` 尚未写入 `process.env`，`getServerUrl()` 回落到 `DEFAULT_SERVER_URL`（生产），用测试环境签发的 token 命中生产端点，必然 401 且无法恢复。磁盘缓存加载（`loadCacheFromDisk`）仍须在 `loadMergedConfiguration` **之前**完成，以便合并时 `getRemoteSettingsSync()` 返回缓存的托管设置。两者职责分离：缓存加载在前，网络刷新在后。
