Appearance
功能规格说明:Agent 配置
创建日期:2025-01-27
用户场景与测试 (必填)
用户故事:显式 AI 服务配置(优先级:P1)
开发者需要通过 Agent 构造函数显式配置 AI 网关设置(API 密钥、基础 URL、模型 ID),而不是依赖环境变量,提供更好的可控性和可测试性。
为什么是这个优先级:这是启用显式配置管理并通过使必需配置可见和可控来提高 API 可用性的核心功能。
独立测试:可以通过使用自定义 AI 配置创建 Agent 实例并验证它使用这些设置而非环境变量来完整测试。
验收场景:
- 假设开发者使用自定义 AI 配置创建 Agent,当代理处理消息时,则它使用提供的配置值
- 假设未向 Agent 构造函数提供配置,当设置了环境变量时,则代理使用环境变量值
用户故事:Token 限制配置(优先级:P2)
开发者需要通过 Agent 构造函数配置自定义 token 限制,以控制消息压缩行为和最大输出 token,而无需设置环境变量。
为什么是这个优先级:Token 限制配置影响性能和成本管理,但次于基本 AI 功能。
独立测试:可以通过使用自定义 token 限制创建 Agent 并验证在指定限制处触发压缩来独立测试。
验收场景:
- 假设开发者通过 Agent 构造函数设置了自定义 token 限制,当token 使用超过该限制时,则触发消息压缩
- 假设未提供 token 限制,当创建代理时,则它使用合理的默认 token 限制
用户故事:模型选择配置(优先级:P3)
开发者需要通过 Agent 构造函数指定默认 AI 模型(代理模型和快速模型),以避免对环境变量的硬编码模型依赖。
为什么是这个优先级:模型配置提供灵活性,但作为核心功能的第三优先级,因为默认模型可以适用于大多数用例。
独立测试:可以通过使用自定义模型配置创建 Agent 并验证指定模型用于 AI 操作来测试。
验收场景:
- 假设开发者通过 Agent 构造函数指定了自定义模型,当执行 AI 操作时,则使用指定模型而非默认模型
用户故事:可配置最大输出 Token(优先级:P2)
作为开发者,我希望通过环境变量、代理创建选项或直接调用参数指定 AI 响应的最大输出 token。
为什么是这个优先级:对控制响应长度和成本至关重要。
独立测试:设置 WAVE_MAX_OUTPUT_TOKENS=2048 或向 Agent.create 传递 maxTokens: 1024 并验证 AI 服务调用使用正确的限制。
验收场景:
- 假设
WAVE_MAX_OUTPUT_TOKENS设置为2048,当调用callAgent时,则请求使用 2048 作为最大 token 限制 - 假设代理创建时带有
maxTokens: 1024,当调用callAgent时,则请求使用 1024 作为最大 token 限制
用户故事:通过环境变量配置 SDK 自定义请求头(优先级:P2)
作为开发者,我希望使用 WAVE_CUSTOM_HEADERS 环境变量配置 SDK 的自定义 HTTP 请求头,以管理认证或环境特定的元数据。
为什么是这个优先级:支持替代认证方法和更好的安全实践。
独立测试:设置 WAVE_CUSTOM_HEADERS="X-Test: 123\nY-Test: 456",初始化 SDK,并验证发出的请求包含这些请求头。
验收场景:
- 假设
WAVE_CUSTOM_HEADERS设置为X-Test: 123,当SDK 发出请求时,则它包含请求头X-Test: 123 - 假设未提供
apiKey但在WAVE_CUSTOM_HEADERS中设置了自定义认证请求头,当SDK 初始化时,则它不抛出验证错误
用户故事:配置首选语言(优先级:P1)
作为用户,我希望在设置文件或代理选项中指定我的首选语言,以便代理用该语言与我交流。
为什么是这个优先级:使非英语使用者能够更有效地与代理交互。
独立测试:在 Agent.create() 或 settings.json 中设置 language: "zh-CN" 并验证系统提示包含语言指令;不设置任何语言时(全新安装)验证仍按默认 zh-CN 注入语言指令,且设置页「AI 回复语言」下拉处于「未设置(默认:中文)」态(显示 ≡ 生效)。
验收场景:
- 假设语言设置为
zh-CN,当我向代理提问时,则代理用中文回答 - 假设语言设置为 "Spanish",当代理解释函数
calculateTotal()时,则解释用西班牙语但calculateTotal()保持不变 - 假设
settings.json与AgentOptions都未设置language(全新安装),当下一轮对话开始,则按默认值zh-CN生效(解析链覆盖项 > settings.json > 默认),系统提示按该值注入语言指令——不得留空导致「模型按自身默认语言回答」;且设置页「AI 回复语言」下拉处于「未设置(默认:中文)」态、其默认值必须是同一个串(zh-CN),即未设置时显示 ≡ 生效(未设置态的表达式见「IDE 插件配置入口」故事场景 7)
用户故事:自定义环境变量(优先级:P1)
开发者需要将自定义环境变量(API 密钥、数据库 URL、功能标志)传递给 Wave Agent SDK,而不需要在代码中硬编码。他们在 settings.json 文件中添加 "env" 字段,并期望这些变量在代理执行上下文中可用。这些变量被存入 Agent 的会话级环境快照(ConfigurationService 实例,每个会话独立),优先级高于 OS 环境变量,但不写入 process.env——这样在 stdio 多会话模式下,一个进程承载多个会话时不会相互污染。例外:WAVE_SERVER_URL 因需被进程级单例(AuthService、远端设置后台拉取)读取,会额外镜像写入 process.env(详见边界说明)。Wave Code CLI 将继承此功能,因为它使用 SDK。
为什么是这个优先级:这提供了必要的配置灵活性,并遵循安全最佳实践,将敏感数据保留在配置文件中而非代码中。
独立测试:可以通过将 env 变量添加到 settings.json 并验证它们在代理进程中可访问来完整测试,提供即时配置价值。
验收场景:
- 假设settings.json 文件包含带有键值对的 env 字段,当Wave Agent SDK 启动时,则这些环境变量存入该 Agent 的会话级环境快照(per-session env snapshot),优先级高于 OS 环境变量;它们不写入
process.env(WAVE_SERVER_URL例外,见验收场景 6 与边界说明) - 假设用户级和项目级 settings.json 文件都包含 env 字段,当代理运行时,则项目级环境变量覆盖同名的用户级变量
- 假设env 字段格式无效,当设置被加载时,则系统显示关于无效环境变量配置的清晰错误消息
- 假设一个 stdio 进程承载多个会话(不同项目/workdir),且各自的 settings.json
env设置了不同的WAVE_MODEL/WAVE_API_KEY,当任一会话解析网关/模型配置时,则只读取本会话的快照,不发生"后启动会话覆盖前一会话"的污染(last-session-wins 污染消除) - 假设settings.json
env中的变量被子进程(bash 工具、hooks、bash 模式命令、后台任务、MCP 模板替换)读取,当这些子进程启动时,则其环境为OS env + 本会话快照的合并(基础设施子进程如 git/worktree/LSP 仍只读 OS env) - 假设settings.json
env中设置了WAVE_SERVER_URL,当配置被加载时,则该值既存入会话快照、也镜像写入process.env,使进程级单例AuthService/remoteSettingsService能读到它(详见边界说明)
用户故事:CLI 不在运行时强制 NODE_ENV,子进程环境与用户原始环境一致(优先级:P1)
Wave Code CLI 入口曾强制 process.env.NODE_ENV ||= "production",以加载 React/ink 生产构建——react-reconciler 开发构建每次组件渲染都会向 Node 全局 perf buffer 写入 performance.measure() 条目(永不清空),长会话累积到百万条会触发 MaxPerformanceEntryBufferExceededWarning 并泄漏约 150MB。但 process.env 的修改会被所有 spawn 的子进程继承:wave 是常驻 daemon,它生成的每个 shell(Bash 工具、! bash 模式命令、后台任务、hooks、shell 快照捕获)都带 NODE_ENV=production,导致 npm install/pnpm install 跳过 devDependencies、构建与测试框架行为被改变。作为 wave 会话中的开发者,我希望子进程环境与启动 wave 时的原始环境一致(未设置 NODE_ENV 即不含该键),以便 npm install 等命令与普通登录 shell 行为一致。
为什么是这个优先级:这是静默行为破坏——依赖缺失报错时根因(NODE_ENV)与环境无关,排查成本高,影响所有把 wave 当作开发环境执行命令的用户。React 生产构建由构建期 esbuild define 保证(对齐 Claude Code 的做法:编译期替换 process.env.NODE_ENV,运行时不做修改),因此运行时强制并非必要。
独立测试:在无 NODE_ENV 的环境启动 wave,echo $NODE_ENV 应为空、npm install 正常安装 devDependencies;在 NODE_ENV=development 环境启动 wave,CLI 进程与子进程均保持 development。
验收场景:
- 假设用户环境未设置
NODE_ENV且启动 wave CLI,当CLI 进程加载时,则CLI 自身process.env.NODE_ENV保持未设置(不注入production),React 生产构建由构建期 define 保证,无MaxPerformanceEntryBufferExceededWarning - 假设用户显式设置
NODE_ENV=development启动 wave,当CLI 加载时,则process.env.NODE_ENV保持development,不被覆盖 - 假设用户环境未设置
NODE_ENV,当wave 会话内通过 Bash 工具、!bash 模式命令、后台任务或 hooks 启动子进程时,则子进程环境不包含NODE_ENV,echo $NODE_ENV输出为空(与普通登录 shell 一致) - 假设用户以
NODE_ENV=development启动 wave,当wave 会话内启动子进程时,则子进程的NODE_ENV为development - 假设用户环境未设置
NODE_ENV,当wave 会话内运行npm install时,则devDependencies 被正常安装,不被production跳过
用户故事:设置实时重载(优先级:P1)
开发者与三端 GUI 用户正在积极工作,需要修改 settings.json 配置(hooks、环境变量、语言、模型、上下文长度、自动记忆、权限等)。他们希望这些更改立即生效而无需重启 SDK、也无需重建会话,实现配置的快速迭代。Wave Code CLI 与三端 GUI(VS Code 扩展 / JetBrains 插件 / 桌面端)都受益于此,因为它们都使用 SDK。
为什么是这个优先级:这是三端设置页「用户偏好保存」的承载机制——设置页保存的偏好类配置写入用户级 ~/.wave/settings.json 后,必须先经过本故事定义的实时重载才能生效;没有它,保存只能靠「销毁并重建会话」强制生效(2026-09-10 前的现状),既慢(保存要等全部会话重建完才回执)又打断正在进行的工作。2026-09-10 用户拍板:用户偏好类配置一律走本通道,不得经 AgentOptions 覆盖层下发(分层职责见边界说明)。
独立测试:在 CLI/SDK 运行时修改用户级 ~/.wave/settings.json 并验证新配置在下一轮对话开始时生效(无需重启、无需重建会话);分别验证语言、模型、上下文长度、自动记忆开关/频率、权限、hooks、env、AGENTS.md 各自的热生效;关闭自动记忆开关后验证自动记忆目录在工作目录之外不再自动放行(安全区随开关热更新)。
验收场景:
- 假设Wave Agent SDK 正在运行,当用户修改 settings.json(用户级
~/.wave/settings.json、项目级{workdir}/.wave/settings.json、{workdir}/.wave/settings.local.json)时,则更改被检测并应用于后续操作,无需重启 SDK、无需重建会话 - 假设Wave Agent SDK 正在处理请求,当settings.json 被更新时,则新设置用于后续代理执行(生效时点见场景 5)
- 假设保存了无效设置,当文件监视器检测到更改时,则系统记录错误但继续使用之前的有效配置
- 假设用户在 settings.json 中修改以下任一配置(热生效键清单),当下一轮对话开始,则新值生效(不重建会话、不重启 SDK):
language;model/fastModel;models[<id>].*(按模型配置,含maxInputTokens、disableThinkingOptions等);maxInputTokens(或经由env.WAVE_MAX_INPUT_TOKENS);autoMemoryEnabled/autoMemoryFrequency;permissions.*(allow/deny/defaultMode/additionalDirectories);hooks;env.*;以及记忆文件内容(用户级~/.wave/AGENTS.md、项目级<workdir>/AGENTS.md) - 假设settings.json 在某一轮对话进行中被修改,当该轮仍在执行,则该轮内保持进入本轮时的配置快照(不得在同一轮内按请求抖动),变更在下一轮开始时整体生效——生效时点语义是「每轮(turn)开始时取一次快照」,既不是「每会话只读一次」,也不是「每个请求各读一次」
- 假设自动记忆目录(
~/.wave/projects/<项目>/memory)与用户记忆文件(~/.wave/AGENTS.md)因自动记忆开启而属于系统级安全区(与用户可配的permissions.additionalDirectories不是同一集合),当用户在 settings.json 中关闭自动记忆,则这两项从系统级安全区移除(其内文件操作回到常规权限判定);当重新开启,则重新加入——两次变更都无需重建会话。构造期一次性注册的系统级白名单须改为动态:复用实时重载路径按resolveAutoMemoryEnabled()幂等地增删(关闭时移除、重新开启时加入),不得只在 Agent 构造期注册一次 - 假设用户级
~/.wave/settings.json在会话启动时不存在,当应用(设置页保存)首次创建并写入该文件,则运行中的会话必须感知并应用新配置(不得因「启动时文件不存在、未纳入监视」而漏掉首次保存)。该保证不得只依赖文件监视:设置页保存路径(stdioupdateUserSettings)在写完文件后显式重载该进程内全部 live 会话的实时配置,并在回包之前完成——因此「保存回执已到 ⇒ 下一轮生效」是确定语义,不存在「回执已到、watcher 尚未落定」的竞态窗口;监视若也命中,重载是幂等的(并发重载按reloadInProgress去重)。空载荷(无差异保存、只回读)不落盘、也不重载
用户故事:配置变更不再需要重建会话(优先级:P1)
(本故事原名「配置变更的构造期副作用与重建」;2026-09-18 拍板:构造期副作用清单已清空,重建路径整体废弃)
作为用户,我希望任何配置变更与插件变更都不以「销毁并重建会话」作为生效手段,以便我的对话不会因为一次变更被掐断正在生成的回合、清空排队消息,也不会进入「稍后重启」这种把生效推迟到我后续动作的悬空状态。
为什么是这个优先级:2026-09-10 前的现状是「任何设置保存都重建池内全部 live 会话」——保存要等全部重建完才回执、会清空排队消息并打断正在流式输出的会话,而绝大多数用户偏好本可热生效(见「设置实时重载」故事);该轮拍板把用户偏好改走实时重载,只给「真正产生构造期副作用的变更」留下重建这一条路,而当时的名单只有插件装卸一项。2026-09-18 拍板:插件变更改为就地重载——由用户主动敲 /reload-plugins 触发换装,不重建 Agent、不打断正在生成的回合、不需要用户选择时机(见 plugin.md「插件变更的就地重载」)。至此构造期副作用清单为空,本规格范围内不再有任何配置变更需要重建会话,与之配套的「重建确认框(立即重启 / 稍后重启)」一并废止。
构造期副作用清单:空。 历史上两项的处置:
- 插件装卸(
installPlugin/uninstallPlugin/enablePlugin/disablePlugin/updatePlugin、内置插件开关如项目设置里的 SDD):2026-09-18 起改为就地重载——变更落盘只置「待应用」信号并由宿主提示,用户敲/reload-plugins后在同一会话内就地换装六类能力(斜杠命令、技能、子代理、钩子、MCP、LSP),见 plugin.md「插件变更的就地重载」。 - 自动记忆开关/频率:2026-09-10 起改为热生效——安全区白名单随 settings.json 变更热更新(见「设置实时重载」故事场景 6)。
独立测试:保存一份仅有用户偏好变化(语言/模型/上下文长度/自动记忆)的配置,验证不触发任何会话重建、保存立即回执、排队消息与流式输出不受影响;安装或卸载一个项目级插件,验证既不弹任何重建确认框、也不重建任何会话,只出现一次「插件已变更」提示,敲 /reload-plugins 后在同一会话(sessionId 不变)内生效(六类能力的换装语义见 plugin.md 该故事的独立测试与验收场景)。
验收场景:
- 假设一次配置保存只涉及热生效键(见「设置实时重载」故事场景 4),当保存完成,则不得重建任何会话;保存回执不等待任何重建
- 假设配置内容与当前生效内容完全一致(无差异,含「一个字都没改就点保存」),当保存完成,则不得重建任何会话、不得清空排队消息、不得打断正在流式输出的会话
- 假设用户装卸/启停/更新插件(或切换内置插件开关),当变更落盘,则不得重建任何会话、不得弹重建确认框、不得打断任何正在生成的回合;变更只产生「待应用」信号与一次宿主提示,由用户经
/reload-plugins触发就地换装(生效语义见 plugin.md「插件变更的就地重载」) - 假设任一配置变更或插件变更发生,当变更完成,则不得出现两按钮的「立即重启 / 稍后重启」确认框,不得登记任何「待重建」待办,也不存在「会话/pane 再次被激活时自动重建」的路径
用户故事:按子代理类型设置请求头(优先级:P2)
SDK 用户需要按子代理类型配置不同的 HTTP 请求头,以便 customFetch 可以区分主代理调用和子代理调用,实现请求级路由、速率限制和可观测性。
为什么是这个优先级:对多代理可观测性和路由重要,但次于核心配置。
独立测试:使用 subagentHeaders: { "Explore": { "X-Subagent-Type": "Explore" } } 创建 Agent,生成 Explore 子代理,并验证子代理的 defaultHeaders 包含类型特定请求头。
验收场景:
- 假设Agent 带有
subagentHeaders: { "Explore": { "X-Subagent-Type": "Explore" } },当创建 Explore 子代理时,则子代理的defaultHeaders包含X-Subagent-Type: Explore - 假设Agent 配置了
subagentHeaders,当创建不在subagentHeaders中的子代理类型时,则子代理仅接收父级defaultHeaders,无额外键 - 假设Agent 带有
defaultHeaders: { "X-Shared": "base" }和subagentHeaders: { "Explore": { "X-Shared": "explore-override" } },当创建 Explore 子代理时,则子代理接收X-Shared: explore-override - 假设Agent 配置了
subagentHeaders,当为子代理请求调用customFetch时,则init.headers包含合并后的类型特定请求头
用户故事:IDE 插件配置入口(优先级:P1)
作为 IDE 插件用户,我希望通过 /config 斜杠命令或头部设置按钮打开设置页并选中「全局设置」选项卡,查看并修改当前生效的语言、上下文长度等配置,以便确认并调整插件的运行环境配置。
为什么是这个优先级:这是未登录用户使用 IDE 插件的主要配置入口,直接影响插件可用性。设置页(批次 2)已提供「全局设置」导航项,/config 复用设置页入口避免维护独立的配置对话框(2026-08-29 用户拍板:弹窗内容迁移到设置页选项卡,对齐 /agents → 子代理、/skills → 技能)。2026-08-31 曾拍板设置页为只读浏览视图(配置修改走 CLI 命令与配置文件);2026-09-01 原型走查用户恢复可编辑:AI 回复语言(下拉)与上下文长度(数字输入)提供编辑控件与「保存」按钮(对齐旧配置弹窗交互),保存经 updateConfiguration RPC 写回配置并即时生效,保存结果(成功/失败)经宿主全局 toast 提示(语义见 desktop-account-and-settings「设置页反馈语义」),设置页不渲染页面内自建提示文字。2026-09-10 用户拍板改造保存路径(三端一致):AI 回复语言、上下文长度、自动记忆开关/轮次等用户偏好保存后写入用户级 ~/.wave/settings.json,经「设置实时重载」即时生效——不再把用户偏好当 AgentOptions 覆盖项经 stdio 下发(该层只保留会话级/单次覆盖语义,见边界说明),因此保存不再触发会话重建,回执与界面刷新不等待任何重建;VSCE / JetBrains / 桌面端必须同语义,不得只在桌面端生效造成多端漂移。
独立测试:在 IDE 中输入 /config 或点击头部设置按钮,验证打开设置页并选中「全局设置」选项卡,可编辑 AI 回复语言与上下文长度并保存;保存后配置生效且展示值刷新,保存成功/失败经宿主全局 toast 提示(不渲染页面内自建提示文字)。
验收场景:
- 假设 用户处于 IDE 中(VS Code 扩展 / JetBrains 插件 / 桌面端),当 用户输入
/config并发送,则 打开设置页并选中「全局设置」选项卡(桌面端打开全页设置,IDE 打开编辑器区域设置标签页),不再弹出独立对话框。 - 假设 用户点击头部设置按钮,当 触发打开设置时,则 效果与
/config一致,打开设置页「全局设置」选项卡。 - 假设 设置页「全局设置」视图已打开,当 页面渲染时,则 展示当前生效的 AI 回复语言(中文 / English,下拉选择,仅决定 AI 回复用语,不改变界面语言)与上下文长度(K 值,数字输入 16–1000,读写同一个全局键
env.WAVE_MAX_INPUT_TOKENS——2026-09-10 拍板,见边界说明「上下文长度的落点」),并提供「保存」按钮;上下文长度行须给出一句可见说明(如「全局默认;当前模型自带上下文上限时以模型配置为准」,从轻实现:不加区块、不加状态源),避免用户把「模型配置遮住全局值」误解为设置失效;该键未设置(settings.json 无此键)时控件必须表达「未设置」而不是假装一个真实值——语言下拉首项为「未设置(默认:中文)」并选中、上下文长度输入框留空并以灰字占位符显示系统默认(见场景 7);保存把用户偏好写入用户级~/.wave/settings.json(经 SDK 实时重载生效,不重建会话),且只把用户真正改动过的字段放进载荷(省略键 = 不改该键,见场景 7–8 与边界说明「省略键 = 不改该键」),成功回发新配置刷新展示,失败保留用户输入;保存结果(成功/失败)经宿主全局 toast 提示(语义见 desktop-account-and-settings「设置页反馈语义」),设置页自身不渲染页面内自建提示文字。 - 假设 设置页已打开,当 用户点击「返回」或切换其他选项卡,则 回到会话视图或切换到对应选项卡,未点击保存的编辑不产生任何消息、不写入任何配置。
- 假设 用户处于桌面端且 pane 布局已生效(单 pane 或多 pane,
desktopPanes推送后),当 在任一对话输入框中输入/config、/agents、/skills或/mcp并发送,则 打开设置页并选中对应选项卡(「全局设置」/「子代理」/「技能」/「MCP 服务」)——pane 自身不渲染设置视图,命令委托给根实例的全页设置,与欢迎页/新对话状态行为一致。 - 假设 用户在任一宿主(VS Code 扩展 / JetBrains 插件 / 桌面端)的设置页保存用户偏好(AI 回复语言、模型/快速模型、上下文长度、自动记忆开关/轮次),当 保存完成,则 三端必须一致地写入用户级
~/.wave/settings.json并经实时重载生效(防多端漂移)——不得把用户偏好经 stdioinitialize/updateConfig当AgentOptions覆盖项下发(否则覆盖层永久遮蔽实时配置,端与端行为漂移);设置页的初始值读取以该文件为落点(经宿主向会话所在进程查询),不得回读宿主私有存储(如 VS CodeglobalState)当作第二真源;但回包必须是生效值——该文件只是用户偏好的落点,企业下发的 Remote 组织配置 / 机器环境变量可以盖过它(取值链见场景 9 与边界说明「用户偏好的层与来源」),只读该文件会让设置页显示值与生效值分叉;该键未设置时(全新安装)初始值按未设置态展示(语言下拉显式项「未设置(默认:中文)」、数字输入留空 + 默认值占位符,见场景 7)、生效值落到同一个默认值(语言zh-CN——默认值只在 SDK 解析链末尾有一处、与设置页未设置态显式项的默认值同串,见边界说明「语言默认值」),保存时该键不写入(见场景 8),不得出现「设置页假装某个值、生效值却是另一个(或为空)」的分叉;上下文长度(K 值,16–1000)读写同一个键env.WAVE_MAX_INPUT_TOKENS(按 K×1000 落盘、回读按同一换算展示;不得改动 SDK 既有解析优先级——该键是全局默认,见边界说明「上下文长度的落点」);保存回执与界面刷新不得等待会话重建。三端同批落地(VS Code 扩展 / JetBrains 插件同样走用户级~/.wave/settings.json实时重载,不再经updateAllSessionsConfig重建会话生效);用户在 IDE 内连远端(VS Code Remote/SSH、JetBrains 远程)时,保存经 stdio RPC 写入会话所在进程的用户级~/.wave/settings.json(远端机器上的该文件),语义与本地一致。 - 假设用户级
~/.wave/settings.json里没有某个用户偏好键(全新安装、或该键从未被写过),当设置页渲染,则该控件必须以「未设置」表达系统默认、而不是显示一个真实值:AI 回复语言下拉的列表首项显示「未设置(默认:中文)」并被选中(下拉没有 placeholder 语义,故用显式项表达);上下文长度输入框留空、灰字占位符为「跟随模型配置(默认 200K)」;自动记忆轮次输入框留空、灰字占位符为「默认 1 轮」;自动记忆开关不做占位态(它的真实默认就是「开」,三态开关更难用),继续显示为「开」。文案与默认值同源:语言项里的默认值 = SDK 解析链末尾的zh-CN(见边界说明「语言默认值」)、上下文长度占位符里的 200K = SDK 默认DEFAULT_WAVE_MAX_INPUT_TOKENS、轮次占位符里的 1 轮 = SDK 自动记忆频率默认值——三者都必须与「未设置时的实际生效值」一致,不得各写一份。 - 假设某个用户偏好键处于「未设置」态、或用户没有改动该控件(当前值与宿主回填的初始值相同),当用户点击「保存」,则该键不得出现在发给宿主的载荷里——「省略键 = 不改该键」是该载荷(webview → host → CLI
updateUserSettings)的部分更新语义:只改语言时其余键(上下文长度、自动记忆开关/频率)都不得出现在报文里,未设置态一律不写该键;因此「用户系统环境里已有WAVE_MAX_INPUT_TOKENS,进设置页随手保存一次就被钉成 200000」不再发生(没输入就不落盘)。把输入框清空(对一个已写过的值)等同于「不改该键」,不提供「清除 / 恢复默认」按钮(把已写入的值退回未设置态)——这是本规格的刻意取舍(载荷没有「删键」语义),用户若要去掉键需手改 settings.json;因此一旦文件里写过某个键,设置页就不再把该控件显示为「未设置」态。 - 假设某个用户偏好键的生效值来自更高层——企业下发的 Remote 组织配置(
remote:用户级~/.wave/settings.json里也写了该键,或该键未设置)或机器环境变量(env:该键对应的 env 键有值而用户级文件里没有该键),当设置页渲染,则设置页必须如实显示生效值,不得回退显示用户文件里的值、也不得显示「未设置」态(前者会让用户以为本地值在生效,后者会让用户以为生效值还是系统默认):①来源为remote时该控件置为不可编辑(用户在此无法本地覆盖组织策略),并给一句可见说明「由组织配置管理」;②来源为env时该控件保持可编辑(用户级文件优先级高于 OS 环境变量,用户在此保存即写入该文件并覆盖环境值),并给一句来源说明(当前值来自系统环境变量、保存后以本页设置为准)。宿主回包除生效值外还须带上每个键的来源层(preferenceSources:remote/user/env/default),设置页据此判定置灰与来源说明;来源为user/default的键与改造前完全一致(不显示任何来源说明)。本场景只覆盖进程级可达的层(Remote / 用户级文件 / 机器环境变量 / 默认),工程范围与视图落点见边界说明「用户偏好的层与来源」。
用户故事:配置服务端地址(优先级:P1)
作为用户,我希望在设置页「全局设置」里直接填写 Wave 服务端地址(如测试环境或私有化部署的地址),而不必手改 ~/.wave/settings.json 的 env.WAVE_SERVER_URL,以便切换当前要连接的服务端。
为什么是这个优先级:服务端地址决定 SSO 登录、AI 网关(<serverUrl>/api/v1)与远端托管配置的端点,是接入测试环境 / 私有化部署的前提。此前唯一入口是手改 settings.json 的 env.WAVE_SERVER_URL 或设置 OS 环境变量,普通用户没有 UI 入口。
独立测试:在设置页「全局设置」的「基础设置」里填写服务端地址并保存,验证写入用户级 ~/.wave/settings.json 的 env.WAVE_SERVER_URL(readUserPreferenceSettings 可读回、其余顶层键与 env 下其它键原样保留);清空后保存不改动该键(不写盘)。
验收场景:
- 假设 设置页「全局设置」视图已打开,当 页面渲染,则 「基础设置」卡片内在「上下文长度」行之后有一行「服务端地址」文本输入框:
env.WAVE_SERVER_URL有值则回填生效值;未设置(settings.json 无该键)则留空并以灰字占位符显示默认地址(与 SDKDEFAULT_SERVER_URL同串,https://codechat.codewave.163.com),不编造真实值(对齐本规格既有未设置态约定,不得出现「设置页假装一个地址、生效值却是另一个」的分叉)。 - 假设 用户在「服务端地址」输入框填入一个地址(如
https://codechat.codewave-test.163yun.com)并点击「保存」,则 该值经updateConfiguration→ 宿主 → CLIupdateUserSettings写入用户级~/.wave/settings.json的env.WAVE_SERVER_URL(顶层env对象内,其余顶层键与env下其它键原样保留),并按「设置实时重载」重载;重载后后续解析(AI 网关${serverUrl}/api/v1、AuthService.getServerUrl())读到新值。 - 假设 「服务端地址」处于未设置态(settings.json 无该键)或用户未改动它,当 用户点击「保存」,则 该键不得出现在载荷里(省略键 = 不改该键);用户把已写过的值清空后保存,同样不写该键(不提供「清除 / 恢复默认」,与其余偏好键一致)。
- 假设 用户在「服务端地址」输入了非空且不以
http:///https://开头的值(如codechat.example.com),当 用户点击「保存」,则 该键不写入 settings.json,并在该行显示一行格式提示(服务端地址须以http://或https://开头)——避免把写坏的地址落盘导致 SSO 登录 / 网关请求指向错误端点;同一次保存里其余有效字段不受影响。 - 假设 生效的服务端地址来自更高层(OS 环境变量
WAVE_SERVER_URL有值、用户级文件无该键),当 设置页渲染,则 该行显示生效值并标注来源(当前值来自系统环境变量、保存后以本页设置为准),保持可编辑——与其余偏好键的env来源语义一致(见「IDE 插件配置入口」场景 9)。
用户故事:快速模型思考禁用配置(优先级:P1)
用户将推理模型(如 deepseek-v4-flash)配置为快速模型(fastModel)后,WebFetch 内容处理、快速子代理(model: fastModel)等轻量任务会偶发 "Empty response from AI" 错误——推理模型的思考(reasoning)消耗了全部输出 token(max_tokens),导致 content 为空。用户希望为不同模型配置各自的"禁用思考"参数,使快速模型场景可按需禁用思考、稳定返回内容,同时不影响主代理对话(agent loop)中的思考能力。
为什么是这个优先级:这是快速模型场景空响应错误的直接修复,影响 WebFetch 等常用工具的可用性。
独立测试:分别配置与不配置 models[X].disableThinkingOptions,触发 WebFetch 内容处理与快速子代理,验证请求携带正确参数;同时验证主代理对话与自动记忆提取、上下文压缩等后台 fork 请求不受影响。
验收场景:
- 假设用户未配置
disableThinkingOptions,当快速模型场景(如 WebFetch 内容处理)发起请求时,则请求不携带任何禁用思考参数,交由模型默认处理(避免对不支持该参数的网关报错) - 假设用户为模型 X 配置
models[X].disableThinkingOptions: {"enable_thinking": false},当快速模型场景使用模型 X 时,则请求携带{"enable_thinking": false} - 假设用户为快速模型配置了
models[fastModel].disableThinkingOptions,当 WebFetch 内容处理与快速子代理使用快速模型时,则两类请求都携带该模型的禁用思考参数 - 假设用户为快速模型配置了
models[fastModel].disableThinkingOptions,当自动记忆提取、上下文压缩等后台 fork 使用主模型(复用主对话 prompt cache,无modelOverride)时,则请求不携带禁用思考参数,思考行为保持模型默认 - 假设用户配置了
models[X].disableThinkingOptions,当主代理对话(agent loop)使用模型 X 时,则请求不携带禁用思考参数,思考行为保持模型默认 - 假设用户为模型 X 配置
disableThinkingOptions: {},当快速模型场景使用模型 X 时,则请求不携带任何禁用思考参数,交由模型默认处理
边界情况
- 当提供部分配置时会发生什么(如 apiKey 但无 baseURL)?
- 系统如何处理无效配置值(空字符串、格式错误的 URL、非数字 token 限制)?
- 当构造函数参数和环境变量同时存在时会发生什么?
WAVE_CUSTOM_HEADERS中的格式错误行如何处理?(应被忽略)- 当 settings.json 在实时重载期间包含格式错误的 JSON 时会发生什么?
- 系统如何处理文件监视期间的文件系统权限错误?
- 系统如何处理快速连续的文件修改?
- 当文件监视器在系统启动时初始化失败会发生什么?
- 设置页加载已保存配置失败时如何展示?(设置页内显示错误与重试入口;编辑保存失败时经宿主全局 toast 提示错误并保留用户输入,不渲染页面内自建提示文字)
- 不同模型的禁用思考参数形态不同(
thinking: {type}/enable_thinking/reasoning_effort),SDK 不做内置映射,由用户在models[X].disableThinkingOptions中按目标接口格式原样配置 - 未配置
disableThinkingOptions时,快速模型场景不发送任何禁用思考参数;SDK 不内置默认值,避免直连不支持thinking参数的网关时报错
边界说明:环境变量作用域与优先级
- 分层职责:
AgentOptions覆盖层 vs settings.json 实时配置(2026-09-10 用户拍板):AgentOptions(构造参数,以及 stdioinitialize/updateConfig传入的会话参数)的语义是会话级 / 单次覆盖——用于 CLI--model、--permission-mode这类一次性指定,以及程序化调用方的显式注入。用户偏好类配置(语言、模型、上下文长度、自动记忆、权限、hooks、env)不得经该层下发:各解析链优先级为覆盖项 > settings.json > env > 默认,把用户偏好塞进覆盖层会永久遮蔽 settings.json 的实时值(层倒置),使「保存即生效 / 实时重载」全部失效(这正是 2026-09-10 前保存必须重建会话的根因)。 - 用户偏好的落点 = 用户级
~/.wave/settings.json:三端设置页保存用户偏好时写入该文件(SDK 已在监视的实时配置源),由「设置实时重载」故事生效;插件装卸等变更走就地重载(见 plugin.md「插件变更的就地重载」),没有任何配置变更需要重建会话(见「配置变更不再需要重建会话」故事)。会话级覆盖仍可经AgentOptions下发,但不得用于承载用户偏好。 - 语言默认值(2026-09-10 追加):语言解析链是
AgentOptions / stdio initialize 覆盖项 > settings.json > 默认,末尾默认值为zh-CN(SDKDEFAULT_LANGUAGE)。取值必须与设置页「AI 回复语言」下拉未设置态显式项「未设置(默认:中文)」的默认值同串(webview 的zh-CN项;不再是改造前各端各自的兜底串Chinese),否则 settings.json 未设置该键时会出现「设置页显示中文、实际按别的语言回复」的分叉(见「配置首选语言」故事场景 3、「IDE 插件配置入口」故事场景 7)。该值原样拼进系统提示# Language\nAlways respond in <值>,因此用户写任意串(如Spanish)都按原样生效,SDK 不做词汇映射。三端宿主不得再各自注入语言默认值(改造前 desktopconfigStore.getConfiguration()、VSCEloadConfiguration()、JBConfigurationData.language三处各自的默认值已随覆盖层链路一并删除)——默认值只在 SDK 解析链末尾有一处,保证 CLI 与三端 GUI 同语义。 - 省略键 = 不改该键(2026-09-10 追加):设置页保存用户偏好的载荷(webview → host → CLI
updateUserSettingsRPC)是部分更新语义——只写载荷里出现的用户偏好键,未出现的键保持文件中现值(CLI 侧updateUserPreferenceSettings读-改-写、宿主侧只把已提供的键放进 RPC patch)。设置页据此把「未设置」表达为「不提供该键」:用户没改过的字段、仍处于未设置态的字段一律不出现(见「IDE 插件配置入口」故事场景 7–8),所以「一个字都没改就点保存」不会把任何键钉进文件。副作用是刻意保留的收益:WAVE_MAX_INPUT_TOKENS只存在于用户系统环境(而非 settings.json)时,进设置页不做任何输入地保存一次不会把它钉成200000。不做「清除 / 恢复默认」按钮(把已写入的键退回未设置态)——该载荷没有「删键」语义,去掉键需手改 settings.json。宿主侧同样不得给缺失键补默认值(否则「省略」会被宿主翻译成一次写入)。 - 用户偏好的层与来源(2026-09-10 追加):用户级
~/.wave/settings.json是用户偏好的落点,但不是「谁最后说了算」——取值按 SDK 各解析链的层序,从高到低为AgentOptions / stdio initialize 覆盖项 > Remote 组织下发 > local(<workdir>/.wave/settings.local.json)> project(<workdir>/.wave/settings.json)> user(~/.wave/settings.json)> env(settings 快照 > OS 环境变量)> 默认值。因此设置页的初始值读取必须是生效值:宿主经无会话的全局 RPC(getUserSettings)向会话所在进程查询,CLI 侧按上述层序归因,回包在生效值之外附preferenceSources(每个键的来源层:remote/user/env/default;每个键恒有来源,缺省即default)。设置页的语义 = 显示用户偏好的值,并如实标注被更高层覆盖时的真实生效值:来源为remote的键显示生效值 + 置灰(组织策略不可被本地覆盖)+「由组织配置管理」;来源为env的键同样显示生效值(不得回落成「未设置」占位符——否则页面显示 200K 而实际生效 64K 分叉)+ 一句来源说明(当前值来自系统环境变量、保存后以本页设置为准),但保持可编辑(用户级文件优先级高于 OS 环境变量,在此保存即写进该文件并覆盖环境变量的值);来源为user/default的键与改造前一致,不显示任何来源说明。两个易错点须钉住:①上下文长度没有顶层键,它的落点是env.WAVE_MAX_INPUT_TOKENS(见边界说明「上下文长度的落点」),因此它的来源层 = 该 env 键所在的那一层(Remote 的env> 用户文件的env> OS 环境变量);②自动记忆开关每层有两条路径——顶层标量autoMemoryEnabled与env.WAVE_DISABLE_AUTO_MEMORY,且顶层标量整层优先于 env 路径(resolveAutoMemoryEnabledFrom先看合并后的配置、再看 env),归因时不得把「Remote 的 env 键」错误地排在「用户文件的顶层键」之前。值的语义与改造前完全一致(键缺失 = 没有任何提供者,设置页按 SDK 默认展示未设置态/「开」),preferenceSources是纯增量字段:老宿主/未实现该字段的宿主回包缺少它时,设置页全部按键可编辑(向后兼容)。归因范围(本 PR 只做进程级可达的层):①override/options(AgentOptions构造参数、stdioinitialize/updateConfig的覆盖项)不做——三端宿主已不再给这些键下发覆盖项、AgentOptions.maxInputTokens也无宿主使用,该层当前恒空,故设置页不展示它的归因,也不为恒空分支另造会话级通道;②project(<workdir>/.wave/settings.json)/local(<workdir>/.wave/settings.local.json)留后续——归因它们需要 workdir,而getUserSettings是刻意的无会话全局 RPC,故被 project/local 覆盖时设置页可能仍显示用户级值(本 PR 不归因、不置灰),待查询入口能带 workdir(或在项目设置视图内做项目级归因)后补齐;③按模型上限的遮蔽不归因(既有语义,非本次回归)——resolveMaxInputTokens链里currentConfiguration.models[被解析模型].maxInputTokens位于env.WAVE_MAX_INPUT_TOKENS之上,因此「在该模型上配了上限」的用户,其「上下文长度」行显示的是 user/env 的具体数字,而真正生效的是模型配置;该行的可见说明「全局默认;当前模型自带上下文上限时以模型配置为准」已覆盖这层歧义,本 PR 不为它引入 per-model 展示或改判定(否则设置页要跟随当前模型变化,超出「进程级可达层」的范围)。视图落点:归因本身是全局的(不依赖 workdir),任何渲染这些偏好控件的视图共用同一回包判定置灰与来源说明;当前这些键的控件只出现在「全局设置」(语言 / 上下文长度 / 服务端地址)与「个性化」(自动记忆开关 / 轮次)两个视图,「项目设置」视图只有 SDD 插件开关、不含用户偏好控件,故本 PR 的落点即前两个视图。本场景的验证落点见「验证分层」表的「被更高层覆盖的键如实显示(场景 9)」行。 - 升级用户的一次性迁移:不做(2026-09-10 追加拍板):改造前用户偏好(
language/contextLength/autoMemoryEnabled/autoMemoryFrequency)存放在宿主私有存储(VSCEglobalState/ JBwave.xml/ 桌面wave-desktop.json)。本规格生效后这些键不再被读作用户偏好的落点,且不做一次性迁移(不把旧值搬进 settings.json):旧language值大多是改造前各端注入的默认串(Chinese),既无法与用户显式选择区分,词汇也与设置页的zh-CN/en-US不同——迁进 settings.json 会把旧默认固化成「用户显式设置」,并制造「设置页显示中文、settings.json 写Chinese」的新分叉(正是本规格要消除的那类分叉)。代价(升级影响,须随发布说明告知):升级用户此前显式选过的语言 / 上下文长度 / 自动记忆开关与频率会静默回落默认值(语言 →zh-CN,其余 → SDK 既有默认),需在设置页重选一次;此后用户偏好的落点只有 settings.json 一处(不再有第二处宿主私有存储),但它不是「唯一真源」——它是取值链里的一层,更高层的 Remote 组织下发与机器环境变量仍会盖过它(见边界说明「用户偏好的层与来源」)。 - 重建确认框已废止(2026-09-18 拍板):2026-09-10 曾规定「重建确认框(立即重启 / 稍后重启)为桌面端专属」——理由是只有桌面端的保存回执会等待会话重建完成,用户才会感知到「保存很久才恢复」,故需要弹框让用户选择时机(VS Code 扩展 / JetBrains 插件的保存回执本就 fire-and-forget、无此症状)。2026-09-18 起插件变更改为就地重载、重建路径整体废弃(见 plugin.md「插件变更的就地重载」与「配置变更不再需要重建会话」故事),该确认框与「桌面端专属」这个例外一并取消;三端「用户偏好一律走 settings.json 实时重载」的同语义要求不变(见「IDE 插件配置入口」故事场景 6)。
- 上下文长度的落点 = 全局
env.WAVE_MAX_INPUT_TOKENS(2026-09-10 用户拍板):设置页的「上下文长度」读写同一个全局键env.WAVE_MAX_INPUT_TOKENS(K×1000;用户原话「那本来就是全局的上下文设置」),不新增 per-model 落点、不改动 SDK 既有解析优先级。因此存在一个刻意保留的语义:该键是全局默认,当当前模型自带上下文上限(服务端下发的models[<id>].maxInputTokens,即resolveMaxInputTokens链中constructorLimit > options.maxInputTokens > models[resolvedModel].maxInputTokens > envSnapshot.WAVE_MAX_INPUT_TOKENS > 默认的靠前项)时,以模型配置为准、全局值被遮蔽——这是既有 SDK 行为,本规格不修改它。为防「改了不生效」的新投诉,设置页「上下文长度」行须给出一句可见说明(文案从轻,如「全局默认;当前模型自带上下文上限时以模型配置为准」;不新增区块、不新增状态源、不引入 per-model 展示)。与来源归因的关系(2026-09-10 追加):模型上限在取值链里位于 env 之上,而它不在「进程级可达的层」范围内,故该行的preferenceSources仍按 user/env 归因——即「配了按模型上限的用户,该行显示 user/env 的数字、生效值是模型配置」这一残差如实保留并记录在案(见边界说明「用户偏好的层与来源」③)。 - settings.json
env与 OS 环境变量的关系:settings.jsonenv存入会话级快照,优先级高于 OS 环境变量,但不写入process.env(WAVE_SERVER_URL例外,见下条)。优先级从高到低:显式构造参数 / stdioinitialize参数 > settings.jsonenv(快照)> OS 环境变量 > 默认值。 WAVE_SERVER_URL支持从 settings.jsonenv读取(镜像到process.env):AuthService与remoteSettingsService是进程级单例,不持有 per-session 快照,因此 settingsenv里的WAVE_SERVER_URL经setEnvironmentVars特例镜像写入process.env,使这些单例能读到。优先级:options.serverUrl/ stdioinitialize参数 > settings.jsonenv(镜像到process.env)> OS 环境变量 > 默认值。由于AuthService是进程单例(一个进程一个 server),同进程多会话的WAVE_SERVER_URL应为同值,镜像不造成跨会话污染。启动 401 竞态已通过 init 顺序消除(loadCacheFromDisk→loadMergedConfiguration写入process.env→startBackgroundFetch读取)。- 服务端地址的落点 = 用户级 settings.json 的
env.WAVE_SERVER_URL(2026-09-15 追加):设置页「全局设置」的「服务端地址」读写 SDK 既有键env.WAVE_SERVER_URL(与「上下文长度」同为 env 落点,见上条),不新增顶层键、不改动 SDK 既有解析优先级(options.serverUrl > process.env.WAVE_SERVER_URL > DEFAULT_SERVER_URL)。该键作为用户偏好的第 5 个键进入UserPreferenceSettings/preferenceSources,但来源层只有user/env/default——不可能为remote(远端托管配置本身取自该地址,不存在「组织下发服务端地址」的闭环),故该行没有「由组织配置管理」置灰态。生效时机:reloadConfiguration→loadMergedConfiguration→setEnvironmentVars会按特例重新镜像该值到process.env,故保存后后续解析(AI 网关 baseURL、AuthService.getServerUrl())即读新值;而正在运行的后台 token 刷新 / 远端设置轮询,以及桌面端本地缓存的服务地址(仅用于拼更新 feed,经getAuthStatus更新,见 desktop-account-and-settings.md),可能要到下一次刷新 / 查询才收敛。只做「必须http(s)://开头」的格式校验,不做其它清理或归一(不补尾斜杠、不改大小写),落盘为原样字符串(见「配置服务端地址」故事场景 4);未设置态 = 不写该键(见同名故事场景 3)。 - SSO 认证 / 远端设置轮询为进程级单例:
authService与remoteSettingsService是进程级单例(一个auth.json/ 远端设置缓存——按用户)。因此对于后台 token 刷新/远端设置轮询,每个进程只有一个serverUrl;AI 网关/模型/密钥的解析本身仍是按会话进行的。 - 基础设施子进程保持 OS-env-only:git/worktree/LSP 等基础设施子进程只读取 OS 环境变量(
PATH/HOME/LC_ALL等),不合并会话快照。WAVE_PLUGIN_GIT_TIMEOUT_MS、WAVE_SHELL、WAVE_GIT_BASH_PATH等"基础设施级"变量不从 settings.jsonenv读取,需通过 OS 环境设置。
边界说明:验证分层(单测 / e2e / 真 host)
本规格与 desktop-account-and-settings.md「用户偏好的保存路径与重建时机」的验收要求按三层测试落地(2026-09-10 追加拍板:两层新测试基建建成后,本需求的每个场景都必须标明落在哪一层)。
三层职责边界:单测(packages/*/tests,vitest,宿主/组件被 mock)验证「本端自己发出的东西对不对」;e2e(packages/webview/e2e,Playwright 真浏览器 + 真 webview bundle,宿主被消息注入替代)验证「真 UI 在真实浏览器里的交互与渲染」;真 host(packages/desktop/tests/integration,真 DesktopHost + 真 wave --stdio 子进程 + 真文件系统,pnpm -F wave-desktop test:realhost)验证「跨进程接缝」——单测永远看不到的那一层。
保存路径热生效(PR-2)
| 场景 | 单测 | e2e | 真 host |
|---|---|---|---|
| 保存后真 CLI 进程未被重启(sessionId 不变)+ 会话不丢(「设置实时重载」场景 1/2、「IDE 插件配置入口」场景 6) | 宿主侧「不再调 updateConfig」 | 保存后未弹重建确认框、未发重建回执(未触发重建) | 主验:保存前后 sessionId 一致、转录文件唯一且连续 |
| 自动记忆开关/频率在保存路径上真的离开 webview(memory-management.md「自动记忆开关」场景 1–6 的入口;#2115 回归网) | settingsMemory.test.tsx(SettingsPage.onSave 载荷 + ChatApp updateConfiguration.configurationData 两键) | 主验:packages/webview/e2e/desktop-settings-auto-memory-save.e2e.ts(真 bundle 点保存 → 真报文断言两键;另验证宿主回包回填开关/轮次) | — (host→CLI 的 stdio 透传由对应透传测试负责,不在此重复) |
settings.json 被真实写入且下一轮读到新值(language 一例,场景 4) | SDK LiveConfigManager 解析 | — | 主验:真 CLI 进程 + 真文件:写文件后下一轮行为变化 |
| 自动记忆开关:关闭后自动记忆目录写入被权限层拒绝、重开恢复(场景 6) | 白名单幂等增删 | — | —(本 PR 未覆盖:驱动权限层需要模型发出工具调用,而真 host 的本地假模型只回文本) |
会话启动时 settings.json 不存在,首次创建/保存后也生效(场景 7;LiveConfigManager existsSync 门禁回归) | 主验:watcher 门禁单测(不存在但父目录存在 → 监视该路径 / 无父目录 → 上溯) | — | —(「启动时文件不存在」在真 host 构造不出:真 CLI 启动即自建 ~/.wave/settings.json(插件市场引导);对应的可观察形态=文件存在但没有该键,见下一行) |
| 保存回执即已重载、紧接的下一轮生效(场景 7 的「不得只依赖监视」;显式 reload) | 主验:bridge 单测断言写完文件后对进程内全部 live 会话各调一次 reloadConfiguration(空载荷不调) | — | 主验:真 host——文件里没有 language 时保存一次,紧接的下一轮即带新语言指令(不设「等 watcher」重试循环) |
被更高层覆盖的键如实显示(场景 9:生效值 + remote 置灰「由组织配置管理」/ env 标注来源但可编辑;边界说明「用户偏好的层与来源」) | 主验:SDK readUserPreferenceView 层序/来源单测(各键 × 各层:上下文长度的 env 键归因、自动记忆开关的两条 Remote 路径、顶层标量整层优先于 env、文件损坏只丢用户层)+ bridge 单测断言两个 RPC 回包 = 生效值 + preferenceSources + settingsGlobal / settingsMemory 断言 remote 键置灰与提示、env 键显示生效值 + 来源说明 + 改得动(载荷含该键)、user / default 键无任何来源说明、缺字段向后兼容 | 主验:settings-org-managed-keys.e2e.ts(真 bundle:remote 置灰 + 提示;env 显示生效值 + 来源说明且可编辑) | 主验:真 host——机器环境变量(WAVE_MAX_INPUT_TOKENS)提供的上下文长度按生效值回给设置页且来源层标为 env |
| 轮内快照不抖动(场景 5) | 快照读取时机 | — | —(本 PR 未覆盖:需要同一轮内的第二次模型调用,假模型只回文本、无法在轮中再次发请求) |
上下文长度落 env.WAVE_MAX_INPUT_TOKENS(边界说明「上下文长度的落点」) | 解析链与换算单测 | — | —(本 PR 未覆盖:假模型不回 usage,宿主的 contextUsage.percent 无从观察取值;落盘值本身已在真 host 断言) |
插件变更不自动生效、只提示,敲 /reload-plugins 后六类能力就地换装(「配置变更不再需要重建会话」场景 3–4) | 断言不发起任何重建 | 主验:真 bundle——敲 /reload-plugins 后的提示与能力生效 | 主验:真 host——变更真落盘、重载后同一 CLI 进程 sessionId 不变 |
| 未敲命令时新开对话按磁盘新状态装载、既有对话保持原状(「插件变更的就地重载」场景 9) | — | — | 主验:真 host——新建会话按新状态装载、既有会话能力不变 |
凭据链路下线(PR-1,已随本规格同批落地)
| 场景 | 单测 | e2e | 真 host |
|---|---|---|---|
| IDE 未认证时欢迎页显示登录入口、遗留直连配置不再关掉它(sso-auth.md「IDE 插件更多菜单与欢迎页」场景 5/7) | welcomeViewFlash.test.tsx | 主验:packages/webview/e2e/ide-welcome-login.e2e.ts | — |
| IDE 宿主设置页无任何凭据输入控件(sso-auth 场景 5 / 边界情况「IDE 宿主不再有直连免登录旁路」) | 四视图渲染断言 | 主验:packages/webview/e2e/ide-settings-credentials-removed.e2e.ts(7 视图全扫 + 带遗留凭据的 configurationResponse 不产生控件) | — |
宿主→CLI 真报文不含 apiKey / baseURL / defaultHeaders(同上边界情况) | 宿主测试断言不透传 | — | 主验:packages/desktop/tests/integration/realHostCredentialWire.integration.test.ts(tee 抓真字节:initialize / updateConfig / sendMessage 双向报文) |
| 未认证时发送入口禁用、禁用原因写在输入框占位文案(sso-auth.md「IDE 插件更多菜单与欢迎页」场景 8) | unauthenticatedInputDisabled.test.tsx(未登录)+ desktopApp.test.tsx(桌面未选目录「should disable the input area when no workdir is selected」两条原因独立 +「shows the login reason, not the workdir reason, when both reasons apply」登录优先) | 主验:packages/webview/e2e/ide-welcome-login.e2e.ts、packages/webview/e2e/desktop-unauthenticated-input-disabled.e2e.ts(真 bundle 下两条原因互不覆盖、登录后恢复) | — |
| 未登录时宿主如实回带未认证、且不发模型请求(同上边界情况) | — | 欢迎页登录入口可见 + 输入框禁用 | 主验:同一真 host 用例(setInitialState.isAuthenticated === false 驱动 webview 门禁;无凭据回合 model.requests 为空、无 endStreaming)。注:仍不新增「未登录即拒绝新建对话/恢复历史」的宿主或 CLI 硬门禁,若将来要求须另立规格 |
main-only CI 说明:真 host 层挂在两个 main-only job——Linux real-host-e2e,以及 Windows check-windows(含 desktop 单元套件 + 真 host,平台差异面);e2e 层 test:e2e 在 PR CI 中为跳过项(非门禁)。本地执行命令:pnpm -F wave-webview test:e2e、pnpm -F wave-desktop test:realhost。