WHAT'S NEW

WillDeep

Here are the changes in this WillDeep update that affect how you use the app. Internal test notes, version chores, and commit history stay out of the way.

Version1.391.0-rc1
Released2026-09-19

What's new

  • 冷启动耗时日志。 2026-09-19 有用户反馈「刚装完,新聊天区要 30 秒才显示」,事后连测三次冷启动都在 4 秒内,没能复现。当时的统一日志全被脱敏,也分不出是哪个阶段在等。现在每次启动都会记下几个关键时刻,从内核启动进程的那一刻起算(取的是 sysctl 里的进程启动时间,所以 dyld 加载和静态初始化这段也算在内):
  • didFinishLaunching:进入 applicationDidFinishLaunching;
  • agentWindowAppeared:Agent 窗口出现;
  • sessionCatalogLoaded:会话列表从磁盘加载完成,附带会话数;
  • usageDashboardRequested / usageDashboardReady:新聊天区的统计面板开始取数、取数完成。Ready 另外记录取数本身花了多久,和它在队列里排了多久分开看。
  • 每个时刻写两份:
  • 一份是 ~/Library/Application Support/WillDeep/AgentAudit/startup-timing.jsonl,一个时刻一行,超过 1MB 轮转一次;
  • 一份是统一日志(subsystem com.willdeep.app,category startup),数字是明文,不会被脱敏。超过 10 秒的时刻记为 error 级别。
  • 每个时刻在一个进程里只记一次。不记录任何会话内容。测试进程写到临时目录,不会写进用户的 AgentAudit。
  • 查看方法:/usr/bin/log show --last 1h --predicate 'subsystem == "com.willdeep.app" AND category == "startup"',或者直接 tail 上面那个 JSONL 文件。
  • AgentWorkerTimingLogWriter 的轮转文件名改成了初始化参数,默认值不变,启动日志和 worker-timing 共用这个写入器。
  • 新增测试 AppStartupTimingLogTests(5 条):同一时刻只记一次、秒数从进程启动算起、JSONL 格式、落盘位置的测试隔离、按指定文件名轮转。
Version1.390.0-rc5
Released2026-09-19

Fixes

  • 修掉全量测试里偶发的 saveThrottleCoalescesWrites 落盘数为 0。 起初怀疑是别的测试并行调用 AIPipe.resetQueueForTesting(),查下来不是:4 处调用都在同一个 .serialized 的 AIPipeQueueSafetyTests 里,App 在 XCTest 下也不初始化 AIPipe。真正共享状态的是别的进程。测试宿主的 bundle id 就是 com.willdeep.app,和本机正在运行的 WillDeep、其它 worktree 里并发跑的测试共用同一个偏好域。它们刷新、清空 AIPipeLogQueue 时会把测试刚落盘的 200 条覆盖掉。现场取证时这个键的值是 (),而测试收尾只会删除这个键,不会写入空数组。
  • AIPipe SDK 新增 +setPersistenceDefaultsForTesting::队列和配置的落盘目标可以替换,传 nil 就恢复 standardUserDefaults。切换在 SDK 的串行队列上进行。线上行为不变。
  • AIPipeQueueSafetyTests 的 4 个用例都改用一次性 suite(com.willdeep.tests.aipipe-queue.<UUID>),收尾时走 TestDefaultsHygiene.cleanup,前缀已登记。这样测试也不会再把测试配置和空队列写进用户真实 App 的偏好域。
  • 落盘断言改为读注入的 suite,另外断言标准域里没有这 200 条。
  • 已反向验证:后台循环执行 defaults write com.willdeep.app AIPipeLogQueue -array 施压,旧测试复现出同样的 (persistedCount → 0) == 200,新测试 10/10 通过。
Version1.387.0-rc1
Released2026-09-18

What's new

  • 读懂 willdeep-rs Runtime 的会话审批档位(契约对齐,配合 rs 0.77.0-rc1)。 rs 的 CLI/TUI 新增 /permissions,

审批档位扩成与本端一一对应的四档,外加 rs 独有的只读工作区策略。

  • WillDeepRuntimeSession 新增可选 approvalMode(协议字段 approval_mode);旧版 Runtime 不发该字段时为 nil。
  • 新增 WillDeepRuntimeApprovalMode 对照:strict ↔ .requestEveryTime、smart ↔ .smart、

