Skip to content

feat(serve): add hooks diagnostic HTTP/ACP surface (issue #4514 T3.9)#4822

Merged
doudouOUC merged 7 commits into
daemon_mode_b_mainfrom
feat/serve-hooks-status
Jun 7, 2026
Merged

feat(serve): add hooks diagnostic HTTP/ACP surface (issue #4514 T3.9)#4822
doudouOUC merged 7 commits into
daemon_mode_b_mainfrom
feat/serve-hooks-status

Conversation

@doudouOUC

@doudouOUC doudouOUC commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Add two read-only HTTP/ACP endpoints for hook configuration status, enabling remote clients (web-shell, SDK consumers) to query workspace and session hooks via the daemon HTTP surface.

  • GET /workspace/hooks — returns config-sourced hooks (user, project, extension sources) with global disabled state and per-event metadata (descriptions, matcher kinds)
  • GET /session/:id/hooks — returns runtime session hooks registered by skills for a specific session
  • /hooks slash command enabled for ACP mode (text output via listCommand)

Why it's needed

Issue #4514 T3.9 tracks the /extensions, /settings, /hooks HTTP surface. Remote clients (web-shell, SDK consumers) currently cannot query hook configuration — the /hooks command is interactive-only. This PR addresses the hooks portion, the simplest of the three (read-only, with an existing non-interactive text output path).

Reviewer Test Plan

How to verify

  1. Start daemon: qwen serve --port 7779
  2. Create a session: curl -X POST localhost:7779/session -H 'Content-Type: application/json' -d '{"cwd":"'$(pwd)'"}' — note the sessionId
  3. Query workspace hooks: curl localhost:7779/workspace/hooks | jq .
    • Expect 200 with initialized: true, disabled: false, hooks: [...] (populated if you have hooks configured), events: {…} (18 entries with descriptions and matcherKind)
  4. Query session hooks: curl localhost:7779/session/<sessionId>/hooks | jq .
    • Expect 200 with hooks: [] (no session hooks registered yet)
  5. Query nonexistent session: curl localhost:7779/session/nonexistent/hooks
    • Expect 404
  6. Check capabilities: curl localhost:7779/capabilities | jq .features — should include workspace_hooks and session_hooks
  7. ACP mode: send /hooks via prompt endpoint — should return text-formatted hook list

Evidence (Before & After)

N/A — new endpoints, no UI changes.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Risk & Scope

  • Read-only: no mutation, no state changes — safe to land without risk to existing functionality
  • 12 files across 4 packages: acp-bridge (types + bridge), cli (acpAgent + server + workspace-service + capabilities + hooksCommand), sdk-typescript (types + client + exports)
  • Compile-time safety: HOOK_EVENT_DESCRIPTIONS and IDLE_HOOK_EVENTS use Record<HookEventName, ...> — new enum members trigger compile errors
  • Forward-compat: ServeUnknownHookConfig without index signature preserves discriminated union narrowing; serializeHookConfig default branch handles future hook types

Linked Issues

Closes partial: #4514 (T3.9 hooks portion)

🤖 Generated with Qwen Code

Copilot AI review requested due to automatic review settings June 6, 2026 09:57
@qwen-code-ci-bot

qwen-code-ci-bot commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR @doudouOUC!

Template: ✓ — all required headings present (What this PR does, Why it's needed, Reviewer Test Plan with How to verify / Evidence / Tested on, Risk & Scope, Linked Issues). Minor: <details> Chinese translation block still missing from the PR body — nice to have but not a blocker.

On direction: this addresses T3.9 from the tracking issue (#4514), adding read-only HTTP/ACP endpoints for hook configuration status. Remote clients (web-shell, SDK consumers) currently can't query hooks — this closes that gap cleanly. The scope (hooks portion only, leaving /extensions and /settings for later) is well-scoped and low-risk. No product-direction concerns.

On approach: +600 lines across 12 files is proportional — types flow through three packages (acp-bridge, cli, sdk-typescript) following the established pattern for status endpoints. The workspace/session endpoint split mirrors MCP, skills, and providers. The idle factory for event metadata and the serializeHookConfig switch are straightforward. One thought: HOOK_EVENT_DESCRIPTIONS (acpAgent.ts) and IDLE_HOOK_EVENTS (status.ts) carry the same event descriptions in two places — understandable given the package boundary (acp-bridge can't import from cli), but worth keeping in sync.

Moving on to code review. 🔍

中文说明

感谢贡献 @doudouOUC

模板:✓ — 所有必要标题已包含(What this PR does、Why it's needed、Reviewer Test Plan 含验证步骤/对比/测试平台、Risk & Scope、Linked Issues)。小问题:PR 正文仍缺少 <details> 中文翻译区块——有则更好,不阻塞。

方向:本 PR 对应 tracking issue #4514 的 T3.9,为远程客户端(web-shell、SDK 消费者)新增只读 hooks 诊断端点。范围合理(仅 hooks,/extensions/settings 后续再做),低风险。产品方向无顾虑。

方案:12 文件 +600 行与范围成正比——类型在三个包中流转,遵循已有 status endpoint 模式。workspace/session 端点拆分与 MCP、skills、providers 一致。HOOK_EVENT_DESCRIPTIONS(acpAgent.ts)与 IDLE_HOOK_EVENTS(status.ts)包含相同的事件描述——因包边界可理解,但需注意同步。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot 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.

PR body does not follow the required template. Please update to use the headings from .github/pull_request_template.md: What this PR does, Why it's needed, Reviewer Test Plan (with Evidence Before/After + Tested on), Risk & Scope, and Linked Issues. See my comment above for details.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Thanks — updated the PR body to follow the required template headings (What this PR does, Why it's needed, Reviewer Test Plan with Evidence/Tested on, Risk & Scope, Linked Issues).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a read-only diagnostic surface for hook configuration/state in qwen serve, exposing both workspace-configured hooks and session-registered (skill) hooks via HTTP/ACP, and wires corresponding SDK client helpers + capability tags.

Changes:

  • Introduce GET /workspace/hooks and GET /session/:id/hooks status endpoints (and corresponding ACP ext-methods) to inspect hook state.
  • Add hook-status schema/types across acp-bridge, cli, and sdk-typescript, including “idle” placeholders and forward-compatible unions.
  • Enable /hooks slash command in ACP and non-interactive modes (text listing via list action).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/acp-bridge/src/status.ts Adds hook status types, ext-method IDs, and an idle workspace hooks factory with event metadata.
packages/acp-bridge/src/bridgeTypes.ts Extends AcpSessionBridge with workspace/session hook status read methods.
packages/acp-bridge/src/bridge.ts Implements bridge methods to request hook status via ACP ext-methods.
packages/cli/src/acp-integration/acpAgent.ts Implements hook status builders + serialization and dispatches new ext-methods.
packages/cli/src/serve/workspace-service/types.ts Extends DaemonWorkspaceService interface with getWorkspaceHooksStatus.
packages/cli/src/serve/workspace-service/index.ts Implements workspace hooks status via queryWorkspaceStatus + idle fallback.
packages/cli/src/serve/server.ts Registers new REST routes for workspace and session hooks status.
packages/cli/src/serve/capabilities.ts Advertises workspace_hooks and session_hooks capabilities.
packages/cli/src/ui/commands/hooksCommand.ts Enables /hooks (and /hooks list) in non-interactive and ACP modes.
packages/sdk-typescript/src/daemon/types.ts Adds SDK mirror types for hook configs/entries and workspace/session hook status snapshots.
packages/sdk-typescript/src/daemon/DaemonClient.ts Adds workspaceHooks() and sessionHooks(sessionId) client methods.
packages/sdk-typescript/src/daemon/index.ts Re-exports newly added daemon hook status/config types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/cli/src/serve/server.ts
Comment thread packages/sdk-typescript/src/daemon/types.ts
Comment thread packages/cli/src/serve/server.ts
Comment thread packages/cli/src/acp-integration/acpAgent.ts
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

📋 Review Summary

This PR adds read-only HTTP/ACP endpoints for hook configuration status (/workspace/hooks and /session/:id/hooks), addressing issue #4514 T3.9. The implementation is well-structured, follows existing patterns, and provides comprehensive type safety. The changes span 12 files with ~600 additions, introducing diagnostic surfaces for both workspace-level config-sourced hooks and runtime session hooks registered by skills.

🔍 General Feedback

  • Strong architectural consistency: The implementation follows the established workspace/* vs session/:id/* split used by other status endpoints (MCP, skills, providers, etc.)
  • Excellent type safety: Uses Record<HookEventName, string> for event descriptions and Record<HookEventName, ServeHookEventMeta> for idle factory—new enum members will trigger compile errors as intended
  • Security-conscious design: Function hook callbacks (callback/onHookSuccess) are correctly stripped from serialization, exposing only metadata
  • Forward-compatibility: ServeUnknownHookConfig without index signature preserves discriminated union narrowing; default branch in serializeHookConfig handles future hook types gracefully
  • Clean separation: Read-only status endpoints are cleanly separated from mutation control-plane methods via SERVE_STATUS_EXT_METHODS vs SERVE_CONTROL_EXT_METHODS

🎯 Specific Feedback

🟡 High

  • packages/acp-bridge/src/status.ts:719ServeUnknownHookConfig includes name, description, timeout, and statusMessage as optional fields, but unknown hook types may not have these fields. Consider making all fields except type truly optional or document that unknown types should only provide type. The current design assumes all future hook types will share these common fields, which may not hold.
    • Suggestion: Either document this assumption or make the interface more flexible: export interface ServeUnknownHookConfig { type: string; [key: string]: unknown; } with a comment about metadata-only serialization.

🟢 Medium

  • packages/cli/src/acp-integration/acpAgent.ts:2158-2162 — The buildWorkspaceHooksStatus error handler catches all errors but only attempts to recover disabled state. If config.getDisableAllHooks() also fails (comment acknowledges "config may be in a broken state"), it silently falls back to false. This could mask configuration issues.

    • Suggestion: Consider adding the error to the errors array even when recovery fails, so operators can see that the hook system is in an inconsistent state.
  • packages/cli/src/acp-integration/acpAgent.ts:2096-2107 — The serializeHookConfig function for 'function' type hooks explicitly excludes the callback field (good for security), but this is implicit rather than explicit. A future contributor might accidentally add it.

    • Suggestion: Add an inline comment like // NOTE: callback intentionally omitted—metadata only to make the security decision explicit for future maintainers.
  • packages/sdk-typescript/src/daemon/types.ts:1335-1351DaemonHookEventName uses (string & {}) widening for forward-compatibility, which is correct. However, the SDK duplicates all hook config interfaces (DaemonCommandHookConfig, etc.) rather than importing from @qwen-code/qwen-code-core or acp-bridge. This creates potential drift.

    • Suggestion: Consider re-exporting or importing the canonical types from core/acp-bridge if possible, or add a compile-time assertion that the SDK types match the bridge types.

🔵 Low

  • packages/acp-bridge/src/status.ts:672-676ServeHookEventMeta has matcherKind as optional, which is correct for events like PostToolBatch and UserPromptSubmit that don't have matchers. However, consumers might forget to check for undefined.

    • Suggestion: Consider adding a JSDoc comment: /** Absent for events that don't support filtering (e.g., PostToolBatch, UserPromptSubmit) */
  • packages/cli/src/acp-integration/acpAgent.ts:2044-2060HOOK_MATCHER_KINDS mapping is a Partial<Record<...>>, which is correct. The code handles missing entries properly. Consider adding a compile-time check (e.g., a type assertion function) that ensures every HookEventName is either in the mapping or intentionally omitted.

  • packages/cli/src/ui/commands/hooksCommand.ts:49,199 — Changing supportedModes from ['interactive'] to ['interactive', 'non_interactive', 'acp'] is correct and aligns with the PR goals. The PR description mentions this change but doesn't explicitly test the ACP mode output path. The test plan mentions "/hooks slash command enabled for ACP mode (text output via listCommand)"—ensure the non-interactive text output is verified.

  • packages/sdk-typescript/src/daemon/DaemonClient.ts:511-530 — The new workspaceHooks() and sessionHooks() methods follow the existing pattern well. Consider adding a brief JSDoc comment to each method explaining what it returns (e.g., "Returns workspace-level hook configuration from all sources").

✅ Highlights

  • Excellent type design: The ServeHookEntry interface correctly captures all necessary metadata (eventName, config, source, matcher, sequential, enabled, hookId, skillRoot) without over-engineering
  • Smart error handling: The buildWorkspaceHooksStatus method gracefully handles missing hook systems and configuration errors, returning structured error cells
  • Consistent patterns: The idle factory createIdleWorkspaceHooksStatus follows the same pattern as createIdleWorkspaceMcpStatus, createIdleWorkspaceSkillsStatus, etc.
  • Comprehensive event metadata: The IDLE_HOOK_EVENTS map provides a complete catalog of all 18 hook events with descriptions and matcher kinds, enabling clients to build rich UIs without hardcoding
  • Clean API surface: HTTP routes (GET /workspace/hooks, GET /session/:id/hooks) are minimal and delegate properly to workspace service and bridge layers

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review response summary

# File Comment Action
1 acpAgent.ts:2110 Redact env values Won't take — V1 returns as-is (same as CLI), auth-gated
2 acpAgent.ts:2122 Redact headers Won't take — same rationale
3 server.ts:1049 Annotate route for audit Deferring — broader hardening effort
4 types.ts:1440 Use DaemonHookEventName for eventName Fixed in 81a87722a
5 server.ts:1054 Add route-level tests Deferring — follow-up PR
6 acpAgent.ts:2344 Add extMethod tests Deferring — follow-up PR

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review

Reviewed the full diff (824 lines, 12 files). The implementation is clean and follows established patterns precisely.

What works well:

  • HTTP routes in server.ts follow the exact pattern of existing status endpoints (GET /workspace/mcp, GET /session/:id/skills, etc.) — buildWorkspaceCtx / requireSessionId + sendBridgeError error handling
  • createIdleWorkspaceHooksStatus in status.ts follows the same idle factory pattern as createIdleWorkspaceMcpStatus, createIdleWorkspaceSkillsStatus, etc.
  • serializeHookConfig correctly strips callback from function hooks (security-conscious — metadata only)
  • buildWorkspaceHooksStatus gracefully handles missing hook system and config errors, returning structured error cells
  • The Record<HookEventName, string> typing on HOOK_EVENT_DESCRIPTIONS and IDLE_HOOK_EVENTS ensures compile errors when new enum members are added — good forward-compat design
  • SDK types (DaemonHookEventName with (string & {}) widening) handle forward-compat correctly
  • hooksCommand.ts mode expansion from ['interactive'] to ['interactive', 'non_interactive', 'acp'] is minimal and correct

No blockers found. Two observations (not blocking):

  1. env (command hooks) and headers (HTTP hooks) are serialized as-is — they may contain secrets. Author's rationale: auth-gated daemon, same exposure as CLI config list. Reasonable for V1.
  2. The event metadata duplication between acpAgent.ts and status.ts is a consequence of the package boundary — not a bug, just a sync risk.

Testing

This PR adds new daemon HTTP endpoints (no modification of existing behavior). CI passed (all checks successful). Real-scenario testing requires the full daemon infrastructure (qwen serve + ACP child process), which is not available in this CI environment without the daemon_mode_b_main base branch runtime. The PR's test plan provides clear curl-based verification steps that a reviewer with the daemon running can follow.

CI status: All checks successful (1 skipped — Qwen PR Review action).

中文说明

代码审查

审查了完整 diff(824 行,12 个文件)。实现清晰,严格遵循已有模式。

优点:

  • server.ts 中的 HTTP 路由完全遵循已有 status endpoint 模式(buildWorkspaceCtx / requireSessionId + sendBridgeError
  • createIdleWorkspaceHooksStatus 遵循已有 idle factory 模式
  • serializeHookConfig 正确地从 function hooks 中剥离 callback(仅暴露元数据)
  • buildWorkspaceHooksStatus 优雅处理 hook system 缺失和配置错误
  • Record<HookEventName, string> 类型确保新增枚举成员时编译报错——良好的前向兼容设计
  • SDK 类型(DaemonHookEventName(string & {}) 展宽)正确处理前向兼容
  • hooksCommand 模式扩展最小且正确

未发现阻塞项。 两个观察(不阻塞):

  1. envheaders 按原样序列化——可能包含敏感信息。作者理由:daemon 需认证,与 CLI 暴露程度相同。V1 合理。
  2. 事件元数据在 acpAgent.tsstatus.ts 中重复——包边界的后果,非 bug,仅同步风险。

测试

本 PR 新增 daemon HTTP 端点(不修改已有行为)。CI 通过。真实场景测试需要完整 daemon 基础设施,当前 CI 环境不具备。PR 的测试计划提供了清晰的 curl 验证步骤。

CI 状态: 全部通过(1 个跳过)。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Reflection

Stepping back: this PR does exactly what T3.9 asks for — exposes hook configuration as read-only HTTP/ACP endpoints so remote clients can query them. The implementation is mechanical in the best sense: it follows the established pattern for status endpoints step by step (types → bridge → agent → routes → workspace service → capabilities → SDK mirror → command mode expansion). Nothing surprising, nothing over-engineered.

My independent proposal would have been essentially identical — same package split, same type flow, same idle factory approach. The PR matches or exceeds it.

The serializeHookConfig function is verbose (~60 lines of repetitive spreads), but this is actually the right choice here: explicit per-type field mapping is easier to audit for security (making sure callback isn't leaked) than a generic abstraction. A future contributor reading this code will immediately see what gets serialized and what doesn't.

No correctness bugs, no security holes, no regressions. CI passes. The only outstanding CHANGES_REQUESTED was from the bot's initial template review — the author has since updated the template to match. That review is stale.

Verdict: approve. This is ready to merge once a maintainer dismisses the stale changes-requested review.

中文说明

总结

本 PR 准确实现了 T3.9 的需求——将 hooks 配置以只读 HTTP/ACP 端点暴露给远程客户端。实现方式是"机械式"的最佳实践:逐步遵循已有的 status endpoint 模式(类型 → bridge → agent → 路由 → workspace service → capabilities → SDK 镜像 → 命令模式扩展)。无意外,无过度设计。

我的独立方案与 PR 基本一致——相同的包拆分、类型流转、idle factory。PR 符合甚至超出预期。

serializeHookConfig 约 60 行重复的 spread 操作,但这在这里是正确的选择:显式的逐类型字段映射比泛化抽象更易于安全审计(确保 callback 不会泄露)。

无正确性 bug、无安全漏洞、无回归。CI 通过。唯一的 CHANGES_REQUESTED 来自 bot 的初始模板审查——作者已更新模板,该审查已过时。

结论:通过。 维护者解除过时的 changes-requested 后即可合并。

Qwen Code · qwen3.7-max

@doudouOUC doudouOUC requested a review from wenshao June 6, 2026 11:41

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

Additional findings (not on diff lines):

[Critical] [build] SDK browser daemon bundle is 108581 bytes, exceeding the 108544 byte limit by 37 bytes. The new DaemonHookEventName union (18 string literals + (string & {})) and 13 new exported type definitions in packages/sdk-typescript/src/daemon/types.ts push the bundle over the limit. The limit needs to be bumped in packages/sdk-typescript/scripts/build.js, or the new types need to be tree-shaken more aggressively.

[Suggestion] [test] fakeBridge() in server.test.ts implements FakeBridge extends AcpSessionBridge (strict, not Partial) but does not provide getWorkspaceHooksStatus() or getSessionHooksStatus(). While this is masked by tsconfig's exclude array, any test that hits /workspace/hooks or /session/:id/hooks will get a runtime TypeError. Add stub implementations to fakeBridge().

[Suggestion] The serve barrel (packages/cli/src/serve/index.ts) does not re-export the new hook types or createIdleWorkspaceHooksStatus, breaking the public-API consistency established by every other workspace/session status surface.

— qwen3.7-max via Qwen Code /review

Comment thread packages/acp-bridge/src/status.ts Outdated
Comment thread packages/cli/src/serve/capabilities.ts
Comment thread packages/cli/src/acp-integration/acpAgent.ts Outdated
Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/cli/src/acp-integration/acpAgent.ts Outdated
Comment thread packages/cli/src/acp-integration/acpAgent.ts Outdated
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review response: wenshao round 1 — all items fixed in 7d8ed8a83

# File Comment Action
1 status.ts:8 HookEventName import type-only Fixed
2 capabilities.ts:217 Missing test arrays Fixed — added to EXPECTED_STAGE1_FEATURES
3 acpAgent.ts:2195 catch block initialized: true Fixedfalse
4 acpAgent.ts:2204 Session builder no try/catch Fixed — added try/catch with error cells
5 acpAgent.ts:2044 Duplicate HOOK_* maps Fixed — consolidated into IDLE_HOOK_EVENTS from status.ts
6 acpAgent.ts:2220 Matcher unconditional spread Fixed — conditional spread
7 Review body: SDK bundle 37 bytes over Fixed — bumped limit 106KB → 108KB
8 Review body: fakeBridge stubs Fixed — added both stubs
9 Review body: serve barrel missing Fixed — added all hook types + IDLE_HOOK_EVENTS

wenshao
wenshao previously approved these changes Jun 6, 2026
Comment thread packages/cli/src/serve/server.test.ts Outdated
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review round — wenshao suggestion (post-approval)

Thread Verdict Action
server.test.ts:894 — missing supertest assertions for hooks routes Agreed Fixed in 16de39c

Added workspaceHooksImpl / sessionHooksImpl to FakeBridgeOpts with call counters, plus two happy-path it(...) blocks for GET /workspace/hooks (200 + shape + call count) and GET /session/:id/hooks (200 + shape + call count).

@wenshao

wenshao commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Local Verification Report

Branch: feat/serve-hooks-statusdaemon_mode_b_main
Commit: 16de39cb5
Environment: macOS Darwin 25.4.0 / Node.js / Vitest 3.2.4

Unit Tests

Package Test Files Tests Result
packages/acp-bridge (all) 8 passed 376/376 passed
packages/cli (serve/server.test.ts) 1 failed 358 passed, 17 failed ⚠️
Total 8 passed, 1 failed 734 passed, 17 failed ⚠️

Test failure analysis (PR branch 17 failed vs base branch 15 failed):

Failure Category Count Pre-existing? Notes
runQwenServe afterEach hook timeouts 13 ✅ Yes (13 on base) All fail with Hook timed out in 10000ms — local env server cleanup issue
Capability registry assertions 3 ✅ Yes (3 on base) Feature list expectations stale on both branches
returns workspace hooks status from the bridge 1 New See below

New test failurereturns workspace hooks status from the bridge:

  • Test expects only PreToolUse in the events object, but the implementation's HOOK_EVENT_DESCRIPTIONS returns all 18 hook events
  • Test description mismatch: "Before a tool is executed" vs actual "Before tool execution"
  • Root cause: Test assertion is too narrow — only checks one event instead of the full event map. Should either assert the complete map or use toMatchObject for partial matching.

ESLint

All 12 source files pass with --max-warnings 0 ✅

Files linted: bridge.ts, bridgeTypes.ts, status.ts (acp-bridge), acpAgent.ts, capabilities.ts, index.ts, server.ts, workspace-service/index.ts, workspace-service/types.ts, hooksCommand.ts (cli), DaemonClient.ts, types.ts (sdk-typescript)

TypeScript Type Check

Package Errors (before rebuild) Errors (after rebuild) Status
packages/core 0 0 ✅ Clean
packages/acp-bridge 4 (3 pre-existing + 1 stale dist) 0 ✅ Clean after rebuild
packages/cli 127 (107 base + 20 stale dist) 43 (all pre-existing patterns) ⚠️ See below

CLI typecheck delta (after core + acp-bridge rebuild):

  • 20 CLI typecheck errors were caused by stale dist/ in acp-bridge — the PR's new types (ServeHookConfig, ServeWorkspaceHooksStatus, IDLE_HOOK_EVENTS, etc.) were not in the built output. All 20 resolved after npm run build --workspace=packages/core && npm run build --workspace=packages/acp-bridge.
  • 3 remaining "new" errors in Session.ts are the standard @google/genai dual-package resolution pattern (same as 40+ other pre-existing errors in HistoryReplayer.ts, BaseJsonOutputAdapter.ts, useGeminiStream.ts, etc.)
  • 0 structurally new typecheck errors introduced by this PR ✅

Whitespace Check

git diff --check: clean ✅

Summary

Check Status
acp-bridge tests (376) ✅ Pass
CLI server tests (358 passed) ⚠️ 17 failed (16 pre-existing, 1 new assertion bug)
ESLint (12 files) ✅ Clean
Core typecheck ✅ Clean
acp-bridge typecheck ✅ Clean (after core rebuild)
CLI typecheck (delta) ✅ No structurally new errors
Whitespace ✅ Clean

Action Items Before Merge

  1. Fix test assertion in returns workspace hooks status from the bridge: update expected events object to match the full HOOK_EVENT_DESCRIPTIONS map (18 events), or use expect(...).toMatchObject(...) for partial matching.

Verified locally by wenshao

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough local verification!

Fixed the test assertion bug in 962c38f:

  1. Missing queryWorkspaceStatus dispatch: fakeBridge.queryWorkspaceStatus() was missing the qwen/status/workspace/hooks case, causing it to fall through to idle() which returns all 18 events from IDLE_HOOK_EVENTS. Added the dispatch case.
  2. Wrong description string: 'Before a tool is executed''Before tool execution' to match IDLE_HOOK_EVENTS.

@doudouOUC doudouOUC requested a review from wenshao June 6, 2026 23:38

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

[Suggestion] Missing test coverage for new bridge and SDK methods:

  1. packages/acp-bridge/src/bridge.test.tsgetSessionHooksStatus() has no round-trip or SessionNotFoundError rejection test (all other session-scoped status methods have both).
  2. packages/sdk-typescript/test/unit/DaemonClient.test.tsworkspaceHooks() and sessionHooks() have no unit tests (existing analogous methods like workspaceMcp(), workspaceSkills() all have mock HTTP response tests).
  3. packages/sdk-typescript/test/unit/daemon-public-surface.test.ts — 11 new type exports added to src/daemon/index.ts but the compile-time fence has no corresponding expectTypeOf assertions.

— qwen3.7-max via Qwen Code /review

Comment thread packages/sdk-typescript/src/daemon/DaemonClient.ts Outdated
doudouOUC added 7 commits June 7, 2026 10:49
Add read-only endpoints for hook configuration status, enabling remote
clients (web-shell, SDK consumers) to query workspace and session hooks.

- GET /workspace/hooks — config-sourced hooks (user/project/extensions)
- GET /session/:id/hooks — runtime session hooks (skill-registered)

Wiring: status types + idle factory (acp-bridge), bridge interface +
impl, ACP agent builders + extMethod dispatch, workspace-service facade,
REST routes, capability tags, SDK types + client methods, barrel exports.
Slash command /hooks enabled for ACP mode (text output via listCommand).

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
Copilot review feedback: DaemonHookEventName was defined but not used
on the entry type, so SDK consumers got plain `string` without
autocomplete/narrowing. Now uses the forward-compat union type.

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
- Fix HookEventName import to type-only (ESLint consistent-type-imports)
- Add workspace_hooks + session_hooks to EXPECTED_STAGE1_FEATURES test array
- Set initialized: false in catch block (was true, contradicted errors cell)
- Add try/catch to buildSessionHooksStatus (matching workspace pattern)
- Consolidate HOOK_MATCHER_KINDS + HOOK_EVENT_DESCRIPTIONS into
  IDLE_HOOK_EVENTS (single source of truth, exported from status.ts)
- Use conditional spread for session hook matcher field (consistency)
- Bump SDK browser bundle size limit 106KB → 108KB for new hook types
- Add fakeBridge stubs for getWorkspaceHooksStatus/getSessionHooksStatus
- Add hooks types to serve barrel exports

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
…tags

Codex review caught that workspace_hooks and session_hooks in
EXPECTED_STAGE1_FEATURES would appear before conditional tags in the
EXPECTED_REGISTERED_FEATURES spread, mismatching the registry
declaration order. Filter them from the spread and append at the
correct position (after non_blocking_prompt, matching the registry).

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
Add FakeBridgeOpts + call counters for workspaceHooksImpl / sessionHooksImpl
and happy-path supertest assertions for GET /workspace/hooks and
GET /session/:id/hooks, matching the pattern of existing diagnostic endpoints.
…description

queryWorkspaceStatus in fakeBridge was missing the
qwen/status/workspace/hooks case, causing it to fall through to idle()
which returns all 18 events. Also fixes description string to match
IDLE_HOOK_EVENTS ('Before tool execution', not 'Before a tool is executed').
Aligns with the codebase convention of using ':id' placeholder instead
of interpolating the actual sessionId value into error labels.
@doudouOUC doudouOUC force-pushed the feat/serve-hooks-status branch from 962c38f to a3a7a41 Compare June 7, 2026 02:51
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review round — wenshao comments (rebase + fix)

Thread Verdict Action
DaemonClient.ts:527 — failOnError leaks sessionId Agreed Fixed in a3a7a41 — uses :id placeholder
Review body — missing test coverage for bridge/SDK Defer Test coverage for bridge/SDK methods is a follow-up; supertest route tests were added in prior commits
Merge conflict with daemon_mode_b_main Resolved Rebased onto latest base, resolved 5 file conflicts (all independent additions from session_rewind feature)

@wenshao

wenshao commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

Local Verification Report

Branch: feat/serve-hooks-statusdaemon_mode_b_main
Environment: macOS Darwin 25.4.0, Node.js local

TypeScript Compilation (tsc --noEmit)

Package PR Branch daemon_mode_b_main Status
packages/core 0 errors 0 errors ✅ Clean
packages/acp-bridge 2 errors 1 error ⚠️ See note
packages/cli 187 errors 167 errors ✅ No new real errors (stale dist/)
packages/sdk-typescript 1 error 1 error ✅ Same pre-existing error

acp-bridge note: The +1 error is PostToolBatch does not exist in type 'Record<HookEventName, ...>' in the new IDLE_HOOK_EVENTS constant. PostToolBatch exists in core's source enum (hooks/types.ts:286) but the locally compiled dist/ is stale. CI builds fresh and this resolves. The pre-existing error (DaemonBridgeTelemetryMetrics) is the same on both branches.

Unit Tests (vitest)

Test Suite PR Branch daemon_mode_b_main Status
acp-bridge (8 files) 376 passed 376 passed ✅ All pass
server.test.ts ❌ import resolution ❌ import resolution ⚠️ Pre-existing (see below)

server.test.ts failure: @qwen-code/acp-bridge/mcpTimeouts import resolution fails in vitest on both the PR branch AND daemon_mode_b_main. This is a pre-existing stale dist/ issue — CI builds fresh dist before running tests, so this doesn't reproduce there.

Code Review

This PR adds hooks diagnostic HTTP/ACP surfaces for issue #4514 T3.9:

New types (status.ts):

  • ServeWorkspaceHooksStatus / ServeSessionHooksStatus — structured status payloads
  • ServeHookEntry with discriminated ServeHookConfig union (command / http / function / prompt / unknown)
  • IDLE_HOOK_EVENTS — static event catalog with descriptions and matcher kinds
  • createIdleWorkspaceHooksStatus() — fallback for pre-channel-ready state

Server routes (server.ts):

  • GET /workspace/hooks — workspace-level hook configuration
  • GET /session/:id/hooks — session-scoped hook status

ACP integration (acpAgent.ts):

  • serializeHookConfig() — serializes HookConfig to the wire ServeHookConfig type with undefined-conditional spreading (no nulls sent)
  • buildWorkspaceHooksStatus() / buildSessionHooksStatus() — query config.getHookSystem() with error fallback

SDK types (sdk-typescript/types.ts):

  • Mirror types with Daemon prefix and forward-compat (string & {}) arm on DaemonHookEventName
  • DaemonClient.workspaceHooks() / sessionHooks() — typed fetch methods

Other:

  • /hooks command now available in non_interactive and acp modes
  • Capability registry entries workspace_hooks / session_hooks
  • Browser bundle size cap bumped 106K → 108K
  • Server tests: 2 new test cases for workspace/session hooks routes

Verdict

Ready to merge — No regressions. All bridge tests pass. Server test failure and TSC errors are pre-existing stale-dist issues confirmed on daemon_mode_b_main. Clean implementation following existing status surface patterns.

@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! ✅ — qwen3.7-max via Qwen Code /review

@doudouOUC doudouOUC requested review from chiga0, yiliang114 and ytahdn June 7, 2026 04:22
@doudouOUC doudouOUC dismissed qwen-code-ci-bot’s stale review June 7, 2026 12:29

Already have 2 approved, 3ks.

@doudouOUC doudouOUC merged commit 3b310db into daemon_mode_b_main Jun 7, 2026
4 checks passed
@doudouOUC doudouOUC deleted the feat/serve-hooks-status branch June 7, 2026 12:29
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
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.

5 participants