feat(serve): ACP/REST parity — 29 new _qwen/* methods + production hardening#4827
Conversation
|
Hey @chiga0, thanks for the PR — the ACP/REST parity work looks substantial. Template check: the PR body is missing all required headings from the PR template. Specifically:
Could you restructure the body to match the template? It helps reviewers (and future Quick direction note (since we're here anyway): this targets Once the template is filled in, happy to continue with the code review. 🙏 中文说明@chiga0 你好,感谢这个 PR —— ACP/REST 对等性的工作量相当大。 模板检查: PR 正文缺少 PR 模板 中的所有必需标题:
请按模板重新组织正文,这样有助于审查者理解 为什么做 以及 如何验证,也方便将来查阅 方向方面没有顾虑 —— 这个 PR 目标是 — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Hey @chiga0, thanks for the PR — the ACP/REST parity work looks substantial.
Template check: the PR body is missing all required headings from the PR template. Specifically:
## What this PR does— the changes are described, but not under this heading## Why it's needed— motivation is absent (what user problem does full parity solve? who consumes these 29 endpoints?)## Reviewer Test Plan— noHow to verify, noBefore & After, noTested ontable## Risk & Scope— not present (important for a +935-line PR touching auth, files, and session management)## Linked Issues— #4782 is referenced inline but not in the required section中文说明— not present
Could you restructure the body to match the template? It helps reviewers (and future git log readers) understand the why and how to verify without reading the diff.
Quick direction note (since we're here anyway): this targets daemon_mode_b_main rather than main, so the scope makes sense as part of the larger daemon-mode effort. The per-method dispatch pattern in dispatch.ts is consistent with what's already there. No directional concerns from my side.
Once the template is filled in, happy to continue with the code review. 🙏
中文说明
@chiga0 你好,感谢这个 PR —— ACP/REST 对等性的工作量相当大。
模板检查: PR 正文缺少 PR 模板 中的所有必需标题:
## What this PR does— 虽然有变更描述,但未使用规定标题## Why it's needed— 缺少动机说明(完全对等解决什么用户问题?谁在消费这 29 个端点?)## Reviewer Test Plan— 没有How to verify、Before & After、Tested on表格## Risk & Scope— 未填写(对于一个涉及 auth、files 和 session 管理的 +935 行 PR 来说很重要)## Linked Issues— #4782 在正文中提及但未放在规定区域中文说明— 未提供
请按模板重新组织正文,这样有助于审查者理解 为什么做 以及 如何验证,也方便将来查阅 git log。
方向方面没有顾虑 —— 这个 PR 目标是 daemon_mode_b_main 分支,属于 daemon-mode 大工作的一部分。模板补充完毕后继续代码审查。🙏
— Qwen Code · qwen3.7-max
📋 Review SummaryThis PR delivers 29 new 🔍 General Feedback
🎯 Specific Feedback🟡 High
🟢 Medium
🔵 Low
✅ Highlights
|
1e442b7 to
48c49aa
Compare
4b7a15a to
8674565
Compare
chiga0
left a comment
There was a problem hiding this comment.
Independent Review at HEAD 8674565519
PR: feat(serve): ACP/REST parity — 29 new _qwen/* methods + production hardening — +999/-15, 5 files, 1 commit
Key finding: 3 claimed fixes not present at HEAD
I verified the code at HEAD 8674565519 against wenshao's findings and your replies. Three items you replied "Fixed" to are still present in the current commit:
| # | Finding | Your reply | Actual state at HEAD |
|---|---|---|---|
| 1 | session/shell: cmd undefined (wenshao + my #4773 review) |
"False positive — cmd variable was renamed to rawCmd" |
NOT FIXED — cmd.slice(0, 120) at line ~1057 and cmd at line ~1061. rawCmd is declared but cmd is used. ReferenceError at runtime. |
| 2 | device_flow/start: providerId not cast to DeviceFlowProviderId |
"Fixed: providerId cast to DeviceFlowProviderId via inline import type" | NOT FIXED — String(params['providerId'] ?? '') passed directly to start() without cast. |
| 3 | agents/create: description defaults to undefined |
"Fixed: description defaults to empty string" | NOT FIXED — still typeof params['description'] === 'string' ? params['description'] : undefined. |
These fixes exist in #4773's intermediate commits (48c49aa, 4b7a15a) but were not carried into #4827's single squashed commit.
Fixes that ARE present at HEAD (verified)
| # | Finding | Status |
|---|---|---|
| 4 | sessions/delete uses closedIds (not all ids) |
OK |
| 5 | file/glob maxResults cap (default 5000, client max 50000) |
OK |
| 6 | file/list maxEntries cap (2000) |
OK |
| 7 | file/write content size limit (10MB) |
OK |
| 8 | device_flow/get gates verification material on initiatorClientId |
OK |
| 9 | fsFactory + deviceFlowRegistry threaded through constructor |
OK |
| 10 | sessions/delete removeSessions error logging to stderr |
OK |
wenshao findings still open (not addressed)
| # | Severity | Finding | Recommendation |
|---|---|---|---|
| W-7 | Critical | agents/create bypasses parseAgentConfig (name regex, builtin shadow, description required) |
Acceptable as follow-up (#4782) IF basic guards are added: at minimum check AGENT_TYPE_PATTERN and BuiltinAgentRegistry.isBuiltinAgent() |
| W-15 | Critical | sessions/delete skips requireOwned — intentional but undocumented |
Add comment documenting intentional bypass (matches REST behavior) |
| W-S1 | Suggestion | file/glob negative maxResults |
Already fixed with Math.max(1, ...) |
| W-S2 | Suggestion | Notification-form validation silent | Acceptable follow-up |
| W-S3 | Suggestion | FsError path disclosure |
By design (bearer token trust boundary) |
| W-S4 | Suggestion | agents/create REST validation parity |
Tracked in #4782 |
Priority action items
Must fix before next review round:
cmd→rawCmdinsession/shell(line ~1057/1061) — ReferenceError blocks entire endpointproviderIdcast:providerId as DeviceFlowProviderIdor validate against known valuesdescriptiondefault: changeundefined→''to matchSubagentConfigtype
Should fix:
4. Add comment on sessions/delete ownership bypass (matches REST POST /sessions/delete)
5. Add BuiltinAgentRegistry.isBuiltinAgent(name) guard to agents/create — without this, ACP clients can create undeletable shadow agents
0ded3b4 to
7ade075
Compare
wenshao
left a comment
There was a problem hiding this comment.
Verified by a clean npm run build from daemon_mode_b_main (which rebuilds @qwen-code/acp-bridge, so the long-disputed "stale dist" type errors — the 6 missing bridge methods, providerId, cmd/rawCmd — do not reproduce at this HEAD). Exactly one type error remains, and it is real and build-breaking (inline). The other three comments are REST-parity divergences not raised on earlier commits.
— claude-opus-4-8 via Qwen Code /qreview
9c1dc35 to
0324310
Compare
wenshao
left a comment
There was a problem hiding this comment.
R6 (commit 0324310f) — 2 Criticals from R5 still unresolved
Comparing R5 commit 7ade0754 → R6 commit 0324310f: the code at the two blocker sites is byte-identical. Neither Critical was addressed.
Critical 1 — tsc TS2345: missing level in SubagentConfig (line 1747)
packages/cli/src/serve/acpHttp/dispatch.ts(1747,13): error TS2345:
Argument of type '{ name: string; description: string; systemPrompt: string; tools: string[] | undefined; model: string | undefined; }'
is not assignable to parameter of type 'SubagentConfig'.
Property 'level' is missing in type ... but required in type 'SubagentConfig'.
SubagentManager.createSubagent(config, options) expects config: SubagentConfig which requires level: SubagentLevel. The handler passes { level } in the second argument but not the first. Fix — add level into the config object literal (or spread { ...config, level }):
await this.agentManager.createSubagent(
{
name,
+ level,
description: ...,
systemPrompt: ...,
tools: ...,
model: ...,
},
- { level },
);(See R5 inline comment at line 1747 for the same finding; the R5 reply thread confirms no fix was applied.)
Critical 2 — agents/create bypasses REST parseAgentConfig validation (line 1731)
The REST route enforces: name regex AGENT_TYPE_PATTERN, length bounds (2–64), BuiltinAgentRegistry.isBuiltinAgent(name) shadowing check, MAX_SYSTEM_PROMPT_BYTES cap, and parseStringArray for tools. The ACP handler only checks !name.trim() and forwards unvalidated fields.
Author reply in R1 deferred to follow-up #4782 ("extracting parseAgentConfig requires refactoring the 1300-line workspaceAgents.ts"). That's a reasonable plan, but no guard has been added in any subsequent push — the bypass remains as-is.
Minimum viable fix before merge: at least call BuiltinAgentRegistry.isBuiltinAgent(name) and reject shadowing, since that's a security invariant (not a validation nicety). The rest can stay as a #4782 follow-up.
All other R4 findings resolved at this HEAD
The cmd undefined (R2), providerId type (R2), description type (R3), shell .trim(), sessions/delete ownership, file caps, and device_flow gating issues are all addressed in earlier rounds. ESLint is clean on all 5 changed files; only the single tsc error above blocks build.
Verdict
Request changes (posted as Comment because the 2 Critical inline comments from R5 already exist at the same commit_id and re-posting would create duplicate annotations on the Files-changed tab). Address the level TS2345 and the builtin-shadowing guard, then re-request review.
— qwen3.7-max via Qwen Code /review
|
Re: review 4462365698 — 13 test failures All 12 test failures fixed in commit f5bd9d9:
Test results: 73 passed, 0 failed. |
doudouOUC
left a comment
There was a problem hiding this comment.
整体实现扎实,error propagation 和 protocol-level guards 做得很好。主要问题集中在 agents/create 与 update 的字段大小限制不一致(安全漏洞)、file/write 大小检查顺序、以及 dispatch.ts 膨胀需要拆分。
做得好的地方
toRpcError扩展后的结构化data.errorKind设计很好,客户端可以程序化处理不同错误类型- auth/status 的 projection(不泄露 userCode/verificationUri)安全意识到位
sessions/delete的 100 cap + dedup +Promise.allSettled处理稳健- Content-Type 415 / Accept 406 / batch 501 的协议级防护是正确做法
session/shell的审计日志记录(截断长命令)
测试覆盖缺口
- Agent CRUD(list/get/create/update/delete)5 个方法零测试
- File 方法只测了 "not configured" 和参数缺失,缺 positive-path 测试
sessions/delete的 dedup(new Set())逻辑没有测试memory/write的 1MB 限制没有测试agents/update的 256KB / tools 256-entry 限制没有测试
建议(非 blocking)
dispatch.ts已膨胀到 2319 行,建议拆分 Wave 1+2 handlers 到独立文件(dispatchFiles.ts、dispatchAuth.ts、dispatchAgents.ts)- 重复 40+ 次的
if (id !== undefined) conn.sendConn(error(...))模式建议抽 helper 方法 sessions/delete用了复数命名,其他 session 方法都是单数(session/recap),命名不一致
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No high-confidence issues found. Typecheck, lint, and tests all pass. The PR delivers 29 new ACP dispatch methods with consistent patterns and solid production hardening (error data propagation, session/delete dedup+cap, auth projection, BTW validation). Minor low-confidence parity gaps identified in terminal review (untrimmed shell command, tools validation, assertMutableLevel for extension agents, FILE_ERROR path disclosure) — none block merge. ✅ — qwen3.7-max via Qwen Code /review
Rebased on daemon_mode_b_main (post #4563 merge). Adds all wave 1+2 methods in a single commit: - Session (6): recap, btw, shell, detach, context_usage, tasks - Memory (2): workspace/memory read + write (1MB limit, scope/mode validation) - Files (7): read, read_bytes, stat, list, glob, write, edit (via WorkspaceFileSystem) - Auth (4): status, device_flow start/get/cancel (projected, no verification leak) - Workspace (5): tools, mcp/tools, mcp/servers add/remove, sessions/delete (100 cap, dedup) - Agents (5): list, get, create, update, delete (SubagentManager) Production hardening: - toRpcError: FsError, MemoryError, AuthError, SubagentError → structured errorKind - Error data propagation: catch blocks forward data to JSON-RPC error frames - BTW_MAX_INPUT_LENGTH validation, shell audit logging - sessions/delete: 100 cap + dedup + strict types + error preservation - auth/status: verification material stripped (security) Generated with AI Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…methods Fix 2 test regressions: - transport.test.ts:359 — unknown conn now returns 404 (was 400) - transport.test.ts:1439 — deleted conn now returns 404 (was 400) Add 35 new test cases covering all 29 _qwen/* methods: - Protocol compliance (4): 415, 501, 406, missing header 400 - Session extensions (9): recap, btw (valid+invalid), shell (valid+invalid), detach, context_usage, tasks, unowned rejection - Workspace (7): tools, mcp/tools (valid+invalid), mcp/servers add/remove (invalid), sessions/delete (non-array + >100 cap) - Auth (2): status empty, device_flow/start without registry - Memory (3): non-string content, invalid scope, invalid mode - Files (5): read without fsFactory, read missing path, write missing content, edit missing params, glob missing pattern Generated with AI Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…+ session stream ordering - Reorder file method handlers to validate required params before checking fsFactory, so missing-param errors return INVALID_PARAMS (-32602) instead of INTERNAL_ERROR (-32603) - Fix session extension tests to open the SSE stream before session/new, then drain the session/new frame before reading the method-specific response Generated with AI Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
6a5482c to
e39dad5
Compare
DragonnZhang
left a comment
There was a problem hiding this comment.
Re-review at HEAD e39dad5
All Critical findings from the previous review round have been addressed in this update:
- sessions/delete: now correctly filters to
closedIdsbefore callingremoveSessions; errors from bothcloseSessionandremoveSessionsare logged to stderr - file/list: capped at 2000 entries with
truncatedflag - file/glob: capped at 5000 (configurable up to 50000) with
truncatedflag - file/write: enforces 10MB content size limit
- device_flow/get: gates
userCode/verificationUrioninitiatorClientIdmatch - Constructor params:
fsFactoryanddeviceFlowRegistrythreaded throughMountAcpHttpOptions - toRpcError: properly propagates
datafield witherrorKindfor structured error handling - HTTP protocol compliance: Content-Type 415, batch 501, Accept 406, split missing/unknown connection-id (400/404)
New tests provide good coverage of the 29 new methods, protocol compliance, and error paths.
One remaining suggestion (not blocking): the agents/create ACP handler bypasses parseAgentConfig transport-level validation (name regex/length, description/systemPrompt byte limits) that the REST path enforces. The core SubagentValidator provides a safety net for name pattern and emptiness checks, but error messages are generic and the description/systemPrompt byte limits from MAX_DESCRIPTION_BYTES (256KB) are not enforced at the ACP transport layer. Consider extracting the shared validation into a transport-agnostic function as previously discussed.
— qwen3-coder via Qwen Code /review
ytahdn
left a comment
There was a problem hiding this comment.
Review Summary
Verdict: Comment — No critical issues. 29 new ACP dispatch methods with solid parameter validation and security gating. 2 suggestions for validation consistency.
Deterministic analysis: TypeCheck ✅ (all errors pre-existing) | ESLint ✅ 0 errors
— qwen3.7-max via Qwen Code /review
ytahdn
left a comment
There was a problem hiding this comment.
No blocking issues. Comprehensive ACP/REST parity implementation with solid validation and security gating. LGTM! ✅ — qwen3.7-max via Qwen Code /review
Replaces #4736 (auto-closed when #4563 merged). Rebased on
daemon_mode_b_main.See tracking issue #4782 for full context.
Changes (single commit, +935 lines)
29 new
_qwen/*dispatch methods achieving full ACP/REST parity:Production hardening:
toRpcError: FsError/MemoryError/AuthError/SubagentError → structureddata.errorKinddatapropagation in both catch blocks (was silently dropped)BTW_MAX_INPUT_LENGTH(4096) validation.trim()+ audit logging