workspace_write ↔ .workspaceAccess、full_access ↔ .fullAccess;read_only 与认不出的拼写返回 nil,不悄悄当成 smart。

  • 共享 fixture willdeep-runtime-public-api-v1.json 已从 rs 同步(会话示例带 approval_mode),往返映射有单测锁定。
  • 本版只读不写:本端仍不调用 rs 的 session.update_approval_mode。
Version1.383.0-rc2
Released2026-09-17

Fixes

  • 后台命令结束时不再丢输出末尾,完成通知里的结论行(RESULT=、PASS/FAIL)不会再缺。

进程退出时,结束回调此前只从管道再读一次就把作业标记为已结束。机器忙时读取回调会落后,管道里还积着

不止一次读得完的输出,末尾那段——恰好是发版、全量测试这类长任务的结论——既进不了内存尾巴、完整日志,

也进不了完成通知;正在进行的那次读取还可能在「已结束」之后才落地。全量测试里约一半轮次能复现(3002 行只剩 2558 行)。

  • 结束时把 stdout / stderr 读到 EOF 再标记结束;读取回调与最终排空串行,不会有数据晚于排空落地。
  • 逃逸的子孙进程仍占着写端时不会卡住:2 秒内没有新数据即收尾,排空总时长封顶 10 秒。
  • shell 后台作业与编码执行器作业共用同一实现(AgentPipeReader)。
Version1.377.0-rc1
Released2026-09-16

What's new

  • 插件页面问模型时可以附图片和视频(桥 2.6.0,能力 ai.images / ai.videos)。

window.willdeep.ai.complete 的 user 消息新增 imagePaths / videoPaths 两个字段,填本插件

生成目录里的本地路径。起因是短剧工坊的内容审核:出图、出片之后要让多模态模型真的看一眼,而

此前消息只收字符串,插件就算在设置里选了识图模型也递不进任何一张图,只能退而审生成时的提示词。

  • 路径钳制与参照图同一套判据:规范化后父目录必须恰好是本插件的生成目录,拒绝符号链接、

相对路径和目录。少了这道钳制,一个插件页就能让宿主读任意本地文件再发给第三方模型。

  • 图片在宿主侧限边重编码:长边限到 1568 像素、转 JPEG,不把生成目录里的大图原样 base64

进请求。单文件 25 MB 上限,解不出来的文件报 unreadableMedia,不发空附件。

  • 视频不依赖 provider 支持 video_url:宿主用 AVFoundation 按时间均匀抽 8 帧(取每段中点,

避开首帧黑场与末帧截断),当作一组有序图片发出,并在消息末尾注明「第几张到第几张是第几个视频、

分别在第几秒」。任何识图模型都能审。

  • 上限:一次请求最多 12 张图、2 个视频;附件只认 user 消息,挂在 system / assistant 上当场拒。
  • 模型不收图时当场拒(modelRejectsMedia),沿用主聊天的识图判定,不发出去等上游回一句

「unknown variant image_url」。

  • 插件审计日志的 plugin_ai 条目新增 attachedImages / attachedVideos 计数,只记数量不记路径。
  • 能力拆成两项而不是一个 ai.media:Web 宿主(willdeep-rs)没有视频解码器,只声明 ai.images;

一项含两种能力的话,插件在那一端会判错。

Version1.376.0-rc3
Released2026-09-16

Improvements

  • Worker 撞上下文窗口时换下一档继续,不再整轮重跑。 此前的链路是「撞窗 → 压缩 →

压完还超 → 抛错」,然后由调用方用大模型从头再跑一遍,前面几十轮全丢。现在在抛错

之前多一手:换下一档模型接着跑,已经跑完的轮次原样保留。

  • 客户反馈的现象是「前期慢、后期还行」,诊断日志里对应的是一条 614 秒、24.9 万

prompt token 的 run,标着 escalated from someim-32b——先用小模型赌一把,撞窗后

整轮重来。

  • 刻意不引入 token 阈值:触发条件是真实撞窗,不是预设数字。这个仓库的模型上下文

