Skip to content

决策日志 ​

翻译说明: 本页是与 英文源规格 一一对应的机器辅助翻译。代码、协议字段和标识符保持原文;如翻译与英文源事实有歧义,以英文版本为准。

基线增量:0.3.0 → 0.4.16 日期:2026-08-05 状态:已接受实施

该日志将以前未解决的问题冻结为具体的决策。

A. 高优先级架构决策 ​

身份证号主题决定基本原理
D001Electron ↔ Rust 运输Rust sidecar + stdio JSON-RPC (NDJSON)简单隔离,可调试,后期可更换
D002SQLite 所有权Rust host-core 独家拥有 SQLite单写入者,权限边界更清晰
D003默认模式Agent产品是座席桌面,不是纯聊天
D004前受限个人资料(由 D189 取代) 以前的聊天只读配置文件已删除;持久的聊天值迁移到 Plan。该产品现在有一个 Agent,具有规划状态和单独的批准边界
D005权限超时120秒→拒绝失败关闭,不会永远挂起
D006allow-session 范围由 toolName 提供简单的用户体验;工作区沙箱仍然强制路径安全
D007~/.pi 兼容性MVP 中没有自动导入在 ~/.pi-desktop 中保持配置所有权干净
D008Node 运行时打包开发人员使用系统 Node;版本通过 ELECTRON_RUN_AS_NODE=1 在 Electron 二进制文件上运行捆绑的 sidecar(未提供单独的 Node)疏通M1–M4;在 M5 处解决,请参阅 03-runtime/07-process-model §6
D009插件运行时隔离目标=单独的进程; M4 可以使用主机管理的沙盒运行时在不削弱 API 网关的情况下实用地发布插件基础
D010首个发布平台仅限 macOS arm64重点验收及包装
D373远程 Agent 控制的 Host 边界(由 D374 与 D375 修订) 远程控制(MVP 之后)经由一个逻辑 Agent Host 运行,它拥有 Sessions、Turns、事件游标、审批、附件、工作区策略与崩溃恢复;生产 Gateway 拥有身份、路由、限流、吊销与审计,Agent Host 向外连接。现有 Electron IPC、host.proxy 与 host-core stdio 边界永不暴露。远程表面需要自己的边界,而不是通往本地 IPC 或 host-core stdio 契约的隧道(ADR 0205,E2E-221 至 E2E-230)
D374远程 Agent 控制 v1 目标修订修订 D373 / ADR 0205:RACP-WS 是 v1 唯一规范绑定(RACP-HTTP 为浏览器 profile,RACP-GRPC 保留);packages/shared 中的 typebox 是唯一契约来源;无头的 packages/agent-host 模块是首个交付物,桌面 IPC、本地 MCP 与 RACP 共同调用;游标为 { epoch, sequence } 且增量为临时数据;回合队列移入 Host;host-core 暴露 permissions.pending;远程审批携带完整本地决策词汇并默认 ask 上限;浏览器客户端使用 cookie profile;首个部署为单租户。对照已交付桌面审查 D373 草案发现远程审批词汇比本地契约窄、待处理请求被当作连接状态、逐 token 增量会耗尽重放窗口、绑定数量超过 v1 承载能力(ADR 0205)
D447无头运行时边界修订 ADR 0205 里程碑 R2 的前置条件(ADR 0284):Agent Host 模块之下与 Electron 无关的运行时层放入 packages/host-runtime —— host-core 与 sidecar 的 stdio 传输、重启监督器(进程模型 §4 的策略)、持久回合生命周期(RuntimeService 作为模块的 RuntimePort:session.beginTurn → 用户行 → prompt → session.endTurn、中止锁、过期终态事件守卫、单飞终结)、带 D299 检查点的转录持久化、基于 host-core 自身注册表的无头启动解析器、已批准 Plan/Goal 的派发(D189),以及 SessionPort / QueueStore / permissions.pending 的 host-core 适配器。Electron main 只保留薄适配层(二进制与资源位置、ELECTRON_RUN_AS_NODE、脱敏 stderr、schema/glibc 诊断、渲染层状态推送)以及自己的本地 prompt 路径。TurnStartRequest 新增可选 userMessageId。不改 IPC、sidecar、host-core、插件 SDK、默认值或持久化数据。pi-host 必须在没有 Electron 的情况下运行与桌面相同的生命周期不变量;第二份中止锁、过期终态事件与持久落库后释放队列的实现会与桌面已修好的那份漂移
D448RACP-WS 传输与设备配对修订 ADR 0205 里程碑 R2(ADR 0285):packages/racp 同时承载规范绑定 RACP-WS 的两端。RacpServer 按目录的角色规则把每个操作分发到 Agent Host 模块与 Host 实现的 RacpHostOperations 接口;RacpClient 关联请求、应答服务端发起的请求、按会话记录最后的持久游标、以有界退避重连,并让断开连接上未完成的调用以 HOST_DISCONNECTED 失败而不是重发。认证采用 header profile:Host 签发的 pdt1. 设备令牌与一次性、有时限的 ppt1. 配对令牌(散列存储、常量时间比较、绝不出现在 URL 中);connection/pair 铸造 owner 设备。connection/pair、project/register、project/browse、server.hostId、events/closed 通知,以及错误码 PAIRING_FAILED、PAIRING_TOKEN_EXPIRED、CAPABILITY_UNAVAILABLE、REMOTE_PATH_NOT_FOUND、REMOTE_PATH_FORBIDDEN、HOST_DISCONNECTED、HOST_BOOTSTRAP_FAILED、HOST_VERSION_MISMATCH、REMOTE_AUTH_FAILED、REMOTE_CONNECTION_FAILED、REMOTE_FORWARD_FAILED 进入契约(规格 §14a)。绑定拒绝非回环地址;附件与工具中继宣告为不可用。SSH 隧道拓扑需要 pi-host 能绑定的服务端和桌面能运行的客户端,以及安全规格描述的配对;不需要机器边界的一致性行为成为包内测试
D451审阅面板只在用户主动操作时打开工具结果永不打开、激活或改变工作面板。移除 tool_end 中的 artifact 门控与 shouldOpenReviewArtifact;Review 仅从 + 新建启动器的 Review 行,或视口固定开关 / Cmd/Ctrl + J 显示的会话保留上下文进入。成功的 workspace Write/Edit 仍会在 transcript 中记录消息级内联审阅卡片。Agent 编辑静默弹出侧边栏会与用户正在阅读的内容和布局争夺注意力,且 transcript 卡片已承载变更证据。见 ADR 0043、04-ux/01-ui-ia.md、04-ux/08-component-spec.md §5、04-ux/09-interaction-patterns.md、E2E-057。
D452计划与目标工件在内置文件视图中打开审批卡的工件打开器把不可变的 .pi/plan/*.md 或 .pi/goal/*.md 路径交给内置 pi.file-manager 视图(该视图可启动时),否则交给宿主机文件标签——与对话文件引用已有的偏好与回退一致(ADR 0241)。工件仍会在其来源会话中创建或激活标签,因此审批揭示本身不变,只有承载界面改变。仅渲染进程;不改协议、宿主、存储、工件内容或权限。见 D189、ADR 0043、04-ux/08-component-spec.md §5.4 与 §10A.2、04-ux/09-interaction-patterns.md §5A、E2E-106。审批打开器原先使用宿主机文件标签,而对话提到的其它项目文件都已进入用户自己的文件视图,于是计划或目标落在了用户唯一不浏览项目文件的界面上。
D459恢复可拖拽侧边栏宽度,过窄时收起修订 D408 / ADR 0238 并恢复 ADR 0141(ADR 0290):展开侧边栏重新成为渲染层拥有的 240px..520px 列(默认 275px),右缘手柄在指针按下时预览、释放时持久,并支持左右方向键(16px)、Home 与 End。指针宽度低于 160px 时以用户操作收起,不覆盖首选展开宽度;键盘调整永不收起。实时上限为扣除 MainChat 450px 下限后的三栏余量。仅渲染进程;沿用 pi.desktop.sidebarWidth 偏好。见 E2E-168。长标签需要可回收宽度;ADR 0238 固定 275px 会挡住这一点,而三栏实时预算本就可以限制用户选定的宽度。该决策曾被误记为 D451(已由「审阅面板只在用户主动操作时打开」占用);按 issue #620 改号。
D457已签名 macOS DMG 改为双图标安装修订 D406 / ADR 0232 / ADR 0204:正式与本地 DMG 只包含 PI-Desktop.app 和 Applications 链接,使用 720×440 品牌背景。不再放入 If app won't open, read this.txt 或 command 助手。macOS ZIP 仍附带打开说明和 PI-Desktop-macOS-open.command,供可信未签名工件使用。见 ADR 0296 与 E2E-196b。标签 DMG 已签名公证(D450);安装盘上的未签名打开说明读起来像报错。
D450签名的 macOS GitHub Release修订 D078 / ADR 0022:GitHub tag 发布使用身份 Developer ID Application: XingYu Liu (DUV63RKYTW) / 团队 DUV63RKYTW,通过 Actions 密钥(CSC_LINK、CSC_KEY_PASSWORD、APPLE_ID、APPLE_APP_SPECIFIC_PASSWORD、APPLE_TEAM_ID)对 macOS DMG/ZIP 做 Developer ID 签名、notarytool 公证、装订和 Gatekeeper 校验;缺少密钥则失败。无证书的本地未签名打包仍可用。workflow_dispatch 仅可把 sign_macos: false 用于未签名调试产物。打包的 macOS 走应用内 electron-updater(ZIP + 合并后的 latest-mac.yml);Linux deb/rpm 与 Windows 便携版 ZIP 仍为通知并打开发布页。禁止 afterPack/afterSign adhoc 签名(ADR 0278)。正式 DMG 应无需 Gatekeeper 警告即可打开,已签名 macOS 安装可下载并重启到新 tag。见 ADR 0289、E2E-196c、E2E-067A。

B. 辅助实现默认值 ​

身份证号主题决定
D011TS 架构验证打字机
D012国际化图书馆i18next + 反应-i18next
D013M3 中的 Bash 执行风格仅非交互式(尚无 PTY)
D014命令面板快捷键Cmd/Ctrl + Shift + P
D015插件工具公开名称强制前缀 plugin_<pluginIdSafe>_<toolName>
D016卸载插件数据默认删除,可选稍后保留数据
D017启用 → 加载失败自动回退到禁用
D018设置中的插件秘密MVP 中不允许
D019插件会话摘要访问默认拒绝 (由 D336 修订:仅在授予 session.read 时,且仅限进行中的工具会话)
D020自动更新后MVP
D021首次运行新手引导内联清单(不是模式向导)
D022本地遥测仅在 MVP 中记录本地日志(无远程遥测)

C. 提供商和模型覆盖决策 (0.3.4) ​

身份证号主题决定基本原理
D023提供商覆盖目标全球市场覆盖(不是一个很小的固定供应商列表)全球化+真实的编码工作流程
D024覆盖策略pi-ai 原生提供商 + 一流的 OpenAI 兼容 + 自定义提供商无需重写每个 SDK 即可实现最大覆盖范围
D308智谱 / Z.AI 命名端点预设修订 D024 / ADR 0116:添加服务的「服务」下拉框提供四套 OpenAI 兼容智谱端点——国内/国际标准 API 以及 GLM Coding Plan——锁定已发布 URL,并写入 models.dev vendorKey(zhipuai、zhipuai-coding-plan、zai、zai-coding-plan)。不新增 apiStyle 或线路适配器。这些 URL 上的 Completions 请求使用 thinkingFormat: "zai" 与 zaiToolStream: true。自定义端点仍可用。用户此前必须手填 BigModel / Z.AI 地址,且 vendorKey: "custom" 可能把 API 与 Coding Plan 目录配错。命名预设把覆盖范围留在现有 OpenAI 兼容路径上。见 ADR 0155 与 E2E-005D。
D389聚合端点上回放 DeepSeek 的 reasoning_content修订 D024:DeepSeek 系模型的 Completions 请求在行 vendorKey、Base URL、模型 ID 或目录 family 能识别为 DeepSeek 时设置 requiresReasoningContentOnAssistantMessages: true。该匹配不改 thinkingFormat。pi-ai 仍对官方 deepseek.com URL 做自动检测。DeepSeek 思考模式在任一条回放的 assistant 消息缺少 reasoning_content 时会 400。PI-Desktop 把 UUID 存成 model.provider,硅基流动、火山方舟和自定义网关永远匹配不上 pi-ai 的 provider === "deepseek" 判断。见 E2E-005E。
D424压缩后保留 DeepSeek 推理修订 D389 / ADR 0136:历史重建时恢复 thinkingSignature;把最近思考写入检查点 details.retainedReasoning 并在摘要后回放;非官方 DeepSeek Completions 行设置 requiresNonEmptyReasoningReplay,使 pi-ai 补丁使用文档化占位符而非 ""。官方 deepseek.com 仍用空串回填。OpenCode / 第三方 DeepSeek V4.1 Flash 在压缩后回传空推理字段会 400(#296)。
D309添加服务的常见路径是服务 + 密钥修订 D308 / ADR 0116 / ADR 0155:新建添加服务对话框先只显示服务。命名端点(智谱 / Z.AI、OpenCode Go)随后显示服务 + API 密钥和主机摘要。自定义端点随后显示名称、Base URL 和 API 密钥。名称(命名行)与接口格式(自定义行)放在高级里。OpenCode Go 是服务选项。没有步进器或厂商卡片网格。把名称、Base URL 和接口格式与服务叠在一起,让已知服务看起来像通用网关。常见路径应是选服务并粘贴密钥。见 ADR 0156 与 E2E-005 / E2E-005B / E2E-005D。
D310自定义接口格式与密钥并排;服务列出 models.dev 主流厂商修订 D309:自定义端点在常见路径上并排显示 API 密钥与接口格式。服务下拉框按国际 / 国内列出 models.dev 的主流厂商端点。命名服务的显示名称仍在高级里。自定义网关需要在密钥旁选择格式;命名厂商应覆盖常用国内外 API。见 E2E-005。
D311服务选择可搜索;发现等待稳定选择修订 D310:添加服务的「服务」控件改为可搜索锚定菜单(与默认模型选择器相同),不再使用原生下拉框。过滤在本地按显示名、vendor key、别名和主机进行。命名服务在填入 API 密钥前不发起模型发现(编辑仍复用已存密钥)。自定义端点仍可在无密钥时探测有效 URL。发现 hook 在防抖结束前不进入 loading,更换端点会立刻清空上一份列表。带分组的原生下拉框在对话框里很卡,切换服务或输入密钥还会每次都重新请求。搜索方式与模型列表过滤一致。见 E2E-005 / E2E-005D。
D312服务按厂商平铺;加入小米修订 D310 / D311:命名服务不再分国际 / 国内,改为单一厂商列表。加入小米(vendorKey: "xiaomi",https://api.xiaomimimo.com/v1,Chat Completions)。自定义端点仍在最前。搜索仍按显示名、vendor key、别名和主机过滤。用户按厂商名搜索,区域标题没有帮助。小米 MiMo 是 models.dev 上的一流厂商。见 E2E-005。
D025模型许可名单无封闭产品许可名单模型流失;高级用户需要自由格式的 ID
D026目录来源捆绑快照 + discovery/refresh + 用户定义离线工作并保持最新状态
D027默认身份模型选择为(providerId, modelId)相同的模型 ID 可以存在于多个网关上
D028秘密通过secretRef进行操作系统安全存储(或受控回退);从未在提供商 JSON 中出现Rust 主机所有权的安全边界
D029本地模型通过兼容 OpenAI 的本地网关支持Ollama/LM Studio/vLLM 无特殊情况架构
D030连接测试在信任提供商运行之前的一流主机方法尽早失败,可操作的设置用户体验
D031秘密后端操作系统安全存储主要+加密文件后备在 macOS 首次发布上稳健
D032工作区忽略安全拒绝列表 + 默认值 + .pi-desktopignoreSafe/predictable 工具 FS 行为
D033工具结果限制256KB/4000 行默认带有显式截断标记 (由 D194、D306 修订)保护上下文和 UI
D306搜索/读取截断表示切断,而不是窗口修订 D033 / D194:Read/Glob/Grep 的 truncated 仅在主机切断了调用方请求的内容时为真(字节预算、单行剪辑、Grep/Glob 命中上限)。一次返回了请求窗口或默认窗口的 Read,即使文件更长也是该窗口的完整结果;totalLines/offset/lineCount 和下一个偏移的 notice 描述剩余部分。默认 Read 窗口为 2000 行(最大 4000)。BUDGET_SEARCH 为 128KB / 4000 行。单行剪辑为 16,384 个字符。UI 截断芯片跟随该标志。48KB / 500 行的窗口把几乎每个源文件和规格表都标成截断,包括已经填满的分页读取,既掩盖了真正的切断,又迫使智能体重新搜索已经读过的内容。
D307修订载荷跟随实时分支修订 D109:重新生成分支在归档后仍会继续生长(之后的提示、从未到达 agent_end 的错误回合),因此每个会丢弃实时分支的操作都先把它写回所属变体。session.activateRevision 在切换前从持久转录本重新归档该系列的实时分支,并从那里取前缀;重新生成路径通过 session.saveRevision { revisionIndex } 刷新被标记的变体;session.saveActiveRevision 对已归档索引做刷新而不是跳过。被刷新的是实时根消息 activeRevision 标记所指的变体;已标记但尚无索引行的变体作为新变体单独存储,绝不覆盖之前的变体。切换同时带上每条幸存消息所属的 turn_id,并保留锚点仍存在的检查点。归档只写一次、永久复用:从一个自 agent_end 归档后又生长过的分支切走再切回,会恢复陈旧副本,并悄悄地从转录本及其 JSONL 中删掉之后的每一回合。
D390主机拥有的重新生成截断修订 D199 / D258 / D307:agent/prompt 在主机锁内通过 session.truncateFrom 截断(先按身份定位、中止残留 running 回合、归档被丢弃的尾部、重写前缀)。保留的 transcript 不会经过 JSON-RPC。超过 64 MiB 的 NDJSON 请求行返回 LIMIT_EXCEEDED,不会结束 stdin 读取器。协议版本仍为 11。见 ADR 0216 与 E2E-246。在多千条消息的会话上重试时,session.replaceMessages 曾在 130 秒时超时,并可能在 64 MiB stdin 上限处失败(issue #211)。
D391主机 stdout 发送端不得比 serve 更长寿修订 D390 / ADR 0216:Windows Alt+Space 钩子只保留 stdout 发送端的弱克隆。stdin EOF 后,丢弃 serve 的发送端会关闭写入器通道,host-core 随之退出。Electron 在写入前拒绝超过 64 MiB 的 NDJSON 请求,并返回 LIMIT_EXCEEDED。主机侧超大回复从截断前缀中窥取 JSON-RPC id,避免客户端等待 130 秒。stdin 结束后 serve 最多等待 stdout 写入器 5 秒。协议版本仍为 11。见 ADR 0217 与 E2E-247。Windows v0.14.6 的 64 MiB stdin 上限会结束读取器,但强引用的键盘发送端让写线程继续运行,因此 host-core 变成僵尸进程,Electron 报告 host RPC timeout: session.replaceMessages(issue #211)。
D392Composer 与传输使用生效的图像输入覆盖修订 D243 / ADR 0101:图像能力先取已发布模型记录;精确 binding 的 supportsImages 为 true 或 false 时覆盖该记录,缺失或 null 则跟随已发布值。Composer 徽章、附件状态和主进程图像传输使用同一个生效结果。没有显式覆盖时,未知/自定义模型仍保持保守行为。见 ADR 0218 与 E2E-163。已配置的端点可能已经通过 binding 覆盖传输图像,但 Composer 行仍隐藏视觉徽章;或者端点已禁用图像输入,行却仍显示已发布徽章。
D393Composer 中用户调用的 Skills修订 D123 / D174 / ADR 0024 / ADR 0039:激活的内置、插件和用户 Skills 出现在 composer slash 菜单末尾独立的 Skills 分组中。选择后插入其精确 id;Electron main 在发送时重新验证当前项目范围,并要求模型调用本地 Skill 工具,同时保留按需加载正文和现有权限。现有命令名优先解决冲突;未激活的 Skills 保持为字面 slash 文本。见 ADR 0219 和 E2E-088b。D174 的模型调用目录仍是 Skill 正文加载和安全契约,但拒绝面向用户的 slash 条目使已经知道工作流的用户难以发现活跃 Skills。
D394Windows 工作面板 chrome 保持单一资源操作组修订 D154 / D357 / ADR 0195:打开的工作面板标题栏只保留一个紧凑资源切换器;资源关闭由现有可键盘操作的上下文菜单行负责,视口固定开关仍是唯一的面板折叠控件,子代理详情使用返回箭头。Windows/Linux 原生控件仍固定在窗口边缘。仅渲染器变更;不改面板状态、窗口几何、IPC、协议或存储。见 ADR 0220 与 E2E-067。标题栏资源 X、视口固定开关和 Windows 原生关闭按钮在窄面板中看起来像重复的关闭操作,并且过于拥挤。
D396渲染器与插件面板滚动条统一为紧凑规则修订 D300:渲染器中的每个滚动容器统一使用 6px、无轨道、静止时透明的滚动条,以及相同的悬停、focus-within、滚动显示和拖动状态。移除侧边栏专用的宽度和透明度覆盖。插件面板 preload 给停靠和独立插件文档(包括内置 Files 视图)注入同一规则和 300ms 的滚动显示标记。Browser 内部加载的外部网页仍由网页自己管理。仅表现层变更;不改变协议、存储、主机运行时或外部网页行为。见 E2E-157。Windows 的经典滚动条让右侧工作面板的 Files 视图明显比对话区更粗,而侧边栏还保留了第二套滚动条样式。
D-LOCAL-message-quotes消息引用与渲染器拥有的侧边聊天(已由 ADR 0268 于 2026-09-16 退役) 修订 D209 / D301:每条用户消息和每个助手回合的悬停操作行新增 Quote 操作,与 Copy、Edit、Delete、Fork、Retry 并列。它把以 > 开头的 Markdown 引用块加一行 chat.quoteSource 归属写入当前会话的 composer 草稿并聚焦输入框;选区位于被点击消息行内时使用该选区,否则使用消息自身文本;摘录上限 2000 字符并带省略号;绝不发送,也不新增任何芯片类型。Open side chat 通过 session.fork 以锚点消息分叉且不激活子会话,把子会话注册为父会话的渲染器侧边聊天,并在现有停靠面板中打开一个 sidechat:<childSessionId> 标签,用同一条事件流经后台转录 reducer 实时流式渲染,提供 Add to main chat、Open as a conversation、紧凑 Send/Stop 输入和现有权限卡。关闭标签或以会话方式打开子会话会移除注册;持久子会话仍是普通会话。不改协议、schema、IPC 或权限。见 ADR message-quotes-and-side-chats 与 E2E-CHAT-quote-prefill 至 E2E-CHAT-side-chat-close。用户需要复用之前某条消息或答案的原文,并在不替换当前可见主对话的前提下追问一个旁支问题,而现有 fork 路径总会激活子会话。 (由 D-LOCAL-selection-overlay 修订:摘录改为从渲染后的 DOM 还原为 Markdown,且消息行内的选区可在跟随选区的浮层中选择「添加到对话」「在侧边聊天中提问」或复制。)
D-LOCAL-response-annotations响应批注作为提示词附件(已由 ADR 0268 于 2026-09-16 退役) 对助手回合修订 D-LOCAL-message-quotes 决定 3:在响应中选中文字并选择「添加到对话」时,把编号批注附加到该回合,而不是改写 composer 草稿——回答正文不被装饰(只有模型写出的引用才渲染为编号引用),composer 显示一个批注附件芯片(提示中列出各条摘录,并提供一键清除),下一条提示在用户自己的文字之前携带 # Response annotations: + 指令 + <response-annotations> JSON([{text, annotation, source:{messageId}}])+ ## My request:,并消费这些批注。可见草稿、乐观行、标题与编辑种子都不含批注内容,已存提示在展示时会还原为用户请求。(2026-09-12 修订:「添加到对话」会打开一个紧凑的评论编辑器,保存时把用户可选的评论写入批注的 annotation;再次批注同一摘录会重新打开该批注的编辑器,而不再是静默无操作;composer 附件会列出每条批注,并提供逐条编辑与移除,清除全部仍然保留。) 引用用户消息以及侧边聊天的 Add to main chat 仍沿用 D-LOCAL-message-quotes 的草稿引用;不改协议、存储 schema、IPC 或权限。见 ADR response-annotations 与 E2E-CHAT-annotation-attachments、E2E-CHAT-annotation-session-state。草稿引用会把模型的话混进用户自己的提示文本,且多处引用没有模型可寻址的编号;参考实现改为把摘录作为带编号的提示数据附加。
D398上下文用量显示偏好修订 D347 / ADR 0184:上下文用量检查器的引导数值——触发器圆弧(strokeDashoffset)、百分比、令牌标签、弹层标题、tooltip 与 aria-label——可通过 AppSettings.contextUsageDisplay("remaining" 或 "used")配置。缺省及非法值回退为 "remaining"。当设为 "used" 时,圆环按 usedRatio 填充,文字展示已用容量对。警告与临界颜色阈值(剩余 ≤ 25 % / ≤ 10 %)仍按剩余容量判定,不随显示模式变化。设置 → 全局 AI → 默认项新增分段控件(剩余 / 已用),位于链接打开目标之后、回车发送之前。仅渲染器改动;无协议、存储、宿主或迁移变更。见 ADR 0222 与 E2E-250。仅显示剩余时在低占用下信号微弱,且不符合习惯用「已用多少」思考的用户。颜色必须始终按剩余判定,否则已用 90 % 仍为绿色会产生误导。
D399项目组手动排序(由 D402 修订) 修订 D093:保留的项目组显示悬停/聚焦可见的手柄。原生拖放以及在手柄上按 ArrowUp/ArrowDown 会按规范化路径写入连续的 order 值,并在渲染器本地的侧边栏偏好中将 projectSort 设为 manual。归档和置顶优先级仍高于手动顺序;项目激活、主机工作区身份、会话顺序和磁盘目录保持不变。多个活动仓库需要不受最近活动或名称影响的稳定工作顺序,同时保持现有按路径索引且由主机拥有的项目模型不变。
D400从有效会话上下文恢复延迟工具修订 D185 / ADR 0048:每个新提示和模式切换前,清除内存中的延迟激活集,然后从有效 buildSessionContext 投影中的成功 ToolSearch 结果(addedToolNames)和成功的延迟工具结果恢复名称。仅保留当前延迟目录和模式仍允许的名称;忽略错误、已中断或缺少结果的占位行,以及助手/用户文本。不改变主机权限或工作区边界。清除激活却保留成功的转录标记,会让模型看到能力证据而下一次 provider schema 中没有对应工具。只从有效的成功证据恢复,既保持 provider 请求一致,也不解析文本或复活过时、被禁止的工具。见 ADR 0225 与 E2E-008a。
D402长按项目标题重排(由 D403 修订) 修订 D399 / D093 / ADR 0227:保留的项目组不再显示重排手柄。在项目标题上静止按住 400ms 后开始指针重排;该延迟前移动超过 8px 会取消按住,因此单击仍会选中并折叠/展开。聚焦标题后按 ArrowUp/ArrowDown 是键盘路径;Escape 取消。持久化、置顶/归档分桶、主机工作区身份、会话顺序和磁盘目录保持不变。见 ADR 0228 与 E2E-253。专用手柄占用一列,并把重排做成标题旁边的第二个控件,而标题本身已经负责选中和折叠。
D403按住标题移动即可重排修订 D402 / D093 / ADR 0228:鼠标和触控笔按住项目标题并移动 8px 开始重排;没有足够移动的单击仍会选中并折叠/展开。触摸不会开始重排。强调色插入线标出前/后放置位置。ArrowUp/ArrowDown 与 Escape 不变。见 ADR 0229 与 E2E-253。静止按住 400ms 是移动端长按模式,比 ChatGPT 一类桌面侧边栏列表更慢。
D404Skill 随 Agent 核心工具集下发修订 D174 / D185 / ADR 0048 / ADR 0219:Skill 加入 Agent 模式核心工具集,因此只要技能目录非空,第一个 provider 请求就带有它的 schema。它从延迟目录中移除,不再出现在 # On-demand tools;其他按需能力与 ToolSearch 不变,Plan 与 Goal 仍然完全不提供该工具。不改协议、存储、权限或技能正文。见 ADR 0230 与 E2E-254。用户输入的 /skill-id 与 # Skills 段落都要求模型调用 Skill,而 schema 中不存在的工具根本无法被调用;延迟注册让任何技能正文加载前都多一次发现往返(issue #204)。
D405顿号打开斜杠菜单修订 D123 / D139 / ADR 0024:当输入框为空时,第 1 个字符提交的「、」(U+3001)会在触发检测前改写为 /,中文输入法因此无需切换输入方式即可打开普通斜杠菜单。只改写该位置;其他位置的「、」仍是普通标点,@ 文件菜单不受影响。仅共享语法与渲染器改动;不改 IPC、存储或补全数据源。见 ADR 0231 与 E2E-255。要唤出 /new、/compact、模式别名或某个 Skill,中文输入法用户必须在书写中途切到 ASCII 输入再切回(issue #65)。
D406macOS DMG 只保留打开说明(由 D457 修订) 修订 D371 / ADR 0204:macOS DMG 以 Finder 名称 If app won't open, read this.txt 展示打开说明,不再包含或暴露可执行的 PI-Desktop-macOS-open.command。macOS ZIP 安装包保留说明和助手。说明为可信未签名构建提供范围明确的终端备用命令;已签名和公证版本无需执行。见 ADR 0232 与 E2E-196b。DMG 应保持应用拖入 Applications 的正常安装路径简洁,同时在未签名应用打不开时提供可见且可执行的处理指引。
D407导入会话后恢复已归档项目针对 issue #250 的渲染器增量行为:核心或插件导入新增项目绑定会话时,导入触发的会话刷新会规范化项目路径,并清除该项目的渲染器归档状态。无路径会话、跳过的导入、没有活动绑定的插件历史路径和普通刷新保持归档状态不变。host 项目行、IPC 通道、插件方法、存储 schema 和数据格式不变。见 ADR 0236 与 E2E-257。host 可以在项目下成功生成导入会话,而渲染器仍将该项目侧边栏行隐藏为已归档。只恢复新导入绑定对应的项目,可以让结果可发现,同时不会在普通刷新时削弱用户的归档选择(issue #250)。
D408三栏布局中优先保障 MainChat针对 issue #267 修订 ADR 0226 / ADR 0151 / ADR 0033:MainChat 保持 450px 硬下限;工作面板上限为动态预算(客户端宽度 − 450px − 展开的左栏宽度,无固定上限);中栏到达阈值时展开的左栏立即让位(sidebar-out 退场期间仍计入预算)。手动重开左栏优先占用右栏宽度,否则以 460px 为目标;关闭右栏只恢复由布局机制收起的左栏。原生窗口不变:预留 seam 保持 0 且不套用任何面板几何。预览模式临时卸载 MainChat 并使用窗口级 chrome 行;侧边栏折叠时,macOS 窗口模式预留 88px、全屏预留 8px 给交通灯(D433)。见 ADR 0238 与 E2E-LAYOUT-three-column-width-priority。固定客户区此前没有明确的宽度优先级,侧边停靠可以把 MainChat 压到下限、使 composer 不可用;显式化让位顺序后聊天在固定窗口内保持可读,且不重新引入原生窗口增长(issue #267)。
D409宿主拥有的会话协作消息(由 D446 修订) 修订 ADR 0237 / ADR 0165 / ADR 0213:Rust host-core 拥有以消息 id 和真实源/目标 Session ID 为键的持久会话协作 ledger。插件驱动的 spawn、send、status、result 和 cancel 使用已审查的 desktop-control 网关;发送者绑定当前插件 Agent 工具调用,目标回合保留原有配置,每条投递由实际持久回合认领。完成回调持久化且最多一次,并引用已结算回合。来源信息随转录行持久化,不能在重生成中伪造、剥离或编辑。增量架构 v16 迁移在重启后保留排队工作但不无人值守重放,执行权限上限和有界自主跳数,同时保持既有 Task 系列不变。见 ADR 0239 与 E2E-PLUGIN-session-orchestrator-real-workers。插件之前的创建/提示轮询路径既无法推断持久回合结果,也无法安全确认双向发送者身份。宿主拥有的 ledger 让投递、来源、回调、取消和重启行为可审计,同时不恢复已撤回的 A2A 协议。
D410独立会话发现与可导航协作投影修订 ADR 0239:新增经审查的 session/collaboration/list 读取操作,限制为最多 100 个未删除 Agent 会话,并只返回 Session ID、标题、状态、更新时间、可读 provider/model 标签和有界创建关系。侧边栏投影增加可读模型标签和最多八个已创建会话引用。创建者/已创建会话引用渲染为可键盘聚焦的导航按钮;独立会话不伪造创建者链接。不改变渲染器存储归属或协作写入边界。见 ADR 0240、E2E-SESSION-independent-top-level-communication 和 E2E-SESSION-hover-card-model-and-links。现有 Session ID 虽然是有效发送目标,但未由插件创建的会话可能不可发现;hover 卡片也只暴露 ID,来源信息不可交互。有界 host 目录和可导航投影让持久会话可通信、可解释,同时不暴露转录或凭据。
D446完成通知允许静默针对 issue #504 修订 D409 / ADR 0239 及 D193 静默回合契约:对当前由 Host 账本解析的完成通知,其第一条结算的 assistant 回复可以没有可见文本也没有工具调用,而不触发静默回合恢复或 EMPTY_MODEL_RESPONSE。该例外由这条回复消耗(无论静默、有文本还是工具批次),在同一次尝试的 provider 重试中保留,并在被接受的用户 steering 进入模型上下文后立即撤销。只有 Main 从 Host 账本解析出的来源信息才能开启它(kind: completion、目标为当前会话、消息 ID 与回复目标 ID 非空);提示文本、插件内容、task/message 投递和恢复的历史都不能。被接受的静默回复仍以空的已完成行持久化,但不进入运行时条目和模型上下文,与恢复转录的处理一致。见 ADR 0239 修订段与 E2E-SESSION-completion-notice-allows-silence。完成提示已经声明无需确认,运行时却重试这次静默并在任务已成功后报错(issue #504)。把例外限定在一条经账本校验的回复上,让普通提示、投递、工具工作和用户 steering 继续遵守 D193;空回复不进入上下文,避免下一次请求被 provider 拒绝。
D447舞台管理器边界看门狗仅限 macOSbootstrap/window.ts 中常驻的 1.5 秒 boundsWatchdog 只在 macOS 运行;其他平台在第一次 tick 时即清除该定时器。它的两个输入都只存在于 macOS(/tmp/pi-window-bounds CG 辅助程序,以及“窗口小于 500×400”这一被 1040×700 最小窗口尺寸变得不可达的判断),因此在别处它什么也恢复不了,只会永远调用 window.setAlwaysOnTop(false)。在 Linux/X11 上这会重复发送 _NET_WM_STATE 移除消息,而 Mutter 收到后会抬升窗口(meta_window_unmake_above 无条件调用 meta_window_raise),于是用户刚聚焦其他窗口约 1.5 秒后,应用就自己跳回窗口栈顶端——即使 _NET_WM_STATE 从未包含 _NET_WM_STATE_ABOVE。macOS 的舞台管理器恢复行为不变。见 D039、D053、D083 与 US-UI-19。一个会反复盖到用户刚聚焦窗口之上的窗口无法使用,而该定时器在 macOS 之外毫无用途;修复选择消除这条消息,而不是让每个平台去容忍它。
D413技能市场公网 HTTPS 目录拉取增量:设置 → 技能市场由 Electron 主进程按共享公网 HTTPS 策略发现 SKILL.md(公网主机语法 + DNS 分类 + 逐跳 redirect)。渲染层不发网。安装仍走 skills.create。目录 id 与 host valid_capability_id 对齐。展开后超过 128 KiB 拒绝写入。内置标题为英文。见 ADR 0243、E2E-SKILL-MARKET-*、issue #287。社区技能发现需要主进程出网,且不能复用插件市场的主机允许列表;复制分类器会与 MCP 市场撞名。
D421Native Pi 会话续接修订基线 D007:把 Pi v3 会话发现为按来源区分的投影,并通过 coding-agent AgentSession/SessionManager 针对其规范 JSONL 继续会话。Rust host-core 仍是 Desktop SQLite/Desktop 成绩单的权威;原生回合不进入 Desktop outbox,也不产生导入副本。原生续接要求精确的已存 provider/auth、项目信任、规范路径/头身份,以及带字节/叶节点校验的协作租约;失败保持可浏览/只读。首片不含原生 rename/delete/move/revisions/Plan/Goal/queue/collaboration;2026-09-14 的 ADR 0254 修订加入原生 fork,含精确流重键与 inode 跟踪的发布;该修订加入的侧边聊天面板已由 ADR 0268 移除。见 ADR 0254 与 E2E-SESSION-native-pi-*。导入扁平副本无法保留 Pi 的树结构,也无法让之后 Desktop 的回合对 Pi Web 可见。
D412仅增量且合并的流式更新修订本地 message_update 契约:追加型流式帧携带 stream: delta 以及 deltaText/deltaThinking(和 reset 标志),不再附带增长中的 content/thinking。运行时每 16ms 合并这些帧,并在语义边界前立即 flush。AgentHost、进行中检查点和渲染器应用增量;message_start/message_end 仍是完整快照。仅尾部 token 变化时,转录活动 part 保持对象身份。协议版本仍为 11。见 ADR 0242、E2E-STREAM-long-turn-keeps-realtime 和 issue #299。每个 token 都在 sidecar、AgentHost 和 IPC 上重新序列化完整助手快照,长回合接近 O(n²) 字节并让后续短块排队变慢。
D416Git clone 只接受语法上的公网主机修订首页 Git clone:parseGitCloneUrl 复用 isPublicHostname,在运行 git clone 前拒绝回环、私网、CGNAT、链路本地、ULA 以及 .local/.localhost 远程。指向公网主机的 HTTPS/HTTP/SSH/git@host:path 仍然有效。file: 和 URL 密码继续拒绝。Git 仍自己做 DNS;这不是市场那种地址固定。见 ADR 0247 与 E2E-CLONE-public-hostname-rejects-private。clone 曾接受 http://127.0.0.1/... 和 RFC1918 字面量,这是市场 HTTPS 拉取已经关掉的局域网/SSRF 缺口。
D417插件运行时主题 API + 侧栏图像令牌在 ui.theme 下新增 pi.app.setTheme 与 pi.themes.upsert/remove/list(ADR 0260 / issue #352)。移除 MAX_THEMES_PER_PLUGIN。运行时 upsert 与加载期相同的 CSS 消毒,并发出 pluginChanged(reason: "themes");setTheme 持久化 AppSettings.theme 并发出 settingsChanged。拆分侧栏绘制:--ds-bg-sidebar 保持颜色;可选 --ds-bg-sidebar-image 承载渐变/图片,macOS vibrancy 在图像层之上叠加 sheen。主题编辑插件无法在面板内应用主题,无法提供不限量主题库,也无法在生产模式免重载实时改 CSS;把渐变塞进颜色令牌会破坏 color-mix / vibrancy 消费方。
D420结构化、有界且脱敏的进程日志修订 ADR 0046 / ADR 0212:每条 app/host/agent NDJSON 记录都有稳定 event 和顶层关联字段。正常工具调用只产生一条完成/失败记录;sidecar 意外退出时为活动工具产生中断记录;工具协议和成绩单保持不变。中心日志会脱敏凭据与本机路径,将结构化数据限制为 8 KiB,用工具结果摘要替代原始输出,并把同一份已脱敏记录镜像到开发控制台。旧的 tool start / tool end 行重复且不清楚,自由文本的子进程/错误详情还可能泄露秘密或无限增长。
D422插件的宿主回合结束事件session:turnEnded 是宿主事件,载荷为 { sessionId, turnId, reason }(completed / aborted / error),对 session.beginTurn 真正开始的每个回合,在回合拆除结束时、持久化的 session.endTurn 尝试之后广播一次。携带的 turnId 是终止运行时事件本身所标识的身份,而不是恰好活动的那个回合;插件工具上下文中的 turnId 也会填充同一值。没有 ack,也没有重放:存活且已订阅的插件只收到一次;与崩溃、重载或宿主退出竞态的投递不作保证;收到该事件也不代表该回合的所有在途工具都已退出,因此清理必须按 turnId 串行化或限定作用域。不需要新权限,目前尚无任何已发布宿主发出该事件(0.14.8 也尚未包含)。见 ADR 0252。驱动 GUI 的插件此前只能用空闲计时器猜测回合是否结束,而计时器会在回合中途触发、在回合结束后再次触发。宿主拥有的「每个回合一次」终止事件加上明确的回合身份,让插件可以精确结算一次,工具上下文中的同一身份还能用来关联迟到的工具结果。
D423移除子智能体轮次上限修订 D328 / ADR 0062 / ADR 0063 / ADR 0119 / ADR 0126 / ADR 0166 / ADR 0210:maxTurns 与 MAX_SUBAGENT_MAX_TURNS 从定义类型、frontmatter 解析器及其钳制/非法告警、UserSubagentRecord / UserSubagentInput、host-core 注册表(记录、输入、frontmatter 解析、文档渲染、MAX_TURNS_CEILING)、五个内置文档、SUBAGENT_PRESETS 以及子智能体编辑器中移除。委托只在完成、父级调用 TaskStop、用户 Stop,或父级终态错误中止它时结束(ADR 0189)。既有文档中的 maxTurns / max-turns / max_turns 现在是无法识别的 frontmatter 键,会像其他未知键一样被忽略:不报错、不告警、不让定义加载失败,也不改写用户文件。truncated 从 SubagentRunStatus、渲染器 SubagentOutcome 联合类型、各语言 chat.subagentStatus 目录项以及委托拓扑的警告计数中移除;timed_out 保留。不改协议版本、schema 版本或存储。见 ADR 0253、E2E-155 与 E2E-SUBAGENT-legacy-turn-limit-frontmatter-is-ignored。父级看不到委托的实时工作,因此无法给出合理的轮次上限,而 60 / 50 / 40 / 80 这些内置兜底值也没有推导依据。该上限的唯一效果是把委托中途杀死,并以带部分报告的 truncated 呈现 —— 用户和父级模型都无法从中恢复。
D244紧凑的上下文用量摘要修订 D103 / D184 / ADR 0047:保留上下文检查器的剩余容量触发器、已用/窗口计数、回合合计、已完成回合速度、精确的提供商数值、聚合的工具类型/调用数/令牌数以及检查点摘要,但把它们渲染为一段简短摘要。从默认面板中移除逐工具行、占比条、来源徽章、解释性估算段落和已用容量计量条。不改动协议、存储、运行时计费或模型元数据。 (由 D347 修订:触发器移到输入框工具栏。)之前的诊断式布局让一次例行的容量检查变得又高又密。保留聚合信号、移除下钻装饰,使默认状态界面可以快速浏览,同时不改变底层用量数据。参见 ADR 0103 与 E2E-060d / US-UI-61。
D347输入框工具栏中的上下文用量检查器修订 D103 / D184 / D244 / ADR 0047 / ADR 0103:紧凑上下文检查器放在输入框右侧工具栏、模型 × 推理芯片左侧,始终对应当前最新一条已报告用量的助手回合。触发器保留剩余容量圆环和百分比,去掉重复的 Context 文字。弹层标题为剩余 tokens + 百分比;下方行用同一套左标签/右数值节奏,只用留白分隔,不画内部分隔线(D297)。答案下方的助理元只保留模型徽章。仅渲染器改动。挂在最新答案下方的检查器会随记录滚出视野。输入框只保留一个入口作为最新快照的权威位置;标题双线通过去掉多余说明文字解决,而不是加分隔线。参见 ADR 0184 与 E2E-060d / US-UI-61。
D355上下文检查器按最后一次请求计算占用修订 D103 / D184 / D244 / D347 / ADR 0047 / ADR 0103 / ADR 0184:剩余容量、已用/窗口计数、本轮合计,以及模型 input/output/cache/reasoning/命中率,都取最新一条已报告用量的助手消息(最后一次模型请求)。占用为该消息的 input + output + reasoning + cacheRead + cacheWrite。它们不是视觉工具循环里每一次请求的加总。已完成回合速度和聚合工具行仍描述该视觉回合。仅渲染器改动;宿主回合汇总和 Token Insights 仍做账单累加。把工具循环里的缓存读取加总后,367k 缓存读取会紧挨着 55k 窗口。OpenCode 的上下文组件只用最后一条助手消息。参见 ADR 0193 与 E2E-060d。
D356可选的子智能体思考覆盖修订 ADR 0062 / ADR 0144:子智能体 frontmatter 在沿用会话和七个规范档位之外接受 thinkingLevel: omit。omit 把智能体记账状态保持为 off,但走低层 provider 流,不合成思考覆盖。显式 off 仍关闭思考。模型配置里已选思考芯片在两种主题下使用强调底和反色主文本。不改存储 schema 或协议版本。见 ADR 0194 与 E2E-203。用户需要区分沿用、显式关闭和保留提供商默认;深色主题下已选芯片原先对比不足。
D348macOS 侧边栏改用 source-list vibrancy修订 D304:主窗口改用 Electron vibrancy: "sidebar",不再用 under-window。nativeTheme.themeSource 跟随应用主题(system / light / dark / 插件 base),进程级生效。仅在 themeSource 真变时重设 vibrancy;缺失的插件主题回落 system。macOS 26 Liquid Glass 让 under-window 变成浅色底板,40% tint 压不住,即使应用和系统都是深色也一样。见 04-ux/08-component-spec.md §1.7 与 US-UI-74 / E2E-076。
D243模型感知的图片附件传输修订 D197 / ADR 0059:输入框文件引用保留结构化的 kind/name/MIME 元数据。Electron main 在匹配时从精确的 models.dev 模型记录解析视觉能力,否则用精确的 pi-ai 回退记录(modalities.input / input 含 image),将图片字节存为 attachments/<sha256>,把符合条件的图片作为瞬态图片块发送,并对未知/非视觉或超大图片回退为安全的 @path。持久消息只存引用和元数据,绝不存 base64;渲染器展示可访问的能力状态。之前只走路径的约定让具备视觉能力的 GPT 模型收到的是一个临时路径而不是图片内容。把能力归属放在所选目录来源上,可以避免提供商发现或未知模型 ID 擅自决定传输方式,而路径回退保留了非视觉的文件工具行为(ADR 0101,由 D266 修订)。
D245OpenCode 风格的有界提供商 429 重试修订 D186 / D233 / ADR 0091:PROVIDER_RATE_LIMITED 在首次尝试之后获得五次静默、可中止的重试,请求建立与流中恢复共享同一个计数器。禁用 pi-ai 的嵌套重试;捕获失败响应的状态码/头部;依次遵循 retry-after-ms、retry-after 秒数、HTTP 日期,然后是 2 秒指数退避,带 25% 正向抖动、上限 30 秒。复用助手气泡,并在主会话与内置子代理中抑制中间的生命周期/错误事件。认证、模型选择、格式错误请求和上下文错误仍然是终止性的;耗尽时记录 retryAttempt: 5 与 providerStatus: 429。之前拆分的单次重试策略过早暴露瞬时 429,并且允许建立层与流层各自漂移。OpenCode 那套有界、感知头部的策略在不隐藏持续性提供商故障、也不叠加嵌套重试的前提下保留了用户控制权。
D237提供商的厂商账户(OAuth)登录提供商行可以用厂商订阅账户认证,而不是 API 密钥。pi-ai 的七个 OAuth 流程在启动时静态注册(registerBunOAuthFlows());Electron 主进程拥有登录/退出编排,并在 host-core 的加密密钥存储之上实现 pi-ai 的 CredentialStore,使用新的引用 secret:provider:<id>:oauth,并按提供商串行化 modify 以满足带锁刷新。auth_kind 新增 oauth,has_secret 放宽为“存在 API 密钥或** oauth 凭据”,新增的 has_oauth 与非敏感的 oauthAccountLabel 驱动徽标并隐藏密钥输入框;主机协议与存储架构不变。厂商卡片列表由 models.getProviders().filter(p => p.auth.oauth) 派生,登录按 vendorKey 幂等 upsert 一行。OAuth 行的 sidecar 启动载荷中 apiKey: "";运行时注入 resolveAuth 回调,调用新的宿主代理方法 provider.resolveAuth,该方法由 Electron 主进程自行应答 —— 绝不转发给 host-core —— 并先用每次启动重写的绑定表校验 (sessionId, providerId)。应答是短时 ModelAuth(apiKey/headers/baseUrl),因此刷新令牌永不离开主进程,而 matches() 让厂商运行时跨回合保持温热。providers.listModels 与连接测试改走已认证的账户,而不是探测 /models;apiStyle 跟随所选模型,并新增 openai_codex_responses 与 pi_messages。交互由 pi-desktop/providers/oauth/* 下的五条调用通道与一条事件通道承载。**Claude Pro/Max、ChatGPT Plus/Pro 与 Copilot 的订阅用户此前必须另买 API 额度才能使用本应用。pi-ai 提供了流程,但明确声明登录编排归宿主应用。厂商令牌约一小时过期,启动时解析一次会让长会话中断,并让 matches() 每回合失配;按请求解析既保持运行时稳定,又只把一个可作废的令牌交给运行模型指令的进程,且仅限该会话绑定的提供商 —— 严格少于今天无条件下发的长期 API 密钥(ADR 0095)。

| D238 | 扁平设置目录与市场上下文归属 | 设置使用可搜索的扁平目录,顺序固定为基础 / 全局 AI / 快捷键 / 说明 / 模型配置 / 导入 / 项目存档 / 信息。移除个人、集成、编码等分组标题。移除设置中的扩展目的地,将官方/镜像/自定义市场来源选择器移入扩展 → 市场并保留原有持久化与刷新行为。不改变 IPC、主机协议、存储、提供商、权限或项目所有权契约。 | 九项设置目录重复了自身层级,且与应用外壳的扩展目的地使用相同名称;将市场来源与市场浏览放在一起可以去掉重复入口,同时保留镜像/自定义来源工作流(ADR 0096)。 |

| D239 | 将全局默认项归入全局 AI | 八项设置目的地保持不变。基础拥有外观和平台支持的关闭行为。全局 AI / AI 拥有权限和默认项卡,包含默认运行模式(Agent / Plan / Goal)、命令 Shell 选择/回退状态和回车发送。模型配置保留默认提供商/模型选择器。只移动渲染器内容和设置搜索归属;持久化设置、主机 API、运行时语义和深层链接契约保持不变(ADR 0097)。 | 默认模式、命令 Shell 和回车发送改变全局智能体行为,将它们放在外观旁边混合了无关内容,也让全局 AI 目的地不完整。 |

| D240 | 独立的厂商 OAuth 账户与设置归属 | 修订 D237 / ADR 0095:每次 OAuth 登录都创建新的提供商行和行级 CredentialStore,即使 vendorKey 与已有账户相同。Vendor accounts 卡片拥有完整的 OAuth 行生命周期并通过 providers.delete 删除;AI services 列表排除 OAuth 行,但默认选择器仍可选择它们。厂商认证绑定和解析使用精确的 providerId;子智能体的含糊厂商/名称别名会安全失败。 | 一个厂商全局行会让第二个账户覆盖或复用第一个凭据,而退出登录还会留下过时的 AI 服务行。行 ID 是唯一明确的账户身份,可精确删除单个凭据和配置(ADR 0098)。 | | D339 | 按提供商覆盖 User-Agent | 已被 D341 取代。 每个 AI 服务和 OAuth 行可存可选 config_json.userAgent。空值保留适配器默认。非空值作为该行出站 HTTP 的最后写入 User-Agent。** | 用户需要覆盖默认 UA,但任意请求头随后由 D341 承接。 | | D341 | 按提供商自定义 HTTP 请求头 | 修订 D339 / ADR 0176:每个 AI 服务和 OAuth 行存可选 config_json.headers。空或 {} 保留适配器默认。非空映射对该行出站 HTTP 最后写入。残留 userAgent 迁到 headers["User-Agent"]。最多 32 个头;键值有长度和保留键限制。见 ADR 0178 与 E2E-005G。 | 用户需要的是任意非密钥请求头,而不只是 User-Agent。 |

| D242 | 内置子智能体继承父会话权限模式 | 内置子智能体默认使用 permission: inherit。内置 fixer 不再覆盖父会话,因此 auto 会覆盖其工作区内和明确外部路径调用,避免第二张授权卡;合格的内置或用户定义仍可显式声明非 inherit scope。 | 观察到的弹窗来自内置 fixer 将 auto 父会话替换为 accept-edits;继承父会话可以修复体验,同时不削弱 host-core 的 containment 和外部路径权限规则(ADR 0100)。 | | D241 | 带标题的设置导航视觉分组 | 设置目录保持八个目的地、扁平且可搜索,但显示四个非交互式本地化分组标题:Personal / 个人(基础、全局 AI、快捷键)、Agent / 智能体(说明、模型配置)、Workspace / 工作区(导入、项目存档)和 About / 关于(信息)。标题之间使用留白,不绘制分割线。搜索过滤时,空分组及其标题一并消失。本决策仅覆盖 D238 对分组标题的禁止,不改变目的地顺序、ID、搜索归属或市场位置。 | 扁平行缺少扫描锚点,视觉上过于紧密。柔和标题可以恢复分组,同时保持单层导航和既有目的地归属。 | | D442 | 市场下载请求携带设备标识 | 修订 D238:官方插件渠道的每次安装与更新都通过 POST /api/v1/download/resolve 解析并携带稳定的 deviceId —— 平台机器标识的 SHA-256 摘要,读不到机器标识时为本地持久化的随机 ID。客户端按顺序尝试返回的镜像,解压前校验 sha256 与 sizeBytes,从不缓存响应,只在平台不可达时回退到目录自身的包地址。GitHub 与 CNB 两个备份渠道以及 custom 继续沿用现有的静态相对 URL 解析。见 ADR 0276 与 E2E-PLUGIN-official-channel-resolves-through-the-platform。 | 平台需要一个稳定的安装标识,才能把一次下载只计一次,并按设备而不是按来源地址限流;上报的是摘要,因此机器码不出本机,也不新增任何界面入口。 |

D. 法典视觉平等决策 (0.3.5+) ​

黄金来源:当地 Codex 电子捕获;当行发生冲突时,最新行获胜。

身份证号主题决定基本原理
D034桌面视觉基线Codex electro-dark 1:1 外壳(炭灰色,浮动输入框,~275px 侧边栏)匹配当地法典的可用性和密度;保留 PI-Desktop 产品品牌
D035外壳显示名称(被 D094 取代) UI chrome 使用 shellName“Codex”; product/about 仍为 PI-Desktop满足视觉 1:1 复制品目标,同时保留 about/settings 中的产品标识
D036主题 chrome 令牌所有 shell chrome(导航、线程、芯片、标题按钮)均使用语义 --ds-* text/surface 令牌;浅色模式下没有原始的gray-0文本当导航在 #f3f3f3 上使用白色墨水时,浅色 macOS 默认不可用
D037深色侧边栏表面深色侧边栏使用 #000000(Codex surface-under);主窗格保持 #181818 (gray-900)匹配电子暗侧条与主表面分离
D038黑暗输入框板(被 D047 取代,然后是 D061) 深色浮动输入框使用实心 #212121,其仰角阴影比光强Codexleved-primary 必须读作 #181818 的方框;单独的透明混合看起来很平坦
D039舞台经理界限永久主机看门狗恢复足迹,而 width/height 保持折叠状态在舞台监督的猛烈攻击下,20 秒的爆发力不够
D040输入框智能控制(被 D091 取代) 自定义努力芯片会打开一个弹出窗口(努力无线电 + 模型标题 + 设置),而不是点击循环替换是因为控件在没有配置pi的情况下改变了标签
D041个人资料页脚(由 D113 取代) 自定义页脚打开配置文件菜单(设置/日志/主题);云徽章仍然是更新替代品D113 使用真实的本地轮廓页脚替换了 cloud/update 替代品和通用齿轮排
D042项目页面(被 D066 索引表取代) 项目是 recent/active 本地工作空间的卡片网格,带有引脚 + 字形颜色(localStorage 最近)无需云项目后端即可匹配 Codex 项目目标密度
D043设置外壳(被 D062/D063 全页 shell 取代) 设置在侧边栏表面 + 内容窗格上使用左侧导航栏 (General/Providers/Plugins/About)比仅顶部的选项卡条更接近 Codex 设置 IA
D044目的地列表镀铬PRs/Scheduled/Plugins使用共享目标行列表+过滤芯片;光卡白色高架无需完整的云后端即可匹配 Codex 目标密度
D045首页空栈空聊天使输入框保持在主流程中(不是绝对仅位于底部的码头);由 D047 分裂生长精制初步修复大空间隙; D047 修正双生长垂直模型
D046输入框占位符(被 D094 取代) 空草稿使用 Codex 占位符 (EN/zh-CN) 而不是空白空白的白板没有墨水,读起来就像是破损的;与早期的视觉金色副本相匹配
D047家庭分裂种植(被 D111 取代的几何) 空置家庭使用 upper/lower 生长区域(英雄项目结束 + 输入框调整结束);暗盒使用 Codex 海拔突出匹配电子双生长; D111 用可滚动流堆栈替换双增长门户以阻止 composer/card 冲突
D048侧边栏最近标签(由 D088 取代) 近期部分使用实时 Codex 黄金标签 EN Recents / zh-CN 最近(并非仅限 asar 的 Tasks/任务)视觉黄金 + 插件和线程列表之间的实时编码 shell 部分标题
D049家庭建议卡(被 D131 取代) 空的主页在英雄下方显示 4 张环境卡(自动调整行),并在点击时预填充启动提示D131 从空置中删除卡行和提示预填充入口点
D050空作曲器板高度(被 D055 取代,然后是 D061) Home 空输入框 min-height ~112px(紧凑);模型芯片显示模型 ID(操作保留在菜单中)匹配 Codex 空板密度;模型芯片镀铬更接近电子模型选择器触发器
D051侧边栏导航密度(会话列表 IA 被 D088 取代;保留行密度) 导航行 ~32 像素间距,最近行 ~28–31 像素,部分标签 Recents/最近关闭 light-home 侧边栏残留 vs cx-home-clean
D052家用立式+夜箱抛光(工作区芯片表面被 D095 取代;保留剩余指导) 上部 pb ~62px(英雄第一墨水 ~y305);光芯片#f3f3f3;灯光输入框海拔更强;黑暗家庭输入框固体 #212121;工具栏控件 28px关闭英雄y≈300和输入框乐队的余热;夜板不得压平至 #181818
D053舞台监督CG检测CG边界助手通过pid匹配任何窗口层;上架恢复前,missing-CG需要streak≥3;避免永久的alwaysOnTopAlwaysOnTop 浮动层破坏了第 0 层帮助器并导致恢复抖动
D054空草稿行+无限提示(∞ 提示被 D094 取代;领先品牌提示被 D160 取代) Composer 自动调整大小绝不能折叠空文本区域高度 (<28px);在草稿左侧保留明显的品牌提示;常亮禁用发送(#bdbdbd 灯亮);更密集的占位符墨水;实心夜板 #212121清空 height:0 自动调整大小隐藏占位符并读取为损坏的 night/light 框;金色草稿行需要可见标记+油墨密度
D055空板吃水Y(印版高度指导被 D061 取代;工作空间芯片密度被 D095 取代) 主页空壳最小高度 ~148px(底部对齐),因此草稿最密集的墨水 ≈y556 与金;芯片紧凑型 28px112px 板左草稿 ~30px 低;在不移动工具栏基础的情况下向上生长板
D056空置的家庭工作区芯片(由 D095 取代) 始终将 project/Local/branch 胶囊隐藏在空置的家中;仅在线程对接的输入框中显示即使有项目标题,cx-home-clean 空金在盘子上方也没有胶囊带
D057主队标记+英雄称号光学(主页标记被 D094 取代;保留标题指导) 空主页 Codex 标记使用较粗的笔画;短工作区基本名称显示为 PI-Desktop 以获得黄金标题跨度英雄残差是细标记+短项目标签下墨标题 vs Codex 黄金
D058首页内容宽度+深色墨水标记家庭双生长最大宽度使用768px(不是14px根下的48rem);首页水平垫12px;英雄 title/night 控件使用主题令牌;夜间本垒板范围仅限于黑暗48rem 在 14px 根部收缩板 ~120px 与 Codex gold 相比;硬编码的光英雄墨水使夜间标题无法读取
D059光禁用发送墨水禁用发送芯片 #8e8e90 + 白色箭头(不是 #bdbdbd)像素匹配 cx-home-clean 空发送控制
D060轻新任务鬼行轻空置新任务透明(无固态芯片);仅悬停清洗金色有图标+标签,无填充药丸;填充的 #e8 芯片是主要导航残留
D061空盘Y+夜高架-初级首页空板最小高度140px + 包裹底垫16px(顶部〜y536–538 /草案〜y552 /脚部阴影〜y674);浅色+深色本垒板使用升高的主填充和向下的高度(无向上的全向发光);深色填充 #212121f5板高有板前光晕;实心夜板 + 重型全向阴影,与 Codex 高架主色和金色脚带不同
D062设置 Codex shell设置使用Codex分组导轨(Personal/Integrations)+搜索+返回应用程序;内容是升高的行面板; Providers/Plugins 保留为本地优先;集成下的 MCP 空状态目的地奇偶校验差距;先前的 4 项扁平导轨与 Codex 设置 IA 不同
D063设置全页接管设置用 Codex 全页 shell 替换应用侧边栏:后退+搜索+图标组 (Personal/Integrations/Coding)、提升的 permission/general 卡、保留本地 Providers/Plugins主窗格内的嵌套设置与实时 Codex 设置黄金不同
D064设置常规内容奇偶校验基本卡行匹配 Codex:默认打开目标、语言、菜单栏、底部面板; nav 添加 Pets/Appshots; sun/pet/snapshot 图标;药丸选择与实时 Codex 设置黄金含量带更接近 1:1
D065设置一般金色抛光权限行包括蓝色了解更多链接+完全访问风险副本;开放目标药丸显示 VS Code 字形; Agent 使用圆形箭头图标;集成顺序Appshots→插件→浏览器→计算机→MCP;输入发送至 Agent全页 shell 后的剩余间隙与 cx-settings-try
D066带有项目的主页 chrome + 项目索引(输入框智能标签由 D091 取代;工作区芯片部分由 D095 取代) 项目打开时主页显示工作区芯片(无 ∞); home placeholder 随心输入/询问任何事情;页脚齿轮+帮助;项目页面是使用 setProject 的 Codex 索引 (search/columns/expand/actions)黄金级 cx-home-clean 与项目 + 项目索引页奇偶校验
D067首页建议字形+芯片间隙(建议字形部分由 D131 取代;输入框芯片间隙部分由 D095 取代) 建议图标与 Codex (code/hammer/refresh/bug) 和 blue/purple/green/orange 音调相匹配;输入框芯片间隙 8px 和更密集的胶囊D131 取出牌; D095 仍然是输入框间距的权威
D068最近的行操作 + 灯具标题(侧边栏操作被 D088 取代,然后由 D093 重新组织;保留夹具标题指导) Active/hover 最近的行显示图钉 + 面板尾随操作; capture/fixtures 更喜欢中文标题的空会话(同步代码)而不是裸新任务金色侧边栏选排镀铬;减少选择残差
D069目的地标题刻度+黑暗新任务幽灵目标页面标题使用 Codex 28px/560 权重;新任务也是黑暗中的透明幽灵;捕获下降英语噪音固定装置和引脚同步代码PR/Projects 标题不匹配;深色 新任务读取为选定芯片
D070设置金色公制抛光设置导轨275px/#f4f4f4;更密集的导航;内容标题偏移量; 32×20 重音切换;帐户右上箭头; 14 像素卡片; 720 像素内容带Residual 与 cx-settings-try(导轨宽度、切换尺寸、标题 Y、外部标记)
D071转录本相互作用奇偶校验工具调用渲染为 Codex 风格的轻量级披露行(脱字号 + 名称 + 单参数提示 + spinner/status,夹紧的插入正文)替换盒装卡片;仅在通过浮动跳转到最新药丸固定到底部时自动滚动(根据 D151 发送/重试/重新生成重新固定);闪烁工作…与经过的时间线;将副本悬停在消息和代码块上盒装工具卡和强制滚动到视图与 Codex 转录本的感觉不同;规范 7.4 滚动暂停未实现
D072Typography/radius 令牌强制执行所有 font-size/weight/line-height/letter-spacing/border-radius 值必须使用 @theme 令牌变量(具有 -plus 半步的 --text-* 斜坡、--font-weight-* 包括 520/560、--leading-*、--tracking-*、12 步 --radius-*); CSS 和 TSX 任意实用程序中的原始文字被连接到 pnpm lint 的 scripts/check-style-tokens.mjs 阻止;准确保留像素值(无视觉变化)约 130 个分散的文字从任何比例上漂移;设计系统文档 §5.2/§6.2 表与实现相比已过时
D073完整渲染器 i18n 覆盖每个用户可见的渲染器字符串都流经 i18next(en 事实来源,zh-CN 通过 satisfies EnglishCatalog):ContextPanel/CommandPalette/PermissionDialog 有线; toast/aria/title/placeholder 文字键控;会话默认标题来自 i18n.t,具有共享的不区分大小写的 isDefaultSessionTitle 匹配器,涵盖跨区域设置的旧标题;专有名词(VS Code、Finder)和母语名称保持未翻译六个组件完全绕过了 i18n;默认标题匹配在商店和侧边栏中重复,并且错过了 zh“新对话”
D131空荡荡的家,没有建议卡(starter-grid 条款由 D205 修改,然后由 D206 取代) 空的聊天主页暂时仅呈现英雄、可选的首次运行清单和输入框;原来的四张探索/构建/审查/修复卡、它们的彩色字形以及它们的提示预填充操作被删除。这取代了 D049/D067 和 D111 的原始卡特定条款,同时保留其单一可滚动流布局。直接输入框仍然是主要任务条目;去掉了原来的装饰起始行,解决了噪音卡的处理。在 D206 将空状态返回到直接进入之前,D205 短暂地重新引入了一个更安静、非提交的开发者网格。
D133项目索引移至设置存档(五个目的地 count/order 被 D166 取代;平面列表演示被 D168 取代) 主页侧边栏不再具有独立的项目目的地。设置在导入之后、信息之前添加项目归档(zh-CN: 项目归档),将压缩目录带到基础/模型配置/导入/项目归档/信息。存档重用持久的项目索引,并且始终包含存档记录,包括搜索、添加、激活、任务扩展、固定、archive/restore 和关闭操作。项目和会话组仍保留在主侧边栏中以进行积极的工作。全局搜索将存档公开为“设置”结果,而不是独立页面。这取代了 D042/D066 的独立目标子句和 D090 的四个目标限制,而不更改项目存储或激活语义。活跃的项目工作已经存在于保留的侧边栏组中;将历史项目管理移至“设置”可减少主要导航,同时保持恢复和存档控件可发现。
D168项目档案演示重新设计(堆叠带布局与每部分面板被 D267 取代) 项目存档呈现三个堆叠带:一个概述横幅(意图句、主要添加项目以及用于项目、打开、存档、会话的四个派生计数器)、一个工具栏(具有清晰的可见性和实时匹配计数的搜索,以及 Recent/Name 排序分段控件),以及一个分组索引,其始终可见的部分运行“固定/所有项目/存档”,每个部分计数、每个部分一个设置面板和细线行分隔符。行包含公开控件、字形、带有 Active/Open/pinned/Archived 标签的名称、一条元行(缩短的等宽路径、分支、会话计数)、相对最后活动时间和 hover/focus-revealed 新任务加行菜单;菜单将 create/edit 置于引脚上方、archive/restore 和破坏性关闭中,并在 Escape 或外部按下时关闭。存档的行被分组和软化,从不隐藏或过滤,因此 D133 的不可见性切换规则仍然有效。这取代了 D133 的平面列表演示,而无需更改项目存储、搜索匹配、会话批处理或激活语义。平面单列表存档对固定、工作和存档记录给予同等重视,并将其披露和行操作隐藏在悬停后面,因此扫描长期持久索引意味着读取每一行。使用派生计数器进行分组并进行显式排序,使状态一目了然,同时保持存档历史记录永久可访问。
D267项目存档是一个工作台,而不是三个堆叠带(行剖析与行内展开被 D455 取代) 修订 D168 的堆叠带布局:设置 → 项目存档改为呈现 D257 的一个工作台构成 —— 一条仅承载页面说明的安静引导行、一个工具栏(使用共享 settings-segment 基元的“最近/名称”排序、带清除可供性与实时匹配计数的搜索、主要“添加项目”),以及一个设置面板,其始终可见的固定/所有项目/存档分组是面板内带每部分计数的非交互标题条,而不是每部分一个面板。装饰渐变英雄带被移除,它承载的四个页面级概览计数器也一并移除,并停用 project.statProjects、project.statOpen、project.statArchived 和 project.statSessions 键;面板标题条上的分组计数现在是唯一的总计。外部大写部分标签也被移除;行几何与能力页面行一致(32px 控件、14px 列表间距、28px 行字形)。除停用上述四个计数器键外,其余仅为演示层变更:D168 的行剖析、行菜单分组、搜索匹配、会话批处理、激活语义和无障碍语义均不变,且不需要新的 ADR。D168 的概述横幅使用了装饰渐变和 --text-xl 计数器格,而设计系统明确禁止两者,且其每部分面板把同一个高架框重复了三次。把计数器降级为内联串只是保留了杂乱而没有换来价值:它们展示的每个总计都已能从分组条计数中读出,因此在工具栏上方重述这些数字既重复了数字,也让该目的地拥有了兄弟页面都没有的页头。移除它们后,持久索引在外观和行为上都与智能体能力页面一致。
D455项目存档改为列表 + 检查器(行剖析被同日的「项目档案呈现为内嵌分组卡片列表」条目取代) 修订 D267 的展开行:设置 → 项目存档保留安静引导行和工具栏,随后是单列工作台。索引仍按已置顶 / 全部项目 / 已归档分组并显示计数,且没有可见性开关(D133)。精简行显示字形、名称、一个状态标签、会话计数和相对时间。单击选中并留在设置页;双击、Enter 或检查器的打开操作激活项目并返回聊天。文件夹、对话(每批 8 条)和行菜单在选中行下方铺满宽度打开。搜索仍匹配会话标题并选中所属项目。仅演示层:无 IPC、存储或宿主改动。见 ADR 0294 与 E2E-038。行内展开和行菜单让长索引难以扫读,单击名称还会离开设置页,不利于管理。
D456会话思考参数不发送修订 ADR 0194 / ADR 0144 / ADR 0221:会话 thinkingLevel 在七个规范档位之外接受 omit。Composer 在推理模型上把 omit 放在菜单最前。设置页 defaultThinkingLevel 在绑定启用任一推理档时也接受 omit。运行时记账仍为 off,走低层 provider 流,不合成思考覆盖。绑定/目录能力列表保持规范档位。Schema v19 把会话 CHECK 扩到包含 omit。握手协议版本不变。见 ADR 0295 与 E2E-203a。显式 off 仍会序列化为关闭思考;用户需要与子智能体一致的会话级不发送。
D458Composer 菜单根层承载推理滑块修订组合的模型 × 推理菜单:列出一个以上等级时,推理等级条目下方放一条原生 range 滑杆,每个等级一个带标签刻度。滑杆和刻度提交通过 configureActiveSession 持久化最后一次待提交档位且不离开根层;条目仍打开经典单选列表。刻度标签不是 Tab 停靠点。滑杆显示一条轨道,每个刻度在轨道上有一个刻度点、轨道下方各有一个可见标签;刻度点行和标签行都是满宽 n 列网格,range 输入覆盖在轨道上并内缩半列减去滑块半径,因此对任意档位数刻度点、滑块和标签共用同一列中心。选中档用 accent token,其余用 muted。仅渲染层。见 issue #417、04-ux/08-component-spec.md 与 E2E-050。改档原先必须进子菜单;Codex 风格滑杆保留单选列表,同时让临时调整只需拖一次。

E.M5 强化决策 (0.4.0) ​

身份证号主题决定基本原理
D078macOS 签名通道(由 D450 / ADR 0289 修订) 静态配置不嵌入证书身份,因此未配置证书的本地构建保持未签名。正式 GitHub tag 发布需要 Developer ID 签名和 Apple 公证;CI 通过 Actions 密钥接收证书材料,并拒绝未装订的工件。贡献者无需证书即可打包,同时已发布的 macOS 工件满足 Gatekeeper 要求。参见 06-delivery/06-release-runbook。
D079应用程序图标/品牌标记 v1(渲染器资源路径由 D221 细化) build/icon_1024.png 是规范的 PI-Desktop 徽标; scripts/make-icon.py 派生 build/icon.icns 而不覆盖 PNG;打包的 macOS 构建、pnpm dev 和渲染器 chrome 重用这些资源在开发、渲染器和打包通道中保持一种视觉标识,同时防止派生脚本恢复过时的生成标记
D080后台监管子出口立即拒绝正在进行的 RPC;退避重新启动(0.5s→4s,每2分钟最多3次); hostStatus 事件导致渲染器性能下降 UI崩溃恢复,无挂起;失败是可见的,而不是沉默的
D081Renderer 沙箱sandbox: true 与完全捆绑的 CJS preload;生产 CSP 删除 unsafe-eval 和 localhost connect-srcElectron 安全基线;由 test:e2e:boot 验证
D082日志通道(部分被 D182 取代) app/host/agent NDJSON 文件,通过主进程记录器进行 5MB 循环(保持 2 循环);审核通道停留在 host-core SQLite可诊断故障,无无限增长;审计需要可查询性
D083窗口状态保持 window-state.json 的最后良好边界(恢复最小 960×640); Stage Manager 架子恢复保留了 Codex 的足迹;捕获运行强制确定性边界用户保留他们的窗口;货架恢复和像素捕获保持确定性
D084跨平台shell策略(被 D190 取代) Bash 工具在每个平台上运行 bash,每个进程解析一次:PI_DESKTOP_BASH 覆盖 → Unix 众所周知的路径 + PATH → Windows Windows bash.exe 源自 Windows 的 Git(git 在 PATH 上,标准安装目录,然后 PATH 减去 WSL System32 启动器); Unix 使用 bash -lc、Windows bash -c + CREATE_NO_WINDOW;安装程序中没有捆绑 bash;缺少外壳表面稳定 SHELL_NOT_FOUND 并附有安装指南被 D190 中的可选 shell 目录和稳定执行标识取代
D085Toast系统v2单个全局 Toast 堆栈(ToastHost + 存储队列)将字符串 setToast: showToast(message, {variant, duration}) 替换为 info/success/warning/error 变体(中性升高板上的语义标记着色的 Lucide 图标)、自动消除 4 秒/错误 8 秒/系统拥有的 0 粘性(无呼叫者计时器)、悬停暂停、最多 4 个重复数据删除、 enter/exit 运动 + 简化运动安全移除,aria-live + 角色 status/alert; 08-组件规范 §17 中的使用规则旧的 toast 是一个裸露的固定 div:没有变体或堆叠,并且大多数调用站点从未清除它,因此消息永远存在;呼叫者手动超时
D086存储架构 v2根据 03-runtime/04 重建单个 pi.sqlite(host-core 独有):kv 命名空间替换 SQLite/NDJSON 并托管插件设置; projects 替换工作区单例;转录本成为规范块数组(messages.content_json + 提取的 text、ms 整数时间、O(1) 每个会话 seq、稳定的 mid rowid),其中 turns 携带状态机状态 + 使用情况汇总和 FTS5 trigram 搜索;新的 models 目录、artifacts、scheduled_tasks+task_runs(移出 Electron 的 JSON,修复了 D002 违规);索引可修剪 audit_log; PRAGMA user_version 迁移与预迁移 .bak;已删除的 showToast(message, {variant, duration})/aria-live 表已删除(registry.json 保持权威)v1 模式是一个有损的 UI 投影(无 turns/usage/blocks/attachments),按 MAX+1 扫描排序,具有零二级索引、两个死表、RFC3339 文本时间以及绕过主机所有权的计划任务;规范的功能(工件视图、成本芯片、运行历史记录、项目分组、全局搜索、目录刷新)没有存储空间
D087沉浸式输入框上下文轨道(被 D095 取代) 项目/本地/分支仍然是一根导轨,但该导轨现在直接连接到输入框外壳,共享其主题表面和底部标高,并删除可见的 8px 间隙和独立的胶囊阴影;取代 D067 的间隙部分分离的胶囊和不同颜色的板使得上下文和提示输入被解读为不相关的控件,而不是 Codex 风格的沉浸式输入框
D089输入框草稿高度提示文本区域默认显示一行可见行,从包装内容自动增长到七行可见行,内部滚动超出第七行,并在内容被删除时收缩; home shell 是内容驱动的,而不是保持 D061 的固定最小 140px保留转录空间和类似 Codex 的密度,同时保持多行编辑可用
D088范围内的主页侧边栏会话(仅针对一个当前项目和无行操作限制由 D093 取代) 将“最近”聚合替换为一个当前项目会话组加上持久无路径临时会话;将其他项目保留在项目索引中;删除最近的 pin/panel 行操作;范围空草稿重用和项目上下文显式 + 创建D093 保留精确路径分组、范围草稿重用和临时边界,同时允许多个保留的项目组和范围组织操作

F. 基线 0.4.2 产品决策 ​

身份证号主题决定基本原理
D090紧凑的设置目录(由 D133 取代的四个目的地计数和顺序) 设置保留了 D063 全页 shell 和 D070 视觉指标,但其栏完全按该顺序包含基础知识、Agent、导入和信息。外观转向基础;提供商进入 Agent。插件管理保留在应用程序外壳的现有插件目标中,其中有可用的 load/enable/disable/uninstall,并且不会在“设置”中重复。这取代了 D062–D065 中更广泛的分组导航和独立 Appearance/Providers/Plugins 展示位置,以及 D070 的特定于客户的轨道指标。删除空的、低价值的和重复的目标,同时保持每个已交付的工作流程均可访问,并使本地优先设置表面更易于扫描
D091Composer 运行时配置Mode 和 provider/model 控件更新活动会话并通过 pi 提示符路径从该会话中读取;没有端到端运行时实现的控件不会被渲染。防止装饰性 effort/attachment 控件并保持每个可见的输入框动作可运行
D092响应式设置内容设置内容填充固定 275px 导轨和窗格装订线后的可用宽度,通过本机窗口的 CSS 弹性布局调整大小。这仅取代 D070 的固定 720 像素内容带以及 D090 中相应的视觉指标保留。高效使用宽桌面窗口,无需添加渲染器调整大小状态或更改紧凑设置目录
D104设置轨道菜单重命名(Agent 标签由 D110 取代;目录顺序由 D133 取代) 设置轨道目标标签为 Basics / Agent / Import / Info(zh-CN:基础 / 智能体 / 导入 / 信息)。目的地 ID 保持为常规/代理/导入/关于;只有面向用户的标签会发生变化。这将重命名 D090 紧凑目录标签,而不更改顺序、内容或深层链接目标。更短、面向操作的标签扫描速度更快,同时保留紧凑的目录

G. 基线 0.4.3 侧边栏和项目决策 ​

身份证号主题决定基本原理
D093侧边栏组织、保留的项目选项卡和会话工作区隔离渲染器保留标准化的开放项目路径和本地 project/session 演示元数据,用于固定、存档、折叠、排序和手动顺序。侧边栏为每个保留路径和临时会话呈现一个独立的可折叠组。面向用户的排序模式有最近的、创建的、最旧的和名称;项目组可通过拖动标题(小幅移动后)或在该标题上按 ArrowUp/ArrowDown 选择 manual,而会话 manual 仍是兼容性值。激活组会重用 project.set,因此 shell 仍具有一个选定的主机工作区。工具执行从持久会话项目中解析其根,并且当另一个选项卡变为活动状态时,每个会话 turns/grants 保持独立。存档、关闭和重排均为渲染器本地且非破坏性的。保留多存储库工作集并使长对话列表易于管理,而无需创建多个主机工作区单例或允许活动选项卡开关重定向后台会话的工具
D094Renderer 产品品牌(停靠的 Composer 徽标由 D160 取代) 所有用户可见的 shell 标识都使用 PI-Desktop:侧边栏 shell 名称、composer 占位符和设置副本。 BrandLogo 通过 Vite 导入由 build/icon_1024.png 派生的标记(参见 D221),用于首页英雄、expanded/collapsed 侧边栏和停靠的输入框;新会话控件使用专用的消息加号图标。 Codex 仅保留在标识外部导入源或设计参考的位置。从产品表面删除意外的第三方品牌和矢量近似,同时保留导入兼容性和 Codex 派生的布局系统
D135独特的侧边栏任务状态指示器对话行保留一个紧凑的前导状态槽,具有语义、形状不同的状态:中性重音轮廓环表示选定,警告橙色呼吸点表示正在进行,成功绿色检查表示已完成,错误红色圆圈警报表示失败。优先级是进行中、选择,然后是最新的最终结果。开始新的回合会清除之前的结果;中止不会产生失败。每个状态都有本地化的可访问文本,并且减少的运动使正在进行的点静态。先前的运行点重复使用了重音标记,并在空闲时消失,使得选定的、活动的工作、完成和失败难以通过超过行背景来扫描或区分

H. 基线 0.4.4 输入框决定 ​

身份证号主题决定基本原理
D095删除输入框工作区上下文栏主目录和线程对接的输入框变体永远不会渲染被动项目/本地/分支轨道或为其保留 layout/elevation。项目选择、会话绑定、分支元数据和工作区范围的工具仍然可以通过非编写器界面使用。这取代了 D052、D055、D056、D066、D067 和 D087 的上下文轨道部分。轨道重复导航,显示两个被动值,并且可以在消耗提示空间的同时显示误导性的后备分支元数据

一、基线0.4.5思维模式决策 ​

身份证号主题决定基本原理
D096端到端的思维模式思维水平是会话范围的,规范值“off” | 最小的 | 低 | 中等 | 高 | 高 | 最大`;能力分辨率和最近级别钳位驱动 Composer 和 pi 请求;思维与最终答案文本分开并持续存在;模式 v3 和协议 v2 携带新字段。仅在模型功能、持久性、IPC、sidecar、pi 运行时、事件、存储和转录路径全部可操作后恢复推理控制
D102自定义提供商思维预设设置显示“关闭”/“仅限开关”/“分级”(以及高级自定义列表)。开关仅持续 supportedThinkingLevels: ["off","high"]; Graded 清除稀疏覆盖并使用保守的默认集; Composer 仅渲染已解析的集合,并且从不为类似布尔的模型(例如 mimo)发明分级选项。与 OpenAI 兼容的自定义端点通常会暴露布尔思维,而不是完整的努力阶梯

K. 工作小组的决定 ​

身份证号主题决定基本原理
D097停靠的工作面板取代了上下文面板右侧托管一个停靠的、可拖动调整大小(320 分钟(720px,60vw))的工作面板,其中包含“审阅”/“终端”/“浏览器”/“文件”选项卡,可通过标题栏按钮或 Cmd/Ctrl+J 进行切换; {open, tab, width} 保留在 localStorage 中。以前的 ContextPanel 覆盖层及其 context.* 副本被删除; workspace/model/status 存在于输入框芯片和设置中。用于检查药剂输出的 Codex 同等工作表面;仅提供信息,覆盖其他地方可用的重复数据
D098Review 选项卡读取 git 工作树 (由 D180 取代)历史性决定:通过 git CLI 审核呈现工作区的未提交状态。被取代,因为提交删除了消息级证据并且无法支持安全的每消息回滚
D099终端选项卡是真正的 PTY(已由 D251 取代)历史决策:旧版工作面板曾提供按会话范围保留的交互式 shell 选项卡。当前产品移除了独立 shell 表面;Agent Bash 仍是非交互式并归对话所有
D100浏览器选项卡嵌入 WebContentsView预览浏览器是一个主进程 WebContentsView,具有渲染器驱动的边界同步、强化(拒绝弹出窗口→外部、拒绝权限请求、仅限 http(s) 导航、隔离持久分区);当阻止覆盖(调色板、权限对话框、设置)打开时它会隐藏。用户驾驶它;代理没有。推荐现代嵌入,无需 webview-tag 警告;隐藏规则通过渲染器覆盖解析其合成器 z 顺序
D138会话范围的内联权限请求工具批准是其原始 sessionId 拥有的内联 PermissionCard,而不是全局对话框。不同的会话保留独立的待处理请求和绝对的超时期限;后台 message/tool/permission 事件仅更新范围内的状态,并且从不激活、覆盖或聚焦其他对话。解析和清理与会话和请求身份相匹配。这仅取代 D100 的权限对话框 overlay/hide 子句;调色板、搜索、设置和其他阻塞表面保留其现有的浏览器隐藏行为。并发代理不得窃取活动工作流程或覆盖彼此的批准请求;现有协议已携带所需的 session/request 身份。
D139导航意图和快捷方式事件防护每个显式会话、项目、页面、分支或历史导航都开始或重用一个渲染器导航意图;异步工作仅在该意图保持当前状态时提交可见状态。全局快捷键忽略仅修饰符和 IME 组合事件,而历史导航也忽略按键重复事件。延迟的 session/project 加载和不完整的键盘事件不得导致无法解释的页面或历史跳转。
D128工件驱动的工作面板选项卡(由 D207 取代的快捷方式子句)工作面板没有空的手动入口点、欢迎选择器、titlebar/menu 命令或 Cmd/Ctrl+J 快捷方式。 file/URL/BrowserPreview/successful-command 工件创建并激活可关闭的顶部选项卡;成功的活动会话工作区 Write/Edit 工件创建并激活单例“审阅”选项卡。文件选项卡由词法规范化路径键入,单例工具选项卡重复数据删除,关闭活动选项卡选择其右侧邻居然后选择左侧,关闭最后一个选项卡隐藏面板,唯一的面板级控件将其折叠,而不删除保留的运行时选项卡。更改可见会话或工作区会关闭并清除选项卡,因此相关资源永远不会跨越上下文边界。启动关闭,没有任何选项卡;只有面板宽度仍然存在,而临时操作系统窗口扩展被排除在启动范围之外。后台会话、失败和临时写入不会抢走焦点。这取代了 D097 的固定选项卡入口点和 {open, tab} 持久性,改进了 D098 的自动刷新,并且仅取代了 D112 的welcome-chooser 子句。匹配 Codex 的输出驱动工作界面,避免空工具启动器,并使每个可见选项卡对应于会话实际生成或显式预览的工作。 D128 纠正最初重复的 D119 标识符; D119 仍然是记录文件存储决策。
D140会话拥有的脏工作区成绩单审核条目*(由 D179 取代)*会话成功生成工作区 Write/Edit 后,其记录以折叠活动组之外的一个明确的“审阅更改”命令结束,而 Git 工作树仍然是脏的;同一项目中的其他会话不会继承该命令。它报告上限文件计数和 addition/deletion 总数,并创建、重新打开或激活 D128 的单个“审阅”选项卡。条目和审阅共享一个工作区键控差异,在工作区激活、成功 Write/Edit/Bash 完成(500 毫秒去抖)、显式审阅刷新和窗口焦点时刷新;排序的请求会丢弃先前的工作空间响应。干净和非 Git 结果明确了该工作区的审查所有权; clean、non-Git、missing-workspace 和 failed-refresh 状态会隐藏该条目。审查所有权是在使用 D142 工作面板上下文重新启动时丢弃的渲染器内存状态。这是上下文 artifact/status 条目,而不是 D128 禁止的空手动启动器。自动面板打开在折叠或选项卡关闭后不会留下可发现的返回路径,而会话所有权可防止不相关的对话声明项目范围的编辑。共享真实的差异可以使对话条目准确并保持评论的重复数据。
D179消息范围内联审核卡*(由 D180 取代)*历史决策:每个成功的工作区 Write/Edit 行通过匹配当前工作区差异来呈现一张卡片。已被取代,因为当前的 Git 状态在提交后消失并且无法提供消息拥有的回滚
D180消息拥有的审查快照和受保护的回滚成功的工作区 Write/Edit 结果携带有界 details.review 证据:快照 id、message/tool id、路径、操作、added/modified/deleted 状态、+/- 计数、块和回滚状态。主机在会话下的工作区之外存储工具前字节和 before/after 哈希值,渲染器仅从转录消息中导出相邻卡片和按时间顺序排列的查看历史记录。 review.rollback 在恢复之前的字节或删除新创建的文件之前验证当前的后工具哈希;冲突永远不会覆盖以后的编辑。会话删除和启动清理删除快照;分叉证据是可见的,但不可逆。消息拥有的快照在 Git 提交后仍然存在,分离相同路径的编辑,并在不信任可变存储库状态的情况下显式回滚
D142会话范围的工作面板运行时上下文(D207 修订的无启动器条款)每个对话都拥有一个内存中的工作面板上下文,其中包含打开状态、有序选项卡、活动选项卡和浏览器资源。会话选择自动投影该上下文,切换不会删除它,切换回来会恢复它。 File/URL/BrowserPreview/command/Review 工件是根据其原始 sessionId 记录的;因此,BrowserPreview 渲染器事件携带 sessionId。背景工件可能会更新其保留的上下文,但永远不会打开、激活、调整大小、导航或聚焦可见面板。没有活动对话的工作区选择会隐藏面板,并且相关资源仍然绑定到其 session/workspace。重新启动会丢弃所有上下文和浏览器资源;只有面板宽度仍然存在。这仅取代了 D128 在可见会话或工作区更改时关闭和清除选项卡的要求; D128 的工件触发器、重复数据删除和 close/collapse 行为保持不变,而 D207 添加了一个仅显示保留上下文的活动会话快捷方式。没有流程所有权边界变化。权限控制工具可以在另一个对话正在加载或可见时完成。全局破坏性选项卡集要么在被清除之前闪烁打开,要么丢失原始对话的工具;会话键控渲染器状态可保持连续性,而不会允许后台工作窃取焦点或使瞬态资源持久。
D154工作面板活动导轨和资源切换器一旦工件打开工作面板,44px 活动栏就会将“审阅”、“终端”和“浏览器”显示为一键式 32px 工具按钮;打开丢失的工具仍使用 D128 的 openWorkPanelTab 路径。 46px 内容标题显示并关闭活动资源,并为每个打开的 tool/file 资源公开一个有界的键盘可操作切换器。面板最小值变为 364 像素,因此导轨保留了之前的 320 像素内容地板(宽度夹被 D167 取代)*。这取代了水平滚动的顶部选项卡和隐藏的空标题上下文菜单,同时保留了 D128 的工件驱动面板条目、资源 deduplication/order、近邻行为和会话范围的运行时所有权。*高频工具应该是可见的并且空间稳定,而长或大量的文件名需要一个带标签的溢出表面,而不是将每个操作压缩到一个标题栏行中。
D157每个用户轮一次视觉助理轮由 thinking/tool 活动分隔的提供商级助理消息仍然是不同的规范转录记录,但 ChatTranscript 将一条用户消息之后和下一条消息之前的所有记录组成一个 role=article 助理轮次。有序的降价片段和活动披露仍然可见;只有组合回合拥有尾随聚合 model/usage 行和 Copy/Fork/Retry 工具栏。复制用段落分隔符连接所有内容丰富的片段; Fork 和 Retry 使用最后一个内容丰富的辅助记录作为其持久边界。具有工具功能的提供商会关闭并重新打开每次工具调用时的辅助消息。将这些传输边界渲染为单独的响应会重复操作工具栏,并使一个代理运行看起来像许多 AI 回复。
D162最新胜出的缓存会话切换渲染器立即标记最新选定的行,合并会话详细信息 prefetch/load 工作,保留 LRU 样式的五转录本内存缓存,并开始最新的转录本读取,而无需等待被取代的读取。工作区对齐可能与转录本 IO 重叠;导航世代门控原子可见提交。 ChatSurface 保持先前的完整视图为非交互式,而 React 推迟更改的会话树,然后在其最终记录处绘制目的地。缓存的快照始终会重新验证。以前的全局选择承诺队列使每次点击都在过时的完整转录 JSONL 读取后面等待,而一次同步长 Markdown 提交会延迟可见反馈。最新获胜的 IO 加上稳定的延迟帧与 Codex 风格的导航相匹配,而不会削弱 session/workspace 隔离。
D156独立的本机窗口和工作面板调整所有权大小(responsive-clamp 和 no-native-reservation 子句被 D163 取代) Electron Main 独家拥有最小 1040x700 的 BrowserWindow 边界,而渲染器拥有持久的首选工作面板宽度。分隔线使用锚定、帧合并预览和发布时提交;转义、指针取消和丢失捕获回滚。本机窗口边缘大小调整永远不会重写面板首选项。这取代了 D083 的恢复最小值并删除了 window/resizeBy 增量通道和调整大小属性启发式。循环所有权使分隔符释放第二次调整操作系统窗口大小,引入异步竞争和平台差异,将窗口移动到显示边缘附近,并在不相关的本机调整大小期间重写面板首选项。
D163固定工作面板的原始宽度预留(由 D167 取代的宽度范围) **开放式对接面板在 364..720 中保留一个承诺的固定宽度;本机 window/sidebar 更改永远不会限制它。 Renderer 通过幂等`window/setWorkPanelReservation({width: 0364..720}) -> {请求,保留}: open requests the committed width, collapse/final close requests zero, and divider commit updates it. In normal state Main adds available native work-area width and reverses it symmetrically, so chat stays stable when the full target fits; otherwise the panel remains fixed and chat absorbs 请求 - 保留`。本机边缘仅调整聊天大小。 Maximized/fullscreen 请求推迟到正常; display/work-area 更改可根据当前可用宽度协调目标,而无需在普通的同一显示移动期间重新应用它。持久基址边界不包括预留宽度及其 x 偏移,并且背景伪影无法更改可见预留。这取代了 ADR 0029/D156 子句,这些子句将面板固定在当前客户端宽度内或禁止面板驱动的目标几何图形,同时保留主边界所有权和分隔符手势规则 (ADR 0032)。**
D167更窄的默认工作面板宽度停靠的工作面板以 280px 承诺默认值打开(比 D163 的 420px 窄三分之一)并固定到 244..720px;渲染器常量、Electron 预留验证器和 .work-panel CSS 底层共享这些边界。双击分隔线可恢复 280 像素。由于工具位于标题切换器中,D154 的 364px 底面背后的 44px-rail-plus-320px-content 基本原理不再适用,因此底面按默认比例缩放。持续更宽的宽度仍然有效,并且在升级时保持不变;最大 720 像素、分隔符手势规则和惰性保留行为 (ADR 0033) 保持不变。该面板的打开范围比大多数 review/terminal/file 内容需求更宽,从固定客户区域内的聊天中获取可读宽度,而旧的 364px 地板使得较窄的默认值无法到达。
D255工作面板可见期间恢复原生预留工作面板仍然是一个在流内、固定宽度的渲染器列,但在展示它之前先通过 window/setWorkPanelReservation 请求它已提交的宽度。退出期间保持面板挂载,等预留请求成功之后再请求零宽并卸载。因此在显示工作区域允许时,原生窗口会增长以在面板可见期间保住 MainChat 的宽度,并在面板折叠或关闭时对称地收回到基准边界。受限的工作区域保留既有的封顶预留和聊天区不足量;持久化的边界不含临时预留几何。这取代了 ADR 0033 的「不做原生扩展」决定,但不改变 IPC 形状、浏览器测量边界路径、Main 拥有的边缘拖拽,或会话范围的面板状态。固定窗口的停靠方式在用户折叠面板之后让应用窗口仍然更宽,导致 MainChat 占用了让出的右列宽度,而不是回到只有聊天时的窗口边界。
D256Windows/Linux 窗口控件走原生任务栏最小化修订 D216 / D252 与 ADR 0078 / ADR 0117:Windows/Linux 的渲染器与原生菜单 minimize 操作调用 Electron 的原生最小化过渡,且它们的 minimize 事件不再转换为 hide(),因此任务栏条目保持可用。macOS 的原生最小化仍然驻留托盘。Windows/Linux 的关闭行为仍遵循 D230 / ADR 0090:关闭按钮仅在 tray 时隐藏到托盘,在 quit 时退出。不改动 IPC 动作、存储、主机协议或后台进程约定。自定义的右上角最小化按钮使用了与「关闭到托盘」相同的托盘隐藏效果,即使关闭行为本来就可配置,用户仍然失去了正常的任务栏恢复路径。
D258记录布局索引、统一的窗口坐标系与身份截断细化 ADR 0120:host-core 为每个会话维护一份记录布局(每条消息和压缩行的字节偏移,以及这些偏移所基于的 file_len),并通过定位到所选的第一行来提供有界窗口,因此打开会话的代价是窗口而不是整段历史。该布局是派生数据的内存缓存:增长只扫描尾部,收缩或替换则重扫,任何重写/删除路径都会丢弃条目;被截断的尾行同时排除在偏移和 file_len 之外。行的分类依靠一次感知深度的扫描来找顶层 type 键而不是解析整行,因此开放式工具结果或检查点细节里嵌套的 type 无法决定行类型;type 现在写在最前面,扫描通常在第一个键就停止,而把它写在负载之后的旧行由同一次扫描读取。读窗口是物理消息行位置,按布局而不是按 last_seq 夹取——只要某次追加的索引提交丢失,去重后的计数就会永久低于文件实际内容,而这一直在把最新消息从尾部切掉。压缩链仍然随任意窗口整体返回。agent/prompt 新增 truncateFromMessageId,由主机对自己的记录解析、未知时以 NOT_FOUND 拒绝,取代了把文件偏移与渲染器数组下标混在一起的 messageStart + userIndex 求和。记录一个分叉出的子会话(列表行、缓存记录、检查点标记)变为无条件执行,只有可见的切换仍受 D139 的导航意图约束。ADR 0120 限定了负载大小,但仍然留下一次完整的顺序扫描来定位每一页,因此长会话打开依然缓慢并随增长而恶化;而两处坐标系混淆一直在静默丢失最新消息,并在分页记录上截断了错误的回合(ADR 0127)
D259瞬时提供商故障的有界共享预算修订 D186 / D245 与 ADR 0050 / ADR 0091:非 429 的瞬时提供商故障共享一份有界的逻辑回合预算,即首次尝试之后四次重试、总计五次提供商尝试,由请求建立与流传输共享。该预算只接纳 NETWORK_ERROR、TIMEOUT、STREAM_FAILED 和可重试的 PROVIDER_ERROR;PROVIDER_ERROR 现在在流阶段也会重试而不只是建立阶段,因此上游网关的 502/503/504 无论落在哪里都能恢复。非 429 的延迟依次遵循 retry-after-ms、retry-after 秒数、HTTP 日期,然后走确定性的 1s/2s/4s/8s 时间表,上限 8 秒;任何能声明延迟的状态(429、408、409、5xx)都会保留捕获到的头部。只重放失败的那次请求;会话、记录和已完成的工具调用不受影响。主会话、内置子代理和一次性输入框增强共享同一套错误码、预算大小与优先级。429 的五次重试预算保持独立,两者互不占用。耗尽时记录 retryAttempt: 4。不改动 IPC、存储、主机协议或提供商配置。一个中继返回 OpenAI API error (502): {"type":"api_error","message":"Upstream API request failed."} 说明的是上游短暂中断,但按阶段各自一次重试的策略把第二个 502 变成了终止性错误卡,而且完全不会重放流中的 502,子代理则拒绝任何非请求阶段的瞬时重试。一份共享的有界预算能从普通的网关抖动中恢复,同时不隐藏持续性中断。
D164双区域设置应用内产品变更日志应用程序更新的产品“新增功能”文本作为 packages/shared (CHANGELOG) 中的双 EN/zh-CN 目录进行维护。 Electron 使用产品 UI 区域设置对发现的 availableVersion 进行主要格式注释,并将其作为可选 UpdateState.releaseNotes 纯文本附加到现有更新 IPC/event 路径上。当注释存在时,环境横幅和“设置”→“信息更新”行会显示一个紧凑的“新增功能”部分。英语是真理的源泉; zh-CN 镜像版本和突出显示计数。 GitHub 自动生成的发布主体仍然仅限于网络,而不是应用程序内源。未引入新的提要 URL、注释通道或渲染器拥有的远程获取(扩展 D120 / ADR 0022)。用户在更新时需要双语发布亮点,而无需第二个网络表面或削弱 Main 对更新交付的唯一所有权。
D357视口固定的工作面板开关修订 D128 / D207 / D221 与 ADR 0068 / ADR 0085:AppShell 在每个非设置路由右上角拥有一个视口固定开关,等价于 Cmd/Ctrl+J:显示活动会话保留的面板上下文、收起可见面板、无会话时禁用。它是唯一的面板级收起控件。Windows/Linux 窗口控件保持视口固定。不改主机协议或 IPC。见 ADR 0195 与 E2E-056。仅快捷键入口让指针用户发现不了面板。

L. 成绩单呈现决策 ​

身份证号主题决定基本原理
D101受 WorkBuddy 启发的转录本密度用户将渲染为紧凑的右对齐软板(min(78%, 560px),微妙的边框+细线阴影)。助手将保持透明全角散文(最大 720 像素)。消息行垂直内边距收紧至 10 像素。 Hover/focus-within 在每圈下显示安静的复制芯片(用户右对齐,助理左对齐)。流媒体助理的答案使用左重音规则。还没有吉祥物、反应或成本芯片 UI。与 WorkBuddy 的任务聊天相比,当前的右对齐用户气泡规格不足且视觉上稀疏;更密集的板提高了可扫描性,同时又不放弃 Codex/developer 约束
D103每条消息模型+令牌元和重试完成的助手在答案下转动表面 modelId + 令牌使用芯片(仅限令牌;悬停细分为 input/output/cache/reasoning)。用法附加在来自 pi-ai 用法的运行时 message_end 上,保留在消息 meta_json 中,并随记录一起重新加载。操作行添加了重试,这会重新发送最近的前面的用户提示。没有货币定价,也没有 like/dislike。WorkBuddy 每条消息元数据改进了 trust/scanability;代币总数已从提供商流出,而定价成本仍需要目录
D105辅助回合的就地再生重新生成将会话记录截断为最近的前面的用户提示(不包括该提示及其后的所有内容),处置会话的实时 pi-agent,并重新发送提示,以便新的 assistant/tool 尾部替换丢弃的分支,而不是堆叠重复的回合。用户期望regenerate重写当前回合;仅附加重试污染了长会话并在重做之上留下了陈旧的答案
D106在转录中保留用户硬换行符用户气泡将纯文本与硬换行符完好无损地呈现。 Composer 仅修剪 leading/trailing 空白;转录本使用 message-user-text 和 white-space: pre-wrap(无强制中间字形断字),因此多行提示永远不会折叠成一个段落。复制和会话重新加载保留原始换行符。多行提示(代码片段、列表、粘贴块)在编码代理中很常见;折叠换行符使得文本难以重新阅读和重新编辑
D107配置提供商工作室**设置 → Agent 使用提供商工作室:摘要英雄、分段默认值、可折叠的 OpenAI 兼容编辑器以及带有秘密徽章、思维预设、测试连接和 make-default/delete 操作的基于卡片的提供商管理。这改进了紧凑设置目录中的提供商演示,而无需添加铁路目的地。 **密集堆叠的表单和狭窄的列表行使得多提供商设置难以扫描并且过度强调次要字段
D110模型配置标签+添加提供商对话框代理目标的设置导轨标签是模型配置(zh-CN:模型配置)。添加提供程序会打开模式对话框,而不是内联可折叠编辑器;目标 ID 仍为 agent。阐明该选项卡的模型设置目的并减少编辑提供商凭据时的页面流失
D108仅在溢出时对话小地图仅当存在至少两条可见的 user/assistant 消息且副本溢出一个视口 (scrollHeight > clientHeight) 时,才会呈现左侧边缘对话小地图轨道。短的一页螺纹隐藏了导轨;流增长、内容大小调整和窗口大小调整重新评估可见性。当每条消息都已经适合屏幕时,导航栏就是噪音。溢出是 jump/preview 导航有用的信号
D109ChatGPT 风格重新生成修订历史记录在 message_revisions(架构 v4)中的稳定 revisionRootId 系列下重新生成已丢弃的 assistant/tool 尾部的存档。实时 root 用户轮流携带 revisionCount / activeRevision 和安静的 current / total 寻呼机在适当位置切换线性变体。首先重新生成将原始分支存储为修订版 1;稍后重新生成追加新分支并标记最新的活动分支。没有自由形状的分支树。 (由 D307 修订)用户希望重新生成能够像 ChatGPT 一样保持先前的答案可达; D105 就地重写单独删除了对比较仍然有用的历史记录
D111空的主页滚动堆栈(由 D131 取代的特定于卡片的条款) 空聊天主页是 home-main-content 内的单个可滚动垂直堆栈。短窗口顶部对齐和滚动;内容保留在文档流中,并且家庭输入框保持非停靠状态。双增长+绝对门户让家庭输入框在较短的窗户上重叠引导;流布局保留每个剩余的块而不会发生冲突
D112工作面板旁边的可读聊天(动态宽度夹被 D163 取代;欢迎选择器被 D128 取代) MainChat 在面板旁边有一个 360 像素的可读目标。只要显示工作区域可以提供完整的承诺面板宽度,D163 就会通过本机宽度保留来保留它;否则,MainChat 会吸收不可避免的不足,同时面板保持固定。仅面板宽度上限可能会在支持的窗口尺寸下为聊天留下大约 109-205 像素;本机保留现在可以保留聊天而不压缩工具表面。
D113受 WorkBuddy 启发的本地个人资料页脚展开的侧边栏以透明的 58 像素页脚结束。其 44 像素配置文件触发器包含 30 像素圆形本地用户字形、两行 Custom + Local profile / 本地配置 标识和 V 形;单独的 32px 帮助快捷方式可打开“设置”→“信息”。 280 像素的配置文件菜单在页脚上方 8 像素处打开,带有重复的身份标题、分隔线和设置/日志/主题操作,保留退出、外部单击、箭头键和焦点恢复行为。这取代了 D041;不暗示云帐户、通知、共享或更新功能。将 WorkBuddy 的头像和操作页脚语法调整为 PI-Desktop 真实的仅限本地功能,同时改进身份层次结构并消除陈旧的云替代
D137仅字形消息工具栏; edit 表示编辑提示消息工具栏仅包含图标:标签位于 CSS hover/focus 工具提示 (data-tip) 和 aria-label 中,从不作为可见的芯片标题(文字按钮仅保留在错误表面上)。编辑将助理答案移至用户回合:它在内联文本区域中打开提示(斜杠将键入键入的 command 表单的种子,以便重新发送重新扩展模板),并在同一会话中使用新文本保存重播 D105/D109 重新生成路径 — 替换的提示及其整个答案尾部被存档为修订版本,因此现有的 current / total 寻呼机会返回到原创。编辑助手自己的文本及其 fork-into-a-child-session 变体 (D134) 被删除;分叉仍作为显式分歧操作。每个答案下面有四个字片,读起来就像一个句子,挤满了文字记录;有用的更正几乎总是“我问错了”,用户希望在历史记录完好无损的情况下重新运行(ChatGPT 语义),而不是在新会话中手动编辑模型的单词
D165助理答案中的安全懒人美人鱼图助理答案散文中的完整 mermaid 栅栏在进入近视口带后呈现为主题感知的 SVG。部分溪流围栏和所有思考散文都保留源代码。渲染器动态加载官方 Mermaid,序列化其全局主题渲染,将源字符上限限制为 20,000 个字符,将边缘限制为 500 个字符,锁定 strict/no-HTML/no-link 配置,并应用第二个 SVG 配置文件清理程序。无效或过大的图表会退回到可见的可复制源;图表工具栏切换源并复制它。图表改进了架构和流程解释,但解析部分流或每个屏幕外的历史栅栏会破坏直接流和快速会话切换行为。严格有界本地渲染增加了无需新协议、网络或 Electron 特权边界的功能。

| D261 | 挂载的记录是一个窗口,而不是已加载的历史 | 在渲染器中细化 ADR 0120 / D258:挂载的历史是已加载历史上的一个尾部窗口——会话切换后的首次提交挂载 15 行,稳定态 60 行,每次增长 40 行。到达顶部分两级升级:窗口仍是局部时先扩大窗口,只有当窗口覆盖了全部已加载历史时才调用 loadOlder。窗口增长与取回一页使用同一个绘制前滚动锚点,因为两者都在阅读位置之上增加高度。窗口按会话重置并夹到已加载历史,因此上一个会话留下的过期预算不会导致过量挂载。水合占位仍然只作用于首次提交。会话缩略图由已挂载条目构建,而不是由每条已加载消息构建。不改动 IPC、存储、主机协议或分页。 | ADR 0120 限定了跨越 IPC 边界的数据量,D258 限定了定位一页的代价,但渲染器仍然挂载它曾经翻到过的每一行,并在整个会话生命周期内保持挂载。content-visibility: auto 会跳过这些行的布局和绘制,但保留它们的 React 树、Markdown AST 和 Shiki 令牌数组——在低内存的 Windows 机器上这正是不该占用的资源,报告的症状就是长会话中聊天区越来越不流畅。窗口化同时限定了保留内存和每帧的协调开销;而把完整集合喂给缩略图会画出点击目标已不存在的刻度。 | | D317 | 实时/持久化记录合并保持时间顺序 | 在渲染器中修订 ADR 0120 / D261 / ADR 0137:mergeLiveSessionMessages 把有界的持久化 session.get 页按时间顺序缝到实时快照上。早于该页的实时行留在它前面;乐观用户行或进行中的助手/工具尾巴留在它后面。重叠的已完成 id 仍以持久化行为准。实时独有的行绝不能追加到持久化页之后。若更早的前缀曾被错误追加到页后,当其 createdAt 早于该页时把它治回前面。仅渲染器:不改动 IPC、存储、主机协议或分页。 (由 D324 修订) | 一直开着的会话会在最新 100 条页之外再积累数百行实时记录。再验证曾把持久化页当作数组前缀、把其余行追加在后面,于是 D261 的尾部挂载窗口画出旧历史,刚发送的提示消失,回合却仍在跑。中止后再空闲加载只显示持久化页,所以看起来像是点了停止再切换,消息才回来。 | | D324 | 空闲切换保留尚未刷入的已完成尾巴 | 在渲染器中修订 D317 / ADR 0137:selectSession 在会话仍在运行或仍有实时来源(liveSessionTranscripts)时,把有界持久化页缝到实时快照上。持久化页尚未包含的已完成助手/工具行予以保留。只有当该页已包含每一个实时 id 时才清掉实时来源,而不是回合一结束就清。仅渲染器:不改动 IPC、存储、主机协议或分页。 | 用户提示在模型运行前就落盘;助手/工具行走异步 outbox,在 message_end 之后才写入。agent_end 之后的空闲再验证只用持久化页,会丢掉已经显示在屏幕上的回复(issue #41)。 | | D327 | 已完成回复在进程重启后仍然存在 | 修订 D299 / ADR 0153 / ADR 0041:message_end 的完成快照在 outbox 追加之前先做检查点;completed/error 的 session.endTurn 仅在该 id 已索引时才删除 .inflight.json;启动恢复跳过已完成残留以便 outbox 先追加;握手等待排空后再用 session.recoverInflightMessages 把剩下的提升为 complete。附加 RPC,不改协议或 schema 版本。 | 与 issue #41 相同的「只剩用户消息」画面,但发生在完全退出之后(issue #42):用户行在模型运行前就落盘,助手/工具行停在 outbox,D324 的实时缝合无法跨进程。 | | D444 | 跨会话消息 id 碰撞时改写;outbox 的 UNIQUE 不再停整队 | 修订 ADR 0041 / D327:session.appendMessage 仍把本会话已索引的 id 当作无操作(或用终态助手替换流式行)。若该全局唯一 messages.id 已属于另一会话,主机在写 JSONL 或索引之前改写为 {sessionId}:{id},之后重放原始 id 也对改写后的行无操作。Electron 持久化 outbox 把 UNIQUE constraint failed: messages.id 当作幂等确认并继续排空,因此一个撞车的 toolCallId 不会卡住之后所有助手/工具行,也不会把 1024 上限填满。不改协议或 schema 版本。 | 工具行曾把 toolCallId(如 call_421522)当作 messages.id,这些 id 并不全局唯一。碰撞会在 JSONL 写完后 SQLite 失败,FIFO outbox 停在队头,所有会话的后续行都无法落盘,退出后再开就像前期历史丢了(issue #523)。 | | D597 | 丢弃 outbox 毒消息;接受投递回合内的 steering | 修订 ADR 0041 / ADR 0239 / ADR active-turn-steering / D444:Electron 持久化 outbox 把消息正文带 PERMISSION_DENIED: 前缀的追加当作永久拒绝,丢掉该行并继续排空。PLUGIN_PERMISSION_DENIED 和其他失败仍暂停。宿主 provenance 在目标会话一致时接受已认领协作投递回合上的 UiMessage.steering,不盖投递来源,并清掉客户端带来的 session_message。跨会话 steering 仍是 PERMISSION_DENIED。不改协议或 schema 版本。 | 向 session-collaboration 投递回合按 Alt+Enter 会被当成投递不匹配拒绝,outbox 永远重试队头,打满 1024 后丢掉后面的助手/工具行,转录只剩用户消息。 | | D328 | 由父级判定的子智能体寿命 | 修订 ADR 0089 / ADR 0119 / ADR 0129:不武装空闲和时长看门狗;父级 agent_end 不中止仍在跑的委托;运行时保持持久回合打开,完成时把报告交给父级。TaskWait 超时和 TaskList 返回心跳。只有 TaskStop 和用户 Stop 会中止委托。显式 maxTurns 和并发上限 10 保留。运行时专用,不改 IPC、存储或主机协议。 | 父级看不到实时委托工作,把 TaskWait 到期当成可以收工;等待是事件循环,模型不是。 | | D352 | 父级终态错误中止残留委托 | 修订 D328 / ADR 0166:父级空闲仍不中止委托。终端父级 provider/stream 错误(含耗尽的 HTTP 429)、溢出恢复失败、变更预算终止或拒绝的提示会中止残留委托、跳过 D328 续跑提示并发出 agent_end。isRunning 在该错误后不计残留委托,因此继续不会变成 AGENT_BUSY。见 ADR 0189 与 E2E-155。 | 父级 429 后 UI 显示继续,子智能体仍占 sidecar,下一条提示变成 session already has an active turn。 | | D354 | 同时标注两个 macOS 发布架构 | 修订 D353 / ADR 0145:两条原生 macOS 发布通道都使用带架构后缀的 electron-builder 模式。公开资产为 PI-Desktop-<version>-arm64.dmg / -arm64-mac.zip 以及 -x64.dmg / -x64-mac.zip。更新源 URL 与校验和保持一致。只改发布资产命名。 | arm64 资产原先不标明架构,x64 又用另一套 Intel 约定。 | | D353 | 区分 Intel macOS 发布工件 | 修订 D285 / ADR 0145:原生 macOS x64 发布通道使用目标特定 electron-builder 模式,公开资产为 PI-Desktop-<version>-Intel.dmg 和 PI-Desktop-<version>-Intel-mac.zip。生成的 latest-mac-x64.yml 保留这些 URL 与校验和。arm64 通道仍用通用版本名。只改发布资产命名。 | 两个架构都用通用版本名时,用户无法可靠区分 Intel 下载。 | | D329 | 由智能体选择的 Bash 超时 | 修订 D190 / D273 / ADR 0054:Bash 默认仍是 60 秒。显式 timeout 为 1 到 21600 秒。超过 21600 的值按毫秒读取、换算并钳到 21600 秒。范围内的 600 和 1800 仍是秒。超时/中止仍杀掉整棵进程树。 | 300 秒上限会杀掉合法构建,并拒绝 timeout: 600 / 1800。 | | D331 | 由宿主拥有的已完成回合 token 历史 | 修订 D103:父级 message.usage 仍是该助手消息的 pi-ai Usage。子智能体花费只累计在持久回合上。Electron 把父级 message_end 用量加上 turn_end.subagentUsage 写入 session.endTurn.usage。stats.getTokenUsageHistory 按本地日历窗口汇总。用户可见仪表盘是插件 pi.token-insights(D335)。 | turns 表已是用量汇总,但 Electron 从未发送 usage,把子智能体 token 并进消息芯片会误导上下文检查器。 | | D335 | Token 用量仪表盘由插件拥有 | 修订 D331 / ADR 0171:设置没有「用量」目的地。全局 token 历史由市场插件 pi.token-insights 展示。宿主仍持久化 session.endTurn.usage 并暴露 stats.getTokenUsageHistory。插件可以把宿主已完成回合中超出转录本助手 meta.usage 的差额(含子智能体)折入 PI-Desktop 事实立方,且不改写 message.usage。不升协议或 schema。 | 设置页重复了插件已有的更弱热力图。见 ADR 0173 与 E2E-186。 | | D326 | 撤回 A2A 与 Peer 协作栈 | 移除 Agent2Agent 代理、a2a.* RPC 域、残留 SubagentMailbox / Peer 工具以及父/委托 A2A 工具。并发委托只通过父级 Task* 报告协作。握手 PROTOCOL_VERSION 10→11,存储 SCHEMA_VERSION 12→13。ADR 0165 取代 0147 / 0162 / 0164。 | 兄弟通道和父到父通道与 Task* 并列却没有产品需求,模型也容易误用。 | | D318 | 从记录文件 / outbox 恢复缺失的 sessions 行 | 在 host-core 与 Electron 主进程中修订 ADR 0041 / D119:活着的 sessions/<id>.jsonl 若 SQLite sessions 行已不在,则在主机启动时(recover_orphaned_sessions)以及 session.appendMessage 时恢复。恢复会重新插入该行并根据文件重建搜索索引,使追加保持幂等。若行和文件都不在,append 会用同一个 id 插入占位行,以便排队的 outbox 能排空。session.delete 丢掉该会话的 outbox 条目,以免占位重建把用户已删除的对话救回来。不改协议版本、schema 或渲染器。 | 长会话可能丢掉 sessions 行(WAL/索引丢失),回合却仍堆在 session-message-outbox.json。appendMessage 于是因 session not found 失败,outbox 卡在队头,侧边栏列不出会话,聊天只剩第一条已刷入的用户行。把该行插回去后 outbox 才能排空(issue #39)。 | | D262 | 大段 Composer 文本粘贴写入会话临时目录 | 不超过持久化 largePasteThreshold 的纯文本 Composer 粘贴仍使用原生文本区域输入;默认值为 600 个字符,有效值为 1 至 1,000,000 的整数。超过阈值时,渲染器通过现有会话粘贴桥发送准确的 UTF-8 text/plain 字节,Electron 将其保存到 <data_dir>/scratch/<sessionId>/pasted/,Composer 在原始选择处插入生成的 @<temporary-name> 标记。渲染器保留会话范围内的标记到规范路径映射,在发送前只解析一次,并排除重复附件/回退序列化。现有剪贴板文件/图像芯片和桥接边界不变;不修改工作区、工件存储、主机协议或 schema。 | 超大的原生粘贴难以编辑且会压迫输入框;现有会话临时目录已经提供有界、隔离的存储和规范路径语义,不会弄脏项目(ADR 0131)。** | | D265 | 单个委派也读作一张卡片,与扇出一致 | 在渲染层修订 D201 / ADR 0062:活动分组里的每个 Task 调用都渲染为整宽委派卡片——主智能体根节点、连接线、每个委派一个节点——单个委派同样如此,不再退回紧凑工具行。聚合标头按数量措辞,因此单个委派不会被说成复数:英文新增 _one 形式,中文只有一个复数类别,故去掉英文复数标记。卡片的其余部分保持不变,不涉及 IPC、存储、主机协议或委派运行时改动。 | 把卡片保留给两个及以上委派,会让同一件事看起来像两个不同功能,而单个这一常见情形恰恰丢失了结果、记录的运行时长和委派的步骤数(ADR 0062 §6)。 | | D319 | 委托卡只承载 Task 扇出 | 修订 D201 / D265 / ADR 0062:连续 Task 启动自成活动组,并且是委托卡内仅有的行。父级思考、工作区工具和生命周期行(TaskWait/TaskList/TaskStop)留在普通处理组,不进拓扑卡片。渲染器专用。 | 连续思考和工具行曾共用一个活动组,父级在 Task 之后的工作会被画进子智能体瓷砖。 | | D320 | 已发送的文件引用保持芯片并可点击打开 | 在渲染器和 Electron 主进程中修订 D209 / ADR 0070:已发送的用户回合解析序列化的 @path / @"带空格的路径" 标记,并把每一项画成与输入框一致的叶子名芯片(图标、省略号文件名、工具提示/无障碍名中的规范路径)。工作区内的 .html/.htm 芯片打开工作面板浏览器;其余允许的文件通过 pi-desktop/fs/open 用系统默认应用打开。该通道把相对路径限制在工作区,把绝对路径限制在工作区、<data_dir>/scratch/ 或 <data_dir>/attachments/。HTTP(S) URL 仍是文本链接。工具行预览不变。不改主机协议或存储 schema。 | 序列化后的完整路径一发送就把输入框里的紧凑节点打散了,而且文件页既不能把 HTML 当页面预览,也不能打开按后缀走系统默认应用的文件(ADR 0163)。 | | D322 | 相对转录路径可预览并打开 | 修订聊天/markdown 文件链接解析:无前缀相对路径仍以工作区为根;./ 和 ../ 在聊天中相对工作区根,预览 markdown 时相对被查看文件的目录。走出工作区的路径保持惰性。助手 markdown 会把带已知扩展名的裸文件词链接化。不改主机协议、IPC 或存储 schema。 | 智能体写工作区相对路径,markdown 文件用 ./ / ../ 链接;这些要么不可点,要么被当成工作区根下的路径。 | | D323 | 进行中的父级回合保持透明 | 修订 D101 / D297:流式助手回合不坐在 --ds-tile 上,也不预留 14px 内边或左侧栏。思考、工作区工具和回答片段像已完成回合一样铺在页面背景上。瓷砖只属于子智能体/委托卡。 | 流式父级回合成了整块瓷砖,和子智能体卡抢视觉层级。 | | D268 | 生命周期行就是子智能体行 | 在渲染层细化 D201 / D265 / ADR 0062 与 ADR 0089:委派生命周期行(TaskWait/TaskList/TaskStop)仍是紧凑工具行,仍不计入拓扑数量,但呈现为子智能体行。它们从运行时返回的名册生成摘要——子智能体名称读自 details.delegations[] 或 details.stopped[],重复者以计数而非列两遍呈现——绝不使用自身的 delegationIds 参数;徽标沿用既有 chat.subagentStatus.* 文案汇总该名册;标签说明对子智能体采取的动作,而不是“已委派”;正文是名册构成的命名表格,前置合并后的报告,而不是 delegations[] 原始 JSON。不涉及 IPC、存储、主机协议或委派运行时改动。 | 生命周期行承载着 Task 卡片无法知道的最终结果(ADR 0089),却渲染为通用工具调用:摘要是一串裸 UUID,正文是 JSON 转储——在一个卡片本应最易读的功能里,它成了最难读的部分。读取它本就返回的名册没有额外成本,却能让整个委派作为一件事被扫读。 | | D263 | 跨显示器拖动是用户意图,而非系统调整 | 工作面板保留的协调不再把所有显示器变化视为同一种情况。os-adjusted(窗口管理器重新适配我们请求的边界,或显示器拓扑发生变化)保持 ADR 0122 的行为不变;user-moved(用户把窗口拖到另一块显示器)以当前位置作为新的基边界,仅将原点规范化进目标工作区域,尺寸保持不变。归属依据未被消费的本机 move 流而非时间期限,协调推迟到移动流稳定之后,因此拖动过程中不会重排边界。跨显示器拖动会推进记住的显示器键并被持久化。不改动 IPC、存储或主机协议。 | 旧逻辑复用原显示器的基边界,再被目标显示器工作区域夹到边缘,导致松开鼠标后窗口跳动,并把过期坐标写入 window-state.json(issue #18、ADR 0132)。** | | D269 | 历史续接让会话大纲保持可达 | 在渲染器中修订 D108 / D261 / ADR 0130:当已加载的行被扣在挂载窗口之上,或主机报告还有更早的一页时,会话大纲保持存在并带一条虚线的「更早历史」续接项,即使挂载的尾部标记少于两个或并未溢出。该续接项走同一条先扩窗再取页的两级路径,普通消息刻度仍然只来自已挂载的行,因此每个消息刻度都有真实的 DOM 目标。记录顶部的加载边界既在滚动时检查也被观察;在尾部填充不足、取回但被扣住的一页或窗口过渡之后,只要该边界仍然可见,历史就会继续推进而不必等待原生滚动事件。一旦不再有更早的历史,D108 的「两个标记加溢出」可见性规则原样生效。不改动 IPC、存储、主机协议或分页。 | 有界的尾部可能只包含一个工具密集的回合而塌缩到不足一屏,而取回更早的一页可能让所有新行都落在尾部挂载窗口之外。这两种过渡都不一定改变 scrollTop,因此只依赖滚动的触发器把更早的历史搁在了那里,也让大纲整体消失。一条显式的续接项诚实地表达了尚不可用的导航,而边界可见性在不编造幽灵消息刻度、也不急切加载整段记录的前提下闭合了推进循环。 | | D270 | 两种凭据共用一个模型选择器 | 在渲染层细化 D237 / D240:模型选择器是一个共享渲染组件,由 AI 服务对话框与厂商账户对话框共同渲染。因此厂商账户获得同一份已发现模型列表、同一种 ModelBinding 形状、同样的按模型上下文窗口与输出上限编辑、同样的已发布思考等级按钮,以及保存时同样把每个绑定收敛到所解析 models.dev 记录已发布等级的处理。两个对话框之间只有左栏标题不同。仅渲染层:不涉及 IPC、存储、主机协议或提供商配置架构改动,也不需要 ADR。 | 这两处界面本是同一个选择器的两份拷贝,而拷贝那一份悄悄丢掉了高级设置与收敛处理,于是厂商账户的绑定可能保留运行时会丢弃的思考等级,而对话框却仍把它算作已启用。用一个组件让这项保证成为结构性的,而不是两个文件都得记住的约定。 | | D271 | 展开的委托运行过程在原处滚动 | 在渲染层细化 D201 / D268 / ADR 0062:委托的嵌套行渲染在高度受限的 .subagent-run-rows 滚动区内,高度 min(420px, 48dvh),并设 overscroll-behavior-y: contain;滚动挂在该内层容器而非 .subagent-run 上,以免裁掉绝对定位的折叠导轨。运行标题留在滚动区之外,该区域为带标签、可获焦的分组。fields 表格获得其他细节块早已具备的 260px 上限;生命周期行合并后的报告渲染为受限的 output 块,而不是无上限的提示块。仅涉及呈现:不改动 IPC、存储、主机协议或运行时。 | 展开一个委派可能在一次提交中新增数十行——执行了四十次工具调用的委托会让转录长出四十行——阅读位置因此跳动,父级自己的下一行被顶出视野。fields 是唯一没有高度上限的细节块,而 D268 把最多 50k 字符的合并报告放进了同样没有上限的 note。 | | D302 | 展开的委托运行过程跟随最新输出 | 在渲染层细化 D271 / D071:有界的 .subagent-run-rows 滚动区独立使用父级转录的吸底跟随契约。展开时钉在最新一行;钉住时内容增长跟随底部;第一次真正的向上手势(滚轮 / 触控板 / 触摸 / 滚动条 / 键盘)暂停跟随并显示嵌套的「回到最新」控件;布局夹持和程序性 scrollTo 不会解除跟随。嵌套滚动区关闭原生 overflow anchoring。跳转控件叠在 .subagent-run-rows 外层的相对定位包装上,绝不放在 .subagent-run 上,因此折叠导轨不会被裁掉。仅涉及呈现:不改动 IPC、存储、主机协议或运行时,也不需要 ADR。 | D271 限制了嵌套运行过程以免撑高转录,但没有给该滚动区吸底。实时委托的新思考、工具和回答行因此追加在已展开卡片的视口下方,用户必须手动往下滚才能看到进展(issue #37)。 | | D272 | 退出时插件按关闭停止,而不是按崩溃 | 在主进程细化规格 07 §3.1:before-quit 调用 PluginRuntime.disposeAll() 而不是 disposeWatchers()。它先把每个已加载插件标记为正在释放并取消其待处理的重启,释放监听,然后并行停止服务并运行 onUnload——每个插件 1.5s,整体 3s,超时后杀掉子进程。onPluginCrash 不再自己发提示,因为运行时在同一条路径上已经发过一次。仅主进程:不涉及 IPC、存储、主机协议或清单改动,因此不需要 ADR。 | 退出把插件子进程留给随后的进程拆除去杀,而一个没有被标记的退出与崩溃无法区分:handleChildExit 的 disposing 守卫从未生效,于是每次退出都按插件记一条 exitCode: 0 的 PLUGIN_CRASHED、弹两次"意外停止"提示,并把重启排进正在关闭的应用。某个本地日志里 633 条崩溃上报有 613 条属于此类,真正的崩溃因此难以看见。 | | D273 | 模型习惯的参数名被接受,而不是被纠正 | 在 Agent 运行时细化规格 03 §4:Read/Write/Edit/BrowserPreview 在 path 之外接受 file_path,Glob/Grep 在 pattern 之外接受 query。两种拼写在 schema 中都是可选的,因此任一都能通过校验;运行时在写锁、主机调用和转录之前把别名折叠到规范名上,要求二者恰好提供其一,两个都给时规范名胜出。Bash.timeout 的 schema 上限放宽到 3600000,好让毫秒值先通过校验,运行时把不小于 1000 的值按毫秒读取并夹到实际生效的 300 秒上限,而 301 到 999 仍是超出范围的秒值。仅运行时:不涉及 IPC、存储或主机协议改动,host-core 的 schema 保持不变。 | 参数名是预训练习惯,不是 schema 能纠正的指令。某个本地月份里 1679 次失败的工具调用有 852 次是参数名不匹配,其中 724 次是 Read 传了 file_path,仅此一项就占 Read 失败的 87%,把它的错误率推到 11%。另有 69 次是按毫秒给的 timeout。每一次都为一个模型自己看不见的错误付掉一个回合。 | | D274 | 不变的已编辑提示也执行重试 | 修订 D137(渲染层):确认一个有效的用户提示编辑后,始终分派现有的编辑-重发/重新生成路径,即使修剪后的文本与原文相同。内联控件使用本地化的“重试”和“取消”;重试保留现有的修订存档、按身份截断、斜杠命令展开和附件行为。仅涉及渲染层和 i18n:不改动 IPC、存储、主机协议或运行时契约。 | 把未变化的确认视为无操作会让“编辑并重新发送”看起来像坏了(issue #23),而旧的“发送”标签暗示这是一个普通的新提示,而不是重放选中的回合。 | | D275 | 在上下文压缩期间保留活动任务边界 | 修订 D203 / ADR 0064(代理运行时):检查点记录不透明的 details.retainedTailMode。当提供商必须在工具结果、toolUse 或溢出恢复后继续时,活动回合检查点只保留最新的用户消息(沿用现有 20,000 令牌上限)。在终止边界、发送新提示之前和手动压缩时,已完成回合检查点的保留尾部为空,摘要是已完成工作的权威内容。没有该模式的旧记录归一化为最新的用户消息。不改变可见转录本、存储 schema、主机所有权或协议形状。 | 压缩掉完成消息却保留多个最近用户提示,会让下一条提示看起来像旧任务的延续(issue #22)。恢复时必须显式保留任务边界,同时活动工具循环仍需保留继续当前任务所需的提示。 | | D278 | 子代理模型选择 | Task.model 覆盖定义中固定的模型;只有带 availableForSubagents 的模型出现在委托目录中;启动时未预解析的模型由按需 RPC 解析 | 父代理需要按任务选择模型,又不能暴露完整的服务商配置。选择加入的标志让委托目录保持有界且有意为之,按需解析则避免 sidecar 启动后新增模型的绑定过期。参见 §3/02 §5f、§3/11 §7 与 E2E-166。 | | D365 | 为实时活动行的每个安静间隔命名 | 修订 D338 / ADR 0175:AgentActivity 增加 preparing、compacting、recovering;starting 显示独立标签;waiting-subagents 携带实时运行快照(name、lastPhase、lastToolName),随子级工具/思考变化更新而非逐 token 更新。该行保持为一条紧凑的内联状态,不恢复活动分组胶囊。 | 压缩、静默回合恢复、工具后间隙与启动都像卡住的通用等待,父级等待也隐藏了委托在做什么(ADR 0198,E2E-008c / E2E-094) | | D599 | 开发构建是一个独立安装 | 收窄 D236 / 修订 ADR 0094:开发构建(未打包,或 PI_DESKTOP_DEV=1)以 PI-Desktop Dev 作为 Electron userData(随之独立的单实例锁、渲染层 localStorage、插件面板 partition 与浏览器面板 Cookie),数据目录为 ~/.pi-desktop-dev。显式 --user-data-dir 仍然优先,E2E 装置正是用它把构建指向临时 profile。正式安装仍保持 PI-Desktop 与 ~/.pi-desktop,既有 profile 不会被搬迁。PI_DESKTOP_DATA_DIR 仍优先于两种 profile,并在作为子进程环境变量传给 host-core 之前被绝对化;Electron 主进程把解析结果回写到该变量,使插件运行时读到同一个根目录。不改 IPC、协议、schema 或正式安装路径。见 03-runtime/07-process-model.md 与 E2E-150。 | 已在运行的正式版持有锁,pnpm dev 一启动就退出;而抢到锁的开发 host 会把第二个 host-core 压到同一个单写者 pi.sqlite、outbox 与日志树上。 | | D602 | 崩溃转储留在数据目录 | Electron 的 Crashpad 报告器在 ready 之前以本地模式启动(uploadToServer: false)。转储放在 <data_dir>/crash-dumps,而不是 Electron 默认的 userData crashDumps 路径,因此 PI_DESKTOP_DATA_DIR profile 不会与其它安装共用转储。下一次持有单实例锁的启动会为新于 crash-dumps.json 的转储写一条 diagnostics 记录,按 Crashpad ptype 分类:任一新转储属于 browser/main 进程则为 error,已恢复的 renderer/GPU/utility 崩溃为 warn。host-core 与 sidecar 崩溃仍走监督器路径。不上传,不改 IPC,不改 schema。 | 崩溃留下的 minidump 无人读取。Crashpad 也会记录已恢复的渲染进程崩溃,因此下次启动用 error 声称上次运行已死是错的;而数据目录之外的转储会逃出 PI_DESKTOP_DATA_DIR 隔离。 | | D619 | 复制公式得到的是它的 TeX 源码 | 仅渲染层:当选区覆盖到渲染出来的公式时,text/plain 从 MathML annotation 写出——行内 $…$,块级 $$…$$ 独占行,也就是 lib/latex-math.ts 把 \(…\) 和 \[…\] 归一到的那组定界符,且每组定界符都像代码段的围栏那样加长到盖过公式内部出现的最长同字符串——而不是 KaTeX 画的那两棵树。落在公式内部的切口会扩展成整个公式。只有公式被改写:归约后的克隆经由 Selection.toString()——复制自己跑的那个序列化器——读回,因此同处一个选区的正文、列表、表格和代码块保留平台自己的读法,包括 innerText 会写出、而复制本就会略过的 user-select: none 界面零件。不含公式的选区,以及在选区所在位置之外触发的复制,整份交回平台。只写一种 flavour:text/plain。接管事件同时丢掉了平台的 text/html,且不再写回——归约后的克隆是应用自己的标记,写回去会带上文本读法已略过的 user-select: none 界面零件;而改为携带渲染结果则会把每个公式粘贴两遍,因为隐藏 MathML 树的只有 KaTeX 自己的样式表,样式表不会随剪贴板一起走。只有一个由外壳持有的文档级 copy 监听器,记录区的右键复制经由同一个模块读取同一个选区。 | 公式粘出来是它的字形,而且每棵渲染树各一份,无法带进 LaTeX 文档或别的 Markdown 编辑器(issue #414)。ADR 0268 当年移除「引用」的理由正是 OS 剪贴板可以替代,那剪贴板就得真的装着源码。 |

M0. 模型目录决策 ​

身份证号主题决定基本原理
D266以 models.dev 作为主要模型目录修订 D136 / ADR 0027:Electron main 加载 https://models.dev/api.json 作为主要的提供商/模型元数据来源,按提供商键或规范化后的 API URL 匹配,并把它的上限、模态、推理选项、工具调用、结构化输出、显示名称和成本字段映射到既有的模型界面。固定版本的 pi-ai 目录仍是远端记录不可用/缺失时的本地回退,并在 models.dev 没有等价物时提供适配器专有的兼容性数据。提供商端点发现和显式模型 ID 对自定义/账户专有模型仍然可用;不向 models.dev 发送任何提供商凭据,也不引入主机模式/协议变更。固定的 pi-ai 目录对运行时适配器是准确的,但落后于更广泛的市场目录。一次有界的、在主进程中完成的 models.dev 抓取,让设置页和输入框拥有当前的模型覆盖面,同时保留离线/本地行为以及 pi-ai 的传输回退。

M.Agent 运行时决策 ​

身份证号主题决定基本原理
D114代理临时文件的每个会话暂存目录每个会话将 <data_dir>/scratch/<sessionId>/ 作为 strict/no-HTML/no-link/Electron/<data_dir>/scratch/<sessionId>/ 的第二个包含根(仅限绝对路径;相对路径保持工作区限制)。该路径在系统提示符中公布,并在 Bash 中公布为 PI_SCRATCH_DIR。 Scratch 在没有权限卡的情况下写入自动允许,并从工件表中排除。 Scratch 是惰性创建的,随会话一起删除,并在启动时清除(孤儿,>7 天陈旧)。 Glob/Grep/BrowserPreview 仍仅限工作区。 Plan 不公开 Write/Edit; Plan Bash 在其解析权限模式下仍然可以改变暂存。Temp/intermediate 文件(一次性脚本、下载的数据、草稿)污染了用户的项目和 git 状态;主机拥有的根具有相同的词法+符号链接防御,可以保持沙箱模型完整,同时为模型提供合法的临时工作空间
D115权限模式:全局默认+每会话覆盖(Composer 演示文稿被 D132 取代;检查点 Plan 子句被 D189 取代) 权限模式管理高风险工具审批:ask(确认所有内容,默认)、accept-edits(自动允许 Write/Edit、确认 Bash/plugins)、auto (自动允许全部)。全局默认值位于设置 defaultPermissionMode 中;每个会话都存储 permission_mode(架构 v5,默认 inherit),在不继承时会覆盖它。解决方案:会话覆盖→全局默认→询问,仅在 host-core tools.execute 中强制执行。 Plan 保留选择器;它的 Write/Edit/plugin 硬拒绝排名高于每种模式,而 Bash 则遵循所选模式。确认每个 Write/Edit 都会使长代理运行高摩擦,但单个全局切换太粗糙 - 受信任的临时会话和有风险的回购会话需要不同的姿势;保持主机端强制执行可保护安全边界
D132Composer权限菜单仅显示有效模式代理模式 Composer 芯片和菜单仅公开 ask、accept-edits 和 auto,直接选择有效模式,没有 global-default/inherit 条目或出处标签。选择一个项目会存储显式会话覆盖。在用户选择模式之前,D115 中现有的 inherit 持久性和分辨率保持不变。这仅取代 D115 的输入框演示条款。继承的条目重复了可选择的模式并暴露了存储来源,而不是用户选择的权限状态;三种有效模式的呈现,在不改变主机执行的情况下,让控制变得直接
D116提供商失败作为辅助消息每个 provider/model 转向失败都会通过可选的 UiMessage.error 附加到持久的 role=assistant、status=error 转录消息。该消息显示本地化的摘要和稳定的代码,将经过编辑的提供程序详细信息保留在可访问的披露后面,并提供适合上下文的重试或设置操作。消息绑定失败从不使用 toast/global 横幅呈现,也从不重新进入以后的模型上下文。错误属于失败的回合;将它们保留在转录本中使得响应在会话 switches/restarts 之后可诊断,而不会污染下一个模型请求或暴露凭据
D127上下文保留重新播种+传输重试除了 user/assistant 文本和思考之外,从持久记录中重新播种重新创建的 pi 运行时还会恢复工具 call/result 对(来自工具行的 role=assistant/status=error/UiMessage.error)。中断的工具行恢复为错误结果;孤立的工具行获得一个合成的仅呼叫辅助载体,因此对保持相邻且格式良好。失败的助理将仅保留成绩单。另外,请求设置使用一次有界 pi-ai 重试来处理暂时的 transport/provider 失败;响应后流恢复由 D186 定义。在任何运行时重新创建(regenerate/edit、配置更改、重新启动)后,纯文本重新播种会崩溃会话的上下文:模型丢失了它收集的所有工具结果,并且在没有可见工具使用的情况下看到自己的历史答案,停止调用工具,将代理会话降级为裸聊天。事件触发器是一个未重试的提供程序超时,迫使用户重新生成。 D127 更正最初重复的 D120 标识符; D120 仍然是由基线 0.4.6 冻结的早期应用程序更新决策。
D136pi-ai 拥有已知模型元数据对于从固定 pi-ai 目录解析的每个模型,Electron main 将完整的 pi 模型快照传递到 sidecar,而 PI-Desktop 仅替换连接标识。提供商设置和模型菜单不会覆盖推理支持、思维水平、context/output 限制、温度或兼容性。通过明确的通用纯文本、非推理回退,未知的自由格式 ID 仍然可用。这取代了 D102 和 D096/D107 的提供商覆盖条款。第二个桌面拥有的模型矩阵丢弃了 pi 元数据,偏离了适配器行为,并使模型语义依赖于冲突的配置;针对已知模型的修复现在属于 pi-ai 或 pi-ai 升级。
D183分段工具和模型延迟日志 (被 D385 / ADR 0212 取代;UI 子句被 D184 取代)每个 tools.execute 调用都按分段计时,而不是一个不透明的持续时间:host-core 在 host 通道上发出 tool timing 行,并在现有通道旁边保留 prompted、permissionWaitMs、overheadMs 和 totalMs durationMs 在 tool_execute / tool_denied 审核行上; sidecar 将 grepable [timing] kind=tool … (hostRttMs) 和 [timing] kind=model … (providerWaitMs、streamMs,包括 failed/aborted 匝) 线写入 agent 通道,可通过 PI_DESKTOP_TIMING=0 进行抑制。原来的 no-UI 条款被 D184 取代;日志记录保持不变。从日志中无法诊断出“执行命令很慢”:批准等待、工具主体和提供程序往返无法区分,因此两个持续时间为 0 毫秒的审核行之间的 45 秒间隙无法说明是用户、模型还是主机。拆分阶段可以使答案可读,而无需重现运行。
D158回合边界上下文检查点压缩*(软边界、模型工具和可见性条款被 D200 取代;模型工具和可见性由 D203 以 Codex 的形式恢复)*PI-Desktop 重用 pi-agent-core 的上下文估计、会话上下文和压缩原语,但拥有编排和持久性。在每个 turn_end 之后,在任何下一个提供程序请求之前,运行时都会评估模型感知的 soft/hard 预算。瞬态重复数据删除指令可以要求模型调用内部CompactContext工具;该工具的正常活动行是 visible/durable,而指令则不是。跨越硬预算会强制生成检查点并阻止失败的请求。达到硬预算一半的最终原子工具批次仅在检查点副本中相当 head/tail-truncated,具有显式标记并保留每个 call/result 信封;原始成绩单行保持完整。精确提供程序溢出会从模型上下文中删除失败的助手,创建一个检查点,然后重试一次。主机协议 v6 在未触及的可见 JSONL 转录本旁边附加检查点记录;重新启动、后期截断和包含边界分叉保留最新的有效检查点。禁用自动压缩会删除该工具和所有自动 threshold/overflow 恢复,而 /compact 仍然可用。 OpenCode DCP 仅是 AGPL-3.0 行为参考,既不链接也不复制 (ADR 0030)。pi 的仅运行结束行为无法保护长工具循环,并且仅靠模型提醒无法保证提供程序的安全。重用 pi 的经过测试的压缩格式,同时添加确定性 turn_end 门,可以防止另一个提供商请求跨越已知窗口,保留用户可见的历史记录,并避免导入不兼容的 plugin/runtime 和许可证边界。
D185惰性每转刀具激活 (始终有效的 CompactContext 子句在 D200 下无效,在 D203 下再次适用于 new_context;激活恢复由 D400 / ADR 0225 修订)sidecar 保留完整的本地工具注册表,但在每个新提示上仅发送模式核心集和本地 ToolSearch。BrowserPreview、插件工具、Skill 和插件开发助手显示为有界紧凑目录条目,并通过精确名称或功能搜索激活;下一轮接收其架构,在支持时使用本机 pi-ai 延迟搜索,并且内存中的集合在下一个用户提示之前重置,然后只恢复有效上下文中仍存在且当前目录和模式允许的成功激活证据。主机权限、遏制、超时和审核保持不变。完整的工具模式使得简单的第一个请求变得不成比例地大,并且跨轮重复的可选功能成本。pi 风格的活动集保留了核心编码人体工程学,同时使辅助工具按需付费且独立于提供商;恢复成功的上下文证据可避免模型看到能力标记却收不到对应架构。
D200难以察觉的后台上下文压缩*(后台预计算、增量触发、静默和由 D203 取代的无模型工具子句)*压缩成为主机拥有的后台活动,没有用户可见的表面。 contextBudget() 保留 D158 硬限制和请求余量,从模型窗口(clamp(hardLimit * 0.2, 8k, 64k),仍然上限为硬预算的一半)而不是设置导出保留尾部目标,并添加 backgroundLimit = floor(hardLimit * 0.7) 作为预计算触发器;软边界被删除。检查点生成与安装分开:buildCheckpoint 生成一个而不保留或激活它,安装重新估计,通过 host-core 附加,并发出 compaction_end。预计算仅在提供程序空闲窗口中运行 - 当工具执行时和运行结束后 - 并且仅当上下文超过后台限制并且自最新检查点的基线以来至少增长了保留尾部目标,因此大尾部不能每次都触发摘要。仅当其底座仍处于活动状态、其 throughMessageId 锚点仍然存在且仍符合当前模型的预算时,预先计算的检查点才会安装在下一回合边界或提示处;任何未命中都会进入未更改的阻塞路径,并且失败的后台构建将被丢弃,没有事件,没有持久性,也没有 ADR 0049 回退。 CompactContext 工具、<context_management> 微移和主机无确认白名单条目均已删除,因此触发完全是确定性的。 compaction_start/compaction_end 获得可选的 phase (background | blocking,缺席意味着 blocking),并且 compaction_end 获得可选的 status { generation, summaryTokens };两者都是协议 v9 中的附加内容。成功的自动压缩不会产生任何提示,不会发生运行状态更改,也不会产生转录行;只有 retained_tail 回退、溢出重试和手动 /compact 仍会通知。上下文使用检查器是单个可见跟踪,读取 status 和持久的 SessionDetail.compaction,生成计数器包含在检查点的不透明 details 内,因此不需要更改记录模式。设置不公开任何压缩控件,并且持久的 contextCompaction 值将被忽略 (ADR 0061)。压缩是正确的,但具有侵入性:它进行烘烤,移动运行状态,在工具调用上花费模型转动,留下记录行,并且总是在用户等待时运行。 Codex 的分级触发和增量范围阈值显示可以在关键路径之外支付摘要,并且其仅主机触发消除了一类浪费的回合。从模型窗口导出预算还修复了将一对绝对令牌计数应用于 32k 和 1M 窗口的问题;保持硬边界不受影响意味着这一切都不会牺牲用户体验的提供商安全。
D201Task 工具背后的有界子代理当目录包含四个内置函数之一或 ~/.agents/subagents/*.md 下的已启用全局文档时,Agent 模式提供 Task。Electron main 为下一次提示加载目录,上限为 16,并且委托报告不会进入父级模型上下文。没有项目级子代理能力目录;不会扫描 .pi/agents。将委托保留为有界工具可以保持父级对上下文和权限的所有权,而全局用户目录使个人委托可跨项目使用 (ADR 0062、ADR 0112)。
D202管理全局子代理定义(页面形态条款已由 D257 / ADR 0126 取代;全局唯一的数据边界依然有效) host-core 扫描 ~/.agents/subagents 下的全局 Markdown 文档,把启用状态存入 <data>/agent-capabilities/subagents.json,并通过设置 > 智能体 > 子代理页面暴露。页面是固定高度的单个全局列表,没有项目选择器或项目级来源。运行时把已启用的全局文档与内置定义合并;格式错误或删除的文档产生诊断或在扫描后消失,能力状态不写入 Markdown 文件。个人委托可以跟随用户而不会修改仓库;明确的全局边界避免第二套项目优先级模型,并让扩展页专注于已安装和市场 (ADR 0063、ADR 0112)。
D203Codex 奇偶校验上下文压缩压缩被重建以匹配 Codex 的机制,反转四个 D200 条款并保留其余部分。所有后台预计算和增量触发范围都被删除:当总上下文跨越 hardLimit 或模型要求新窗口时,prepareNextTurn() 同步压缩。检查点仅携带摘要和最近的 用户 消息 - pi 的切割点仍然标记边界,但其分叉前缀和最近的尾部被折叠回摘要输入中,因此摘要覆盖整个范围,并且保留的尾部是从最多 20,000 个令牌(上限为硬预算的一半)的用户消息中最新先重建的,交叉消息被截断而不是丢弃,然后恢复到时间顺序。两个系列运行相同的生命周期:summary 和 fresh_window 翻转,不请求摘要并存储固定标记文本,由构造选项或 PI_DESKTOP_COMPACTION_STRATEGY 选择,并且既不在设置中也不在 i18n 中公开。面向模型的 new_context 工具返回(无参数,Codex 的逐字描述,在主机上无确认白名单,永远不可分配给子代理)以及附加到当前回合系统提示的两个每个窗口预算提醒 - 一个在 clamp(hardLimit * 0.15, 8k, 32k) 剩余,一个在 2,000 - 每个声明一次并在安装时重置。 host-core 保留整个检查点链(read_compactions、write_transcript_with_compactions、每条记录分叉验证、SessionDetail.compactions),并将 compaction 保留为其最新元素。 compaction_start/compaction_end 删除 phase,并且 compaction_end 将 status 替换为 mark { id, throughMessageId, generation, summaryTokens, summarized };记录在其覆盖的消息之后为每次压缩绘制一个分隔行,结束其落在内部的辅助回合并删除锚消失的标记,并且每次成功的压缩都会在 fallback/overflow/manual toast 之上引发一个警告 toast。故意偏离 Codex:摘要位于保留用户之前,因为 buildSessionContext 修复了该顺序,hardLimit 保持“窗口 - 输出保留”而不是窗口的 90%,该工具已在两个系列中注册,并且提醒是系统提示附加我们自己的阈值和措辞 (ADR 0064)。上一轮引用了 Codex,同时在四个方面实现了相反的结果,其上下文部分声称,当 new_context 存在时,Codex 没有模型端压缩工具。用户在被告知 Codex 的机制会逆转不可察觉目标后,特别询问了 Codex 的机制。 Parity 还根据其自身优点购买了三件东西:仅保留用户消息的检查点要便宜得多,并且在没有结果的情况下无法搁置工具调用,可见行使有损操作可以再次从记录中进行审核,并且警告将“改为开始新会话”决策放在其所属的位置。成本被接受:用户再次等待压缩。
D186有界提供商流恢复和诊断提供程序请求设置使用一次有界 pi-ai 重试。流式传输开始后的瞬态 STREAM_FAILED、NETWORK_ERROR 或 TIMEOUT 在可中止退避后的同一回合中重播一次;失败的助手将从模型上下文中删除,并重用其可见消息 ID。第二次失败是致命的。提供程序 AppError.details 仅携带有界阶段、计时、提供程序 status/code 和重试诊断。突变指导在 Edit 不匹配后使用一个新的 read/regeneration;同一路径的第二个失败的 Edit 或第二个失败的 shell patch 命令会返回终止工具提示,而不是修复旧的补丁工件。无界或重新生成驱动的恢复使得瞬态流终止变得昂贵,并且使得补丁循环在没有新信息的情况下消耗轮次;有限的运行时间预算保留上下文和用户控制,同时保持故障可诊断(ADR 0050)。

| D187 | 资源隔离主机 RPC stdio | host-core 通过每个方向的一个专用命名操作系统线程读取 stdin 并序列化 stdout,而不是通过 Tokio 的动态阻塞池。线程重试中断和瞬态 STREAM_FAILED/NETWORK_ERROR 错误,同时保留 NDJSON 帧;无法创建控制线程是结构化启动失败。登录 shell 路径探测还将帮助程序线程创建视为尽力而为,并回退到继承的路径。 RPC/tool 入场限制保持不变。 | 当操作系统线程创建返回 Resource temporarily unavailable(macOS 上的错误号 35)时,Tokio stdio 可能会出现恐慌,从而将临时资源压力转变为 HOST_UNAVAILABLE;隔离控制管道会删除该进程级崩溃路径,同时保留有限的过载行为 (ADR 0051)。 | | D191 | 仅限代理模式;聊天已重命名为只读 | (被 D188/D189 取代:模式选择器返回为 Agent | Plan, and chat migrates to plan) agent is the only session mode the product exposes. The former chat profile is renamed 只读 and keeps its Read/Glob/Grep hard deny in host-core, but it has no UI surface: no top-bar toggle, no composer chip, no Settings row, no palette command or slash alias, and no localized labels. The host normalizes chat to 只读 on every write path (session.create, session.configure, session.import) and the permission gate is negative — anything that is not agent gets the read-only surface — so an unknown or legacy value can never widen the tool set. Error codes become BASH_DISABLED_IN_READ_ONLY / WRITE_DISABLED_IN_READ_ONLY. A boot fix-up rewrites existing sessions.mode =“聊天” rows and a stored defaultMode of chat to 代理。 | 该产品从来不希望用户实现的模式切换是枪炮和沉重的 UI 重量:会话可能会滞留在只读配置文件上而无法返回,并且两个工具集使每个工具、提示和权限更改的表面增加了一倍,都必须进行推理。在主机端保持窄配置文件强制执行可以保留导入行和旧行的安全边界,而无需为其提供控件 (ADR 0055)。 | | D369 | 有效的子智能体思考元数据 | (由 D395 修订) 即时 Task 结果、SubagentRunResult 与生命周期快照携带传给每个子运行的有效 modelId 与 thinkingLevel;拓扑节点与侧栏标题在模型名后显示原始规范非 off 级别,off、omit 与不支持推理时仅显示模型。宿主协议、存储 schema、provider 请求与生命周期行为不变。** | 委托卡片需要目标模型钳制后实际发送的级别,而不是从父级或定义重新推导的值(ADR 0202、ADR 0221、E2E-219) |

| D395 | UI 中的规范思考等级值 | 修订 D369 / ADR 0202:Composer、模型配置和委派界面直接显示 off、minimal、low、medium、high、xhigh 和 max,不再翻译。所有语言目录移除这些值;有效元数据、钳位、provider 请求、协议和存储保持不变。见 ADR 0221 与 E2E-219。 | 思考等级是稳定的协议值,本地化标签会使同一个 provider/runtime 设置在不同应用语言下显示不同。 | | D445 | 压缩摘要先重试并按实际提示大小预检,再回退保留尾部 | 修订 ADR 0049 第 1 条 / D203:自动摘要请求携带有界的 pi-ai RetryPolicy(3 次重试,2s/4s/8s 退避,可被中止;由 pi-ai 判定哪些错误是瞬时的)。预检守卫按 pi 实际序列化的提示(工具结果已截至 2 000 字符)估算大小,不再对原始消息逐条求和。若该提示仍超出窗口,先尝试恰好一次缩减输入(工具结果截为 500 字符前缀、去掉思考块、不删除任何消息),再运行保留尾部回退。ContextCompactionMark 新增可选 fallback?: "retained_tail",由持久化的 details.fallback 派生;转录行将此类检查点标为摘要生成失败,而不是 摘要 ≈N tokens。不改记录 schema、协议版本或 host-core。见 ADR 0282 与 E2E-084。 | Issue #543:真实长会话 10 次压缩里 6 次是约 112 token 的恢复说明。摘要请求没有重试、按原始大小的守卫跳过了本可放下的摘要、转录行把每次回退都显示成成功摘要。 |

N. 通知决定 ​

身份证号主题决定基本原理
D117持久任务通知收件箱(本机投递条款由 D350 修订)Rust host-core 独占拥有 schema-v6 notifications 表,并且仅在 Electron 报告结果尚未可见时,当 session.endTurn 将运行轮移至 completed/error 时,才会自动插入一个结构化 task.completed / task.failed 行。 Renderer 通过允许列表查看上下文 IPC 提供当前聊天会话;仅当其窗口为 visible/focused 并且该会话匹配时,Main 才会抑制插入,而未知、背景、隐藏或未聚焦状态无法安全通知。 turn_id UNIQUE 可以防止重复,中止是静默的,会话删除级联,并且只保留最新的 200 行。标题栏响铃显示准确的未读计数、All/Unread、行 mark-read/session 激活、标记所有已读以及通过完整的 keyboard/accessibility 行为进行清除。协议 v4 添加了单数 notification.list/markRead/markAllRead/clear; session.endTurn 返回插入的记录,Electron 发出渲染器 notification.changed,并且仅当主窗口未聚焦时,它才会显示本机系统通知,该通知单击 restores/focuses 窗口并发出 notification.activated。持久化行包含结构化 kind/session/turn/error 数据以及会话名称快照,从未本地化通知 title/body 散文。任务收件箱没有权限、计划提醒、偏好、云通知或插件通知源;插件本机通知是单独的 D213 表面。 D113 的个人资料页脚保持不变。通知应该恢复用户没有看到的任务结果,而不是重复当前聊天中已经可见的结果。有界主机拥有的收件箱可保持 background/unfocused 结果的持久性和可导航性,而不会侵犯 SQLite 所有权、重复事件或将每个终端事件转变为通知历史记录。

O. 桌面 shell 决定 ​

身份证号主题决定基本原理
D118平台应用程序菜单和窗口镶边macOS 安装传统的系统应用程序菜单并保留隐藏式交通信号灯。 Windows/Linux 使用共享的 46px 无框架外壳以及本地化的 File/Edit/View/Window/Help 菜单和渲染器绘制的 minimize/maximize-or-restore/close 控件。两个菜单表面都通过固定的 AppMenuCommand 允许列表路由渲染器拥有的操作;渲染器菜单通过单独的固定允许列表路由本机 editing/window 操作。目标打包在 Electron 打包之前构建本地发布主机。这增加了平台就绪的 shell 行为,但不会逆转 D010:Windows/Linux 发布资格在 MVP 后仍然有效。默认的 Electron 菜单会使 macOS shell 命令不完整,而无框 Windows/Linux 窗口会丢失应用程序菜单和窗口控件。共享允许列表可以保持行为一致,而不会暴露任意特权命令桥。
D120应用程序更新交付(amended by D364 / D603) Electron Main 独家拥有固定的 GitHub 发布源、更新轮询、类型化状态和安装生命周期。开发被禁用;打包的 macOS 和非 AppImage Linux 使用通知和链接传递,而 Windows NSIS 和 Linux AppImage 在应用程序内下载并在退出时安装。 Renderer IPC 无法提供 Feed URL。自动故障保持环境状态,显式检查表面状态,下载状态保持可操作。更新程序始终强制使用 allowPrerelease = false,因此预发布安装(例如 0.2.0-rc.6)会跟踪 GitHub 的最新稳定版本,而不是 electro-updater 的默认同通道引脚。 D126 随后发布由标签矩阵生成的每个平台提要,而 macOS 仍保持手动状态,直到签名通道合格为止。将软件包安装保持在沙盒渲染器之外,匹配每种安装程序格式的交付,并在菜单、设置和更新横幅之间提供一种一致的状态 (ADR 0022)。如果没有稳定通道引脚,RC 不会构建更新的稳定通道,因为电子更新程序将 rc 视为自定义通道。
D313主进程拥有的 GitHub 问题反馈GitHub issue 表单是唯一入口。Bug 表单必填描述、复现步骤、预期/实际行为、应用版本和操作系统;功能表单必填问题和期望改动。设置 → 信息提供一条「问题反馈」,走 pi-desktop/app/openFeedback。Electron Main 构造固定的 bug_report.yml URL,用主进程版本信息预填 app-version / os / environment,再以 shell.openExternal 打开。渲染器不能提供 URL。功能请求仍从 GitHub 模板选择器进入。不改 host 协议、存储或更新源(ADR 0157)。缺少版本或复现步骤的报告无法排查;由渲染器选择目标会削弱 D120 同样的「GitHub URL 归主进程」规则。
D330由主进程拥有的 openExternal 协议白名单每次 shell.openExternal 先解析 URL。只有 http:、https:(含主机名和写出的 //)以及 mailto:(非空地址)会以规范化 href 到达操作系统。file:、javascript:、data: 和自定义方案被拒绝。见 ADR 0168。任意协议的 openExternal 会把沙箱渲染器变成打开本地文件或执行脚本的入口。
D332分类的插件文件预览与实时工作区事件修订 ADR 0104 / 0105 / 0109 / 0111:在 fs.read 下增加 fs.readPreview(文本 / 图片 data URL / 二进制 / tooLarge,上限与宿主 Files 页相同)。工作区事件同时广播给停靠视图和独立面板。插件需要与 Files 页同类的预览,以及工作区变更的实时信号。
D334受约束的聊天内图片显示修订桌面 fs/read 仅工作区条款:fs/read、fs/reveal 和 fs/open 共用 resolveOpenablePath(工作区、<data_dir>/scratch/、<data_dir>/attachments/,以及 attachments/<sha256> blob)。读取会 realpath 目标。聊天需要内联图片,但不能把任意绝对路径交给渲染器。
D336由宿主代发的插件补全与会话上下文修订 D019:插件在授予 session.read 时可经 pi.session.getLlmContext() 读取进行中工具会话的面向模型转录,并在授予相应权限时经 pi.agent.complete() 跑一次性补全。见 ADR 0174。插件需要第二意见补全,但不能自己拿提供商凭据。
D340用户可配置的出站代理设置 → 通用 → 网络把代理暴露为系统 / 直连 / 自定义。自定义接受 http/https/socks5 URL 和绕过列表,持久化为可选 AppSettings.networkProxy。Chromium session.setProxy 覆盖内置浏览器和 net.fetch;sidecar 经 undici dispatcher 应用。见 ADR 0177 与 E2E-190。Node fetch 忽略操作系统代理,Clash/V2Ray/SOCKS5 用户能浏览却不能调模型。
D342从本地智能体存储导入模型配置修订 D007:设置 → 导入仍不自动导入 ~/.pi。显式模型配置卡片扫描 Claude Code、Codex、OpenCode、Pi 和 CC Switch 提供商行,列出不含密钥的草稿,并由 modelConfig/importRun 经 providers.create 复制 API 密钥。等价提供商(规范化 base URL + API 风格 + 同一凭据)被跳过。见 ADR 0179、ADR 0188 与 E2E-209。用户已经从这些存储导入会话,否则要在模型页重打同样的端点和密钥。
D351模型配置导入保留不同凭据修订 D342 / ADR 0179:导入提供商仅在规范化端点、API 风格和凭据都匹配时视为等价。同一端点不同 API 密钥创建独立提供商行,并仍可在 Composer 中选择。Electron main 经宿主密钥边界解析已有 API 密钥。见 ADR 0188 与 E2E-209。CC Switch 在同一网关端点存多个账户;仅按端点幂等会静默丢掉除第一个以外的配置。
D344由主进程拥有的文件选择能力修订 D197 / D334:Composer 原生文件和图片选择器把选中的绝对路径留在 Electron main,用短时、绑定发送者、一次性令牌保护。composer/importFiles 只接受该令牌和持久会话 id;渲染器从不提供源路径。MVP 文件选择器只提供常规文件。见 ADR 0181 与 E2E-102h。渲染器 IPC 不是用户点击门禁:导入载荷接受任意绝对路径会变成本地文件外泄原语。
D314已发布语言注册表与语言选择器修订 D073:界面语言列在 @pi-desktop/i18n(en、zh-CN、zh-TW、tr)。本地名称永不翻译。设置 → 常规的语言是可搜索选择器(跟随系统 + 注册表),不再是三张预览卡。插件标签保持 en + zh-CN 契约;没有插件翻译的外壳语言使用英语回退;产品更新日志跟随每个已发布产品语言。见 ADR 0160、ADR 0182 与 E2E-091。预览卡无法扩展到两种以上语言;注册表让添加语言无需重写外观卡即可发货。
D345繁体中文外壳语言修订 D314 / ADR 0160:以独立完整外壳目录发布 zh-TW,本地名称为繁體中文。zh-TW、zh-Hant、zh-HK 和 zh-MO 解析到繁体中文外壳;通用 zh 和简体中文区域继续解析到 zh-CN。持久化的 AppSettings.language 加入 zh-TW,Electron 打包 zh-TW 与 zh_TW 语言环境目录;应用内变更日志加入对应的 zh-TW 目录,使发行说明跟随当前繁体中文外壳。不改变宿主协议或存储架构版本。繁体中文用户需要独立的外壳和发行说明语言;复用简体中文目录会使自动检测和可见术语不正确。
D346P0 国际外壳语言修订 D314 / ADR 0160 / ADR 0182:发布完整的 de、es 和 fr 外壳目录,本地名称为 Deutsch、Español 和 Français。de-*、es-* 和 fr-* 解析到对应基础目录;持久化的 AppSettings.language 接受三个新 ID;匹配的变更日志目录让发行说明跟随当前语言。不改变宿主协议、存储架构或 IPC 版本。西班牙语、法语和德语是缺失的最高价值 P0 国际化覆盖,而现有注册表和可搜索选择器已经可以扩展到更多语言。
D349韩语应用程序壳修订 D314 / ADR 0160 / ADR 0183:提供完整 ko 外壳目录,本地名한국어,英文名 Korean。ko 和 ko-* 解析到韩语目录;持久化 AppSettings.language 接受 ko;Electron 打包韩语 Chromium locale,并有匹配的韩语发版日志。不改主机协议或存储 schema。韩语用户需要独立完整外壳和发版说明语言。
D350按焦点区分本机通知投递修订 D117 / ADR 0107:渲染器在 notification/showNative 终端结果调用中标记 kind: "task",在 asktool、工具权限和 Plan 审批调用中标记 kind: "interactive"。缺失或未知值默认为 task。任务本机投递仍然仅在未聚焦时进行,包括聚焦的背景会话;交互投递仅在确切的询问会话已在聚焦窗口中可见时抑制,因此聚焦其他会话时可以收到横幅。交互询问从不创建持久任务收件箱行,插件本机通知仍使用独立的权限门控 API。不改变主机协议或存储架构版本。PR #84 扩展共享 handler 的门控时,使聚焦的背景终端完成也显示了本机横幅,同时实现了聚焦背景交互询问通知的目标。显式 kind 在同一个 Electron 边界隔离这两种用户可见策略。
D358在进行中重试行显示 provider 原因修订 ADR 0175:AgentActivity.retrying 可携带有界、已脱敏的错误详情。紧凑重试行在静止时保持原样;悬停或键盘聚焦揭示本地化摘要、稳定码/状态和 provider 消息。中间重试不会变成转录错误行。见 ADR 0196 与 US-UI-60d。用户需要当前重试原因,又不能复制最终助手错误。
D359用宿主一次性补全总结首轮会话标题首轮提示回退保持同步。agent_end 后渲染器调用白名单 session/summarizeTitle。Electron 解析会话模型并跑关闭思考的一次性补全;有效结果经 session.rename 持久化。自动替换拒绝已持久的 manualTitle。见 ADR 0186 与 E2E-021a。截断的首轮提示当侧栏标签很差,但标题生成不能挡住回合或把凭据暴露给渲染器。
D361内联图片传输使用 10 MB 应用侧上限修订 ADR 0101 / D197:MAX_INLINE_IMAGE_BYTES 为 10,000,000。具备视觉能力的模型把不超过该十进制 10 MB 上限的图片作为瞬时 base64 接收;更大的图片和非视觉模型保留 @path 回退。Electron main 和 sidecar 共用该常量。不改协议或 schema。MiniMax 的 OpenAI 兼容端点单张图上限是 10 MB;更高的应用上限会在 Composer 已经内联字节后被提供商拒绝。
D316可搜索主题选择器修订 ADR 0160:设置 → 常规的主题是可搜索选择行(与语言相同的锚定菜单),不再是三张预览卡。系统 / 浅色 / 深色钉在顶部;插件主题在分隔线之后。AppSettings.theme 与插件主题回退不变。见 ADR 0161 与 E2E-091。插件主题会撑破三列卡片网格,而语言选择器已经解决了可增长列表的控件问题。
D121品牌macOS开发主机macOS 上的 pnpm dev 通过 .cache/electron-dev/ 下已安装的 Electron 主机包的指纹、临时签名的 PI-Desktop 副本启动 electro-vite。生成的捆绑包仅更改开发主机元数据、可执行文件名称、捆绑包标识符和 ICNS 资源;它永远不会改变 node_modules。 Windows/Linux 保持库存开发可执行,而打包通道仍归电子制造商所有。AppKit 会忽略顶级应用程序身份的运行时 app-name/menu 覆盖,并从主机包中获取本机菜单名称和“关于”图标;需要品牌开发主机才能与打包的 PI-Desktop 保持一致。
D129无菜单 Windows/Linux 窗口镀铬应用程序菜单仅为 macOS 系统菜单表面。 Windows/Linux 保留共享的无框架 46px 标题栏和渲染器绘制的 minimize/maximize-or-restore/close 控件,但在窗口内不渲染 File/Edit/View/Window/Help 菜单,并且不保留左侧标题栏空间。通过 renderer/native Web 内容处理,现有应用程序、编辑、缩放、全屏和关闭快捷方式仍然可用;仍然可以通过“设置”->“信息”访问更新检查。这仅取代 D118 和 ADR 0021 的 Windows/Linux 渲染器菜单部分。窗口内桌面菜单会重复 macOS 特定的系统菜单镶边、占用导航空间,并且不属于 PI-Desktop 的无框 Windows/Linux 标题栏。
D130侧边栏页脚通知条目持久通知铃声从主标题栏移动到扩展侧边栏页脚右侧单独的 32px 操作,取代了 D113 的“帮助”快捷方式。其未读徽章和完整的 D117 收件箱行为保持不变;弹出窗口在页脚的上方和右侧打开,并且主标题栏中没有重复的贝尔。这仅取代 D113 和 D117 的条目位置条款。通知历史记录属于持久的本地配置文件控件,页脚位置使主标题栏保持安静,同时保留紧凑、熟悉的状态条目。
D141规范 Windows 本机应用程序身份(由 D443 修订) Electron Main 在准备就绪之前设置产品名称,并在创建任何窗口之前将 com.pi-desktop.app 注册为 Windows 进程 AppUserModelID。该 ID 是现有的 electron-builder/NSIS 应用程序 ID; Windows 打包显式保留 PI-Desktop 作为可执行文件和“开始”菜单快捷方式。本机通知属性、通知设置、任务栏分组、安装的快捷方式和打包的可执行标识必须公开 PI-Desktop,而不是库存 Electron 主机。 D121 保持不变:Windows 开发可以使用库存 Electron 可执行文件,而其面向操作系统的运行时标识使用规范的 AUMID。app.setName 更改 Electron 的内部名称,但不更改通知和 shell 集成使用的 Windows 标识。跨运行时和打包的一个稳定 ID 可防止观察到的通知源泄漏和相邻 shell 品牌漂移,而无需更改已发布的 NSIS 升级身份。
D204空的家庭任务输入表面(取代 D111 的非停靠家庭输入框条款及其上下文快速行动条款;D205 中的起始网格修正被 D206 取代) **空的聊天主页将受限制的英雄和可选的首次运行清单保留在可滚动内容区域中。主输入框是滚动条的底部保留兄弟,在聊天表面的底部保持可见,并且从不覆盖内容。 **直接编辑器仍然是稳定的主要操作,并且流程布局保留了短窗口内清单的可达性(ADR 0066)。
D205ChatGPT 启发的空屋指导(被 D206 取代) 空的聊天主页在英雄和可选清单之间添加了一个紧凑的四卡开发人员入门网格:探索代码库、构建功能、修复错误和审查更改。每张本地化卡仅预填充并聚焦底部输入框;它从不发送提示或创建回合。 D204 中保留的底部合成器和单个可滚动主页流程保持不变。之前的英雄专属中路留下了太多未使用的空间,并且没有提供起始线索。 ChatGPT 清晰的空状态层次结构提高了第一个任务的可发现性,而开发人员特定的提示则保持表面有目的性而不是促销性。
D206删除空置的开发者入门卡空的聊天主页不会呈现开发人员入门卡、入门字形或上下文快速操作行。保留了内敛的英雄、较短的辅助线、可选的新手引导清单以及D204的底部保留输入框;任务输入直接在编辑器中启动。这取代了 D205,而不改变 D204 的滚动和底部保留布局。审查证实,直接输入框是首选的任务输入界面,并且这些卡片为空荡荡的家添加了不必要的决策层。
D208可恢复的本机工具路径合约保留 D185 的延迟 Glob/Grep 边界,但使每个提示和模式明确显示 Read 接受现有常规文件、Glob 接受目录、Grep 接受文件或目录。目录读取返回 INVALID_ARGUMENT 加上结构化的 Glob 恢复参数;显式文件 Grep 仅搜索该文件并将 include 应用于其基本名称。工具错误在其 ToolCallRows 上仍然可见,而活动组仅报告处理持续时间,并且从不从子行推断终端转向失败;终端代理事件和专用结果表面仍然具有权威性 (ADR 0069)。持久会话反复显示目录读取和文件作为目录 Grep 错误,然后将恢复的工作显示为最终失败。窄主机边界的兼容性加上一个结果所有者消除了重试和错误失败 UI,而无需将每个搜索模式恢复到 Agent 核心。
D216跨平台托盘驻留最小化Electron Main 在 macOS、Windows 和 Linux 上创建一个打包资源托盘图标。每个主窗口最小化路径都会被拦截并隐藏窗口,而不需要配置主机或sidecar;托盘 click/double-click、Show 和 macOS 应用程序激活 恢复并聚焦现有窗口(如果已关闭,则创建一个窗口)。本地化托盘菜单显示“显示 PI-Desktop”和显式“退出 PI-Desktop”操作。关闭窗口仍然是退出操作,托盘销毁加上退出使用现有的有序 before-quit 关闭路径。用户需要后台工作才能继续而不丢失应用程序窗口,而最小化在本机 macOS 控件和自定义 Windows/Linux shell 中必须意味着相同的事情。主要拥有的托盘生命周期避免了渲染器权限扩展并保持显式退出可观察。
D218主机拥有的跨平台插件面板窗口栏插件面板窗口采用主窗口的 46px 平台窗口栏:macOS 使用 hiddenInset,交通灯位置为 {x:16,y:16};Windows/Linux 使用无框窗口,并提供 112px 宽的自定义最小化、最大化或还原、关闭控制区。沙盒化插件 preload 在封闭的 Shadow DOM 中渲染 manifest 标题与控件,在现有顶部内边距之外再按标题栏高度偏移内容,并使用一个私有、验证发送方、固定动作集合的窗口操作通道;window.pluginBridge、每插件 partition 与 host protocol v9 均不扩展。重新打开最小化的面板时会还原并聚焦窗口。Electron 默认窗口框架使插件工具看起来脱离 PI-Desktop,且各平台表现不一致。由 preload 拥有窗口栏可实现一致性,同时不将不受信任的插件 HTML 移入主机渲染器,也不暴露通用 Electron 窗口权限(ADR 0081)。
D234插件拥有的面板表面与主机窗口控制胶囊插件面板在 macOS、Windows 和 Linux 上均使用无框窗口。沙盒化 preload 在每个平台预留相同的透明 46px 安全区,并在右上角的封闭 Shadow DOM 中仅渲染一个固定胶囊,胶囊恰好包含最小化、最大化或还原、关闭三个按钮。主机不再渲染面板标题或开发安全区提示;插件拥有标题、工具栏、背景以及其他所有可见面板界面。普通流内容会自动偏移,固定或粘性界面使用现有的 --pi-plugin-titlebar-height: 46px,插件自己的拖拽区域使用 -webkit-app-region: drag,交互控件退出拖拽。私有的验证发送方控制通道、本地化标签、页面自适应颜色、window.pluginBridge、每插件 partition、host protocol v9 和存储架构保持不变。本决策修订 D218 / ADR 0081 以及 D232 / ADR 0082。主机标题栏占用了插件自己的视觉层级,并且仍然让 macOS 与 Windows/Linux 表现不同。紧凑的主机控制胶囊保留安全的原生窗口操作,同时把面板表面交还给插件。
D235严格 46px 插件拖拽带与简约胶囊插件面板在每个平台均严格预留 46px 的透明主机拖拽带。普通流内容按该数值偏移,拖拽带内除胶囊外不接受插件点击,开发面板显示本地化提示,说明顶部 46px 仅用于拖拽。主机不渲染面板标题。固定在右上角的胶囊保持在拖拽带内,恰好包含最小化、最大化或还原、关闭三个按钮,并使用简洁的页面自适应表面,不添加厚重阴影或模糊效果。本决策修订 D234 / ADR 0092;私有的验证发送方控制通道、封闭 Shadow DOM、window.pluginBridge、protocol v9 和存储架构保持不变。无框面板需要可靠的拖拽目标和清晰的开发约束,但主机拖拽带不能增长,也不能抢占插件自己的视觉层级。
D343自定义全局文字缩放设置 → 常规 → 外观在字体行下增加字体大小。星巴克式杯型档位(中杯 / 大杯 / 超大杯 / 超超大杯)加上百分比滑杆,持久化为可选 AppSettings.fontScale(1 = 产品字号阶,缺失 = 1,范围 0.8–1.5、步进 0.025)。渲染器在根元素设置 --font-scale;每个 --text-* 令牌和 --leading-row 都是 calc(<产品 px> * var(--font-scale)),共享 Lucide 图标使用同一倍率,正文、chrome、标题、代码和图标无需重载即保持相对阶。界面不出现 px 输入。窗口放大/缩小/重置仍独立。未发布的遗留 fontSize px 字段在没有 fontScale 时按 px / 14 迁移。无协议/存储版本升级。--text-* 阶本身就是一组相对尺寸;一个倍率可以一次缩放所有台阶。窗口缩放仍会缩放布局。只改阅读区的 px 会让同一窗口出现两套字号,还强迫用户填一个只对应 --text-base 的数字(ADR 0180)。
D232自定义全局界面字体(由 D598 修订) 设置 → 基础 → 外观新增可搜索的字体选择器(触发器以当前字体的字样预览)。选择结果持久化为 AppSettings.fontFamily(CSS 字体栈);缺失时保持内置 --font-sans 令牌栈,渲染器通过覆盖根元素的 --font-sans 应用字体栈,无需重载。四款内置字体——Geist、Inter、Noto Sans SC、LXGW WenKai——以 woff2 格式按 SIL OFL 1.1 本地发布并附许可证文本;每个自定义字体栈都追加 CJK 回退层,等宽字体栈保持不变。系统已安装字体由 Electron 主进程仅使用平台工具枚举(macOS 用 system_profiler、Windows 用 PowerShell、Linux 用 fc-list),去重/排序/过滤并缓存 60 秒,通过新增的允许通道 pi-desktop/app/systemFonts 暴露;主机协议 v9 与存储架构 v10 不变。用户需要 Codex/dbx 风格的全局字体偏好,但沙盒化渲染器无法枚举系统字体,而针对仅渲染器偏好不应扩大主机 RPC 面。内置 OFL 许可字体保证每个可选字体均商用安全且可离线使用,60 秒缓存限制了 macOS 枚举的耗时(ADR 0083)。
D598应用不再随包发布任何字体修订 D232 / ADR 0083:所有内置字体全部移除。apps/desktop/src/assets/fonts/(四个 woff2 文件及其 OFL 许可证文本)、apps/desktop/src/styles/fonts.css 以及 styles/globals.css 中的 @import "./fonts.css"; 均已删除,BUNDLED_FONTS、BundledFont 类型、FontOption.license、选择器上的许可证角标,以及全部八份语言目录中的 settings.fontBundled 一并移除。选择器保留「跟随系统」(持久化为空字符串)与系统已安装字体两类选项,枚举机制完全不变,仍通过白名单通道 pi-desktop/app/systemFonts 获取;已保存的字体栈若不再匹配任何选项,仍在「已保存」(custom)分组中排在首位。每个生成的字体栈现在追加纯系统 CJK 回退层 "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif,前面不再有内置字体。此前保存的指向内置字体的字体栈(例如 "Geist", "PingFang SC", …)按原样保留,显示在「已保存」分组,并穿过该回退层渲染。协议版本、schema 版本与存储迁移均不变。四款 OFL 字体重复提供每个目标操作系统本就具备的字形覆盖,占用 out/renderer 约 15.7 MiB 的 woff2(LXGW WenKai 7.6 MiB、Noto Sans SC 7.4 MiB),并为操作系统已安装列表之外的四个选项承担字体文件许可证告知义务。见 ADR 0298、E2E-126 与 E2E-092。
D230用户可配置的关闭行为与关闭到托盘Windows/Linux 的关闭行为是由 Electron 主进程存放在 <data>/close-behavior.json 的持久化偏好。ask 是尚未设置的过渡态:首次关闭弹出原生模态框(取消 / 关闭到托盘 / 退出),选定其一即永久保存,取消则保持窗口打开且偏好仍未设置。可设置的只有 tray 和 quit —— 设置 → 通用仅在 Windows/Linux 渲染两项单选段(关闭到托盘 / 退出应用),pi-desktop/window/closeBehavior/set 拒绝 ask,因此选择可以切换但无法退回到每次询问。tray 把窗口隐藏到 D216 常驻的托盘图标之下(点击恢复,菜单显示或退出);切换到 quit 不会销毁该图标,因为最小化到托盘仍然需要它。除了已经获准的退出之外,每一次非 macOS 的关闭都会被拦截;quitting 与 macOS 的关闭直接放行,quit 分支自己调用 app.quit(),而 window-all-closed 只在 tray 下保持沉默,因此启动探针与显式退出不受影响,常驻托盘也不会让 quit 会话继续活着。在 macOS 上 closeBehavior/set 同样以 INVALID_ARGUMENT 失败。边界看门狗跳过最小化和隐藏的窗口,所以隐藏到托盘的窗口不会被强制恢复。macOS 保持原生 Dock 生命周期(D216、ADR 0078、ADR 0090)。最小化本来就把窗口收进 D216 的托盘;缺口在关闭:它在 Windows/Linux 上直接退出。固定的关闭到托盘会让期待退出的用户意外,所以这个选择只问一次、记住、并可在设置里回访 —— 与 Codex 风格外壳让长时会话继续存活但不接管关闭按钮的做法一致。
D363显式退出在关机前确认修订 D230 / D216:Cmd+Q、应用菜单退出和托盘退出显示一次原生警告(取消 / 退出)。取消让应用继续运行,之后的退出会再次询问。确认走有序 before-quit 关机。已在 D230 对话框选择退出的关窗路径设置 quitConfirmed,不再问第二次。boot、supervision 和 capture 探针跳过对话框,应用内更新所执行的退出同样跳过:autoUpdater.quitAndInstall() 会先于 app.quit() 启动平台安装器,而该安装器一旦发现应用超出等待窗口就会中止,因此更新重启必须不经询问直接进入有序关机。见 E2E-204。显式退出会拆掉 host-core、sidecar 和进行中的工作;一步确认避免误触 Cmd+Q 或托盘退出。
D252Windows 任务栏保留原生最小化/恢复(显式的渲染器/菜单最小化条款被 D256 / ADR 0123 取代) 修订 D216 / ADR 0078:当聚焦的 Windows 主窗口从它的任务栏按钮被切换时,Electron 让原生 minimize 过渡走完,而不是隐藏窗口。任务栏条目保持可用,下一次点击任务栏会恢复并聚焦它。显式的渲染器/菜单最小化操作仍然隐藏到常驻托盘;macOS/Linux 的原生最小化仍然驻留托盘。不改动 IPC、存储、主机协议或后台工作。D216 无条件的 window.hide() 把任务栏发起的 Windows minimize 事件当成托盘隐藏处理,意外移除了任务栏条目,只给用户留下托盘恢复这一条路。
D236每个数据目录只允许一个桌面实例Electron 主进程在模块求值期间调用 app.requestSingleInstanceLock() —— 在 app.setName 之后(锁位于由应用名派生的 userData 路径下),且在 logger、持久化 outbox 或任何其他数据目录访问之前。没有拿到锁的启动直接调用 app.quit() 且不引导任何东西:readiness 与 before-quit 处理器都提前返回,因此它不会在运行中实例的数据目录内创建窗口、托盘、子进程或日志行。持有锁的实例通过与托盘“显示”操作相同的路径响应 second-instance,恢复并聚焦主窗口,若窗口已关闭或隐藏到托盘则重新创建。只有在未设置 PI_DESKTOP_DATA_DIR 时才请求该锁,因此 E2E 测试装置、截图装置和有意并行的 profile 保持原有的随时启动行为。IPC 通道、宿主协议和存储架构均未变化。第二次启动会引导出一个完整的第二份应用:在单写者 pi.sqlite(D002)上再开一个 host-core,在同一目录下再建一份 outbox 和日志树,再加一个托盘、一次启动器快捷键注册和一个更新器,导致两个 shell 在同一个数据库上出现分歧。再次启动应用是想看到已经在运行的那一个,把锁的作用域限定在安装本身也让使用独立数据目录的运行仍可启动(ADR 0094)。
D367宿主拥有的插件会话导入与归属 APIpi.session 增加 import、importBatch、list、get、listMessages、rename、delete;每个来源必须声明 contributes.sessionSources;host-core 生成 id,并把所有操作限定在 (pluginId, source, externalId) 归属内;导入永不激活项目/provider/模型绑定。Schema v14 增加 session_import_origins 与 sessions.deleted_at;协议 v11 不变。回收站保留转录,仅属主可清除。外部历史插件需要持久的导入/读取/更新/删除,而进行中的 session.getLlmContext 与核心 session.import 边界不是安全的插件归属边界(ADR 0200,E2E-214 / E2E-215)
D368显式插件项目 id 与宿主拥有的会话刷新受权限门控的 pi.project.create({ path }) 创建或复用项目并返回宿主生成的 id,不激活工作区;导入项可绑定已有 projectId,省略则保持未绑定,历史来源路径永不成为工具根。Electron main 在插件导入、重命名、删除成功后发出 pi-desktop/session/event/changed;渲染器复用 refreshSessions(),跳过的导入不发事件,已关闭的项目标签不会重新打开。向插件暴露 workspace.set 会改变用户的活动工作区,且插件写入需要宿主拥有的渲染器刷新路径(ADR 0201,E2E-216)
D370桌面操作的本地 MCP 控制平面(由 D372 修订) Electron Main 内可选启用的 Streamable HTTP MCP 服务器绑定 127.0.0.1,用持久随机 bearer token 认证,按回环主机名校验 Origin,写入受模式限制的连接清单,并委托给渲染器使用的同一组已注册 IPC 处理器。命名工具覆盖项目/会话/Agent 流程;pi_desktop_invoke 可达带风险标记的操作目录;危险操作需要 confirm: true;成功的外部变更复用渲染器会话变更事件。仅本地且启动时失败软着陆。外部 Agent 需要驱动运行中的桌面,又不能有第二套权限或持久化实现,也不能触碰推迟的远程 Gateway / WebUI 边界(ADR 0203,E2E-220)
D372收紧本地 MCP 控制平面边界修订 D370 / ADR 0203:首版目录为项目/会话/Agent/工作区流程加已审阅的读取;剥离秘密形态的参数;session/configure 视为危险;监听地址断言为回环;协议版本协商而非回显;包含 structuredContent 的结果有界;渲染器会话刷新仅限变更操作。首版目录仍暴露原生选择器、写秘密的 provider/OAuth/MCP 路径与未确认的 session/configure,并把会话读取当作渲染器变更(E2E-220)
D377插件桌面控制需要原生用户同意通过 pi.desktop.invoke 调用的 dangerous 桌面操作,需要插件给出 confirm: true,然后由用户在宿主拥有的原生对话框中作答;该对话框只显示目录中的操作 id、描述和有界的参数预览;关闭对话框、拒绝,以及没有对话框服务的宿主都以 PERMISSION_DENIED 失败。read/write 操作与 MCP 契约(D372)不变。控制器的 confirm 标志由调用方设置,因此插件(或其背后的模型)可以在没有人参与的情况下删除会话或把会话切到 auto,而任何由插件绘制的确认卡片都可能被提示注入的成绩单改换标签(ADR 0208,E2E-236)

P. 转录本存储决策 ​

身份证号主题决定基本原理
D119成绩单文件存储; SQLite 仅索引架构 v7:消息内容从 SQLite 移入 ~/.pi-desktop/sessions/ 下的每个会话 JSONL 文件 — <id>.jsonl(会话标头行,然后每条消息一个规范的块数组消息行,RFC3339 标记)加上用于重新生成分支的仅附加 <id>.revisions.jsonl。 messages 删除 messages/content_json 并成为纯索引(排序、提升过滤列、提取 text 馈送 FTS); message_revisions 将 messages_json 替换为 message_count,并且仅在数据库中跟踪 is_active。写入首先是文件,然后是索引事务;读取时跳过 unknown/torn 行并删除重复消息 ID keep-last;完全重写是临时文件+原子重命名;会话文件仅与其会话一起删除,而绝不会删除 age/orphan-swept。打开 v7 之前的数据库会将其存档为 pi.sqlite.v6.bak 并进行全新引导 — 显式中断重置,删除所有 v1-v6 迁移代码。 RPC 线路格式未更改,因此 Electron/renderer/importers 无需更改。数据库的增长不受携带工具args/results和思考有效负载的限制; codex/claude-code-style 每个会话文件使记录保持人类可读、可 grep 和可移植的状态,而 SQLite 则保持小型、快速的索引(列表、搜索、徽章)。选择了开发阶段中断重置而不是迁移机制。
D122独立对话会话分叉协议 v5 添加了主机拥有的 session.fork:将空闲源的完整活动规范转录复制到具有重新映射的 message/tool-call id 和继承的 project/provider/model/mode/thinking/permission 配置的新独立会话中。不会复制轮转、重新生成修订、通知、工件、会话授权、任意 scratch 输出、运行时状态、引脚状态和父子沿袭。被引用且仍存在的粘贴或导入输入会复制到子任务自己的 scratch 文件,并重写转录本及检查点中的路径(ADR 0023)。创建分支激活子分支; D109 保持不变,因为没有引入消息级分支树。单个主机拥有的快照保留了规范块和持久性一致性,同时为用户提供了 Codex 风格的发散工作流程,而无需将独立对话与重新生成变体混为一谈。
D134助理响应叉和可逆编辑(由 D137 取代的编辑子句) 完成的辅助工具栏显示“复制”、“分叉”、“编辑”和“重新生成”,但没有“删除”。 Fork 使用可选的 throughMessageId 调用现有主机拥有的 session.fork,生成一个独立会话,其规范记录以该响应结束。编辑使用相同的隔离子项,仅替换其中选定的辅助文本,并将 original/edited 尾部存储为两个条目的 D109 修订系列,以便现有寻呼机可以恢复其中之一。两者都需要空闲源、重新映射 message/tool-call id,并且从不共享源会话 id、运行时、转录本、修订版或提供程序缓存状态。响应级别的分歧和纠正应该保持可逆,而不改变源或让编辑的历史重用从不同助手内容构建的缓存运行时状态。
D199重新生成主机 RPC 锁下存档的分支session.saveActiveRevision 在状态锁下的一次主机调用中执行读取、分支存档和 revisionCount / activeRevision 标记,替换 Electron 主程序的 session.get + session.replaceMessages 读取-修改-写入。标记仅重写 root 用户的转录行并在写入时重新读取文件,因此同时附加的行会保留下来; Electron main 首先排空持久性发件箱,并跳过存档并发出警告,而不是存档不完整的分支。 session.replaceMessages 在重写过程中携带每条幸存消息所属的 turn_id,并且仅对于在呼叫持续时间内拥有整个记录的呼叫者而言才被记录为安全 (ADR 0060)。助手和工具消息通过 ADR 0041 发件箱异步到达 SQLite,因此渲染器端快照可以早于回合的最终消息 - 然后整个转录文本重写从转录文件和索引中将其连同每行的 turn_id 一起删除。只有主机可以原子地读取和写入转录本。

问:仍然推迟 ​

  1. 准确的市场域名/提供商 ID
  2. 私有市场授权机制
  3. 签名密钥分发操作细节
  4. 远程目录更新通道详细信息(URL/signature)
  5. 每个供应商预设的确切推荐默认模型

完整的开放列表位于 open-questions.md 中;这个 部分仅镜像 marketplace/catalog 项目,但仍然没有阻挡任何内容。

U. 设置铁路图标 ​

身份证号主题决定基本原理
D143设置目录导轨图标五个设置目标使用固定 Lucide 字形:基础 = SlidersHorizontal、模型配置 = Bot、导入 = Download、项目存档 = Archive、信息 = Info。此导轨上未使用 Refresh/rotate 字形。先前的映射重用了 Settings/RefreshCcw/RotateCw,它读取为通用 gear/reload 而不是目标语义;单色 Lucide 保持紧凑目录可扫描。
D166设置目录分为 AI 和快捷方式目的地设置栏按顺序从五个增加到七个目的地:基础知识 (SlidersHorizontal)、全局 AI/AI(Sparkles,新)、快捷方式(Keyboard,新)、模型配置 (Bot)、导入 (Download)、项目存档 (Archive)、信息(Info)。权限和上下文管理从基础转向新的人工智能目标;键盘快捷键从“基础”移动到新的“快捷键”目的地;开发人员从基础转向信息。基础仅保留外观和默认值。这取代了 D133 的五个目标 count/order 和 D143 的五个图标集(添加 Sparkles 和 Keyboard),而不更改任何设置的语义、全页 shell 或轨道指标。基础知识积累了六张不相关的卡片,将全局人工智能行为(权限模式、上下文压缩)和快捷方式配置与外观和感觉一起埋葬;拆分它们为每个关注点提供了一个可扫描的主页,同时在模型配置中保持 provider/connection 配置独立。

R. 未来的决策规则 ​

  • 架构边界更改需要新的 ADR
  • 实现默认值可以在此日志+相关规范中更新
  • D001–D010 的任何逆转都需要明确的基线碰撞

S. Composer 输入决策 ​

身份证号主题决定基本原理
D123Composer 斜线命令,三个来源在编辑器的位置 0 处键入 / 将打开一个内联命令菜单,其中合并 (a) <workspace>/.pi/prompts/*.md 和 ~/.pi/agent/prompts/*.md 中的 pi 提示模板(项目在名称冲突时覆盖用户全局;frontmatter session.get/session.replaceMessages),(b) 通过在与调色板搜索共享的一个注册表中定义的斜杠别名的内置调色板命令,以及 (c) 插件调色板命令。发送时,builtin/plugin 调用通过现有渲染器开关/commandPalette/execute 在本地执行,无需创建会话或提示模型;模板调用在持久化之前在 Electron 主 agent/prompt 处理程序中扩展(来自 pi-agent-core 的 parseCommandArgs + substituteArgs),持久化 content = expanded 加上一个携带键入表单的新可选 command 字段;未知的 /foo 作为文字文本发送。重用 pi 的精确 CLI 语义和模板资源,保持代理重新设定种子的准确性(重新设定种子重播 content),并通过将键入的调用呈现为芯片(ADR 0024)来保持脚本的可读性。
D124@ 文件引用是纯文本灯光引用令牌边界处的 @ 在工作区索引上打开一个模糊文件菜单,该索引由新的仅限 Electron 的通道 pi-desktop/fs/index 提供服务(文件+目录、具有忽略设置步行回退的 git ls-files -co --exclude-standard 快速路径、带截断标志的 8000 个条目上限、短 TTL 缓存、工作区根目录、无工作区软失败)。接受会为文件创建规范的 @rel/path 引用(当路径有空格时引用 @"a b.txt" ),并插入 @dir/,目录中不带尾随空格; D209 修改已完成的文件草稿演示,但不更改发送的纯文本路径。该模型遵循 Plan 和 Agent 中的读取工具的参考。没有提示内容内联或提供程序附件转换;剪贴板文件具体化由 D197 定义。匹配 pi CLI 面向模型的语义,避免上下文膨胀和截断规则,并使用户驱动的文件浏览保持在每个 ADR 0019 的代理 tool/permission 路径之外。
D125Composer 自动完成交互和 IME 合约**一个菜单组件锚定在编辑器上方,完整的编辑器宽度,焦点始终保留在文本区域中。打开时的按键:↑/↓ 循环环绕,Enter/Tab 接受,Escape 仅关闭菜单(优先于输入框的 clear/blur Escape),实时输入过滤器;当某个项目突出显示时,Enter 永远不会发送,并且空结果列表将被视为已关闭。菜单在外部鼠标按下、模糊、删除超过触发字符或会话切换时关闭。所有按键处理和触发检测都位于 IME 防护后面(`isComposing
D197Composer 剪贴板文件成为会话草稿引用当 Composer 剪贴板包含一个或多个操作系统 files/images 时,渲染器会拦截文件粘贴,通过仅限 Electron 的 pi-desktop/composer/pasteFiles 通道传输有界字节和 name/MIME 元数据,并包含持久会话 ID。 Electron main 验证会话、清理名称并在 <data_dir>/scratch/<sessionId>/pasted/ 下写入唯一文件; D209 修改呈现草稿,同时保留每个返回的绝对路径作为发送给代理的带引号或不带引号的 @ 引用。纯文本粘贴仍然是原生的。家庭输入框首先创建或重用持久会话。剪贴板字节永远不会进入提示符、工作区或工件存储,并且会话删除会删除带有临时根的粘贴文件。修复了损坏的 file/image 粘贴路径,而不会弄脏项目或发明特定于提供程序的二进制提示管道;它重用了现有的暂存遏制和生命周期合同。
D209Composer 文件引用将紧凑显示与规范提示路径分开完成的工作区文件选择和物化剪贴板文件成为渲染器拥有的、会话范围的参考芯片。芯片始终仅显示叶名称,通过 tooltip/accessibility 元数据公开规范路径,并且可以在不删除临时字节的情况下将其删除。在提交之前,引用会在可见草稿之后以稳定的顺序序列化,并使用 D124 的精确引用,因此仅供引用的草稿、斜线模板和 Agent/Plan/Goal 模式别名保留其当前行为。接受的调度会清除活动编辑器,但保留仅渲染器、回合范围的预序列化快照,而智能停止仍然可以撤消未应答的发送。该撤消操作会恢复原始文本和碎片,而不是预填充序列化消息内容;回复开始后中止保留部分记录并且不恢复任何内容。 Rejected/failed 发送保留活动草稿。 ComposerPastedFile.name 是经过清理的原始叶标签,而 path 保留 UUID 支持的绝对存储身份。未添加二进制有效负载、提供程序附件、主机 RPC 或架构。 (由 D362 修订:芯片是 contenteditable 草稿中的行内哨兵元素)长相对路径和 UUID 支持的临时路径淹没了提示行,即使只有代理需要它们。将表示与序列化分离可以保留精确的工具可读路径、重复名称标识、textarea/IME 行为和纯文本提供程序合约 (ADR 0070)。
D362接受的 @ 文件插入与粘贴相同的行内芯片修订 D209 / D125:从 @ 菜单接受已完成的工作区文件(Enter、Tab 或点击)会把触发标记替换为草稿中的一个私用区哨兵,并把该哨兵存到引用上,路径与剪贴板粘贴相同。芯片绘制在光标处;该确认不会发送。目录仍插入 @dir/ 文本。无哨兵的引用不再是可见草稿表面。工作区相对芯片在切换项目时仍会丢弃;绝对 scratch/paste 路径保留。不改 IPC、存储、主机协议或 schema。芯片改为行内后,文件接受仍会剥掉 @query 并存储无哨兵引用,因此不再绘制,Enter 会让文件消失。
D333工作面板浏览器是随应用打包的 pi.browser 插件工作面板 Browser 启动项、chrome 和代理 CDP 作为随应用打包的插件 pi.browser 交付(默认启用、可禁用、不可卸载)。访客 WebContentsView 和调试器仍由宿主拥有,只能通过受 browser.cdp 门控的公开 pi.browser.* 访问。访客页边界夹紧到调用插件视图。原始 CDP 走白名单(拒绝 cookies/storage/Target/Fetch)。宿主 BrowserPreview 仍是 Plan 安全门面,插件禁用时失败。不升协议/schema。用户需要整个浏览器像 Files 一样可选,但不能把任意网页放进沙盒插件视图。见 ADR 0170。
D211全局插件启动器可自定义的 openPluginLauncher 快捷方式在 macOS 上默认为 Option+Space,在 Windows/Linux 上默认为 Alt+Space。Electron main 在启动后全局注册它,并拥有一个在启动完成后预热的 620×440 无框工具窗口,以最靠近指针的显示器为中心;macOS 使用跨工作区面板,而 Windows 使用 host-core 低级钩子来保留默认 Alt+Space 组合键,并在该钩子不可用时保留聚焦窗口回退。沙盒渲染器仅列出已启用且就绪的面板插件,通过原始字符、无声调完整拼音或拼音首字母(加上规范化的 name/id/description)匹配中文名称,并通过现有插件面板主机打开选定结果。Up/Down 选择,Enter/click 打开,Escape/blur 隐藏,IME 组合输入期间从不分派。附加的 toggle/dismiss/shown IPC 仅存在于 Electron 本地;host-core 的附加快捷键 method/notification 仅携带绑定事件。插件面板需要一个键盘优先的入口点,使其在 PI-Desktop 未聚焦时仍可工作,同时不恢复完整 shell,也不削弱现有面板的 sandbox/permission 边界(ADR 0072/0076/0080)。
D212运行输入框阶段下一轮配置和停止轮保持吞吐量在活动回合期间,草稿文本和 mode/thinking/permission 选择器保持可编辑。单一提交槽位仅在草稿为空时显示停止;有内容时显示可用的发送,并将下一条提示加入队列。渲染器会投影每个会话的最新完整配置,并仅在终端事件之后调用现有的仅空闲 session/configure,因此运行时保持固定状态;等待 Plan/Goal 批准仍可进行门编辑。用户停止的部分答案保留 responseDurationMs;当最终提供商输出使用不存在时,可见思考+答案文本被估计为每个标记的四个 Unicode 代码点到可选的 responseOutputTokens 中。 Rust 转录本元数据保留这两个字段,精确使用获胜,并且 UI 标记仅估计吞吐量。准备下一个提示不应等待当前流,但主机安全需要不可变的运行中配置。停止的可见答案仍然具有足够的测量数据,可用于有用的、明确的近似生成速度(ADR 0073)。
D215Windows 保留的全局快捷方式回退在 Windows 上,host-core 为有效的 Alt+Space 默认绑定安装一个狭窄的 WH_KEYBOARD_LL 挂钩,因为 shell 可能会从 Electron 的 globalShortcut 中保留它。 hook 仅消耗匹配的和弦并发出 keyboard.shortcut({ binding: "Alt+Space" }); Electron 切换现有插件启动器。每当设置更改时,Electron 都会调用附加的 keyboard.setGlobalShortcut({ binding }),而 host-core 仅针对该精确绑定启用挂钩。其他平台和自定义绑定保留在 Electron 的全局快捷路径上。每当 PI-Desktop 未获得焦点时,Windows 的活动窗口系统菜单保留都会导致附带的全局启动器不可用。范围狭窄的主机回退会保留快捷方式,而不读取文本或扩展 plugin/renderer 权限 (ADR 0076)。
D217启动完成后预热插件启动器主应用及后端完成启动后,Electron 创建并加载保留的插件启动器,同时保持窗口隐藏。窗口创建使用一个共享的进行中 promise,因此预热期间收到的快捷键会复用同一次渲染器加载;若预热失败,则销毁未完成的窗口、清除 promise,并允许下次调用重试。每次显示窗口时仍会刷新实时插件目录。将 BrowserWindow 分配和渲染器启动移出首次可见调用,使启动器的显示延迟更稳定,同时不拖慢主应用启动,也不削弱插件目录的新鲜度、IPC 或沙盒边界(ADR 0080)。
D219插件启动器的最近使用历史启动器渲染器把每个成功打开过的插件 id 连同时间戳记录在渲染器本地的 localStorage 里(键 pi.desktop.pluginLaunchHistory,上限 24 条),并在查询为空时按最近使用顺序呈现。键入查询后,先按搜索相关性排序,仅在同等相关时用最近使用作为决胜条件,因此未用过的插件按名称排在最近使用的之后。存储损坏或不可用时降级为原有的名称顺序,不影响启动。这只是细化 D211 的呈现,不改变 IPC、主机 RPC、权限、协议 v9 或存储架构 v11。以键盘为先的启动器用户会反复打开同一批插件;记住上次使用顺序让最常用的目标始终只差一个 Enter,而用户一旦键入,相关性仍然是权威。
D221渲染器产物体积控制(内置 CJK 字体条款由 D598 取代) 渲染器构建显式设置 minify: "esbuild",因为 electron-vite 将其渲染器预设硬编码为 minify: false。pi-drop-legacy-font-fallbacks 插件以 enforce: "pre" 在 Vite 将 url() 登记为资源之前剥离逗号前缀的 woff/truetype src 条目,因为内置 Chromium 始终支持 woff2;唯一来源为旧格式的字体保持不变。BrandLogo 导入由规范母版派生的 192x192 标记 src/assets/brand/logo-{light,dark}.png,而非 build/ 中的 1024px 安装器图标,在不改变视觉标识的前提下细化 D079/D094。packaging-footprint.test.mjs 对这三项均有断言,使框架默认值或被回退的导入无法静默回归。两个内置 CJK 字体依据 ADR 0083 第 2 节保持不裁剪。out/renderer 从 31 MiB 降至 24 MiB 且无功能损失;未压缩的默认值与安装器图标导入属于意外成本,而 CJK 子集化会丢失用户内容中的字形,需要修订 ADR
D198Goal模式为第二种合约模式Goal 是第三种持久操作模式 (agent / plan / goal),它端到端地重用 Plan 审批管道。 EnterGoalMode 将活跃的 Agent 切换为 Goal 合约状态; SubmitGoal(title, markdown, question) 写入主机拥有的不可变 .pi/goal/<unique-name>.md 工件并插入一个待处理的 plan_approvals 行。单个审批表在 schema v11 中获得 kind 列(plan 或 goal,默认为 plan);这种类型(而不是预计的计划状态)选择提示、工件目录、提交工具和 i18n 命名空间,因此一个批准栏和一个执行队列同时服务于这两者。 Plan 和 Goal 是合约模式:它们共享一个工具白名单(Read/Glob/Grep/BrowserPreview/Bash 加上他们自己的提交工具)、针对持久模式评估的 Write/Edit/plugin/unknown 工具的一台主机硬拒绝、共享 *_IN_PLAN 错误代码以及对无人值守运行的 PLAN_REQUIRES_INTERACTIVE_SESSION 拒绝。经批准的 Goal 在 Agent 中自主执行:它选择自己的方法并继续工作,直到验证每个验收标准或边界阻止它,然后逐个报告标准。线 kind 是可选的,不存在意味着 plan,因此这是协议 v9 中的附加内容。Plan 回答“执行这些步骤”;用户还需要“达到这个结果,自己决定步骤”(Claude Code的计划模式和Codex的目标型运行)。在批准的合同中制定目标声明、验收标准和边界可以保持自治的可审核性,并且按类型区分一条管道可以避免出现不可避免的漂移的平行表、批准表面和许可边界。
D144侧边栏主镀铬为 14 像素扩展侧边栏主要镶边(新任务、插件、会话标题、页脚标识名称、配置文件菜单操作)使用 --text-base (14px)。 Project/group 标题和空状态副本使用 --text-md (13px)。部分标签使用 --text-sm (12px)。主要侧边栏内容不得使用微型 --text-xs 带。13px 侧边栏主体在 14px 聊天界面旁边感觉尺寸过小;仅碰撞原色铬可以保持密度,同时恢复视觉平衡,而无需全局类型比例变化。
D145禁用可编辑字段上的浏览器文本更正桌面渲染器中的每个文本 input 和 textarea 都会禁用浏览器文本校正:spellCheck={false}、autoCorrect="off" 和 autoCapitalize="off"。共享 Input/Textarea 原语默认这些值;原始字段(编写器、消息编辑、命令选项板、全局搜索、settings/plugins/projects 搜索、模型搜索、浏览器 URL 栏、提供程序模型组合)显式设置它们。复选框和非文本控件保持不变。编码提示、路径、模型 ID 和 URL 不得带有红色下划线或通过 Chromium/OS 文本更正自动更改; shell 是一个应用程序,而不是文档编辑器。

| D146 | 启动画面 + 动作令牌 | 虽然引导程序不完整,但渲染器显示品牌全窗口启动启动画面(徽标、shell 名称、标语、可访问的 app.starting、软进度条)而不是纯状态文本。在 ready 之后,飞溅会保持短暂的最小停留时间(~420ms),然后在安装的外壳上淡出(~280ms)。全局运动使用 CSS 令牌 --motion-duration-{fast,normal,slow} 和 --motion-ease-{out,in,standard} 以及共享的 overlay/surface 输入关键帧;交互式转换更喜欢这些标记。减少的运动会将 splash/overlay 运动折叠到接近于零并冻结进度条。崩溃 chrome 使用 app.uiCrashed。 | 启动是第一次运行的时刻,以前感觉尚未完成;简短的品牌标志无需装饰性戏剧即可传达准备就绪的信息,共享的动作令牌使 shell 过渡一致且更流畅,同时仅保留反馈。 | | D147 | 交互细节润色(选择、CJK 标签、动作填充) | 可复制表面在单色坡道上使用主题感知的 ::selection(文本-主混合)、caret-color 和 accent-color;聚焦环与透明色混合(无白色清洗)。高流量镶边(跳转最新、停止、菜单、搜索行、通知、工作面板选项卡关闭、品牌芯片)通过 --motion-duration-fast 进行转换。滚动条为 8 像素,悬停拇指更强。空主堆栈间隙为 24px。在 lang=zh-CN 下,部分标签会删除 uppercase/wide 跟踪。未定义的 --radius-token-row 被替换为 --radius-sm。 | 中性口音 + 动作令牌传递后残留的金色抛光间隙:浏览器蓝色选择、突然的悬停填充、中文 chrome 上仅拉丁文的标签样式以及一个未定义的半径令牌。 | | D150 | Composer 运行时芯片下降器 | Composer 工具栏芯片(Agent/Plan、思考、权限模式、模型 ID)将标签完全墨水化在 28px 胶囊内:芯片和标签行高为 --leading-compact,芯片不会在控件上使用 overflow: hidden 进行剪辑,并且模型标签使用水平省略号而不使用 leading-none。 autoCorrect="off"/autoCapitalize="off"/Input/Textarea/Input/Textarea 上的下降器在光线和黑暗中都必须保持可见。 | leading-none 加上截断溢出,模型 ID 和标签上的字形下降部分(例如 Agent)/接受编辑,使底部工具栏看起来被切断。 | | D151 | 发送重新固定的文字记录如下 | 通过发送、重试或重新生成开始回合始终会重新固定转录跟随模式,隐藏跳转到最新的药丸,并在新内容到达之前滚动到底部。手动滚动在回合过程中仍会暂停跟随;跳跃控制仍然是唯一恢复的非回合方式。这改进了 D071,而无需在每个标记上恢复强制滚动。 | 向上滚动查看历史记录的用户仍然期望他们发送的下一个提示能够登陆最新的交易所;发送后暂停跟随,将新回合隐藏在跳跃药丸后面。 | | D152 | 直接运行时流渲染 | 辅助内容直接通过增量 Markdown 块缓存渲染每个运行时流块。渲染器不添加 requestAnimationFrame 打字机状态循环。 KaTeX 的 Vite 内联字体仍然是本地资源,并被狭窄的 font-src 'self' data: CSP 指令所允许。 | 重复的动画循环可能会在持续流期间触发 React 的嵌套更新防护,而之前的 CSP 会阻止捆绑的数学字体并产生控制台错误。 | | D153 | 推理会话默认为最大思考 | (由 D303 取代) 新创建的会话(其继承的默认模型支持推理)从该模型的 pi 发布的 supportedThinkingLevels 中的最高规范条目开始。非推理模型和缺失的能力元数据从 off 开始;现有会议保留其持久选择。这改进了 D096,而无需添加提供程序覆盖。 | 具有推理能力的模型应该默认使用最强大的可用努力,同时保留明确的每会话选择和 pi-ai 的模型权威。 | | D264 | Composer 输入避开全量排序与冗余 DOM 写入 | @ 文件菜单用有界 top-K 选择从匹配列表中挑出可见行,而不再排序全部匹配;该选择返回的行集合与顺序与完整排序完全一致,而较小且按命令类型分组的命令列表保留完整排序。Composer 自动调整高度与 --composer-dock-height 发布都是幂等的:高度未变化时不做 DOM 写入,也不会使整个文档样式失效,height: auto 测量探针仅在文本框可能需要收缩时读取。增长到七行、超过第七行后内部滚动,以及 delete/submit 后的收缩均保持不变。当一次按键既未改变草稿文件引用状态也未改变光标位置时,两者都保持原有的身份与取值,因此草稿缓存序列化 effect 和自动完成触发检测不再每次按键都重跑。不改动 IPC、存储、主机协议或 schema。 | 在大型工作区中打开 @ 菜单时,每次按键都要对整个 8000 条的文件索引做一次完整排序才显示 50 行,还要为测量探针付一次强制同步重排,而每次按键新建的 fileReferences 数组身份又会重跑草稿缓存序列化 effect。代价是输入延迟而非正确性;与完整排序的顺序等价性由单元测试覆盖。 |

T. 发布交付决策 ​

身份证号主题决定基本原理
D126三平台发布交付(电梯D010)(amended by D364 / D603) 标签构建将矩阵生成的每个工件发布到 GitHub 版本:macOS dmg/zip (arm64)、Windows NSIS x64、Linux AppImage + deb (x64),每个都有块图和平台的 latest*.yml 电子更新器提要。发布提要会激活 D120 的 Windows NSIS 和 Linux AppImage 的应用内更新通道; macOS 保持通知和链接模式,直到签名通道合格为止。 NSIS 工件名称固定为无空格 (PI-Desktop-Setup-${version}.${ext}),因为 GitHub 资产 URL 会损坏空格。根据基线凹凸规则(基线 0.4.7),D010 的仅限 macOS 范围被提升;发布管道本身在 v0.1.1-rc.1/v0.1.1 上通过了端到端合格。无论如何,管道都会在每个标签上构建并验证所有三个平台;将安装程序保留为过期的 Actions 工件(保留 90 天)会阻止用户安装它们,而不会增加安全性。发布更新源是交付的重点:具有应用程序内通道的平台会静默更新,并且未来的平台回归会通过实际安装而不是未使用的工件来呈现。
D364Windows portable exe without installer(superseded by D603) Amend D120 / D126 / ADR 0022: tag builds publish a Windows x64 portable exe PI-Desktop-Portable-${version}.exe alongside the NSIS installer PI-Desktop-Setup-${version}.exe. The portable target does not write latest.yml. Packaged portable runs (PORTABLE_EXECUTABLE_FILE) use notify-and-link delivery. NSIS installs keep in-app download and quit-and-install. Data stays in the existing application data directory. Portable requests user execution level.Company environments that whitelist a single executable cannot run the NSIS installer. Applying the NSIS updater to a portable run would install the app, so portable stays manual (ADR 0197, E2E-211).
D603Windows portable delivery uses a normal ZIPAmend D364 / ADR 0022 / ADR 0197: tag builds publish a Windows x64 portable ZIP PI-Desktop-Portable-${version}.zip alongside the NSIS installer PI-Desktop-Setup-${version}.exe. The Windows release helper builds NSIS and ZIP separately, stamps ZIP app metadata with piDistribution = "zip", and keeps the ZIP target out of latest.yml. Extracted ZIP runs use notify-and-link delivery; the updater still recognizes PORTABLE_EXECUTABLE_FILE for older portable executables. Data stays in the existing application data directory.The self-extracting portable wrapper could trigger administrator prompts and did not provide a reliable normal executable identity for the taskbar. A user-extracted ZIP launches PI-Desktop.exe directly and preserves the manual-update boundary.
D260发布文档是一个版本载体稳定版本号提升必须在打标签之前更新每一处带版本号的载体:双语言的应用内变更日志及其测试清单、每个工作区的 package.json(包括之前被第三个工作区根 scripts/release.mjs 跳过的 docs/package.json)、Cargo 工作区版本与 host-core 的锁文件条目、APP_VERSION,以及 README.md 和 README.zh-CN.md 中声明的 <major>.<minor>.x 发布线。scripts/check-release-docs.mjs 校验全部这些;scripts/release.mjs 在提升版本号之后运行它,只要任一载体不一致就拒绝提交或打标签,--skip-docs-check 仅保留给刻意的非发布性版本提升。扩展 D164。仅有应用内变更日志这道闸门,导致已发布的文档落后:版本已到 0.10.8,两个 README 仍在宣传 0.5.x 线,而 docs/package.json 停在 0.5.8。标签是不可逆的,所以这项检查在标签存在之前运行,而不是作为评审礼节。
D371显式的未签名 macOS 首次启动助手(由 D406 和 D443 修订) 每个 macOS 分发都附带可执行的 PI-Desktop-macOS-open.command,放在 DMG 上拖入 Applications 手势下方的可见首启行。它只查找 /Applications/PI-Desktop.app 与 ~/Applications/PI-Desktop.app,校验 CFBundleIdentifier 为 net.aiuo.pi-desktop,仅在存在时移除 com.apple.quarantine,然后打开应用。永不使用 sudo,不接受任意路径,也不替代 Developer ID 签名或公证。未签名的 macOS 通道可能被 quarantine 拦截并显示误导性的"已损坏"提示,而仅限终端的 xattr -cr 说明比启动失败所需的范围更宽(ADR 0204,E2E-196b)
D443规范应用 ID 与 macOS 代码签名标识修订 D141 / D371 / ADR 0204:应用 ID 为 net.aiuo.pi-desktop(APP_ID、electron-builder appId、macOS CFBundleIdentifier、Windows AppUserModelID)。开发用 macOS 宿主为 net.aiuo.pi-desktop.dev。不要在 afterPack/afterSign 对未签名包做 adhoc 签名:嵌套 Electron helper 此时尚未签名,codesign 会报 code object is not signed at all。见 ADR 0278、issue #524。所有者域名为 net.aiuo.pi-desktop。未签名包的 Identifier 绑定推迟到有安全的 helper 签名路径之后。

V. 扩展激活决策 ​

身份证号主题决定基本原理
D192每个扩展类型共享的激活范围**插件、用户 MCP 服务器和用户技能均带有 enabled: boolean 加上 `scope: { mode: "global" \“项目”;项目: string[] }. enabled stays separate so switching an extension off never discards its project list, and the three-state control (off/projects/global) is derived, not stored. Matching is case-insensitive, trailing-separator-insensitive and subdirectory-inclusive; a missing scope resolves to global; a projects-mode extension is inactive in a session with no project. Scope is enforced both when a per-turn catalog is assembled and again at dispatch (tools.execute, UserMcpRuntime.callTool, loadUserSkillBody`,命令执行)。面向代理的表面在会话的项目上进行过滤,面向应用程序的表面在活动窗口的表面上进行过滤;主题根本没有范围。**
D193用户拥有的 MCP 服务器不需要插件host-core 按 MCP ID 在 ~/.agents/servers 或 <project>/.agents/servers 下保存单个 JSON 配置文件;启用状态和项目覆盖只存于 <data>/agent-capabilities/mcp.json。设置 > 智能体 > MCP 通过带级别的 mcp.list、mcp.active、mcp.upsert、mcp.remove 和 mcp.setEnabled 驱动;项目记录按 ID 或 label 在过滤关闭项前遮蔽全局记录。现有 MCP 编辑器和桌面 mcp/test 操作提供传输校验与连接反馈。用户拥有的服务器是可迁移配置而不是插件包。将文件与应用本地状态分离,可以在不修改用户文件的情况下让项目覆盖全局服务器,并让删除清理保持确定性 (ADR 0112)。
D194.agents 根目录中的用户技能是 Markdown 文档host-core 扫描 ~/.agents/skills 和 <project>/.agents/skills,接受直接 Markdown 文件以及 <skill>/SKILL.md,通过带级别的 skills.list 和 skills.active 暴露 frontmatter 的 name/description。单文件导入会把源文件物理复制到选定目录。启用状态和项目覆盖存于 <data>/agent-capabilities/skills.json,绝不写入文档;过滤关闭项前项目记录先遮蔽全局记录。技能仍是可迁移的 Markdown 文档,而应用本地状态文件把启用决定留在用户内容之外 (ADR 0112)。
D257能力页面合并为一个可编辑的工作台细化 D193 / D194 并取代 D202 的页面形态:设置 > 智能体 > 技能 / MCP / 子智能体三个页面都在一块抬升面板之上呈现单条工具栏(级别筛选做成带实时计数的分段控件、一个搜索框、项目选择器、主操作按钮),面板内部按级别分组标题分隔,每一行自带级别徽标。三个页面都通过早已存在的 host 调用完成新建、编辑和删除;新能力落在筛选器当前指向的级别上,主操作按钮直接写出该目标位置。破坏性的行内操作收进每行的溢出菜单,并且需要先解除保险再执行。忙碌状态只作用于发出请求的那一行,骨架屏只在首次绘制时出现,后续加载把已在屏幕上的行调暗,启用开关先乐观翻转、只在 host 拒绝时回滚。skills.reveal 接受 { id, level?, projectPath? } 并转交给本就同时解析两者的 skills.read;仅传 id 字符串仍然可用。子智能体保持全局唯一(没有筛选器、没有项目选择器),D193 / D194 / D202 里关于能力状态、优先级和 .agents 路径的规则一条都没有改动。这些页面只能列出能力,却无法管理它们:d24e1ee0 删掉了两个编辑器面板,而 create/update/remove/reveal 的 RPC 一直活着;级别是页面结构而不是筛选条件,于是两个分组标题就吃掉了本来就不高的页面的大半;一个未完成的请求会通过 busy={loading || busyKey !== null} 禁用所有控件;每次切换开关后都等待重新加载,列表又被换回骨架屏。显示项目级技能时也只发送了 id,而单靠 id 无法解析一条没有全局同名记录的项目记录 (ADR 0126)。

2026-07-28 — 插件市场、面板和高风险 API ​

  • 官方本地市场提供商可以对 browse/search/install .piplug 包进行校验和验证。
  • 插件面板通过 pluginBridge 在沙盒隔离窗口中运行。
  • 高风险插件主机 API(fs.write、net.fetch、剪贴板、openExternal)仅在明确授权的情况下可用。
  • 支持每个插件自动更新;权限扩展升级需要审查。

2026-07-28 — 官方插件市场存储库 ​

  • 专用存储库 vastsa/pi-desktop-plugins 是官方市场来源。
  • PI-Desktop 远程获取 catalog.json(缓存在 ~/.pi-desktop/plugins/market/ 下)。
  • 插件维护者将源代码与存储库脚本打包在一起,并通过推送到该存储库来发布。
  • 仅当远程获取失败时,本地捆绑目录才会保留后备状态。

2026-07-28 — 市场模板 + 详细信息窗格 ​

  • 官方仓库获得了实用的模板插件demo.workspace-summary和CONTRIBUTING指南。
  • PI-Desktop 市场 UI 现在通过 market.getDetail 打开包含自述文件、变更日志和版本列表的详细信息窗格。

2026-07-28 — 侧边栏类型平衡 ​

  • 扩展侧边栏主要镀铬使用 --text-base (14px) 而不是 --text-md (13px)。
  • Project/group 标题跳转至 --text-md;部分标签在 --text-sm 中保持次要地位。
  • 决策 D144:使微型标记远离主要的左栏内容,以便栏与正文可读性相匹配,而不改变全局类型斜坡。

2026-07-28 — 聊天 Markdown 散文重新设计 ​

  • 助理降价 (.prose-chat) 进行了重新设计,以实现更密集、更平静的文字阅读:更清晰的标题层次结构、带重音的块引号、更安静的列表标记、带边框的内联代码、zebra/hover 表格以及带有等宽语言标签的插入代码卡。
  • 思考散文在次要颜色/text-sm-plus 处重用相同的层次结构,因此推理在视觉上保持从属于答案。
  • Renderer 行为(流块分割、GFM/math、Shiki)不变;这是 globals.css + component/design 规范中的仅演示通行证。

2026-07-28 — 中性灰色口音(无蓝色品牌) ​

  • --ds-accent / --ds-accent-hover / --ds-accent-soft / --ds-info 现在解析为灰度(深色:白色→灰色-100→灰色-300;浅色:#1a1c1f→#303030→#5d5d5d)而不是 Codex 蓝色。
  • Markdown 链接、块引用、焦点环、插件 CTA、切换和选定会话环通过标记自动继承中性重音。
  • 项目色点和 docs/specs 更新为蓝色作为品牌口音。

2026-07-28 — 插件页面浅色主题令牌通行证 ​

  • Marketplace/plugin chrome CSS 删除了原始的 blue-slate 后备(#4f7cff、#2a3144、#121826,...),现在仅消耗 --ds-* 代币。
  • 选项卡、操作、搜索、卡片、权限模式和徽章通过中性灰色强调系统适应 light/dark。

2026-07-28 — Markdown 浅色主题纸质通行证 ​

  • 浅色 .prose-chat / .code-block 表面针对白色聊天纸进行了重新调整:更柔和的下划线链接、平坦的灰色代码卡(无泥泞阴影)、更安静的 blockquotes/tables/kbd/math 和二次思维墨水。
  • 深色降价处理在精神上没有改变(插入木炭代码,通过accent-soft的浅灰色链接)。

2026-07-28 — One Dark Pro 代码高亮 ​

  • 聊天围栏突出显示从 demo.workspace-summary/market.getDetail 切换为 PI-Desktop/--text-base。
  • 代码卡绘制单个编辑器表面(#fafafa / #282c34);嵌套的 PI-Desktop/--text-base/token 背景强制透明,因此不会出现双重清洗。

2026-07-28 — 禁用可编辑字段的文本更正 ​

  • 共享 Input/Textarea 原语默认为 spellCheck={false}、autoCorrect="off" 和 autoCapitalize="off"。
  • 输入框、消息编辑、palette/search 字段、settings/plugins/projects 搜索、模型搜索、浏览器 URL 栏和提供商模型组合遵循相同的合同。
  • 决策 D145:browser/OS 拼写和自动更正 chrome 在桌面 shell 中保持关闭状态。

2026-07-28 — 启动启动画面和动作令牌 ​

  • 引导路径绘制 StartupSplash(品牌标记、外壳名称、标语、进度条),直到 host/settings 引导程序完成。
  • 共享 CSS 动作令牌和 overlay/surface 输入关键帧、抛光对话框、搜索、Toast和交互式填充。
  • 决策 D146:飞溅是带有减少运动安全退出的启动反馈;目录获得 app.uiCrashed 并完成 zh-CN empty-home/custom 副本。

2026-07-28 — 交互细节完善 ​

  • 主题感知 ::selection、caret-color 和 accent-color 将 copy/edit 镀铬保留在中性灰色强调坡道上。
  • 悬停填充最新跳转、停止、composer-plus、搜索行、profile/notification 菜单和工作面板选项卡关闭使用共享动作令牌。
  • :lang(zh-CN) 下的 CJK 部分标签使用正常跟踪,无需强制大写。
  • 空主堆栈间隙固定为 24px;使用悬停拇指将滚动条细化为 8px;品牌芯片半径采用--radius-sm。
  • 决定 D147。

2026-07-28 — 工作面板/设置光表面抛光 ​

  • 轻型工作面板使用安静的 #fafafa 插入列,带有白色标题带,因此它与白色聊天纸分开,没有厚重的边框。
  • 浏览器 URL、通用字段控件、快捷键键帽、分段轨道和切换旋钮接收浅色主题表面和与设置药丸对齐的对焦环。
  • 文件树、差异标头、调整大小手柄和目标过滤器可通过共享运动令牌轻松悬停填充。
  • 浅色对话稀松布软化至 28% 墨水,因此升高的白色对话保持可读性。
  • 决定 D148。

2026-07-28 — 面向用户的 i18n 复制通行证 ​

  • 英语和中文目录重写了高流量的 shell 副本,使其远离内部 行话:本地服务代替 host/backend,AI 提供商代替裸机 提供商、项目而不是用户字符串中的工作区、市场刷新 而不是“来自仓库”,临时聊天,以及更平静的 status/error 措辞。
  • 空状态、入门、设置帮助、插件权限和通知 用简单的语言解释结果,同时保持稳定的 i18n 键和 插值名称。
  • 决定 D149。

2026-07-28 — Composer 运行时芯片下降 ​

  • 模型、模式、思维、权限芯片不再使用leading-none下 溢出剪裁;标签使用 --leading-compact,因此下行部分保持可见。
  • 长模型 ID 仍然通过 .model-chip-label 水平椭圆化。
  • 决定 D150。

2026-07-28 — 发送重新固定的文字记录如下 ​

  • 从发送、重试或重新生成开始一轮重新固定记录并 即使用户向上滚动浏览历史记录也会跳到底部。
  • 流跟随仅在活动回合期间手动滚动时保持暂停; 跳转到最新的药丸仍然会继续执行,而不会开始新的回合。
  • 决定 D151。

2026-07-28 — 直接运行时流渲染 ​

  • 助手响应现在直接显示运行时的渐进块 通过增量 Markdown 块缓存,无需每帧第二个 React 状态循环。
  • Renderer CSP 仅允许本地和 Vite 内联数据字体,允许捆绑 KaTeX 字形无需打开远程字体源。
  • 决定 D152。

2026-07-28 — 推理课程默认为最大思考 ​

  • 新会话继承应用程序的默认模型并选择其最高的 当该模型支持推理时,pi-发布了思维水平。
  • 非推理或未解决的模型保留 off,而现有会话保留 他们储存的思维偏好。
  • 决定 D153。在设置页持久化每模型 defaultThinkingLevel 后由 D303 取代。

2026-07-28 — 工作面板活动导轨 ​

  • 开放的工作面板现在将审阅、终端和浏览器保持在紧凑的状态 44 像素活动导轨,具有清晰的 active/open 状态。
  • 内容标题显示活动资源,直接关闭,并使用 用于所有打开的工具和文件资源的有界键盘可操作切换器。
  • 以前的水平滚动选项卡和隐藏的标题上下文菜单是 删除;工件驱动的面板打开和会话所有权保持不变。
  • 决定 D154。

2026-07-28 — 侧边栏 project/session 列表类型密度 ​

  • 侧边栏项目组标题和 session/thread 标题第一步标记更安静 比以前的机身镀铬尺寸如此密集的列表扫描得更干净。
  • 会话标题使用 --text-md (13px); project/group 标题和空状态 复制使用 --text-sm (12px)。主要镶边(新任务、插件、页脚)保留 在 --text-base。
  • D155决定;扩展侧边栏的主要列表被 D159 取代 内容。

2026-07-28 — 独立窗口和工作面板调整大小 ​

  • 本机窗口边缘现在仅调整 Electron shell 的大小;工作面板打开, 折叠、关闭和分隔符提交使外部边界保持不变。
  • 分隔线使用锚定指针增量、帧合并预览、回滚 取消,以及更广泛的稳定命中区域。不再有响应式夹紧 覆盖保留的首选宽度。
  • 删除了渲染器到主 window/resizeBy 通道,以编程方式调整大小 归因和面板特定的窗口状态偏移。
  • D156决定; ADR 0029。

2026-07-28 — 每个用户轮流使用单个助手工具栏 ​

  • 由工具调用分隔的提供商级助理消息仍然是规范的 模型重新播种、持久性和分叉边界的转录记录。
  • 聊天记录由一条之后的每一条 assistant/thinking/tool 记录组成 用户消息和下一个之前变成一个视觉助理,保留 订单,同时公开一个聚合元行和一个 Copy/Fork/Retry 工具栏。
  • 复制加入所有内容丰富的辅助片段;分叉并重试目标最后一个 内容丰富的助理记录。决定 D157。

2026-07-28 — 回合边界上下文检查点压缩 ​

  • PI-Desktop 现在会在每个 turn_end 之后、下一个之前评估上下文 提供商请求,具有短暂的软提醒和确定性的硬提醒 检查站警卫。
  • 持久的检查点记录重建模型上下文,无需删除或隐藏 可见的文字记录消息;精确的提供商溢出收到一个压缩的 重试。
  • 该实现重用 pi-agent-core 原语。 OpenCode DCP 仍然是 AGPL-3.0 行为参考,不是依赖项或复制的实现。
  • D158决定; ADR 0030。

2026-07-28 — 侧边栏排版与全球身体比例一致 ​

  • 扩展侧边栏会话标题返回 --text-base (14px),与 应用程序主体和主侧边栏镀铬; project/group 标题和空状态副本 返回到相邻的 --text-md (13px) 层。
  • 部分标签和辅助元数据保留在 --text-sm (12px),并且 现有的紧凑行间距、截断和侧边栏尺寸保持不变。
  • D159决定;取代 D155 并恢复 D144 的主要列表层次结构。

2026-07-28 — 无图标的输入框提示行 ​

  • 主目录和线程停靠的输入框提示行不渲染领先的品牌图标; 草稿文本和占位符墨迹直接与输入装订线对齐。
  • 规范徽标保留在主页英雄、侧边栏、本机应用程序中 身份、启动画面和关于表面。会话创建控制保留 他们专用的消息加号图标。 D160 决定; ADR 0031。

2026-07-28 — 平滑最新胜利会话切换 ​

  • 会话行现在立即确认最新选择,预取 深思熟虑的 hover/focus,合并详细信息,并保留最近的五个 用于温暖重访和背景重新验证的成绩单。
  • 取代的转录读取不再阻止最新的请求。工作空间 在摘要元数据允许的情况下,对齐与转录本 IO 重叠,而 导航生成仍然拥有唯一可见的提交。
  • 之前的完整文字记录保持为灰色、非交互式框架 而 React 准备更改会话的 Markdown 树;减少运动使用 静态进度轨迹。决定 D162。

2026-07-28 — 紧凑的侧边栏会话标题 ​

  • 扩展侧边栏会话标题使用 --text-md (13px),与紧凑型相匹配 project/group 层,而主要操作和页脚标识仍保留在 --text-base(14 像素)。
  • 保留行高、截断、缩进、粗细和侧边栏尺寸 不变。
  • D161决定;优化了 D159 的会话标题大小。

2026-07-29 — 固定工作面板的本机宽度预留 ​

  • 对接面板现在在打开时保持其承诺的 364..720px 宽度; 本机边缘仅调整 MainChat 的大小。
  • 打开、collapse/final 关闭和分隔符提交设置一个幂等本机宽度 目标。当工作区可以保留完整宽度时,聊天保持稳定 仅吸收否则不可避免的短缺。
  • Maximized/fullscreen 几何等待正常状态。持续的基界 排除预留 width/x 移位,并且背景工件无法更改 可见目标。
  • D163决定; ADR 0032(窗口扩展部分被 ADR 0033 取代: 工作面板是一个内部扩展坞,永远不会扩展操作系统窗口)。这个 取代 D156 和 ADR 0029 的相反部分。

2026-07-29 — 双区域设置应用内产品变更日志 ​

  • 在 packages/shared 中发送 EN + zh-CN 产品亮点并将其附加到 UpdateState.releaseNotes 从 Electron Main 当发现更新时。
  • 更新横幅和设置 → 信息显示紧凑的新增内容列表 没有新 Feed 或 IPC 域的活动产品区域设置。
  • 发布标记需要在标记构建之前更新两个区域设置目录。
  • D164决定;扩展 D120/ADR 0022。

2026-07-29 — 发布流程:强制双区域变更日志门 ​

  • 稳定的应用程序版本颠簸/标签必须更新 标签前的 packages/shared/src/changelog.ts (EN + zh-CN);运输 没有目录条目是发布过程失败 (D164)。
  • 编入发布运行手册§4.1,人工智能开发工作流程矩阵+ 禁止的做法、更改清单、AGENTS.md 和 scripts/release.mjs 头因此特工和人类撞到了同一扇门。
  • GitHub 自动生成的发行说明仍然仅限网络。

2026-07-29 — 上下文检查点中的有界原子工具批次 ​

  • 自动压缩现在限制了超大的最终并行工具结果 在检查点副本中进行批处理,而不是重复失败 转录本边界。
  • 保留的副本保留每个 call/result 信封,分发文本 使用显式 head/tail 截断标记公平地预算,并省略重复项 与提供商无关的详细信息;原始成绩单行保持完整。
  • D158决定;修改了 ADR 0030 之前阻止不可分割的政策 批次位于保留尾盖上方。

2026-07-29 — 助理答案中的安全懒人美人鱼图 ​

  • 通过动态方式完成答案散文渲染中的 mermaid 栅栏 仅在视口附近加载、主题感知的美人鱼块;部分流和 思考散文仍然是源代码。
  • 渲染被序列化并限制在 20,000 个源字符/500 个边缘。 严格的 Mermaid 配置加上 DOMPurify SVG 清理会删除链接, DOM 插入之前的嵌入媒体、外部 HTML 和 URL 属性。
  • 无效或过大的图表通过复制和视图控件回退到源; 没有 IPC、存储、进程、CSP 或外部网络边界更改。
  • 决定 D165。

2026-07-31 — 更薄的默认工作面板宽度 ​

  • 停靠的工作面板现在以 280 像素打开,而不是 420 像素,第三窄, 其固定夹变成244..720px。
  • WORK_PANEL_DEFAULT_WIDTH、WORK_PANEL_MIN_WIDTH(渲染器和 Electron Main) 和 .work-panel CSS 底板保持同步;分隔符双击 恢复新的默认值。
  • D154 的 364 像素地板尺寸适合 320 像素内容旁边的 44 像素活动导轨; 导轨变成了标题切换器,因此地板按默认比例缩放。
  • 持续更宽的宽度仍然有效,并且 720px 最大值保持不变。
  • D167决定;取代 D154/D163 和 ADR 0033 §4 中的宽度限制。

2026-07-31 — 项目档案演示重新设计(堆叠带由 D267 取代) ​

  • 设置 → 项目档案被重建为三个带:一个概述横幅 四个派生计数器、一个搜索 + Recent/Name 排序工具栏和一个分组 索引按固定/所有项目/按部分计数存档的顺序排列。
  • 行获得始终可见的披露控制、状态标签、单个元行 (路径、分支、会话计数)、相对上次活动时间和快速新建 行菜单旁边的任务操作;菜单现在将 create/edit 分组到引脚上方, archive/restore,以及按 Escape 或外部按键关闭并解散。
  • 归档记录保持分组和软化而不是过滤,因此 目的地仍然没有可见性切换(D133)。项目存储、搜索 匹配、八个批次的会话显示和激活语义是 不变。
  • D168决定;取代 D133 的平面列表演示。D267 随后用一条仅含说明的引导行和一个面板 取代了概述横幅与每部分面板,并停用四个页面级计数器;行剖析和分组规则 延续不变,分组计数仍保留在标题条上。

2026-07-31 — 插件页面重新设计(演示文稿由 D196 修改) ​

  • 原始插件页面被重建为四个带:一个概述带,带有四个派生带 计数器(已安装、已启用、更新、高风险访问),一个保留的标头 一个上下文主要操作和移动检查更新/应用自动更新/ install-package / load-local 进入溢出菜单,分段 安装/市场开关承载计数,以及选项卡主体。
  • 安装的行按状态分组为需要注意/可用更新/ 在一个细细分隔的面板内激活/关闭。 状态:“错误” | “load_error” and errorMessage 现在浮出水面而不是被删除;行 操作折叠为悬停显示的面板按钮和溢出菜单 (自动更新、作为危险项目卸载)位于始终可见的开关旁边。
  • 权限按风险列分级 07-plugins/13-plugin-permissions-matrix.md:带有塌陷风险的筹码 溢出在行以及详细信息表中的高/中/低部分以及 安装对话框。升级评论标签权限新版本添加为New, 安装队列根据差异删除声明的集合的重复数据。
  • 详细信息从嵌套侧边栏移至右侧表(稀松布、Escape 和 外部按下关闭、粘性安装操作、可选版本行)。 市场卡呈现字母组合字形并且从不获取 iconUrl,因此 渲染器不执行远程图像加载。
  • D169决定;取代平面列表、药丸选项卡和内联详细信息窗格 07-plugins/07-plugin-marketplace.md §7/§14。

2026-07-31 — Renderer 样式表拆分为每个表面的部分 ​

  • apps/desktop/src/styles/globals.css 成为仅导入入口点。的 规则移入同一目录中的 22 个部分,每个部分拥有一个表面: tokens、base、chrome、chat-shell、composer、sidebar-threads、 messages、prose、ui-kit、overlays、theme-overrides、 composer-menus、settings、destinations、projects、sessions、 work-panel、providers、chat-links、composer-autocomplete、plugins、 responsive。
  • 导入顺序是级联,不得重新排序:令牌和基础 首先,按构建顺序排列特征层,响应/减少运动尾部 最后,所以它仍然可以覆盖它之前的内容。
  • 分割是连续的——没有规则改变相对于另一个的位置。的 连接的部分文件逐字节地再现预分割文件,并且构建的 渲染器 CSS 前后字节相同,因此 Tailwind v4 解决了 @theme 块与 tokens.css 相同。
  • 样式断言通过加载有效级联 apps/desktop/test/helpers/styles.mjs,内联本地 @import 按声明顺序排列的行。测试不得直接读取部分内容。
  • 侧边栏会话样式同时存在于 sidebar-threads.css 和 sessions.css 中 因为原始文件将它们交错;文件头交叉引用。
  • 设计令牌量表现在位于 styles/tokens.css 中;守卫在 scripts/check-style-tokens.mjs 遍历整个 src 树并且不受影响。
  • D170 决定; 04-ux/07-ui-design-system.md 假定的单文件布局 §版式和 04-ux/08-component-spec.md 不再适用。

2026-07-31 — 插件技能激活和插件开发套件 ​

  • contributes.skills 已激活。 PluginRuntime.getSkills() 读取每个 在提示时声明技能文件,仅适用于授予的插件 agent.prompt.inject,并且只能通过收容守卫门控 fs API 使用。代理运行时呈现 # Plugin skills 部分,上限为 总共 16 KiB,每项技能 8 KiB — 它自己的预算,而不是 32 KiB 指令 ADR 0037 链 — 并将其排序在内置技能之后但之前 项目说明,因此用户自己的文件拥有最终决定权。运行时重用 技能摘要上的键,因此启用插件、撤销权限,或者 编辑技能文件会淘汰空闲运行时间,而不是重用陈旧的运行时间 提示。缩小路线图差距 R2。
  • 插件创作作为第一方包 @pi-desktop/plugin-devkit 提供, 它拥有 scaffold / check / pack 共享的一个实现 pi-plugin CLI,PluginScaffold / PluginCheck / PluginPack 代理 Electron main 提供的工具,以及插件页面的新插件 模板动作。 check 重现 host-core 强制执行的规则,因此通过 这意味着安装将通过; pack 仅写入存储(方法 0) .piplug 条目,因为 extract_zip_bytes 不接受其他任何内容。缩小路线图差距 R3 的模板和 contributes.skills/PluginRuntime.getSkills() 项。
  • 捆绑插件被拒绝作为交付工具:插件无法生成 .piplug(HOST_API_ALLOWLIST 中没有存档 API)并且脚手架将 需要高风险 fs.write.workspace 才能获得应用程序应具备的功能 自己提供。
  • 内置 plugin-development 技能仅针对插件工作区激活 — 工作区根目录下的插件 manifest.json,或加载的开发插件 在它里面——所以一个普通的会话只需支付三个工具描述的费用。
  • 监视开发插件并在保存时热重载,去抖 300 毫秒, 忽略 node_modules / .git / dist / target,上限为 16 个插件,并且 重新启动后重新武装。重新加载永远不会扩大权限集: 将清单与选取文件夹时批准的集进行比较,然后 新权限通过 PERMISSION_DENIED 停止重新加载,同时删除 权限确实生效。重新加载失败会保留手表,以便修复保存 恢复插件,并通过 toast 和 pluginChanged 进行报告 — host-core 没有用于运行时端加载失败的 RPC,因此注册表行不会 不移动到 load_error。弥补了 R3 热重载项目的路线图差距。
  • D171决定;记录为 ADR 0039。
  • 该决定的立即注入部分于同一天被替换为 D174:技能现在以目录和 Skill 工具的形式到达模型。开发套件, 热重载和工作空间门子句保持不变。

2026-07-31 — 从模板创建插件会打开文件夹 ​

  • 从模板创建插件现在还会打开所选文件夹作为 活动项目(workspace.set,它注册项目并切换到 聊天),而不仅仅是作为加载的开发插件。加载仅使插件 跑;开发需要工作空间内的源、代理、文件 面板和内置 plugin-development 技能均已读取,并且需要 用户通过“打开文件夹”重新选择同一文件夹纯粹是摩擦。
  • 激活通过现有的 activateProject 在渲染器中运行 操作而不是来自模板 IPC 处理程序,因此项目状态、侧边栏 项目列表和导航意图防护保持其单一所有者。
  • 成功 toast 区分两种结果:如果文件夹不能 作为项目打开,插件保持加载状态,Toast仅说明这一点, 而不是声明一个不存在的工作空间。
  • 加载现有的本地插件文件夹(加载本地插件)故意保留 它当前的行为:运行别人的插件不是切换的理由 用户的项目。
  • D172决定;无 ADR — 这完成了 ADR 0039 描述的流程。

2026-07-31 — 工作面板标题菜单:工具优先,无重复条目 ​

  • 统一的标题菜单列出了四个工具(审阅、终端、浏览器、 文件)首先,按固定顺序,然后 - 在分隔符之后,并且仅当 它们存在——文字记录打开的资源。列出之前的布局 每个打开的工具两次:一次在资源切换器中,另一次在 创建新部分,这使得“打开”和“切换到”无法区分。 现在,每个工具行都有自己的打开状态和关闭控制,因此 单行是该工具的全部功能。
  • 激活已打开的工具会激活其现有选项卡,而不是激活 将其替换为新的单例,因此浏览器保留其 URL。标题 操作簇固定在右侧 (margin-left: auto),因此 close/collapse 控件不再随标签长度滑动,并且尾随关闭插槽 每行始终保留,因此标签和空心点永远不会移动。
  • 菜单行拥有真正的 DOM 焦点(WAI-ARIA 菜单模式)而不是移动 突出显示:触发器的 ArrowDown/ArrowUp 在活动行或最后一行上打开, Arrow/Home/End 仅遍历行,Delete/Backspace 关闭焦点行,同时 菜单保持打开状态,重点关注其邻居,并且 Escape/Tab/selection 将焦点返回到触发器。仅会话切换即可关闭菜单 隐式 — 选择一行会显式关闭它,因此前一个 活动标签键自动关闭(每当背景工件出现时就会触发 更改了活动选项卡)消失了。
  • 缺少 panel.tabs.file 是裸文件选项卡显示文字的原因 file 标签;目录现在包含它加上 panel.tools,以及过时的 panel.openTool 已删除。
  • D173决定;无 ADR — 仅在 现有的工作面板架构(ADR 0033)。

2026-07-31 — 插件技能是模型调用的 ​

  • contributes.skills 在 agent.prompt.inject 之后的加载时间被激活。 每个条目可能是一个路径或{ path, id?, name?, description? };头条新闻 文档中提供 name / description,而清单中没有。
  • 基本系统提示仅包含一个目录 - 技能 ID、名称和 描述被削减至 240 个字符。正文不在提示中;模型 通过 Electron main 在本地提供服务的内置 Skill 工具获取一个 针对 plugins.loadSkillBody(id),因此 sidecar 永远不会保存技能文本。
  • 上限:每个插件 32 个技能,每个文档 128KB。清单没有 agent.prompt.inject 仍然有效——技能早于许可门——并且 运行时只是跳过它们。
  • 技能 ID 加入 packages/agent-runtime/src/runtime.ts 中的运行时重用密钥, 因此启用或禁用插件会重建运行时而不是服务 陈旧的目录。
  • 拒绝:面向用户的斜杠命令。技能是座席应具备的指导 当任务需要它时就可以到达,而不是用户必须知道存在的命令。
  • D174决定;弥补了“已解析但从未激活”的差距 07-plugins/14-plugin-roadmap.md R2。

2026-07-31 — 插件主题附带 CSS 文件 ​

  • contributes.themes 声明 { id, label, path, base? } 并要求 ui.theme。 path 是与插件相关的 .css 文件; base (light | dark, 默认 dark) 命名覆盖图层所在的调色板。
  • 主进程读取文件并运行 sanitizeThemeCss():无 @import,无 url() 在 data: 之外,没有不可解析的 url(,没有 javascript: / expression(,无标记序列,256KB 上限,每个插件 8 个主题。的 渲染器通过 plugin/themes 接收完成的文本并将其注入到一个 <style id="pi-plugin-theme"> 附加在应用程序自己的样式表之后。
  • AppSettings.theme 扩大为 plugin:<pluginId>:<themeId>。当提供 插件被禁用、卸载或加载失败,应用程序回退到 system 而不是渲染无样式的外壳。
  • 被拒绝:令牌-JSON 贡献。验证会更安全,但是 它只能表达我们想要枚举的标记;样式表可以让 主题到达令牌列表忘记的表面,并且消毒剂加上 附加顺序规则将风险限制在外观上。
  • 决定 D175。

2026-07-31 — 通过 stdio 和远程 HTTP 插件 MCP 服务器 ​

  • contributes.mcpServers 声明 { id, label?, transport } 加一 传输字段:stdio 需要 command / args / env,http 需要 url / headers。权限是独立的:mcp.server.local 用于 stdio, mcp.server.remote 为 HTTP。
  • apps/desktop/electron/main/plugin-mcp.ts 讲协议 2025-06-18 — initialize、tools/list、tools/call — 作为通过 stdio 或流传输的 NDJSON HTTP/SSE。预算:10 秒连接,每次调用 100 秒,8 个 tools/list 页,每个 4MB stdio 系列,每个服务器 64 个工具,每个插件 8 个服务器。连接是惰性的; 卸载后进行拆卸。
  • 发现的工具在中注册为 plugin_<pluginIdSafe>_<serverId>_<toolName> 现有的插件工具图,因此它们继承了审计跟踪、超时和 禁用交换机,没有新的路由。它们始终是 risk: "medium": 架构和描述来自第三方,因此自我声明的风险级别 不值得信赖。
  • env 和 headers 仅通过插件自己的设置进行解析 { "setting": "<key>" };主机环境永远不会被传递(D018)。 stdio 子进程获取 PATH、temp/locale 变量和声明的值 — 无 否则。 command 必须是裸路径名称或与插件相关的名称; url 必须是 https 除非主机环回。
  • 两种传输方式而不是单独的 stdio:托管 MCP 端点很常见 足以让 stdio-only 推送插件将其包装在本地垫片中, 这更糟糕——一个额外的过程和一个无法审查的代理。
  • D176决定; ADR 0038。

2026-07-31 — 驻留插件服务及其重启策略 ​

  • contributes.services 声明 { id, label?, autoRestart? } 落后 background.service,每个插件最多 4 个。插件调用 pi.services.register({ id, start, stop });经纪商在之后调用 startonLoad(5 秒预算)和 stop 在 onUnload 之前,因此服务永远不会上线 在插件自己的生命周期之外。
  • 服务存在于插件的 utilityProcess 中,因此崩溃会导致该服务崩溃 该进程和主管重新启动整个插件:退避 1 秒、2 秒、4 秒, 8秒、16秒上限为30秒;最多 5 次尝试;能存活 60 秒的过程是 健康并且计数器重置。 autoRestart: false 选择退出。最后一次之后 尝试插件保持 failed — 可见的故障胜过无声的崩溃 循环。
  • 每个服务状态 (starting | running | stopped | failed) 和 重新启动计数通过 plugin/services 读取并呈现为芯片上的 插件页面;每个转换都会发出 pluginChanged 和 reason: "service" 和 plugin.service.* 审核条目。
  • 手动启用/禁用优于主管:明确的操作取消 挂起计时器并清除尝试计数器。
  • D177决定; ADR [0040] (../../adr/0040-plugin-resident-services-and-message-bus.md)。

2026-07-31 — 仅声明主题的插件间消息总线路由 ​

  • pi.bus.publish / subscribe 需要 bus.publish / bus.subscribe 和 a contributes.bus 中的匹配条目:发布者列出具体主题, 订户列表模式。仅授予许可不会带来任何影响,因此 清单保留了插件所说和听到的内容的完整描述。
  • 主题是点分隔的段([a-zA-Z0-9][a-zA-Z0-9_-]*,≤8 个段, ≤128 个字符)。 * 匹配一个段; ** 匹配一个或多个尾随 段,并且可能只出现在最后。
  • 路由存在于代理中。消息中携带topic、from、payload, 主机分配的 at;发布者被排除在自己的扇出之外;交货 是一种新的单向 { t: "event" } 框架的即发即忘,因此楔形 订阅者无法阻止发送者。该框架也使得之前 存根 pi.events.on / off 真实。
  • 上限:每个有效负载 64KB,每个插件 16 个订阅,每个滚动 100 个发布 10 秒窗口,因 LIMIT_EXCEEDED / RATE_LIMITED 和审核行而失败。
  • 有效负载传送数据,而不是能力:接收消息授予 订阅者没有它尚未持有的任何内容,因此主题应被视为 在应用程序内公开。
  • D178决定; ADR [0040] (../../adr/0040-plugin-resident-services-and-message-bus.md)。

2026-08-02 — Bash 工具继承用户的登录 shell 路径 ​

  • 在 Unix 上,第一个 Bash 调用会探测用户的登录 shell 的 PATH — $SHELL(后备 /bin/zsh → /bin/bash → /bin/sh)带有 -lic 'printf %s "$PATH"' — so -l sources login files and -i 来源 交互式rc,匹配一个新的终端。探头限制为 5s, 每个进程缓存(OnceLock);仅保留最后一个标准输出行(rc 横幅被忽略),stderr 被丢弃(缺少 tty 噪音),并且 非零退出、缺少 shell 或超时静默回退到主机 路径。
  • 每个 Bash 子进程都会通过 cmd.env("PATH", ...); bash -lc 仍然在启动时重新运行 bash 配置文件 (conda/brew 挂钩可能会在注入的基数顶部添加 prepend/dedupe/reorder 条目)。 Agent 命令仍然是 POSIX bash;解析后的 bash 二进制文件没有改变。 Windows 使 bash -c 与主机环境保持一致(无变化)。
  • 修复了 macOS Finder/Dock 启动时 bash -lc 单独无法看到 nvm, pnpm,或在 ~/.zshrc / ~/.zprofile 中初始化的 Homebrew 工具。
  • D181决定; ADR [0045] (../../adr/0045-bash-inherits-user-login-path.md)。

2026-08-02 — 将进程日志路由到类别文件中 ​

  • app、host 和 agent 通道仍保留本地 NDJSON 文件,但每个通道 通道现在是包含重点 <category>.log 文件的目录。应用程序 记录使用显式 lifecycle/session/tool/permission/plugin/provider/ persistence/updater/diagnostics/terminal/runtime 类别;主机和代理 stderr 分为同一类别,时序线隔离 在 timing.log 中。
  • 每条记录都带有一个 category 字段。子 stderr 按行缓冲, 解码为 UTF-8,并在解码之前去除 ANSI 控制序列 坚持下来了。未知子输出进入 runtime.log。
  • 两个旋转文件的旋转仍为 5 MB,但该限制适用于每个文件 类别文件。记录器使用 UTF-8 记录和处理的字节长度 旋转和磁盘故障尽最大努力。现有的平面日志文件不是 迁移过程中删除。
  • D182决定; ADR 0046。

2026-08-02 — 上下文使用检查器 ​

  • 用紧凑的 Codex 式触发器替换超大的上下文环 结合了剩余容量环、Context 标签和百分比。悬停 和键盘焦点打开一个带有上下文窗口的非模式面板,确切地说 提供商 input/output/cache/reasoning 使用情况,聚合生成速度 tokens/s,以及按首次执行顺序排列的每种独特的工具类型。
  • 工具行聚合重复调用并公开调用计数、参数标记、 结果代币、总估计足迹、分享条和累积已知 持续时间。运行时间估计使用 pi-agent-core 现有的四个字符 - 每个令牌启发式;提供商报告的使用量仍然是权威的总量 UI 将工具行标记为估计值。
  • 生成速度是提供商输出和最终结果的完整回合快照 流持续时间;活动助理流不显示实时令牌率 柜台。
  • 上下文窗口总计来自所使用的匹配 pi-ai 模型元数据 由Agent sidecar 提供;提供商元数据和 128K 默认值仍然是后备方案 对于未知模型。
  • UiMessage.responseDurationMs、UiMessage.toolUsage 和可选 tool_end.toolUsage 事件字段是可加的,因此较旧的持久消息 并且同行仍然可读。
  • 检查器面板在文档正文级别呈现为固定的、 碰撞感知视口叠加。它遵循转录滚动和窗口 调整大小、围绕触发器翻转并夹紧到视口边缘而不是 被转录滚动容器剪切。
  • 检查器从相同的 pi-ai 模型解析其上下文窗口总数 记录传递给代理 sidecar,丰富 cached/discovered 模型行; 提供程序元数据和 128K 默认值仍然是未知模型的后备方案。
  • D184决定; ADR 0047。

2026-08-02 — 延迟每回合工具激活 ​

  • sidecar 保留完整的本地注册表,但仅发送模式的核心 工具和本地 ToolSearch 出现新提示。 (CompactContext 也被 此处始终处于活动状态,直到 D200 移除工具; D203 将其恢复为 new_context,再次始终处于活动状态。) Agent 遵循 pi 的编码代理核心 (../../adr/0047-context-usage-inspector.md/ADR/sidecar/ToolSearch),而 聊天保持 (../../adr/0047-context-usage-inspector.md/ADR/sidecar)。代理模式 ToolSearch/CompactContext, BrowserPreview、插件工具、Skill 和插件开发助手是 由有界紧凑目录条目而不是完整参数表示 模式。
  • 精确名称或功能搜索最多激活四个匹配项 下一个模型回合。 addedToolNames 允许 pi-ai 提供商使用本机延迟 搜索在加载点序列化这些定义;其他提供商使用 重建的活动刀具列表。激活会在下一个用户提示之前重置。
  • 主机权限、工作区和暂存控制、超时和审核 行为不变。持久化工具结果保留激活标记 有效的转录重建。
  • D185决定; ADR 0048。

2026-08-04 — 绑定提供商流恢复和诊断 ​

  • 提供程序请求设置现在具有一次有限的 pi-ai 重试。瞬息流, 网络,或流式传输开始后超时失败会进行同轮重试 具有短暂的可中止退避;失败的助手将从模型中删除 上下文和可见的助手 ID 被重用。
  • terminated 和等效的不完整流消息映射到 STREAM_FAILED。第二次故障仍然是终止状态并带有有限阶段, 计时、提供程序 status/code 和重试尝试诊断(如果可用)。
  • 突变恢复是明确有限的:使用 Edit 作为一个唯一的本地 替换,使用 Write 进行连贯重写,然后允许一个新的 不匹配后的 read/regeneration。第二个相同路径 Edit 失败或 失败的 shell patch 命令会发出终止工具提示,而不是 修复旧的补丁工件。
  • D186决定;请参阅 ADR 0050。

2026-08-04 — 使用保留尾部恢复自动压缩失败 ​

  • 自动阈值和提供商溢出压缩失败现在尝试 结束前确定性的、积极限制的保留尾部检查点 轮到了。先前的检查点摘要在可用时会被保留, 完整的可见转录本保持不变,并且宿主持久性加上 硬预算重新检查仍然是强制性的。
  • 摘要输入是针对模型窗口进行预检的,因此显然 过大的摘要请求会直接转到有界恢复路径 等待提供商拒绝或超时。
  • 生命周期事件用 fallback: "retained_tail" 标记恢复,因此 渲染器保持运行活动并显示警告。手动 /compact 仍然存在 快速失败,永远不会默默地放弃历史背景。
  • D158决定;修改 ADR 0030 并添加 ADR 0049。

2026-08-04 — 资源隔离主机 RPC stdio ​

  • Host-core 不再使用 Tokio 的 stdio 适配器作为其 NDJSON 控制管道。 一个命名的操作系统线程读取 stdin,一个命名的操作系统线程序列化 stdout, 保持每条消息的成帧和重试中断或短暂 NDJSON/EAGAIN 错误。
  • 这会关闭 Tokio 的阻塞池在发生恐慌后的进程退出路径 操作系统使用 Resource temporarily unavailable 拒绝另一个工作线程 (macOS 上的错误号 35)。未能创建控制线程将被报告为 启动错误;这不是未处理的线程生成恐慌。
  • 登录 shell 路径探测也使用 thread::Builder 并回退到 当可选帮助程序无法启动时继承的 PATH。
  • D187决定;请参阅 ADR 0051。

Plan 检查点和 shell 决策 (0.4.14) ​

较早的本地 Plan 操作状态决策仍记录为 D188 历史。 D189 是执行机构并取代 D188 和 ADR 0052.D190定义了D189使用的shell执行合约。仅限代理 并行制定的决定重新编号为 D191 (ADR 0055) 并被取代 由 D188/D189 提供;随之而来的三个决定为 D192, D193 和 D194。

身份证号主题决定基本原理
D188将聊天配置文件替换为 Plan 状态(被 D189 取代) **PI-Desktop 有 1 个 pi Agent 和 1 个产品选择器:`Agent计划. Plan is that Agent after entering planning state, never a second Agent, planner model, planner service, or permission mode. Agent remains the default. Persisted sessions, app defaults, and scheduled values stored as 聊天migrate to计划; the internal 页面 = “聊天”route may remain as a conversation-surface detail. Plan exposes读取, Glob, Grep, 浏览器预览, Bash, CompactCon text, and ExitPlanMode; it denies Write, 编辑, plugin tools, and unknown tools. Plan retains permission-mode selection: Bash prompts under askandaccept-edits, and runs without confirmation under auto`,因此 Plan 是计划意图而不是严格的只读安全配置文件。**
D189Plan 检查点工件、批准和执行纪元**相同的 pi Agent 使用 `AgentPlan, with Agent default. Plan calls SubmitPlan(标题, markdown, 问题)as the only tool in its assistant batch. Rust host-core writes the submitted Markdown bytes unchanged to a new immutable unique file under<workspaceRoot>/.pi/plan/*.md; it stores the relative artifact path, SHA-256, and byte size together with structured title/question fields in the existing plan_approvalsrow. No title/question wrapper is added and no prior artifact is replaced. The approval surface displays title, question, an artifact opener, absolute expiry, and status, and offers only Approve or Reject. Approve requires an explicitask, accept-edits, or autopermission mode, with Ask selected by default; Reject carries no mode. The approval expires at one absolute 30-minute deadline and usesPLAN_APPROVAL_TIMEOUT. The same plan_approvalsrow carriesexecution_idandexecution_statethroughqueued → 运行 → 已完成
D190可选择的命令 shell 目录和执行标识主机核心公开稳定的平台感知目录 ID:windows-powershell、cmd、git-bash 和 bash;平台目录仅包含该平台支持的 ID。 defaultCommandShell 保留在主机设置中,并且设置写入拒绝不可用或错误的平台 ID。如果持久选择稍后变得不可用,则有效 shell 会有意回退到第一个可用的平台 shell。 Bash 工具和 tools.execute 协议名称保持不变;每个回合都会固定有效的 shell ID 和方言,并且主机在使用 COMMAND_SHELL_CHANGED 生成之前拒绝过时的 ID/dialect。 Shell 标识是目录选择,而不是可执行路径哈希。 Bash 分别流式传输 stdout 和 stderr,使用强制的 60 秒默认超时和 1-300 秒覆盖,并且 cancellation/timeout 关闭完整的进程树。用户可以选择命令语言,而无需增加协议工具,而平台验证、显式回退和转固定目录身份可保持执行的可预测性
D604信任用户自己填写的网络端点对用户填写的 URL 修订 ADR 0243 / 0245 / 0247;沿用 ADR 0142 / 0257 / 0300。用户自己在设置里填写的 URL——市场源、git 远端、MCP OAuth 端点、生成图片 URL——改按"用户端点"策略判定:回环、RFC1918、CGNAT、link-local、ULA、site-local 与 .local 都可达,明文 http 也可用。这一切由一个开关决定:networkPolicy.mode(relaxed / strict),默认 relaxed,并取代此前按界面分散的确认(明文开关、WebDAV 的 allowInsecureHttp、networkProxy.allowFakeIp),旧的 allowInsecureUserEndpoints: false 迁移为 strict;首次明文访问会弹一次告知(insecureNoticeAcknowledged)。第三方内容在任何模式下仍只允许公网并把校验过的地址固定到连接:registry 记录、目录正文、目录内部的文档 URL、以及每一次重定向目标。云元数据、unspecified、multicast、reserved、documentation 在任何输入上一律拒绝。默认代理绕过列表增加 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16。不改主机协议、不改存储 schema:networkPolicy 只是既有 app settings 里的一个字段,写入时校验。拒绝用户自己填写的局域网地址并没有消除那次请求——它只是把同样的工作搬到应用旁边的 shell 或浏览器里,因此这条边界牺牲了功能,却没有阻止用户已经做出的决定。SSRF 风险在第三方内容那一侧,所以边界的那一半保持不变。见 ADR 0304。

2026-08-05 — 仅代理模式 ​

  • agent 是唯一的会话模式。之前的 chat 配置文件已重命名 read-only; host-core 仍然强硬否认 Write/Edit/Bash 对此,但什么也没有 在 UI 中可以选择、显示或命令它。
  • 模式门到处都是负值(mode != "agent" → 只读表面) 并且 chat 在每个主机写入路径上标准化为 read-only,因此遗留或 未知值失败关闭而不是拓宽工具。
  • BASH_DISABLED_IN_CHAT / WRITE_DISABLED_IN_CHAT 成为 BASH_DISABLED_IN_READ_ONLY / WRITE_DISABLED_IN_READ_ONLY。
  • 数据库打开重写 sessions.mode = 'chat' 和存储的 defaultModechat 到 agent — 架构 v7 内的数据修复,而不是版本碰撞。
  • D191决定,由D189取代;看到 ADR 0055。它删除了模式概念 返回为 Plan (D188/D189, ADR 0052/0053);该条目被保留,因为 read-only 规范化和它所描述的 chat 修复已发布。

2026-08-05 — 记录中的结构化工具结果呈现 ​

  • 扩展工具行不再呈现参数的 JSON.stringify 和 结果。纯渲染器模块将每个已知的有效负载映射到标记块:文件 并将内容编写为突出显示的代码,将命令编写为 shell、stdout 和 stderr 作为单独的块,Glob 结果作为可点击路径,Grep 命中按每个分组 文件,失败作为错误注释。未映射的插件有效负载降级为 label/value 字段和标记块;只有嵌套对象才会保留 JSON 主体。
  • pi-ai 结果包络携带 details 中的结构化有效负载, 将其重复作为模型的文本。仅渲染结构化的一半,因此没有 字节显示两次。
  • 折叠的行携带结果芯片(退出代码、match/file 计数、替换 计数、写入或读取大小、truncated、scratch),因此读取结果时无需 扩大。成功退出不会获得任何筹码。
  • 搜索结果遵循主机的 outputMode:分组命中、路径列表或 每个文件的总计。主机 notice(范围、剪裁线、读取窗口)渲染 作为它所限定的块下的中立注释,绝不作为错误。
  • 仅当没有 ReviewChangeCard 拥有一个时,编辑行才绘制自己的差异,保持 工作区编辑单一来源。内联权限卡共享相同 块渲染器的参数预览。
  • 块仅基于扩展构建,并且突出显示超过 100 KB 或 800行;列表和差异有上限并报告隐藏的剩余部分。
  • D192决定;仅呈现器演示,因此没有 ADR。参见 04-ux/08-component-spec.md §9 和 E2E-145。

2026-08-05 — 无声助手在出现错误之前重新运行一次 ​

  • 不再报告以没有工具调用且没有可见文本结束的回合 一样完整。运行时将空助手从模型上下文中弹出, 将无输出微移附加到系统提示符,并调用 agent.continue() 一次 - 重用可见的气泡 ID 并吞掉第一次尝试的 turn_end / agent_end,因此用户看不到成功的恢复。
  • 即使存在推理内容,空白可见文本也会触发它。的 观察到的失败正是这种形式:一个 2830 个字符的结论 思考和一个空的 text,对用户来说什么也没有。时机 log 保留 thinkingOnly,因此这两个形状保持可区分。
  • 微调位于 state.systemPrompt 而不是 prepareNextTurn, 仅塑造飞行中的回合,一旦循环,其上下文就会被丢弃 停止。仅当仍然不变时才恢复,因此并发提示重建 获胜。
  • 第二次沉默结束:可重试 EMPTY_MODEL_RESPONSE, 现有的助手错误行通过“重试”操作进行呈现。每重新运行一次 提示,因此同一提示中的溢出恢复不能多次尝试。
  • D193决定;恢复现有循环合约内,因此没有 ADR。参见 03-runtime/02-agent-runtime.md §5e、03-runtime/08-error-codes.md §3.2、 和 E2E-146。

2026-08-05 — 每个工具的输出预算、范围搜索和规定的协作规则 ​

  • 单个 256KB/4000 行上限被每个工具预算取代:48KB Read/Glob/Grep,Bash 标准输出 96KB,Bash 标准错误 96KB 尾部保留,2000 每行字符无处不在。搜索结果可重新获取——缩小搜索范围 模式,提前抵消——这样他们就赚得更紧的一半;失败的 命令的最后一行是可操作的行,因此 stderr 保留其尾部。
  • Read 使用 offset / limit 进行分页,并且从不拒绝文件大小。的 旧的 >512KB 拒绝说“使用 Grep 或 Bash 对其进行采样”,这就是 无分页读取成为无界 sed 管道。 Grep 取 path, include、outputMode、headLimit; Glob 取 path、limit;两者都订购 按修改时间,最新的在前。
  • 显式 path 禁用父忽略文件。如果没有这个,范围 搜索 node_modules 或 dist 返回零并将模型推回到 shell — 并且 Grep 无法读取自己的溢出文件。
  • 超出预算的 Bash 输出溢出到每个会话的暂存目录中,因此标记 命名一个真实的文件。 Read/Glob/Grep 未嵌入任何标记:content 保留 字节忠实,因此从中复制出来的文本仍然与 Edit 匹配,并且 窗口元数据加上 notice 具有与同级字段相同的事实。
  • 系统提示现在直接说明协作规则 - 在 用户的语言,每个工具批次之前的一句话,最多不超过一个批次 没有可见的文字,用文字回答而不仅仅是推理,完成结束 结束 — 并指出相对于 shell 等效工具,对作用域工具的偏好。 “更喜欢简洁、可行的答案”是唯一的附近规则,并且是一个推理 模型执行它时什么也没说。
  • D194决定;工具模式在不破坏调用者的情况下扩大,并且提示文本是 不是接口,因此没有 ADR。请参阅 03-runtime/16-tool-result-limits.md, 03-runtime/02-agent-runtime.md §7 和 E2E-147。

2026-08-05 — 用户拥有的 MCP 服务器和技能,范围按项目划分 ​

  • 插件、用户 MCP 服务器和用户技能现在共享一种激活形状: enabled 加上 { mode: "global" | "projects", projects: [] }。布尔值 与范围保持分离,因此关闭某些内容可以保留项目 缩小到的列表,off / projects / global 控制 UI 渲染是从该对派生的,而不是存储为第三种模式。
  • 匹配不区分大小写且不区分尾随分隔符,因为 macOS 和 Windows 都为我们提供了一个目录的大小写不同的拼写,以及一个作用域 路径覆盖其子目录,因此不必列出 monorepo 根目录 逐个封装。 projects 范围的扩展在会话中处于非活动状态 没有项目:“这些项目”是关于项目的声明。
  • 当构建每回合目录时会检查范围并在调度时再次。 会话的寿命比列出其工具的提示要长,并且模型可以使用工具 see 是一个它将尝试调用的工具,因此在一个地方进行过滤会留下一个漏洞 在重新确定范围和下一个提示之间。主题保持未定范围——外观是 应用程序范围内的功能,而不是每个项目的功能。
  • 添加 MCP 服务器很简单。 parseMcpImport 读取 mcpServers 记录每个自述文件打印以及 servers、裸图和单一对象 变体,从 url 推断 http,因为一半的配置省略了 type,并且 报告每个条目的跳过原因,因此十五分之一的错误条目并不致命。
  • 用户 MCP 服务器在首次使用时连接并缓存其工具;失败的 握手一直失败,直到用户对其进行编辑或按“测试”。保存更改 服务器是什么会断开连接,重命名它不会 - 陈旧 工具清单比缺少工具清单更糟糕。
  • 用户技能是一项 SKILL.md。 D174 的合约未受影响,这就是为什么 编辑器需要一个描述并将其放在正文上方:描述是 唯一进入提示的部分。
  • 四个选项卡而不是一个合并列表。插件已安装,MCP 服务器已安装 配置好,技能写好;他们的行需要一个连接灯,一个字节 分别是计数器和权限矩阵,一个列表将隐藏所有 位于最低公分母行后面。
  • D192、D193、D194 号决定;新的 host-core 注册表、新的 RPC 方法和 新的主进程运行时,因此 ADR 0053。请参阅 07-plugins/01-plugin-system.md 和 07-plugins/03-plugin-api.md。

2026-08-05 — 权限控制的外部路径和可移植的本机搜索 ​

  • Read、Glob、Grep、Write 和 Edit 的显式 path 参数为 根据持久会话工作区进行分类并之前从头开始 正态风险矩阵。在 ask 和 accept-edits 中,外部路径发出 具有所请求路径预览的现有权限卡;在 auto 中,它 无卡执行。允许一次和现有工具范围 允许会话授予仍然可用,而拒绝、超时和取消 返回 TOOL_DENIED 而不触及路径。
  • 批准的解析器再次规范化最深的现有祖先 执行时间,因此 .. 和符号链接转义无法跳过边界。的 外部位置不会提升为工作空间根目录;外部读取和 搜索保留绝对路径,外部突变保留在审阅之外 和工作区工件记录。
  • sidecar 公开主机的有界搜索控件(Read.offset/limit、 Glob.path/limit 和 Grep.path/include/outputMode/headLimit) 规范的 filesWithMatches 拼写;主机标准化公共 files_with_matches 和 files-with-matches 提供商别名。指导 更喜欢本机工具和可移植工作空间相对路径,以及 shell 搜索 作为有界的、特定于平台的后备。
  • D195决定;外部路径能力和扩大的搜索模式发生变化 host/runtime 边界,因此 ADR 0057。请参阅 03-runtime/03-tools-and-permissions.md、03-runtime/06-host-rpc-protocol.md、 03-runtime/15-workspace-ignore-rules.md、03-runtime/16-tool-result-limits.md、 和 E2E-019/E2E-019e。

2026-08-05 — 扩展页面密度和主题可读操作 ​

  • 扩展目标不再呈现四张数字概览 乐队。已安装、MCP、技能和市场仍然是单独的选项卡;选项卡 计数、已安装状态组计数和待更新警报保留 可操作状态,无需将其复制到静态摘要行中。
  • 共享主要和次要按钮使用语义重音、表面、文本和 边界令牌。主要操作反转当前主题表面;中学 动作在两个主题中都保持不透明的升高表面和可见边框。
  • 决定 D196 修改了 D169 的表述部分,但没有改变 插件、MCP、技能、市场、权限或运行时合约。请参阅 ADR 第0058章 07-plugins/07-plugin-marketplace.md 和 E2E-024N/E2E-060b。

2026-08-05 — 剪贴板文件成为会话临时引用 ​

  • 输入框现在可以区分本机文本粘贴和剪贴板有效负载 包含文件或图像。文件字节仅在之后发送到 Electron main 渲染器有一个持久的会话ID;家庭输入框创建或重复使用一个 转移之前。
  • Main 验证会话并在下面写入有界的、唯一命名的文件 <data_dir>/scratch/<sessionId>/pasted/。该草案收到普通 @absolute/path 引用,并引用空格,因此现有 Read/Glob/Grep 路径语义处理粘贴的材料,无需二进制文件 提示内容或项目突变。
  • 决策 D197 和 ADR 0059 定义新的 renderer/main 边界。参见 04-ux/08-component-spec.md §11.7–11.8, 03-runtime/01-ipc-protocol.md §13c, 03-runtime/03-tools-and-permissions.md §4b, 03-runtime/04-data-storage.md 和 E2E-102。

2026-08-06 — Goal 模式加入 Plan 作为合约模式 ​

  • 会话现在保留三种操作模式之一:agent、plan 或 goal。 Composer 左芯片周期 Agent → Plan → Goal → Agent,并且 /goal-mode (builtin.mode.goal) 是其 palette/slash 条目。
  • Goal 协商目标声明、验收标准和边界 的有序步骤。 EnterGoalMode 和 SubmitGoal 镜像 EnterPlanMode 和 SubmitPlan,写入 .pi/goal/<unique-name>.md 和一个待处理的 plan_approvals 行。 plan_approvals.kind(架构 v11)是唯一 鉴别器;针对另一种类型运行的提交工具失败 PLAN_KIND_MISMATCH。
  • Plan 和 Goal 是合约模式。主机硬否认 Write/Edit/plugin/unknown 工具、Bash 遵循权限模式规则、 *_IN_PLAN 错误代码、单一待批准不变量以及 PLAN_REQUIRES_INTERACTIVE_SESSION 拒绝预定运行均适用于 两者均根据会话的持久模式进行评估。
  • 批准后,会话变为 Agent 并且排队执行携带 种,因此 Goal 运行会选择自己的方法,对每个方法进行自我检查 接受标准,在规定的边界处停止,并报告标准 标准。
  • D198 号决定定义了这一点。请参阅 03-runtime/01-ipc-protocol.md §5.4, 03-runtime/02-agent-runtime.md §5b/§7.2a, 03-runtime/03-tools-and-permissions.md §10.1, 03-runtime/04-data-storage.md §4.6a/§7, 03-runtime/06-host-rpc-protocol.md §5.1, 03-runtime/08-error-codes.md、03-runtime/10-session-state-machine.md、 04-ux/04-builtin-commands.md 和 04-ux/08-component-spec.md §11。

2026-08-06 — 重新生成分支存档在主机 RPC 锁下 ​

  • 转动完成用于归档已完成的重新生成分支 从 Electron main 读取-修改-写入:session.get,然后 session.replaceMessages 在 revisionCount / activeRevision 上标记 用户根。助手和工具消息通过异步到达 SQLite 持久性发件箱 (ADR 0041),因此快照可能早于本回合的最终结果 消息和整个记录重写将其从记录文件中删除 和索引,以及每行的 turn_id。
  • session.saveActiveRevision 现在执行读取、存档和标记 在一台主机调用状态下锁定。该标记仅重写 root 用户的 转录行并在写入时重新读取文件,因此在 在此期间幸存下来。 Electron main 首先清空发件箱并跳过存档 并发出警告,而不是归档不完整的分支。
  • session.replaceMessages 携带每个幸存消息所属的 turn_id 整个重写过程中,并且仅对拥有该对象的调用者来说才被记录为安全的 通话期间的完整文字记录。
  • 决策 D199 和 ADR 0060 定义了这一点。参见 03-runtime/04-data-storage.md §4.9/§7, 03-runtime/06-host-rpc-protocol.md §4 和 E2E-118。

2026-08-06 — 上下文压缩变得难以察觉 ​

  • 压缩由主机驱动且安静。 CompactContext 工具 <context_management> 软边界微移,主机未确认 允许列表条目已消失,因此长时间会话永远不会花费模型轮询 被压缩并且永远不会为其增加转录行。
  • contextBudget() 保持 D158 硬限制和净空不变,导出 来自模型窗口的保留尾部目标而不是设置,并添加 硬预算的 70% 的背景限制。后台预计算也 自最新以来,至少需要增长保留尾部目标 检查点的基线,因此大尾巴无法在每个回合重新触发。
  • 生成和安装是分开的。检查点仅内置于 提供程序空闲窗口 - 当工具执行时,以及运行结束后 - 因此 摘要请求永远不会与流式传输共享提供商连接。 如果其底座仍处于活动状态,它会安装在下一回合边界或提示处, 它的锚仍然存在,并且仍然符合当前模型的预算; 否则,不变的阻塞硬边界会处理它。失败了 后台构建被丢弃,没有事件,没有持久性,也没有 ADR 0049 后备。
  • ADR/compaction_start 增益可选 phase,以及 compaction_end 获得可选的 status { generation, summaryTokens };两者 在协议 v9 中是附加的。成功的自动压缩通知 没有人。上下文使用检查器是唯一可见的跟踪,并且设置 根本不暴露任何压缩控件。
  • 决定 D200 和 ADR 0061 修改 D158 / ADR 0030 / ADR 0049。参见 03-runtime/01-ipc-protocol.md §6、03-runtime/02-agent-runtime.md §5.1/§7.1、 03-runtime/03-tools-and-permissions.md §0/§2/§10.1, 03-runtime/06-host-rpc-protocol.md、04-ux/06-settings-ia.md、 04-ux/08-component-spec.md §7.3/§8.3/§11.5, 04-ux/09-interaction-patterns.md §3A 和 E2E-084。

2026-08-06 — 重建上下文压缩以匹配 Codex ​

  • 上一轮引用了 Codex,然后在四个上实施了相反的做法 很重要。 Codex 仅同步压缩,通过以下方式测量整个上下文 默认情况下,为每个发出一个 ContextCompaction 项和一个 Warning 压缩,并有一个真正的面向模型的工具(new_context)。 D200 的背景 部分还指出 Codex 没有这样的工具,这是错误的。用户 在被告知可以逆转不可察觉性后,询问 Codex 的机制 他们早些时候要求进行一轮。
  • 压缩再次内联。后台预计算,其70%的限制, 增量范围触发,删除三个空闲调用点; 当上下文与 hardLimit 或模型交叉时,prepareNextTurn() 会进行压缩 称为 new_context,与 Codex 的 should_roll_over 匹配。
  • 检查点携带摘要以及最近的用户消息,仅此而已。圆周率 切割点仍然标记边界,但其分叉前缀和最近的尾部 被折叠回摘要输入中,因此没有消息离开模型上下文 未被发现——仅过滤尾部就会导致数据丢失的错误。 保留最新的优先到 20,000 个令牌,交叉消息被截断 而不是被丢弃,并且助理消息会带着他们的工具调用,所以 没有孤立的呼叫到达提供商。
  • 两个 Codex 系列都存在:摘要路径和 fresh_window 翻转 不需要摘要并安装固定的标记文本。家庭是一个 内部开关(构造选项,然后 PI_DESKTOP_COMPACTION_STRATEGY),设置和 i18n 中不存在,因为没有 用户可以从设置行判断权衡,Codex 不会询问他们 到。
  • new_context 是无参数的,带有 Codex 的逐字描述,位于 主机无确认白名单,并且永远不能分配给子代理。两个 预算提醒 — clamp(hardLimit * 0.15, 8k, 32k) 和 2,000 个代币 剩余 - 附加到当前回合的系统提示中,每回合领取一次 检查点窗口,并且从未保留或显示。
  • host-core 保持整个检查点链,每条记录有效,因为 每次压缩的记录行必须在重新启动、重写和分叉后继续存在。 compaction_end 丢弃 phase 并携带 mark 而不是 status;的 记录在每个检查点覆盖的消息后面绘制一个分隔行,并且 每一次成功的压缩都会引发一次警告。上下文检查器 保持其路线,现在是最新的标记。
  • 故意与法典存在三个偏差:摘要先于保留的内容 用户,因为 buildSessionContext 修复了该顺序,hardLimit 保持不变 “窗口 - 输出储备”而不是窗口的 90%(我们没有单独的 全窗防护),并且该工具在两个系列中注册,而不是 只有翻滚一。
  • 决定D203和ADR 0064修改D200 / ADR 0061并恢复D158 / ADR 0030的 可见性属性。请参阅 03-runtime/01-ipc-protocol.md §6, 03-runtime/02-agent-runtime.md §5.1、03-runtime/03-tools-and-permissions.md §2/§10.1,03-runtime/04-data-storage.md §2.1, 03-runtime/16-tool-result-limits.md §4、04-ux/08-component-spec.md §7.3/§7.4/§8.4a/§11.5、04-ux/09-interaction-patterns.md §3A 和 E2E-084。

2026-08-06 — Task 工具背后的有界子代理 ​

  • 会话可以将一项独立的工作委托给子代理并获得 返回一份书面报告。定义是 Markdown 文档——四个 内联内置函数(explorer、code-reviewer、test-runner、fixer)以及 ~/.agents/subagents/*.md 下已启用的全局用户文档,每次发射重新读取, 上限为 16,格式错误的文件降级为发射诊断。没有项目级子代理能力来源。
  • 定义从 Read/Glob/Grep/BrowserPreview/ 声明其自己的 tools Bash/Edit/Write 并且在声明无时为只读 (Read, Glob, Grep)。一个 委托永远不会从其会话继承突变权限,无法访问插件, 技能、模式或元工具,并且没有自己的 Task 工具。它的调用运行 通过相同的 tools.execute 主机路径,因此包含和许可 模式不变。
  • 定义可能会固定 model: <provider>/<model>,每次启动时解析一次 Electron 主要针对配置的提供程序(通过 id、供应商密钥或显示名称, 最多 8 个不同的提供商)。无法解析的引脚使 Task 调用失败,并显示 工具错误而不是回退到会话模型。
  • Task 仅在 Agent 模式下提供。扇出来自执行模式: 会话 Agent 运行 toolExecution: "parallel",所有其他工具都是 sequential,并且只有全 Task 批次同时运行,上限为 4 插槽和每个代表在其自己的 maxTurns(默认 24,最大 80)。的 sidecar 通过 PathMutex 序列化相同路径 explorer/code-reviewer 调用。
  • 父模型上下文获得有界报告(12k 字符),但什么也没有 否则。委托消息和工具行被发出并保存 消息 meta 中的 parentToolCallId / agentName,但运行时会跳过 重建上下文时它们,并且委托的终止崩溃为 工具结果而不是达到 Electron main 的回合处理。
  • 转录本将属性行嵌套在其 Task 行内一层,并且 让他们远离回合流和小地图。一台 Task 保持紧凑; 活动组中的两个或多个派生一张仅渲染器委派卡 具有聚合状态和主代理到委托拓扑。节点重用 现有行披露、结构化结果和嵌套行。拓扑结构是 源自实时和重新加载时的持久归因,不添加任何协议或 存储形状,并且既不发明委托依赖性,也不发明不可用的 父摘要节点。待处理的权限请求成为每个会话队列: 仅头应答、ID 匹配删除、中止时拒绝整个队列以及 卡片上注明了提出请求的代表以及后面有多少人在等待。
  • 决策 D201 和 ADR 0062 定义了这一点。参见 03-runtime/02-agent-runtime.md §5f/§7.2b/§8, 03-runtime/03-tools-and-permissions.md §10.2, 03-runtime/04-data-storage.md §4.7a, 04-ux/03-permission-ux.md §6a、04-ux/08-component-spec.md §9.9,以及 E2E-119。

2026-08-07 — 空屋使用直接底部任务入口 ​

  • 空聊天主页使用直接底层输入框并保留英雄和可选 在一个可滚动区域中加载内容。
  • 家庭输入框是该地区的最低保留兄弟,所以它仍然存在 清单内容在底部可见,同时短时间独立滚动 窗户。
  • 决策 D204 和 ADR 0066 定义布局。参见 04-ux/01-ui-ia.md、04-ux/07-ui-design-system.md、 04-ux/08-component-spec.md 和 E2E-063 / US-UI-64。

2026-08-07 — 空置房屋获得开发者入门指南(已取代) ​

  • 空置的房屋现在有四个紧凑的本地化开发商启动项目 英雄所以中间表面很有用,但不会成为营销面板。
  • 激活启动器可以预先填充并集中输入框。它不发送 提示或创建会话轮,并且短窗口滚动仍然保留 访问每个块。
  • D205 决定在 D206 取代 D204 之前定义了该修正案。参见 04-ux/01-ui-ia.md、04-ux/07-ui-design-system.md、 04-ux/08-component-spec.md 和 E2E-063 / US-UI-64。

2026-08-07 — 空置的家删除了开发者入门卡 ​

  • 空荡荡的家不再渲染开发任务卡、入门字形或 上下文快速操作。
  • 英雄、可选的入门清单和直接底部输入框保留; 任务输入直接在编辑器中启动。
  • 决定 D206 取代 D205,同时保留 D204 的可滚动布局。参见 04-ux/01-ui-ia.md、04-ux/07-ui-design-system.md、 04-ux/08-component-spec.md 和 E2E-063 / US-UI-34 / US-UI-48。

2026-08-07 — 工作面板获得直接键盘输入点 ​

  • Cmd/Ctrl + J 是名为 openWorkPanel 的可自定义渲染器快捷方式。 它揭示了活动会话在提交时保留的工作面板上下文 宽度,无需创建资源选项卡;上下文菜单仍然是 创建审阅、终端、浏览器或文件。没有活动会话并且 设置页面是无操作上下文。
  • 工件驱动的资源创建、会话所有权、后台事件 隔离、塌陷行为和宽度持久性保持不变。
  • D207 决定取代了 D128 的无全局快捷方式条款并修改了 D142 的条款 无启动器演示。参见 ADR 0068、04-ux/01-ui-ia.md、 04-ux/08-component-spec.md、04-ux/09-interaction-patterns.md 和 E2E-056。

2026-08-10 — 本机路径错误恢复,没有错误转向失败 ​

  • Read 仅限文件,Glob 仅限目录,Grep 接受文件或 目录;目录读取错误包括结构化 Glob 恢复参数。
  • 恢复的工具错误在其自己的行上仍然可见,但不再转动 包含处理组成终端破坏面。
  • D185 的延迟 Agent 搜索工具保持不变。决策 D208 和 ADR 0069 定义兼容工具和转录合同。

2026-08-10 — Composer 文件参考使用紧凑草稿芯片 ​

  • 已完成的工作区文件选择和粘贴的会话草稿文件现已上线 作为渲染器拥有的、会话范围的引用,而不是公开它们的 文本区域中的规范路径。
  • 输入框仅显示可移动的叶子名称芯片。完整路径仍然可用 工具提示和辅助技术,重复的叶子名称保留不同的 规范身份。
  • 紧接着现有提交调度程序之前,引用在之后序列化 具有未更改的带引号或不带引号的 @path 语法的可见文本。参考- 仅发送、斜杠模板和 Agent/Plan/Goal 别名保留其现有的 行为;失败的发送保留文本和参考。
  • 接受的发送仅保留其内存中的 session/turn-scoped 副本 预序列化草案,而未答复的智能停止仍然是可能的。那 停止恢复原始文本和碎片;回复内容开始后,中止 保留部分转录本并且不恢复草稿。
  • ComposerPastedFile.name 是经过消毒的原始叶子标签,而 path 保留 UUID 支持的绝对存储名称。剪贴板字节,暂存 遏制、清理、主机 RPC、存储模式和提供商有效负载仍然存在 不变。
  • D209 和 ADR 0070 决定修改了 D124 / ADR 的草案提交条款 0024 和 D197 / ADR 0059。参见 03-runtime/01-ipc-protocol.md §13c, 03-runtime/03-tools-and-permissions.md §4b, 04-ux/07-ui-design-system.md §8.1,04-ux/08-component-spec.md §11.7–11.8, 04-ux/09-interaction-patterns.md §8a 和 E2E-102/E2E-102a。

2026-08-11 — 全球角落使用受 Apple 启发的形状层次结构 ​

  • 固定半径使用常规 4/6/8/10/12/14/16/18/20/24px 梯子。紧凑且 中型桌面控件使用圆角矩形;明确的药丸和圆圈 标记仍然保留用于语义胶囊、等宽图标控件、 开关、轨道、头像和点。
  • 嵌套曲面的角对齐时遵循 Apple 的同心度规则: 外半径等于内半径加上边缘之间的插入量。 结构全宽外壳面板保持方形。
  • 输入框通过共享半径比例保持其 20px 可见半径。 实验性 corner-shape 渲染被推迟,因为 Electron 37 的 Chromium 138 运行时不支持它。
  • 决策 D210 修改了 D072 之前的像素保留半径阶梯,没有 改变其仅令牌强制执行。 ADR 0071 记录理由。参见 04-ux/07-ui-design-system.md §6.2 和 US-UI-72。

2026-08-11 — 全局插件启动器和不间断的下一回合准备 ​

  • macOS 上的 Option+Space 和 Windows/Linux 上的 Alt+Space 打开居中、 无框架插件启动器。它按中文名称搜索可启动面板, 完整拼音、拼音声母、标准化name/id和描述,然后打开 通过现有面板主机突出显示的结果。
  • 在积极回答期间,输入框草稿和模式、思考和许可 下一回合的选择仍可编辑,而发送仍处于禁用状态。的 主机报告空闲后,渲染器仅刷新最新的完整选择。
  • 用户停止的部分响应保留经过的持续时间和确切的提供商 存在时输出;如果没有最终使用,则回落到明显近似的值 输出计数,以便生成吞吐量能够在持久性和重新加载中幸存下来。
  • 决策 D211/D212 和 ADR 0072/0073 定义本机窗口 IPC, 运行中配置和转录元数据合同。参见 03-runtime/01-ipc-protocol.md、03-runtime/02-agent-runtime.md、 04-ux/07-ui-design-system.md、04-ux/08-component-spec.md、 04-ux/09-interaction-patterns.md 和 E2E-120。

2026-08-12 — 插件可以请求和发送本机通知 ​

  • 为了兼容性,pi.ui.notify 仍然是应用内 Toast。插件与 现有 notify 权限获得 getNotificationPermission, requestNotificationPermission 和 showNativeNotification API。
  • 本机插件通知对象仍由 Electron main 持有。该 API 返回尽力而为的 granted / denied / unknown / unsupported 状态, 因为 Electron 没有跨平台的只读权限查询。原生插件通知不会创建 持久的任务收件箱行或会话激活事件。 激活事件。
  • 决策 D213 和 ADR 0074 定义了公共 API 以及与 D117的应用程序拥有的任务通知合约。参见 07-plugins/01-plugin-system.md、07-plugins/03-plugin-api.md、 07-plugins/13-plugin-permissions-matrix.md 和 E2E-122。

2026-08-12 — 开发插件获得明确的权限上限重新加载 ​

  • 自动开发插件重新加载继续拒绝清单权限 添加,保留无声扩大的边界。
  • 插件页面现在提供 source: "dev" 行的重新加载。新的 plugin/reload 调用解析注册的文件夹,通过 现有的 Electron 运行时路径,并用当前的 宣布权限为其新的上限。
  • 决策 D214 和 ADR 0075 定义了该添加剂 renderer/main IPC 表面。 主机RPC和存储架构版本保持不变;看到 07-plugins/10-plugin-devex.md、07-plugins/12-plugin-ipc-and-host-services.md、 和E2E-022B。

2026-08-12 — Windows 保留插件启动器和弦 ​

  • Windows' Alt+Space 系统菜单保留可能会导致 Electron 的全局 当应用程序未聚焦时,快捷方式注册失败。主持人 核心现在拥有一个用于默认绑定的窄低级键盘钩子, 消耗和弦,并发出 keyboard.shortcut 至 Electron。 Electron 切换现有的启动器窗口;自定义绑定继续使用 正常 Electron 全局快捷路径。
  • 决策 D215 和 ADR 0076 定义了这种附加主机集成。协议 版本 9 和存储架构 11 保持不变。

2026-08-13 — 插件启动器渲染器在启动后预热 ​

  • Electron 在主应用及后端完成启动后创建并加载保留的插件启动器, 同时保持窗口隐藏。
  • 预热期间收到的快捷键会复用同一个进行中的创建 promise; 预热失败后仍可重试,每次显示窗口时也仍会刷新插件目录。
  • 决策 D217 和 ADR 0080 仅取代 D211/ADR 0072 中首次使用时延迟创建的条款。 IPC、host RPC、协议 v9 和存储架构 v11 均保持不变。

2026-08-12 — 审批卡聚焦工件并记住模式 ​

  • Plan/Goal 批准卡现在仅显示标题、主持人创建的工件 opener/path、拒绝和批准。已提交 question/description,状态, validity/deadline,并且不会呈现内联警告。
  • 选择的询问/接受编辑/自动批准模式会被记住 渲染器本地设备首选项并成为下一个批准的默认值; 询问仍然是安全的后备措施。
  • 标题派生的工件文件名保留 Unicode 字母数字字符,以便 本地化的计划标题仍然可以识别。主机现有内部 截止日期仍然是 compatibility/fail-closed 边界,但不会暴露 批准卡。
  • 决策 D215 和 ADR 0076 修改 D189/ADR 0053。协议和存储版本 保持不变。

2026-08-14 — 插件启动器记住最近打开过的插件 ​

  • 由快捷键唤起的启动器把每个成功打开的插件 id 记录在渲染器本地的 localStorage 里,并在查询为空时按最近使用顺序显示,因此上一次打开的 插件始终只差一个 Enter。
  • 一旦键入,搜索相关性仍是权威,最近使用只在同等相关的匹配之间做决胜; 存储损坏或不可用时降级为原有的名称顺序,不影响启动。
  • 决策 D219 细化 D211 的呈现。IPC、主机 RPC、协议 v9 与存储架构 v11 均保持不变。

2026-08-14 — 新任务在带上输入之前不进历史 ​

  • 点击「新任务」现在打开一份未持久化的草稿:渲染器不再在点击时调用 session.create(也不再复用旧的空草稿)。第一条消息 —— 或者粘贴的 文件附件,它们需要一个会话来挂靠 —— 才让会话落地,随后它以提示词为标题 出现在侧边栏历史里。
  • 第一条消息之前在输入框工具栏做的选择(模式、思考等级、权限模式、模型) 保留在草稿上,并在会话创建时应用;只动工具栏不会产生任何历史行。
  • 侧边栏的历史过滤器会丢掉标题仍为默认未命名值的会话,这既隐藏了本次 改动之前遗留的空草稿,也隐藏了将来任何意外产生的空会话。
  • 决策 D220 取代 D088/D093 中复用空草稿的条款:在首次输入之前不存在草稿 会话,所以没有什么可复用。协议 v9、主机 RPC 与存储架构 v11 保持不变; 改动局限在渲染器 store、Composer 与 Sidebar。

2026-08-21 — 新任务立即创建一个持久化的空槽位 ​

  • 「新任务」现在按当前项目或「临时」分组最近一个未归档的会话来解析。最新会话 为空时直接选中并复用;最新会话非空时,先真正创建一个空会话、刷新进侧边栏 并选中,然后才接受第一条提示。
  • 渲染器对同一分组的「新任务」请求做串行化,因此快速连点不会竞争插入多个空 会话。这条规则由项目分组和无路径的「临时」会话共用;当最新会话是较新的 非空会话时,更早的空行会被有意保留不动。
  • SessionSummary.messageCount 由 host-core 从 sessions.last_seq 派生,取代 标题启发式作为「空」的判定。空行可见;标题本地化和手动重命名仍是展示层 关注点。
  • 决策 D252 取代 D220 与 ADR 0084。启动首页仍是未持久化的渲染器草稿,但显式 的「新任务」是急切且持久的。messageCount 在协议 v9 内属于增量字段;存储 架构 v11 不变。参见 ADR 0113 以及 E2E-011b / E2E-011d / E2E-011e。

2026-08-14 — 工作面板快捷键改为切换而不只是打开 ​

  • Cmd/Ctrl + J(openWorkPanel)现在既能揭示关闭的工作面板,也能折叠 可见的工作面板,走的是与标题栏折叠控件相同的 store action。折叠会保留 会话的标签、活动资源、浏览器资源和已提交宽度,因此再按一次即可恢复 之前的界面。
  • 快捷键 id 仍为 openWorkPanel,使现有的按键覆盖继续有效;只有 设置 → 快捷键 里的标签改成描述「切换」。
  • 无活动会话与设置页里的空操作上下文、由工件驱动的资源创建、会话归属, 以及后台事件隔离,均保持不变。
  • 决策 D221 修订 D207,并推翻 ADR 0068 当初否决的切换方案;D128 与 D142 的其余条款仍然成立。参见 ADR 0085、04-ux/01-ui-ia.md、 04-ux/08-component-spec.md、04-ux/09-interaction-patterns.md 以及 E2E-056。协议 v9、主机 RPC、IPC 通道与存储架构 v11 保持不变。

2026-08-14 — 一条记录下来的更改是列表行,不是卡片 ​

  • 审阅更改卡拍平成 .tool-row 节奏上的单行(24px 行高、披露 V 形、 等宽路径、右侧 +/− 计数):1px 边框、2px 状态导轨、图标板、状态 胶囊、第二行元信息、阴影,以及浅色主题的白色填充全部去掉,只留悬停 填充作为唯一的行装饰。默认 280px 的审阅面板原先把大部分宽度花在这些 装饰上,而不是花在路径上。
  • 状态现在以 Git 风格的字母(A/M/D)承载,并用既有的按状态强调色 着色,因此从不单靠颜色传达;本地化的状态词 —— 以及已回滚状态,该行以 删除线呈现 —— 保留在行的无障碍名称里。展开的 hunk、带哈希保护的回滚、 aria-expanded/aria-controls 披露语义与消息归属均不变;回滚变成一个 无边框的文字操作。
  • 审阅标签的摘要条去掉图标、着色带和胶囊边框,标签主体也去掉内嵌填充, 使列表读起来是平的。自审阅标签变成会话更改历史以来就没有任何界面渲染过 的 Git-diff 时代 .diff-file* 规则被删除(.diff-file-counts 保留, 改名 .diff-counts)。
  • 决策 D222 细化 D097 的审阅标签与消息归属卡片的呈现;审阅的真值模型、 回滚语义与面板归属规则仍然成立。仅涉及呈现:参见 04-ux/08-component-spec.md §4.2/§4.4/§4.5 与 §5。协议 v9、主机 RPC、 IPC 通道与存储架构 v11 保持不变。

2026-08-14 — PI-Desktop 始终是一个常规 macOS 应用 ​

  • 插件启动器窗口向 Electron 请求了 visibleOnFullScreen 却没有带 skipTransformProcessType,于是 Electron 对整个进程执行了 TransformProcessType(kProcessTransformToUIElementApplication), PI-Desktop 从 Dock 和 Cmd+Tab 中消失。ADR 0080 的启动预热让这件事 每次启动都发生。启动器现在传入 skipTransformProcessType: true; 进程类型永不被转换,且 app.dock.hide()/app.setActivationPolicy 在主进程中仍然禁止使用。
  • 启动器保留 canJoinAllSpaces 与 FullScreenAuxiliary:它仍然覆盖每一个 常规 Space 以及 PI-Desktop 自己的全屏窗口。覆盖另一个应用的全屏 Space 这件事被放弃 —— macOS 把它留给附属类应用 —— 从那种 Space 里唤起启动器 会激活 PI-Desktop。
  • macOS 激活时除 activate 之外也从 did-become-active 恢复外壳,条件是 应用已启动、未在退出且没有可见窗口,因此 Cmd+Tab 与 App Exposé 能让 隐藏到托盘的窗口重新出现(D216/ADR 0078),而启动器和插件面板的激活 不会移动主窗口。
  • 决策 D223 约束 ADR 0072 的启动器窗口并补全 D216 的恢复路径;ADR 0080 的 预热与启动器延迟预算仍然成立。参见 ADR 0086、 03-runtime/07-process-model.md §5 与 E2E-127。协议 v9、主机 RPC、 IPC 通道与存储架构 v11 保持不变。

2026-08-14 — 揭示出来但没有资源的工作面板列出它的四个工具 ​

  • Cmd/Ctrl+J 揭示面板而不创建标签,这让主体成了标题栏下方的一片空白。 主体现在渲染一个空状态 —— 平铺图标、标题、一行文案 —— 后面跟着标题菜单 列出的同样四个工具(审阅、终端、浏览器、文件),呈现为朴素的 28px 入口行。
  • 一行调用的是与菜单中同名项相同的「创建或选中」路径,因此不可能产生重复 标签。这些行只在主体完全没有标签时存在;快捷键本身仍然什么都不创建, 从而保持 D128 由工件驱动的入口模型 —— 这些行是用户的选择,不是揭示面板 的副作用。
  • 空主体不暴露为 role="tabpanel",因为没有标签为它命名;它的行是位于 一个以「工具」命名的 role="group" 内部的按钮。
  • 面板的所有空状态(无资源主体与每个标签自己的)现在共用一个 WorkTabEmpty 组件,采用应用其余部分已经在用的比例 (.ext-empty、.projects-empty):38px 圆形平铺图标、标题、弱化文案, 并在 34ch 而不是 48ch 换行,因为面板可以只有 244px 宽。
  • 入口行保持克制 —— 悬停填充与聚焦环,没有卡片、没有主视觉插画、没有大 图标 —— 因此这不会把 D206 从空首页移除的东西重新引入,也仍然处在 design-system §14「不做营销式空状态」的范围之内。
  • 「打开项目」空状态里不放操作按钮:openProject() 会重置面板上下文并 隐藏面板,那个按钮会撤销提供它的那个界面。
  • 决策 D224 仅涉及渲染器。协议、主机 RPC 与存储架构保持不变。

2026-08-14 — 上下文用量检查器改为点击打开 ​

  • 检查器触发器不再在 pointerenter 或获得焦点时打开。一次点击 —— 或在 聚焦按钮上按 Enter/Space —— 切换该面板,那个为了让面板在触发器与 面板之间存活而设的 140ms 悬停宽限关闭计时器随之删除。阅读 token 分解、 滚动工具列表、在面板内选中文本,这些都比一只飘走的指针活得更久,而悬停 模型偏偏惩罚它们。
  • 关闭是显式的:再次激活、在触发器与面板之外的捕获阶段 pointerdown, 或者 Escape(它还会把焦点还给触发器)。原有的「触发器滚出视野即关闭 面板」规则仍然成立,body 层面的避让式定位也一样(ADR 0047 §6)。
  • 该面板不再是 tooltip。它带上 role="dialog" 与本地化的 Context 标签; 触发器把 aria-describedby 换成 aria-haspopup="dialog",并保留 aria-expanded/aria-controls。键盘用户得到的是一个稳定的面板,而不是 一个失焦即消失的面板。
  • 决策 D225 修订 D184/ADR 0047,且仅涉及渲染器。协议、主机 RPC 与存储 架构保持不变。

2026-08-14 — 运行行的命令属于它的头部,而不是正文 ​

  • 展开一个 run 行时,开头是它刚刚在折叠头部打印过的命令,于是读者见到的 第一样东西就是自己已经看过的那一行,而他们为之展开的输出从它下面才开始。 正文现在只承载命令产生的通道(Output、Errors);命令只出现一次,在头部。
  • 因此头部长出了正文不再提供的两样东西。V 形旁边的复制按钮给出命令的原始 写法 —— 头部的摘要被挤成一行,所以那不是被复制的内容 —— 结果则用词写出 (Done、Failed、Denied、Working…),配一个同色调的点。命令运行时 这个点脉动,取代该行的旋转指示器;含义由标签承载,所以色调只是装饰, prefers-reduced-motion 下它静止。
  • 扣下命令的开关是 hideSummaryArg,这个选项的含义是「折叠行已经显示了这个 参数」。PermissionCard 不带它构建自己的呈现,因此继续显示它正在询问的 那条命令,那正是这张卡的全部意义。通用参数回退因为同样的理由被抑制: 一条因为头部会显示而被扣下的命令,不该在它什么都没打印时又作为参数回来。
  • 头部是一个 flex 行,容纳披露按钮、复制按钮和一个仅供指针的 V 形,因为按钮 不能嵌套在按钮里。悬停填充从 header 移到 head,这样它不会在新控件下方 提前中止,而 :has() 让无法展开的行不取填充。复制与 V 形在悬停、聚焦或 展开之前保持安静;状态标签始终可见。V 形仅为指针复制 header 的作用 (aria-hidden、tabIndex={-1}),可见状态同时充当该行的 live region, 因此结果只被播报一次而不是两次。
  • 这取代了被撤回的原地终端卡:那个方案因为在成绩单内部复刻面板装饰而被回退。 该行保留标准的披露形态,只补上读者原本缺的东西。
  • 决策 D226 细化 D192 的结构化工具呈现,且仅涉及渲染器。协议、主机 RPC、 IPC 通道与存储架构保持不变。参见 04-ux/08-component-spec.md §9.2/§9.3/§9.5/§9.10 与 E2E-129。

2026-08-14 — 运行行报告的是命令做了什么 ​

  • 该行的结果原本取自工具调用的状态,因此只要某一层忘了把调用标为失败, 一条以 1 退出的命令也能读作 Done。现在它读自 shell 自己的报告: exitCode 非零就是 Failed,被杀掉、根本没报退出码的 shell 也是 Failed,只有 exitCode: 0 才是 Done。主机在标记 Bash 结果时早就 应用了这条规则;该行不再依赖它在上游被应用过。
  • 不报退出码的工具回退到调用的状态。两者都没有的行 —— 一次导入、一条写了 一半的消息 —— 什么都不说,而不是声称成功,并把播报交回隐藏的 live region。 失败自动展开跟随派生出来的结果,因此失败的命令总会展开它自己的行, 无论是哪一层发现的。
  • 展开的正文去掉它的卡片。run 行的各个块去掉标题、边框、填充和逐块复制 按钮,把输出留成朴素的等宽文本,读起来就像它来源的那个终端。260px 上限 及其滚动保留:一次长构建不该把成绩单的其余部分埋掉。
  • 去掉标题会让 Errors 只靠色调区分,因此每个通道的名称仍然为辅助技术 渲染 —— 视觉上隐藏,语义上在场。
  • 决策 D227 细化 D226,且仅涉及渲染器。协议、主机 RPC、IPC 通道与存储 架构保持不变。参见 04-ux/08-component-spec.md §9.2/§9.3/§9.5/§9.10 与 E2E-129。

2026-08-14 — 流中限流走有界重试 ​

已于 2026-08-19 被 D245 取代;下面的「只重试一次」行为作为历史决策保留, 不再是现行的 provider 策略。

  • 流中出现的 HTTP 429 现在被归类为可重试的 PROVIDER_RATE_LIMITED,与 STREAM_FAILED、NETWORK_ERROR、TIMEOUT 一起走运行时的同回合有界重放 路径:一次 750 ms 可中止的退避,把失败的助手消息从模型上下文移除,然后用同 一个可见的助手气泡把该回合重放一次。
  • 重试预算不变 —— 每条提示一次同回合重试。第二次 429 仍是终态,照常发出助手 错误以及带 retryAttempt: 1 与 providerStatus: 429 详情的生命周期 error 事件,因此转录保留手动重试操作。
  • 请求建立阶段的 429 此前已由 pi-ai 的一次有界重试覆盖(尊重 Retry-After, 上限 8 秒);本决策补上流开始之后那层包装无法再介入的空档。
  • 决策 D233 细化 D186,仅涉及运行时。协议、主机 RPC、IPC 通道和存储架构不变。 参见 03-runtime/02-agent-runtime.md §5d、03-runtime/08-error-codes.md、 ADR 0091 和 E2E-149。

2026-08-15 — 被停止的修改循环要把话说出来 ​

  • D186 的重复保护对同路径 Edit 的每次失败一视同仁,因此它允许的两次失败可能都被花在 那些已经告诉模型下一步该做什么的错误上。EDIT_TAG_MISMATCH、EDIT_TAG_UNKNOWN 与 EDIT_LINES_UNSEEN 各自交回了实时 tag 或被扣下的内容,因此现在每一个在每条路径上都有 一次免费尝试。其余每个代码仍在第一次出现时计数,而一次落盘的写入会清除该路径的历史。
  • 该保护通过终止工具批次来停止循环,而 pi-agent-core 把这读作“没有更多工作”。因此本轮 以一张失败的工具卡结束且没有最终消息,与一个选择什么都不说的模型无法区分。runtime 现在用 MUTATION_RETRY_BUDGET_EXHAUSTED 敲定该 assistant 行——可重试,因此会话记录 保留它的重试入口——并发出对应的错误事件,这也让该 turn 被记为 error 而不是 completed。
  • 决策 D228 修订 D186,且仅涉及运行时。协议、主机 RPC、IPC 通道与存储 schema 均未改变。 参见 03-runtime/18-line-anchored-edit-contract.md §9.3、 03-runtime/03-tools-and-permissions.md §4d、 03-runtime/08-error-codes.md §3.3、ADR 0089,以及 E2E-140/E2E-141。

2026-08-15 — 文件权限带着范围,删除可以恢复 ​

  • 三个 fs 权限原本是覆盖整个工作区的开关:fs.read.workspace 能读到 .env,而 recursive: false 只约束单次 fs.remove 调用 —— 一次 glob 加一个循环就能覆盖整棵树。现在权限只说明插件是否可以碰文件; manifest.fs 按模式说明可以碰哪些,glob 相对该模式的 root。fs.read 可以申请整棵树 —— 出口被限制在 manifest.net.domains 之内,宽读不再 能把任何东西带出去 —— 但 fs.write 与 fs.delete 在校验阶段就会被拒绝 整树模式。
  • 每次 pi.fs.* 调用按固定顺序经过四道闸门,且后一道只能否决:声明 ∩ 已授权、 两侧的 realpath 包含性检查(因此一个指向工作区之外的软链在字符串比较 放行的地方失败)、无条件的凭据拒绝清单加上主机自己的数据目录,最后是 声明的范围。范围之外则原生询问用户 —— 拒绝 / 允许一次 / 本会话允许 —— 会话级答复只存在内存里,随进程消亡。没有同意服务的主机选择拒绝,而不是 假定同意。
  • 删除是被限量的,而不只是被划定范围,因为它是重跑插件也无法撤销的那一个 操作。own: true 允许插件删除它自己写过的东西,由它自己数据目录里的 路径+mtime 台账记录,用户一旦编辑该文件即失效;其余都需要声明范围。 删除走 shell.trashItem,保持非递归,并以每滚动分钟 50 次刹车。主机 不为提供撤销而复制用户的任何数据 —— 操作系统回收站就是全部机制。
  • root: "userSelected" 用常驻权力换取触达范围:pi.fs.requestDirectory() 请用户挑一个目录,目录内不需要 manifest 范围,句柄仅存在内存。这正是 让「整盘」类插件在没有永久授权的情况下仍然成立的东西。
  • 划分范围之前的旧权限名仍可安装,并在加载时被降级:fs.read.workspace 保留整棵树,fs.write.workspace 在 manifest 说明写哪里之前什么都写不了, fs.delete.workspace 变成 own: true。能力被缩减,而不是留一个洞; 市场预检与 pi-plugin check 都会指出这次降级,好让作者比用户先看到。
  • 决策 D229 延续 ADR 0008 D009,并落实 07-plugins/13-plugin-permissions-matrix.md 承诺过却从未强制的确认策略。 manifest 新增 fs,pi.fs.remove / pi.fs.requestDirectory 加入受托管 API;主机 RPC 与存储架构在其余方面保持不变。参见 ADR 0088、 07-plugins/02-plugin-manifest-schema.md §5.2、 07-plugins/04-plugin-security.md §6 以及 07-plugins/13-plugin-permissions-matrix.md。

2026-08-16 — 主动后台子代理委托 ​

  • Task 不再阻塞:它在后台启动一个委托并立即返回 delegationId。三个 新的 Agent 模式核心工具驱动生命周期 —— TaskWait(收敛运行中的委托, mode: "any" + minCompleted 提前收敛,已结算的委托可按 id 重读)、 TaskList(状态报告)和 TaskStop(停止运行中的委托)。运行时持有 每会话委托注册表;回合结束、父级中止或销毁时仍在运行的委托会被停止。
  • 基础系统提示词新增 ## Delegation 章节(目录非空时),列出积极触发 模式 —— 并行探索、对抗审查、多文件实现、省上下文的搜索、批量分片 —— 以及收敛规则;Task 描述重写为以这些触发模式开头。
  • 内置增长为四个:explorer 按 omo-slim 风格重写(工具选择指引、并行 搜索、<files>/<answer> 报告格式);新增可写的 fixer,从完整规格 实现多文件改动(tools: [Read, Glob, Grep, Edit, Write, Bash]、 maxTurns: 40、<summary>/<changes>/<verification> 报告格式)。
  • 内置定义与用户定义可声明 permission: inherit | ask | accept-edits | auto(默认 inherit)。sidecar 将作用域附加到委托的 tools.execute 调用;host-core 在该模式下裁决调用,而不是使用会话的有效权限模式,契约 模式的硬拒绝和外部路径门禁仍然生效。项目定义不得声明作用域:它随仓库 一起到来,因此该声明在解析时被丢弃并留下警告,其委托在会话的有效模式下 运行。fixer 随 accept-edits 发布,因此它在工作区内免提示写入,而 Bash 和外部路径保持会话行为。
  • TaskWait 会阻塞回合,因此 timeoutSeconds 默认 600 秒、上限 900 秒。 超时不是失败 —— 它返回已完成的报告和一句“再调一次”的提示,所以低上限 只花一次往返,却换来可响应的停止。
  • MAX_SUBAGENT_CONCURRENCY 变为每会话 10 个运行委托的上限;会话已有 10 个委托在运行时 Task 以工具错误失败。
  • 决策 D231 修订 D201/ADR 0062,触及 sidecar、host-core 的 tools.execute 权限裁决、内置定义、系统提示词和渲染器的工具呈现映射。 参见 ADR 0089 与 03-runtime/02-agent-runtime.md §5f/§5f.1。

2026-08-17 — 插件面板将可见表面交给插件 ​

  • 插件面板窗口现在在 macOS、Windows 和 Linux 上均使用无框窗口。preload 在每个平台预留相同的透明 46px 安全区,并仅在右上角绘制固定胶囊,其中包含 最小化、最大化或还原、关闭三个按钮。
  • 主机不再渲染 manifest 标题或开发安全区提示。插件拥有标题、工具栏、背景和 其他所有可见面板界面;固定或粘性界面使用现有的 --pi-plugin-titlebar-height 变量,插件自己的拖拽区域可以使用 -webkit-app-region: drag。
  • 决策 D234 修订 D218 / ADR 0081 以及 D232 / ADR 0082。私有的验证发送方 控制通道、封闭 Shadow DOM、本地化标签、主题自适应胶囊、window.pluginBridge、 protocol v9 和存储架构保持不变。参见 ADR 0092、07-plugins/01-plugin-system.md、 04-ux/07-ui-design-system.md 和 E2E-024D。

2026-08-17 — 插件面板保持严格的 46px 拖拽带 ​

  • 插件面板在每个平台均严格预留 46px 的透明主机拖拽带。普通流内容按该数值 偏移,除胶囊外,拖拽带内的点击不可用。
  • 主机不渲染面板标题。开发面板显示本地化的非交互提示,说明顶部 46px 仅用于 拖拽;生产面板隐藏该提示。简约胶囊固定在右上角的拖拽带内,提供最小化、最大化 或还原、关闭按钮。
  • 决策 D235 修订 D234 / ADR 0092。页面自适应胶囊继续使用封闭 Shadow DOM、本地化 标签、焦点和 reduced-motion 行为、私有的验证发送方控制通道、window.pluginBridge、 protocol v9 和存储架构。参见 ADR 0093 和 E2E-024D。

2026-08-18 —— 每个数据目录只允许一个桌面实例 ​

  • 应用已在运行时再次启动 PI-Desktop,不会再开出第二份应用。Electron 主进程在 接触数据目录之前先取得单实例锁,重复启动的进程会在创建窗口、托盘、子进程或 日志行之前退出。
  • 运行中的实例通过托盘“显示”所用的同一条路径恢复并聚焦主窗口来响应这次启动, 因此已关闭或隐藏到托盘的窗口会回来,而不是旁边多出一个 shell。
  • 决策 D236 保护 D002 的单一 SQLite 写者以及围绕它的那些单例(outbox、托盘、 启动器快捷键、更新器)。锁的作用域是安装本身,因此使用自有 PI_DESKTOP_DATA_DIR 的运行 —— E2E 测试装置、截图装置、并行 profile —— 启动方式不变。IPC、宿主协议和存储架构均未变化。参见 ADR 0094、 03-runtime/07-process-model.md 和 E2E-150。

2026-08-18 — 厂商账户(OAuth)登录 ​

  • 提供商行现在可以用厂商订阅账户认证。pi-ai 的七个 OAuth 厂商 —— anthropic、openai-codex、github-copilot、openrouter、kimi-coding、xai、 radius —— 一次性全部支持,因为卡片列表由 models.getProviders().filter(p => p.auth.oauth) 派生,而不是写死。 registerBunOAuthFlows() 在启动时调用一次:pi-ai 用变量说明符的动态 import 加载流程,electron-vite 无法打包这条路径。
  • 凭据存放在既有的加密密钥存储中,使用新引用 secret:provider:<id>:oauth,与该行的 API 密钥并存(而非取代)。 has_secret 放宽为“任一种凭据”,使既有的就绪判断保持正确; has_oauth 与非敏感的 oauthAccountLabel 是新增字段。auth_kind 新增 oauth —— 它本来就是自由字符串,因此主机协议 v9 与存储架构 v10 不变。 删除提供商会同时删除两个引用及两条 secrets_meta 记录。
  • Electron 主进程拥有登录/退出:oauth.ts 在 secrets.* 之上实现 pi-ai 的 CredentialStore,按提供商串行化 modify 以满足带锁刷新,并通过 providersOauthVendors/Start/Respond/Cancel/Logout 与 pi-desktop/providers/oauth/event 事件流把 AuthInteraction 桥接到 渲染层。浏览器回调、设备码、单选与手动贴码都走同一条事件流,因此单个 对话框渲染厂商要求的任何形态,取消会中止本地回调服务器或轮询循环。
  • sidecar 按请求解析认证。OAuth 行以 apiKey: "" 启动;运行时注入 resolveAuth,它调用宿主代理方法 provider.resolveAuth。主进程在本地 应答 —— 绝不转发给 host-core —— 并先用每次启动重写的绑定表校验 (sessionId, providerId),然后返回短时 ModelAuth(apiKey/headers/ baseUrl,Copilot 正是靠 baseUrl 绑定它自己的账户端点)。刷新令牌 永不离开主进程,sidecar 拿到的东西严格少于密钥行今天下发的长期密钥。
  • matches() 不需要新字段:厂商行的 apiKey 恒为 "",每次启动新建的 resolveAuth 闭包在 JSON.stringify 中消失,因此 OAuth 会话跨回合复用 同一个温热运行时。
  • 模型发现与连接测试改走账户:providers.listModels 读取 models.getAvailable(它已应用厂商自己的 filterModels,因此 Copilot 只列出订阅包含的模型),连接测试通过解析认证来证明账户,而不是探测 /models。行的 apiStyle 跟随所选模型 —— Copilot 跨越多种线路 API —— 并新增两种样式:openai_codex_responses 与 pi_messages。
  • 设置 → 模型配置在提供商列表上方新增“厂商账户”分区;已登录的行显示账户 徽标,其对话框用该徽标替换 API 密钥输入框。
  • 决策 D237 扩展 D028/D031,触及 host-core 的密钥与提供商模块、Electron 主进程、代理运行时的 provider binding 以及设置界面。参见 ADR 0095 与 03-runtime/14-secrets-storage.md。

2026-08-18 —— 相互独立的厂商 OAuth 账户 ​

  • D240 修订 D237 / ADR 0095。提供商行 id 就是 OAuth 账户身份,每次登录都创建 一个新行,即使 vendorKey 与已有账户相同。每一行各自拥有自己的 pi-ai 集合、凭据存储、刷新串行化链和 secret:provider:<id>:oauth 引用。
  • 设置现在每个概念只有一个归属:「厂商账户」渲染并移除 OAuth 行;「AI 服务」 只渲染 API 密钥 / 自定义行。已连接的 OAuth 行仍可在默认模型控件中被选中, 移除它则会清空或修复指向已删除 id 的默认值。
  • sidecar 绑定一组精确的 OAuth 提供商 id,main 按该 id 解析认证。子智能体的 厂商 / 名称别名只在唯一时才被接受,因此重复的厂商账户不会被误选。参见 ADR 0098、03-runtime/11-provider-model-system.md 和 E2E-151。

2026-08-18 —— 内置子智能体继承父会话权限模式 ​

  • 内置 fixer 不再声明 permission: accept-edits;与其他内置定义一样,默认 使用 permission: inherit,并采用父会话的有效权限模式。
  • 在 auto 下,内置委托的工作区内、高风险和明确外部路径操作与父会话一样自动放行; ask 与 accept-edits 保留原有审批边界,合格的内置或用户定义上的显式非 inherit scope 仍是有意的覆盖。
  • 决策 D242 修订 D231 / ADR 0089。host-core 的外部路径门禁和 containment 检查不变; 修复移除了意外的内置 scope 覆盖,而没有放宽安全边界。参见 ADR 0100、 03-runtime/02-agent-runtime.md §5f/§5f.1 和 E2E-142。

2026-08-18 —— 按模型能力选择图片附件的传输方式 ​

  • 剪贴板图片仍是紧凑的结构化输入框引用,不会被插成 base64,也不会被强制变成 可见的 @path 文本。Electron main 依据会话根目录校验来源,并把图片字节存 在 <data>/attachments/<sha256> 下;持久化的用户消息只存引用、类型、名称、 MIME 类型和大小。
  • 精确的 pi-ai 模型记录决定视觉能力。input.includes("image") 的模型通过 sidecar 以瞬态 image block 收到合格的图片。未知 / 自定义模型、非视觉模型以及 超过 10 MB 内联上限的图片改走安全的 scratch / 项目路径回退;重放的内容存储 图片使用会话内的 replayed/ 副本。渲染器的发现流程不能把未知模型提升为 视觉模型。
  • 决策 D243 修订 D197 / ADR 0059 中关于图片的部分。它修复了观察到的 GPT 视觉 缺口,既没有把 provider 逻辑搬进渲染器,也没有把二进制数据放进主机持久化。 参见 ADR 0101、03-runtime/01-ipc-protocol.md §5.1/§13c、 03-runtime/04-data-storage.md、04-ux/08-component-spec.md §11.7–11.8 和 E2E-102c–102e。

2026-08-19 —— models.dev 成为首选模型目录 ​

  • D266 修订 D136 / ADR 0027。Electron main 以有界超时加载公开的 https://models.dev/api.json 目录,并按厂商 key 或归一化后的 API URL 匹配 提供商记录。设置、Composer 和运行时的模型元数据优先使用匹配到的记录。
  • 目录把显示名、上下文 / 输出上限、输入模态、推理选项、工具调用与结构化输出 标志以及价格映射到既有的模型界面。请求从不携带 API 密钥或 OAuth 凭据。 catalogSource 在渲染器元数据里区分 models.dev 与 pi-ai 回退,不改动主机 schema。
  • 若远程目录无法加载或没有匹配记录,则由钉住版本的 pi-ai 目录提供已知的原生 模型和适配器兼容性。回退之后,自定义或账户特定的提供商发现以及显式 ID 仍然 可用。参见 ADR 0133、03-runtime/11-provider-model-system.md、 03-runtime/13-model-catalog-and-selection.md 和 E2E-066/E2E-080/E2E-154。

2026-08-19 — 工作面板成为插件扩展点 ​

  • 决策 D246。插件通过 contributes.views(id、纯文本或本地化标题、图标 token、 HTML 入口、排序)声明工作面板表面,由新的低风险权限 ui.view 把关。 ui.view 与 ui.panel 相互独立,因此插件可以只提供停靠视图、只提供独立窗口, 或两者兼有。
  • 视图渲染为主进程持有的 WebContentsView,按渲染进程测量的矩形定位 —— 这正是 预览浏览器自 D100 起使用的机制 —— 并完整复用插件已有的隔离:同一套沙箱 preload、与该插件面板窗口相同的 persist:pi-plugin-<id> 分区,以及同一份 net.domains 出口过滤。出口策略与分区名提取为共享函数,使两种放置方式无法 漂移。<iframe> 方案被否决,因为 Electron 无法为 iframe 单独分配会话分区, 那会让停靠视图的隔离严格弱于它所替代的独立窗口。
  • 图标是宿主拥有的封闭 token 列表,而非插件提供的标记:图标绘制在宿主 chrome 内,插件 SVG 会成为注入面,并让插件得以伪装成宿主。未知 token 降级为字母瓷砖, 而不是让校验失败。
  • 视图列表按激活范围过滤,这与贡献主题(pluginThemes,刻意不过滤,因为所选主题 是全局唯一设置)不同。视图属于项目内的工作,因此按项目范围限定的插件不得在别处 提供它。打开时会重新校验权限、范围与入口是否存在;渲染进程从不是权威。
  • 决策 D247。审阅、终端与文件迁出宿主,作为随应用打包的官方插件走同一条 contributes.views 通道,只保留浏览器一个内置工具。这为插件 API 引入了第一方 消费者,使 API 的缺口变成已发布功能的缺陷。它需要新的 pi.review.* 与 pi.terminal.* API;terminal.pty 被划为 critical,因为 PTY 等同于以用户身份 任意执行,这一代价是有意承担的,而不是用第一方私有特权绕开。
  • 参见 ADR 0104、ADR 0105、07-plugins/02-plugin-manifest-schema.md §4/§5/§7、 07-plugins/03-plugin-api.md §6、07-plugins/13-plugin-permissions-matrix.md §2/§3、04-ux/08-component-spec.md §5 与 E2E-152。

2026-08-18 —— 紧凑的上下文用量摘要 ​

  • 上下文检查器保留点击 / 键盘触发、剩余百分比、已用 / 窗口计数、本轮合计、 已完成回合速度、精确的 provider 数值、聚合工具摘要、检查点摘要、body 级 portal、定位、点击外部关闭以及 Escape 焦点返回。
  • 默认面板变成一段简短摘要:回合 / 速度数值去掉外框,provider 与工具用量变成 紧凑的行内行;逐工具行、占比条、来源徽章、解释估算的段落和已用容量表盘全部 移除。聚合工具总数前的 ~ 仍表示这是估算值。
  • 决策 D244 修订 D103 / D184 / ADR 0047。协议、存储、运行时计量和模型元数据 保持不变。参见 ADR 0103、04-ux/08-component-spec.md 和 E2E-060d / US-UI-61。

2026-08-19 —— OpenCode 风格的有界 provider 429 重试 ​

  • HTTP 429 现在在请求建立和流恢复两个阶段都由运行时拥有的同一套策略处理。 预算为首个请求之后五次重试,跨阶段共享,并在 pi-ai 内嵌的重试包装里禁用。
  • 重试保持静默且可中止。retry-after-ms、retry-after 秒数和 HTTP 日期值 优先于 2 秒起步的指数退避;正向 25% 抖动与 30 秒上限保证客户端有界。失败 响应的状态和头部从 fetch 捕获,因为 pi-ai 常规的响应回调不暴露建立阶段的 失败。
  • 主会话与内置子智能体复用可见的助手行,并抑制中间的生命周期 / 错误事件。 预算耗尽时发出一个终态 PROVIDER_RATE_LIMITED 错误,带 retryAttempt: 5 与 providerStatus: 429;在等待期间中止不会再发起任何 后续 provider 请求。
  • 认证、模型选择、请求格式错误和上下文失败仍是终态。其他瞬时 provider 失败 仍保留一次建立阶段或流中重试,直到 D258 为它们提供独立的共享有界预算。
  • 决策 D245 修订 D186 与 D233。参见 ADR 0091、 03-runtime/02-agent-runtime.md §5d、03-runtime/08-error-codes.md 和 E2E-149。

2026-08-19 — 文件成为第一个内置插件 ​

  • 决策 D248 落实 D247 的第一步。浏览项目现在由随应用打包的 pi.files 插件承担, 与任何第三方插件一样经由 contributes.views 进入面板,宿主工具列表不再包含 文件条目。审阅与终端在其能力成为插件 API 之前仍保持内置。
  • 内置插件带有 source: "builtin"。host-core 在每次启动时依据 Electron 通过 PI_DESKTOP_BUILTIN_PLUGINS_DIR 报告的目录协调注册表,因此应用更新会刷新版本 与贡献,而属于用户的两项状态 —— 是否启用、激活范围 —— 会被保留。它们不可卸载, 只能禁用;新版本不再附带的插件不会留下孤儿注册行。
  • 只有「工具」发生了迁移。file:<path> 选项卡是对话产生的工件 —— 对话打开的文件 链接或计划检查点 —— 仍归宿主所有,与审阅的工件完全一致。审阅迁移时将适用同样的 划分。
  • 编写该插件立刻验证了 D247 的前提:fs.glob 返回的是上限 500 条、不含目录的扁平 文件列表,无法支撑惰性目录树,因此在既有 fs.read 权限下新增了 pi.fs.list。 它施加与 glob 相同的守卫 —— 声明范围、deny-list、受保护路径、跳过重目录 —— 因为列目录本身就是一次读取;目录始终返回,使范围很窄时仍能得到可导航的树。
  • 参见 ADR 0105、07-plugins/03-plugin-api.md §3/§6、 07-plugins/13-plugin-permissions-matrix.md §2、 04-ux/08-component-spec.md §5 与 E2E-153。

2026-08-19 — 面板内置工具定为终端与浏览器 ​

  • 决策 D249 修订 D247 / ADR 0105,并终结它开启的迁移。三项计划迁移中的两项基于其 自身理由被取消;文件保持已迁移状态。
  • 终端保持宿主内置。 将其做成插件需要 terminal.pty 权限,而一旦发布,任何 第三方插件都可申请以用户身份任意执行。相对于「让终端走公开通道」所换回的收益, 这是过宽的信任边界;仅对内置插件开放 PTY 通道则属于 D247 明令禁止的私有后门, 同样被否决。该权限不予引入。
  • 审阅保持宿主所有,并重新归类为产物面板。 其数据依 ADR 0043 属「消息所有」 —— 每次成功的工作区 Write/Edit 各自携带有界的 details.review 记录,与对话一同 持有 —— 因此迁移它只会把这份所有权搬进宿主,再通过桥交还回去。改为套用 D248 的 「工具 vs 产物」划分,审阅回到它本就所处的位置:由 Write/Edit 产物创建与重建, 正如 file:<path> 标签由文件链接创建。
  • 因此审阅退出工具启动器 —— 启动器面向用户主动挑选的表面 —— 但仍是宿主渲染的活动 标签类型。关闭它不会丢失任何东西:逐条改动的内联卡片仍留在对话中。
  • HEADER_TOOLS 现为终端与浏览器。文件是内置插件,审阅与 file:<path> 属于产物 表面,插件贡献的视图并列显示在工具旁。
  • 参见 ADR 0105、04-ux/08-component-spec.md §5 与 E2E-153。

2026-08-19 —— 内置命令面精简为核心五条 ​

  • 决策 D250 将第一方 registry 冻结为 builtin.session.new、 builtin.agent.compact 和三个 builtin.mode.* 条目。对应的内置输入框别名 只有 /new、/compact、/agent-mode、/plan-mode 和 /goal-mode。
  • 会话删除、中止、项目、设置、插件、日志、重命名、命令面板、窗口重载和 DevTools 条目从 registry 与渲染器 dispatch 中移除;遗留的 newChat、openProject、 openSettings 别名也一并移除。专用界面工作流和插件命令不受影响。
  • 模式别名保留一次性 /mode <prompt> 行为:先切换模式,再将剩余文本作为可见用户 回合发送。旧别名不再是受支持的内置契约。
  • 参见 ADR 0106、04-ux/04-builtin-commands.md、ADR 0024、ADR 0034 和 E2E-117。

2026-08-19 —— 移除面板内置交互式终端 ​

  • 决策 D251 取代 D249 以及 ADR 0019/0105 中关于终端的条款。工作面板不再拥有 交互式 shell;需要交互式 shell 时使用外部终端。
  • 浏览器仍是宿主提供的工作面板工具;文件仍由 pi.files 插件提供,审阅和 file:<path> 仍是工件表面,插件贡献的视图继续可用。
  • Agent Bash 保持非交互式并归对话所有:命令、输出、状态、复制行为和终端图标 不变。本次移除只删除 PTY UI、IPC、类型、依赖、打包配置和相关测试。
  • 不新增插件 PTY 权限或内置插件私有替代通道。参见 ADR 0108、E2E-058。

2026-08-24 —— Windows 任务栏最小化保留任务栏条目 ​

  • 决策 D252 针对 Windows 原生任务栏路径修订 D216 / ADR 0078。点击已聚焦窗口的 任务栏按钮完成普通的原生最小化,并保留 PI-Desktop 的任务栏条目。再次点击 恢复并聚焦窗口;窗口只是被遮挡时点击该条目仍保持现有的置前行为。
  • 渲染器 / 菜单发起的显式最小化操作仍驻留托盘,macOS / Linux 的原生最小化 路径也一样。常驻托盘、后台进程、窗口边界、IPC 和存储契约不变。
  • 参见 ADR 0117、03-runtime/07-process-model.md、 04-ux/09-interaction-patterns.md 和 E2E-124。

2026-08-24 —— 渲染器拥有的排队提示与平滑的「立即发送」 ​

  • 决策 D253 为智能体运行期间发送的提示增加一个按会话、仅存于渲染器的 FIFO 队列。队列保存可见的提示及其草稿快照,支持单独移除,切换会话后依然存在, 并有意不做持久化、重启后不重放。
  • 输入框只有一个提交槽位。运行进行中时,非空草稿显示「发送」并追加到队列; 空草稿显示「停止」,因此用户清空草稿即可露出立即停止操作。所属会话的 agent_end 之后,渲染器通过既有的 agent/prompt 路径排出一项。「立即发送」 把一项提升到队首并调用增量的 pi-desktop/agent/stop;sidecar 设置 pi-agent-core 的一次性 shouldStopAfterTurn 标志,于是当前的助手回复 / 工具 批次跑完、持久化回合正常关闭之后,优先的提示才开始。立即中止保持现有行为, 且从不清空队列。
  • 这改变了渲染器状态归属并新增一个公开的 Electron IPC 通道,但不改动 host-core 协议 v9、存储架构 v11 或「同时只有一个运行中回合」的约束。参见 ADR 0118 和 E2E-011f。

2026-08-24 —— 子智能体采用事件驱动的空闲与总时长超时 ​

  • 决策 D254 用 600 秒的空闲看门狗和 21,600 秒的总时长上限取代默认的子智能体 回合数上限。空闲计时器在回合、消息和工具生命周期事件时重置,并在工具执行 期间暂停;总时长计时器包含工具执行。(由 D260 修订:空闲默认值现为 300 秒, 且每个智能体事件都会重置计时器。)
  • 超时返回 timed_out,并带 SUBAGENT_IDLE_TIMEOUT 或 SUBAGENT_DURATION_TIMEOUT,尽可能保留最新的部分助手输出。provider 致命 错误、父级中止和按定义显式设置的 maxTurns 保持不变。
  • maxTurns 现在可选(none、0 和省略都表示无限制),显式值上限 80。定义 可以在文档给出的范围内通过 idle-timeout 和 max-duration 覆盖看门狗;非法 值告警并回退到默认。(由 D260 修订:各内置定义都声明一个显式的兜底回合数, 而不是无限运行。)
  • 内置 explorer 获得 Bash 用于有界的仓库检查,code-reviewer 仍为只读。 共享状态类型、运行时计时、委托拓扑、i18n 和 E2E 覆盖都暴露新的超时结果。
  • 参见 ADR 0119、03-runtime/02-agent-runtime.md §5f、 03-runtime/08-error-codes.md、03-runtime/09-logging-and-observability.md 和 E2E-155。

2026-08-25 —— 折叠工作面板恢复基础窗口边界 ​

  • 决策 D255 取代 ADR 0033。渲染器仍把工作面板布局为文档流内的 flex 列,但在 呈现它之前先请求一份等于已提交面板宽度的原生预留。退出时面板保持挂载,直到 零宽度预留成功为止,因此折叠右侧面板会让应用窗口回到仅聊天的边界,而不是 让 MainChat 吞掉释放出来的那一列。
  • 目标状态预留规划器、受限显示器下的行为、Browser 的测量边界同步、由 Main 拥有的原生边缘调整大小,以及会话作用域的面板状态都保持不变。参见 ADR 0122 和 E2E-056。

2026-08-25 —— 窗口控件使用原生任务栏最小化 ​

  • 决策 D256 针对 Windows / Linux 的渲染器与原生菜单最小化操作修订 D216 / D252。自定义的右上角最小化按钮现在使用操作系统原生的最小化过渡,因此任务栏 条目仍可用于恢复;关闭到托盘仍只由记住的关闭行为控制。
  • macOS 的原生最小化仍驻留托盘;常驻托盘、关闭偏好、IPC 形状、主机协议和后台 生命周期在其他方面不变。
  • 参见 ADR 0123、03-runtime/01-ipc-protocol.md、 04-ux/09-interaction-patterns.md 和 E2E-124。

2026-08-27 —— 智能体能力页面合并为一个工作台 ​

  • 决策 D257 细化 D193 / D194,并取代 D202 中关于设置 > 智能体 > 技能 / MCP / 子智能体页面形态的条款。每个页面现在都是一条工具栏(带计数的分段级别筛选、 一个搜索框、项目选择器、主操作按钮)加一块按级别分组标题分隔的面板,而且三 个页面都通过本次改动之前就已存在的 host 调用完成新建、编辑和删除。
  • 按行的忙碌状态取代整页锁死,骨架屏只在首次绘制时出现,启用开关先乐观翻转、 被 host 拒绝时回滚。
  • skills.reveal 现在携带 level 和 projectPath,因此没有全局同名记录的项目 级技能也能解析;处理器仍然接受裸 id。
  • 除了这一处载荷放宽之外都只改渲染层:没有 host-core 改动、没有新 RPC、没有存储 或 schema 变更。D193 / D194 / D202 里关于能力状态、优先级和 .agents 路径的 规则全部不变。
  • 参见 ADR 0126、04-ux/06-settings-ia.md 和 E2E-103。

2026-08-27 —— 发布文档是版本面(D260) ​

  • 稳定版应用的版本提升 / 打标签必须在打标签之前更新每一个带版本的面: 已发布语言的变更日志及其测试清单、所有工作区包的版本(包括此前被跳过的 docs/package.json)、Cargo 工作区 + host-core 锁文件的版本、APP_VERSION, 以及 README.md / README.zh-CN.md 中声明的发布线(D260,扩展 D164)。
  • scripts/check-release-docs.mjs 校验整套内容;scripts/release.mjs 在提升 版本后调用它,失败时拒绝提交或打标签。
  • 已写入发布运行手册 §4.1、AI 开发工作流矩阵 + 完成定义 + 禁止做法、变更清单 和 AGENTS.md。

2026-08-26 —— 瞬时 provider 失败共享一份有界重试预算 ​

  • 决策 D259 修订 D186 / D245 与 ADR 0050 / ADR 0091。非 429 的瞬时 provider 失败现在在请求建立和流传输两个阶段共享一份有界预算:首次尝试之后四次重试, 因此上游网关的 502/503/504 无论出现在响应头之前还是流中都能恢复。只重放 失败的那次请求;会话及其工具状态不受影响。
  • 该预算接纳 NETWORK_ERROR、TIMEOUT、STREAM_FAILED 和可重试的 PROVIDER_ERROR。非 429 的延迟依次尊重 retry-after-ms、retry-after 秒数、 HTTP 日期,然后才是确定性的 1s/2s/4s/8s 序列,上限 8 秒;凡是能声明延迟的 状态都会保留捕获到的头部。
  • 主会话、内置子智能体和一次性输入框增强共用同一策略。429 的五次重试预算 保持独立。预算耗尽记录 retryAttempt: 4。
  • 认证、模型选择、请求格式错误和上下文失败仍是终态。不改动 IPC、存储、主机 协议或 provider 配置。
  • 参见 ADR 0128、03-runtime/02-agent-runtime.md §5d、 03-runtime/08-error-codes.md、E2E-096 和 E2E-149。

2026-08-27 —— 子智能体空闲看门狗限制的是沉默,不是慢 ​

  • 决策 D260 修订 D254。每个智能体事件都会重新武装空闲计时器,包括以 message_update 到达的单个流式 token,因此看门狗只在完全无响应时触发。 持续产出的委托无论回合多慢都不会被空闲终止,工具执行期间的暂停不变。
  • 由于这个窗口现在度量的是「空气死寂」而不是工作量,默认空闲超时从 600 秒 降到 300 秒;它按实测的委托最后一个流式 token 到下一次响应之间 174 秒的 p99.9 等待来定,而不是按工作可能需要多久。它必须低于 600 秒的 TaskWait 默认值,这样真正卡住的委托能在一次等待内落定为 timed_out,而不是拖住 父级整整一个窗口甚至更久。21,600 秒的总时长上限和 10–21,600 的覆盖范围 不变。
  • TaskWait 到期现在报告「Still running after Ns」,并说明这不是失败、委托 仍在工作,这样父级不再把一次普通的未完成等待当成失败的委托。
  • 各内置定义获得与其职责匹配的回合兜底 —— explorer 60、code-reviewer 50、 test-runner 40、fixer 80 —— 取代无限回合,因此循环不收敛的委托会以 truncated 结束并带上部分报告,而不是一直跑到总时长上限。
  • 不改动 IPC、存储、主机协议或 provider 配置。参见 ADR 0129、 03-runtime/02-agent-runtime.md §5f、03-runtime/08-error-codes.md 和 E2E-155。

2026-08-27 —— 已挂载的转录是一个窗口,而不是已加载的历史(D261) ​

  • 决策 D261 在渲染器中细化 ADR 0120 / D258。它们限定了什么可以跨过 IPC 边界、 定位一页要花多少代价;但渲染器仍然把分页取回的每一行都挂载出来,并在会话 整个生命周期里保持挂载。
  • 已挂载的历史变成一个尾部窗口:会话切换后的第一次提交挂 15 行,稳态 60 行, 每次增长 40 行。到达顶部时,窗口未满则先增长;只有当窗口覆盖了全部已加载 历史,才去取更早的一页。
  • content-visibility: auto 本来已经跳过了这些屏外行的布局与绘制,但它保留 了它们的 React 树、Markdown AST 和 Shiki token 数组 —— 在低内存的 Windows 机器上这是错的资源,而报告聊天区在长会话中越来越迟钝的正是这种机器。 窗口化同时限制了驻留内存和每帧的协调开销。
  • 窗口增长与取回一页采用相同的绘制前滚动锚定,因为两者都在阅读位置上方增加 高度。窗口按会话重置并钳制在已加载历史之内,因此过期的预算不会过度挂载。 水合占位条只作用于第一次提交:稳态窗口下方若有永久占位条,会在增长触发点 之前放一个空白视口。
  • 会话小地图由已挂载的条目构建。它通过在滚动容器里找到标记对应的节点来响应 点击,因此若用完整集合,画出的短线会跳到无处。代价是浏览器自带的页内查找 只能触及已挂载的行。
  • 不改动 IPC、存储、主机协议或分页。参见 ADR 0130、 04-ux/08-component-spec.md §8 和 E2E-159。

2026-08-28 —— 大段文本粘贴成为会话临时文件引用(D262) ​

  • 不超过持久化 largePasteThreshold 的纯文本粘贴仍使用原生文本区域输入;默认值为 600 个字符,有效值为 1 至 1,000,000 的整数。
  • 更大的文本通过现有粘贴桥以准确的 UTF-8 text/plain 字节传输,并保存到所属会话的 <data_dir>/scratch/<sessionId>/pasted/ 目录。Composer 在原始插入符号/选择处插入生成的 @<temporary-name> 标记,并将规范路径映射保留在渲染器草稿中,包括缓存和未答复的智能停止快照。
  • 发送时在原位置只解析生成的标记一次;不会追加 basename、作为重复结构化附件发送,或写入工作区。现有剪贴板文件/图像芯片行为和临时目录清理保持不变。
  • 该设置是应用设置 JSON 中的新增字段;主机规范化会为旧设置提供默认值,不需要数据库迁移。参见 ADR 0131、04-ux/06-settings-ia.md、04-ux/07-ui-design-system.md §8.1、04-ux/08-component-spec.md §11.7–11.8、04-ux/09-interaction-patterns.md §8a 和 E2E-102g。

2026-08-28 —— 跨显示器拖动是用户意图,而非系统调整(D263) ​

  • 决策 D263 修订 Main 中的 D255 / ADR 0122。工作面板保留基于记住的基边界进行规划, 并针对 Main 最后应用的矩形协调后续本机增量。该协调把所有显示器变化压成一个 displayChanged 布尔量并复用记住的基边界,这对系统重新适配是正确的, 对用户拖到另一块显示器的窗口则是错误的。
  • 报告为 issue #18,自 0.10.0 起的每个版本都可复现:把窗口拖过显示器边界后, 松开鼠标时窗口会跳动。协调挂在每个本机 move 上,因此在拖动过程中就以原显示器的 基边界重排,目标工作区域再把该 x 夹到边缘,并保留旧显示器的 y。面板状态无关紧要 —— 关闭的面板请求宽度 0,但仍会重排并调用 setBounds。
  • 布尔量变为 none、os-adjusted、user-moved 三态的 DisplayTransition。 os-adjusted 逐字保留 ADR 0122,使受限显示器保持用户意图、宽敞显示器恢复完整保留。 user-moved 以窗口当前位置推导基边界,仅规范化原点进入目标显示器工作区域; 即使目标显示器更小也保留尺寸,因为基边界是 ADR 0122 下可恢复的用户意图, 收缩会被持久化且无法撤销,缺口由保留量吸收。
  • 归属依据未被消费的本机 move 流而不是时间期限,因为 Electron 没有为本机移动 提供 drag-begin/drag-end 事件对,而期限会让正确性取决于主进程调度以及推迟几何的 最大化窗口。move 处理器只标记待处理的拖动,并把协调推迟到移动流稳定之后, 因此 Main 不会在拖动过程中与窗口服务器抢位置。显示器拓扑事件会先清除标记, 所以拖动之后的热插拔仍归属系统。
  • 同一处误判此前会把过期坐标写入 window-state.json,使窗口在用户已离开的显示器上 重新打开。user-moved 现在会推进记住的显示器键并持久化,并按保留路径同样的方式 规范化,因为最大化窗口会推迟几何、让这次保存成为该拖动的唯一消费者; os-adjusted 仍拒绝持久化。未被消费的标记会在期限后退休, 因此不会泄漏到之后的系统显示器变化中。
  • 不改动 IPC、存储或主机协议;window/setWorkPanelReservation 保持其形状。 参见 ADR 0132、03-runtime/01-ipc-protocol.md 和 E2E-160。

2026-08-28 —— Composer 输入是一条被测量过的延迟路径(D264) ​

  • 聊天输入框的逐键延迟有三个被测量出来的原因,全都落在输入路径上, 没有一个是正确性缺陷。@ 菜单打开时,每次按键都会对最多整个工作区文件索引 (8000 条)做模糊匹配,再对全部匹配做完整排序,只为显示 50 行; 现在的有界 top-K 选择返回同样顺序的同样行,每次按键从 2.4–3.0 ms 降到 0.25–0.78 ms。filterCommands 保留完整排序:命令列表很小, 且其比较按命令类型分组,top-K 在那里没有收益。
  • 自动调整高度的 effect 此前每次按键都强制一次 height: auto 测量探针, 而每次探针都是一次同步重排。这个探针不能简单去掉,因为只有它能揭示 草稿现在需要更少的行数:在固定的已应用高度上,溢出的文本框报告的是该高度, 而不是内容高度。这同时也解释了为什么在内容溢出时按已应用高度读取是等价的 —— 那一次 scrollHeight 读数就是完整的内容高度。因此探针只保留给 文本框可能需要收缩的情形:已应用高度高于最小值,且内容不再填满它。
  • 重新发布未变化的 --composer-dock-height 并不是免费的。该属性挂在 document.documentElement 上,因此每次 setProperty 都会使整个文档 (包括转录区)的样式失效。在取整后的高度未变化时跳过写入, 使一行之内的输入不再走那条路径;高度与 overflowY 也只在计算值 与上次应用的值不同时才写入。
  • 剩下的开销是 React 的身份抖动。Array.prototype.filter 即使不丢弃任何元素 也会分配新数组,因此引用过滤器现在在无删减时返回原数组, 以 fileReferences 为键的草稿缓存序列化 effect 不再每次按键重跑; 同样的空操作保护也覆盖 workspacePath effect 和 clearDraftForKey。 setCursor 会忽略未变化的取值,因为 onSelect 在每次光标移动时都会触发, 这曾使单纯的方向键导航都会重渲染输入框并重跑自动完成触发检测。
  • 不改动 IPC、存储、主机协议或 schema,也不新增依赖。行为保持不变: 菜单行与顺序一致、同样的 28px 单行下限、同样的七行上限。 参见 04-ux/08-component-spec.md §11.5 和 US-UI-55。

2026-08-29 —— 展开的委托运行过程在原处滚动(D271) ​

  • 决策 D271 在渲染层细化 D201 / D268 / ADR 0062。展开一个委派节点会把委托的 整个运行过程内联进父级转录,因此一个执行了四十次工具调用的委托会一次新增四十行。
  • 涉及三处没有高度约束的表面:.subagent-run 没有任何高度上限;.tool-fields 是唯一没有 260px 上限的细节块,而 .tool-row-content、.tool-file-list、 .tool-match-list 早已具备;D268 又把运行时限制在 50k 字符的 TaskWait 合并报告放进了同样没有上限的 note。
  • 滚动特意挂在新的内层容器 .subagent-run-rows 上,而不是 .subagent-run: 折叠导轨绝对定位在 left: -8px,位于运行块内边距盒之外,在运行块上设置 overflow 会裁掉那条标示委托上下文边界的导轨。标题留在滚动区之外,因此归属 信息不会从它所标注的行上滚走。
  • 该滚动区是带标签、可获焦的 role="group":指针用户本可滚动它,而键盘用户 否则无法抵达。

2026-08-29 —— 生命周期行就是子智能体行(D268) ​

  • 决策 D268 在渲染层细化 D201 / D265 / ADR 0062 与 ADR 0089。D265 让每个 Task 调用都成为整宽委派卡片,但把三个生命周期行(TaskWait/TaskList/TaskStop) 留作通用工具行。而这些行是最终结果唯一出现的地方——Task 在委派启动的那一刻 就返回,之后永远显示 running——因此它们既是信息量最大、也是可读性最差的部分。
  • 可见缺陷很具体:生命周期行以委派 ID 作为参数,于是 getToolSummary 打印出一串 裸 UUID;其结果载荷没有按工具的映射,正文退回到 delegations[] 的格式化 JSON; 标签写作“已委派”,而那是 Task 的动作,不是等待或停止的动作。
  • 修复方式是读取运行时本就返回的名册。details.delegations[](TaskWait/TaskList) 与 details.stopped[](TaskStop)携带子智能体名称、状态和注册表时间戳,正是 读者需要的信息:该行按名称生成摘要并对重复者计数,用共享的 chat.subagentStatus.* 文案汇总状态,并把名册渲染为命名字段表格,前置合并后的报告。
  • 有意保持不变:生命周期行仍然不是拓扑节点,仍不得抬高子智能体数量 (isDelegationActivityItem);运行时及其 delegationId 契约不动;IPC、存储与 主机协议面均未移动。本次仅涉及呈现。

2026-08-28 —— 单个委派也读作一张卡片,与扇出一致(D265) ​

  • 决策 D265 在渲染层修订 D201 / ADR 0062。原决策把委派卡片保留给同一活动分组中 两个及以上的 Task 调用,单个 Task 仍退回紧凑工具行。于是同一件事看起来像 两个不同功能,而单个这一常见情形恰恰是丢失结果、记录的运行时长和委派步骤数的 那一个。
  • 现在活动分组里的每个 Task 调用都渲染为委派卡片:根节点、连接线、每个委派一个 节点。卡片其余部分不变,聚合标头、直播时只自动展开一次的规则、节点折叠以及 报告只打印一次的规则都原样沿用。
  • 聚合标头按数量措辞。英文为四个状态标签新增 _one 形式;中文只有一个复数类别, 因此其 _other 形式承担所有数量,并去掉了让单节点卡片显示“1 个 Subagents”的 英文复数标记。
  • 不改动 IPC、存储、主机协议或委派运行时:卡片仍然在直播和重载两条路径上,由持久化 的 parentToolCallId / agentName 归属推导。参见 ADR 0062 §6、 04-ux/08-component-spec.md §9.9 和 E2E-119。

2026-08-28 — 标题栏带在所有平台上都预留空间(D266) ​

  • 决策 D266 修复目的页面上仅出现在 macOS 的回归。主窗格顶部的 46px 带是不透明、绝对定位的 .main-titlebar,因此可滚动的路由内容在所有平台 上都从它下方经过。但只有 win32 和 linux 为 .page-frame 做了填充, 而那条保留被描述为窗口控件问题 — macOS 原生绘制交通灯,于是 darwin 看起来可以豁免。
  • 并不能。无论窗口控件由谁绘制,该带都是不透明的,所以在 macOS 上它遮住了 插件页面标头:标题行被裁切,已安装 / 插件市场分段控件贴到了窗口顶边。
  • .page-frame 现在在 darwin 上也保留 --ds-toolbar-height 加 8px 缓冲,与 .thread-content 已经在三个平台为记录区做的事一致。因为共用该框构, 修复同时覆盖插件、定时任务和合并请求页面。
  • 叠在带之上的表面保持现有处理:插件详情侧面板在 z-index: 60 渲染, 仍从顶边开始,只有 win32 / linux 下降到带下方以避开窗口控件。
  • 仅渲染层 CSS:无 IPC、存储、宿主协议或组件结构变更。参见 04-ux/08-component-spec.md §2.3 和 E2E-087a。

2026-08-29 — 项目存档是一个工作台,而不是三个堆叠带(D267) ​

  • 决策 D267 修订 D168 对设置 → 项目存档的演示层规定,采用技能 / MCP / 子智能体已在使用的 D257 一个工作台构成:一条安静的引导行、一个工具栏、 一个面板。
  • 概述横幅被移除。它是一张装饰渐变卡片,承载四个 --text-xl 计数器格, 而设计系统明确禁止这种处理。四个页面级计数器随之一并移除:它们承载的 每个总计都已能从面板内的分组条计数中读出,因此在工具栏上方重述这些数字 既重复了数字,也让该目的地拥有了兄弟页面都没有的页头。这撤销了 D168 对 概览计数器的要求,并停用 project.statProjects、project.statOpen、 project.statArchived 和 project.statSessions 键,两个语言包中均已删除。 引导区现在是一条安静的说明行,与能力页面一致。
  • 三个每部分面板合并为一个面板:固定 / 所有项目 / 已存档成为带计数的 面板内非交互标题条,因此页面只有一个高架框,列表内也不再嵌套标题。 定制排序药丸控件改用共享分段控件基元,行几何与能力页面行一致 (32px 控件、14px 列表间距、28px 行字形);外部大写部分标签被移除。
  • 除上述四个停用的计数器键外,其余仅为演示层变更:行剖析、行菜单分组、 搜索匹配、八个一批的会话显示、激活语义和无障碍语义全部不变。无 IPC、 存储、宿主协议或组件归属变更,因此不需要新的 ADR。
  • 参见 04-ux/06-settings-ia.md、04-ux/07-ui-design-system.md、E2E-038 和 US-UI-23。

2026-08-29 —— 历史续接让会话大纲始终可达(D269) ​

  • 决策 D269 在渲染器中修订 D108 / D261 / ADR 0130。D261 把已挂载历史变成尾部 窗口,并把已挂载条目喂给小地图,因此没有短线能指向被扣留的行。这在短线上 是对的,但让大纲对它藏起来的历史无话可说;随后 D108 的可见性规则又在已 挂载尾部少于两个标记或能放进一个视口时把整条导轨移除。
  • 两种过渡让这种情况在正常使用中就会出现:有界尾部可能是一个塌缩到不足一屏 的工具密集回合,取回的更早一页也可能整体落在尾部挂载窗口之外。两者都不一定 改变 scrollTop,而唯一的升级触发是原生滚动事件,于是向上的行程可能在磁盘 上还有更早历史、却没有导轨可用的情况下停下。
  • 现在只要还有更早的历史 —— 已加载但被扣留的行(hiddenAbove > 0)或主机上 更早的一页(hasMoreBefore)—— 大纲就保持存在,并在顶部带一条点状的 「更早历史」续接项。它是真正的控件,不是消息短线:走同样的两阶段先增长后 取页路径,页在加载时禁用并标注,从不声称有消息预览。普通消息短线仍只由 已挂载行构建,因此 D261 的可达性规则严格成立。
  • 推进不再依赖滚动事件。转录既有的顶部加载行就是被观察的边界:在尾部不足一 屏、取回但被扣留的一页或窗口过渡之后,只要它仍停在近顶带内,转录就持续 升级。近顶滚动检查不变,且现在与观察器共用一个阈值常量。
  • 一旦不再有更早历史,D108 的「两个标记 + 溢出」规则原样适用,因此已完成的 短会话仍不显示导轨。仅涉及渲染器:不改动 IPC、存储、主机协议或分页,挂载 窗口预算也不动。
  • 参见 04-ux/08-component-spec.md §7 与 §8、ADR 0130 和 E2E-159。

2026-08-30 —— 两种凭据共用一个模型选择器(D270) ​

  • 决策 D270 在渲染层细化 D237 / D240。设置 → 模型配置此前带着同一个模型选择器的 两份拷贝:AI 服务对话框是完整的那一份,厂商账户对话框是已经走偏的简化拷贝。
  • 拷贝那一份共用列表与 ModelBinding 形状,却没有“高级”折叠区,因此账户的按模型 上下文窗口、输出上限与思考等级都无法编辑;它也没有保存时的收敛处理——账户可能 持久化运行时会丢弃的思考等级,而对话框仍把它算作已启用。
  • 现在由一个共享渲染组件负责行合并、筛选、勾选、自定义模型输入、已选面板、“高级” 折叠区,以及向已发布等级集合的收敛。两个对话框都渲染它;只有左栏标题不同,厂商 账户对话框额外传入自己的保存状态。账户对话框放大到提供商对话框的双栏尺寸, 重复的已选面板与自定义模型 CSS 一并删除。
  • 仅渲染层:不涉及 IPC、主机协议、存储或提供商配置架构改动,因此不需要 ADR。 唯一的用户可见变化是厂商账户现在拥有与 AI 服务相同的按模型高级设置,并获得 同样的保存时收敛。
  • 参见 04-ux/06-settings-ia.md、04-ux/08-component-spec.md §19、 03-runtime/11-provider-model-system.md §10 与 E2E-162。

2026-08-30 —— 退出时插件按关闭停止,而不是按崩溃(D272) ​

  • 决策 D272 在主进程细化规格 07 §3.1。before-quit 之前释放的是插件的 监听, 把宿主子进程留给紧随其后的进程拆除去杀。
  • 没有人宣告过的退出与崩溃无法区分。没有任何地方设置 disposing,因此 handleChildExit 顶部的守卫从未生效,一次正常退出会按插件走完崩溃路径:一条 exitCode: 0 的 PLUGIN_CRASHED 错误日志、一个"意外停止"提示,以及 superviseCrash 把重启排进一个正在关闭的应用。提示来了两次,因为运行时和 onPluginCrash 处理器各为同一事件发了一次。
  • 代价体现在诊断上。在某个本地配置里,633 条崩溃上报有 613 条属于此类,全部落在 app shutdown 后五秒内,真正的那 20 条因此几乎不可见。
  • 现在由 disposeAll() 负责退出:先在一趟里把每个插件标记为正在释放并取消其待 处理的重启,这样某个子进程在同伴还在停止时死掉也已被覆盖;释放监听;然后并行 停止服务并运行 onUnload。整个过程有界——每个 onUnload 1.5s(短于显式卸载 的 5s,因为用户已经要求应用关闭),整体 3s,超时后直接杀掉子进程。 onPluginCrash 中重复的提示被删除。
  • 仅主进程:不涉及 IPC、存储、主机协议或清单改动,因此不需要 ADR。插件获得一条 此前没有的保证——退出时 onUnload 会运行——用户也不再在每次干净退出时看到 失败提示。
  • 参见 07-plugins/05-plugin-lifecycle.md §3.1。

2026-08-30 —— 模型习惯的参数名被接受,而不是被纠正(D273) ​

  • 决策 D273 在 Agent 运行时细化规格 03 §4。工具 schema 写的是 path 和 pattern;模型照旧发 file_path 和 query。
  • 数字把它从模型问题变成了设计问题。某个本地月份里 1679 次失败的工具调用中,852 次 是参数名不匹配。其中 724 次是 Read 传了 file_path——占全部 Read 失败的 87%,仅此一项就把 Read 的错误率推到 11%。Write(55)、Edit(56)、 Grep(13)和 Glob(4)以同样的两个别名重复了这一模式。
  • 参数名是预训练习惯,不是一段描述能劝退的东西。而且拒绝发生在调用到达主机之前的 校验器里,于是这个回合花在了一个模型无从预料、从它那侧也没有理由预料的错误上。
  • 现在 Read、Write、Edit 和 BrowserPreview 在 path 之外接受 file_path,Glob 和 Grep 在 pattern 之外接受 query。两种拼写在 schema 中都是可选的,任一都能通过校验,运行时在下游任何环节看到之前就把别名折叠到规范名 上——写锁的键、tools.execute 的参数、持久化的转录只带规范名,因此这条线以下没有 任何地方需要认识两种拼写。每一对恰好要求提供其一;两个都给时规范名胜出。
  • Bash.timeout 是同一问题的第二个版本:69 次失败是按毫秒给的超时 (600000、900000、1800000),而字段上限是 300 秒。schema 上限放宽到 3600000——高于观察到的最大值 1_800_000——好让毫秒值根本能通过校验,运行时把不小于 1000 的值按毫秒读取并夹到实际 生效的 300 秒上限。阈值取 1000 而不是 300 是刻意的: 像 301 这样的值更可能是一个越过上限的秒值,把它改写成 0.301s 会比它今天得到的错误 更糟。
  • 仅运行时:不涉及 IPC、存储或主机协议改动。host-core 的 schema 及其参数解析保持不 变,因为运行时在它们之上完成归一化,而 tools.list 没有别的消费方。
  • 参见 03-runtime/03-tools-and-permissions.md §4。

2026-08-31 —— 不变的已编辑提示也执行重试(D274) ​

  • 决策 D274 修订渲染层的 D137。确认一个有效的用户提示编辑后,现在始终 分派现有的编辑-重发/重新生成路径,即使修剪后的文本与原文完全相同。
  • 内联控件在当前语言中显示“重试”和“取消”。重试保留现有的修订存档、 按消息身份截断、斜杠命令展开和附件行为;取消和 Escape 保持提示不变。
  • 仅涉及渲染层和 i18n:不改动 IPC、存储、主机协议或运行时契约。参见 ADR 0135、04-ux/08-component-spec.md 和 E2E-073。

2026-08-31 —— 在上下文压缩期间保留活动任务边界(D275) ​

  • 决策 D275 修订代理运行时的 D203 / ADR 0064。检查点现在持久化不透明的 details.retainedTailMode 边界。
  • 提供商必须继续活动工具回合时,检查点只保留最新的用户消息,并沿用现有的 20,000 令牌上限。终止回合边界、发送新提示之前或手动压缩时,不保留裸的历史 用户消息;摘要是权威内容,下一条提示就是新任务。
  • 没有该模式的旧检查点归一化为最新的用户消息。可见转录本、主机所有权的存储和 协议形状不变。参见 ADR 0136、运行时/存储/工具结果规格和 E2E-164。

2026-08-31 —— 访问过的会话各自保留一个已挂载面板(D276) ​

  • 决策 D276 修订 D162 以及 ADR 0130 的第 4、5 条。会话区为最近访问过的每个 会话挂载一个面板,上限三个,因此切换是可见性交换,而不是把同一个转录重新 指向另一份数据。
  • 热切换在第一帧就揭示目的地面板及其自身的滚动位置。冷切换让当前面板保持 完全可读,仅以顶部细进度条标示等待,同时输入框不可交互;不透明度调暗已 移除。
  • 首次激活仍定位到最新一轮;重新访问恢复该面板自己的偏移。隐藏面板保留布局 盒、保持惰性,且不执行跟随滚动、分页或测量。
  • 仅涉及渲染层:不改动 IPC、存储、主机协议或运行时契约。参见 ADR 0137、 04-ux/08-component-spec.md、04-ux/09-interaction-patterns.md、 E2E-011、E2E-071d 和 E2E-071g。

2026-08-31 —— 并发子智能体可以直接互相传递消息(D277) ​

  • 决策 D277 扩展 D201 / ADR 0062 / ADR 0089,不修订它们。一个会话作用域的邮箱 让并发的委托通过一个可选加入的工具 —— Peer(action=...) —— 交换有界文本, 可在定义的 tools: 列表中声明。action 参数选择操作:send(带 to?、 text、topic?、inReplyTo?)、inbox(带 from?)或 wait(带 timeoutSeconds?、from?)。这三个操作最初是三个工具 (PeerSend / PeerInbox / PeerWait);ADR 0140 把它们并成一个工具, 让对等消息在计数和阅读上都是单一能力。
  • 它存在的原因是父级在任何委托开始之前就写好了每一份简报,因此不可能总是 知道各方向彼此独立。路径锁能防止撕裂写入,却防不住过期前提;而通过一个 阻塞在 TaskWait 里的父级转发便条,会耗掉委托本来要保护的上下文。
  • 可选加入且默认关闭:没有内置定义声明 Peer,因此每个现有定义都保持 ADR 0062 的隔离。只声明 Peer 而没有任何工作工具的定义在 Task 时被拒绝。
  • 按智能体名称寻址,从不按委托 id;发送者由运行时在 spawn 时绑定,因此 from 不是模型输入。Peer 工具按委托单独构建,不进入会话工具目录,因此 父级没有通往委托的第二条通道。
  • 消息在进程内:没有 permissionScope、没有 host-core 调用、不占工具预算。 上限为每条消息 2,000 字符、每个收件箱 64 条(丢弃最旧的并报告丢失)、每次 运行 60 次发送,以及低于 300 秒空闲看门狗的 120 秒等待上限。排空是破坏性的。
  • 对等流量从不进入父级的模型上下文,委托会被告知它的报告仍是父级唯一读取的 内容。没有跨会话消息、没有与父级的消息、没有嵌套委托、没有持久历史。
  • 仅涉及运行时和 shared:不改动 IPC、存储、主机协议或渲染器,也不改动 AgentEventEnvelope —— 对等消息本来就是一次由 parentToolCallId 和 agentName 归属的委托工具调用。参见 ADR 0138、ADR 0140、 03-runtime/02-agent-runtime.md §5f.2 和 E2E-165。

2026-09-01 —— 子智能体模型选择(D278) ​

  • 决策 D278 在智能体运行时和 provider 模型系统中扩展 D201 / ADR 0062 / ADR 0089。Task 工具获得可选的 model 参数("provider/modelId"),在该次 运行中覆盖委托 frontmatter 钉住的模型。解析优先级:Task.model → 定义 frontmatter 钉住值 → 会话模型。
  • ModelBinding.availableForSubagents(布尔值,默认 false)是 provider 模型 绑定上的可选加入标志。启用后,该模型出现在注入父智能体系统提示的委托目录 里。父级看到的模型摘要列出所有可用于委托的模型。
  • 父级指定的模型未配置或未启用委托时,Task 工具返回一个列出可用模型的工具 错误。与当前会话 provider/model 完全相同的重复视为继承,等价于省略 model,因此空的委托目录不会把父级模型变成一个假的「模型不可用」错误。
  • 实现澄清(2026-09-13,ADR subagent-model-opt-in):subagentProviders 可包含仅供定义固定使用的模型,不是覆盖许可列表。新增启动字段 subagentModelKeys 单独表达许可,缺省为空,并参与运行时复用判断。 只有启动许可键进入复用快照。按需授权写入独立缓存,不得用不同 provider id 覆盖固定模型,也不会替换空闲运行时。重复定义自己的固定键视为省略 model。 按需匹配使用唯一提供商查找。Task 目录同时展示各定义的默认模型。
  • 范围澄清(2026-09-15,issue #386):该许可约束所有让 AI 为委派工作挑选模型 的入口,不只是 Task.model。session/collaboration/spawn 的 modelKey 指向未勾选的模型时,在创建 worker 之前以 PERMISSION_DENIED 拒绝;省略该键 或写出默认模型自己的键仍按继承处理。models.list 依旧返回全部就绪模型及其 标志——该标志只是给调用方的提示,权威判断仅在 Electron main。
  • sidecar 启动时未预解析的模型通过发往 Electron main 的 provider.resolveSubagentModel RPC 按需解析,凭据和 models.dev 快照都在 那里。
  • 参见 03-runtime/02-agent-runtime.md §5f、 03-runtime/11-provider-model-system.md §7 和 E2E-166。

2026-09-01 —— 原生窗口边缘调整大小在恢复前等待稳定(D279) ​

  • 决策 D279 保留 Electron 在所有平台上的原生窗口边缘和角落调整大小所有权, 包括无边框的 Windows/Linux 外壳,并继续使用 1040×700 的最小尺寸。渲染层 拖动区域不会替代操作系统命中测试,也不会引入第二条调整大小路径。
  • 边界恢复会等待原生边界快照稳定 300ms 后,才可能恢复过小或被收起的窗口。 正常基础边界会在最后一次调整大小/移动事件后去抖 600ms 保存,关闭时仍会 刷新最新的稳定状态。工作面板预留产生的预期 setBounds 结果不会被计入跨 显示器用户移动归因。
  • 不涉及 IPC、存储、宿主协议或渲染层调整大小契约。这是对 D156/D163 的细化, 并由 E2E-167 覆盖。

2026-09-01 —— 展开侧边栏宽度可由用户调整(D280;由 D408 取代) ​

  • 展开侧边栏拥有持久化的首选宽度,范围为 240..520px,默认 275px。 右边缘渲染器手柄从按下位置预览宽度,让 MainChat 连续回流,并只在指针 释放时保存。
  • 手柄是垂直 ARIA 分隔器。左右方向键按 16px 调整,Home 和 End 选择边界, 键盘操作立即提交。Escape、指针取消、捕获丢失和卸载会将正在进行的指针 手势回滚到起始宽度。
  • 侧边栏折叠独立于首选展开宽度。不新增 IPC、原生窗口边界、工作面板预留或 项目/会话顺序契约。参见 ADR 0141 和 E2E-168。
  • D408 取代了本宽度契约在当前 shell 中的行为:展开侧边栏固定为 275px, 历史上的调整大小手柄会隐藏。D459 / ADR 0290 恢复了可调整大小的手柄, 并增加低于 160px 时收起的规则。

2026-09-01 —— 支持非回环 HTTP MCP 端点(D281) ​

  • 用户拥有和插件声明的 MCP 服务器都接受绝对的 http:// 和 https:// 地址, 包括可信的局域网地址。不支持的 scheme 和格式错误的 URL 仍然无效。
  • 用户 MCP 编辑器会对非回环 HTTP 显示明确的未加密连接警告。插件声明的端点 仍然需要 mcp.server.remote,且主机必须被 manifest.net.domains 覆盖。
  • MCP 客户端关闭自动重定向,最多允许五跳,只接受 HTTP(S) 目标,并在每一跳 重新检查插件出网策略。跳转到未声明主机时,会在下一次请求前阻止。
  • Marketplace catalog 和插件包下载规则不变:非回环 HTTP 包下载仍然拒绝。 参见 ADR 0142 以及 E2E-024K/E2E-100。

2026-09-01 —— 任务标题可由用户重命名(D282) ​

  • 任务名称属于会话元数据,可从侧边栏会话溢出/右键菜单或项目存档中的任务行 编辑。两个入口共用同一个本地化模态编辑器,支持焦点管理、Escape/取消关闭, 并将输入限制为 1–80 个 Unicode 码点。
  • 现有 session.rename IPC/RPC 路径在主机边界校验并去除首尾空白;空标题或超长 标题返回 INVALID_PARAMS。成功重命名只更新 sessions.title,不会更新 updated_at、转录、消息数、项目绑定、空会话状态或历史通知标题快照。
  • 自定义标题会继续阻止首条 prompt 的自动标题生成。保存及重启后,侧边栏、顶栏、 项目存档和搜索都从当前摘要显示标题。参见 ADR 0143 和 E2E-021a。

2026-09-01 —— 用户可配置的思考等级覆盖(D283) ​

  • ModelInfo 仍是原始的 models.dev 记录。已有的 ModelBinding 不再覆盖其 发布的推理字段或能力标签,因此目录中的推理模型在设置里仍按推理模型显示。
  • 设置始终渲染七个规范思考等级。发布的等级为新的已知模型绑定提供初始值; 未知模型和目录中的非推理模型显示同一组未选中的选项,可以为兼容端点显式 启用。
  • 精确的模型绑定决定有效的思考能力。它的显式等级在保存时被保留,并由 Composer、Electron main 和 sidecar 直接使用,不与目录求交集。空或仅 off 的绑定解析为 off;没有自动的推理推断。不改动 IPC、存储或主机协议。参见 ADR 0144 和 E2E-005/E2E-050/E2E-162/E2E-163。

2026-09-01 —— 设置导轨采用紧凑标签层级(D284) ​

  • 设置导航使用简洁且处于同一层级的目的地标签:常规、AI、快捷键、指令、模型、 技能、MCP、子智能体、导入、项目和信息。英文使用对应的 General、AI、 Shortcuts、Instructions、Models、Skills、MCP、Subagents、Import、Projects 和 Info。
  • 视觉分组命名为偏好、智能体、工作区和系统(Preferences、Agent、Workspace、 System)。目的地 ID、顺序、搜索行为和内容归属保持不变。
  • 导轨标签可以短于选中页面的描述性标题,例如“模型配置”或“项目归档”,让导航 保持整齐,同时不丢失内容窗格中的上下文。

2026-09-01 —— 原生 macOS Intel 发布通道(D285) ​

  • D285 扩展 D126 和 ADR 0145。标签构建现在使用 arm64 的 macos-15 和 Intel x64 的 macos-15-intel,同时保留现有的 Windows x64 和 Linux x64 通道。
  • 静态 macOS 目标提供 DMG 和 ZIP,但不固定架构;每个本机运行器传入匹配的 electron-builder 架构参数,并在本地构建 Rust 主机 sidecar。工作流程会拒绝 与矩阵条目不匹配的运行器架构。
  • macOS 更新源会在上传工件前按架构重命名,并在发布阶段合并为一个 latest-mac.yml。在签名的应用内通道完成资格验证前,macOS 仍使用通知和链接模式。

2026-09-02 —— 工作面板外侧与内侧调整大小的归属(D286) ​

  • 右侧工作面板打开时,外侧的原生右边缘和右侧角落调整的是面板目标宽度 (244..720px),同时保持基础会话宽度不变。Main 通过一个 Electron 本地事件 预览并提交该目标;左侧和其他原生边缘仍调整基础聊天宽度。
  • 内侧的渲染器分隔条通过有界的 window/setWorkPanelChatWidth 通道 (1040..10000px)调整基础会话宽度,面板预留保持不变。指针更新被串行化, 取消时恢复按下时的目标值。
  • 这是 Electron 本地的行为改动;主机协议仍为 v9。参见 ADR 0146 和 E2E-056/E2E-167。

2026-09-03 —— 转录落定遮罩与绘制前贴底(D287) ​

  • 首次提交是有界的(历史超过初始挂载预算)的会话,会在同一次提交里于一层 不透明的骨架遮罩下挂载。遮罩只覆盖转录滚动区域;停靠的输入框保持可见可用。 当滚动区域的 scrollHeight 与 clientHeight 连续三帧读数相同,或达到 600ms 上限时揭开,并在 160ms 内淡出。每个采样帧都会把仍固定的转录重新贴底, 因此揭开的那一帧已位于最新回合。小地图和跳转控件在遮罩揭开后才挂载。短转录 不会显示遮罩。
  • 固定跟随改为在内容 ResizeObserver 回调内、浏览器绘制之前直接贴底,而不是 在回调里申请一帧:申请的帧落在变高的内容已经以未贴底状态绘制一次之后。内容按 border-box 观察,因此输入框发布的底部预留(padding)变化也会触发跟随;同时观察 滚动容器本身,让视口尺寸变化时底部仍然可见。
  • 发送时在主机往返之前就清空输入框,若 store 拒绝发送则恢复草稿;发送失败后 用户新输入的文字绝不会被覆盖。发送读取 textarea 的实时值,并且两处静默拒绝 (模型未配置、粘贴仍在保存)现在会弹出提示。参见 E2E-071i。

2026-09-03 —— 用户行在主机往返之前插入(D288) ​

  • 用户的提示在发送的那一帧就出现在转录里,而不是等主机回显。此前该行要等主机 完成回合准备(运行时启动、session.beginTurn、附件准备、落库),主机变慢时 按下 Enter 后转录会明显长时间不变。
  • 渲染进程生成消息 id(crypto.randomUUID()),以该 id 插入行 (optimisticUserMessage),并通过 AgentPromptRequest.messageId 传给主机。 当该 id 是会话中尚不存在的 UUID 时,主机以它持久化并回显持久行,否则自行 生成。因此回显会原位 upsert 乐观行;已包含回显的持久读取优先于乐观行,尚未 包含的则保留该行可见(mergeLiveSessionMessages)。
  • 乐观行显示输入的文字及按源路径列出的文件引用;回显带来展开后的斜杠命令正文、 command 芯片、会话作用域的附件 ref 以及修订元数据。
  • 撤回:发送从未到达主机时(被待处理的计划拒绝,或 IPC 失败),渲染进程只移除 自己的行对象,因此落库之后的失败会让持久回显与错误行并存。智能停止已把该行 视为已发送的提示并拉回输入框。可见会话插入 messages;后台会话(排队提示 排空)插入其渲染缓存,回显也落在那里。
  • 编辑重发走同一路径:改写后的提示立刻替换旧行,主机回显再将其落定。参见 E2E-071i。

2026-09-03 —— 显式禁用应用键盘快捷键(D289) ​

  • 应用快捷键覆盖有三种状态:属性缺失使用平台默认值,合法的可移植绑定字符串 表示自定义,显式的 JSON null 表示「未绑定」(Unbound)且不派发任何操作。 非法字符串保留现有的安全回退到默认值。
  • 设置把「禁用」与「恢复默认」分开展示,并渲染本地化的未绑定状态。未绑定的 操作不参与冲突检测,且仍可编辑。这份共享状态驱动渲染器匹配、macOS 菜单 加速键和全局插件启动器。
  • 解绑 openPluginLauncher 会注销任何先前的 Electron 全局快捷键、禁用 Windows 宿主钩子并移除聚焦窗口回退。宿主钩子在 Electron 应用有效设置之前保持禁用。 可配置的 macOS 原生角色菜单项在解绑时变为无角色的可点击项,使 Electron 无法 恢复其默认加速键。
  • 现有的设置 JSON、IPC 方法、协议版本、存储架构和插件本地快捷键契约保持不变。 参见 ADR 0148 和 E2E-072。

2026-09-04 —— 转录中平静的运行状态动效(D290) ​

  • 用静态可读文本加紧凑的状态标记,取代「工作中」指示器和实时活动标签所用的 高对比文字闪光。流开始前的「工作中」指示器使用三个错开的小圆点;活动中的 活动标签使用一个小脉冲标记,而工具旋转指示和既有的助手流式导轨保留各自的 状态反馈。
  • 标记脉冲对合成器友好,循环上限一秒,并在 prefers-reduced-motion 下禁用; 状态仍以静态文本和圆点保持可见。不改动协议、存储或运行时行为。参见 ADR 0149 和 E2E-053/E2E-083。

2026-09-04 —— 用平静的行内智能体标记取代空首页的吉祥物精灵图(D291) ​

  • 用一个 100px 的行内 SVG 取代空首页主视觉里随机的位图姿势图集,SVG 包含一个 紧凑的中性智能体、一圈细轨道和一个信号点。核心缓慢呼吸,眼睛偶尔眨动。
  • 该标记是确定性的装饰元素。指针悬停不改变它的节奏或几何;减少动效时冻结其 CSS 动画,同时保持同样的尺寸、位置和语义颜色。
  • 不改动协议、存储或运行时行为。参见 ADR 0150 和 E2E-046/E2E-099/US-UI-17。

2026-09-04 —— 让工作面板留在固定的应用窗口内(D292) ​

  • 工作面板仍是文档流内的右侧列。打开与折叠通过在现有客户区内做 flex 分配 动画完成,而不是扩大或缩小原生 BrowserWindow。
  • 渲染器拥有的内侧分隔条调整持久化的 244..720px 面板宽度;原生窗口边缘只 调整应用窗口,从不改写面板偏好。Escape、取消和丢失指针捕获都恢复按下时的 面板宽度。
  • window/setWorkPanelReservation 接缝为兼容而保留,但把每个合法请求归一化为 { requested: 0, reserved: 0 }。原生 Browser 视图仍跟随渲染器测量的面板 矩形。参见 ADR 0151 和 E2E-056/E2E-160/E2E-167。

2026-09-04 —— 空首页播放八帧挥手吉祥物(D293) ​

  • 用一张经处理的八帧 GIF 取代空首页的行内 SVG 智能体标记,占用现有的 100px 槽位。循环先短暂停留在待机帧,然后播放挥手;指针悬停不改变节奏。
  • 减少动效时通过 CSS 把 GIF 换成静态的第一帧 PNG。该标记保持装饰性,没有随机 选择、JavaScript 计时器或悬停状态。
  • 不改动协议、存储或运行时行为。参见 ADR 0152 和 E2E-046/E2E-099/US-UI-17。

2026-09-04 —— 按主题区分的空首页吉祥物 GIF(D294) ​

  • 用专门的浅色和深色八帧素材加对应的静态 PNG 取代单一的空首页吉祥物 GIF。 CSS 跟随 document.documentElement[data-theme];除 light 以外的值都使用 深色那一对。
  • 减少动效时仍换成当前主题的第一帧 PNG。播放、悬停和装饰角色不变。
  • 不改动协议、存储或运行时行为。参见 ADR 0152 和 E2E-046/E2E-099/US-UI-17。

2026-09-04 —— 通知收件箱只列出失败(D295) ​

  • 侧边栏页脚的收件箱只渲染 task.failed 行,未读徽章也只统计未读的失败。 例行的顺利完成此前把真正需要关注的行淹没了。
  • host-core 仍原样持久化 task.completed 记录。它们继续驱动侧边栏会话结果 徽标和窗口未聚焦时的原生通知;只有收件箱浮层在渲染进程边界把它们过滤掉 (inboxNotifications / inboxUnreadCount)。
  • 标记全部已读和清除仍作用于完整的持久记录,因此隐藏的完成记录会与可见的 失败一起被读取或删除。协议与存储不变。仅取代 D117 中"任务完成行"的条款。

2026-09-04 —— 扩展页不再绘制分割线(D296) ​

  • 扩展目标页把所有页内线条换成色调与间距:标头和工具栏去掉底边线,分段选项 卡放在带底色的胶囊轨道上、当前项以投影抬起,已安装列表由每组标签下堆叠的 柔和色块行组成,不再是一块细线分隔面板。市场来源设置、分类芯片、卡片、空 状态、行详情块以及详情抽屉的安装条与各节都遵循同一套三层方案(页面、色块、 抬起)。原本带内嵌描边的芯片改为填充。
  • 细线只保留在浮层——行菜单与页面菜单、提示气泡、详情抽屉和对话框——那里的 边缘是层级提示而非分区。菜单分组分隔线改为留白。
  • 详情抽屉在所有平台都预留标题栏区带,而不只是 Windows/Linux:路由表面在入场 动画结束后仍保留 transform,抽屉的固定层因此叠在路由内部,macOS 上区带曾 盖住其标题行。
  • 截图装置:插件夹具在页面挂载后重新注入(挂载时的刷新曾把它覆盖掉),注入 期间桩掉市场搜索与详情调用,并新增 pi-plugins-row-details / pi-plugins-sheet 场景。协议、存储与运行时行为不变。细化 D169 的已安装页 条款及 UI IA §3.5。

2026-09-05 —— 滚动条只在悬停或滚动时显示(D300) ​

  • 渲染进程的每个滚动容器现在都使用 base.css 定义的同一套安静滚动条:8px、 无轨道、静止时滑块透明。只有所属滚动容器被悬停(:hover::-webkit-scrollbar-thumb) 或带有 data-scrolling 时才绘制滑块,指针悬停在滑块上或拖动时加深。此前全局 滑块以 16% 文字色常显,只有侧边栏两个列表在静止时隐藏(D147、E2E-157)。
  • lib/scrollbar-reveal.ts 由 main.tsx 安装,补上 CSS 表达不了的状态:在 document 上以被动捕获方式监听 scroll,给发生滚动的元素打上 data-scrolling 标记,并在其最后一次滚动事件 800 毫秒后清除;视口滚动则标记 <html>。程序化 滚动(流式输出时的固定跟随)同样会显示滑块,与 macOS 覆盖式滚动条一致。释放时 清除它设置过的所有标记。
  • 各样式分片不再设置 scrollbar-width 或 scrollbar-color(提问工具选项、子智能体 行、工具字段、侧边栏列表)。WebKit 与 Chromium 会在设置了任一属性的元素上忽略 ::-webkit-scrollbar,因此这些表面此前渲染的是常显的原生滚动条而非自定义样式。 侧边栏列表在全局规则之上保留 6px 宽度、20% 文字色和 focus-within 显示;代码块 的覆盖规则因冗余而移除。interaction-polish.test.mjs 断言静止/显示规则以及标准 属性不存在;scrollbar-reveal.test.mjs 覆盖标记的生命周期。取代 D147 的滚动条 条款,并把 UI 设计系统侧边栏节的处理推广为全局。协议、存储与运行时行为不变。

2026-09-05 —— 全应用去边框化(D297) ​

  • 把 D296 从扩展页推广到渲染进程的每个表面。页内的描边——面板环、卡片轮廓、 行与节的分隔线、左侧导轨、表单字段边框、内嵌细线芯片、侧边栏页脚与导轨接缝、 工作面板的边缘与标头线、输入框的层级描边——全部换成三层色调加间距:页面、 --ds-tile(文字色 3.5% 混合;悬停 6%、加深 8%)以及 --ds-raised 加 --ds-raised-shadow。标记定义在 tokens.css;D296 的 --plugins-* 变量成为其别名。
  • 列表变为以 2–6px 间距堆叠的色块行(设置行、快捷键表、能力页、服务与模型行、 项目归档、会话导入、通知收件箱、版本历史)。选中态(主题与语言卡片、分段控件、 级别筛选)是抬起的胶囊或色块加已有对勾,不画选中边框。表单控件为色块填充加 强调色焦点环。Markdown 去掉代码块、表格、引用块与标题的线条(斑马行、色块、 留白)。子智能体树在色块组上用 2px 着色条作连接线,节点抬起。
  • 浮层保留边缘:菜单、弹出层、自动补全、搜索对话框、对话框、提示、吐司与悬停卡 保留 0 0 0 0.5px border-default 加阴影,但其内部的线条已移除。控件本身的形态 (开关焦点环、滚动条内边距、加载圈描边、状态点挖空)不是分隔线,予以保留。
  • 主题卡片的对勾字形改用 --ds-bg-primary 墨色,而非在深色主题的白色强调色上 不可见的 #fff。样式测试按文件断言新方案。协议、存储与运行时行为不变。取代 UI 设计系统 §6.4 的描边行与 D148 的 0.5px 字段描边。

2026-09-05 —— 流式回复会被检查点保存并恢复(D299) ​

  • 会话中正在流式输出的助手回复由 Electron 主进程至多每 1.5 秒通过 session.saveInflightMessage 检查点到 host-core,写入 sessions/<id>.inflight.json,每个会话一个原子替换的文件。此前回复只在 message_end 时才持久化,因此应用退出、sidecar 崩溃或回复中途重启会丢掉用户 已经看着流式输出的全部文本;重启后会话只剩下提示。
  • 检查点是临时的。同一 id 的最终行会移除它,completed/error 的回合结束会移除 它,而主机启动扫描以及 sidecar 丢失时的 session.endTurn { recoverInflight: true } 会把最终行从未落盘的残留检查点提升为该回合的 aborted 助手行写入转录。用户停止 不动检查点,留给即将到达的最终行处理。已被索引的 id 的迟到检查点会被丢弃。子代理 回复不做检查点。
  • 退出时先刷写检查点,再通过 sidecar 中止活动回合,并在 host-core 释放前有界等待 (总计 2 秒)其中止行经由发件箱落盘。此前 sidecar 在 host-core 已经关闭时才被杀掉。
  • 渲染器的停止不再为了落定已开始的回复而重写转录;它只在内存中落定,把持久副本 留给运行时自己的中止最终行。那次重写与发件箱追加竞争,可能删除它本想保留的回复, 并且会把渲染器显示截断过的行写回覆盖完整行。未应答提示的撤销(以及消息删除) 现在从完整持久转录与实时行的合并结果重写,并在该合并结果上重新判定撤销。 ADR 0153、E2E-171。

2026-09-05 —— 输入框草稿在卸载和切窗后仍保留(D301) ​

  • 输入框草稿仍按会话留在渲染器内存中,但缓存改为模块级 Map,而不再是某一个 Composer 实例里的 useRef。空态切换时首页/停靠输入框仍会卸载再挂载,离开聊天 进入设置/插件/其他页面时 ChatSurface 仍会卸载;这些卸载以前会丢掉内存 Map, 因此未发送的输入在切窗口/切页面后消失(issue #38)。
  • 下一次挂载从同一槽位恢复。卸载(layout cleanup)、会话切换、文件引用变化,以及 窗口失焦/可见性隐藏,都会捕获 contenteditable 的实时值,以免尚未重渲染的按键 丢失。若 Chromium 在窗口位于后台时清空 contenteditable,聚焦/可见性恢复会把缓存 值重新绘制回去。首页文件引用按空会话 id 归属,而不是 __home__ 缓存键。
  • 成功发送仍只清除提交该请求的会话槽位;删除会话仍丢掉其槽位;不写磁盘。 composer-draft-cache.ts 与 composer-draft-cache.test.mjs 覆盖隔离、清理、卸载 再挂载和 Composer 接线。扩展 E2E-011c。协议、存储与运行时行为不变。

2026-09-05 —— 展开的委托运行过程跟随最新输出(D302) ​

  • 决策 D302 在渲染层细化 D271 / D071。D271 把委托的嵌套行放进有界的 .subagent-run-rows 滚动区,以免展开一个四十次工具调用的运行过程撑高父级 转录,但该滚动区没有吸底。实时卡片因此停在展开时刻的偏移,思考、工具和 回答行在视口下方追加(issue #37)。
  • 嵌套滚动区现在独立使用父级转录的吸底跟随契约:展开时钉在最新一行,钉住时 内容增长跟随底部,第一次真正的向上手势暂停跟随并显示嵌套的「回到最新」 控件。布局夹持和程序性 scrollTo 都不算那次手势。.subagent-run-rows 关闭原生 overflow anchoring,以免对抗吸底。
  • 跳转控件叠在滚动区外层的相对定位包装上,绝不放在 .subagent-run 上,因此 绝对定位的折叠导轨不会被裁掉。仅涉及呈现:不改动 IPC、存储、主机协议或 运行时。E2E-173。

2026-09-05 —— 新会话从绑定的默认思考等级开始(D303) ​

  • 设置页已经持久化 ModelBinding.defaultThinkingLevel,E2E-163 也要求新会话从该 等级开始。但首页草稿 Composer 芯片和 persistSessionAndSelect 却用 highestSupportedThinkingLevel 播种,所以用户把默认设为“低”后,每个新会话仍从 Max(或最高已启用等级)开始。启动时的回退链也永远轮不到存储的默认值,因为会话 行在建号时已经被写成最高档。
  • 新草稿和新建会话现在取所选模型绑定的默认思考等级,并钳位到已启用档位。只有绑 定没有存储默认值时才回落到最高已启用档。在没有活动会话的草稿上切换模型也会按 新模型的默认值重新播种;已有会话在新模型支持当前等级时仍保留该等级。
  • 协议、存储和运行时钳位规则不变。已有会话行保留其存储的 thinkingLevel。 取代 D153。见 E2E-082 / E2E-163 / E2E-174。

2026-09-05 —— 新建任务在主机 IO 之前露出空目标(D305) ​

  • 新建任务以前要等 session.list、session.create、再一次 session.list 和 session.get 才离开上一场转录。这段链路让点击像卡住:旧对话留在屏幕上, 然后才跳到空态。列表刷新只是为了避免刚发出的第一条消息仍被当成空会话; 渲染器已经能从 runningSessions、实时行和已提交草稿判断这种情况。
  • 创建会话现在第一帧就清掉上一场对话,并从 session.create 摘要插入持久行。 复用该组最新空会话时,同一帧提交空转录。发送和粘贴会等待进行中的创建, 而不是再开一个槽位;创建期间在空首页打的字会迁到新会话。
  • 决策 D305 只修正 D252 / ADR 0113 的时序。复用规则、持久空槽位和协议/存储 版本不变。ADR 0154,E2E-011a / E2E-011d / E2E-011g。

2026-09-05 —— 启动页复用 macOS 侧边栏玻璃态(D304) ​

  • macOS 上启动页(.startup-splash)去掉不透明的 --ds-bg-primary 底色,改用 侧边栏同款玻璃配方:--ds-sidebar-glass-tint 加上下 sheen 渐变,叠在窗口原生 under-window vibrancy 之上。ready 一翻转 shell 就会挂载到启动页之下、早于 最短停留结束;在玻璃后面它会透过 tint 漏出来,因此 darwin 上启动页加载期间 shell 保持 visibility: hidden,退出淡出时再以 opacity transition 淡入(不用 animation,避免侧边栏自身的 sidebar-in 挂载动画被重放),与启动页交叉过渡到 不透明主面板和玻璃侧边栏。
  • Windows 与 Linux 仍保留不透明启动页;规则与侧边栏一样限定在 :root[data-platform="darwin"]。启动页、侧边栏和导轨共用同一套玻璃选择器,避免 配方漂移。玻璃 token 的注释同时标注两个使用方。 macos-sidebar-vibrancy.test.mjs 断言 darwin 下启动页使用 tint 与 sheen, 基础规则仍为 --ds-bg-primary。扩展 UI 设计系统 §8.3;无协议、存储或运行时 行为变化。E2E-076。

2026-09-05 —— 搜索/读取截断表示切断,而不是窗口(D306) ​

  • Read.truncated 以前只要文件在返回窗口之后还有内容就为真,而 48KB / 500 行上限会切断普通源码和规格表。于是几乎每次 Read 都亮起 UI 截断芯片, 并让模型去 Grep,哪怕调用方已经用 offset/limit 填满了窗口。
  • truncated 现在仅在本次结果被切断时为真:字节预算打断窗口、某行被剪辑, 或 Grep/Glob 的上限挡住了剩余命中。填满的 Read 窗口即使文件更长也是 truncated: false;notice 写出下一个偏移,而不再催促 Grep。
  • 默认 Read 窗口回到 2000 行(最大 4000)。BUDGET_SEARCH 为 128KB / 4000 行, 让典型源码的该窗口实际装得下。单行剪辑为 16,384 个字符,规格表行能完整 返回,压缩后的超长单行仍会被剪。
  • 决策 D306 修订 D033 / D194。涉及 host-core 工具结果、sidecar 的 Read schema 文案,以及截断芯片契约。不新增 ADR:字段仍是布尔值,已经尊重 truncated 的调用方继续有效。见 03-runtime/16-tool-result-limits.md 与 E2E-147 / E2E-175。

2026-09-05 —— 修订载荷跟随实时分支(D307) ​

  • 一个重新生成系列的活动变体只在重新生成后的第一次 agent_end 归档一次, 之后再不碰它。而实时分支一直在生长:之后的每条提示都追加在上面,以 provider 错误结束的回合更是根本到不了 agent_end。把根消息切走再切回, 会恢复第一次 agent_end 时的归档,并据此重写转录本及其 JSONL,把之后 的每一回合都删掉。一个真实会话里有 17 条消息(一个改过文件的回合)正 暴露在这种风险下。
  • 现在每个会丢弃实时分支的操作都先把它写回自己的变体。 session.activateRevision 读取持久转录本,找到该系列(根 id,或第一条 带该 revisionRootId 标记的用户消息),刷新根消息 activeRevision 标记 所指的变体,然后才切换;当该系列存在时,前缀取自转录本而不是渲染进程 的副本。应用主进程的重新生成路径向 session.saveRevision 传入 revisionIndex,让被丢弃的尾部刷新它的变体,而不是被当作"已归档"直接 丢掉。session.saveActiveRevision 对已归档索引做刷新。
  • 刷新只是在追加式修订文件里多写一行(同一 (rootUserId, revisionIndex) 以最后一条为准,read_revision 本来就是这样)并更新 message_count; 不新建索引行。被重新生成标记过、但因回合失败从未归档的分支会存为新变体, 因此永远不会覆盖之前的变体。
  • 切换同时把每条幸存消息所属的 turn_id 带过索引重建(以前会把整个会话 的归属清零,和 replace_messages 早先的问题一样),并保留锚点仍存在的 检查点。
  • 决策 D307 修订 D109。涉及 host-core 的 sessions.rs / rpc/mod.rs 与 应用主进程的重新生成路径;渲染进程不改。不新增 ADR:文件格式和协议形状 不变,revisionIndex 是可选参数。见 03-runtime/04-data-storage.md §4.9 与 03-runtime/06-host-rpc-protocol.md。

2026-09-05 —— 智谱 / Z.AI 命名端点预设(D308) ​

  • 添加智谱要先知道四个 URL 中该粘贴哪一个。添加提供商对话框现在提供一个 「服务」选择器,包含中国区和国际标准 API 以及 GLM Coding Plan。选中一项会 填入名称、锁定公布的 Base URL,并存储 models.dev 的 vendorKey。
  • 行仍是 openai_compatible + chat_completions。pi-ai 已经能与这些主机对话; PI-Desktop 不新增智谱 apiStyle 或 SDK。
  • 匹配 URL 或 vendorKey 的补全请求会设置 thinkingFormat: "zai" 与 zaiToolStream: true,因为存储的提供商 id 是 UUID,无法使用 pi-ai 按 zai / zai-coding-cn 提供商名的检测。
  • 决策 D308 修订 D024。参见 ADR 0155、03-runtime/11-provider-model-system.md、 03-runtime/12-provider-config-schema.md 和 E2E-005D。

2026-09-05 —— 添加提供商的常见路径是「服务 + 密钥」(D309) ​

  • 添加提供商表单原先把服务、名称、Base URL、API 密钥和 API 格式堆在一起。 命名端点本来就知道名称、URL 和线路格式。
  • 新对话框从「服务」开始。命名的智谱 / Z.AI 和 OpenCode Go 随后显示服务 + API 密钥和一段主机摘要。自定义端点随后显示名称、Base URL 和 API 密钥。 「高级」保留名称(命名端点)和 API 格式(自定义端点)。
  • OpenCode Go 移入「服务」选择器,用户不必再到 API 格式下面找它。
  • 决策 D309 修订 D308。参见 ADR 0156、04-ux/06-settings-ia.md 和 E2E-005 / E2E-005B / E2E-005D。

2026-09-05 —— 自定义格式放在密钥旁;models.dev 头部厂商(D310) ​

  • 自定义端点现在把 API 格式放在 API 密钥旁边,而不是「高级」里。
  • 「服务」先列出由 models.dev 支撑的国际与中国一方端点,每个都带公布的 URL 和线路风格。
  • 决策 D310 修订 D309。参见 04-ux/06-settings-ia.md 和 E2E-005。

2026-09-05 —— 可搜索的服务选择器,发现不再卡顿(D311) ​

  • 「服务」原先是带分组的原生下拉框,在对话框里打开很卡,也无法过滤约 20 个厂商。
  • 现改为与默认模型选择器相同的可搜索锚定菜单:按显示名、vendor key、别名或主机过滤;分组仍是国际 / 国内,自定义端点在最前。
  • 选择命名厂商在填入 API 密钥前不再探测 /models。输入密钥不会在每次按键时进入 loading;hook 等待 600 ms 防抖。更换端点会立刻清空上一份列表。
  • 决策 D311 修订 D310。见 04-ux/06-settings-ia.md 与 E2E-005 / E2E-005D。

2026-09-05 —— 服务按厂商平铺并加入小米(D312) ​

  • 服务选项不再分国际 / 国内。自定义端点仍在最前,其后是可搜索的单一厂商列表。
  • 小米是命名端点:models.dev vendorKey: "xiaomi",https://api.xiaomimimo.com/v1,Chat Completions。别名 mimo、xiaomimimo。
  • 决策 D312 修订 D310 / D311。见 04-ux/06-settings-ia.md 与 E2E-005。

2026-09-05 —— 已发布语言注册表与语言选择器(D314) ​

  • 设置 → 常规的语言原先是三张预览卡(跟随系统 / 简体中文 / English),无法扩展,名称也是写死的。
  • 界面语言现在列在 @pi-desktop/i18n:en、zh-CN、tr。本地名称保持 endonym。「跟随系统」仍通过 preload 跟随 app.getLocale()。
  • 外观卡的主题仍是三张预览卡。语言改为可搜索选择器:顶部钉住「跟随系统」并显示检测到的本地名称,然后列出已发布语言。搜索匹配本地名称、英文名称和 id。
  • 插件标签和更新日志仍为 en + zh-CN,其它语言回退到英文。
  • 决策 D314 修订 D073。见 ADR 0160、04-ux/02-i18n-english-first.md、04-ux/06-settings-ia.md 与 E2E-091。

2026-09-05 —— Grep 在本机有 rg 时优先使用(D315) ​

  • Codex 在本机有 rg 时用 shell 搜索。PI-Desktop 仍把 Grep 作为面向模型的工具,只在进程 PATH 或 Unix login PATH 上找到用户安装的 rg 时把它当作实现后端。
  • 结果形状、128KB / headLimit 预算、按修改时间新到旧、以及显式 path 的 ignore 例外仍由 host 定义。rg 以空 stdin 和 argv 直接 exec,不经过 Bash。spawn 失败或 exit 2 回退到进程内 ignore + regex。PI_DESKTOP_RG 指定二进制;PI_DESKTOP_DISABLE_RG 强制回退。
  • 代理仍应调用 Grep,而不是在 Bash 里跑 rg。Read 不变。安装包不自带 rg。
  • 决策 D315 修订 ADR 0057 / D195。见 03-runtime/03-tools-and-permissions.md、03-runtime/02-agent-runtime.md §7 与 E2E-147。

2026-09-05 —— 可搜索主题选择器(D316) ​

  • 设置 → 常规的主题原先是三张预览卡(系统 / 浅色 / 深色),插件主题会挤进同一网格。
  • 外观卡现在与语言使用同一可搜索选择器:顶部钉住系统、浅色、深色,插件主题在分隔线之后。搜索匹配名称、描述、id 和插件 id。
  • AppSettings.theme 以及不可用插件主题回退到 system 不变。
  • 决策 D316 修订 ADR 0160。见 ADR 0161、04-ux/06-settings-ia.md 与 E2E-091。

2026-09-05 —— A2A 寻址跨越同一宿主上的会话(D318) ​

  • 决策 D318 修订 ADR 0147。本地 A2A broker 仍留在 host-core,带能力令牌认证和 持久化任务;「仅限同一上下文」的发现与发送限制被解除,因此两个处于不同打开 会话中、具备 A2A 能力的委托可以协作。
  • a2a.agents.list 返回所有其他活跃智能体;卡片携带 contextId。 a2a.message.send 可以寻址任何已注册的对端(省略 to 时仍优先同会话对端)。 任务访问按成员资格 —— 请求方或执行方 —— 而不是按上下文。 A2A_CROSS_CONTEXT_DENIED 保留在线路列表中,但不再产生。
  • 活跃对端 id 在整个注册表内唯一:冲突的 card.name 在注册时加后缀去重,运行 时采用返回的 agentId。a2a.task.event / a2a.push 增加 recipientContextId;每个会话运行时只在该字段与自己的 sessionId 匹配时才 投递事件。
  • 不变:父级不能调用 A2A,已落定的委托会注销,没有嵌套委托,没有远程智能体。 参见 ADR 0162、03-runtime/02-agent-runtime.md §5f.2 和 E2E-165c。

2026-09-05 —— 实时/持久化记录合并保持时间顺序(D317) ​

  • 再验证一个仍在跑的会话时,曾把有界持久化页当作数组前缀,把每条实时独有行追加在后面。长会话一旦积累超过最新 100 条,D261 的尾部挂载窗口就会把旧历史画在底部,刚发送的用户提示被藏起来,回合却继续跑。停止后再切换一次看起来像“恢复”,是因为空闲路径跳过了这次合并,只显示持久化页。
  • mergeLiveSessionMessages 现在按时间顺序缝合:早于该页的实时行留在前面,重叠的已完成行以持久化副本为准,乐观或流式尾巴留在后面。若更早的前缀曾被追加到页后,当其 createdAt 早于该页时把它治回前面。
  • 决策 D317 修订 ADR 0120 / D261 / ADR 0137。见 04-ux/09-interaction-patterns.md 的会话隔离,以及 E2E-177。

2026-09-05 —— 从记录文件 / outbox 恢复缺失的 sessions 行(D318) ​

  • 长会话可能从侧边栏消失,记录被重置成第一条用户行,同时数百个回合堆在 session-message-outbox.json。SQLite sessions 行没了;session.appendMessage 因 session not found 失败;outbox 卡在队头不再排空。
  • 主机启动现在会恢复每一个 sessions 行缺失的活 JSONL,重新插入该行并根据文件重建搜索索引。session.appendMessage 做同样的恢复;文件也不在时用同一个 id 插入占位行,以便排队的 outbox 能排空。session.delete 丢掉该会话的 outbox 条目,以免占位把用户已删除的对话救回来。
  • 决策 D318 修订 ADR 0041 / D119。见 03-runtime/04-data-storage.md、03-runtime/07-process-model.md 与 E2E-178。

2026-09-05 —— 父智能体跨对话协作(D321) ​

  • 决策 D321 修订 ADR 0147 / ADR 0162。Agent 模式的会话运行时在其生命周期内 注册一张 kind: "parent" 的 A2A 卡片,并把 A2A 作为核心父级工具暴露出来, 因此同一宿主上的两个对话可以协作。
  • 发现与发送按 kind 划定范围:父级看到其他父级,子智能体看到其他子智能体。 父级不能寻址子智能体,包括它自己的,因此 Task* 仍是通往委托的唯一通道。
  • 入站的父级事件排队;空闲会话把它们前置到下一条用户提示,而不是自动开始 一个回合。一个对话在本进程拥有 Agent 运行时之后才可达。
  • 参见 ADR 0164、03-runtime/02-agent-runtime.md §5f.2 和 E2E-165d。

2026-09-05 —— 已发送的文件引用保持芯片并可点击打开(D320) ​

  • 输入框芯片发送后变成完整 @path 文本,用户刚放进输入框的节点被挤成一长串路径;临时目录的绝对路径也无法点击。
  • 对话区现在把这些标记画成与输入框一致的叶子名芯片。工作区 HTML 在侧边浏览器预览;其余允许的文件通过 pi-desktop/fs/open 用系统默认应用打开。
  • 决策 D320 修订 D209 / ADR 0070。见 ADR 0163、04-ux/08-component-spec.md §8.3 / §11.8、04-ux/09-interaction-patterns.md §8a.2、03-runtime/01-ipc-protocol.md 的 fs,以及 E2E-180。

2026-09-05 —— 转录中的相对路径可预览并打开(D322) ​

  • 助手 Markdown 里的相对文件路径经常不可点击,而在查看的 Markdown 文档中, ./ / ../ 链接是从工作区根目录而不是该文件所在目录解析的。
  • 渲染器现在把 Markdown 中裸露的工作区文件标记链接化,并在文件标签页预览 Markdown 时把 ./ / ../ 相对于当前查看的文件解析。向上逃逸的父路径保持 惰性。remark 插件是一个 unified attacher,因此打开会话不会因 tree.type 崩溃。
  • 决策 D322。参见 04-ux/08-component-spec.md §8.3 和 E2E-182。

2026-09-05 —— 实时的父级回合保持透明(D323) ​

  • D297 用整回合的 --ds-tile 取代了流式左导轨,这样色调淡入淡出不会让文本 重排。但这把普通的思考、工具和答案片段像子智能体卡片一样装进了盒子。
  • 正在流式输出的父级回合现在与已完成回合一样留在页面背景上:没有导轨、没有 预留的 14px 缩进、没有整回合的 tile。tile 只保留在委托卡片(D319)和嵌套 的 .subagent-run 上。
  • 决策 D323 修订 D101 / D297。参见 04-ux/08-component-spec.md §8.4 / §9.9 和 E2E-059a / US-UI-60。

2026-09-05 —— 空闲切换保留尚未刷入的已完成尾巴(D324) ​

  • 回合一结束,切走再切回来会加载持久化 session.get 页,丢掉仍只存在于渲染器里的助手/工具行(持久化 outbox 尚未刷入)。用户提示能留下,是因为它们在模型运行前就写入了。
  • selectSession 现在对仍有实时来源的空闲会话也合并实时行,和仍在运行的会话一样。只有当持久化页包含每一个实时 id 时才清掉实时标记。
  • 决策 D324 修订 D317 / ADR 0137。见 04-ux/09-interaction-patterns.md 的会话隔离,以及 E2E-183。

2026-09-05 —— 父级 A2A 始终在 Agent 目录中(D325) ​

  • D321 只在 broker 成功签发令牌之后才注册父级,然后把 A2A 注入目录。注册 迟到或失败会让模型没有 A2A 工具。ToolSearch 也从不列出核心工具,于是 模型把按需查找未命中当成「A2A 不存在」。
  • Agent 模式现在在运行时构造时就把 A2A 放进核心目录。首次调用(或首条 提示)签发令牌。该工具从不延迟。只要模式是 Agent,跨对话指引就在 Agent 系统提示里,而不只在注册之后。
  • 决策 D325 修订 ADR 0164 / D321。参见 03-runtime/02-agent-runtime.md §5f.2 和 E2E-165d。

2026-09-05 —— 撤回 A2A 与 Peer 协作(D326) ​

  • 进程内的 Peer 邮箱(D277 / ADR 0138 / ADR 0140)和 host-core 的 A2A 栈 (ADR 0147 / ADR 0162 / ADR 0164 / D318 / D321 / D325)被撤回。没有兄弟通道, 也没有父级到父级的通道。
  • A2A 与 Peer 不是核心工具、不是可分配的子智能体工具,也不是 ToolSearch 的结果。命名了任一者的定义被视为未知工具,带解析告警丢弃。
  • 握手协议版本从 10 升到 11,不再宣告 "a2a"。存储架构从 12 升到 13,并删除 A2A 表。v10/v11 混合配对在握手时被拒绝。
  • 决策 D326 记录为 ADR 0165,它取代 ADR 0147、ADR 0162 和 ADR 0164。参见 03-runtime/02-agent-runtime.md §5f.2、 03-runtime/03-tools-and-permissions.md §10.2、 03-runtime/06-host-rpc-protocol.md §3–§4 和 E2E-165。

2026-09-06 —— 已完成回复在进程重启后仍然存在(D327) ​

  • 回合结束后关闭应用再打开,只剩用户提示(issue #42)。用户行在模型运行前就落盘;助手/工具行走异步 outbox,在 message_end 之后才写入。D324 在同进程切会话时缝上了这些实时行;进程退出后渲染器内存没了。
  • message_end 现在在 outbox 追加之前检查点完成快照,settleIf 不会清掉写入期间开始的新回合。completed/error 的 session.endTurn 仅在该 id 已索引时才删除 .inflight.json。启动恢复把回合已是 completed 的残留检查点提升为 complete 助手行,而不是 aborted。
  • 主机握手在渲染器能够 session.get 之前等待 outbox 排空,然后 session.recoverInflightMessages 提升 outbox 未能落盘的已完成残留检查点。启动恢复会跳过已完成残留,以便完整的 outbox 行先写入。
  • 决策 D327 修订 D299 / ADR 0153 / ADR 0041。见 03-runtime/04-data-storage.md §2.1 和 §5、03-runtime/07-process-model.md §5、03-runtime/10-session-state-machine.md §4、E2E-171 和 E2E-184。

2026-09-06 —— 由父级判断子智能体寿命(D328) ​

  • 父智能体之前会中止未完成的委托:TaskWait 到期、模型收尾,然后 agent_end 杀掉残留的运行。空闲和总时长看门狗则是第二条独立的杀死路径。
  • 决策 D328 修订 ADR 0089 / ADR 0119 / ADR 0129。运行时不再武装空闲或总时长 计时器。父级空闲不会中止委托。委托完成时,运行时在同一个持久化回合内把 它们的报告作为提示交给父级。TaskList / 超时的 TaskWait 携带心跳。只有 TaskStop 和用户的「停止」会中止委托。
  • 参见 ADR 0166、03-runtime/02-agent-runtime.md §5f 和 E2E-155。

2026-09-06 —— 由智能体选择的 Bash 超时(D329) ​

  • 300 秒的 Bash 覆盖上限会杀掉长构建,并拒绝显式的 timeout: 600 / 1800。 默认 60 秒不变。
  • 决策 D329 修订 D190 / D273 / ADR 0054:接受 1–21,600 秒;超过该上限的值视为 毫秒,转换后再钳制。超时和中止仍会杀掉整个进程树。
  • 参见 ADR 0167、03-runtime/03-tools-and-permissions.md §5 和 E2E-115。

2026-09-06 —— 主进程拥有的 openExternal 白名单(D330) ​

  • 聊天 Markdown、插件面板、启动器、OAuth 和嵌入预览都可能把原始 URL 交给 shell.openExternal。用户点击 file:、javascript:、ms-msdt: 或自定义 scheme 时会进入操作系统协议处理器。
  • Electron Main 现在解析每一条这样的 URL。只有带主机名的 http(s) 和带地址的 mailto: 会打开。不允许的 URL 会抛错,因此 OAuth 报告 opened: false,插件 仍返回 INVALID_ARGUMENT。工作区文件继续走 shell.openPath。
  • 决策 D330 修订 ADR 0109。见 ADR 0168、05-security/01-security.md、 07-plugins/04-plugin-security.md §8 与 E2E-185。

2026-09-06 —— 接受的 @ 文件保留为行内芯片(D362) ​

  • 从 @ 菜单完成工作区文件时,仍会把触发符替换为空字符串并存储无哨兵引用。 那是芯片位于文本区域上方时的约定;芯片迁入 contenteditable 草稿后,只有哨兵字符会绘制,因此 Enter 会让文件消失。
  • 接受文件现在会在光标处插入一个私用区哨兵,并把该哨兵存到引用上,与剪贴板粘贴一致。Enter/Tab 确认菜单且不发送。目录仍插入 @dir/ 文本。工作区相对芯片在切换项目时仍会丢弃(绝对 scratch 路径保留),其哨兵一并从草稿中剥离。
  • 决策 D362 修订 D209 / D125。见 04-ux/08-component-spec.md §11.8、 04-ux/09-interaction-patterns.md §8a.1 与 E2E-102a。

2026-09-06 —— 随应用打包的 Files 预览与实时工作区事件(D332) ​

  • Files 插件无法预览图片;Reveal 替换标题操作后不再提供“用默认应用打开”; 用轮询检测项目切换;停靠视图收不到主题/语言事件。
  • fs.readPreview 在现有 fs.read 门控下把一份可读文件分类为 text、image、 binary 或 tooLarge。面板事件会同时发给停靠视图。workspace:changed 会投递给 面板和插件进程。
  • 决策 D332 记录为 ADR 0169。见 07-plugins/03-plugin-api.md §3/§5/§6、 04-ux/08-component-spec.md §5.2.2 与 E2E-153。

2026-09-06 —— 工作面板浏览器是随应用打包的 pi.browser 插件(D333) ​

  • 工作面板 Browser 启动项、chrome 和代理 CDP 原先是宿主 HEADER_TOOLS 行加上 Main 拥有的访客页。Files 已经证明第一方面可以走随应用打包插件;用户需要对 整个浏览器同样的启用/禁用产品,但不能把任意网页放进沙盒插件视图。
  • 随应用打包的 pi.browser 拥有 chrome 和代理 Browser 工具。访客 WebContentsView 和调试器仍由宿主拥有,只能通过受 browser.cdp 门控的公开 pi.browser.* 访问。访客页边界夹紧到调用插件视图。原始 CDP 走白名单 (拒绝 cookies/storage/Target/Fetch)。宿主 BrowserPreview 仍是 Plan 安全门面, 插件禁用时失败。不升协议/schema。
  • 决策 D333 修订 ADR 0019 / 0104 / 0105 / 0108。见 ADR 0170、 07-plugins/03-plugin-api.md 与 E2E-008b。

2026-09-07 —— 受限的会话内图片显示(D334) ​

  • 粘贴/上传的图片存为无扩展名的 attachments/<sha256> blob。渲染器源无法加载它们,历史回合只显示芯片;fs/open 还把该引用当成工作区相对路径。
  • fs/read、fs/reveal、fs/open 共用 resolveOpenablePath(工作区、scratch、attachments,以及 blob 引用)。读取会 realpath。fs/readImageDataUrl 只返回有界图片 data URL。客户端 mimeType 不能把非图片扩展名重新分类。
  • 决策 D334 记录为 ADR 0172。见 03-runtime/01-ipc-protocol.md、04-ux/08-component-spec.md §8.3 与 E2E-187。

2026-09-07 —— 宿主代发的插件补全与会话上下文(D336) ​

  • 插件可以注册工具,但不能列出已认证模型、读取当前 LLM 上下文,也不能用用户的额度打一次侧向补全。
  • pi.models.list、pi.session.getLlmContext、pi.agent.complete 是公开宿主 API。凭据和线路调用留在 Electron main。会话上下文绑定到进行中的工具会话(修订 D019)。
  • 随应用打包的 Advisor UX 暂时不发布;公共 API 仍可供显式安装的插件使用。
  • 决策 D336 记录为 ADR 0174。见 07-plugins/03-plugin-api.md、07-plugins/13-plugin-permissions-matrix.md 与 E2E-188 / E2E-189。

2026-09-07 —— OpenCode Go 会话路由头(D337) ​

  • OpenCode Go 会拒绝缺少 x-opencode-session 的 LLM 请求(MissingSessionID)。 pi-ai 0.85 不发这个头;官方 Pi coding-agent 在智能体层注入它。
  • 决策 D337 修订 ADR 0116:agent-runtime 在发往 OpenCode Go 及任何 opencode.ai 主机的会话、子智能体、提示增强和插件一次性请求上发送 x-opencode-session、 x-opencode-client: pi-desktop 和 PI-Desktop 的 User-Agent,使用持久化的 对话 id。
  • 参见 03-runtime/02-agent-runtime.md §6.2、03-runtime/11-provider-model-system.md、 03-runtime/12-provider-config-schema.md、ADR 0116 和 E2E-005D。

2026-09-07 —— 用实时智能体活动状态解释安静的活动回合(D338) ​

  • 转录在第一个事件之前显示一个通用的运行计时器,但 provider 等待、重试退避 或父级等待委托工作时仍可能没有任何一行来解释延迟。用户只能从「停止」按钮 推断还在活动。
  • 既有的归一化 status 事件现在携带一个可选的、由运行时拥有的 AgentActivity 阶段:starting、waiting-model、retrying 或 waiting-subagents。渲染器 按会话保存它,并渲染一条带已用时间的紧凑本地化状态行;不增加百分比、不 重复进度卡片,也不改变中止语义。
  • 决策 D338 记录为 ADR 0175。参见 03-runtime/01-ipc-protocol.md、 03-runtime/02-agent-runtime.md、04-ux/09-interaction-patterns.md 和 E2E-008c / E2E-094。

2026-09-07 —— 按提供商覆盖 User-Agent(D339) ​

  • 网关和厂商订阅会检查 User-Agent。pi-ai、Anthropic OAuth、OpenCode 和 Codex 各自打上不同的默认值,Codex 还会在额外头部之后覆盖 User-Agent。schema 列出 了 headers,却从未存储或发送它们。
  • 决策 D339:每个 AI 服务和 OAuth 行可以存储可选的 config_json.userAgent。 为空时保留适配器默认值。非空值通过 fetch 包装加流选项头部,成为该行出站 HTTP 上最后写入的 User-Agent。仅在高级 UI 中提供;首次 OAuth 登录不收集它。 matches() 包含 userAgent。
  • 参见 ADR 0176、03-runtime/12-provider-config-schema.md 和 E2E-005G。

2026-09-08 —— 按提供商自定义 HTTP 头(D341) ​

  • 网关需要的不只是 User-Agent。ADR 0176 留下了未实现的 schema headers 映射, 并使用一个稀疏的单字段高级编辑器。
  • 决策 D341:每个 AI 服务和 OAuth 行可以存储可选的 config_json.headers。 为空时保留适配器默认值。非空映射通过 fetch 包装加流选项头部,在该行出站 HTTP 上最后写入。遗留的 userAgent 迁移到 headers["User-Agent"]。保留的 认证键和逐跳键被拒绝。高级 UI 是一个紧凑的键 / 值编辑器。首次 OAuth 登录 不收集头部。matches() 包含该映射。
  • 参见 ADR 0178、03-runtime/12-provider-config-schema.md 和 E2E-005G。

2026-09-08 —— 用户可配置的出站代理(D340) ​

  • 模型请求、市场下载、更新和内置浏览器各自走不同 HTTP 栈,都没有产品设置。sidecar 里的 Node fetch 不使用系统代理。
  • 设置 → 常规 → 网络增加系统 / 直连 / 自定义。自定义是一个 HTTP 或 SOCKS5 URL 加上回环绕过。Chromium 会话、main net.fetch、sidecar undici 与 host-core 市场 curl 共用它。工作区 Bash 不继承代理凭据。
  • 决策 D340 记录为 ADR 0177。见 04-ux/06-settings-ia.md、03-runtime/07-process-model.md 与 E2E-190。

2026-09-08 —— 从本地智能体存储导入模型配置(D342) ​

  • 设置 → 导入已经能扫描会话。同一批工具还把提供商地址、模型 id 和 API 密钥写在本机配置里,用户否则要在模型页重填。
  • 增加独立的模型配置卡片:显式扫描 Claude Code / Codex / OpenCode / Pi / CC Switch,密钥留在主进程扫描缓存,通过 providers.create 写入。OAuth/订阅令牌不复制。仅跳过相同归一化端点、API 风格和凭据的等价提供商;同一端点的不同凭据保持独立。D007 的禁止自动导入仍然有效(ADR 0188)。
  • 决策 D342 记录为 ADR 0179。见 04-ux/06-settings-ia.md、04-ux/08-component-spec.md §18.5 与 E2E-192。

2026-09-08 —— 模型配置导入保留不同凭据(D351) ​

  • CC Switch 可以在同一个网关端点保存多个命名配置档案。仅按端点去重会 创建第一个记录,并静默跳过使用不同 API 密钥的其余档案。
  • 导入等价性现在同时比较归一化端点、API 风格和凭据。同一端点的不同密钥 创建独立提供商,并继续出现在 Composer 的模型菜单中;未变化的凭据仍会 幂等跳过。
  • Electron main 通过宿主密钥边界解析已有 API 密钥用于比较。原始值不会进入 渲染器、日志或提供商元数据。
  • 决策 D351 修订 D342 和 ADR 0179,记录为 ADR 0188,并由 E2E-192 覆盖。 不改变主机协议或存储 schema。

2026-09-08 —— 主进程拥有的选择器能力(D344) ​

  • 输入框的文件选择器此前把原生绝对路径返回给渲染器,渲染器随后可以通过 composer/importFiles 重放任意路径;它还宣称支持文件夹,而导入器拒绝目录。
  • 选择器 IPC 现在把用户选中的路径存在 Electron main 里,放在一个短期、绑定 发送方、一次性的令牌之后。导入只接受该令牌和持久化的会话 id,MVP 选择器 只提供常规文件。
  • 决策 D344 记录为 ADR 0181。参见 03-runtime/01-ipc-protocol.md、 04-ux/08-component-spec.md §11.7–11.8 和 E2E-102h。

2026-09-08 —— 自定义全局文字缩放(D343) ​

  • --text-* 阶是一组相对尺寸。窗口缩放会把布局一起放大,不是文字偏好。只改阅读区的 px 会让同一窗口出现两套字号。
  • 设置 → 常规 → 外观增加字体大小:杯型档位中杯 / 大杯 / 超大杯 / 超超大杯,加上 80%–150% 滑杆,持久化为可选 AppSettings.fontScale(1 = 产品字号阶)。渲染器设置 --font-scale,全部 --text-* 台阶和共享 Lucide 图标按比例缩放。
  • 决策 D343 记录为 ADR 0180。见 04-ux/06-settings-ia.md、04-ux/07-ui-design-system.md 与 E2E-193。

2026-09-08 —— P0 国际外壳语言(D346) ​

  • 发布完整的德语、西班牙语和法语外壳目录、可搜索的本地语言名、区域到基础语言 的解析,以及匹配的应用内变更日志目录。AppSettings.language 扩展 de、es 和 fr,不改动主机协议、存储架构或 IPC 版本。
  • 决策 D346 记录为 ADR 0183。参见 04-ux/02-i18n-english-first.md、 04-ux/06-settings-ia.md 和 E2E-091。

2026-09-08 —— 输入框工具栏中的上下文用量检查器(D347) ​

  • 紧凑上下文检查器原先挂在最新助手回合下方,会话一滚动就够不着。协作者同意把唯一入口移到模型选择器旁,而不是两处显示同一份数字。
  • 决策 D347:检查器放在输入框右侧工具栏、模型 × 推理芯片左侧,始终对应当前最新一条已报告用量的助手回合。触发器是圆环加百分比。弹层标题为剩余 tokens + 百分比;内部分隔线仍然禁止(D297)。答案下方的助理元只保留模型徽章。
  • 见 ADR 0184、04-ux/08-component-spec.md §8.3 / §11.3 与 E2E-060d。

2026-09-08 —— 按焦点区分本机通知投递(D350) ​

  • PR #84 为 asktool、工具权限和 Plan 审批询问增加本机通知时,扩大了现有 showNative handler 的范围。这也让聚焦的背景终端完成显示本机横幅,违反了 E2E-065。
  • 决策 D350:应用拥有的终端调用使用 kind: "task",仍然只在未聚焦时投递。 交互询问调用使用 kind: "interactive",仅在确切询问会话已在聚焦窗口中可见时 抑制,因此聚焦其他会话时可以通知。省略或未知值默认为 task;交互询问仍在 持久任务收件箱之外,插件本机通知继续使用独立 API。
  • 决策 D350 记录为 ADR 0187。见 03-runtime/01-ipc-protocol.md、 04-ux/08-component-spec.md、04-ux/09-interaction-patterns.md 与 E2E-065a。

2026-09-08 —— macOS 侧边栏改用 source-list vibrancy 材质(D348) ​

  • 洗灰侧栏在 深色应用主题 + 深色系统外观 的 macOS 26 上也能复现。只同步 nativeTheme.themeSource 能修深色应用 + 浅色系统;当 Liquid Glass 在深色外观 下仍画出浅色底板时,压不住 D304 的 under-window。所以材质改为 sidebar。
  • 决策 D348:主窗口改用 Electron vibrancy: "sidebar"。 nativeTheme.themeSource 跟随应用主题偏好(system / light / dark / 插件 base),让原生菜单和毛玻璃底板与渲染器一致。该赋值是进程级的,因此 Windows/Linux 创建窗口时的 shouldUseDarkColors 也会跟随应用偏好,与 windowSetBackgroundColor 一致。plugin: 主题消失时重新 apply。只有 themeSource 真正变化时才 setVibrancy。薄的 --ds-sidebar-glass-tint 配方不变。修正 D304。
  • macos-sidebar-vibrancy.test.mjs 断言 sidebar 材质、偏好映射、设置/ pluginChanged 路径,以及 darwin 存活窗口守卫。见 04-ux/08-component-spec.md §1.7 与 US-UI-74 / E2E-076。

2026-09-08 —— 会话标题摘要与按焦点区分的原生任务通知(D349/D350) ​

  • 首条提示会立即显示规范化的 48 字符回退标题。首轮完成后,Electron 根据会话的有效提供商/模型运行关闭推理的 session/summarizeTitle 单次请求;渲染器通过 session.rename 持久化成功结果。
  • 渲染器本地会话元数据持久化 manualTitle。自动标题会跳过该标记,也会跳过既不是已知默认标题、也不是首条提示回退标题的持久化标题,因此手动标题和已有摘要在重启后不会被覆盖。
  • 原生任务完成通知和交互式提示共用 Electron IPC 入口,但使用明确的 kind。主窗口可见且聚焦时(包括聚焦在后台会话时)抑制任务横幅;交互式提示仍只抑制当前可见会话,因此聚焦的后台请求仍会提醒。持久任务插入仍遵循 D117,插件通知保持独立。
  • 见 ADR 0186 / ADR 0187、03-runtime/01-ipc-protocol.md、03-runtime/02-agent-runtime.md、04-ux/08-component-spec.md 与 E2E-021a / E2E-065。

2026-09-08 —— 父级终态错误中止残留委托(D352) ​

  • D328 让父级空闲后残留委托继续跑,好让长任务收尾。父级 429 耗尽后持久回合已经以 error 结束并显示“继续”,另一个模型上的子智能体却仍把 sidecar 标成忙碌。
  • 决策 D352 修订 D328 / ADR 0166。父级空闲行为不变。父级终态错误会中止残留委托、跳过续跑提示,并把会话恢复为空闲,这样“继续”会被接受。失败的 TurnOutcomeCard 不会被随后的 agent_end 盖成已完成。
  • 见 ADR 0189、03-runtime/02-agent-runtime.md §5f 与 E2E-155。

2026-09-09 —— 区分 Intel macOS 发布工件(D353) ​

  • 原生 macOS 通道可能生成只有版本号的通用安装包名称,用户容易把 Intel 下载误认为 arm64 下载。
  • 决策 D353 修订 D285 / ADR 0145:Intel x64 通道向 electron-builder 传入 目标专用命名模板,发布 PI-Desktop-<version>-Intel.dmg 和 PI-Desktop-<version>-Intel-mac.zip。x64 更新源保留这些 URL 和校验和, arm64 继续使用通用 macOS 名称。
  • 这只改变发布工件命名;更新器归属、签名和通知加链接交付模式保持不变。 见 E2E-092。

2026-09-09 —— 明确标注两个 macOS 发布架构(D354) ​

  • D353 让 x64 工件使用 -Intel 后缀,却让 arm64 工件继续使用只有版本号的 通用名称,因此 PI-Desktop-0.14.4.dmg 仍无法标明架构。
  • 决策 D354 修订 D353 / ADR 0145:两个本机通道都传入目标专用命名模板,发布带 -arm64 或 -x64 后缀的 DMG/ZIP。按架构的更新源保留最终 URL 和校验和。
  • 这只改变发布工件命名;更新器归属、签名和通知加链接交付模式保持不变。 见 E2E-092。

2026-09-09 —— 上下文检查器按最后一次请求计算占用(D355) ​

  • 输入框检查器把最后一条消息的占用和视觉回合加总混在一起,工具循环之后 缓存读取和本轮合计会大于上下文窗口。
  • 决策 D355 修订 D103 / D184 / D244 / D347 / ADR 0047 / ADR 0103 / ADR 0184: 占用、剩余容量、已用/窗口计数、本轮合计,以及模型缓存/输入/输出/推理/ 命中率,都取最后一条已报告用量的助手消息。占用为 input + output + reasoning + cacheRead + cacheWrite。速度和聚合工具行 仍描述该视觉回合。
  • 仅渲染器改动。宿主已完成回合汇总和 Token Insights 仍做账单累加。 参见 ADR 0193 与 E2E-060d。

2026-09-09 —— 可选的子智能体思考覆盖与可读的已选等级(D356) ​

  • 子智能体编辑器现在提供「继承会话」、「不发送」和七个规范思考等级。「不发送」 存储为 thinkingLevel: omit。
  • omit 让智能体的记账状态保持 off,但使用底层 provider 流,因此不会合成 任何思考覆盖。现有的继承和显式 off 仍然是不同的。
  • 模型配置里的思考芯片在两种主题下都使用强调色 / 反色文字的选中态,让已启用 的等级一目了然。不需要改动存储架构或协议版本。参见 ADR 0194 和 E2E-203。

2026-09-09 —— 固定在视口的工作面板切换按钮(D357) ​

  • 决策 D357 修订 D128 / D207 / D221 以及 ADR 0068 / ADR 0085。AppShell 在每个 非设置路由的右上角拥有一个固定在视口的切换按钮。它是 Cmd/Ctrl + J 的指针 等价物:揭示当前会话保留的上下文而不创建标签页,折叠可见面板而不删除标签页。 没有会话时它被禁用。
  • 该切换按钮是唯一的面板级折叠控件。工作面板头部只保留资源关闭。Windows / Linux 的窗口控件仍固定在窗口右边缘;面板打开时,头部预留该带加切换按钮, 以便关闭标签页仍可触及。
  • 没有主机协议、IPC 通道或原生应用菜单命令。Artifact 触发器和会话作用域的 上下文不变。参见 ADR 0195 和 E2E-056 / E2E-070 / US-UI-57。

2026-09-09 —— Windows 免安装便携版 exe(D364) ​

  • Windows 标签发布此前只提供 PI-Desktop-Setup-<version>.exe,公司环境若只能 白名单单个可执行文件或禁止安装程序,就无法运行。
  • 决策 D364 修订 D120 / D126 / ADR 0022:Windows x64 通道在 NSIS 安装程序之外 发布 PI-Desktop-Portable-<version>.exe。便携版目标不写入 latest.yml。 已打包的便携版运行(PORTABLE_EXECUTABLE_FILE)使用通知加链接交付;NSIS 仍走应用内下载并在退出时安装。数据仍在现有应用数据目录。便携版请求 user 执行级别。
  • 参见 ADR 0197 与 E2E-211。

2026-09-22 —— 信任用户自己填写的网络端点(D604) ​

  • 用户自己填写的端点——模型 base URL、MCP 服务器、市场源、git 远端、 生成图片 URL——可以解析到回环、RFC1918、CGNAT、link-local、ULA、 site-local 或 .local,也可以使用明文 http。这一切由一个主机侧 开关决定:settings.networkPolicy.mode(relaxed | strict),默认 relaxed;它取代了此前的三个确认开关(networkProxy.allowFakeIp、 configSync.allowInsecureHttp、自填明文开关),旧的 allowInsecureUserEndpoints: false 迁移为 strict。首次明文访问会弹一次 告知。https 访问局域网主机不需要开关。
  • 第三方内容不变。registry 记录、市场目录正文、目录内部的文档 URL、 以及每一次 HTTP 重定向目标,仍然只允许公网,并把校验过的地址固定到 连接上。共享客户端按每一跳的来源判定,只有用户发起请求的首跳可以是 user。
  • 云元数据(169.254.169.254、100.100.100.200、fd00:ec2::254、 metadata.google.internal、metadata、instance-data)、unspecified、 multicast、reserved、documentation 地址在任何输入上一律拒绝,包括用户 自己填写的字段。
  • 默认代理绕过列表增加私网段,配置了代理之后不再吞掉本机模型服务、NAS 或 MCP 端点。见 05-security/01-security.md §4.1/§4.2、 04-ux/06-settings-ia.md 与 ADR 0304。

2026-09-22 —— Windows 便携交付改为普通 ZIP(D603) ​

  • 决策 D603 修订 D364 / ADR 0022 / ADR 0197。Windows x64 通道现在在 NSIS 安装程序 旁发布 PI-Desktop-Portable-<version>.zip。发布脚本分别构建两个目标,并给 ZIP 应用元数据写入 piDistribution = "zip";ZIP 目标不写入 latest.yml。
  • 用户解压 ZIP 后直接启动 PI-Desktop.exe。ZIP 运行使用通知加链接更新交付,更新器 仍识别旧便携版 exe 的 PORTABLE_EXECUTABLE_FILE。现有数据目录和 NSIS 应用内更新 通道保持不变。
  • 参见 ADR 0197 与 E2E-211。

2026-09-09 —— 为每个安静间隔命名活动行(D365) ​

  • ADR 0175 只命名了 waiting-model、retrying 和 waiting-subagents。 压缩上下文、空回复补救、工具结束后到下一次请求之间的空隙,以及 starting,仍然看起来像卡住的泛化等待。父级等待委托时也不说明 那些 Subagent 正在做什么。
  • 决策 D365 修订 D338 / ADR 0175:AgentActivity 增加 preparing、 compacting、recovering;starting 用自己的标签展示; waiting-subagents 带上仍在运行目标的实时快照(name、lastPhase、 lastToolName),只在子级工具/思考变化时更新,而不是每个 token。 该行仍是一条紧凑的内联状态,不恢复活动组上的状态胶囊。
  • 参见 ADR 0198、03-runtime/01-ipc-protocol.md、 03-runtime/02-agent-runtime.md、04-ux/09-interaction-patterns.md, 以及 E2E-008c / E2E-094。

D366 未分配。它先被用于插件会话导入决策,该决策在与 main 同步时重编号为 D367 / ADR 0200;随后又被用于"仅在发版时部署文档站"决策(配 ADR 0199),该决策已于 2026-09-09 回退。D366 与 ADR 0199 均已退役,不要复用。D376 同样未分配:main 将其短暂承载的远程控制修订重编号为 D374,并把 D375 分配给了发布排序决策,因此 D376 也保持退役。

2026-09-09 —— 宿主拥有的插件会话导入与归属 API(D367) ​

  • 外部历史迁移插件需要持久的导入/读取/更新/删除能力,但现有的进行中 session.getLlmContext 和核心 session.import 并不是安全的插件归属边界。
  • 决定 D367 / ADR 0200 增加 P0/P1 pi.session 方法:import、 importBatch、list、get、listMessages、rename 和 delete。每个来源 必须由 contributes.sessionSources 声明。主机生成 id,并按 (pluginId, source, externalId) 归属限制所有操作;导入不会激活项目、provider 或 model 绑定。
  • Schema v14 增加 session_import_origins 和 sessions.deleted_at;协议仍为 v11。Trash 为归属插件保留转录本以便 purge。P2/P3 创建、消息变更、绑定、 批量删除和标签 API 继续延期。参见 ADR 0200 和 E2E-214/E2E-215。

2026-09-09 —— 显式插件项目 id 与宿主拥有的会话刷新(D368) ​

  • 导入会话需要显式归属持久化项目,但向插件暴露 workspace.set 还会改变用户当前工作区;插件写入也需要由宿主拥有的渲染器刷新路径。
  • 决策 D368 / ADR 0201 增加受权限保护的 pi.project.create({ path })。它创建或复用项目并返回宿主生成的 id,但不会激活工作区。导入项可以提供已有的 projectId;省略时仍保持未绑定,历史来源路径不会变成工具根目录。list/get 投影会报告显式绑定。
  • Electron 主进程在插件导入、重命名和删除成功后发送 pi-desktop/session/event/changed;渲染器复用 refreshSessions(),跳过的导入不发送事件,已关闭的项目标签页不会被重新打开。参见 E2E-216。

2026-09-09 —— 子智能体的有效思考元数据(D369) ​

  • 委托卡片需要的是实际传给每个子运行的思考等级,而不是在目标模型钳制之后 从父级或定义重新推导出来的值。
  • 决策 D369 / ADR 0202 把有效的 modelId 和 thinkingLevel 加进 Task 的即时 结果、SubagentRunResult 和生命周期快照。拓扑节点和侧边停靠栏头部在模型名 之后显示原始规范的非 off 等级;off、omit 和不支持的推理仍只显示模型。 不改动主机协议、存储架构、provider 请求或生命周期行为。D395 / ADR 0221 移除了规范值的翻译。参见 E2E-219。

2026-09-09 —— 未签名 macOS 首次启动助手(D371;由 D406 修订) ​

  • 默认 macOS 通道仍保持未签名;下载的可信工件仍可能因为 Apple 的 quarantine 检查而被阻止,并显示容易误解的“应用已损坏”。原有的 xattr -cr 说明只能 在终端执行,而且清理范围超过了启动失败所需的范围。
  • 决策 D371 / ADR 0204 为每个 macOS 分发包加入可执行的 PI-Desktop-macOS-open.command。DMG 会把它放在“拖入 Applications”手势下方 的可见首次启动区域。
  • 助手只查找 /Applications/PI-Desktop.app 和 ~/Applications/PI-Desktop.app,并先校验 CFBundleIdentifier 为 com.pi-desktop.app;在存在时递归删除唯一的 com.apple.quarantine 属性, 然后打开 PI-Desktop。它不使用 sudo,不接受任意路径,也不替代 Developer ID 签名或公证。打开说明保留可信来源警告和更窄范围的终端备用命令。参见 E2E-196b。

2026-09-09 —— 桌面操作的本地 MCP 控制面(D370) ​

  • 外部 Agent 需要驱动运行中的桌面完成项目、会话、Agent、工作区和已审查的应用操作, 同时延后的远程 Gateway / WebUI 边界保持不变。
  • 决策 D370 / ADR 0203 在 Electron Main 内增加默认关闭的 Streamable HTTP MCP 服务。 服务只绑定 127.0.0.1,使用持久化随机 bearer token,校验回环 Origin,写入受权限保护 的连接清单,并委托给与渲染器相同的已注册 IPC 处理器。
  • 命名工具覆盖常见项目/会话/Agent 流程;pi_desktop_invoke 访问显式的风险标记操作目录。 排除密钥通道和渲染器专属原生选择器,危险操作要求 confirm: true。成功的外部变更复用 现有渲染器会话变更事件,使可见状态跟随控制调用。
  • 端点仅限本地并在启动失败时降级,不创建远程认证、云同步或第二套权限实现。参见 02-architecture/01-architecture.md、03-runtime/01-ipc-protocol.md、 05-security/01-security.md 和 E2E-220。

2026-09-10 —— 收紧本地 MCP 控制面边界(D372) ​

  • D370 的第一版目录仍暴露了原生选择器(plugin/loadDev)、provider/OAuth/MCP 密钥写入路径,以及无需确认的 session/configure,并把会话读取当成渲染器变更。
  • 决策 D372 修订 ADR 0203:第一版目录改为项目/会话/Agent/工作区流程加上已审查的 只读操作;剥离密钥形态参数;session/configure 视为危险;监听地址必须是回环; 协议版本协商而不是回显;包括 structuredContent 在内的结果有界;渲染器会话刷新 仅限变更操作。参见 E2E-220。

2026-09-10 —— 远程 Agent Control 使用专用 Host 边界(D373) ​

  • 远程控制属于 MVP 后能力,不得暴露现有 Electron IPC、host.proxy 或 Rust host-core stdio 边界。
  • 决策 D373 / ADR 0205 定义逻辑 Agent Host,由它拥有 Sessions、Turns、事件游标、 审批、附件、工作区策略和崩溃恢复。生产 Gateway 拥有身份、路由、速率限制、撤销和 审计;Agent Host 通过出站链路连接。
  • RACP v1 保持语义操作模型与传输解耦:WebSocket JSON-RPC 是主要交互绑定, HTTP/JSON + SSE 是浏览器绑定,TLS 上的 gRPC 是原生/Gateway 绑定。各绑定共享事件 序列、快照、幂等、授权和错误语义。参见远程架构、协议、安全、发布规格以及 E2E-221 至 E2E-230。

2026-09-10 —— 在实现前修订远程 Agent Control 目标(D374) ​

  • 将 D373 草案与已上线的桌面对照评审后发现:远程审批词汇比本地的 allow-once / allow-session / deny 和 Plan/Goal 权限模式契约更窄;待处理的 权限请求是连接状态而不是 Host 状态;逐 token 的 delta 会耗尽回放窗口;三个必需 绑定加两套 IDL 超出 v1 的承受范围;浏览器无法在 WebSocket 和 SSE 上满足仅头部认证。
  • 决策 D374 修订 ADR 0205:RACP-WS 是 v1 唯一规范绑定,RACP-HTTP 是浏览器 profile,RACP-GRPC 保留;packages/shared 中的 typebox 是唯一契约来源;首个交付物 是无 Electron 依赖的 packages/agent-host 模块,桌面 IPC、本地 MCP 和 RACP 都调用它; 游标为 { epoch, sequence },delta 为瞬态事件,首个日志放在内存;回合队列移入 Host; host-core 暴露 permissions.pending;远程审批携带完整本地决策词汇;远程权限上限默认 ask;审批寿命成为面向远程订阅者的有界 Host 策略;Host link 是中继 profile;浏览器 客户端使用 cookie profile;Gateway 身份源在 R3 时决定;首个部署为单租户。参见修订后的 远程架构、协议、安全、发布规格以及 E2E-221 至 E2E-230。

2026-09-10 —— 按已记录的需求排序远程控制(D375) ​

  • #176 与 #140 要求从本地桌面操作远程 Linux 或 WSL 机器上的项目;#100 要求把任务与 审批通知推送到 Telegram、微信、Slack 或 Webhook 并能回传简单指令。没有任何已记录的 请求要求浏览器或手机端控制桌面,而维护者已公开承诺 #100。
  • 决策 D375 第二次修订 ADR 0205:rollout R2 改为 SSH 隧道远端 Host,pi-host 包由 经用户自己的 SSH 会话上传的引导脚本从 GitHub Releases 下载并校验 checksum,只绑定 loopback,与桌面配对,经端口转发以 RACP-WS header profile 连接,并由不变的 renderer 通过 lib/api.ts 下的桌面 RACP 客户端适配层渲染。RACP 新增远端 Host profile(session/configure、session/fork、session/rename、session/delete、 session/compact、workspace/list、workspace/read、workspace/diff、 terminal/*)和同一里程碑内的反向工具中继(tools/advertise、tool/execute), R2 不拆分。远程会话存在于其 Host 上;provider 配置经 SSH 写入;SSH 配对的桌面设备 豁免远程权限上限,除非设置 applyCeilingToPairedDevices;排队回合由 host-core 持久化并在重启后挂起;远程审批寿命默认 30 分钟。R3 改为出站消息集成,Webhook 优先。 Gateway、浏览器 profile 和 gRPC 绑定保留规格但不排期,Gateway 身份源定为 pi-backend 规格中的 PI 账号服务。参见修订后的远程规格以及 E2E-231 / E2E-232。

2026-09-10 —— 远程控制保持用户本地化:不引入第一方身份(D385) ​

  • 维护者要求用户的客户端绝不经由项目方运营的服务认证,远程控制必须完全留在用户 自己的机器与基础设施上。
  • 决策 D385 第三次修订 ADR 0205 并撤回 D375 第 10 条:链路中不存在任何项目方运营的 身份、账号或中继服务;客户端持有的唯一凭据是用户自己的 Host 在配对时签发的设备 token;Gateway 若日后排期,由用户自托管并以这些 Host 签发的凭据准入;OIDC 联合与 pi-backend 账号服务不在远程控制范围内。Host 的出站连接只有用户的 SSH 主机、用户 自己的消息渠道、用户配置的模型 provider,以及只读的 GitHub Releases pi-host 下载。SSH 隧道拓扑、设备配对和消息集成本已满足该规则。

2026-09-10 —— 在 host-core 中持久化 Host 拥有的回合队列(D386) ​

  • D375 把排队的 prompt 移入 Host 并选择持久化;无头 Agent Host 模块需要一个能在重启后 存活且绝不自行启动工作的存储,而 host-core 独占 SQLite。
  • 决策 D386 / ADR 0213 增加 schema v15 的 turn_queue 表以及增量的 session.queuePush / session.queueList / session.queueRemove / session.queuePrioritize 方法。push 按主体与 幂等 key 幂等,每会话最多八条,随会话删除级联。模块在 host-core 就绪后恢复条目,把每个 恢复的会话挂起到 controller 接入,并且只在活动回合终止事件之后释放一条。协议保持 v11; renderer 的内存队列已退役,其“立即发送”成为 RACP turn/prioritize 加优雅停止。

2026-09-10 — 启动时明确指出降级安装与非原生构建(D380) ​

  • 在被 0.14.6-rc.4 迁移到 schema 14 的数据目录上安装 0.14.5,会静默重启 host 三次并显示“无法连接本地服务”;只有 logs/host/runtime.log 写着 database schema version 14 is newer than supported 13。Intel macOS 构建在 Apple Silicon 上同样通过 Rosetta 运行而没有任何提示。
  • 决策 D380 扩展 Linux glibc 守卫模式(E2E-195):Electron 从最后一段 stderr 解析 host-core 的 schema 拒绝信息,首次失败即停止监管,并推送带 DB_SCHEMA_TOO_NEW 和两个 schema 版本号的 hostStatus,横幅据此说明应安装 哪个版本。启动时还会检测非原生构建(macOS 用 sysctl.proc_translated,其他 平台用 os.machine()),并在启动状态上附带 archMismatch;渲染层显示可关闭 的提示,标注 Intel / Apple Silicon。不新增错误码:两者都沿用 HOST_UNAVAILABLE,复用 message 的状态令牌通道。不尝试向下迁移。见 E2E-239 / E2E-240。

2026-09-10 — 同一路径允许三次突变恢复失败(D379) ​

  • 之前的重复保护会在同一个 Edit 路径或已识别的 shell 修补键累计两次失败后结束提示。 这个边界可能会在模型仍在应用上一次失败返回的不同恢复提示时过早停止。
  • 决策 D379 修订 D186 与 ADR 0087 / ADR 0207:同一路径的突变保护器在每个提示内允许 累计三次失败。可恢复错误代码仍各有一次宽限,成功突变会清除该路径历史,第 3 次计数 的失败返回 terminate: true 与 MUTATION_RETRY_BUDGET_EXHAUSTED。已识别的 shell 修补命令使用同一上限。不改变 IPC、存储、主机协议或工具结果结构。见 E2E-140 / E2E-141。

2026-09-10 —— 插件桌面控制需要原生用户同意(D377) ​

  • 一次代码与文档评审发现:持有 desktop.control 的插件可以在没有人参与的情况下 运行 dangerous 目录操作(删除会话、更改权限模式、批准工具):共享控制器的 confirm: true 由插件自己设置,唯一的确认只是插件选择绘制的任何卡片。同一次评审 还发现 Electron 的 fetch 服务允许白名单中的主机把插件请求重定向到未声明的主机, manifest.main 与 ui.panel 跳过了路径包含检查,而规格 15 的安全拒绝名单与忽略 层从未在 host-core 中实现。
  • 决策 D377(ADR 0208):来自插件的 dangerous 桌面操作需要插件给出 confirm: true,然后由用户在宿主拥有的原生对话框中作答;该对话框只写明目录中的 操作、描述和有界的参数预览;拒绝与无界面的宿主都以 PERMISSION_DENIED 失败。 MCP 契约(D372)不变。
  • 同一变更中不新增决策的部分:pi.net.fetch 只有一条路径,即在每次重定向前重新 检查 net.domains 的运行时跳转循环;manifest.main 与 ui.panel 在插件内部 校验并解析;host-core 对未限定范围的遍历强制执行规格 15 的安全拒绝名单 (WORKSPACE_PATH_DENIED)、应用默认值、.pi-desktopignore 和 <data_dir>/ignore;路径解析器会跟随悬空符号链接,因此经由它的写入无法离开工作区; 对未知会话的 tools.execute 返回 SESSION_NOT_FOUND,而不是继承全局工作区。 参见 E2E-234 至 E2E-238。

2026-09-10 — 将 PowerShell 7 作为 Windows 可选命令 Shell (D381) ​

  • 问题 #151:Windows 用户安装 PowerShell 7(pwsh.exe)后无法让 Bash 在它下面 运行。PowerShell 7 与 ADR 0054 中 windows-powershell 条目所解析的随系统内置 5.1 并排安装,且从不替换它,而目录中没有其它可选条目: crates/host-core/src/tools/shell.rs 没有 pwsh.exe 解析路径, COMMAND_SHELL_IDS 只列出四个 ID。
  • 决策 D381(ADR 0209)在 Windows 目录中新增稳定 ID windows-pwsh ("PowerShell 7"),并修订 ADR 0054 §1。解析顺序为 %ProgramFiles%\PowerShell\7\pwsh.exe(宿主进程为 32 位时追加 %ProgramW6432%),再到 PATH 上的 pwsh.exe;解析失败返回 SHELL_NOT_FOUND 并列出两处已搜索位置。它沿用 powershell 方言与既有的 非交互式脚本,且从不会被隐式选中,因此平台默认值与既有用户的 shell 保持不变。
  • 协议、存储与工具面均为增量改动:CommandShellId 新增一个成员,而折叠的 设置校验与首个可用回退无需新代码路径,也没有新增 RPC 方法、工具名或schema 迁移。 参见 E2E-112。

2026-09-10 —— 已结束的 Subagent 会读回自己失败的原因(D382) ​

  • 以 failed、timed_out 或 aborted 结束的委派,只显示结果胶囊和耗时。原因其实 存在但没有读者:SubagentRunResult.error 挂在委派名册条目上,而拓扑节点及其侧边 栏详情渲染的是 Task 行自身的结果——按 ADR 0089,它在委派启动的那一刻就被固定为 running。因此在发出任何消息行之前就死掉的委派,只会留下一个满是步骤、毫无解释的 面板。
  • 决策 D382:从生命周期行的 details.delegations[] 与 details.stopped[] 收集委派的 error: { code, message },使用与已定状态相同的"后写覆盖"规则;侧边栏详情的结尾 用一张错误卡片收束,复用转录错误卡片的视觉语言与 errors.<code> 词表,代码未登记时 回退到本地化的 chat.subagentStatus.* 结果标签。卡片跟随"非成功终态"而非仅跟随错误 字段,因此已完成或仍在运行的委派永远不会显示它。不改运行时、IPC、存储或工具结果: 运行时早就在上报该错误,这里只是不再丢弃它。参见 04-ux/08-component-spec.md §5.7 与 E2E-198。

2026-09-10 —— 子代理可以声明自己的输出上限(D383) ​

  • 子代理定义此前只能限制轮数,不能限制响应长度。maxTurns 贯穿 frontmatter、 记录、输入、编辑器草稿与运行时,而委托唯一拥有的输出上限来自其模型绑定: packages/agent-runtime/src/model-capabilities.ts 中的 ModelBinding.maxTokens 会限制该模型的所有调用方,因此为一个话多的委托调高它,会话自身也会被一起调高。 SubagentDefinition、SubagentDraft 与 UserSubagentRecord 都没有 maxTokens, SubagentRun 也从不传入。
  • 决策 D383(ADR 0210):定义可以声明 maxTokens,按与 maxTurns 相同的宽松键名 解析,并以 200000 为天花板。省略、none 或 0 表示跟随模型已发布的上限; 超出 1–200000 的值视为笔误,会被报告并忽略或钳制,而不会转发。声明的值会覆盖委托 为自己构建的模型上的 maxTokens,因此适配器派生出的 max_tokens / max_completion_tokens / max_output_tokens 都会带上它,而会话自身的请求仍沿用 模型绑定。它存储在能力文档的 frontmatter 中,并在编辑器既有 Advanced 折叠区的 轮次上限旁编辑。不新增 IPC、工具结果或存储结构:该字段沿用既有的子代理记录与输入。 见 E2E-119 / E2E-155。

2026-09-10 — 移除诊断性 timing 日志流(D385) ​

  • 移除 D137/D183 引入的仅用于 timing 的进程日志流:sidecar [timing] 记录、 host tool timing 记录、启动阶段 timing、更新器 timing、独立的 timing 类别 以及 PI_DESKTOP_TIMING 不再输出。
  • 保留关键的生命周期、状态变更、权限、工具、插件、provider、持久化、更新器和错误记录。 既有审计字段以及 UI/协议中的时长元数据保持不变,因为它们用于安全取证、成绩单展示和 有界的 provider 诊断,而不是用于生成冗余进程日志。
  • 既有 timing 文件属于历史本地数据,不会自动删除或迁移。

2026-09-11 —— Agent 扩展成为插件贡献点(D388) ​

  • D387 把 ExtensionAPI 模块作为第二个扩展面交付,带有自己的注册表、设置标签和启用 存储。维护者要求只有一个扩展面:pi CLI 扩展应当成为 PI-Desktop 插件,像插件一样 安装、启用、限定范围和展示。
  • 决策 D388 / ADR 0215 修订 D387:模块以 contributes.agentExtensions 声明,由新增的 高风险权限 agent.extension 门控;没有该权限的 manifest 无效,记录的授权中缺少它的 插件照常加载但跳过模块并记审计。启用与项目范围归插件所有。“导入 pi 扩展”把 pi CLI 扩展复制到 <dataDir>/plugins/imported/<slug> 并生成 manifest,注册为开发插件; 选择器之前的确认即信任决定。独立注册表、其存储文件、设置目的地以及 list / enable / rescan / add-path / remove 通道全部移除;插件行显示 agentExtension 能力、权限、加载 状态、已注册名称和诊断。sidecar 的 loader、Runner、hooks、命令与提示桥接不变。持有 agent.extension 的插件的市场分发等签名到位。规格 16 §1 至 §3、§10.2、§11; E2E-241 至 E2E-245 已在插件形态夹具上重新执行。

2026-09-10 —— 聚合端点上回放 DeepSeek 的 reasoning_content(D389) ​

  • DeepSeek 思考模式要求回放的每条 assistant 消息都带 reasoning_content。当轮没有思考内容时仍需该字段为空串 "";缺字段会返回 HTTP 400。
  • pi-ai 只在 model.provider === "deepseek" 或 Base URL 含 deepseek.com 时自动打开该开关。PI-Desktop 把 UUID 存成 model.provider,因此硅基流动、火山方舟、自定义中转和其他聚合网关永远匹配不上。
  • 决策 D389 修订 D024:vendorKey、URL、模型 ID 或目录 family 能识别为 DeepSeek 的 Completions 行设置 requiresReasoningContentOnAssistantMessages: true。该匹配不改 thinkingFormat,因此 OpenRouter 等聚合器保持原有思考线路。见 E2E-005E 与 03-runtime/11-provider-model-system.md。

2026-09-10 —— 重新生成在主机 RPC 锁下截断 ​

  • 重试/重新生成曾把整份 kept transcript 经 session.replaceMessages 送进一条 NDJSON,超大会话会在 130 秒超时或 64 MiB stdin 上限处失败(issue #211)。
  • 现在由 session.truncateFrom 在主机锁内完成截断、中止残留 running 回合并归档被丢弃的尾巴。agent/prompt 只为启动配置做有界 session.get。
  • 超长控制管道行以 LIMIT_EXCEEDED 应答,不再结束 stdin 读取器。
  • 决策 D390 与 ADR 0216。见 E2E-246。

2026-09-11 —— 主机 stdout 发送端不得比 serve 更长寿 ​

  • ADR 0216 让重试不再整包传输 transcript,超长行也不再结束 stdin 读取器。在 Windows 上这还不够:Alt+Space 钩子持有 stdout 发送端的强引用,stdin 结束后写线程不退出,host-core 变成僵尸,Electron 报 host RPC timeout: session.replaceMessages(issue #211)。
  • 钩子改为只保留弱引用。serve 最多等写线程 5 秒。Electron 在写入前拒绝超过 64 MiB 的请求行。主机侧 LIMIT_EXCEEDED 从截断前缀里取出 JSON-RPC id。
  • 决策 D391 与 ADR 0217。见 03-runtime/07-process-model.md、03-runtime/06-host-rpc-protocol.md §7 与 E2E-247。

2026-09-11 —— Composer 与传输使用生效的图像输入覆盖(D392) ​

  • 高级模型设置按 provider/model binding 保存 supportsImages 三态值。缺失或 null 跟随已发布模型记录;true 或 false 为该端点显式启用或禁用图像输入。
  • Composer 模型行、附件状态和 Electron main 传输共同使用 binding 的生效能力。 不修改共享的已发布 ModelInfo;没有显式覆盖的未知/自定义模型仍保持保守行为。
  • 决策 D392 修订 D243 / ADR 0101。见 ADR 0218、 03-runtime/13-model-catalog-and-selection.md §11.2、 04-ux/08-component-spec.md §11.7–11.8 与 E2E-163。

2026-09-11 —— Composer 中用户调用的 Skills(D393) ​

  • D174 的模型调用目录仍负责 Skill 正文加载和安全契约,但不提供面向用户的 slash 条目, 使已经知道所需工作流的用户难以发现活跃 Skills。
  • D393 / ADR 0219 将激活的内置、插件和用户 Skills 作为 composer / 菜单末尾的独立分组。 选择后插入精确 id;Electron main 重新验证当前项目范围,并要求模型调用本地 Skill 工具。 输入的命令仍作为 transcript chip 保留,不新增主机协议或持久化 schema。

2026-09-11 —— Windows 工作面板 chrome 保持单一资源操作组(D394) ​

  • 打开的工作面板标题栏只保留一个紧凑资源切换器。资源关闭通过现有统一上下文菜单行完成, 因此 Windows 原生关闭控件旁边不再出现第二个 X。
  • 视口固定的工作面板开关仍是唯一的面板折叠控件。子代理详情使用返回箭头, Windows/Linux 原生控件仍固定在窗口边缘。
  • 决策 D394 修订 D154 / D357 / ADR 0195。这是仅渲染器的变更,不改变面板状态、窗口几何、 IPC、协议或存储。见 ADR 0220 与 E2E-067。

2026-09-11 —— UI 中的规范思考等级值(D395) ​

  • 思考等级是稳定的协议值,但本地化会使同一个 provider/runtime 设置随应用语言变化。
  • 决策 D395 / ADR 0221 修订 D369 / ADR 0202:Composer、模型配置和委派界面直接显示 off、minimal、low、medium、high、xhigh 和 max。这些值从所有语言目录中移除; 有效元数据、钳位、provider 请求、协议和存储保持不变。见 E2E-219。

2026-09-11 —— 渲染器与插件面板滚动条统一为紧凑规则(D396) ​

  • Windows 的经典滚动条让右侧工作面板的 Files 视图明显比对话区更粗,尽管主渲染器 已经使用安静的自定义滚动条。侧边栏还保留了单独的宽度和滑块透明度覆盖,因此应用 实际上存在两套滚动条样式。
  • 决策 D396 修订 D300:渲染器中的每个滚动容器统一使用 6px、无轨道、静止时透明的 滚动条,以及相同的悬停、focus-within、滚动显示和拖动状态。移除侧边栏专用覆盖。 插件面板 preload 给停靠和独立插件文档(包括内置 Files 视图)注入同一规则和 300ms 的滚动显示标记。Browser 内部加载的外部网页仍由网页自己管理样式。
  • 这是仅表现层的变更,不改变协议、存储、主机运行时或外部网页行为。见 04-ux/07-ui-design-system.md、04-ux/08-component-spec.md 与 E2E-157。

2026-09-11 —— 消息引用与渲染器拥有的侧边聊天(D-LOCAL-message-quotes) ​

(已由 ADR 0268 于 2026-09-16 退役:功能与相关界面均已移除,这些 ID 永久退役且不得复用;此处记录仅作历史留存。)

  • 每条用户消息和每个助手回合新增 Quote 操作,与 Copy、Edit、Delete、Fork、Retry 并列。 它把以 > 开头的 Markdown 引用块加一行 chat.quoteSource 归属写入当前会话的 composer 草稿,选区在被点击消息行内时使用选区、否则使用消息自身文本,摘录上限 2000 字符并带省略号,随后聚焦输入框,绝不发送。
  • Open side chat 通过现有 session.fork 以锚点助手消息或用户消息分叉,但不激活子会话, 因此主对话仍保持可见会话。子会话注册为父会话的渲染器侧边聊天,工作面板打开一个 sidechat 标签,经共享的后台转录 reducer 实时流式渲染,并提供 Add to main chat、 Open as a conversation、紧凑 Send/Stop 输入和现有权限卡。
  • 关闭标签或以会话方式打开子会话会移除注册及其转录投影;持久子会话仍是侧边栏、 会话列表和搜索中的普通会话。不改协议、存储 schema、IPC 通道或权限。 见 ADR message-quotes-and-side-chats 与 E2E-CHAT-quote-prefill 至 E2E-CHAT-side-chat-close。

2026-09-11 —— 选区的引用浮层(D-LOCAL-selection-overlay) ​

(已由 ADR 0268 于 2026-09-16 退役:功能与相关界面均已移除,这些 ID 永久退役且不得复用;此处记录仅作历史留存。)

  • 引用不再必须回到答案末尾。消息行内存在非空文本选区时,在选区上方悬浮一个浮层:水平居中于选区,限制在 裁剪祖先(转录滚动容器)的矩形内,并且不越过停靠输入区的顶边;浮层按应用其他 body portal 浮层的方式挂载, 并在对话滚动时跟随选区移动,而不是直接消失。
  • 浮层提供三个动作:「添加到对话」(chat.addToChat)按 D-LOCAL-message-quotes 的引用契约把摘录写入当前会话的 composer 草稿; 「在侧边聊天中提问」(chat.askInSideChat)以该行为锚点分叉子会话,并把摘录作为子会话的提问发送,答案在面板 中流式返回而不进入当前可见对话;复制(复用 chat.copy)把 Markdown 写入剪贴板。每个动作都会先清除原生选区; 跨消息行的拖选不显示浮层;只读投影不渲染浮层。
  • 摘录改为从渲染后的 DOM 还原,而不是 Selection.toString()。与公式相交的选区会扩展到整个公式,并取 KaTeX 的 application/x-tex 注解还原为 $…$ 或 $$…$$;代码块还原为带语言的围栏,围栏长度超过片段内最长的反引号串; 表格每行还原为一条 a | b 行;行内代码保留反引号,文件引用芯片保留其代码文本。消息末尾的 Quote 操作与浮层 共用这一条还原路径。
  • 2000 字符上限、> 引用块、chat.quoteSource 归属、聚焦行为,以及不发送、不建会话、不写转录的边界均未改变。 与参考实现的两处差异是刻意的,因为引用文本会落回本应用渲染的 Markdown 草稿:公式使用 $…$ / $$…$$ (remark-math),表格行使用 a | b。仅渲染器变更;除 composer 的 data-composer-dock 钩子外,不改协议、 存储 schema、IPC 或权限。见 ADR message-quotes-and-side-chats 与 E2E-CHAT-quote-prefill、E2E-CHAT-selection-markdown、E2E-CHAT-selection-side-chat。

2026-09-11 —— 响应批注作为提示词附件(D-LOCAL-response-annotations) ​

(已由 ADR 0268 于 2026-09-16 退役:功能与相关界面均已移除,这些 ID 永久退役且不得复用;此处记录仅作历史留存。)

  • 在助手回合中选中文字并选择「添加到对话」,会为该回合附加一条带编号的批注:id、锚定的回合、摘录(Markdown, 上限 2000 字符)、空的评论字段,以及作为编号的捕获顺序。批注属于渲染器侧的会话状态;重复批注同一摘录不生效, 既不持久化,也不单独发送给宿主。
  • 回答正文保持不变:本应用不向用户阅读的文本中插入任何标记。模型写出的 :codex-annotation{index="N"} 指令 (提示词要求模型为每一条它处理的批注写出该指令)会渲染为小型编号引用,其提示为摘录;标记不参与选区、引用或复制。 composer 显示一个带数量的附件芯片,其提示列出 N. 摘录,并提供一个一键清除控件。芯片不会进入可编辑文本,因此 D209 的智能停止与 D301 的草稿保留均不受影响。
  • 带批注的发送会组合 # Response annotations: + 指令 + 含 [{"text","annotation","source":{"messageId"}}] 的 <response-annotations> + ## My request: + 用户文本,并消费这些批注。可见草稿、乐观行、侧边栏标题和编辑种子都 不含批注内容;已存提示在展示时会还原为请求文本。
  • 引用用户消息以及侧边聊天的 Add to main chat 仍沿用 D-LOCAL-message-quotes 的草稿引用。不改协议、存储 schema、IPC 通道或权限。 见 ADR response-annotations 与 E2E-CHAT-annotation-attachments、E2E-CHAT-annotation-session-state。
  • (2026-09-12 修订。)「添加到对话」以及助手回合的批注操作会打开一个紧凑的评论编辑器, 而不是静默附加:摘录在选区被收起之前先做快照,评论可多行且可选;保存时把评论写入该批注的 annotation(或更新已有批注),取消/Escape 直接丢弃且不发送任何内容。再次批注已附加的摘录会 重新打开该批注的编辑器;composer 的数量芯片会打开批注列表,可逐条编辑评论或移除单条,并保留 原有的一键清除。编辑器归属于打开它的会话;对已发送或已移除批注的保存不会产生任何变更。 不改协议、存储 schema、IPC 通道或权限。
  • (悬浮索引修订,ADR floating-annotation-index。) 用可展开/收起的悬浮批注框替代输入框弹出列表。 原文位置显示相同编号,点击可定位并用独立浮层高亮;不向 Markdown 插入文字。 定位先解除自动跟随,必要时加载历史;同文案的不同位置分别编号。 编号与发送数组顺序一致,定位偏移不发送给模型;发送后仍清空,不新增持久化。见 E2E-CHAT-annotation-source-index。

2026-09-11 —— 上下文用量显示偏好(D398) ​

  • 上下文用量检查器的引导数值——触发器圆弧(strokeDashoffset)、百分比、令牌标签、弹层标题、tooltip 与 aria-label——可通过 AppSettings.contextUsageDisplay("remaining" 或 "used")配置。缺省及非法值回退为 "remaining"。
  • 当设为 "used" 时,圆环按 usedRatio 填充,文字展示已用容量对。
  • 警告与临界颜色阈值(剩余 ≤ 25 % / ≤ 10 %)仍按剩余容量判定,不随显示模式变化。
  • 设置 → 全局 AI → 默认项新增分段控件(剩余 / 已用),位于链接打开目标之后、回车发送之前。
  • 决策 D398 修订 D347 / ADR 0184。见 ADR 0223 与 E2E-250。

2026-09-11 —— 项目组支持手动排序(D399) ​

  • 保留的项目组显示悬停和键盘聚焦时可见的手柄。原生拖放会根据指针位置将源项目插入目标之前或之后;聚焦手柄后按 ArrowUp/ArrowDown 提供键盘操作,按 Esc 可取消拖动。
  • 重排会写入连续的规范化路径 order 值,并将渲染器本地的项目排序切换为 manual。置顶和归档优先级、项目激活、主机工作区身份、会话顺序和磁盘目录均保持不变。
  • 决策 D399 修订 D093。不引入协议、主机、IPC 或持久项目 schema 变更;详见侧边栏交互规格和 E2E-253。

2026-09-11 —— 从有效会话上下文恢复延迟工具(D400) ​

  • 新提示或模式切换会清除内存中的延迟激活集,然后从有效会话上下文恢复 成功的激活证据。ToolSearch 行贡献 addedToolNames;成功的延迟工具行 贡献自身的工具名。
  • 恢复只保留当前模式延迟目录中的名称。错误、已中断和缺少结果的占位行会 被忽略,也不会解析助手/用户文本作为激活证据。主机权限、工作区遏制和 审核行为保持不变。
  • 决策 D400 修订 D185 / ADR 0048。见 ADR 0225 与 E2E-008a。

2026-09-11 —— 长按项目标题重排(D402) ​

  • 保留的项目组不再渲染重排手柄。在项目标题上静止按住 400ms 后开始指针重排;该延迟前移动超过 8px 会取消按住,因此单击仍会选中项目并切换折叠。
  • 标题进入拖动后,移动指针会高亮同一置顶/归档分桶中的另一组;释放时按指针相对目标垂直中点插入其前或后。聚焦标题后按 ArrowUp/ArrowDown 是键盘路径,Escape 取消拖动。
  • 重排仍写入连续的规范化路径 order 值,并将渲染器本地的项目排序切换为 manual。主机工作区身份、会话顺序和磁盘目录保持不变。
  • 决策 D402 修订 D399 / D093 / ADR 0227。见 ADR 0228 与 E2E-253。

2026-09-11 —— 按住标题移动即可重排(D403) ​

  • 鼠标和触控笔按住项目标题并移动 8px 开始重排。没有足够移动的单击仍会选中项目并切换折叠。触摸不会开始重排,以便列表可以滚动。
  • 强调色插入线按指针相对目标垂直中点标出前/后放置位置。ArrowUp/ArrowDown 与 Escape 不变。
  • 决策 D403 修订 D402 / D093 / ADR 0228。见 ADR 0229 与 E2E-253。

2026-09-11 —— Skill 随 Agent 核心工具集下发(D404) ​

  • Skill 加入 Agent 模式核心工具集,因此只要技能目录非空,第一个 provider 请求就带有它的 schema。它不再属于延迟目录,也不会出现在 # On-demand tools。
  • 注册门槛不变:只有目录非空时才存在该工具,Plan 与 Goal 仍然完全不提供它。其他所有按需能力与 ToolSearch 本身保持原有延迟行为。
  • 决策 D404 修订 D174 / D185 / ADR 0048 / ADR 0219。见 ADR 0230 与 E2E-254。

2026-09-11 —— 顿号打开斜杠菜单(D405) ​

  • 当输入框为空时,第 1 个字符提交的「、」会在触发检测之前改写为 /,中文输入法因此无需切换输入方式即可打开普通斜杠菜单。
  • 只改写该位置:出现在草稿其他位置的「、」仍是普通标点,@ 文件菜单不受影响。
  • 决策 D405 修订 D123 / D139 / ADR 0024。见 ADR 0231 与 E2E-255。

2026-09-12 —— macOS DMG 只保留打开说明(D406) ​

  • macOS DMG 展示 PI-Desktop-macOS-opening-help.txt,Finder 名称为 If app won't open, read this.txt,不再包含或暴露可执行的 PI-Desktop-macOS-open.command。
  • macOS ZIP 安装包保留打开说明和可执行助手。说明为可信未签名构建提供范围明确的 终端备用命令;已签名和公证版本无需执行。
  • 决策 D406 修订 D371 / ADR 0204。见 ADR 0232 与 E2E-196b。

2026-09-12 —— 导入会话后恢复已归档项目(D407) ​

  • 核心或插件导入新增项目绑定会话时,在导入触发的刷新期间清除该会话规范化项目路径对应的渲染器归档状态。
  • 无路径会话、跳过的导入、仅有历史路径的插件会话和普通刷新保持归档状态。host 模型以及现有 IPC、插件、存储和数据格式契约保持不变。
  • 决策 D407 记录 issue #250 的修复。见 ADR 0236 与 E2E-257。

2026-09-13 —— 三栏布局中优先保障 MainChat(D408) ​

  • 流入式外壳把 MainChat 视为第一优先列:硬下限 450px,工作面板上限为动态预算(客户端宽度 − 450px − 展开的左栏宽度,无固定上限);展开的左栏在阈值处立即让位,且其 sidebar-out 退场动画期间仍计入共享预算。
  • 手动重开左栏优先占用右栏宽度,否则以 460px 为目标。关闭右栏只恢复由布局机制收起的左栏;手动收起保持收起。
  • 原生窗口不参与:预留 seam 保持 0,不套用任何面板宽度或 x 偏移几何。
  • 预览模式卸载 MainChat,让工作面板填充侧边栏之外的客户区。AppShell 提供窗口级 chrome 行承载 shell 操作和本机控件;侧边栏折叠时,macOS 窗口模式预留 88px, 全屏预留 8px 给交通灯。
  • 决策 D408 记录 issue #267 的行为。见 ADR 0238 与 E2E-LAYOUT-three-column-width-priority。

2026-09-13 —— 独立会话发现与可导航协作投影(D410) ​

  • 已审查的插件网关提供有界的 session/collaboration/list 读取操作,返回未删除 Agent 会话,包括在 Session Orchestrator 之外创建的会话。不返回项目路径、凭据、转录或消息预览;send 继续使用真实 Session ID,并沿用现有 host 授权路径。
  • 侧边栏协作摘要现在带有可读的 provider/model 标签以及有界的创建者/已创建会话引用。渲染器把这些引用展示为可键盘聚焦的导航按钮,并通过现有选择路径打开持久会话。
  • 决策 D410 修订 ADR 0239。见 ADR 0240、E2E-SESSION-independent-top-level-communication 和 E2E-SESSION-hover-card-model-and-links。

2026-09-12 —— 用 Alt+Enter 向当前回合补充指令 ​

  • 普通 Send/Enter 仍是 Host 拥有的 follow-up;Alt+Enter 携带必填的预期回合 id, 将输入提交到当前持久回合。
  • 复用 pi-agent-core 在下一次模型请求边界消费的 steering,保留已启动工具及当前 模型、工作区和权限配置。
  • 正在停止或已结束的目标拒绝输入,不将其重定向到其他回合。已接收输入保留在历史中, 取消后不会独立重放。
  • 必要时将前一条回复的临时快照与已接收用户输入一并记入日志;只原位落定索引中的 流式助手,并保持已完成消息的幂等性。见 ADR active-turn-steering 和 E2E-AGENT-alt-enter-steers-active-turn。

2026-09-13 —— 加固会话协作导航与投递 ​

  • 协作引用现在携带 available。指向已删除或已不存在的会话的引用以 available: false 报告;渲染器把它渲染为文本而不是导航控件,打开一个已不存在的会话会报告可见错误,而不是提交空转录。session_collaboration_messages.source_session_id 有意不设外键,因此投递记录在发送者被删除后仍然保留,并被报告为不可用。
  • 六个 session/collaboration/* 操作仅限第一方插件:它们要求经过认证的插件工具调用上下文,因此被排除在 MCP 可见目录之外,同时仍可通过 pi.desktop.listOperations 和 pi.desktop.invoke 使用。这修订了 ADR 0203 / D370 的“同一审查操作目录”说法以及插件权限矩阵中的 desktop.control 行。
  • 结算投递时现在会断言条件式的 queued 到 running 抢占确实改变了一行,因此在该竞争中落败不会为同一次投递创建第二个回合;spawn 也在与会话插入相同的事务内评估其 worker 上限。
  • Electron 投递结算不再丢弃宿主重启之前记录的结算,已经在运行的 drain 会接收在其期间排队的结算,而不是把它们推迟到无关的触发条件。持久化的投递失败保持稳定的 CODE: message 形式。
  • 针对编排刷新路径的审查修正把模型目录查找的工作量限制为每次读取,而不再依赖缓存淘汰;并用探针本身并不保证的观察替换那些不可能失败的 E2E 启动条件。
  • 见 ADR 0239、ADR 0240、E2E-SESSION-hover-card-model-and-links。

2026-09-13 —— 文件视图改为 vendor 的可更新插件(issue #304) ​

  • 工作面板的文件视图不再是 pi.files,而是第三方 pi.file-manager 某个发布版本的 vendor 副本,随 apps/desktop/resources/plugins/ 打包,并在它自己的 UPSTREAM.md 中记录来源。旧插件被删除,host-core 会在下次启动时清掉它遗留的注册表行。
  • 内置意味着默认且不可删除,但不是被冻结:内置插件可以从市场更新,该更新会跨过下一次启动;应用自带严格更新的版本时仍以自带版本为准。uninstall 按「该构建是否随应用打包」以 ID 拒绝,而不是按行的 source 判断,因此更新过的插件依然不可卸载,而被某构建移除的插件重新可卸载。
  • 只有严格更新的市场条目才会被当作更新提供。版本相同不算,目录中更旧的版本也不算。
  • 编辑写入留在插件自己的进程内,由它维持工作区路径牢笼、原子写、冲突检测与写入审计;宿主的权限网关不介入这些调用:manifest 无法申报整树写入。
  • 见 ADR 0241(取代 ADR 0105)、ADR 0104、E2E-153、E2E-PLUGIN-bundled-plugin-keeps-a-marketplace-update。

2026-09-14 —— 仅增量且合并的流式更新(D412) ​

  • 追加型 message_update 帧携带 stream: "delta" 以及新块本身。增长中的 content/thinking 留在运行时累加器和 message_end 里。重试替换仍发送完整快照。
  • 16ms coalescer 拼接待发送增量,并在 tool、terminal、abort、error 和 retry 事件前立即 flush,以保持顺序。
  • AgentHost 把增量应用到 live items,小增量帧跳过热路径上的 JSON.stringify。 进行中检查点从同一批增量重建快照。渲染器先拼接同一帧内的增量,再补丁 live 行。 未变化的活动 part 保持对象身份。
  • 协议版本仍为 11。见 ADR 0242、E2E-STREAM-long-turn-keeps-realtime 和 issue #299。

2026-09-13 — 技能市场公网 HTTPS 目录拉取 (D413) ​

  • 技能市场的 search/fetch 在 Electron 主进程执行,渲染层 CSP 仍禁止出网,安装走现有 skills.create。
  • 共享 isSafePublicHttpsUrl / isPublicHostname / isPublicIpLiteral 做语法分类;主进程再检查 DNS 与每一跳 redirect。
  • 扫描 id 净化为 host valid_capability_id。预览展示组装正文;超过 128 KiB 不写入。
  • 见 ADR 0243、E2E-SKILL-MARKET-*、issue #287。

2026-09-14 —— 加固 MCP 市场公网网络边界(D414) ​

  • MCP 市场源和目录端点的校验由渲染器与 Electron Main 共享。只接受无凭据的公网 HTTPS,拒绝回环、私网、CGNAT、链路本地、组播、保留、文档、基准测试、ULA 和 site-local 地址范围,并覆盖 mapped/compatible IPv6 形式。
  • Main 在每次请求前解析主机名,并把选中的公网地址固定到 HTTPS socket。重定向手动跟随,在每一跳重新检查并固定地址,最多五跳。源响应上限为 4 MiB,每次最多处理 16 个源,browse/search 缓存按时间、key 数量和条目数量限制。
  • Registry 的 npm/PyPI 版本以及 runtime/package 参数会保留到安装模板。只映射 streamable-http 公网 HTTPS remote;远程 header 占位符变成明确的安装表单值。跨 origin 的 MCP 重定向不会转发调用方 header。
  • 手动配置的用户 MCP 仍保留 ADR 0142 的显式本地/LAN 端点策略。见 ADR 0245 和 E2E-MCP-MARKET-*。

Tray session shortcuts (issue #293) ​

ADR tray-session-shortcuts amends the D216 native tray menu with Running, Unread, and Pinned groups after global priority assignment: each non-empty group keeps up to three rows, then overflowing groups reclaim the share smaller groups leave unused, in priority order, up to nine rows in total. Host session/inbox reads and runtime events remain the source of truth; renderer organization is mirrored without a persistence change. macOS single-click opens the menu without restoring the window; selection is delivered after bootstrap and uses normal session navigation. Validation contract: E2E-TRAY-bounded-session-navigation.

2026-09-14 — Upstream integration compatibility ​

The authoritative English amendment documents semantic local ADR/E2E identities, acknowledgement-scoped annotation consumption, text-only Alt+Enter steering, and the retained upstream work-panel lifecycle. The annotation and side-chat parts of that amendment are retired by ADR 0268; the upstream work-panel lifecycle stays. See the English decision log.

2026-09-14 —— Git clone 只接受语法上的公网主机(D416) ​

  • 首页「克隆 Git 项目」仍接受 https/http/ssh/git@host:path,并拒绝 URL 密码和 file:。
  • 主机名用与市场守卫相同的 isPublicHostname 分类。私网和回环字面量在解析阶段失败。
  • Git 自己的 DNS/SSH 不变;Electron Main 不固定 clone 连接。
  • 见 ADR 0247 与 E2E-CLONE-public-hostname-rejects-private。

2026-09-14 —— 主题 CSS 校验只检查真正生效的 CSS(D417) ​

  • ui.theme 消毒器在 @import、标记和脚本关键字检查前遮蔽注释体与字符串字面量, 每个被遮蔽字符对应一个空格,偏移仍指向原文;每个 url(...) 参数按原样保留, 因此真实引用仍按目标判定。
  • 采用字符级三态扫描而非 /* … */ 剥离:在 content: "/*"; 中浏览器看到的是字符串, 朴素剥离会把它当成注释,从而藏掉其后的真实 @import "x.css";。
  • examples/plugins/hello/themes/midnight.css 恢复加载。只是收窄了误拒面:没有新增能力、 没有格式变化,对从不出现被禁关键字的样式表行为完全不变。见 issue #334 与 E2E-024J。

2026-09-14 —— 主题包内资源与原生窗口背景(D418) ​

  • contributes.themes[].assets 声明插件包内的图片与字体文件(扩展名白名单、总量上限 4MB)。宿主在插件包内解析它们、拒绝依赖目录、把命中的 url() 改写为 plugin-asset://<pluginId>/<path>,并通过宿主自有的特权协议提供;该处理器只按已加载 插件自己声明的清单应答。未声明的引用仍被拒绝,原始路径不会到达渲染器,因此 data:-only 规则与既有全部拒绝行为不变。权限仍沿用 ui.theme。
  • contributes.windowAppearance.backgroundColor.{light,dark} 接受 #rrggbb / #rrggbbaa,需要新增的 ui.window.appearance 授予。它只在该插件的某个 主题被选中时、且仅在非 macOS 上生效;还原靠推导而非记忆——渲染器每次根据持久化偏好与 实时主题目录重新计算,所以切换、禁用、卸载都会收敛回宿主背景,没有需要回滚的存储值。
  • 内置主题改由同一张共享表提供该值(packages/shared/src/theme.ts):BUILTIN_THEMES 只写一次每个调色板的 windowBackground,isThemeColorScheme 只写一次「这是内置 id」的判断,由渲染器、main、面板宿主、面板 preload 与主题选择器共同读取。此前 #ffffff / #181818 这对字面量散在四个文件里,内置路径与插件路径可能因此分叉。 见 ADR 0248、issue #335 与 E2E-024J。

2026-09-14 —— 停靠列的颜色是标记而非字面量(D419) ​

  • 工作面板列及其内部条目栏的表面色改由 --ds-bg-dock 与 --ds-bg-dock-raised 提供。浅色下为 #fafafa 与 #ffffff;深色下为 var(--ds-bg-secondary) 与 transparent,与改动前基础规则解析出的值完全一致。
  • work-panel.css 里原有 6 处 :root[data-theme="light"] 字面量,既抬高特异度压过 基础规则、又不读变量,导致整列在任何插件主题下都保持宿主底色。这些覆写已删除。
  • 设计系统的表面层级表现在登记了这两个标记,§6.4 也写下了它们要维护的规则:外壳绘制的 表面色必须来自标记,而 :root[data-theme] 覆写里写字面量正是失效模式。
  • settings.css(导航轨、搜索框、开关钮、能力搜索)等文件另有同类洞,见 issue #339。

2026-09-14 —— 结构化、有界且脱敏的进程日志(D420) ​

  • 每条 app/host/agent NDJSON 记录都有稳定的点号分隔 event 和顶层关联字段。 正常工具调用只产生一条完成或失败记录;sidecar 意外退出时为活动工具产生中断 记录。工具协议和成绩单保持不变。
  • 中心日志会脱敏凭据格式、敏感键名和本机路径,限制字符串及结构化数据,并将每条 记录的 data 限制在 8 KiB。host-core 审计 payload 经过相应整形,并限制序列化 payload 大小。
  • 工具结果只保留结果、错误码、时长、字段名、内容块数量以及 stdout/stderr 大小, 不复制原始参数、输出或插件响应。子进程 stderr 和主进程回退日志使用稳定事件; 开发控制台镜像与文件保持同一份已脱敏记录。
  • 见 ADR 0250 和 E2E-034。

2026-09-15 — 删除项目会移除其所属会话 (D421) ​

  • projects.remove({ path }) 是新增的宿主 RPC。它移除一条持久项目行、附加到该行的每个 会话、这些会话的转录本/scratch/review 文件以及该项目的持久记忆,并且从不触碰磁盘上的 项目文件夹。未知路径返回 { removed: false, sessionsRemoved: 0 } 而不是报错,桌面端仍会移除 该路径自身的记录:只有残留的最近项目条目还能让这一行保持可见。
  • 项目索引是四个来源的并集(持久行的组投影、pi.desktop.recentProjects、由会话推导出的 项目,以及活动工作区)。因此删除会在同一操作中移除渲染器本地的记录;只删除数据库行会让 该行仍然可见,这也是此前的手动变通做法还需要清理渲染器存储的原因。
  • 作为已存储多文件夹项目组根目录的路径会被拒绝并给出提示消息,以便该组保留有效的 Primary 根目录;遗留的单根投影照常删除。
  • 只要该项目下仍有会话在运行,删除就会被拒绝(1008 / CONFLICT,"project has running sessions"):运行中的轮次仍拥有其工具与工作目录,并且仍在追加自己的转录本,因此批量删除 会等项目空闲,而不是在事后复活出一个桩会话。渲染器也通过 project.deleteRunningBlocked 提前拦截同一操作。单会话删除保持现有语义。
  • 该操作被刻意排除在 CONTROL_OPERATION_SPECS 之外,因此本地 MCP 控制无法删除项目。 见 ADR 0251 与 E2E-PROJECT-delete-removes-project-and-owned-sessions。

2026-09-13 —— 插件的宿主回合结束事件(D422) ​

  • session:turnEnded 发送到插件进程、插件面板页面和停靠视图,载荷为 { sessionId, turnId, reason },其中 reason 为 completed、aborted 或 error;对 session.beginTurn 真正开始的每个回合(用户提交、已批准的计划执行、 定时运行)发送一次。
  • 该通知在回合拆除结束时、持久化的 session.endTurn 尝试之后发出,携带终止 运行时事件的 turnId 而不是恰好活动的回合。插件工具上下文中的 turnId 会填充同一身份,由宿主原样转发。
  • 收尾函数是所有终止路径唯一的入口,且必须显式给出 turnId:缺失身份的终止事件, 或所属回合已不再拥有该会话的终止事件,既不结算也不宣告。回合记录按 (sessionId, turnId) 作为键,因此重复的终止事件会加入首次认领,而不会宣告两次。
  • 该事件不需要权限,没有 ack 也没有重放;跨插件崩溃、重载或宿主退出的投递不作 保证,也不代表该回合的所有在途工具都已退出。目前尚无任何已发布宿主发出它, 首个包含它的版本尚未发布。
  • 决策 D422 由 ADR 0252 记录。

2026-09-15 —— 移除子智能体轮次上限(D423) ​

  • 在 ADR 0166 撤掉空闲与总时长看门狗之后,maxTurns 是最后一个定义级杀死开关,而父级无法为它定值:它看不到委托的实时工作,因此无法区分「再一轮就收敛」和「永远不会收敛」。内置兜底值(60、50、40、80)没有推导依据,而编辑器自己的初始状态本来就是不限制。
  • 决策 D423 把该字段从 SubagentDefinition、frontmatter 解析器、UserSubagentRecord / UserSubagentInput、host-core 注册表、五个内置文档、SUBAGENT_PRESETS 以及子智能体编辑器的「高级」折叠区中移除。委托只在完成、父级调用 TaskStop、用户 Stop,或父级终态错误中止它时结束。
  • 仍声明 maxTurns 的既有文档继续正常加载:该键现在是无法识别的 frontmatter,会像其他未知键一样被忽略,不报错、不告警、也不改写用户文件。因此依赖该上限的定义会静默失去它。
  • truncated 子智能体状态从共享运行状态联合类型、渲染器结果联合类型、各语言的 chat.subagentStatus 目录项以及委托拓扑的警告计数中一并移除。尽管 D328 已撤掉产生它的看门狗,timed_out 仍保留在类型中。
  • 不改协议版本、schema 版本或存储:host-core 的子智能体输入结构仍然忽略未知字段,注册表解析的是 Markdown frontmatter 而不是表列。
  • 见 ADR 0253、03-runtime/02-agent-runtime.md §5f、04-ux/06-settings-ia.md §7、E2E-155 与 E2E-SUBAGENT-legacy-turn-limit-frontmatter-is-ignored。

2026-09-15 — 主题资源改为绝对路径(D422) ​

  • ADR 0248 要求主题资源是包内相对路径,于是插件必须把用户选的图片复制进自己的包。 开发插件是递归监听的,每次复制都会重载插件并关掉该插件的面板窗口;而插件自己的 数据目录根本无法被引用。
  • normalizeThemeAssetPath(SDK)与 normalize_theme_asset_path(host-core)现在 只接受绝对路径(C:/art/bg.png、/art/bg.png,或两者的 file: 写法)。包内相对 路径不再被当作资源;受影响面仅限声明过 assets 的主题。
  • pi.themes.upsert 会解析主题 CSS 里的资源引用,并登记进该插件的资源表(插件加载 期间有效),因此换图不需要在插件包内写任何文件。
  • 保持不变:扩展名白名单、声明资源的总量 4MB 上限、./.. 拒绝、url() → plugin-asset:// 改写、nosniff、no-store、卸载即撤销。绝对路径在 URL 里做 percent-encoding,由处理器解码。
  • 代价:插件现在可以把它能读到的任意本地文件交给渲染器。manifest 声明的资源仍可 静态审查;运行时登记的随插件消失。
  • 见 ADR 0255、07-plugins/02-plugin-manifest-schema.md、 07-plugins/04-plugin-security.md,以及 ADR 0248 §1(路径解析部分已被取代)。

2026-09-15 —— 压缩后保留 DeepSeek 推理(D424) ​

  • OpenCode / 第三方 DeepSeek 中转在上下文压缩丢掉助手思考后,会拒绝空的 reasoning_* 回传(#296)。官方 deepseek.com 仍接受 ""(#223 / D389)。
  • 决策 D424 修订 D389 / ADR 0136:历史重建时恢复 thinkingSignature;把最近思考暂存在检查点 details.retainedReasoning 并在摘要后回放;对非官方 DeepSeek Completions 行设置 requiresNonEmptyReasoningReplay,使 pi-ai 补丁填入文档化占位符而非 ""。见 ADR 0256 与 E2E-005E。

2026-09-15 —— 回合队列的优先区块与行内操作(D429) ​

  • “立即发送”从“跳到队首”改为优先区块:被提升的条目写入其会话内的 MAX(priority) + 1(架构 v18,新增可空 priority 列),因此多次提升按点击顺序投递,等待队列在其后保持自身的 position 顺序。对已经带优先级的条目录再次提升返回 CONFLICT,而不是再次移动。
  • 排队行在“立即发送”和删除之外新增上移、下移与编辑。上移/下移通过 session.queueReorder 与相邻的等待条目互换,绝不跨入优先区块;编辑移除该行并把捕获的草稿(文本加内联文件引用)回填输入框,输入框非空时拒绝执行。
  • 被提升的行在 Host 投递之前保持锁定:上移/下移、编辑与删除均被禁用,“立即发送”显示为已决定。
  • 优先区块以相邻的用户消息投递:第一个已优先条目在边界处启动回合,其余条目通过引导通道注入同一回合,因此整块只被回复一次,而不是每行各回一次。被注入条目自己的回合被标记为已取消;运行时拒绝接收的条目仍留在队列中,在下一个边界作为自己的回合启动。
  • 已结算的回合对队列具有权威性:终态事件可能被丢弃(Main 不会转发点名它已不再拥有的回合的终态事件),也可能根本没发出,而留在 Host 中处于活动状态的回合会永久占住该会话的队列——“点立即发送再暂停”导致的卡住正源于此。现在结算会在模块内关闭该回合,且在 drain 进行中到达的请求会被重试而不是丢弃。见 ADR 0265、03-runtime/01-ipc-protocol.md(§5.6)、03-runtime/04-data-storage.md、04-ux/08-component-spec.md(§11)与 E2E-QUEUE-promote-orders-delivery-by-click。

2026-09-16 —— 手动展开详情时保持阅读位置(D430) ​

  • 手动展开会把自己的标题交给拥有它的滚动器,发生在展开状态改变之前、且是同步的。 内容 ResizeObserver 仍会在每次内容尺寸变化时重新触底一个已固定的转录(D287),它无法区分「流式增长」和「读者展开一行」,所以先让读者表达意图:转录退出跟随模式,滚动器在同一个观察器回调内恢复该标题的视口偏移,持续覆盖高度仍在变化的每一帧(活动组会动画 grid-template-rows)。.thread-scroll 上的 overflow-anchor: none 意味着这件事必须显式做。
  • 锚点是标题元素相对滚动器顶边的偏移,而不是 scrollTop;浏览器自身的边界夹紧会被采纳为新的锚点。 内容无法到达的修正量、以及标题上方同时发生的高度变化,都在不与浏览器逐帧对抗的前提下被处理。没有延时「把底部抢回来」的补偿:hold 会在真实滚动输入、跳到最新控件、新一轮或导航时结束。
  • 每个滚动所有者各持自己的位置,并把 hold 向外传递。 转录滚动器与嵌套的委派停靠区(useFollowScroll,D302)共用同一个控制器;停靠区变高同时也在增大转录的内容高度,因此仍处于跟随模式的外层滚动器会把同一个标题再次拽走。
  • 输入会归属到真正能消费它的滚动器。 只有真正能让某个容器移动的输入才可以释放跟随或已持有的位置:按在行、控件或可编辑字段上属于普通点击,文本框里的按键属于该字段,被嵌套滚动器消费的输入不算外层的手势。方向键仍能滚动,Space 仍能激活已获得焦点的标题。
  • 程序化的立即滚动记录的是滚动器实际到达的 scrollTop,并且亚像素容差只作用于非手势产生的滚动事件。在 DPR 1.1 下跟随请求 841 而实际得到 840.909,严格比较时这个小数会被读成读者向上滚动并解除跟随。真实手势仍按精确值比较,所以一个像素的上滚依然会解除固定。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动。见 04-ux/09-interaction-patterns.md §9.1 与 E2E-CHAT-disclosure-toggle-keeps-reading-position。

有读者反馈:点击工具、思考或活动标题展开时,整个转录会被拽上去整整一个展开详情的高度,即使是停在底部、甚至该轮已经结束的会话也一样。

2026-09-16 —— 运行中任务的拒绝归属删除对话框,而不是菜单(#360,D431) ​

  • 修订 D421 的渲染层一半。侧边栏菜单与项目索引菜单一旦发现该项目有任何会话在运行,就用一条转瞬即逝的 project.deleteRunningBlocked 警告拒绝「删除项目」,并在 ProjectDeleteDialog 挂载之前就返回。这条拒绝随 toast 一起消失,没有留下任何可继续的路径,于是带有一个在跑任务的项目根本无法删除 —— 这正是 #360(「项目管理中无法真正删除项目」)所描述的体感。
  • 现在两个菜单都始终打开该对话框。每个入口都会传入该项目当前在运行的会话 id(runningSessions[session.id],作用于它本来就已匹配的行:侧边栏的 entry.sessions、索引的 sessionMatchesIndexProject),对话框在每次渲染时从该 prop 推导文案,因此对话框打开期间新开始或已结束的回合都会在用户确认之前被反映出来。
  • 对话框新增一行警告,点名有 个会话正在运行,并把确认按钮文案换成 project.deleteRunningConfirm(「停止任务并删除」)。确认会精确中止这些会话,然后才调用 deleteProject,所以删除一个正在运行的回合仍然是针对已陈述后果的第二次显式确认。取消不删除任何东西。
  • 宿主侧的守卫未改动:只要仍有关联会话存在运行中的回合,projects.remove 依旧以 1008 / CONFLICT 拒绝,对话框也依旧把该拒绝映射为 project.deleteRunningBlocked。中止循环之后才开始的回合,正是这条兜底覆盖的情况。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动,也没有新增默认值。project.deleteRunningBlocked 的含义、文案与全部翻译保持不变。见 ADR 0251、D421 与 E2E-PROJECT-delete-running-sessions-are-named-and-stopped。

2026-09-16 —— 插件 workspace fs 根跟随发起调用的会话(D432) ​

  • 所有模式根为 workspace 的插件 pi.fs.* 调用都解析到窗口级唯一的可见工作区,因此从会话 B(项目 B)发起的插件智能体工具,在用户切换标签页后读到的是项目 A 的根;而在没有可见工作区时,所有会话会一起以 NOT_FOUND 失败(D093 已为内置工具执行定下同一条规则,却从未传到插件)。
  • plugin-runtime.ts 现在用新增的私有辅助函数 fsRoot(loaded, rule) 解析该根:由发起这次调用的工具会话所属项目决定,通过新增的宿主服务 getWorkspacePathForSession 询问。plugin-services.ts 用它本就维护的「会话到项目」映射完成接线,因此没有引入新的真相来源。
  • 未改动:面板桥调用没有工具会话,宿主不跟踪的会话也一样,它们的根仍是可见工作区;userSelected 模式依旧保留用户通过 requestDirectory() 选定的目录。
  • 现在没有可见工作区时会话根也能解析,所以临时对话按会话各自继续工作,而不是所有会话一起失败;这种状态下的面板调用仍然失败关闭。于是 workspace 根的 NOT_FOUND 更窄:既没有解析出调用会话的项目,也没有可见工作区。
  • 权限、realpath 包含、拒绝名单、声明范围与运行时同意这四类闸门都没有改动,插件 API 表面也未变化:pi.workspace.get 仍然以可见工作区及其项目组作答。
  • 见 ADR 0266、07-plugins/03-plugin-api.md §3、07-plugins/13-plugin-permissions-matrix.md §6 与 E2E-PLUGIN-fs-root-follows-the-calling-session。

2026-09-16 —— 引用、批注与侧边聊天已移除(ADR 0268) ​

  • 引用、文本选区浮层、响应批注(评论编辑器、悬浮批注索引、源标记、composer 批注附件)与渲染器拥有的侧边聊天已从所有界面移除。消息操作行保留 Copy、Edit、Delete、Fork、Retry 与 Regenerate;composer 保留草稿、芯片、智能 Stop 与会话级草稿槽;工作面板保留其余标签类型;session.fork 与 Fork 操作不变。
  • 发送路径不再拼装 # Response annotations: 块,显示层还原函数 requestTextWithoutAnnotations 一并删除,因此移除之前已存入会话的提示词在转录、编辑种子与队列预览中都按存储原文显示。
  • 永久退役且不得复用:D-LOCAL-message-quotes、D-LOCAL-selection-overlay、D-LOCAL-response-annotations、D-native-sidechat-stream,以及 E2E-CHAT-quote-prefill、E2E-CHAT-selection-markdown、E2E-CHAT-selection-side-chat、E2E-CHAT-annotation-attachments、E2E-CHAT-annotation-session-state、E2E-CHAT-annotation-source-index、E2E-CHAT-annotation-ack-and-steering、E2E-CHAT-side-chat-fork、E2E-CHAT-side-chat-stream、E2E-CHAT-side-chat-add-to-main、E2E-CHAT-side-chat-promote、E2E-CHAT-side-chat-close、E2E-SESSION-native-side-chat-fork-survives-close。
  • 未改动宿主协议(v11)、存储 schema(v15)、IPC 通道、权限与 Rust 侧。见 ADR 0268。

2026-09-16 —— 没有文字的控件声明自己的正方形几何 ​

  • .icon-btn 与带文字的胶囊按钮共用,宽度来自内容 —— 图形加左右各 8px 内边距。于是每一处只有图标的使用都变成了"高大于宽":侧边栏收起控件与输入框的添加按钮渲染为 31×28,.composer-right 内的增强与撤销控件为 35×28,设置里的服务商操作按钮因 14px 图形而为 30×28。同一个功能在会话顶栏早就是 28×28 的正方形,也就是同一个功能存在两种尺寸。
  • 这些控件现在声明 .icon-btn-square:把两个轴都固定到新增的 --ds-control-size(28px)标记,保留 flex: 0 0 以免拥挤的工具条把控件压回非正方形,并去掉侧向内边距(在全局 border-box 下,那会给 15px 的图形只留 12px 内容区)。只有图标的控件现在与顶栏开关、工作面板操作、发送/停止控件一直以来的正方形保持一致。
  • .composer-right .icon-btn 不再设置水平内边距。带文字的模型/思考胶囊自己用 !important 设置内边距,所以那条声明只会把它旁边的纯图标控件撑宽;增强控件在加载态仍保留自己的带文字几何。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动。见 04-ux/07-ui-design-system.md §11.1 与 04-ux/08-component-spec.md §3.7。

2026-09-16 —— 每个 chrome 图标控件共用一套几何 ​

  • 预览态与路由带上的动作按钮(.title-nav-btn)此前渲染为 22px 的方块,使用 --radius-2xs 与 --ds-tile 底色,而它们的同级控件是 28px:会话顶栏里同一个侧边栏开关、视口固定的工作面板开关,以及工作面板自身的操作。同一个控件、同一个 46px 带内,却有两种尺寸和两种形状。
  • 这些动作按钮现在采用共享的 chrome 控件几何(.ct-icon-btn 组):由 --ds-work-panel-toggle-size 得到的 28px 正方形、flex: 0 0 以免拥挤的带把它们挤扁、--radius-md、透明底座加语义化 hover 底色,以及其它 chrome 控件统一使用的 15px 图形。.title-nav-btn 只保留按压反馈与 active 色调。
  • 预览态动作组的车道宽度改为由它所预留的控件推导:--ds-preview-action-lane-width: calc(2 * var(--ds-work-panel-toggle-size) + 4px + 8px),这样面板头部的预留与按钮本身不会再各自漂移。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动。见 04-ux/08-component-spec.md §2。

2026-09-16 —— macOS 交通灯预留量只有一个来源(D433) ​

  • 渲染层为 macOS 交通灯留出的窗口模式引导内缩,原本是一个手抄的 76px:散在 apps/desktop/src/styles/chrome.css 与 apps/desktop/src/styles/work-panel.css 里的五处声明 —— .main-titlebar-left(64px,即该值减去行自身的 12px 内边距)、.conversation-topbar.ct-collapsed(--ct-lead-inset)、.sidebar-header、.window-chrome-row:not(.sidebar-expanded),以及预览态的 .work-panel-header(calc(76px + var(--ds-preview-action-lane-width)))—— 每一处还各有一个 [data-fullscreen="true"] 分身。它们谁都没有和主进程交给 Electron 的 trafficLightPosition 建立关联,因此任一侧单独改动都会与另一侧漂移。
  • 原生几何现在只有一个归属地 packages/shared/src/window-chrome.ts:MAC_TRAFFIC_LIGHT_POSITION({x:16,y:16})与 MAC_TRAFFIC_LIGHT_CLUSTER_WIDTH_DIP(60),并由 MAC_TRAFFIC_LIGHT_EDGE_DIP(16 + 60 = 76)给出灯簇右缘。主进程用这些常量放置按钮,渲染层在启动时把右缘注入 --ds-traffic-light-edge。所有消费点都读 --ds-window-lead-inset,它由 chrome.css 针对 :root[data-platform="darwin"] 组合一次:--ds-traffic-light-edge + --ds-traffic-light-gap(12px)= 88px。
  • 按钮此前紧贴绿灯右缘,间隙为 0。现在灯簇右缘与第一个 shell 控件之间保留了 12px 呼吸空间,这也是窗口模式从 76px 变为 88px 的原因。
  • 逐个选择器的 [data-fullscreen="true"] 覆盖规则已删除。全屏会隐藏按钮,同一个 token 的 [data-fullscreen="true"] 规则一次性把所有消费点的预留量降为普通的 8px 留白。
  • Windows 与 Linux 行为不变:--ds-window-lead-inset 保持 0px 默认值,这两个平台仍使用右侧 120px 带内由渲染层绘制的控件。插件面板窗口创建的是普通无框架 BrowserWindow,没有原生交通灯,因此不受影响。
  • 仅渲染层 + 共享常量:无协议、存储、宿主、权限或迁移改动,也没有新增默认值。
  • 见 04-ux/08-component-spec.md、04-ux/09-interaction-patterns.md 与 E2E-LAYOUT-three-column-width-priority。

2026-09-16 —— 工作面板头部的按钮现在是一组 ​

  • 工作面板头部此前读起来是两簇:头部内部的动作组(+ 与最大化,相距 4px),以及头部之外视口固定的折叠开关。它们之间的分隔由 .work-panel-actions 上的四条声明叠加而成 —— margin-right: 8px、padding-right: 8px、1px 的 --ds-border-subtle 分隔线,以及另一个车道 token --ds-work-panel-toggle-gap: 20px —— 实际间距 37px 外加一条细线,于是同一个 46px 行内同样的三个 28px 方块看起来像两簇毫不相干的控件。
  • 现在整行由同一个 token 控制间距。--ds-work-panel-control-gap: 4px 取代了 --ds-work-panel-toggle-gap,同时是头部的 flex 间距(标签条到动作组)、.work-panel-actions 的间距(+ 到最大化),以及头部右侧内边距以 calc(size + inset + control-gap)(44px)形式花费的、动作组到固定折叠开关的间距。.work-panel-actions 只声明间距:分隔线、内缩与外边距都已移除。
  • 车道仍然为整个开关预留空间,+ 触发按钮在车道内与开关之间仍有 36px(4px + 28px 的最大化控件 + 4px),因此 WORK_PANEL_HEADER_PROBE 的 24px 断言与 E2E-152 的命中区域契约依然成立。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动,也没有新增默认值。见 04-ux/08-component-spec.md §5.2、04-ux/07-ui-design-system.md §4.3 与 E2E-LAYOUT-three-column-width-priority。

2026-09-16 —— 工作面板的控件是安静的图标,而不是填充的底座 ​

  • 工作面板头部的 + 与最大化控件此前各有一个 --ds-tile 底座,视口固定的折叠开关在 aria-pressed="true" 时又叠加 --ds-tile-deep 与 --ds-raised-shadow。在浅色主题下,这一行三个 28px 控件读起来就是三个填充方块,开关更像一颗悬浮在面板头部之上的抬升圆形胶囊 —— 与旁边左上角的侧边栏开关、新任务控件完全不是同一种语言。
  • + 与最大化现在加入 chrome.css 中的共享 chrome 控件组 —— 也就是为顶栏侧边栏开关、视口固定的面板开关与预览态/路由带动作组提供底座的同一条规则 —— 因此它们共用一套几何(28px 方形、--radius-md、flex: 0 0)、同一个透明底座、指针悬停时的语义化 --ds-bg-hover 淡色,以及 opacity: 0.4 的禁用状态。它们在 work-panel.css 中重复的规则已删除,该分部只保留动作组的间距。
  • 开关的按下状态去掉填充与抬升阴影,保留被激活的墨色,因此打开状态就是图形切换加 --ds-text-primary。aria-pressed 仍向辅助技术传达状态,.app-work-panel-toggle 继续拥有视口固定定位与 no-drag 排除区。
  • 键盘焦点不变:全局 :focus-visible 轮廓与其他 chrome 图标控件一致地生效,因此移除已废弃的 :focus-visible 背景淡色不会损失任何焦点提示。
  • 仅渲染层:几何、间距、内缩、协议、存储、宿主、权限与迁移均无改动,也没有新增 token。见 04-ux/08-component-spec.md §2.3 与 §5.2、04-ux/07-ui-design-system.md §4.3 与 E2E-LAYOUT-three-column-width-priority。

2026-09-17 —— 内置子智能体可以关闭(D434) ​

  • 设置 > 智能体 > 子智能体把五个随应用发布的默认项列为只读行,而用户自建的文档带启用开关,于是有一个委派对象完全无法关闭:复制一个内置项再停用副本,随应用发布的定义仍留在目录里,因为被停用的用户文档在加载器合并前就被过滤掉,也就没有遮蔽任何东西。
  • 每个内置行现在都带同一个开关。句柄存放在 <data>/agent-capabilities/subagent-builtins.json —— 一个独立文件,因为用户文档扫描会清理它永远看不到的 id 的状态 —— 并由 host-core 通过 agents.disabledBuiltins / agents.setBuiltinEnabled 暴露。Electron 主进程把被关闭的句柄交给 loadSubagentDefinitions,由它把它们从委派目录中剔除,而 subagent/catalog 仍会在其新增的 builtins 列表中返回该内置项,并标记 enabled: false。
  • 被关闭的内置项保留自己的行并变暗,因此这个开关就是重新打开的入口。同名的用户文档继续生效并继续遮蔽随应用发布的定义,在文件夹中显示与删除仍然没有,因为内置不是文件;宿主不可用时也不会贡献任何排除项。见 ADR 0270、04-ux/06-settings-ia.md §2、03-runtime/01-ipc-protocol.md §12c 与 E2E-SUBAGENT-settings-lists-builtin-defaults。

2026-09-17 —— 按请求实际会走的线路判定公网地址(D436) ​

  • 技能市场的主进程守卫此前用本地解析器判定目标主机,而 net.fetch 走的是 Chromium 的代理栈(ADR 0177)。在 TUN / fake-IP 解析器下,那个答案是应用永远不会建立的连接的合成地址(198.18.0.0/15),于是每个目录源都被判为「被应用的地址校验阻止」(issue #419)。
  • 现在每一跳都向承载 net.fetch 的会话询问它自己的判定(Session.resolveProxy),共享的 classifyProxyRoute 把该答案归为 proxied / direct / unknown。在 proxied 线路上,isAcceptableResolvedAddress 只容忍解析器自身产物的那一类(benchmark);所有真实内网类别与「解析器没有应答」仍然拒绝。direct 线路默认与改动前逐字一致,但显式 allowFakeIp 仅可额外放行 benchmark 占位地址;列表里任何位置出现 DIRECT 都按 unknown 处理(Chromium 可能回退到它),而 unknown 走严格路径。
  • 拒绝与市场的 failureDetails 现在都带 route,因此「直连线路上的 fake-IP 拒绝」与「读不出线路的拒绝」在诊断里可以区分。
  • MCP 市场现在也在每一跳向 Electron session 询问线路:完整代理线路使用 net.fetch,让 fake-IP 源能够到达已配置的代理;直连和未知线路默认继续使用固定 Node HTTPS 地址的严格公网规则。显式 allowFakeIp 选项仅为透明路由器/TUN 部署放行 benchmark 占位地址,真实私网答案在所有线路上仍然拒绝。见 ADR 0245。

2026-09-17 —— dock 内的问题卡是 composer 板(#360,D437) ​

  • asktool 问题卡挂在透明的 composer dock 里(Composer.tsx 把它渲染为 .composer-stack 的直接子节点,成绩单内的挂载已移除),但它仍在绘制流动层的 --ds-tile 洗色:3.5% 墨色混合、没有阴影。在浅色页面上这留下一块 #f7f7f7 面板加白色选项行,紧邻下方的 composer 板 —— 这正是 #360 第 1 项(“选择交互面板缺少背景色”)读到的样子。该 dock 规则自己的注释早已声称这张卡“使用与 Plan/Goal 批准相同的决策表面”,而同一槽位里的 .plan-approval-bar 绘制的是 --ds-bg-composer 加 --ds-shadow-composer(04-ux/03-permission-ux.md §9、04-ux/08-component-spec.md §11.5)。
  • .composer-stack > .asktool-card 现在绘制那块板:--ds-bg-composer 加 --ds-shadow-composer(浅色 #ffffff,深色 color-mix(in oklab, #212121 96%, transparent) 配合 composer 阴影)。
  • 它的控件移到该板的内嵌层 —— 也就是 .plan-approval-split 在同一块板上已经使用的层:.asktool-option 与 .asktool-custom-input 去掉 --ds-raised 与 --ds-raised-shadow,改用 --ds-tile-deep,悬停/选中混合也以 --ds-tile-deep 为基底。不做这次翻转,浅色主题下选项行会白上加白,因为该调色板里 --ds-raised 与 --ds-bg-composer 都是 #ffffff。15 px 的选项标记保留 --ds-tile-deep —— 与侧边栏复选框同一枚标记 —— 它与内嵌行之间的对比,和它与侧边栏之间的对比完全一致。
  • 仅渲染层:无协议、存储、宿主、权限、迁移或偏好改动,也没有新增默认值。两层在两种调色板里都是既有 token,因此贡献主题可以分别用 --ds-bg-composer / --ds-tile-deep 移动板与行。见 04-ux/11-asktool-question-card.md、04-ux/07-ui-design-system.md §6.4 与 E2E-078。

2026-09-17 —— 呼出与隐藏窗口合并为一个开关键(#360,D438) ​

  • issue #360 第 3 项要求把「呼出窗口」与「隐藏/关闭窗口」两个全局快捷键合并成一个键。 D384 当年给的恰好是相反的安排 —— summonWindow(Mod+Shift+W)作为 closeWindow(Mod+W)的「对称键」,并由 04-ux/09-interaction-patterns.md §1.1 记载 —— 因此 D438 取代 D384 的这一半;同一条决策里的插件 Plan 安全动作选项不受影响。 编号跳过 D435 与 D437,因为其它在途工作已先占用。
  • 快捷键目录现在只保留 window 组里的一个 toggleWindow;closeWindow 与 summonWindow 已从 KEYBOARD_SHORTCUT_IDS 移除,因此两个退役组合键都不再被任何 东西占用:它们既不是随应用发布的默认值,也不会注册到 globalShortcut,既不是设置行, 也不是菜单加速键,而被释放出来的 Mod+Shift+W 可以重新交给插件使用。D439 后来把 开关键自己的默认值从 D438 选的 Mod+W 挪开;合并后的 id、切换语义与下面的迁移规则 都不受这次改键影响。
  • 按下这个键是一次判定,而不是一次关闭。windowToggleAction 对可见且在前台的窗口执行 Window.hide(),其余情况显示并获得焦点(已隐藏、已最小化、被其它应用挡在后面都算 「不可见」)。Window.hide() 就是隐藏的全部路径,所以 Windows/Linux 的关闭行为询问、 其中的「退出」选项、window-all-closed 与 before-quit 都不会被触达,没有窗口被销毁, 应用继续运行;托盘图标、同一个键或 macOS 的应用激活都能把窗口调回来。 windowControl("close") 仍是窗口自己的关闭按钮,依旧走关闭行为。
  • 已持久化的 settings.keybindings 仍可能写着这两个已退役的 id,两个进程遵循同一条规则: migrateKeybindingOverrides 在读取时就折叠映射 —— Electron 主进程在注册加速键和构建 菜单之前,渲染层 store 在任何消费方看到它之前 —— 因此没有任何表面会按退役 id 行事。 规则有序且幂等:已存在的 toggleWindow 覆盖原样胜出;否则第一个带绑定值的退役项胜出, closeWindow 优先,因为隐藏才是开关键的主要职责;只有在没有绑定值竞争时, 仅写入 null 的退役项才被尊重,所以明确的「未绑定」不会被随应用发布的默认值顶替; 与自身退役默认值相同的存储值不携带任何意图(设置界面会删除等于发布默认值的覆盖项), 因此 Mod+W/Mod+Shift+W 这类值会被丢弃而不是被复活。存储本身不做改写:折叠后的映射 由下一次快捷键保存写入,旧版本读到新映射时只是忽略不认识的 id。
  • 窗口可见性键在 macOS、Windows 与 Linux 上都是受支持的全局快捷键,因此按一个键来记录: 菜单标签与设置行在全部八套语言包里本地化,快捷键卡片显示唯一的一行「窗口」, 而不是两行互相矛盾的行。
  • 见 ADR 0211(已修订)、04-ux/09-interaction-patterns.md §1.1 与 §1.4、 04-ux/06-settings-ia.md(快捷键选项卡)、07-plugins/03-plugin-api.md、 07-plugins/04-plugin-security.md、03-runtime/01-ipc-protocol.md (NATIVE_MENU_ACTIONS)、E2E-072 与 apps/desktop/test/window-toggle-shortcut.test.mjs。

2026-09-17 — Git 检出成为新建项目的来源(D438) ​

  • 新建项目对话框此前只收集项目名称与本地文件夹(ADR 0233),而首页「克隆 Git 项目」入口只在绑定项目的会话 hero 中渲染。全新安装因此必须先打开一个无关的本地文件夹才能克隆仓库。
  • 对话框现在拥有两种对等来源:此电脑与 Git 仓库。Git 来源保留同一个名称字段(在用户输入前用仓库名预填),增加仓库地址输入框和一个克隆保存位置行,并复用切换器的 parseGitCloneUrl 规则,因此私网、回环、链路本地、带凭据与非法远程都会让创建按钮保持禁用(ADR 0247)。
  • 主进程新增可加的 project/cloneCheckout({ url, parentPath }):它克隆到显式指定的父目录并返回 { path, name },不更改当前工作空间、也不弹出选择器。project/clone 为首页切换器保留原有的原生选择器行为。
  • 项目创建语义不变:两种来源调用同一个项目 slice 助手,project-group/create 仍写入唯一的持久记录,检出目录成为同一逻辑项目组的主要根(ADR 0233)。
  • 渲染器加一条窄的主进程能力:协议、schema、host RPC、权限、存储与偏好均无变化。见 ADR 0273、03-runtime/01-ipc-protocol.md §9、04-ux/08-component-spec.md 与 E2E-258。

2026-09-16 —— 可拖拽的对话内容宽度(D439) ​

  • 居中的对话带、空首页和输入框共用一个首选最大宽度,默认 760px,持久化为 AppSettings.chatContentMaxWidth。左右边缘各一条手柄(静止不可见;悬停显示短胶囊,拖拽时略加长加深)同步改这个宽度,列保持居中。拖拽下限 560px。

  • 实际宽度是 min(可用窗格减去两侧 24px, 首选值),侧栏或工作区挤压时自适应压缩,不改写偏好。收起侧栏不再把内容带到 640px。

  • 助手/工具/决策行跟随内容带;用户气泡仍是 min(82%, 600px)。仅渲染器。见 ADR 0277、E2E-208、E2E-CHAT-content-width-handles。

2026-09-17 —— 窗口开关键避开 macOS 的关闭窗口组合键(#360,D439) ​

  • D438 把两个键合并到了 Mod+W,但这个键不能作为系统级全局加速键的默认值: macOS 把 Cmd+W 用于自己的「关闭窗口」命令,全局占用它等于从所有其它应用程序 手里把这个组合键抢走,而不只是关闭本窗口。因此 Mod+W 正是开关键唯一不能持有的键。
  • 快捷键目录现在把 toggleWindow 发布在 Alt+Shift+W 上 —— 不含平台修饰键,因此在 macOS、Windows 与 Linux 上是同一个组合键 —— 它与任何发布默认值、保留的编辑组合键 以及平台命令都不冲突。合并本身、隐藏/显示语义、以及退役的 closeWindow/summonWindow 覆盖项的折叠规则,完全保持 D438 定义的样子。
  • 在 macOS 上 Mod+W 现在也拒绝交给插件(isReservedKeybinding),因此即使应用不再 使用它,平台仍然独占它自己的这个组合键。
  • 只重复「本版本发布默认值」或「已被取代的默认值」的 toggleWindow 存储值不携带用户 意图,折叠时会丢弃:曾经持久化过那个短命 Mod+W 默认值的配置会迁移到 Alt+Shift+W,而不是把 macOS 的组合键冻结住;真实改绑(Ctrl+Alt+T)与明确的 「未绑定」(null)原样保留,退役项的自定义值也仍然优先于被丢弃的旧默认值。
  • 编号取 D439,因为 D435 与 D437 已被其它在途工作占用。注意:本日志当前有两处 D438 —— 本文件上方的窗口开关键合并决策(#360,重绑前)与紧随其后的 Git 检出决策 (ADR 0273)。后者是在开关键 D438 合入之后才追加的,编号需要由其作者改到 D440 或 之后的可用号;本条不代为改写他人决策正文。
  • 见 ADR 0211(再次修订)、04-ux/09-interaction-patterns.md §1.1 与 §1.4、 04-ux/06-settings-ia.md(快捷键选项卡)、07-plugins/03-plugin-api.md、 07-plugins/04-plugin-security.md、E2E-072 与 apps/desktop/test/window-toggle-shortcut.test.mjs。

2026-09-17 —— 会话与项目行在第二次点击时才删除(D441) ​

  • 侧边栏的会话菜单项与两个项目菜单(侧边栏与项目索引)此前在第一次点击「删除」时就移除该行。应用内其他所有破坏性行操作 —— 设置里的模型服务行、厂商账户行以及能力表格 —— 都先武装再改写控件标签,于是最不能撤销的两个动作反而是唯一单击即生效的。
  • 这一模式现在由 hooks/use-armed-delete.ts 为整个渲染层拥有:同一个 ARMED_DELETE_MS(3200ms)失效时间与同一个 useArmedDelete()。能力页面继续从 AgentCapabilityLayout 导入它,而该布局改为再导出这个共享 hook,不再保留一份带自己超时的副本,因此这一模式不会在设置行与两个菜单之间漂移。
  • 会话菜单项以 session.id 作为武装键;两个项目菜单则以 project: 前缀的键武装,因此会话与项目永远不会共用一次武装。被武装的菜单项带 data-armed="true"、危险色淡底,并在八种语言里都把标签换成 nav.deleteTaskConfirm / project.deleteMenuConfirm("Delete?" / "确认删除?")。两次点击之间菜单保持打开;点击外部、按 Escape 或武装超时都会解除武装且不移除任何内容。
  • 这是对 D431 在空闲场景下的修订:仍有运行中轮次的项目依然打开 ProjectDeleteDialog,由它指明这些会话并先停止它们再删除;宿主的 1008 / CONFLICT 拒绝在两条路径上仍映射为 project.deleteRunningBlocked。没有运行中轮次的项目由对话框原本使用的同一个 store 动作移除,并发出同样的成功 toast,因此它的第二次点击就是用户已经给出的确认。
  • 编号取 D441:D438 已被两处占用 —— 窗口开关键合并(#360,D438 与后续的 D439 重绑)与 Git 检出决策(ADR 0273),后者本日志已标明要改到 D440。本条不 代为改写他人决策正文。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动,也没有新增默认值。磁盘上的文件夹仍永远不被触碰。见 04-ux/09-interaction-patterns.md、D421、D431、06-delivery/04-e2e-test-plan.md 的 E2E-PROJECT-delete-removes-project-and-owned-sessions,以及 apps/desktop/test/two-step-delete.test.mjs。

2026-09-16 —— 侧边栏列表节奏为 1px / 2px / 8px ​

  • 侧边栏的会话列表原本没有可供阅读的节奏。行与分组标题在一个分组内紧贴(0 间隙),两个项目分组相距 2px,唯一的留白是分区标签上方的那 4px。相邻的 28px 行绘制的圆角悬停填充彼此相接,于是两行读起来像一整块,折叠的分组与相邻分组的距离,和一个展开后带十行的分组一样远,而分区标签距离它上方的行,比距离它所引出的行更近。
  • 这些列表现在陈述同一条阶梯:行与行之间相隔 1px,项目分组标题与分区标签位于其首行上方 2px,侧边栏分区位于其上方分区之下 8px,展开的项目分组带有一段 8px 的尾部。
  • 该尾部属于展开的分组本身,因此间距仅由前一个分组决定:展开的分组之后是 8px,无论下一个分组是展开还是折叠,折叠的分组之后则是 1px,两种情况都一样。滚动容器自身的间隙保持统一的 1px,差异存在于分组主体的内缩量中,它会随行一起消失。因此折叠一个分组既不会让间距取决于相邻分组的状态,也不会让它自己的尾部突然跳变。
  • 项目分组内的日期标签是该节奏中的普通行,而不是第二层分组:它们不保留状态、不保留展开披露,也没有 aria-expanded,并且它们自身的内缩量改为对称的 4px,而不再是上方 6px、下方 2px,于是它们不再盖过 1px 的行间隙。
  • space-0.25(1px)作为密集列表节奏的发丝级步进加入间距比例;设置栏在其导航项之间本来就已经使用 1px。节间隙规则不再声称适用于侧边栏,侧边栏有自己的节奏。
  • 两个行预算随该节距变化:固定主体仍显示八行(233px,原为 224px),独立的会话主体仍显示五行(146px,原为 140px),因为 28px 的行网格已无法把自身的间隙容纳进旧的数字里。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动。见 04-ux/07-ui-design-system.md §6.1 与 §13,以及 04-ux/08-component-spec.md §6.2 与 §6.6。

2026-09-16 —— 侧边栏项目分组以单行网格折叠 ​

  • 上述节奏上线时,分组折叠使用的是 300ms 慢速时长上的 max-height: 2000px → 0,外加一段独立的 200ms opacity 淡出与 padding 过渡。max-height 的曲线大部分长度都落在内容高度之上,于是可见的位移只是末尾的一小段跳变;而更短的 opacity 曲线在约三分之二处就把分组清空了:行先消失,空盒子随后才合上。折叠一个项目读起来像是两段彼此分离的动画。
  • 分组主体现在是一个单行网格,其行在 200ms 的正常时长内从 1fr 动画到 0fr。每一帧都是该分组实测高度的真实比例,因此折叠是一次连续运动,并且整个折叠过程中 opacity 始终为 1 —— 行是被裁剪掉的,不是淡出的。
  • 主体由三层组成:网格层、带 min-height: 0 与 overflow: hidden 的裁剪层,以及持有 1px 行间隙与固定 2px / 7px 内缩量的列表层。内缩量必须位于裁剪层内部,因为动画盒上的垂直内边距会把 0fr 行撑开,并在行消失后留下分组的尾部。
  • 折叠的分组仍把行挂载在该 0fr 行内,因此它同时是 inert 与 aria-hidden,并离开 Tab 顺序。在 prefers-reduced-motion: reduce 下,折叠保留两端状态并以接近零的时长完成。
  • 滚动预算、缩进、固定与独立列表,以及共享的 .sidebar-session-group-body 弹性列都保持不变;只有项目主体以网格折叠。
  • 仅渲染层:无协议、存储、宿主、权限或迁移改动。见 04-ux/08-component-spec.md §6.2 与 06-delivery/04-e2e-test-plan.md 的 E2E-LAYOUT-sidebar-project-group-fold。

2026-09-17 —— 侧边栏选中态与工作区上下文分离 ​

  • 项目标题与项目、置顶、独立会话行共享整行悬停背景、圆角和过渡;标题按钮透明,会话选中背景优先于悬停,不再叠加第二层背景。
  • 项目是工作区和折叠控件,不是独立选中的页面。当前工作区保留圆点及业务行为,但不再绘制持久选中背景,没有选中会话时也不例外。上下文由 data-current-workspace 标记,不再使用项目的 active 类。
  • 会话选中仍以聊天页及正在切换的目标为准;折叠分组不转移选中态。焦点轮廓、操作按钮反馈、窗口失焦释放和拖拽目标优先级保持独立。
  • 仅改变渲染表现,不改变持久化、工作区激活、权限或协议。见 04-ux/01-ui-ia.md、04-ux/08-component-spec.md、04-ux/09-interaction-patterns.md §9.1c 及 06-delivery/04-e2e-test-plan.md 的 E2E-LAYOUT-sidebar-row-states。

2026-09-17 —— 设置共享侧栏材质,返回直接恢复布局 ​

  • 主侧栏与设置导航共享 sidebar-surface:Windows/Linux 共享不透明颜色和背景图,macOS 共享现有原生 vibrancy、tint 与 sheen。设置外壳允许材质透出,右侧内容与顶部条保持不透明;仅内容内部播放入场动画。
  • 旧 --ds-settings-rail-bg 作为共享颜色的可选回退保留,显式 --ds-bg-sidebar 优先;不修改主题或原生窗口 API。
  • 侧栏挂载不再等同于展开。专用过渡 hook 只响应可见主界面的折叠状态变化,设置导航取消未完成阶段,返回恢复既有宽度和状态,不重播 sidebar-in。动画事件防护、超时清理、自动恢复及减少动态效果仍保留;首次显示由原有启动揭示处理,不再叠加侧栏宽度动画。
  • 不改变 item 缩进、宽度、工作区状态、数据或权限。见 04-ux/06-settings-ia.md、04-ux/07-ui-design-system.md、04-ux/08-component-spec.md 及 06-delivery/04-e2e-test-plan.md 的 E2E-LAYOUT-sidebar-settings。

2026-09-17 —— 路由表面在入场动画结束后不再持有堆叠上下文 ​

  • 插件安装同意模态框 —— 以及随后的插件详情抽屉 —— 原本绘制在不透明的 46px 标题栏带之下,因此该栏没有被模态遮罩覆盖,栏内控件仍然可点击。路由页面挂载在 .route-surface 内,而它的入场动画带有 animation-fill-mode: both:填充让已结束的入场动画在页面挂载期间始终「生效」,从而让该表面持续成为堆叠上下文,页面内渲染的浮层因此无法绘制到窗口 chrome 之上。
  • .route-surface 与 .settings-content-enter 的入场动画不再声明 forwards / both 填充(apps/desktop/src/styles/chat-shell.css)。入场动画的终点就是元素自身的状态,因此该填充本是多余的;去掉之后,入场结束时表面不再持有堆叠上下文,路由页面内的浮层重新绘制在标题栏带之上。关键帧仍然只有 opacity,入场动画照常播放。
  • 插件详情抽屉保留顶部的 --ds-toolbar-height 预留(apps/desktop/src/styles/plugins.css):在 Windows/Linux 上该右上角归渲染器绘制的窗口控件所有(z-index: 200),它们位于任何浮层的 z-index 之上。
  • 插件浮层从刻度外的 80 与 60 归回到 z-dialog(40)(plugins.css,发行说明浮层一并处理):路由表面不再困住它们之后,它们就在根堆叠上下文中参与比较,而在 80 上会盖住对话框自身弹出的叶子级弹出层(60)与 toast(50)—— 其中包括安装对话框的「已复制」提示。现在有源测试钉住这一关系。
  • 仅渲染层:无 IPC、存储、schema、协议或插件 SDK 改动,也没有新增默认值。见 04-ux/07-ui-design-system.md §9、apps/desktop/test/settings-dialog-overlay.test.mjs,以及 06-delivery/04-e2e-test-plan.md 的 E2E-LAYOUT-three-column-width-priority(其四条浮层断言位于 scripts/e2e-three-column-layout.mjs)。
  • 未做、留作后续的事项:真正的模态 —— 不可交互化、焦点收束、快捷键让位 —— 需要这些浮层进入浏览器顶层(<dialog>.showModal())。在它们打开的弹出菜单、工具提示与 toast(portal 到 document.body)同样迁入顶层之前,这一步无法进行,因为顶层内容会绘制在它们之上并使其不可用;此前的一次尝试正因此被撤回。

2026-09-19 —— MCP 目录由协议护栏约束,而不是被截断 (D452) ​

  • 修订 D176 / ADR 0038:每服务器的 tools/list 上限不再是「64 个工具、8 页」的静默截断。客户端现在在 Codex 同级的协议护栏下跟进分页直到最后一页 —— 2048 个工具、100 页、重复或畸形游标、整轮遍历 30 秒 —— 突破任一护栏的服务器会失去它的握手,而不是贡献其目录的一个前缀。
  • 该上限从来不是对提示词的保护:MCP 工具以 ToolSearch 之后的延迟按需条目到达模型,宣传它们的提示词区块另有独立上限,因此一个 300 个工具的服务器在被激活之前不产生任何成本。
  • plugin.mcp.tools.truncated 已移除。拒绝会以 plugin.mcp.connect / mcp.connect 记入审计并带上护栏的错误码与消息,对用户自建的服务器则显示为其状态消息。
  • 见 ADR 0038 与 E2E-024K;apps/desktop/test/plugin-mcp.test.mjs 覆盖完整目录、各项护栏,以及把旧的截断行为作为失败基线。

2026-09-19 —— 移除设置页面的语音卡片(ADR 0291) ​

  • 设置 → AI 不再有语音区块:VoiceSettingsCard 与 features/settings/voice-settings.tsx、AI 目的地的 settings.speechTitle / settings.speechTranscribe / settings.speechSynthesize 搜索关键词、.settings-speech-* 样式、八种出厂语言各十三条 settings.speech* 文案,以及为已撤回 Composer 控件所写、无任何引用的七个 chat.transcribe* / chat.speak* 文案全部删除。
  • 宿主能力保留:speech/getStatus、speech/transcribe、speech/synthesize、AppSettings.speech 校验、两个内置协议、speech.adapter.register 插件权限与渲染器 API 桥接均未改动。绑定由调用方通过宿主设置接口写入,插件与 IPC 调用是其唯一消费方。
  • 早已撤回、本次一并修正文档:ADR 0281 第 5 条中的 Composer 转写与朗读入口(其麦克风与朗读控件已于 2026-09-18 在 344ef4ec2/PR #555 中移除),以及 04-ux/08-component-spec.md §2.5 描述的 Composer 语音控件;为它们写下的文案随之退役。
  • 仅渲染层:无 IPC 通道、存储 schema、宿主 RPC、权限或 Rust 改动,也不会丢弃已存绑定。见 ADR 0291、04-ux/06-settings-ia.md、03-runtime/20-speech.md、E2E-008e。

2026-09-19 —— 远端主机的 SSH 引导(D453) ​

  • 桌面用系统 ssh 客户端在用户已能通过 SSH 到达的机器上安装并配对 pi-host,因此 ~/.ssh/config、agent 与跳板机照常生效,应用不持有任何 SSH 密钥;BatchMode=yes 让需要交互式密码或口令短语的主机立即以带类型的错误失败,而不是把模态框吊在一个不可见的提示后面。
  • 桌面按远端平台、以自己的版本解析 pi-host 包,持有发布随附的 SHA-256,并在任何下载之前拒绝未发布的目标(目前只有 linux-x64)。上传的脚本在远端 $HOME 下下载、校验并安装该包;SSH 通道上不传输任何可执行字节。
  • 脚本以 umask 077 运行并回显 PI_HOST_READY / PI_HOST_PAIRING_TOKEN,因此一次性配对令牌只存在于工作文件和 SSH 通道上,既不落在全局可读路径,也不出现在 URL 中(安全规格 §3.4)。PI_HOST_READY.version 必须与桌面版本一致;不一致为 HOST_VERSION_MISMATCH(D375),并在建立转发之前就已检查。
  • 已配对主机的记录改存 SSH 描述符(metadata.transport = "ssh")而非 URL,因为本地转发端口在每次启动间并不稳定;隧道管理器在每次启动时重新建立 ssh -N -L,并收编(adopt)引导自己打开的存活转发,使配对只建立一条隧道。描述符在每次读取注册表时都会重新校验,格式不合规时降级为「非 SSH 主机」,而不会用垃圾参数去 spawn ssh。
  • pi-desktop/remoteHost/bootstrap 加入 list / pair / remove,connection/pair 交换被抽成单一的 exchangePairingToken,粘贴 URL 与 SSH 引导两条路径共用。
  • 本次不做:终端工作面板客户端(Stage 5)、反向工具中继(Stage 6)、resync 看门狗、非 Linux 与 Windows 远端目标,以及经 SSH 通道下发 provider 配置 —— 刚引导好的主机在配置 provider 之前,turn/start 仍以 MODEL_NOT_CONFIGURED 关闭。见 ADR 0292、06-delivery/07-remote-control-rollout.md §7 与 05-security/02-remote-control-security.md §3.4。

2026-09-20 —— 项目存档改为列表 + 检查器(D455) ​

  • 决策 D455 修订 D267 的展开行:设置 → 项目存档保留安静引导行和工具栏,把单一展开列表改成精简行,检查器在选中行下方铺满宽度(ADR 0294)。
  • 单击行会选中并留在设置页。双击、Enter 或检查器的打开操作激活项目并返回聊天。归档和关闭仍使该目的地保持打开(D133 / D267)。
  • 文件夹、对话(按最新活动、每批 8 条)和原有菜单放在检查器中。归档记录仍分组可见。
  • 仅演示层:无 IPC、存储或宿主协议改动。见 04-ux/06-settings-ia.md 与 E2E-038。

2026-09-20 —— 提示词增强模型卡移到设置 → AI ​

  • 修订 ADR 0121 与 2026-09-18 D447 的放置:增强提示词卡(默认模型 + 思考强度)离开设置 → 模型, 放到设置 → AI,紧挨提示词增强之后,让同一操作的模板与改写模型在同一目的地。该卡仍自带标题, 因为模型选择器需要标题 / 当前值 / 控件。设置搜索在 AI 页索引这两行。仅演示层:无 IPC、存储或 宿主协议改动。见 04-ux/06-settings-ia.md 与 04-ux/12-prompt-enhancement.md §5。

2026-09-20 —— 提示词增强合成一张设置 → AI 卡 ​

  • 修订同日的两卡放置:增强提示词卡并入设置 → AI 的提示词增强。自定义模板开关、默认模型与思考强度共用一个标题。设置搜索仍索引 promptEnhancementModelTitle,因此搜「增强提示词」仍会落到 AI。仅演示层:无 IPC、存储或宿主协议改动。

2026-09-20 —— 会话思考参数不发送(D456) ​

  • Composer、会话存储和宿主校验在七个规范档位之外接受 thinkingLevel: omit。 绑定芯片与目录能力列表保持规范档位;omit 是客户端选择,不是已发布档位。
  • 运行时钳位在推理模型上保留 omit。智能体记账仍为 off,父级流与子智能体 共用低层 omit 路径,适配器不发送思考字段。显式 off 仍关闭思考。
  • Composer 推理菜单在所选模型有已启用规范档位时把 omit 放在最前,芯片渲染 规范字符串。设置页推理绑定的 defaultThinkingLevel 也接受 omit,新会话 可以不发送思考覆盖。Schema v19 重建 sessions 以使 CHECK 包含 omit。见 ADR 0295 与 E2E-203a。

2026-09-19 —— 丢弃 outbox 毒消息;接受投递回合内的 steering(D597) ​

  • 决策 D597 修订 ADR 0041、ADR 0239、ADR active-turn-steering 与 D444。 宿主追加错误消息带 PERMISSION_DENIED: 前缀时是永久的、消息级拒绝。 Electron outbox 丢掉该行并继续排空,避免一条毒消息把 1024 上限填满。 PLUGIN_PERMISSION_DENIED 这类子串以及其他宿主失败仍暂停。
  • 向已认领的协作投递回合做 steering 是该会话里额外的人类输入:不受投递 内容/附件契约约束,不继承投递来源,并清掉客户端带来的 session_message。 指向另一会话的 steering 仍是 PERMISSION_DENIED。
  • 见 03-runtime/04-data-storage.md、03-runtime/06-host-rpc-protocol.md、 E2E-SESSION-outbox-poison-does-not-drop-history。

2026-09-20 —— 项目档案呈现为内嵌分组卡片列表 ​

  • 仅修订 D455 的呈现:该目的地仍为单列,详情在选中行下方,没有并排面板; 但索引改为 iOS 意义上的内嵌分组列表。每行自左向右是身份(颜色字形、项目名、 一个状态标签、缩短的等宽路径),自右向左是细节(会话计数、相对上次活动时间、 展开指示器)。行仍以行间距分隔,从不使用分隔线。
  • 选中的行就是它自己卡片的表头,因此详情面板不重复名称、路径或状态标签: 它先是操作栏(新任务、项目不是当前工作区时的「打开」、更多菜单), 接着是只读的文件夹与分支信息、会话计数,最后是会话列表。更多菜单的条目、 顺序与两步删除保持不变。
  • 方向键选中改为沿渲染顺序(分组顺序 + 当前排序)移动,而不是索引构建顺序,并把真实 焦点移到该行;该处理器会让位于展开的卡片,因此详情内的 Enter 仍是该控件自身的操作。 行元素 id 经过编码,只靠分隔符区分的两条路径不会再撞车。索引为空时在宿主列表返回前 显示等待指示器,而不是「暂无项目」;展开的卡片与展开指示器遵循减少动效设置。
  • 再次单击已展开卡片的那一行会收起卡片,走同一个共享切换函数。选中与卡片展开是 两个独立状态:当前行在卡片收起时仍保留选中环,展开指示器只在卡片真正展开时 转向下方。用方向键移动选中时总会展开落点所在的那一行。 仅演示层:无 IPC、存储、宿主协议或激活语义改动。见 04-ux/06-settings-ia.md、 04-ux/07-ui-design-system.md 与 ADR 0294。

2026-09-20 —— 项目档案默认收起,且不再带页面级说明文字 ​

  • 该目的地不再呈现说明行:工具栏是页面标题下的第一个元素,形态与智能体能力页面和 导入目的地一致。project.archiveSubtitle 键从全部语言目录中退役,而不是留作未使用。
  • 用户单击某一行之前不展开任何内容。选中与卡片展开原本是两个状态,现合并为一个: 收起的索引不高亮任何行,aria-expanded 与旋转的展开指示器只描述展开的卡片, 项目从列表中消失(搜索不再匹配)时卡片随之关闭。resolveSelectedPath (总会解析出一个选中项:优先当前工作区、其次首行)随它所服务的自动展开一并移除。
  • 仅演示层:无 IPC、存储、宿主协议或激活语义改动。搜索仍匹配会话标题,并让所属项目 留在索引中。见 04-ux/06-settings-ia.md。

2026-09-20 —— 菜单根层直接承载推理滑块(#417,D458) ​

  • Composer 的推理等级此前只能在一个子菜单里的竖向单选列表中切换。Issue #417 希望参考 Codex 桌面版做成滑动条,同时保留原有的「点击值标签进入下拉列表」交互。
  • 合并后的「模型 × 推理」菜单根层现在直接展示一个原生 range 输入,每个已启用等级一个刻度,并配有可点击的刻度标签,位置就在「推理等级」条目正下方。拖动滑块或点击刻度都会通过同一条 configureActiveSession 路径立即提交等级,并停留在菜单原处,因此临时调整不必再往子菜单里跑一趟。「推理等级」条目本身仍然打开经典单选列表,保留其单选语义、末尾勾选、上/下/Enter/左键契约与返回根层的行为。
  • 滑块在获得焦点时自行掌管方向键/Home/End/Enter,因此这些按键用于调整等级而不再驱动菜单导航,Escape 仍然关闭菜单。拖过多个刻度只保留最后一次待提交的档位,提交按 latest-wins 串行,空闲会话的持久化不会把中间档写进去;会话或模型变化会作废未完成的提交。本地拖动前导值避免受控输入在 store 确认回写之前被弹回。刻度标签可点但不进入 Tab 顺序。当菜单只列出一个等级时,滑块整体隐藏。
  • 等级值仍是未翻译的规范字符串,在绑定暴露已启用规范档位时也包括会话级 omit(D456)。七级阶梯、提供方过滤与钳制规则均未改动;仅渲染层:无协议、存储、宿主、权限或迁移改动。见 04-ux/08-component-spec.md、04-ux/07-ui-design-system.md 与 E2E-050。

2026-09-20 —— 开发构建是一个独立安装(D599) ​

  • 正式打包版与 pnpm dev 不再共享定义「一个安装」的那两个目录。开发构建运行在操作系统应用数据根目录下的 PI-Desktop Dev(即独立的 Electron userData,随之独立的单实例锁、渲染层 localStorage、插件面板 partition 与浏览器面板 Cookie),数据目录为 ~/.pi-desktop-dev。正式安装仍保持 PI-Desktop 与 ~/.pi-desktop 不变,既有 profile 不会被搬迁。
  • 因此已在运行的正式版不再拒绝开发启动所需的锁,两者也不会把两个 host-core 压到同一个 pi.sqlite、同一个持久化 outbox 或同一棵日志树上。PI_DESKTOP_DATA_DIR 仍然优先于两种 profile,并且在作为子进程环境变量传给 host-core 之前被绝对化;Electron 主进程把解析结果回写到该变量,使插件运行时读到同一个根目录,而不是回退到正式版默认值。
  • 显式 --user-data-dir 仍然优先,E2E 装置正是用它把构建指向临时 profile。
  • 决策 D599 收窄 D236 并修订 ADR 0094:锁仍以「安装」为作用域,而开发构建现在是第二个安装,不再是一个含义模糊的第二个进程。未改动任何 IPC 通道、宿主协议、存储 schema 或正式安装的路径。见 03-runtime/07-process-model.md、03-runtime/04-data-storage.md 与 02-architecture/03-repo-structure.md。

2026-09-20 — 应用不再随包发布任何字体(D598) ​

  • 决策 D598 修订 D232 / ADR 0083。四款内置字体(Geist、Inter、Noto Sans SC、LXGW WenKai)、 它们的 woff2 文件与 OFL 许可证文本、styles/fonts.css 及其 @import 全部删除, BUNDLED_FONTS、BundledFont 类型、FontOption.license、选择器的许可证角标, 以及全部八份目录中的 settings.fontBundled 键一并移除。
  • 设置字体选择器的形态不变:「跟随系统」持久化为空字符串,其后是 Electron 主进程 通过 pi-desktop/app/systemFonts 枚举的系统已安装字体(仅用平台工具,去重、过滤、排序、 缓存 60 秒)。bundled 分组消失;不再匹配任何选项的已保存字体栈仍排在列表中 「已保存」分组的首位。
  • 每个生成的字体栈追加纯系统 CJK 回退层 "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif,前面不再有内置字体。此前保存的指向内置字体的字体栈 (例如 "Geist", "PingFang SC", …)按原样保留,显示在「已保存」分组,并穿过该回退层 渲染。无迁移、协议或 schema 变更。
  • 渲染器产物不再产出任何应用字体面,只剩 KaTeX 的 woff2 字形。见 ADR 0298、 04-ux/06-settings-ia.md、04-ux/07-ui-design-system.md 与 E2E-126。

2026-09-20 —— 崩溃转储留在数据目录(D602) ​

  • Crashpad 在 ready 之前以本地模式启动(uploadToServer: false)。转储放在 <data_dir>/crash-dumps,而不是 Electron 默认的 userData crashDumps 路径, 因此 PI_DESKTOP_DATA_DIR profile 不会与其它安装共用转储。标记文件 crash-dumps.json 记录已经报告过的最新 mtime。
  • 下一次持有单实例锁的启动会为新于该标记的转储写一条 diagnostics 记录, 按 Crashpad 的 ptype 注解分类:任一新转储属于 browser/main 进程则为 error,已恢复的 renderer、GPU 或 utility 崩溃为 warn。扫描失败是一条 crashDumpReportFailed 警告,永不挡住第一扇窗口。
  • host-core 与 sidecar 崩溃仍走监督器路径以及 host / agent 日志通道。 什么都不上传。见 03-runtime/07-process-model.md §4、 03-runtime/09-logging-and-observability.md 与 03-runtime/04-data-storage.md。

2026-09-20 —— Composer 下方的停靠区遮住正文绘制(D603,issue #728) ​

  • 正文滚动容器占满会话面板,内容通过实测 Composer 高度预留底部空间。此前绝对定位的 停靠区是透明的,因此正文行可以继续绘制到悬浮输入框下方,并从底部空隙或圆角外侧露出。
  • .composer-dock-docked 现在横跨整个宽度绘制不透明的 --ds-bg-primary 工作区表面。 Composer 原有的半透明高架令牌、圆角、阴影和滚动预留保持不变;底层遮罩只移除越过 Composer 边界的正文绘制。
  • 仅改动渲染器 CSS 与主题表面回归测试;不改变滚动状态、协议、持久化、主题 schema 或权限。见 04-ux/08-component-spec.md 与 E2E-CHAT-opaque-floating-decision-and-retry-surfaces。

2026-09-21 —— 上下文估算采用保守校准(D606) ​

  • 所有预算阈值读取同一个数字,该数字按请求的真实开销校正。pi 的估算器以最后一条助手用量为锚、其余按 chars / 4 估算, 这会低估中文文本约 2–4 倍;没有锚点时还会完全漏掉系统提示与工具结构。
  • 两类误差分开应用:逐字符偏差是按比例(与量级无关)作用于猜测尾部;无锚点残差只在量级相当的样本(0.5×–2×)上按比例 应用,否则只加观测到的固定开销(上限 32,000 词元)。因此 100k 量级的样本不会作为比例系数套到 1M 的投影上。
  • 校正不对称:向上修正有三个观测即生效;向下修正需要三个方向一致的样本、每次最多 15 %,且不会低于原始估算的 85 %—— 处在硬限制 1.18× 的投影仍会压缩。偏离预测 0.5×–3× 的报告视为误报,连续两次误报冻结向下修正。 见 03-runtime/02-agent-runtime §5.1。

2026-09-21 —— 每个请求里的工具调用 id 必须唯一(D608,issue #718) ​

  • Anthropic 系端点(含 DeepSeek)在请求中同一个调用 id 出现两次时,会以 tool_use ids must be unique 拒绝整个回合, 期间该会话无法继续。转录是仅追加的快照流、容忍重试造成的重复追加,因此同一次调用可能两次进入组装后的上下文:同一个行 id (宿主按 keep-last 读取时已折叠)或两个不同行 id(宿主无法折叠)。
  • 因此上线前的最后一个视图对每个 toolCall id 只保留第一次出现,丢弃其后重复的调用或结果,使提供商校验的「一调用一结果」 配对保持完整;没有重复的请求原样返回(返回同一对象,而非副本)。磁盘上的内容不会被改写,压缩与保留规则也不变。
  • 一旦发生丢弃,会在 agent 日志通道上报告一次,带上会话与 id,使下一次同类报障能指向写入方而不只是提供商那句话。 见 03-runtime/02-agent-runtime §5。
  • 守卫刻意放在请求边界而不是历史重建处:这样也能覆盖会话运行期间产生的重复,而重建期的过滤看不到它。

2026-09-21 —— 插件崩溃上报带上退出码但不复制原始输出(D607,issue #747) ​

  • 宿主进程崩溃路径此前只报插件 id,于是报障里只有 plugin host process exited: <id> 一句话——issue #747 的报告者手里 就是这么一句。现在运行时代码本来就已经拿到的退出码会出现在消息、failed 服务状态、plugin.crash 审计记录与 plugin 日志通道里;Windows 上的硬故障(0xC0000005 一类,以负的有符号整数交付,同时打印其无符号十六进制形式)与 插件自己调用 process.exit(1) 是两类不同的问题,而这个字段是唯一能区分它们的。
  • 插件 stdout/stderr 不复制进加载错误、崩溃审计记录或崩溃日志负载,因为其中可能包含工作区数据或敏感信息。不新增任何持久化,权限与 API 面不变; 既有的 plugin.stdio 审计流保持不变。
  • 见 07-plugins/05-plugin-lifecycle.md §3.1。

2026-09-20 —— 新建项目的名称默认取主要文件夹(D609) ​

  • 新建项目对话框原本要求先填名称才能创建,本地选文件夹时不得不先编一个标题; Git 来源此前已用仓库名预填该字段(D438、ADR 0273)。
  • 名称字段现在对两种来源都可选。它按所选来源预填——本地来源取第一个(主要) 文件夹的名称,Git 来源取仓库名——用户一旦自己输入就不再跟随来源。创建按钮 只由来源决定是否可用(一个文件夹,或解析成功的地址加保存位置),字段留空时 回退为同一个默认名。
  • 派生名称会截断到 MAX_PROJECT_NAME_CHARS(80),以保证侧边栏偏好持久化和 名称字段都能原样接受。文件夹名解析移到 apps/desktop/src/lib/project-name.ts,与对话框的文件夹行共用。
  • 仅渲染器改动:不改协议、存储、宿主、权限或迁移,也不新增默认值。见 04-ux/08-component-spec.md、ADR 0233、ADR 0273、E2E-012a / E2E-256。

2026-09-21 —— 存储的模型绑定数组逐条读取(D610,issue #784) ​

  • config_json.models 是提供商模型列表唯一的存放处,而它不止一个写入方:设置保存、插件声明,以及对存储行的外部编辑。此前它被当作单个 Vec<ModelBinding> 解码,于是只要有条目不再符合 schema,全部兄弟条目都会被丢弃——之后该提供商读回来只剩由 defaultModelId 派生的那一个 legacy 默认模型,其它已配置模型全部消失,且没有任何说明。报告的复现正是删掉某一个绑定的 maxTokens:库里 12 条绑定,读回来 1 条。
  • 因此 contextWindow 与 maxTokens 在线为可选,并且数组逐条解码。缺失的键、或显式的 0,都读作 0 并由既有的零值归一化补上通用默认值(128,000 / 8,192)——这与只带 defaultModelId 的记录被物化时用的是同一个值,也与未声明限额的插件清单已经产生的值相同。两条路径从此一致,而不是一条拒绝另一条接受的东西。
  • 仍然解码失败的条目只损失自己,不损失整个数组:可读条目按存储顺序保留,失败会在宿主日志里上报,带上提供商 id、条目下标与原因,因此可定位而不是静默。缺失的 models 键、空数组、以及所有条目都不可读的数组,都仍旧读作 legacy 绑定,使提供商无论存储形态如何都保持可选;只有第三种会被上报,因为空数组是合法状态,而缺失键则是早于绑定机制的记录。
  • 不改写磁盘上的任何内容,写路径仍是显式的:providers.update 依旧只持久化客户端发来的绑定。接受显式模型数组替换前,host 会检查存储值;若已降级则以 MODEL_BINDINGS_DEGRADED 拒绝,避免设置页的部分视图擦除不可读条目;不涉及模型数组的提供商字段仍可更新。见 03-runtime/12-provider-config-schema.md §2。

2026-09-21 —— 手输的自定义模型 id 从模型库播种(D611) ​

  • 通过 id 新增自定义模型时,即便模型库已经发布了该 id,每条绑定仍只得到通用的 128,000 / 8,192 种子值与空思考等级,用户不得不重新输入本可以自动获得的限额。
  • 设置选择器现在通过新的只读快照渲染器通道 providers.lookupModel 把输入的 id 与 models.dev 匹配,并按拾取模型的口径播种绑定——已发布的上下文窗口、最大输出 token 与思考等级——同时存储的 id 仍保持用户输入的原文。记录先以通用种子值落下,答案返回 后再原地升级;因此查询慢、失败或未发布时仍然只留一行可用记录,期间的编辑或删除也 不会被覆盖。
  • 不访问提供商网络、不调用主机、不改 schema、不改持久化数据;未命中时行为与之前完全 一致。参见 03-runtime/12-provider-config-schema.md §2 与 §9。

2026-09-21 —— 新增提供商不再抢走应用默认值(D612) ​

  • 新增提供商的流程无条件写入 defaultProviderId/defaultModelId,画图流程也无条件 写入 imageGeneration,于是配置第二个服务会把用户正在使用的默认模型、默认画图模型 悄悄换掉。
  • 新增流程改为与编辑一致的规则:已存储的默认值只要能解析就保留,只有无法解析时才被 替换。模型默认值的“能解析”指默认提供商行仍存在且仍提供该模型——与设置页摘要行渲染 用的是同一套解析——因此提供商被删除后残留的 id 不算默认值,新增的提供商可以填补那 个本来显示为未设置的位置。画图默认值用镜像规则判断其存储绑定,候选列表仍会累积新提 供商的图片模型,选择器里不会少行。
  • 只有在没有任何默认值可解析时才写设置;显式的“设为默认”操作、编辑路径,以及删除已 选模型后回落到首个剩余绑定的行为都不变。行为由 apps/desktop/test/default-model-display.test.mjs 与 apps/desktop/test/image-generation-default.test.mjs 固定, provider-model-config.test.mjs 断言新增分支会走这两个判断。

2026-09-22 — 循环拥有自己的上下文数组(D620) ​

  • 当某个回合的较晚迭代跑了工具后,state.messages 会把那些迭代追加的每条消息存两份,而下一回合的首个请求正是用该数组组装的。 其中“带文本与工具调用的辅助消息”的重复项在请求守卫(D608)之后残留为只剩文本的克隆,卡在调用与回答它的结果之间;pi-ai 随后会为 这个仍处于待配对的调用合成一条 “No result provided” 输出,与真实结果并列,于是同一个 call id 带上了两条输出,端点以 Duplicate tool output for call_id(HTTP 400,不可重试)拒绝整个回合。该数组会被复用,因此其后每个回合都以同样方式失败, 继续 也无法让会话恢复。
  • pi-ai 的循环会把每个流式辅助消息与每个工具结果 push 进它拿到的上下文,而它自己的 message_end 监听器又把同一个消息对象 push 回 state.messages。rebuiltAgentContext() 返回的正是这个活数组,两次 push 因此落在同一个数组里;现在它返回副本,与 pi 自己的 createContextSnapshot() 在 prompt() 与 continue() 里的做法一致。委托侧的回合边界此前交出的也是同一个活数组 (subagent.ts 的 prepareNextTurn),现已同样改为副本。每个消息边界处循环视图与 state.messages 携带同样的消息、同样的顺序, 只是不再是同一个数组——这正是把重复项挡在下一回合请求之外的原因。
  • convertToLlm 处的请求守卫(D608)改为与提供商实际校验的对象一致:调用与结果的 id 按“上线可见”的那一段比较;当结果与调用在 item 段上不一致时,结果改挂到调用的 id 上;当一条辅助消息里的工具调用全部已被占用时,整条消息被丢弃,而不再保留那个把调用与结果隔开的克隆。 丢弃日志同时给出被移除的消息条数。若一条消息在复写某个已占用调用的同时还带一个新调用,则保留该消息及其新调用——这是守卫接受的唯一一种 id 复用;目前没有写入方会产生这种部分复写。
  • 不涉及提供商传输、宿主协议、版本、存储迁移、转录重写,也不改变压缩/保留/预算规则。 03-runtime/02-agent-runtime.md §5c 与 E2E-RUNTIME-loop-context-ownership 记录了该契约与覆盖情况。

2026-09-21 — 限制 Desktop 可信扩展生命周期等待 ​

  • 现有 30 秒处理器预算扩展到通知、启动和关闭事件;模块加载、工厂初始化分别使用 同一预算。此前无界的等待变为有界,事件内等待 UI 回答也受此限制。
  • 中止使当前等待失效;销毁拒绝新派发且只执行一次关闭。迟到完成不能向旧派发提供 结果。既有失败继续、结果归并、插件归属和权限不变,不强制终止进程内代码。
  • 未接通事件仍可注册,并在现有诊断中显示。参见 07-plugins/16-trusted-extensions.md §6 与 E2E-HOOKS-cancel-and-dispose。

2026-09-22 —— 指令源不可读时拒绝斜杠提交(D613,issue #795) ​

  • Composer 在发送时读取合并后的指令列表,并把失败吞成 null,而提交路径无法把它与 “没有这个指令”区分开。于是在该 IPC 读取失败时输入的 /compact 会作为字面提示词发给 模型,模型据此把它当成指令执行。
  • 解析现在返回三种结果,而不再是一个可空值:已解析(内置 / 插件 / 扩展分发)、未知 (模板、别名与无 id 条目继续走提示词路径)、不可用。不可用时拒绝提交、保留草稿并显示 chat.slashCommandSourceUnavailable。失败的读取不写 TTL 缓存,因此下一次发送会重试; 缓存仍热时,一次数据源抖动不会影响解析。
  • 拒绝是刻意 fail-closed:只有指令源能判断 /name 是否为控制指令,因此读不到时,看起来像 普通文本的名称也会被拒绝,而不是靠猜测。模板与确实未知的别名保持原有提示词路径。由 apps/desktop/test/slash-command-source.test.mjs 与 03-runtime/01-ipc-protocol.md §13c 固定。

2026-09-22 —— 手动压缩有自己的传输超时与持久化判定(D614,issue #795) ​

  • agent.compact 是阻塞式 RPC,会在 sidecar 内消耗一次完整的模型摘要请求:pi 序列化会话、 流式生成摘要,并重试瞬时失败。它此前沿用扁平的 130 秒传输默认值,因此 888KB 上下文约 158 秒的压缩以 sidecar RPC timeout 结束,而 sidecar 仍在继续工作并落盘了检查点——用户 被告知压缩失败,而下一个回合证明它其实成功了。
  • 超时现在由 sidecar 真正执行的预算推导:每次尝试的流空转看门狗(180 秒)、1 + 3 次 尝试、2/4/8 秒重试退避,以及传输余量 —— 即 AGENT_COMPACT_RPC_TIMEOUT_MS,刻意按方法 区分,而不是放宽全局默认值。
  • 两条宿主路径(Electron 的 agentCompact IPC 与 RuntimeService.compact)也不再把手动 压缩的传输超时当作 sidecar 的判定:它们会重新读取持久化的 session.compaction 记录, 发现新检查点已落盘就报告成功、记录该不一致,否则原样抛出超时。sidecar 自己报告的判定 绝不会被这样改写。由 packages/host-runtime/src/runtime-service.test.ts、 packages/shared/src/protocol.test.ts、 apps/desktop/test/plugin-timeout-budgets.test.mjs 与 03-runtime/01-ipc-protocol.md §5.4 固定。

2026-09-22 —— 空记录读取会重试,且绝不入缓存(D615,issue #795) ​

  • 渲染层把每一次持久化 session.get 窗口都当真,并写入缓存。当宿主正在重写记录文件时, 对拥有数千条消息的会话返回的空窗口被当成“空快照”存下,侧边栏悬停预取又反复送出它, 于是面板一直空白,只有重启应用才能恢复。此前没有任何逻辑区分“读取失败或读到陈旧数据” 与“真的空会话”,也没有任何路径重试。
  • 现在的判断依据是会话自己的计数:若侧边栏计为有历史的会话返回零条消息,就再读一次, 随后保留用户已有的快照,否则显示 chat.sessionTranscriptEmpty。这类空页绝不会写入记录 缓存,因此悬停预取不会污染之后每次打开。
  • 这是防御措施,而不是宿主侧根因:记录重写是报告者 0.15.1 的存储布局,而读取侧仍然返回 “会话存在但没有消息”,而不是报告记录不可读。见 04-ux/09-interaction-patterns.md §跨选项卡的会话隔离。

2026-09-22 —— 从未拿到初始状态的渲染器有了有界表面与退出通道(D616) ​

  • 渲染器的启动过程本身没有超时:bootstrap() 要么发布初始状态,要么什么都不发布, 因此一次始终没有落地的读取会让窗口一直停在启动表面上——没有菜单、没有数据,除了 强杀进程无事可做(issue #831)。渲染器现在自己监视这段等待: STARTUP_SLOW_HINT_MS(30 秒)在不判定启动失败的前提下为启动表面加上日志、诊断与 退出,STARTUP_STALLED_MS(180 秒)把它变成恢复表面,后者还提供重跑 bootstrap() 的重试。
  • 两个界限都高于 main↔host 的 RPC 上限(DEFAULT_RPC_TIMEOUT_MS,130 秒),因此慢 但成功的启动不会被读成失败;看门狗从不取消它所监视的启动,成功完成的启动会用 shell 替换该表面。恢复表面替换启动画面(同一时刻只挂载一个启动表面),渲染器绘制的窗口 控制按钮保持在其之上,因此无边框的 Windows/Linux 窗口始终可以关闭。
  • 渲染器新增退出通道 pi-desktop/app/quit(api.quitApp()),其处理函数与“退出” 菜单项一样调用 app.quit(),因此有序关停、确认对话框和关闭行为都仍是应用已有的 那一套。参见 03-runtime/07-process-model.md §3 与 E2E-076。

2026-09-22 —— 摘要失败的压缩保留真实近期窗口(D617,issue #827) ​

  • 自动压缩的摘要请求失败时安装的保留尾部检查点最多只带一条最新用户消息,重建存储的 检查点也会以同样方式收窄尾部。于是在一条长回合里,下一次模型请求只剩一句用户话: 报告中的会话在两次检查点之间丢掉约 168 条消息(3 条用户 / 34 条助手 / 131 条工具), 而磁盘上的转录本与界面行看起来完好。
  • 回退现在保留真实的近期窗口——压缩范围内最新的连续消息,包含所有角色,上限为 keep-recent 目标,以及携带摘要与恢复提示之后安全预算剩余的空间——并用 details.retainedTailShape 标记,使重建时回放整窗。没有该标记的检查点(每个成功 检查点,以及标记出现之前写入的记录)仍归一化为只保留最新的用户消息。active_turn 回退还会在窗口装不下时保留当前任务的用户消息;回退会丢弃 pi 本来就会丢的助手消息, 以及调用不在窗口内的工具结果;details.failureReason 记录 no_new_history / summary_budget / summary_provider / checkpoint_oversized,而不是提供商错误文本。
  • 单次缩减后仍过大的摘要提示不再被跳过:范围会被切成每片都能放下的连续分片,最多 16 次 请求,每次携带上一片的摘要,检查点报告这些请求的用量总和。只有空范围或超过该请求上限 的范围才因预算原因回退。见 ADR 0302、03-runtime/02-agent-runtime.md、ADR 0049、ADR 0282。

2026-09-20 —— 复制公式得到的是它的 TeX 源码(D619,issue #414) ​

复制之后公式的边界仍然可解析:相邻的行内围栏之间补一个分隔符;正文里的每个美元 符号都会被转义,本会把围栏转义掉的反斜杠串同理。annotation 里的空白原样保留; 加宽过的多行行内公式用一个字面的 <span> 包住,以免它粘在行首时开出块级公式。 TeX 里的换行不做压平,因为它可能用来终止 % 注释。这个包裹是 text/plain 里的 Markdown 源码,不是 text/html 负载;对禁用行内 HTML 的外部编辑器不作兼容承诺。 回归覆盖两个复制入口,以及相邻公式、围栏两侧的正文美元符号、格式化包装、行与块 的边界、补白,以及含 TeX 注释的多行公式各自的 Markdown 往返。 含 - x、+ x、* x、> x 或内部空行的块级公式,复制之后必须仍是同一个公式 节点。共享的 remark 语法把尚未闭合的公式留在流式尾块里直到围栏闭合,其后的正文 及其源码偏移保持不变。脚注定义出现在引用之后——紧随其后,或稍后才流式到达——会 渲染成真正的引用与脚注区,而不是字面的 [^1];这正是把切片脱离整条消息单独解析 时,按块切分要付的代价。CRLF、普通列表、引用块、GFM 表格、围栏代码,以及那些 必须继续切分的源文,由 markdown-blocks.test.mjs 覆盖。

  • KaTeX 每个公式都画两遍:一棵给无障碍技术读的 MathML 树,一棵由定位 span 组成的 可视树,两者都是文档里真实的文本。于是平台自带的复制把公式写成了它的字形,而且 写了两份,从来不是这条回答被写下时的源码。
  • 现在,当选区覆盖到渲染出来的公式时,复制会从 MathML annotation 里把 TeX 读回来: 行内 $…$,块级 $$…$$ 独占行。这正是 lib/latex-math.ts 在 remark-math 运行前把 \(…\) 和 \[…\] 归一到的那组定界符,因此粘回去的公式会渲染回它 来处的那个公式。落在公式内部的切口会扩展成整个公式——半个 TeX 表达式不成其为 公式,而横在两棵树之间的切口会从一棵里取到源码、从另一棵里取到字形。
  • 每组定界符都像代码段的围栏那样,加长到盖过公式内部出现的最长同字符串,因为 $\$5 + x$ 会在那个转义美元处收口,粘回去变成普通文字。除此之外行内保持窄 形式,而理由并不是它乍看上去的那个:单行的 $$…$$ 永远开不出块级公式,因为 math flow 不允许开栅栏之后的 meta 段里出现 $。它保持窄形式,是因为行内公式 的 TeX 可能带换行——只有 \(…\) 那条路径的换行被归一化抹平了——此时 $$ 会 落在行首、公式其余部分跟在它后面,那才是一个 flow 开栅栏,会把整段吞掉。
  • 块级公式在这里并不总是一个块。normalizeLatexMathDelimiters 对 \[ … \] 是 等宽原地改写,因此数学节点仍是行内的,再由 remarkLatexBracketDisplay 提升, 于是 KaTeX 把 .katex-display 画在句子自己的段落里。归约照原样放入段落插槽: p 套 p 作为标记是非法的,但在这里无害——克隆只会经由文本序列化器读取, 而它把嵌套块当成一个边界。$$ 上方有一个换行就足以让 remark-math 开出块级 公式,所以粘回去的仍是它来处的那个块级公式。两种 \[ … \] 形态都由 E2E-CHAT-copy-formula-as-tex 钉住。
  • 表格单元格是唯一一处块级插槽必须实测、而不能靠推理定论的地方;这里记下实测结果, 因为推理恰好指向了相反的方向。remarkLatexBracketDisplay 会提升任意位置的 \[ … \] 节点,单元格也不例外,所以 .katex-display 确实会落进 <td>; 而表格序列化器用制表符拼接单元格,于是看上去那里的 $$ 围栏会被制表符封死、 把该行其余内容吞掉(a\tb\n$$\nE = mc^2\n$$\t2 是一个值一直延伸到行尾的 公式节点)。Chromium 并不会写出这种字符串:块级盒子在单元格里同样是块级边界, 因此该行在它周围断开,围栏在自己的行上闭合,相邻单元格也落在自己的行上。这个 边界也不是插槽凭空造出来的——平台自己对同一行的读法里同样没有制表符,因为 .katex-display 对它也会断行——所以在这里保留块级形式,正是这条决策其余部分 所依赖的那种一致性,而不是它的例外。「在单元格里收窄成行内形式」被考虑过并否决: 那会拿块级语义去修一个序列化器根本不产生的形态,还会把平台自己都不写的制表符 塞回去。E2E-CHAT-copy-formula-as-tex 同时钉住了这两半,因此哪天 Chromium 开始对 含块级盒子的单元格做制表符拼接,会在那里失败,而不是在用户的剪贴板里。
  • 只有公式被改写。选区里其余的一切——正文、列表、表格、代码块——由平台自己序列化: 归约后的克隆被选中,经由 Selection.toString()——复制真正跑的那个序列化器——读回。 手写一遍树的遍历只能近似那套空白规则,而它每差一点,就会悄悄改写平台本来 已经做对的内容:段落里被放回源码的折行、列表项之间多出的空行、代码块里被压掉的 空行。
  • 序列化器必须是复制自己那一个,而不是它的近邻。innerText 读出来几乎一样,却完全 不认识 user-select,于是会把 base.css 标为惰性的界面零件一起写出去——代码块的 语言标签会变成剪贴板里多出来的一行 js。把那条规则在归约里重述一遍,正是这条决策 所拒绝的手写遍历;跑平台自己的序列化器,让一致性从"近似"变成"精确"。克隆在选区 原本所处的那个元素里读,而不是挂到 body 上,因此决定这份读法的层叠就是实时 文档的层叠;反过来在克隆上重述选择契约(内联一条 user-select: text)会把它 倒置——外壳默认不可选、由文档型表面 opt-in,于是仅因继承而不可选的界面零件会进 入剪贴板。
  • 其余选区一律不碰:不含公式的选区归平台管。除此之外不再判断这次复制"属不属于" 该选区,因为 Chromium 的事件目标本就是从选区推导出来的,非塌陷选区必定在自己 内部抬起复制;加一道目标检查只会误伤用 selectNodeContents 构造的选区,记录区 自己的「选中文本」就是其中之一。输入框里抬起的复制也带不走残留的记录区选区—— 一个 frame 只有一个选区,聚焦任何字段都会让文档选区塌陷,而塌陷的选区本就会被 拒绝。
  • 只有一种剪贴板 flavour:text/plain。接管复制的同时也丢掉了平台的 text/html, 而且不再写回。归约后的克隆是应用自己的标记:把它序列化会带上 base.css 标为 user-select: none、文本读法已经略过的界面零件——代码块的 js 标签和它的复制 按钮——还有 data-source-* 记账信息,以及一个嵌在段落里的块级插槽,那是同一个 选区的第二份、也更差的读法。改为写回渲染结果同样不行:隐藏 MathML 树的只有 KaTeX 自己的样式表,而样式表不会随剪贴板一起走,于是富文本粘贴目标会看到每个 公式出现两遍——正是这条决策要消除的那种重复。索性不写这个 flavour,两头都解决: 富文本目标回退到纯文本,而那正是源码。代价是含公式的选区粘进富文本目标时,会 失去平台 text/html 本来会携带的标签级格式;复制要的就是源码。
  • 只有一个文档级 copy 监听器,由外壳装上、也由外壳摘掉(hooks/use-copy-tex.ts)。 因为 Markdown 自己不渲染任何包裹元素——回答、工作面板的文件预览、插件 readme 通过不同的父节点抵达同一批公式——也因为复制本来就是一个文档级手势。记录区的 右键复制经由同一个模块读取同一个选区,两个入口不会把同一个选区的两种读法送进 剪贴板。
  • 把消息切成互相独立解析的块——这早于 D619,D619 只是把它换到了渲染语法上——仅当某一片不依赖其他片的解析上下文时才成立。链接与脚注定义恰是反例:它在整条消息范围内解析,脚注还要跨消息编号、复用和回链,于是脱离定义单独解析的引用会以字面 [^1] 留在正文里,脚注本身消失。因此只要消息中任意位置声明了定义,该消息就整体不切分。把定义注入每一片的做法被否决:那会让脚注按片各自重新编号,并把回链指向错误的引用。被接受的代价是这类消息在流式期间失去按块记忆——这正是渲染器在引入分块之前一直承担的成本,而它在聊天回答中的罕见程度把这个代价限住了。
  • D619 不新增任何界面、store 状态或操作行条目:它把 ADR 0268 当年据以移除「引用」 的那个 OS 剪贴板,变成名副其实的替代品。仅渲染层——未改动任何 IPC 通道、宿主协议、 存储 schema、权限、Plugin SDK 或 i18n 键。见 04-ux/08-component-spec.md §8.7 与 E2E-CHAT-copy-formula-as-tex。

2026-09-22 —— 交给 pi 的文件操作收集器使用它认识的拼写(D618,issue #827) ​

  • 检查点的 readFiles / modifiedFiles 以及摘要追加的 <read-files> 段来自 pi 自己的 extractFileOpsFromMessage,它只匹配小写名字 read / write / edit——即 pi 自己 工具的名字。PI-Desktop 注册的是 Read / Write / Edit,因此该收集器什么都匹配不到, 每个检查点都报出空文件清单;issue #827 在排查压缩报告时发现了这一点。
  • 转换只发生在边界这一侧:withPiFileOpToolNames 把交给 pi prepareCompaction 的条目 复制一份,并改写这三个名字的拼写。存储字节不变——transcript、检查点与重建后的上下文都 保留我们的拼写——而且当历史里没有这类调用时原样返回入参数组,常见路径不产生任何分配。
  • 只转换这三个名字。pi 的收集器不读其它名字,因此 Grep、Glob、Bash、插件与 MCP 名字 保持我们注册的拼写,被摘要的文本只在 pi 真正消费该名字的地方发生变化。

本地优先 · 模型可替换 · 插件可扩展。 AIUO.NET