Skip to content

fix(agent): 修复 Claude 鉴权、会话切换和品牌图标 - #1007

Open
Mucheen wants to merge 5 commits into
1lck:previewfrom
Mucheen:fix/agent-auth-switching-icons
Open

Mucheen wants to merge 5 commits into
1lck:previewfrom
Mucheen:fix/agent-auth-switching-icons

Conversation

@Mucheen

@Mucheen Mucheen commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Claude 使用支持 Anthropic Messages 的 API Key 服务商时,适配器的 gateway 模式会额外加入占位 Bearer acp-proxy,部分服务商因此返回 401;另外,Agent 选择已改变时,界面可能仍绑定旧连接,首次切到 Codex 还会短暂显示保存的旧模型。关闭最后一个会话标签时,准备状态还可能永久阻止发送。

本次修复使 Claude 新建和恢复会话走同一条原生 API Key 路由,Agent 切换立即更新界面,会话设置确认后再显示模型,关闭最后一个标签会自动准备新会话并在确认后恢复发送;同时参照 CC GUI 完善底栏和 Agent 菜单的品牌图标。

改动说明

  1. Claude 鉴权与历史恢复:复用固定版本适配器公开的 _meta.claudeCode.options,在 session/new、session/load 通过 ACP stdio 传递服务商、密钥和模型。SDK 环境与程序化 settings 使用同一路由,并清除冲突的 Bearer、OAuth、自定义请求头、云平台路由及 apiKeyHelper;缺少凭据直接报错。Codex 继续使用原有 gateway 鉴权。

  2. Agent / 模型切换:增加直接观察 AgentConversationFeatureModel 的视图,在选择变化时解析正确连接,并按 Agent ID 更新连接视图。保留各 Agent 已有的连接、历史与会话配置;模型仍等待上游确认再更新显示。

  3. 会话准备状态:连接、新建及历史加载期间显示“正在加载会话设置…”,等待包含默认模型修正的会话确认,避免先显示旧模型再跳变。等待期间同时阻止按钮和 Enter 提交;失败或上游不提供配置选项时结束等待。关闭最后一个标签由连接模型立即发起新建请求,复用已有创建状态防止重复请求;旧确认不能重新选中关闭的会话,新确认后底栏和发送恢复。还有其他标签时继续切到已有会话,断连时不创建。补齐中英文提示。

  4. 底栏与模型品牌图标:Claude 使用橙色星芒,Codex 使用随深浅主题变化的灰色结形标志;Agent 入口为 18 点图标、28 点点击区域,模型入口及模型列表同步品牌。复用既有 SVG,在内存中染色以保留原生菜单的颜色;缓存包含资源包、尺寸和颜色,共用模板保持原有行为。

  5. Agent 下拉菜单:Claude / Codex 名称前均显示 16 点品牌图标,使用原生单选菜单独立呈现选中勾选,勾选与品牌图标同时保留。为 Agent、模型、权限入口补充稳定的无障碍标识。

  6. 回归、契约与文档:新增真实 SwiftUI 宿主回归,覆盖选择观察、确认前后、过期响应、新建/历史加载、失败回退、连接复用和清理,以及四种品牌/主题组合的原生图标像素检查。Rust fixture 覆盖 Claude 冲突凭据清理和新建/恢复路由;真实集成测试支持选择 Claude,并要求切到不同模型后收到回复,后续选项取自最新确认目录。同步共享契约、中文 Agent Note 和两份生成的功能矩阵。

  7. 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 字节一致。本次修正其采样工作量,未通过跳过检查或提高预算解除失败。

平台与已知限制

  • Windows 复用共享 Rust 路由,本次没有完成 Windows 对话界面或实机验收;功能矩阵保留相应待完成状态。
  • 服务边界检查仍报告 macos/Sources/Lithe/Models/AppModel/AppModel+RunConfiguration.swift 为 616 行,超过 600 行。该文件与当前 preview 字节完全一致,本 PR 未修改;不将全部静态门禁标为通过。
  • Claude SDK 的子进程凭据暴露仍存在:stdio 保证覆盖 Lithe 启动 ACP 适配器,官方 SDK 会把对象形式的 options.settings 序列化为 CLI 的 --settings 参数,API Key 因而可出现在 Claude CLI 子进程参数和 SDK debug 输出中。已在共享契约和中文 Agent Note 明确该边界。把 Key 仅移至 options.env 需要另行验证用户/项目 settings 的覆盖优先级;本 PR 保留当前可用的鉴权路由,不声称已消除这项上游风险。
  • 鉴权依赖固定适配器的公开扩展,升级适配器时须继续验证新建及历史恢复。该修改未改变 C ABI/应用 JSON 形状,未修改已安装第三方包,也未新增下载、磁盘缓存或可复用工作树资源。

分支同步