窗口本身是按模型名匹配猜的(实测 9 个上报值里 6 个偏小),拿猜来的分母做阈值并不

可靠;而且小活永远撞不到窗,也就永远不会误升档、不涨成本。

  • 另外,「派发前按 prompt 大小预判」这条路拦不住上述那条 run:promptTokens 是

多轮累计的,它首轮并不大,是跑着跑着涨上去的。

  • 两条限制:一次 run 只换一次档(下一档还撑不住说明任务本身超出这条链);下一档窗口

不比当前大就不换(换了也过不了同一道校验)。

What's new

  • timing 日志新增换档观测:midRunEscalationPromptTokens(换档那一刻的累计 prompt)

与 midRunEscalatedFromModel(换档前的模型),独立字段而非塞进自由文本的 note

(那里已被 escalated from X 占用,混在一起无法统计)。

  • 目的是回答一个目前答不出来的问题:小模型究竟在多少 token 上撑不住。此前日志里

只看得到升级之后的最终数字,与失败点无关,阈值也就无从定起。攒够真实样本再谈

要不要加预判。

Version1.375.0-rc3
Released2026-09-14

Fixes

  • 从源码目录装插件时,前端依赖和嵌套的版本库状态会被算进内容指纹、并原样搬进共享安装目录。 此前只有顶层的 .git 被排除,ui/node_modules 那 5000 个依赖文件既进指纹(每次列插件都要哈希一遍),又被复制进 ~/.willdeep/plugins/——而那个目录是与 willdeep-rs 共用的,98 MB 装机产物两端都得背着。git worktree 在包里留下的嵌套 .git 文件同理。
  • 改成按路径段排除 .git / node_modules / .cache,不看深度也不看它是目录还是文件;指纹计算与安装剪枝共用同一份名单(AgentPluginPackageLoader.digestExcludedPathComponents)。两侧必须认同一份名单,否则同一个包在「装之前」和「装之后」会算出两个指纹,安装会报 proposalChanged——willdeep-rs 就是在这一点上翻车的(那边复制按名字剪任意类型、指纹只跳目录,于是短剧工坊整个包装不上),这次一并对齐避免重演。
  • 两端现在对同一个包算出同一个 digest。已装插件不受影响——实测共享目录里的包都不含这三类路径,指纹不变,不会出现「包内容已变」误报。
  • networkDomains 写错格式不再静默失效。 此前一条都不校验,而代发请求做的是精确匹配或 *. 后缀匹配:https://example.com、example.com:443 这类写法装得上却永远匹配不中任何主机,插件作者拿到的是一个解释不了的 networkHostNotDeclared,而他明明在清单里写了那个域名。现在按共享 schema 的 pattern 在装的时候就拒,并说明正确写法。
  • 这一条刻意不走「认不出就降级」:那份名单是 net.fetch 唯一的门,不是可选展示项。
  • 新增契约测试直接拿 schema 里的 pattern 与宿主判定逐例对账,防止两端各自漂移。

Improvements

  • 插件系统设计文档补上三条跨宿主契约:executeCommand resolve 的是 JSON 字符串而不是对象(另一端回对象时页面会报 "[object Object]" is not valid JSON)、包内容排除名单、以及「弹原生文件框不是跨宿主可移植的动作」。同时更新 rs 的能力现状:它已补齐到桥 2.5.0,两端差集只剩 ai.reasoning。
Version1.372.0-rc11
Released2026-09-13

