Skip to content

fix: stabilize browser media sessions - #26

Open
lanyue-llk wants to merge 9 commits into
lex-mainfrom
codex/browser-face-media-gate-v2
Open

fix: stabilize browser media sessions#26
lanyue-llk wants to merge 9 commits into
lex-mainfrom
codex/browser-face-media-gate-v2

Conversation

@lanyue-llk

Copy link
Copy Markdown

Summary

  • preserve room-owned audio rendering across browser session transitions
  • make session stop idempotent and clean up resources reliably
  • verify browser echo-cancellation behavior
  • keep the activity-intelligence frontend deployment isolated from other Lex Voice instances

Verification

  • 264 frontend tests passed
  • lint passed
  • production build passed
  • local end-to-end Start Call → agent ready → End Call passed on the isolated deployment

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 1m 59s —— View job


Todo

  • 获取 PR 上下文
  • 拉取 diff 并分析改动
  • 检查 LiveKit room/track 生命周期
  • 检查浏览器媒体权限失败路径
  • 检查 React hook 清理
  • 检查 Next.js 客户端/服务端边界、环境变量泄露
  • 输出 review 结论

Review 结论

有条件通过

问题及建议

  • [P0] hooks/useBrowserSourceClient.ts:165logBrowserAudioCaptureDiagnostics(captureTrack)(内部调用 assertBrowserEchoCancellationActive,见 lib/browser-audio-capture.ts:60-66)在 try 块(第 168 行开始)之前执行。一旦某些浏览器/设备上 getSettings().echoCancellation 未回报为 true(常见于部分移动端/Safari),该函数会直接 throw,此时已通过 createLocalAudioTrack 拿到的麦克风 audioTrack 不会被 stop()/mute(),导致麦克风硬件保持占用(热麦克风泄漏),且整个 ensureAudioPublished 直接失败,Start Call 流程中断。建议:把该诊断/断言调用移入 try 块内,或在抛出前显式停止 audioTrack,或将其改为仅记录日志、不 throw(毕竟目的是诊断而非硬性阻断)。
  • [P1] PR 描述提到"keep the activity-intelligence frontend deployment isolated from other Lex Voice instances",但本次 diff 的 7 个文件均不涉及部署/环境隔离相关改动,描述与实际变更不匹配,建议确认是否有遗漏的 commit 或描述需要更正。
  • [P1] components/livekit/filtered-audio-renderer.tsx:402-410hooks/useBrowserSourceClient.ts:165:新增的诊断日志使用裸 console.info/console.warn 无条件输出(前者在每次远端音轨开始播放时触发),而同文件其余诊断日志均通过 debugAudioLog(debugAudio, ...)(见 filtered-audio-renderer.tsx:24)按调试开关控制。建议保持一致,避免生产环境控制台噪音。
  • [P2] lib/session-stop.ts:26-33isLiveKitRoomNotFoundError 要求 status === 404 且 code === 'not_found' 同时成立,未见对 livekit-server-sdk 实际抛出错误的形态做注释/引用确认;若 SDK 版本升级后只设置其中一个字段,会静默退回到"删除失败"分支。建议补充来源说明或做更宽松的判断(如 status === 404 || code === 'not_found',需结合 SDK 语义确认)。
  • [P2] 新增测试(tests/browser-audio-capture.test.mjs)仅覆盖 lib/browser-audio-capture.ts 中的纯函数,未覆盖 useBrowserSourceClient.ts 中实际调用路径(即上述 P0 场景:assert 抛出时 track 是否被正确清理),建议补充针对 ensureAudioPublished 的集成测试。

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 1m 24s —— View job


Todo

  • 获取 PR 上下文
  • 拉取 diff 并分析改动
  • 检查 LiveKit room/track 生命周期
  • 检查浏览器媒体权限失败路径
  • 检查 React hook 清理
  • 检查 Next.js 客户端/服务端边界、环境变量泄露
  • 输出 review 结论

Review 结论

有条件通过

