Conversation
通过 Claude 适配器公开会话参数传递 API Key 路由,同时覆盖新建与历史恢复,清除冲突鉴权并保留 Codex gateway 行为。 直接观察所选 Agent 连接,等待会话设置确认后展示模型,防止旧配置闪现及准备期间提交。 统一底栏、模型入口与 Agent 菜单品牌图标,保留原生单选勾选,并补充回归、共享契约、Agent Note 与功能矩阵。
xiaoyumuxi
left a comment
There was a problem hiding this comment.
重点检查了 Claude API-key 路由、session/new 与 session/load 的一致性、gateway 登录分支、模型/Agent 切换准备态以及品牌图标缓存。凭据只通过 ACP session metadata 传入,Claude 不再走冲突的 gateway authenticate;新建和历史恢复都复用同一路由,相关 Rust/UI 回归测试覆盖到位,当前 CI 全绿。没有发现阻断项。
1lck
left a comment
There was a problem hiding this comment.
Review 结论
⛔ 存在阻塞问题
Findings
[P1] 关闭最后一个会话标签后,输入框一直显示"正在加载会话设置…",无法发送
位置: macos/Sources/Lithe/Views/Agent/AgentConversationView.swift:164-173(第 170 行),连带 AgentComposerView.swift:148、:210
实际影响:
-
新增的
isPreparingSession在.ready状态下,把selectedSessionID == nil && errorMessage == nil当成"准备中"。但没有任何代码在这个状态下发出新建请求,结果是:- 底栏一直显示加载提示;
- 发送按钮被禁用,回车报"仍在准备";
- 头部 "+" 按钮(
AgentConversationView.swift:212,.disabled(feature.selectedSessionID == nil))和标签条的新建按钮(:413,.disabled(showsNewTab))也都不可用。
-
用户只能切换 Agent 再切回来,或从历史中选一个会话来恢复。PR 之前,同样状态下
AgentConnectionModel.send会在selectedSessionID == nil时调用prepareConversation()并把消息排队,所以可以直接发送。
触发条件: 连接为 .ready,当前只有一个已打开的会话。用户点"会话标签"按钮展开标签条,再关闭这个标签。closeConversation 在 AgentConnectionModel.swift:253-255(本 PR 未修改)把 selectedSessionID 设为 openSessionIDs.last,此时为 nil。prepareConversation() 只在 onAppear 和 connectionState 变为 .ready 时调用,这条路径不会触发。
复现步骤: 连接 Codex 或 Claude,等首个空会话准备好。点击 rectangle.split.2x1 展开标签条,关闭唯一的标签。输入文字后回车,或点击发送。
修复建议: 二选一
- 在
AgentConnectionModel.closeConversation中,当selectedSessionID变为nil且connectionState == .ready时调用prepareConversation()。 - 或者让
isPreparingSession在.ready时只看isCreatingSession || selectedConversation?.isLoading == true,"ready 到发出创建请求之间"的空档改用模型状态判断,不依赖selectedSessionID == nil。
同时在 AgentConversationSelectionTests 中加一个用例:准备完成 → 关闭最后一个标签 → 断言底栏不再显示加载,且发送能触发 newSession。
观察项(不阻塞,不计入 Findings)
- Claude 的 API Key 会以 Claude Code CLI 子进程的
--settings命令行参数出现。 这是历史问题,本次没有加剧,但新增文档的措辞容易让人误读:- SDK 把对象形式的
settings经JSON.stringify拼成--settings <json>追加到 CLI 的 argv。 - 打开 SDK debug 时,
Spawning Claude Code: ${args}会把完整 argv 写进 debug 文件。 - 本 PR 在
rust/lithe-agent-host/src/session_routing.rs:43把ANTHROPIC_API_KEY放进了settings.env。PR 之前的 gateway 模式下,适配器同样把带x-api-key: <key>的ANTHROPIC_CUSTOM_HEADERS合并进settings.env,泄露面相同。 - 契约
shared/contracts/rust-core-api.md:54和 Agent Note 第 26 行写的是"不进入 Lithe 启动适配器的命令行参数"。字面上没错,但 SDK 再拉起的 CLI 子进程就在 argv 里带着 Key,同用户进程、活动监视器、诊断包都能看到。 - 建议另开 Issue:评估只在
options.env中放 Key、settings.env只放非敏感路由的可行性(需要处理用户settings.json中ANTHROPIC_API_KEY的覆盖优先级)。同时在契约和 Note 中写明这一边界。
- SDK 把对象形式的
与其他 PR 的关系
-
与 #1016 合并后语义一致。 用
git merge-tree在内存中合并后检查:AgentComposerView的成员声明顺序与调用处一致。- #1016 新增的
submit()最终调用send(),会受到本 PR 新增的isPreparingSession拦截。命令补全在准备期间仍可用,但不会发送。行为合理。 tests.rs的两组改动互不重叠。合并后的acp-events-v1.json和platform-feature-matrix.json都能正常解析。- 唯一的文本冲突在生成文件
docs/development/platform-parity-matrix.md,应以合并后的 JSON 运行node scripts/generate-platform-feature-matrix.mjs重新生成,不要手工解决。 - 注意:P1 问题在合并 #1016 后依然存在。
-
与 #1008 的冲突。
.agents/notes/implemented/architecture/2026-09-25-shared-acp-agent-conversation.md在"## 决策"首条位置冲突:本 PR 把空行换成了"Agent 菜单标志与勾选",#1008 在同一位置插入了"思考强度名称"和"权限模式呈现"。两边应全部保留。platform-parity-matrix.csv/.md也有冲突,同样以 JSON 为准重新生成。
Summary
Rust 鉴权路由实现得很集中,Codex 路径被 protocol 过滤隔离开了。fixture 与 Harness 测试覆盖了新建、恢复、凭据缺失三种情况。日志脱敏沿用 redact(secret),没有新增输出 Key 的日志。
Swift 侧把 Agent 选择观察拆成 AgentConfiguredConversationView,层次更清楚。染色图像只存在内存,缓存键包含颜色,不会改写共用的模板。
主要问题是新的准备状态谓词漏了一个可达状态,会把主要发送路径卡死。修复工作量小,但需要补测试。
CI 全部通过。Windows 对话路径没有实机验证。
|
针对这次 review,处理结果如下。P1 修复已提交于 0f543b77,CI 采样修复于 5dc54c03。
已同步 |
1lck
left a comment
There was a problem hiding this comment.
Lithe Review|P1 修复复核
结论:原 P1 已修复,本次限定复审未发现需要阻塞合并的问题。
范围:当前 HEAD 5dc54c0,重点复核末标签关闭修复、直接调用链与相关回归,不扩大为整份 PR 的全面批准。
Scope Check
- AgentConnectionModel.swift:244–258 在选中末标签关闭后立即复用 prepareConversation(),不再依赖视图重现或连接状态变化。既有 ready/createToken 检查避免断连创建及重复请求。
- token 校验拒绝旧确认;新确认选中新会话并解除准备状态。已有标签回退、忙碌/加载/权限期间关闭保护及创建失败路径未发现本次引入的回归。
- AgentConversationSelectionTests.swift:11–73 覆盖 Claude/Codex 的唯一创建请求、重复准备、过期确认、底栏恢复及新会话发送,并清理窗口和模拟连接。#1008 继续独立跟踪。
Verification
独立读取了该 HEAD 的 CI:六个 PR workflows 均成功。Swift job 日志明确记录末标签回归两组参数通过(0.315 秒),完整 Swift 1,574 项通过。
https://github.com/1lck/Lithe-IDEA/actions/runs/36890112203/job/110463390778
本轮仅静态只读复审并读取已有 CI,没有启动应用或重跑测试。新增回归使用原生宿主与模型事件驱动,未直接模拟点击发送/Enter;两条提交路径另做了静态核对。
Claude SDK 子进程 --settings/debug 中的凭据暴露仍存在,shared/contracts/rust-core-api.md:66–74 已澄清边界。Windows 对话 UI 和实机验收仍待完成;CI 成功不消除这两项限制。
Claude 使用支持 Anthropic Messages 的 API Key 服务商时,适配器的 gateway 模式会额外加入占位
Bearer acp-proxy,部分服务商因此返回 401;另外,Agent 选择已改变时,界面可能仍绑定旧连接,首次切到 Codex 还会短暂显示保存的旧模型。关闭最后一个会话标签时,准备状态还可能永久阻止发送。本次修复使 Claude 新建和恢复会话走同一条原生 API Key 路由,Agent 切换立即更新界面,会话设置确认后再显示模型,关闭最后一个标签会自动准备新会话并在确认后恢复发送;同时参照 CC GUI 完善底栏和 Agent 菜单的品牌图标。
改动说明
Claude 鉴权与历史恢复:复用固定版本适配器公开的
_meta.claudeCode.options,在session/new、session/load通过 ACP stdio 传递服务商、密钥和模型。SDK 环境与程序化 settings 使用同一路由,并清除冲突的 Bearer、OAuth、自定义请求头、云平台路由及apiKeyHelper;缺少凭据直接报错。Codex 继续使用原有 gateway 鉴权。Agent / 模型切换:增加直接观察
AgentConversationFeatureModel的视图,在选择变化时解析正确连接,并按 Agent ID 更新连接视图。保留各 Agent 已有的连接、历史与会话配置;模型仍等待上游确认再更新显示。会话准备状态:连接、新建及历史加载期间显示“正在加载会话设置…”,等待包含默认模型修正的会话确认,避免先显示旧模型再跳变。等待期间同时阻止按钮和 Enter 提交;失败或上游不提供配置选项时结束等待。关闭最后一个标签由连接模型立即发起新建请求,复用已有创建状态防止重复请求;旧确认不能重新选中关闭的会话,新确认后底栏和发送恢复。还有其他标签时继续切到已有会话,断连时不创建。补齐中英文提示。
底栏与模型品牌图标:Claude 使用橙色星芒,Codex 使用随深浅主题变化的灰色结形标志;Agent 入口为 18 点图标、28 点点击区域,模型入口及模型列表同步品牌。复用既有 SVG,在内存中染色以保留原生菜单的颜色;缓存包含资源包、尺寸和颜色,共用模板保持原有行为。
Agent 下拉菜单:Claude / Codex 名称前均显示 16 点品牌图标,使用原生单选菜单独立呈现选中勾选,勾选与品牌图标同时保留。为 Agent、模型、权限入口补充稳定的无障碍标识。
回归、契约与文档:新增真实 SwiftUI 宿主回归,覆盖选择观察、确认前后、过期响应、新建/历史加载、失败回退、连接复用和清理,以及四种品牌/主题组合的原生图标像素检查。Rust fixture 覆盖 Claude 冲突凭据清理和新建/恢复路由;真实集成测试支持选择 Claude,并要求切到不同模型后收到回复,后续选项取自最新确认目录。同步共享契约、中文 Agent Note 和两份生成的功能矩阵。
CI Git 帧采样修复:离屏图形采样保留完整 1,000 行历史,分别绘制开头、中间、末尾的 40 行视口;采用生产行高,正确平移中段坐标,并检查真实像素的 alpha,防止空位图给出虚假的快速结果。保留 10 秒测试总预算、100 ms 单帧中位数上限和完整图结构/Release 基线,同步 CI 文档与中文工程笔记。
验证
本轮合并后的 macOS 相关 Agent 回归 91 项计时记录通过:
test-stability-macos.sh -- --filter 'AgentConversation|AgentHistory|AgentFileReference|AgentProviderConfiguration|AgentBrandIconResource|AgentManagement|AgentSessionSelectorPresentation'。Swift Testing 总计报告 92 个测试,计时报告记录 91 项;包含 Claude/Codex 末标签关闭回归,以及 统一 Agent 权限及思考强度的上游名称与图标 #1008 的权限名称、英文思考强度和其他控件本地化回归,无性能警告。末标签回归本轮合计 63 ms;此前已验证修改前失败、修改后通过。逐测试 JSON、JUnit 和 HTML 已核对。Rust Agent Host 74 项通过,3 项真实服务测试默认忽略;忽略项不计入通过。
cargo fmt --manifest-path rust/Cargo.toml -p lithe-agent-host -- --check通过。已单独完成真实 Claude 文件读取→修改→执行 Node 测试→追问(52.878 秒),取消与历史恢复(14.521 秒),以及 Sonnet→Haiku 切换后正常回复(8.791 秒)。
macOS 构建、打包和实际界面验收通过:Claude/Codex 切换、加载状态、模型菜单搜索/勾选、两个品牌菜单图标及选中勾选显示正常;真实 Codex 回复已通过。
典型界面工作流及最新菜单验证前后,bundle 990 个文件清单及 SHA-256 一致。测试窗口、临时连接和旧应用进程已清理。
测试稳定性、共享契约、Agent Note、Windows 静态边界、bundle 只读边界、功能矩阵及实际变更门禁、diff 空白检查通过。本轮测试窗口和模拟连接均关闭,未启动额外应用或发送真实模型请求。
Git 性能基线完整入口通过:Debug 19 项计时记录通过(帧采样包含 3 组位置参数,2 项真实窗口测试按默认规则跳过),Release 2 组 1,000/5,000 行场景通过。命令:
LITHE_GIT_PERFORMANCE_SKIP_TEST_BUILD=1 ./scripts/test-git-performance-baseline.sh,Swift 6.3.3 与 CI 一致。本轮修改后的视口采样合计 421 ms;保留原有所有预算。已定位原 CI 失败:Swift 单元测试 1,574 项和回滚集成测试已通过,Git 图形用例也通过断言,但全历史重复绘制使其耗时 11.791 秒,超过计时工具 10 秒上限。该 Git 用例在修改前与最新
preview字节一致。本次修正其采样工作量,未通过跳过检查或提高预算解除失败。平台与已知限制
macos/Sources/Lithe/Models/AppModel/AppModel+RunConfiguration.swift为 616 行,超过 600 行。该文件与当前preview字节完全一致,本 PR 未修改;不将全部静态门禁标为通过。options.settings序列化为 CLI 的--settings参数,API Key 因而可出现在 Claude CLI 子进程参数和 SDK debug 输出中。已在共享契约和中文 Agent Note 明确该边界。把 Key 仅移至options.env需要另行验证用户/项目 settings 的覆盖优先级;本 PR 保留当前可用的鉴权路由,不声称已消除这项上游风险。分支同步
已再次同步最新
preview(7248ba10,已合入 #1008)。冲突集中在共享 ACP 决策笔记与生成的 Markdown/CSV 矩阵:笔记同时保留本 PR 的 Agent 观察/品牌菜单决策和 #1008 的权限名称/英文思考强度决策;矩阵从自动合并后的 JSON 重新生成,语义三方比较确认完整保留共 125 条能力记录。Swift 选择器及中英文资源自动合并后已验证,保留 #1008 的权限图标和说明、本 PR 的品牌及无障碍标识,并原样保留末标签 review 修复和 CI 视口采样修复。保留原 PR 提交历史,使用普通快进推送。