Fixes

  • 智能审核老让人点确认,问题不在裁判判得严,在于很多命令压根没送到裁判面前。 翻了 8 月以来的审计日志:裁判真上场的 491 次里放行了 82.5%,掉线 11.6%(其中 41 次是网关 502)。而人工审批的头号理由是「命令引用了敏感路径或类凭据值」,320 次排第一。这个标记会置位打字确认,打字确认又一票否决送裁判的资格,所以它一旦误判,命令就直接落到人手上,裁判连看都看不到。逐条回放后,里面混着两类纯粹的子串误伤:
  • .env 是裸子串匹配,kubectl 的 containers[0].envFrom 一头撞上去。 kubectl get deployment -o jsonpath='{...envFrom}' 这种纯读查询被判成读密钥,.env[?(@.name=="X")] 的 jsonpath 同理。现在按 / 切段判末段,只有 .env 本身和 .env.local 这样的才算,.envFrom、.environment 不再命中。
  • ssh -i ~/.ssh/id_rsa 里的私钥被当成在读密钥。 那把钥匙是用来登录的,正是它存在的意义。于是每条上服务器的运维命令都要人工点一次。现在只豁免 ssh/scp/sftp 的 -i 取值本身;ssh host 'cat ~/.ssh/id_rsa'、scp host:~/.ssh/id_rsa . 这些真去读密钥的形态照常拦。rsync -i 是 --itemize-changes,故意不在豁免名单里。
  • 恶意命令黑名单里那条「凭据外泄」的例外保护,实际上等于没有。 它本来应该兜住误报——判定命令有没有上传语义,没有就放行。但 uploadMarkers 里放着一个 "@",意味着任何 user@host 的 ssh/scp 都满足「上传语义」;post、put 也是裸子串,output、input、compose、postgres 全都命中。等于这个例外对真实命令永远为真。现在上传标记必须是独立的 flag 或 @file 形态。
  • 同一个函数里的出口命令也是裸子串匹配,journalctl -u niuwo-mail-service 里的 mail 被当成发信出口。日志里有实锤:一条纯读的远程排障 ssh … journalctl … 被判成私钥外泄,直接 deny。现在出口命令必须真的是某一段的命令头。
  • 三处改动都不放宽真实防护:cat ~/.ssh/id_rsa | curl --data @-、tar czf - ~/.aws/credentials | nc 这些照样拦,新加的测试把两个方向都锁住了。
Version1.372.0-rc10
Released2026-09-13

Fixes

  • submodule 指针在 rc9 里被悄悄回退了。 rc9 那次提交用 git add -A,而提交所在的工作树里 submodule 还停在加 CI 之前的旧提交上,于是「提交所有改动」把 11 个指针一并写回旧值,把 rc8 那次指针提升抵消掉了。App 内容不受影响(.github 不在任何内置插件的打包清单里),但仓库记录的插件版本对不上各插件仓库的 main。
  • 这正是拆 submodule 时写在 rc8 条目里的那条代价——改插件是两次提交——第一次踩中的就是我自己。教训:在 submodule 仓库里别用 git add -A,它会把子模块指针当成普通改动一起提交,而指针回退在 diff 里只是一行 40 位哈希,看不出严重性。
Version1.372.0-rc6
Released2026-09-12

Fixes

  • 首尾帧那个「生成 N 张候选」按钮灰掉时,一个字都不解释。 用户看到的是「生成过一次就再也点不动了」,于是以为不能重新生成。实际上重新生成一直是允许的,新候选追加在后面,不覆盖已选定的那张;按钮的禁用条件里根本没有「已有候选」这一项。真正拦住它的是三件事之一:分镜草稿还没采用、另一项生成还在跑、没选中分镜。
  • 讽刺的是解释一直存在,只是挂在点击处理函数里。按钮被 disabled 之后点击事件永远不触发,那句「请先采用当前分镜草稿」就永远没机会出现。
  • 现在把原因直接写在按钮下面。不加新控件,只补文案。
  • 单元测试不再往用户真实的 worker 计时台账里灌桩运行。 AgentWorkerTimingLog.defaultURL() 在测试进程里也指向用户的 AgentAudit/worker-timing.jsonl。9 天积了 4060 条,其中 2755 条 deep 是桩(rounds=1、工具调用 0、token 1,模型名里直接写着 test),照这份数据算工种分布会得出「deep 占 68%、运维占 1%」,而真实开销 98% 在 32 次 ops_runner 上。判据与 AgentDispatchCoordinator.processDefaults 等三处保持同一套,测试进程按 pid 落到临时目录。
  • defaultURL() 另开了一个显式的 underTests 参数:改道之后测试进程里再也走不到生产那条分支,「日志落在 AgentAudit 旁边」这条老断言会变成永远验不到的死代码。传 false 让它继续守着真实落点,而不是把断言改成迁就改道。
Back to WillDeep