已再次同步最新 preview(7248ba10,已合入 #1008)。冲突集中在共享 ACP 决策笔记与生成的 Markdown/CSV 矩阵:笔记同时保留本 PR 的 Agent 观察/品牌菜单决策和 #1008 的权限名称/英文思考强度决策;矩阵从自动合并后的 JSON 重新生成,语义三方比较确认完整保留共 125 条能力记录。Swift 选择器及中英文资源自动合并后已验证,保留 #1008 的权限图标和说明、本 PR 的品牌及无障碍标识,并原样保留末标签 review 修复和 CI 视口采样修复。保留原 PR 提交历史,使用普通快进推送。

通过 Claude 适配器公开会话参数传递 API Key 路由,同时覆盖新建与历史恢复,清除冲突鉴权并保留 Codex gateway 行为。
直接观察所选 Agent 连接,等待会话设置确认后展示模型,防止旧配置闪现及准备期间提交。
统一底栏、模型入口与 Agent 菜单品牌图标,保留原生单选勾选,并补充回归、共享契约、Agent Note 与功能矩阵。
@ghfind-review ghfind-review Bot added the review: medium ghfind author score; see https://ghfind.com label Oct 1, 2026
xiaoyumuxi
xiaoyumuxi previously approved these changes Oct 1, 2026

@xiaoyumuxi xiaoyumuxi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

重点检查了 Claude API-key 路由、session/new 与 session/load 的一致性、gateway 登录分支、模型/Agent 切换准备态以及品牌图标缓存。凭据只通过 ACP session metadata 传入,Claude 不再走冲突的 gateway authenticate;新建和历史恢复都复用同一路由,相关 Rust/UI 回归测试覆盖到位,当前 CI 全绿。没有发现阻断项。

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 展开标签条,关闭唯一的标签。输入文字后回车,或点击发送。

修复建议: 二选一

  1. 在 AgentConnectionModel.closeConversation 中,当 selectedSessionID 变为 nil 且 connectionState == .ready 时调用 prepareConversation()。
  2. 或者让 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 中写明这一边界。

与其他 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 对话路径没有实机验证。

处理 PR 1lck#1007 review:在连接模型发起最后一个标签关闭后的新建请求,补齐 Claude/Codex 原生回归;明确 Claude SDK CLI 参数与 debug 的凭据边界。同步最新 preview 并重新生成合并后的功能矩阵。
修复 PR 1lck#1007 CI:原用例重复绘制完整 1000 行历史,CI 耗时 11.791 秒超过未调整的 10 秒上限。改为开头、中段、末段 40 行视口采样,校验实际像素,保留完整结构、单帧预算与 Release 基线;同步 CI 文档和中文工程笔记。
@Mucheen

Mucheen commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

针对这次 review,处理结果如下。P1 修复已提交于 0f543b77,CI 采样修复于 5dc54c03。

  1. P1:关闭最后一个标签后无法发送——已修复。
    AgentConnectionModel.closeConversation 在选中会话变为 nil 时调用既有 prepareConversation();由其 ready 和创建 token 的检查保证只在连接可用时发起一次 newSession。不再依赖视图重新出现或 connectionState 再变化。保留准备期间的模型加载提示和发送限制,收到新会话确认后恢复底栏和发送;还有其他标签时继续回退到已有会话。
    新增 Claude/Codex 参数化原生 SwiftUI 回归,覆盖「准备完成→关闭最后标签→唯一新建请求→重复准备不重复请求→旧确认被忽略→新确认恢复底栏→消息发往新会话」。先确认修改前失败,再确认修改后通过;测试窗口与模拟连接均清理。
    代码:连接模型、回归测试。

  2. Claude SDK 子进程凭据观察项——已澄清边界,暴露风险仍存在。
    共享契约和中文 Agent Note已明确:stdio 保证覆盖 Lithe 启动 ACP 适配器这一层,SDK 仍会将对象 settings 序列化到 Claude CLI 的 --settings 参数,debug 输出也可能记录完整参数。本 PR 不声称消除了整个进程树中的 Key 暴露。
    将 Key 仅放到 options.env 的改造仍需独立评估,尤其要验证用户/项目 settings 中旧 Key 的优先级,以及新建、恢复和服务商切换都使用所选凭据;当前保留经过验证的鉴权路由。

  3. 验证与 CI——当前 head 已通过所有被选中的检查。
    本地相关 Agent Swift 计时记录 88 项通过,Rust Agent Host 74 项通过,3 项真实服务集成测试按原规则默认忽略。随后 CI 的失败实际发生在 Git 图形采样:断言已通过,但重复绘制全部 1,000 行的用例耗时 11.791 秒,超过计时工具的 10 秒上限。
    已改为完整历史上的开头/中段/末段 40 行视口采样,检查实际图形像素,保留原有时间预算、完整图结构与 Release 验证。完整本地 Git CI 入口通过:19 项 Debug 计时记录、2 组 Release 场景。
    最新 macOS CI(含 Swift、Git 性能、Rust/Swift bridge 和双架构打包)、Windows CI、数据库 CI、插件 CI、Agent Notes 与功能矩阵检查均通过。Windows 真实 JDT 专项按路径分类器跳过;这不代表已完成 Windows Agent 对话 UI 的实机验收。

已同步 preview 的 afb7a766,矩阵以合并后的 JSON 重新生成,当前 PR 无冲突。#1008 的权限菜单与英文强度功能保持独立,未并入本 PR。可以重新 review P1 修复及相关回归。

@1lck 1lck left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 成功不消除这两项限制。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: medium ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants