diff --git a/.agents/notes/implemented/architecture/2026-09-25-shared-acp-agent-conversation.md b/.agents/notes/implemented/architecture/2026-09-25-shared-acp-agent-conversation.md index 19dd4d4dc..37da72617 100644 --- a/.agents/notes/implemented/architecture/2026-09-25-shared-acp-agent-conversation.md +++ b/.agents/notes/implemented/architecture/2026-09-25-shared-acp-agent-conversation.md @@ -17,6 +17,8 @@ Agent 对话默认关闭,打开某个项目的 Agent 面板时才启动本机 ## 决策 +- **思考强度名称**:强度选项使用上游英文名称,例如 Low、Medium、High、Xhigh、Max、Ultra,不经过通用本地化。子菜单、设置行当前值和模型底栏复用同一个呈现入口,避免同一强度在不同位置显示中英文混合。菜单标题“思考强度”和其他配置仍按界面语言显示;不改上游选项 ID、可选范围或确认流程,也不手工补齐当前 Agent 未提供的强度。 +- **权限模式呈现**:菜单和底栏保留 Agent 上游名称,例如 Claude 的 Manual、Accept edits、Plan、Auto、Bypass permissions;模型和思考强度也使用上游名称。名称不能进入全局翻译表,否则 `Plan` 会碰到其他界面的“计划”翻译,导致个别选项与其余名称混用语言。Claude 与 Codex 按已知权限 ID 共享对应图标,Claude 继续显示上游实际提供的编辑、计划等额外选项。依据 [Claude 权限模式说明](https://code.claude.com/docs/en/permissions),说明可按界面语言呈现,但保留实际 Agent 的审批与环境约束,不把 Codex 的沙箱说明直接套到 Claude。只改变显示,不修改选项 ID、数量、顺序或权限命令;上游确认继续决定当前值。未知选项保留其原文和默认图标,不按列表位置猜测权限,也不把 Claude 的五项折叠成三项。不新增状态、资源或运行时写入。 - **侧栏入口图标**:Agent 使用 Lithe 原创的“对话气泡 + 星光”线性 SVG,由工作台 renderer 显式绑定,避免落入通用模块图标而让用户误认成插件管理。图标作为固定源资源随应用打包,复用现有模板着色与选中态;不下载、不生成运行时文件,不影响安装包的只读边界。面板内 Codex/Claude 标志仍表示具体供应商。 - **安装包图标解析**:Agent 品牌图标从应用的 `Contents/Resources/Lithe_Lithe.bundle` 只读加载;资源包或图标缺失时使用默认标志。不要在已安装的 `.app` 中直接调用 SwiftPM(Swift 包管理器)生成的 `Bundle.module`:它只查应用旁的资源包和编译目录,找不到会直接终止进程,开发机器残留的构建资源还会掩盖问题。开发和测试入口保留 SwiftPM 回退;安装版不使用编译目录,也不通过启动时复制资源来修补发行包,避免改变代码签名和 Sparkle 增量更新基线。 @@ -54,7 +56,7 @@ Agent 对话默认关闭,打开某个项目的 Agent 面板时才启动本机 - **面板始终显示完整布局**:功能关闭或没有配置 Agent 时,面板照样显示会话标题、消息区和输入框,用户可以输入;发送时先校验(功能是否开启、是否有已配置的 Agent、模块是否启动完成),不通过就在输入框上方给出提示并提供进入设置的按钮,不会启动任何进程。 - **文件拖入对话框**:参考 CC GUI 普通文件的路径引用交互,原生输入框接受 Finder 和项目树的文件 URL,显示可移除标签,并支持文件选择器。一次最多 32 个引用,按拖入顺序去重;切换会话清除旧文件草稿,发送失败保留文字和引用。功能模型把引用与文字一起排队,Rust Host 用上游 ACP SDK 的 `resource_link` 发送,Agent 自己读取文件并遵守现有权限。文件 URL 是这次消息的原生上下文,不作为跨平台项目标识持久化。正确做法是发送“说明这两个文件”加两个引用;不要在 View 里读文件、复制进缓存或拼装隐式文件内容提示词。图片目前同样作为文件引用,没有缩略图或多模态字节上传;这避免了与本次需求无关的内存、大小和格式策略,后续需要图片输入时再按上游能力协商。 - **对话面板**:停靠在编辑器右侧,与 Maven 共用右侧槽位。输入框下方可以切换 Agent,列出的是已经绑定服务商或 Codex 订阅的 Agent;打开的会话以标签形式排列。每个 Agent 第一次被选中时才建立连接;项目里所有 Agent 的权限提醒会合并显示到项目标签上。 -- **对话区的视觉与交互**:按 CC GUI 参考设计使用单行会话标题、居中的品牌标志、上下文栏和底部工具栏。只有多会话或用户主动展开时才显示标签条,避免空会话标题重复。空态不放进滚动列表,否则无法在剩余高度内居中。输入区复用 `LitheSplitPaneView`,尺寸约束跟随可用高度,拖动只更新局部容器。搜索只筛选当前已加载消息,不请求 Agent 或改动历史。品牌 SVG 来自参考项目锁定的 `@lobehub/icons` 5.8.0,以静态源资源随 SwiftPM bundle 打包并携带 MIT 许可,不新增运行时下载或可复用缓存。配置选择器按参考项目的底栏设计:权限与模型入口位于输入区底部,模型弹出面板提供本地名称/ID/分组搜索、品牌图标、蓝色选中行和绿色勾选,不显示模型说明;权限弹出面板保留上游说明并本地化已有文案。思考强度、速度及其他上游配置放到模型面板底部的子菜单,未提供时不显示。选项数量和权限语义仍以 ACP 为准,不照截图补选项。选择后收起面板,原确认/失败状态机继续拥有当前值,等待确认时禁止再次配置。弹出与搜索状态只在视图内保存,会话切换重建选择器以清除旧会话的弹出状态;不重建输入框或清空草稿。使用 macOS 原生 popover 管理定位和关闭,设置子选项在同一个 popover 内展开侧栏,避免嵌套 popover 关闭父面板;不另写鼠标全局监听或位置缓存。上下文百分比使用 Agent 上报的用量,缺失时展示 0% 占位与“上下文: 0.0%”简短提示;占位不作为已上报用量写回会话状态。 +- **对话区的视觉与交互**:按 CC GUI 参考设计使用单行会话标题、居中的品牌标志、上下文栏和底部工具栏。只有多会话或用户主动展开时才显示标签条,避免空会话标题重复。空态不放进滚动列表,否则无法在剩余高度内居中。输入区复用 `LitheSplitPaneView`,尺寸约束跟随可用高度,拖动只更新局部容器。搜索只筛选当前已加载消息,不请求 Agent 或改动历史。品牌 SVG 来自参考项目锁定的 `@lobehub/icons` 5.8.0,以静态源资源随 SwiftPM bundle 打包并携带 MIT 许可,不新增运行时下载或可复用缓存。配置选择器按参考项目的底栏设计:权限与模型入口位于输入区底部,模型弹出面板提供本地名称/ID/分组搜索、品牌图标、蓝色选中行和绿色勾选,不显示模型说明;权限弹出面板保留上游名称,只对说明使用界面语言。思考强度、速度及其他上游配置放到模型面板底部的子菜单,未提供时不显示。选项数量和权限语义仍以 ACP 为准,不照截图补选项。选择后收起面板,原确认/失败状态机继续拥有当前值,等待确认时禁止再次配置。弹出与搜索状态只在视图内保存,会话切换重建选择器以清除旧会话的弹出状态;不重建输入框或清空草稿。使用 macOS 原生 popover 管理定位和关闭,设置子选项在同一个 popover 内展开侧栏,避免嵌套 popover 关闭父面板;不另写鼠标全局监听或位置缓存。上下文百分比使用 Agent 上报的用量,缺失时展示 0% 占位与“上下文: 0.0%”简短提示;占位不作为已上报用量写回会话状态。 - **进程与诊断**:启动时把 Agent 可执行文件所在目录放到 `PATH` 最前面。npm 把 `codex-acp` 和 `node` 装在同一个目录,而 Mac 图形应用拿不到登录 shell 的 `PATH`。关闭时先向整棵进程树发 SIGTERM,并记录树里的每个进程 ID,稍等后再对仍存活的进程发 SIGKILL,因为 codex-acp 的 app-server 会比外层进程晚几秒退出。API Key 模式下 Agent 意外退出时,报错里附上 stderr 最后 20 行,其中的 Key 会被替换掉;订阅模式仅显示受控错误,不附加账号工具的原始输出。 正确做法:新平台的界面通过平台适配器把 fixture 里的命令交给 `lithe-agent-host`,并把工具权限选择交给用户。 diff --git a/docs/development/platform-parity-matrix.csv b/docs/development/platform-parity-matrix.csv index 241d81460..4364fcbe7 100644 --- a/docs/development/platform-parity-matrix.csv +++ b/docs/development/platform-parity-matrix.csv @@ -7,6 +7,8 @@ agent-entry-discoverability,AI,Agent 对话,新安装及模块关闭时仍显示 agent-conversation-history,AI,Agent 对话,CC GUI 风格历史页:标题与会话 ID 搜索、更新时间、复制 ID、刷新与恢复、收藏与本地重命名、单条与批量移除确认及恢复、筛选、多选批量管理及 Markdown 导出,已实现,待验证,未实现,不适用,Agent,macOS:在深浅主题与窄宽面板查看历史页,验证标题/ID 搜索、空结果、返回保留草稿、刷新失败不丢列表、打开历史恢复会话;验证收藏/重命名重启后保留且不同项目/Agent 隔离;筛选和全选只操作可见项,单条与批量移除先确认,取消不修改记录,确认只移除提示时选中的会话,移除后可恢复且不改上游文件;单个与批量 Markdown 导出等待历史回放完成、保持原选中标签,取消/失败不写部分记录;部分回放后失败再导出必须重新加载并等待完成,断连后的未完整回放不可导出,完整快照断连后仍可导出;关闭项目释放导出等待。消息数量只显示已加载的用户与 Agent 消息,未知总数不伪造。Windows 页面待实现。,,macos/Sources/Lithe/Views/Agent/AgentHistoryView.swift; macos/Sources/LitheAgentConversationModule/Application/AgentHistoryFeatureModel.swift; macos/Sources/Lithe/Platform/MacOS/Agent/MacAgentHistoryAdapters.swift; macos/Tests/LitheTests/AgentHistoryTests.swift,rust/lithe-agent-host/src/lib.rs agent-context-usage,AI,Agent 对话,Agent 上下文用量:输入框圆环百分比、悬停已用/容量 token,未使用时零占位、会话隔离与压缩后更新,已实现,待验证,部分实现,待验证,Agent,macOS:真实 Agent 对话后检查输入框顶部圆环百分比;分别悬停圆环与百分比,立即在上方显示已用/容量 token,移开或切换面板后关闭浮层,窄面板浮层不越界;首次、Agent 未上报或已上报零使用量时显示 0%,悬停仅显示“上下文: 0.0%”,内部缺失状态不伪造 token 或容量;切换会话/Agent、新建会话不串用量,压缩后用量可降低,不累加计费用量;模型确认切换和断连后清除旧容量,重新加载或上报后更新;窄宽面板及深浅主题检查布局和辅助功能。Windows 共享 host 可转发 usage_update,页面待实现。,,macos/Sources/LitheAgentConversationModule/Application/AgentContextUsage.swift; macos/Sources/Lithe/Views/Agent/AgentContextUsageView.swift; macos/Tests/LitheTests/AgentConversationFeatureModelTests.swift; macos/Tests/LitheTests/AgentConversationPresentationTests.swift,rust/lithe-agent-host/src/lib.rs; shared/fixtures/agent/acp-events-v1.json agent-acp-conversation,AI,Agent 对话,可选 ACP Agent 对话:API Key 网关登录、项目会话与历史恢复、模型/权限/思考配置、不依赖凭据的本机默认模型同步、旧默认模型按上游推荐修复与品牌模型图标、CC GUI 风格可搜索模型与权限弹出面板、工具详情与权限、停止确认和进程清理,已实现,待验证,部分实现,待验证,Agent,macOS + Codex + API Key:验证发消息前的上游配置选项和确认;无自定义服务商或 API Key 时确认仍显示本机顶层默认模型、保留密钥缺失错误;修改本机默认模型后重连,连续新建空会话确认不再使用旧导入模型;配置不在上游目录中的旧模型时确认新会话使用目录内的上游推荐值、拒绝与超时不显示假成功且 Codex 模型菜单显示 OpenAI 标志;验证工具输入/输出/文件/变更、权限拒绝、停止确认及超时重连、历史加载失败保留记录;临时项目跑通读文件→修改→执行测试→追问,复用文档观察和脏缓冲区保存保护。检查模型搜索(名称/ID/分组、空结果)、品牌图标/选中状态、无模型说明、权限说明、思考/速度子菜单、确认/失败与会话切换关闭弹出面板;检查项目切换、退出清理,以及 CC GUI 风格空态、搜索、标签、可调整输入区和配置菜单在窄宽面板及深浅主题下的布局。Windows 与 Claude 端到端仍待验证。 安装包环境下打开 Agent 面板,确认 Codex/Claude 图标从 Contents/Resources 加载且不依赖构建目录;资源包或图标缺失时显示默认图标而不闪退,读取前后包内文件清单和内容不变。,,macos/Sources/LitheAgentConversationModule; macos/Sources/Lithe/Views/Agent; rust/lithe-agent-host; macos/Tests/LitheTests/AgentBrandIconResourceTests.swift,rust/lithe-agent-host +agent-thinking-level-labels,AI,Agent 对话,Agent 思考强度英文呈现:Claude/Codex 子菜单、设置行当前值和模型底栏统一使用上游英文名称,已实现,已验证,未实现,待验证,Agent,2026-10-01 macOS:最新 preview 独立 PR 分支 83 项 Agent 回归通过;含 PR #1007 前置修复的组合构建 87 项回归及真实界面验收通过;中文真实界面确认 Codex 的 Low、Medium、High、Xhigh、Max、Ultra 和 Claude 的 Low、Medium、High、Max 均显示英文。两边分别切换 Low,确认子菜单、设置行当前值和模型底栏同步英文,并恢复 Claude Medium、Codex Xhigh;权限、速度和思考强度标题仍显示中文。不改原始选项 ID、顺序和确认行为,运行前后 bundle 全部 990 个文件 SHA-256 一致。Windows 对话 UI 仍待实现。,,macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift; macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift,shared/contracts/application-boundary.md +agent-approval-mode-presentation,AI,Agent 对话,Agent 权限模式呈现:Claude/Codex 名称保留上游原文,说明按界面语言显示并补齐图标,保留各 Agent 实际支持的选项及权限语义,已实现,待验证,未实现,待验证,Agent,中文和英文环境分别验证 Claude/Codex:权限名称保留上游原文,Plan 菜单与底栏一致,已知权限 ID 显示对应图标,未知选项原文和默认图标不变;说明使用当前界面语言,保留各 Agent 实际的审批和环境约束。检查原始 ID、数量、顺序及确认/失败行为不变。使用指定语言的资源 Bundle 验证名称与通用 Plan 翻译不会串用;整合紧凑布局后检查双行说明和长列表滚动。Windows 对话 UI 待实现。,,macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift; macos/Resources/en.lproj/Localizable.strings; macos/Resources/zh-Hans.lproj/Localizable.strings; macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift,shared/contracts/application-boundary.md agent-tool-activity-timeline,AI,Agent 对话,连续 Agent 工具调用(执行命令、列文件、读文件与编辑等)合并为默认折叠的时间线卡片,汇总总数与失败/中断/完成进度,并逐条展开输入、输出与文件位置,已实现,待验证,未实现,不适用,Agent,macOS:让真实 Agent 连续列文件、读取文件、执行命令和编辑,确认相邻工具调用只显示一张默认折叠的时间线卡片;展开后每条状态、完整输入、输出和文件位置仍可查看,进行中、全部完成、失败和中断时汇总正确。用 Agent 文字说明和新一轮用户消息隔开工具调用,确认保留文字顺序且不会跨边界合并;搜索工具输入、输出或文件路径时自动展开匹配组;覆盖文件路径仅由 diff 内容上报、未出现在工具标题、输入输出或 locations 中的情况,历史恢复结果与实时流一致。在窄宽面板及深浅主题检查长标题截断、悬停全文、列表滚动和详情可读性。Windows Agent 对话 UI 尚未实现。,,macos/Sources/Lithe/Views/Agent/AgentTranscriptView.swift; macos/Sources/Lithe/Views/Agent/AgentToolGroupView.swift; macos/Tests/LitheTests/AgentConversationPresentationTests.swift,windows/tauri/src/features agent-turn-statistics,AI,Agent 对话,发送后实时计时、每轮结束固定耗时、Agent 上报的输入/输出 token 与缓存/推理用量详情,已实现,待验证,部分实现,待验证,Agent,macOS:在真实 Agent 会话中确认发送后计时包含建会话、加载历史、工具和权限等待;正常完成、取消确认、请求失败和断连后停止计时,下一轮仍可看到上一轮统计。并发会话切换验证耗时与用量隔离;上报 usage 时显示准确输入/输出并悬停查看总量、缓存和推理值,缺失时只显示耗时。历史回放不编造统计,上下文占用与订阅额度不作为输入/输出 token。检查深浅主题和窄宽面板。Windows 共享 Host 已透传可选 usage,对话 UI 尚未接入。,,macos/Sources/Lithe/Views/Agent/AgentTurnStatisticsView.swift; macos/Sources/Lithe/Views/Agent/AgentTranscriptView.swift; macos/Sources/LitheAgentConversationModule/Application/AgentTurnStatistics.swift; macos/Sources/LitheAgentConversationModule/Application/AgentConnectionModel.swift; macos/Tests/LitheTests/AgentConversationFeatureModelTests.swift; macos/Tests/LitheTests/AgentConversationPresentationTests.swift,rust/lithe-agent-host/src/prompt.rs; rust/lithe-agent-host/src/tests.rs; shared/fixtures/agent/acp-events-v1.json agent-file-references,AI,Agent 对话,从 Finder 或项目树拖入文件到 Agent 输入框,文件选择器、可移除标签、多文件去重和数量限制、文件引用随消息发送及失败保留草稿,已实现,待验证,部分实现,待验证,Agent,macOS:从 Finder、项目树拖入临时文件(多文件、中文/空格名)到输入区与上下文栏,检查高亮和可移除标签、去重、数量限制、会话切换和发送失败保留草稿;用隔离项目验证文字+文件与纯文件发送、历史加载后引用正确送到原会话、Agent 读取与权限流程。图片按文件引用处理,无多模态上传。Windows 原生拖放和 Claude 端到端待验证。,,macos/Sources/Lithe/Views/Agent/AgentComposerView.swift; macos/Sources/Lithe/Views/Agent/AgentFileReferenceList.swift; macos/Sources/LitheAgentConversationModule/Application/AgentFileReference.swift; macos/Tests/LitheTests/AgentFileReferenceTests.swift; macos/Tests/LitheTests/AgentConversationFeatureModelTests.swift; rust/lithe-agent-host/src/tests.rs,rust/lithe-agent-host/src/prompt.rs; shared/fixtures/agent/acp-events-v1.json diff --git a/docs/development/platform-parity-matrix.md b/docs/development/platform-parity-matrix.md index 4305bb633..50256bcb0 100644 --- a/docs/development/platform-parity-matrix.md +++ b/docs/development/platform-parity-matrix.md @@ -4,9 +4,9 @@ - 最后复核:2026-09-29 - 盘点状态:initial-static-inventory(根据 macOS Views/Application/Services、Windows features/extensions 和共享契约的代码入口进行初版盘点;未替代真实运行验收。) -- 功能项:120 -- macOS:实现:✅ 105 已实现,🟡 3 部分实现,❌ 7 未实现,🧩 5 平台专属;验证:✔️ 0 已验证,🔍 108 待验证,— 12 不适用 -- Windows:实现:✅ 102 已实现,🟡 11 部分实现,❌ 5 未实现,🧩 2 平台专属;验证:✔️ 0 已验证,🔍 113 待验证,— 7 不适用 +- 功能项:122 +- macOS:实现:✅ 107 已实现,🟡 3 部分实现,❌ 7 未实现,🧩 5 平台专属;验证:✔️ 1 已验证,🔍 109 待验证,— 12 不适用 +- Windows:实现:✅ 102 已实现,🟡 11 部分实现,❌ 7 未实现,🧩 2 平台专属;验证:✔️ 0 已验证,🔍 115 待验证,— 7 不适用 ## 实现状态定义 @@ -30,7 +30,7 @@ > 每一行对应一个可以单独验收的用户能力;区域和功能组只用于导航,不作为状态统计单位。单元格第一行是实现状态,第二行是验证状态。
-AI · 19 个能力点 +AI · 21 个能力点 | 功能组 | 能力点 | macOS | Windows | 负责人 | 验证方式 | 备注 | | --- | --- | --- | --- | --- | --- | --- | @@ -42,6 +42,8 @@ | Agent 对话 | **CC GUI 风格历史页:标题与会话 ID 搜索、更新时间、复制 ID、刷新与恢复、收藏与本地重命名、单条与批量移除确认及恢复、筛选、多选批量管理及 Markdown 导出**
agent-conversation-history | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Agent/AgentHistoryView.swift`、`macos/Sources/LitheAgentConversationModule/Application/AgentHistoryFeatureModel.swift`、`macos/Sources/Lithe/Platform/MacOS/Agent/MacAgentHistoryAdapters.swift`、`macos/Tests/LitheTests/AgentHistoryTests.swift` | ❌ 未实现
— 不适用
`rust/lithe-agent-host/src/lib.rs` | Agent | macOS:在深浅主题与窄宽面板查看历史页,验证标题/ID 搜索、空结果、返回保留草稿、刷新失败不丢列表、打开历史恢复会话;验证收藏/重命名重启后保留且不同项目/Agent 隔离;筛选和全选只操作可见项,单条与批量移除先确认,取消不修改记录,确认只移除提示时选中的会话,移除后可恢复且不改上游文件;单个与批量 Markdown 导出等待历史回放完成、保持原选中标签,取消/失败不写部分记录;部分回放后失败再导出必须重新加载并等待完成,断连后的未完整回放不可导出,完整快照断连后仍可导出;关闭项目释放导出等待。消息数量只显示已加载的用户与 Agent 消息,未知总数不伪造。Windows 页面待实现。 | | | Agent 对话 | **Agent 上下文用量:输入框圆环百分比、悬停已用/容量 token,未使用时零占位、会话隔离与压缩后更新**
agent-context-usage | ✅ 已实现
🔍 待验证
`macos/Sources/LitheAgentConversationModule/Application/AgentContextUsage.swift`、`macos/Sources/Lithe/Views/Agent/AgentContextUsageView.swift`、`macos/Tests/LitheTests/AgentConversationFeatureModelTests.swift`、`macos/Tests/LitheTests/AgentConversationPresentationTests.swift` | 🟡 部分实现
🔍 待验证
`rust/lithe-agent-host/src/lib.rs`、`shared/fixtures/agent/acp-events-v1.json` | Agent | macOS:真实 Agent 对话后检查输入框顶部圆环百分比;分别悬停圆环与百分比,立即在上方显示已用/容量 token,移开或切换面板后关闭浮层,窄面板浮层不越界;首次、Agent 未上报或已上报零使用量时显示 0%,悬停仅显示“上下文: 0.0%”,内部缺失状态不伪造 token 或容量;切换会话/Agent、新建会话不串用量,压缩后用量可降低,不累加计费用量;模型确认切换和断连后清除旧容量,重新加载或上报后更新;窄宽面板及深浅主题检查布局和辅助功能。Windows 共享 host 可转发 usage_update,页面待实现。 | | | Agent 对话 | **可选 ACP Agent 对话:API Key 网关登录、项目会话与历史恢复、模型/权限/思考配置、不依赖凭据的本机默认模型同步、旧默认模型按上游推荐修复与品牌模型图标、CC GUI 风格可搜索模型与权限弹出面板、工具详情与权限、停止确认和进程清理**
agent-acp-conversation | ✅ 已实现
🔍 待验证
`macos/Sources/LitheAgentConversationModule`、`macos/Sources/Lithe/Views/Agent`、`rust/lithe-agent-host`、`macos/Tests/LitheTests/AgentBrandIconResourceTests.swift` | 🟡 部分实现
🔍 待验证
`rust/lithe-agent-host` | Agent | macOS + Codex + API Key:验证发消息前的上游配置选项和确认;无自定义服务商或 API Key 时确认仍显示本机顶层默认模型、保留密钥缺失错误;修改本机默认模型后重连,连续新建空会话确认不再使用旧导入模型;配置不在上游目录中的旧模型时确认新会话使用目录内的上游推荐值、拒绝与超时不显示假成功且 Codex 模型菜单显示 OpenAI 标志;验证工具输入/输出/文件/变更、权限拒绝、停止确认及超时重连、历史加载失败保留记录;临时项目跑通读文件→修改→执行测试→追问,复用文档观察和脏缓冲区保存保护。检查模型搜索(名称/ID/分组、空结果)、品牌图标/选中状态、无模型说明、权限说明、思考/速度子菜单、确认/失败与会话切换关闭弹出面板;检查项目切换、退出清理,以及 CC GUI 风格空态、搜索、标签、可调整输入区和配置菜单在窄宽面板及深浅主题下的布局。Windows 与 Claude 端到端仍待验证。 安装包环境下打开 Agent 面板,确认 Codex/Claude 图标从 Contents/Resources 加载且不依赖构建目录;资源包或图标缺失时显示默认图标而不闪退,读取前后包内文件清单和内容不变。 | | +| Agent 对话 | **Agent 思考强度英文呈现:Claude/Codex 子菜单、设置行当前值和模型底栏统一使用上游英文名称**
agent-thinking-level-labels | ✅ 已实现
✔️ 已验证
`macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift`、`macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift` | ❌ 未实现
🔍 待验证
`shared/contracts/application-boundary.md` | Agent | 2026-10-01 macOS:最新 preview 独立 PR 分支 83 项 Agent 回归通过;含 PR #1007 前置修复的组合构建 87 项回归及真实界面验收通过;中文真实界面确认 Codex 的 Low、Medium、High、Xhigh、Max、Ultra 和 Claude 的 Low、Medium、High、Max 均显示英文。两边分别切换 Low,确认子菜单、设置行当前值和模型底栏同步英文,并恢复 Claude Medium、Codex Xhigh;权限、速度和思考强度标题仍显示中文。不改原始选项 ID、顺序和确认行为,运行前后 bundle 全部 990 个文件 SHA-256 一致。Windows 对话 UI 仍待实现。 | | +| Agent 对话 | **Agent 权限模式呈现:Claude/Codex 名称保留上游原文,说明按界面语言显示并补齐图标,保留各 Agent 实际支持的选项及权限语义**
agent-approval-mode-presentation | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift`、`macos/Resources/en.lproj/Localizable.strings`、`macos/Resources/zh-Hans.lproj/Localizable.strings`、`macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift` | ❌ 未实现
🔍 待验证
`shared/contracts/application-boundary.md` | Agent | 中文和英文环境分别验证 Claude/Codex:权限名称保留上游原文,Plan 菜单与底栏一致,已知权限 ID 显示对应图标,未知选项原文和默认图标不变;说明使用当前界面语言,保留各 Agent 实际的审批和环境约束。检查原始 ID、数量、顺序及确认/失败行为不变。使用指定语言的资源 Bundle 验证名称与通用 Plan 翻译不会串用;整合紧凑布局后检查双行说明和长列表滚动。Windows 对话 UI 待实现。 | | | Agent 对话 | **连续 Agent 工具调用(执行命令、列文件、读文件与编辑等)合并为默认折叠的时间线卡片,汇总总数与失败/中断/完成进度,并逐条展开输入、输出与文件位置**
agent-tool-activity-timeline | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Agent/AgentTranscriptView.swift`、`macos/Sources/Lithe/Views/Agent/AgentToolGroupView.swift`、`macos/Tests/LitheTests/AgentConversationPresentationTests.swift` | ❌ 未实现
— 不适用
`windows/tauri/src/features` | Agent | macOS:让真实 Agent 连续列文件、读取文件、执行命令和编辑,确认相邻工具调用只显示一张默认折叠的时间线卡片;展开后每条状态、完整输入、输出和文件位置仍可查看,进行中、全部完成、失败和中断时汇总正确。用 Agent 文字说明和新一轮用户消息隔开工具调用,确认保留文字顺序且不会跨边界合并;搜索工具输入、输出或文件路径时自动展开匹配组;覆盖文件路径仅由 diff 内容上报、未出现在工具标题、输入输出或 locations 中的情况,历史恢复结果与实时流一致。在窄宽面板及深浅主题检查长标题截断、悬停全文、列表滚动和详情可读性。Windows Agent 对话 UI 尚未实现。 | | | Agent 对话 | **发送后实时计时、每轮结束固定耗时、Agent 上报的输入/输出 token 与缓存/推理用量详情**
agent-turn-statistics | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Agent/AgentTurnStatisticsView.swift`、`macos/Sources/Lithe/Views/Agent/AgentTranscriptView.swift`、`macos/Sources/LitheAgentConversationModule/Application/AgentTurnStatistics.swift`、`macos/Sources/LitheAgentConversationModule/Application/AgentConnectionModel.swift`、`macos/Tests/LitheTests/AgentConversationFeatureModelTests.swift`、`macos/Tests/LitheTests/AgentConversationPresentationTests.swift` | 🟡 部分实现
🔍 待验证
`rust/lithe-agent-host/src/prompt.rs`、`rust/lithe-agent-host/src/tests.rs`、`shared/fixtures/agent/acp-events-v1.json` | Agent | macOS:在真实 Agent 会话中确认发送后计时包含建会话、加载历史、工具和权限等待;正常完成、取消确认、请求失败和断连后停止计时,下一轮仍可看到上一轮统计。并发会话切换验证耗时与用量隔离;上报 usage 时显示准确输入/输出并悬停查看总量、缓存和推理值,缺失时只显示耗时。历史回放不编造统计,上下文占用与订阅额度不作为输入/输出 token。检查深浅主题和窄宽面板。Windows 共享 Host 已透传可选 usage,对话 UI 尚未接入。 | | | Agent 对话 | **从 Finder 或项目树拖入文件到 Agent 输入框,文件选择器、可移除标签、多文件去重和数量限制、文件引用随消息发送及失败保留草稿**
agent-file-references | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Agent/AgentComposerView.swift`、`macos/Sources/Lithe/Views/Agent/AgentFileReferenceList.swift`、`macos/Sources/LitheAgentConversationModule/Application/AgentFileReference.swift`、`macos/Tests/LitheTests/AgentFileReferenceTests.swift`、`macos/Tests/LitheTests/AgentConversationFeatureModelTests.swift`、`rust/lithe-agent-host/src/tests.rs` | 🟡 部分实现
🔍 待验证
`rust/lithe-agent-host/src/prompt.rs`、`shared/fixtures/agent/acp-events-v1.json` | Agent | macOS:从 Finder、项目树拖入临时文件(多文件、中文/空格名)到输入区与上下文栏,检查高亮和可移除标签、去重、数量限制、会话切换和发送失败保留草稿;用隔离项目验证文字+文件与纯文件发送、历史加载后引用正确送到原会话、Agent 读取与权限流程。图片按文件引用处理,无多模态上传。Windows 原生拖放和 Claude 端到端待验证。 | | diff --git a/macos/Resources/en.lproj/Localizable.strings b/macos/Resources/en.lproj/Localizable.strings index 355fc7a60..2e090e0a8 100644 --- a/macos/Resources/en.lproj/Localizable.strings +++ b/macos/Resources/en.lproj/Localizable.strings @@ -1160,6 +1160,11 @@ "Ask for approval" = "Ask for approval"; "Approve for me" = "Approve for me"; "Full access" = "Full access"; +"Always ask before making changes" = "Ask before making changes or running actions that require approval."; +"Automatically accept all file edits" = "Automatically approve file edits; other actions follow permission rules."; +"Create a plan before making changes" = "Read and plan without modifying files."; +"Claude handles permission decisions" = "Claude evaluates and approves actions automatically."; +"Accepts all permissions" = "Skip routine approval prompts; environment and managed policies still apply."; "Always ask to edit external files and use the internet" = "Always ask to edit external files and use the internet"; "Only ask for actions detected as potentially unsafe" = "Only ask for actions detected as potentially unsafe"; "Unrestricted access to the internet and any file on your computer" = "Unrestricted access to the internet and any file on your computer"; diff --git a/macos/Resources/zh-Hans.lproj/Localizable.strings b/macos/Resources/zh-Hans.lproj/Localizable.strings index 2e8b7b013..689960b16 100644 --- a/macos/Resources/zh-Hans.lproj/Localizable.strings +++ b/macos/Resources/zh-Hans.lproj/Localizable.strings @@ -2399,6 +2399,11 @@ "Ask for approval" = "请求批准"; "Approve for me" = "替我审批"; "Full access" = "完全访问"; +"Always ask before making changes" = "修改文件或执行需要权限的操作前请求你的批准。"; +"Automatically accept all file edits" = "自动批准文件编辑,其他操作仍按权限规则处理。"; +"Create a plan before making changes" = "阅读和分析后制定计划,不修改文件。"; +"Claude handles permission decisions" = "由 Claude 自动评估并审批操作。"; +"Accepts all permissions" = "跳过常规权限审批,仍受环境和管理策略限制。"; "Always ask to edit external files and use the internet" = "修改工作区外文件或访问网络时先请求批准。"; "Only ask for actions detected as potentially unsafe" = "自动审批,仅在检测到潜在风险时请求你的确认。"; "Unrestricted access to the internet and any file on your computer" = "允许访问网络和电脑上的所有文件。"; diff --git a/macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift b/macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift index e26f085db..785028132 100644 --- a/macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift +++ b/macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift @@ -17,29 +17,35 @@ enum AgentSessionSelectorPresentation { return localized(option.name) } - static func choiceTitle(_ choice: AgentSessionConfigOption.Choice, in option: AgentSessionConfigOption) -> String { + static func choiceTitle(_ choice: AgentSessionConfigOption.Choice, in option: AgentSessionConfigOption, bundle: Bundle = .main) -> String { if option.id == "fast-mode" { - if choice.id == "off" { return String(localized: "Standard") } - if choice.id == "on" { return String(localized: "Fast") } + if choice.id == "off" { return localized("Standard", bundle: bundle) } + if choice.id == "on" { return localized("Fast", bundle: bundle) } } - return option.category == "model" ? choice.name : localized(choice.name) + // Agent-owned names must not collide with translations used elsewhere in the app. + if option.category == "model" || option.category == "mode" || option.category == "thought_level" { + return choice.name + } + return localized(choice.name, bundle: bundle) } - static func currentTitle(_ option: AgentSessionConfigOption) -> String { - option.choices.first { $0.id == option.currentValue }.map { choiceTitle($0, in: option) } ?? option.currentValue + static func currentTitle(_ option: AgentSessionConfigOption, bundle: Bundle = .main) -> String { + option.choices.first { $0.id == option.currentValue }.map { choiceTitle($0, in: option, bundle: bundle) } ?? option.currentValue } static func modeIcon(_ id: String) -> String { switch id { - case "read-only": "bubble.left.and.bubble.right" - case "agent": "checkmark.shield" - case "agent-full-access": "bolt" + case "read-only", "default", "manual": "bubble.left.and.bubble.right" + case "agent", "auto": "checkmark.shield" + case "agent-full-access", "bypassPermissions": "bolt" + case "acceptEdits": "pencil" + case "plan": "list.bullet.rectangle" default: "slider.horizontal.3" } } - static func localized(_ text: String) -> String { - String(localized: String.LocalizationValue(text)) + static func localized(_ text: String, bundle: Bundle = .main) -> String { + String(localized: String.LocalizationValue(text), bundle: bundle) } } @@ -116,7 +122,7 @@ struct AgentSessionSelectors: View { private func modelSummary(_ model: AgentSessionConfigOption) -> String { let effort = options.first { $0.category == "thought_level" } - return [model.currentLabel, effort.map(AgentSessionSelectorPresentation.currentTitle)].compactMap { $0 }.joined(separator: " ") + return [model.currentLabel, effort.map { AgentSessionSelectorPresentation.currentTitle($0) }].compactMap { $0 }.joined(separator: " ") } private func selectorLabel(@ViewBuilder content: () -> Content) -> some View { diff --git a/macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift b/macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift new file mode 100644 index 000000000..e0d05e527 --- /dev/null +++ b/macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift @@ -0,0 +1,72 @@ +import Foundation +import Testing +@testable import Lithe +@testable import LitheAgentConversationModule + +@MainActor +@Suite("Agent selector names") +struct AgentSessionSelectorPresentationTests { + @Test + func approvalMenuAndCurrentValueKeepAgentNamesDespiteChineseTranslationCollisions() throws { + let chinese = try localizationBundle("zh-Hans") + // The unrelated app translation must remain available while Agent names bypass it. + #expect(AgentSessionSelectorPresentation.localized("Plan", bundle: chinese) == "计划") + let names = ["Manual", "Accept edits", "Plan", "Auto", "Bypass permissions", "Future mode"] + let ids = ["manual", "acceptEdits", "plan", "auto", "bypassPermissions", "future"] + var option = AgentSessionConfigOption( + id: "mode", name: "Mode", category: "mode", currentValue: "plan", + choices: zip(ids, names).map { .init(id: $0.0, name: $0.1) } + ) + for language in ["zh-Hans", "en"] { + let bundle = try localizationBundle(language) + #expect(option.choices.map { + AgentSessionSelectorPresentation.choiceTitle($0, in: option, bundle: bundle) + } == names) + for choice in option.choices { + option.currentValue = choice.id + #expect(AgentSessionSelectorPresentation.currentTitle(option, bundle: bundle) == choice.name) + } + } + } + + @Test + func thinkingAndModelNamesUseTheSameUpstreamRuleInBothLanguages() throws { + let names = ["Low", "Medium", "High", "Xhigh", "Max", "Ultra"] + for language in ["zh-Hans", "en"] { + let bundle = try localizationBundle(language) + for category in ["thought_level", "model"] { + var option = AgentSessionConfigOption( + id: category, name: category, category: category, currentValue: "Medium", + choices: names.map { .init(id: $0, name: $0) } + ) + for choice in option.choices { + option.currentValue = choice.id + #expect(AgentSessionSelectorPresentation.choiceTitle(choice, in: option, bundle: bundle) == choice.name) + #expect(AgentSessionSelectorPresentation.currentTitle(option, bundle: bundle) == choice.name) + } + } + } + } + + @Test + func speedAndOtherControlsStillUseTheSelectedLanguage() throws { + let bundle = try localizationBundle("zh-Hans") + let speed = AgentSessionConfigOption( + id: "fast-mode", name: "Speed", category: "model_config", currentValue: "on", + choices: [.init(id: "off", name: "Disabled"), .init(id: "on", name: "Enabled")] + ) + #expect(AgentSessionSelectorPresentation.currentTitle(speed, bundle: bundle) + == bundle.localizedString(forKey: "Fast", value: nil, table: nil)) + let other = AgentSessionConfigOption( + id: "other", name: "Other", category: "custom", currentValue: "plan", + choices: [.init(id: "plan", name: "Plan")] + ) + #expect(AgentSessionSelectorPresentation.currentTitle(other, bundle: bundle) == "计划") + } + + private func localizationBundle(_ language: String) throws -> Bundle { + let repository = URL(fileURLWithPath: #filePath).deletingLastPathComponent() + .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() + return try #require(Bundle(url: repository.appendingPathComponent("macos/Resources/\(language).lproj"))) + } +} diff --git a/shared/platform-feature-matrix.json b/shared/platform-feature-matrix.json index ad707092a..3a0e9fc33 100644 --- a/shared/platform-feature-matrix.json +++ b/shared/platform-feature-matrix.json @@ -266,6 +266,54 @@ "owner": "Agent", "verification": "macOS + Codex + API Key:验证发消息前的上游配置选项和确认;无自定义服务商或 API Key 时确认仍显示本机顶层默认模型、保留密钥缺失错误;修改本机默认模型后重连,连续新建空会话确认不再使用旧导入模型;配置不在上游目录中的旧模型时确认新会话使用目录内的上游推荐值、拒绝与超时不显示假成功且 Codex 模型菜单显示 OpenAI 标志;验证工具输入/输出/文件/变更、权限拒绝、停止确认及超时重连、历史加载失败保留记录;临时项目跑通读文件→修改→执行测试→追问,复用文档观察和脏缓冲区保存保护。检查模型搜索(名称/ID/分组、空结果)、品牌图标/选中状态、无模型说明、权限说明、思考/速度子菜单、确认/失败与会话切换关闭弹出面板;检查项目切换、退出清理,以及 CC GUI 风格空态、搜索、标签、可调整输入区和配置菜单在窄宽面板及深浅主题下的布局。Windows 与 Claude 端到端仍待验证。 安装包环境下打开 Agent 面板,确认 Codex/Claude 图标从 Contents/Resources 加载且不依赖构建目录;资源包或图标缺失时显示默认图标而不闪退,读取前后包内文件清单和内容不变。" }, + { + "id": "agent-thinking-level-labels", + "area": "AI", + "group": "Agent 对话", + "capability": "Agent 思考强度英文呈现:Claude/Codex 子菜单、设置行当前值和模型底栏统一使用上游英文名称", + "macos": { + "implementationStatus": "implemented", + "verificationStatus": "verified", + "evidence": [ + "macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift", + "macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift" + ] + }, + "windows": { + "implementationStatus": "missing", + "verificationStatus": "pending", + "evidence": [ + "shared/contracts/application-boundary.md" + ] + }, + "owner": "Agent", + "verification": "2026-10-01 macOS:最新 preview 独立 PR 分支 83 项 Agent 回归通过;含 PR #1007 前置修复的组合构建 87 项回归及真实界面验收通过;中文真实界面确认 Codex 的 Low、Medium、High、Xhigh、Max、Ultra 和 Claude 的 Low、Medium、High、Max 均显示英文。两边分别切换 Low,确认子菜单、设置行当前值和模型底栏同步英文,并恢复 Claude Medium、Codex Xhigh;权限、速度和思考强度标题仍显示中文。不改原始选项 ID、顺序和确认行为,运行前后 bundle 全部 990 个文件 SHA-256 一致。Windows 对话 UI 仍待实现。" + }, + { + "id": "agent-approval-mode-presentation", + "area": "AI", + "group": "Agent 对话", + "capability": "Agent 权限模式呈现:Claude/Codex 名称保留上游原文,说明按界面语言显示并补齐图标,保留各 Agent 实际支持的选项及权限语义", + "macos": { + "implementationStatus": "implemented", + "verificationStatus": "pending", + "evidence": [ + "macos/Sources/Lithe/Views/Agent/AgentSessionSelectors.swift", + "macos/Resources/en.lproj/Localizable.strings", + "macos/Resources/zh-Hans.lproj/Localizable.strings", + "macos/Tests/LitheTests/AgentSessionSelectorPresentationTests.swift" + ] + }, + "windows": { + "implementationStatus": "missing", + "verificationStatus": "pending", + "evidence": [ + "shared/contracts/application-boundary.md" + ] + }, + "owner": "Agent", + "verification": "中文和英文环境分别验证 Claude/Codex:权限名称保留上游原文,Plan 菜单与底栏一致,已知权限 ID 显示对应图标,未知选项原文和默认图标不变;说明使用当前界面语言,保留各 Agent 实际的审批和环境约束。检查原始 ID、数量、顺序及确认/失败行为不变。使用指定语言的资源 Bundle 验证名称与通用 Plan 翻译不会串用;整合紧凑布局后检查双行说明和长列表滚动。Windows 对话 UI 待实现。" + }, { "id": "agent-tool-activity-timeline", "area": "AI",