问题及建议

  • [P0] hooks/useBrowserSourceClient.ts:165ensureAudioPublished 内):logBrowserAudioCaptureDiagnostics(captureTrack)try 块(第 168 行开始)之前调用,其内部 assertBrowserEchoCancellationActivelib/browser-audio-capture.ts:60-66)会在 getSettings().echoCancellation !== true 时直接 throw。此时 createLocalAudioTrack 已产生的 audioTrack 未进入 try 块,第 246-251 行的清理逻辑(stop()/mute())不会执行,导致麦克风硬件持续占用(热麦泄漏),ensureAudioPublished 直接失败、Start Call 中断。该问题在上一轮 review 中已指出,本次 push 未修复。建议:将诊断/断言调用移入 try 块内,或改为仅记录日志不 throw。
  • [P1] 本次 diff 相对 origin/lex-main 新增 10988 行、修改 58 个文件,但与 PR 描述(稳定浏览器媒体会话)直接相关的仅 hooks/useBrowserSourceClient.tslib/browser-audio-capture.tslib/session-stop.tscomponents/livekit/filtered-audio-renderer.tsxapp/api/session/stop/route.ts 等少数文件。其余约 4000+ 行为整套 components/agents-ui/*components/ai-elements/*components/ui/*(含 960 行 react-shader-toy.tsx)等 shadcn UI 组件库代码,以及 package.json 中新增十余个不相关依赖(@xyflow/reactaicmdkembla-carousel-reactmedia-chromeshikistreamdowntokenlens@rive-app/react-webgl2 等)、新增 app/api/token/route.tsapp/layout.tsx 中硬编码 "Built with Lexmount Agent Studio" 品牌 header。这些改动与"稳定浏览器媒体会话"目标无关,建议拆分为独立 PR,避免审查风险与合并冲突面被放大。
  • [P1] components/livekit/filtered-audio-renderer.tsx:402-410hooks/useBrowserSourceClient.ts:165:新增诊断日志使用裸 console.info/console.warn 无条件输出(前者在每次远端音轨开始播放时触发),而同文件其余诊断日志均通过 debugAudioLog(debugAudio, ...)(见 filtered-audio-renderer.tsx:24)按调试开关控制,风格不一致,会产生生产环境控制台噪音。建议统一走 debugAudioLog
  • [P2] lib/session-stop.ts:26-33isLiveKitRoomNotFoundError 要求 status === 404 && code === 'not_found' 同时成立,未注明依据的是 livekit-server-sdk 哪个版本的错误形态;若后续 SDK 只设置其中一个字段会静默退回"删除失败"分支。建议补充来源注释或放宽为 ||(需先确认 SDK 语义)。
  • [P2] tests/browser-audio-capture.test.mjs 仅覆盖 lib/browser-audio-capture.ts 纯函数,未覆盖 useBrowserSourceClient.tsensureAudioPublished 的实际调用路径,即上述 P0 场景(assert 抛出时 track 是否被清理)没有回归测试保护。建议补充针对该路径的集成测试。
  • [P2] app/api/token/route.ts 是新增的、与 app/api/connection-details/route.ts 功能重复的 token 路由(来自 shadcn agents-ui 模板),虽有 NODE_ENV !== 'development' 门禁,但与本 PR 目标无关,属于上述范围蔓延的一部分,建议随同其他无关文件一并移出本 PR。

Fix this →

@lanyue-llk
lanyue-llk changed the base branch from lex-main to main August 28, 2026 07:28
@lanyue-llk
lanyue-llk changed the base branch from main to lex-main August 28, 2026 09:23
@lanyue-llk
lanyue-llk force-pushed the codex/browser-face-media-gate-v2 branch from 473b555 to d16d644 Compare August 28, 2026 09:32
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 1m 48s —— View job


Todo

  • 获取 PR 上下文
  • 拉取 diff 并分析改动(git diff origin/lex-main...HEAD,11 个文件,+295/-42)
  • 检查 LiveKit room/track 生命周期
  • 检查浏览器媒体权限失败路径
  • 检查 React hook 清理
  • 检查 Next.js 客户端/服务端边界、环境变量泄露
  • 输出 review 结论

Review 结论

不通过(此前两轮 review 已指出的 P0 问题本次 push 仍未修复)

问题及建议

  • [P0] hooks/useBrowserSourceClient.ts:165logBrowserAudioCaptureDiagnostics(captureTrack)(内部调用 assertBrowserEchoCancellationActive,见 lib/browser-audio-capture.ts:58-66)仍在 try 块(第 168 行开始)之前执行。当浏览器/设备上 getSettings().echoCancellation !== true 时会直接 throw,此时 createLocalAudioTrack 产生的 audioTrack 未进入 try 块,第 246-251 行的 stop()/mute() 清理逻辑不会执行,导致麦克风硬件持续占用(热麦泄漏),ensureAudioPublished 直接失败、Start Call 中断。该问题已连续两轮 review 提出,仍未修复。建议:把诊断/断言调用移入 try 块内,或让 assertBrowserEchoCancellationActive 仅记录日志不 throw。
  • [P1] components/livekit/filtered-audio-renderer.tsx:404-410hooks/useBrowserSourceClient.ts:717-721:新增诊断日志使用裸 console.info/console.warn 无条件输出(前者在每次远端音轨开始播放时触发),而同文件其余诊断日志均通过 debugAudioLog(debugAudio, ...)(见 filtered-audio-renderer.tsx:24)按调试开关控制,风格不一致,会在生产环境产生控制台噪音。建议统一走 debugAudioLog(或等价开关)。
  • [P2] tests/browser-audio-capture.test.mjs 仍只覆盖 lib/browser-audio-capture.ts 纯函数,未针对 hooks/useBrowserSourceClient.tsensureAudioPublished 的实际调用路径补充回归测试,即上述 P0 场景(assert 抛出时 track 是否被正确清理)没有测试保护。建议补充集成测试。
  • [P2] lib/session-stop.ts:26-33isLiveKitRoomNotFoundError 仍要求 status === 404 && code === 'not_found' 同时成立,未注明依据的是 livekit-server-sdk 哪个版本的错误形态;本次已补充单测(tests/session-stop.test.mjs),但若后续 SDK 只设置其中一个字段仍会静默退回“删除失败”分支。建议补充来源注释。

本轮改进(已确认解决)

  • 此前指出的“diff 混入约 4000+ 行无关 shadcn UI 组件/依赖”范围蔓延问题已解决,本次 diff 仅涉及与浏览器媒体会话相关的 11 个文件。
  • app/api/connection-details/route.ts 移除 RoomConfiguration 相关逻辑,避免与旧版 LiveKit server 的不兼容问题,改动合理且有对应测试更新。
  • lib/transcription-history.ts 新增跨 stream 用最终文本替换未完成片段的逻辑,并补充了对应单测,逻辑正确。

Fix this →

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 1m 13s —— View job


Review 结论

不通过(连续第 4 轮 review,此前已指出的 P0 问题本次 push 仍未修复)

问题及建议

  • [P0] hooks/useBrowserSourceClient.ts:165logBrowserAudioCaptureDiagnostics(captureTrack)(内部调用 assertBrowserEchoCancellationActive,见 lib/browser-audio-capture.ts:58-66)仍在 try 块(第 168 行开始)之前执行。当 getSettings().echoCancellation !== true(部分移动端/Safari 常见)时会直接 throw,此时第 160 行 createLocalAudioTrack 产生的 audioTrack 未进入 try 块,第 246-251 行的 stop()/mute() 清理逻辑不会执行,导致麦克风硬件持续占用(热麦泄漏),ensureAudioPublished 直接失败、Start Call 中断。该问题已连续三轮 review 提出,本次 push 依旧未变动。建议:将该诊断/断言调用移入 try 块内,或让 assertBrowserEchoCancellationActive 仅记录日志不 throw。
  • [P1] components/livekit/filtered-audio-renderer.tsx:407-410:新增播放诊断日志使用裸 console.info/console.warn 无条件输出(每次远端音轨播放时触发),而同文件其余诊断日志均通过 debugAudioLog(debugAudio, ...)(见 filtered-audio-renderer.tsx:24)按调试开关控制。hooks/useBrowserSourceClient.ts:719logBrowserAudioCaptureDiagnostics 内的 console.info)同样存在此问题,此前两轮已指出,仍未修复,会在生产环境产生控制台噪音。建议统一走 debug 开关控制。
  • [P2] tests/browser-audio-capture.test.mjs 仍只覆盖 lib/browser-audio-capture.ts 纯函数,未针对 hooks/useBrowserSourceClient.tsensureAudioPublished 的实际调用路径补充回归测试,即上述 P0 场景(assert 抛出时 track 是否被正确清理)依旧没有测试保护。
  • [P2] lib/session-stop.ts:27-33isLiveKitRoomNotFoundError 仍要求 status === 404 && code === 'not_found' 同时成立,本轮已补充单测(tests/session-stop.test.mjs:48-59),但未注明依据的是 livekit-server-sdk 哪个版本的错误形态;若后续 SDK 只设置其中一个字段仍会静默退回"删除失败"分支,属于残余风险,可后续补充来源注释。

本轮改进(已确认)

  • app/api/connection-details/route.ts 移除 RoomConfiguration 相关逻辑,改动合理,避免与旧版 LiveKit server 不兼容。
  • lib/transcription-history.ts 用最终文本替换未完成片段的合并逻辑正确,已补充单测。
  • lib/session-stop.tsisLiveKitRoomNotFoundError 补充了单测。

Fix this →

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 1m 39s —— View job


Review 结论

不通过(连续第 4 轮 review,此前指出的 P0 热麦克风泄漏问题本次 push 仍未修复)

问题及建议

  • [P0] hooks/useBrowserSourceClient.ts:165ensureAudioPublishedlogBrowserAudioCaptureDiagnostics(captureTrack)(内部调用 assertBrowserEchoCancellationActive,见 lib/browser-audio-capture.ts:58-66echoCancellation !== true 时 throw)仍在 try 块(第 168 行开始)之前执行。此时第 160 行 createLocalAudioTrack 已创建的 audioTrack 未进入 try,第 246-251 行的 stop()/mute() 清理逻辑不会执行,导致麦克风硬件持续占用(热麦泄漏),ensureAudioPublished 直接失败、Start Call 中断。该问题已连续四轮 review 提出,代码未变动。建议:把该诊断/断言调用移入 try 块内,或让 assertBrowserEchoCancellationActive 仅记录日志不 throw。Fix this →
  • [P1] components/livekit/filtered-audio-renderer.tsx:409hooks/useBrowserSourceClient.ts:720:诊断日志仍使用裸 console.info/console.warn 无条件输出(前者每次远端音轨播放时触发),而同文件其余日志均通过 debugAudioLog(debugAudio, ...) 按调试开关控制,风格不一致,会在生产环境产生控制台噪音。已连续三轮指出,未修复。建议统一走 debug 开关。
  • [P2] tests/browser-audio-capture.test.mjstests/browser-room-session.test.mjs 仍只覆盖纯函数或字符串匹配 hook 源码,未对 ensureAudioPublished 中 assert 抛出时 audioTrack 是否被清理的实际路径(即上述 P0 场景)做集成测试保护。
  • [P2] lib/session-stop.ts:27-33isLiveKitRoomNotFoundError 仍要求 status === 404 && code === 'not_found' 同时成立,未注明依据的是 livekit-server-sdk 哪个版本的错误形态;若后续 SDK 只设置其中一个字段会静默退回"删除失败"分支。已补充单测(tests/session-stop.test.mjs),残余风险较低,可后续补来源注释。

本轮新增的 hooks/useRoom.ts:266await browserSourceClient.stop(),在 waitForRoomDisconnected 前清理陈旧的 browser 媒体 runtime)逻辑合理:stop()hooks/useBrowserSourceClient.ts:411-415)在无 runtime 时直接返回,是幂等操作,且有对应测试更新,未发现问题。

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 1m 4s —— View job


Todo

  • 获取 PR 上下文
  • 拉取 diff 并分析改动(git diff origin/lex-main...HEAD,18 个文件,+462/-70)
  • 重点检查此前多轮已指出的 P0 热麦克风泄漏问题是否修复
  • 检查 React hook 清理 / Next.js 边界 / 环境变量泄露
  • 输出 review 结论

Review 结论

不通过(连续第 5 轮 review,此前四轮已指出的 P0 热麦克风泄漏问题本次 push 仍未修复)

问题及建议

  • [P0] hooks/useBrowserSourceClient.ts:167-171captureTrack = audioTrack.mediaStreamTrack 后紧接调用 logBrowserAudioCaptureDiagnostics(captureTrack)(内部调用 assertBrowserEchoCancellationActive,见 lib/browser-audio-capture.ts:58-66echoCancellation !== true 时 throw),该调用仍位于 try 块(第 171 行开始)之前。第 163 行 createLocalAudioTrack 已创建的 audioTrack 一旦触发此 throw,第 249-253 行的 stop()/mute() 清理逻辑不会执行,导致麦克风硬件持续占用(热麦泄漏),ensureAudioPublished 直接失败、Start Call 中断。此问题已连续四轮 review 提出,代码逻辑(含行号偏移)未发生任何变化。建议:把该诊断/断言调用移入 try 块内,或让 assertBrowserEchoCancellationActive 仅记录日志不 throw。Fix this →
  • [P1] components/livekit/filtered-audio-renderer.tsx:405-410hooks/useBrowserSourceClient.ts:734-739:诊断日志仍使用裸 console.info/console.warn 无条件输出(前者每次远端音轨播放时触发),而同文件其余日志均通过 debugAudioLog(debugAudio, ...) 按调试开关控制,风格不一致,会在生产环境产生控制台噪音。已连续四轮提出,仍未修复。建议统一走 debug 开关。
  • [P2] tests/browser-audio-capture.test.mjstests/browser-room-session.test.mjs 仍只覆盖纯函数或字符串匹配 hook 源码,未对 ensureAudioPublished 中 assert 抛出时 audioTrack 是否被清理的实际路径(即上述 P0 场景)做集成测试保护。
  • [P2] lib/session-stop.ts:27-33isLiveKitRoomNotFoundError 仍要求 status === 404 && code === 'not_found' 同时成立,未注明依据的是 livekit-server-sdk 哪个版本的错误形态;已补充单测(tests/session-stop.test.mjs),残余风险较低,可后续补来源注释。

本轮改进(已确认)

  • lib/browser-media-capture-timeout.ts(新增)为摄像头采集/发布/媒体网关状态发布增加超时与资源释放(hooks/useBrowserSourceClient.ts 摄像头路径、lib/livekit-media-gate.ts:234-244),逻辑合理且有对应单测。
  • hooks/useRoom.ts:262-266waitForRoomDisconnected 前调用 browserSourceClient.stop() 清理陈旧的 browser 媒体 runtime,stop() 幂等,改动合理,有测试覆盖。
  • app/api/session/stop/route.ts + lib/session-stop.ts 对 LiveKit room 已不存在的情况做幂等处理,改动合理。

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Claude finished @lanyue-llk's task in 53s —— View job


Review 结论

不通过(连续第 6 轮 review,此前多轮已指出的 P0 热麦克风泄漏问题本次 push 仍未修复)

问题及建议

  • [P0] hooks/useBrowserSourceClient.ts:168logBrowserAudioCaptureDiagnostics(captureTrack)(内部调用 assertBrowserEchoCancellationActive,见 lib/browser-audio-capture.ts:58-66echoCancellation !== true 时 throw)仍在 try 块(第 171 行开始)之前执行。第 163 行 createLocalAudioTrack 已创建的 audioTrack 一旦触发此 throw,第 249-253 行的 stop()/mute() 清理逻辑不会执行,导致麦克风硬件持续占用(热麦泄漏),ensureAudioPublished 直接失败、Start Call 中断。此问题已连续五轮 review 提出,代码逻辑未变化。建议:把该诊断/断言调用移入 try 块内,或让 assertBrowserEchoCancellationActive 仅记录日志不 throw。Fix this →
  • [P1] components/livekit/filtered-audio-renderer.tsx:405-410hooks/useBrowserSourceClient.ts:734-739:诊断日志仍使用裸 console.info/console.warn 无条件输出(前者每次远端音轨播放时触发),而同文件其余日志均通过 debugAudioLog(debugAudio, ...)(见 filtered-audio-renderer.tsx:24)按调试开关控制,风格不一致,会在生产环境产生控制台噪音。已连续五轮提出,仍未修复。建议统一走 debug 开关。
  • [P2] tests/browser-audio-capture.test.mjstests/browser-room-session.test.mjs 仍只覆盖纯函数或字符串匹配 hook 源码,未对 ensureAudioPublished 中 assert 抛出时 audioTrack 是否被清理的实际路径(即上述 P0 场景)做集成测试保护。
  • [P2] lib/session-stop.ts:27-33isLiveKitRoomNotFoundError 仍要求 status === 404 && code === 'not_found' 同时成立,未注明依据的是 livekit-server-sdk 哪个版本的错误形态;已补充单测(tests/session-stop.test.mjs),残余风险较低,可后续补来源注释。

本轮改进(已确认)

  • hooks/useRoom.ts:262-266waitForRoomDisconnected 前调用 browserSourceClient.stop() 清理陈旧的 browser 媒体 runtime,避免 Room 意外 disconnect 后新会话拿不到麦克风,stop() 幂等,改动合理。
  • lib/browser-media-capture-timeout.ts(新增)为摄像头采集/发布/媒体网关状态发布增加超时与资源释放(hooks/useBrowserSourceClient.ts 摄像头路径、lib/livekit-media-gate.ts:234-247),逻辑合理且有对应单测。

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant