Commit 21ce320
feat(proactive): 主动搭话模式/频率 API + 内置控制器插件 (#1331)
* feat(proactive): 主动搭话模式/频率 API + 内置控制器插件
为主动搭话新增稳定的 API 与一个内置系统插件 `proactive_controller`。
其他插件不再需要直接操作 conversation-settings 的一堆 `proactive*Enabled` /
`proactive*Interval` 字段,只通过
`ctx.plugins.call_entry(proactive_controller:set_mode, {...})` 等就能集中
调度。
- `main_routers/proactive_router.py` 新增四个端点:
- GET/POST /api/proactive/mode 套用预设 (off / normal / focus / frequent)
- GET/POST /api/proactive/settings 字段子集读/部分更新
- `plugin/plugins/proactive_controller/`:新内置插件
- 入口:set_mode / set_settings / get_state / command(聚合入口)
- 默认关闭:首次启动且 `proactiveChatEnabled` 字段缺失时套用 off;已有用户偏好原样保留
- i18n 覆盖 zh-CN/zh-TW/en/ja/ko/ru/es/pt
- 顺带修一个单位 bug:`utils/preferences.py` 中 interval 校验从
`1000 <= v <= 3600000`(毫秒)改为 `1 <= v <= 3600`(秒),与前端
`app-state.js` 的 `proactive*Interval` 单位一致;此前前端发的 interval
值因校验单位 mismatch 被静默丢弃。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(proactive): applied 回显改为保存后回读 + 空 except 加 warning log
回应 CodeRabbit / Codex / github-code-quality 三家 reviewer 的 inline 评论:
- `POST /api/proactive/mode` 与 `POST /api/proactive/settings` 的 `applied` 字段原本直接回显请求 payload,但 `save_global_conversation_settings`
还会做第二轮类型 / 范围过滤(如 `1 <= interval <= 3600`),被丢弃的字段仍会让端点返回 success,调用方会误判为生效。改成保存后回读
`aload_global_conversation_settings`,仅返回真实落盘字段;`settings` 端点额外加 `rejected` 列出被静默丢弃的字段名,避免哑失败。
- `proactive_controller` 插件 startup 中 `api_timeout_seconds` 解析失败的空 except 改为 warning log,记录原始值 + fallback 值,便于 debug。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(proactive): 隐私模式(proactiveVisionEnabled)锁为用户绝对控制权
`proactiveVisionEnabled` 是前端隐私模式开关的反面(`is_privacy_mode_enabled`
== `not proactiveVisionEnabled`),涉及屏幕内容采集,必须由用户在 UI 自己决定,
插件和预设都不能越权写入。本 PR 之前 `off`/`focus` 预设会把它设为 False、
`normal`/`frequent` 设为 True,会绕过用户的隐私选择。
修复:
- 引入 `_USER_OWNED_FIELDS = {proactiveVisionEnabled}` 概念,构造
`_PROACTIVE_WRITABLE_FIELDS = _PROACTIVE_FIELDS - _USER_OWNED_FIELDS`。
- 从所有 4 个预设里删掉 `proactiveVisionEnabled`,切 mode 不再改隐私选择。
- 模块导入时 self-check:预设若意外混入 user-owned 字段,直接抛 RuntimeError,
把加预设时忘了筛挡在导入阶段。
- `POST /api/proactive/settings` 显式拒绝 user-owned 字段,并通过新字段
`rejected_user_owned` 报告给调用方。
- 插件 `set_settings` 在 client 侧也做同样过滤;input_schema 和 `set_mode`
描述里也写明 vision 是用户专有,避免 LLM 试图调它。
- 读路径(GET endpoints)不变,仍返回隐私字段当前值,方便插件读取后据此
决定是否要建议用户开/关,而不是自己写。
注:`proactiveVisionChatEnabled` / `proactiveVisionInterval` 仍由插件可调,
因为隐私已被主开关 gated,副开关不构成额外隐私越权。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(proactive): applied 判定改为按值比较(修复 Codex P1)
Codex P1 指出:`_readback_subset` 用 key existence 判断字段是否生效,
但磁盘上若已存在该字段的旧值,新提交的值被底层 validation 拒绝时仍会
被误标为已生效(applied 显示的是旧值而非传入值)。
改进:
- helper 改名 `_readback_persisted(payload: Mapping) -> (applied, rejected)`,
按 `latest[k] == payload[k]` 比较;新旧值不一致即诊断为 rejected。
- `POST /api/proactive/mode` 也用同一 helper,并在 preset 出现 rejected
时回报——这对预设来说理论上不应发生,可作为 server 端跟进信号。
- `POST /api/proactive/settings` 同步切换;行为与之前差别:传 0 等
非法 interval 给一个磁盘上已有 interval=15 的字段时,原实现回报
applied=15(误导),新实现回报 rejected=[proactiveChatInterval]。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(proactive): preset self-check 增加 unknown-key 探测
CodeRabbit nitpick:当前 self-check 只挡 ``_USER_OWNED_FIELDS`` 泄漏,
未挡键名拼写错误(如 ``proactiveChatEnable`` 漏 d)。叠加一条
``set(preset) - _PROACTIVE_WRITABLE_FIELDS`` 检查,让导入阶段就抛出
明确异常,而不是等用户调 set_mode 才发现字段静默被丢弃。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(proactive): readback 比较增加 type 严格校验(修复 Codex P2)
Codex P2 指出:Python 中 True 等于 1、False 等于 0,所以传 int 1 给 bool 字段时——saver 用 isinstance(v, bool) 会拒,但回读 latest[k] == payload[k] 仍会让磁盘上的 True 与传入的 1 判等,重新把"已被拒绝的非法 0/1"误标成 applied。
修:抽出 _value_matches(actual, expected) helper,要求 type(actual) is type(expected) 才比较相等,彻底切断 bool/int 跨类型相等的陷阱。_readback_persisted 改用这个 helper。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Hongzhi Wen <cartabio.coder1@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 34f4a29 commit 21ce320
13 files changed
Lines changed: 639 additions & 1 deletion
File tree
- app
- main_routers
- plugin/plugins/proactive_controller
- i18n
- utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1466 | 1466 | | |
1467 | 1467 | | |
1468 | 1468 | | |
| 1469 | + | |
1469 | 1470 | | |
1470 | 1471 | | |
1471 | 1472 | | |
| |||
1512 | 1513 | | |
1513 | 1514 | | |
1514 | 1515 | | |
| 1516 | + | |
1515 | 1517 | | |
1516 | 1518 | | |
1517 | 1519 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
0 commit comments