v0.1.12
更新日志
紧随 v0.1.11 的第二轮全面加固:换一批新眼睛对全库再审一遍(上下文核心 / 服务层 / 工具层 / Web / 提示词与 harness 设计),加一轮真实浏览器的全视图视觉评审(明暗主题 × 桌面/手机 × 全部六个页面)。找到的 19 个问题全部修复,并由独立的对抗复核确认无一误修。1433 项后端 + 243 项前端测试全绿。
修复(稳定性 / 安全)
- 状态轮询不再可能崩掉进行中的回合:右侧面板的上下文用量显示会高频调用
token_count(),它的 token 备忘录写入与工作线程trim()的字典遍历同处一份数据——特定时机下抛RuntimeError: dictionary changed size during iteration,直接砸断正在流式输出的回合。备忘录现在有了自己的锁,正常聊天 + 状态轮询的组合彻底安全。(73e5c27) - 密钥环写入原子化:
desktop.json存着你所有 provider 的 API key,过去是就地截断重写——写到一半崩溃/断电会清空全部密钥;两个并发的保存操作还会互相丢失对方的条目。现在走临时文件 + 原子替换(0600 权限),所有读改写(含旧版 key 迁移路径)在一把锁下串行。 - 断线重连不再丢帧:角色正在输出时恰好重连,重放窗口内新产生的帧过去既不在重放快照里、也不会实时下发——永久静默丢失。重放现在循环追平后才切换到实时流。
- Linux bwrap 沙盒下
terminal(workdir=…)生效了:过去 jail 参数硬编码切到工作区根目录,模型指定的子目录被静默忽略(npm install跑错地方却毫无提示)。macOS 与 Landlock 一直是对的,现在三层一致。 - 搜索在沙盒被拒时如实报错:过去会静默退化到不经沙盒的纯 Python 文件读取;现在与 terminal 一致——沙盒不可用就明确拒绝,绝不静默降级。
修复(提示词 / harness 设计)
execute_code不再宣传已下架的联网工具:它的模型可见文档还在教web_search/web_extract,但这两个工具早已搁置——模型照着写脚本必然撞墙白烧一回合。文档与真实能力对齐;顺带清掉了沙盒暂存目录里最后一处旧名残留。terminal超时文案与真实行为对齐:超过 600 秒的前台超时实际是截断(附提示),不是文档声称的"拒绝"。- 每回合尾部瘦身:volatile tail 过去每回合重发两句永远不变的工作区介绍(缓存前缀里已有一份)——现在只发真正动态的事实(隔离/网络/日期),长会话省下可观的重复 token。
修复(Web)
- 会话切换的竞态关死:刚销毁的会话的迟到帧过去可能写进下一个会话的消息流;四个即时回调补上了销毁守卫。
- 连接建立前发消息不再报错:先前会弹一条生硬的 "not connected"——现在自动进入持久发送队列,连上即按序送出(气泡即时出现,符合"每次点击立即响应"的原则)。
- 聊天设置的粘性开关(个人网站 / 演员模式 / 沙盒)在快照或角色变化时正确回收,不再显示上一个角色的残留状态。
- Gateways 空状态居中重排,去掉重复按钮。
其余
窄窗口切换后立即裁剪上下文(而非等到下一条消息);请求日志遇到单条超限记录不再自清空;hub 超时的操作会真正取消而非孤儿化;世界书关键词扫描少做一遍全文小写化;现场审计方法论沉淀进 docs/AGENT-FIELD-NOTES.md(含一个会伪装成主题 bug 的无头浏览器渲染伪影)。
Changelog
The second full-sweep hardening, right after v0.1.11: a fresh five-track audit (context core / server / tools / web / prompt & harness design) plus a real-browser visual review of every view × theme × viewport. All 19 findings fixed and independently re-verified by an adversarial review. 1433 backend + 243 frontend tests green.
Fixes (stability / security)
- Status polling can no longer crash an in-flight turn: the side panel's context-usage display calls
token_count()frequently; its token memo writes raced the worker thread'strim()dict iteration — under the right timing,RuntimeError: dictionary changed size during iterationkilled the streaming turn. The memo now has its own lock; chatting with the panel open is fully safe. (73e5c27) - The keyring is written atomically:
desktop.jsonholds every provider API key and used to be truncate-rewritten in place — a crash mid-write wiped all keys, and two concurrent saves could silently drop each other's entries. Now temp-file + atomic replace (0600), with every read-modify-write (including the legacy-key migration) serialized under one lock. - Reconnect no longer loses frames: reconnecting while the character is mid-stream could permanently drop frames produced during the replay window (in neither the snapshot nor the live feed). The replay now loops until caught up before switching live.
terminal(workdir=…)works under the Linux bwrap jail: the jail argv hardcoded a chdir to the workspace root, silently ignoring the requested subdirectory (npm installran in the wrong place with no hint). macOS and Landlock were already correct; all three tiers now agree.- Search fails honestly when the jail is refused: it used to silently degrade to unjailed pure-Python file reads; it now refuses with a visible error, same as the terminal — the isolation ladder never degrades silently.
Fixes (prompt / harness design)
execute_codeno longer advertises the shelved web tools: its model-facing docs still taughtweb_search/web_extract, which are deliberately disabled — the model would write a script straight into a dead end. Docs now match real capability; the last legacy-named staging file is gone too.terminaltimeout wording matches behavior: an over-limit foreground timeout is clamped (with a note), not "rejected" as the schema claimed.- Slimmer per-turn tail: the volatile tail re-sent two never-changing sentences about the workspace every turn (already taught once in the cached prefix) — it now carries only the dynamic facts (isolation/network/date), saving real tokens over long sessions.
Fixes (web)
- Session-switch races closed: a just-disposed session's late frames could write into the next session's message stream; all four immediate callbacks gained the disposal guard.
- Sending before the connection is ready no longer errors: instead of a raw "not connected", the message rides the persisted send queue and delivers once attached (the bubble appears instantly, per the instant-response principle).
- Sticky chat-settings toggles (personal website / actor mode / sandbox) reconcile on snapshot/character changes instead of showing a previous character's leftovers.
- Gateways empty state centered, duplicate button removed.
Everything else
The context trims immediately after a narrow-window model swap (not at the next message); the request log never self-empties on a single oversized record; timed-out hub operations are cancelled rather than orphaned; the world-book keyword scan lowercases the scan text once instead of per entry; the field-audit methodology (including a headless-browser rendering artifact that mimics a dark-theme bug) is recorded in docs/AGENT-FIELD-NOTES.md.