feat(daemon): add session tasks snapshot endpoint#4578
Conversation
📋 Review SummaryThis PR implements a read-only daemon session task snapshot API ( 🔍 General Feedback
🎯 Specific Feedback🟡 High
🟢 Medium
🔵 Low
✅ Highlights
|
There was a problem hiding this comment.
Pull request overview
Adds an out-of-band, read-only “session tasks snapshot” surface to the daemon stack so UIs (notably web-shell) can inspect background tasks during an actively streaming prompt without going through the prompt FIFO.
Changes:
- Introduces
GET /session/:id/tasksinqwen serve, backed by ACP status extMethodqwen/status/session/tasks. - Extends the TypeScript SDK with task snapshot types plus
DaemonClient.sessionTasks()/DaemonSessionClient.tasks(). - Adds a web-shell
/taskslocal slash-command handler that fetches and prints the snapshot, plus unit coverage.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/web-shell/client/utils/tasksCommand.ts | Adds local /tasks formatter + handler that fetches session task snapshots via SDK. |
| packages/web-shell/client/utils/tasksCommand.test.ts | Adds unit tests for snapshot formatting and local command handling. |
| packages/web-shell/client/hooks/useDaemonSession.ts | Exposes actions.getTasks() that delegates to session.tasks(). |
| packages/web-shell/client/App.tsx | Intercepts /tasks before generic slash forwarding/enqueueing. |
| packages/sdk-typescript/test/unit/DaemonSessionClient.test.ts | Verifies DaemonSessionClient.tasks() hits /session/:id/tasks. |
| packages/sdk-typescript/test/unit/DaemonClient.test.ts | Verifies DaemonClient.sessionTasks() URL-encodes session ids and sends client id header. |
| packages/sdk-typescript/src/index.ts | Re-exports new daemon task snapshot types from the SDK root. |
| packages/sdk-typescript/src/daemon/types.ts | Defines daemon-side session task snapshot wire types. |
| packages/sdk-typescript/src/daemon/index.ts | Re-exports daemon task snapshot types. |
| packages/sdk-typescript/src/daemon/DaemonSessionClient.ts | Adds tasks() convenience method on session client. |
| packages/sdk-typescript/src/daemon/DaemonClient.ts | Adds sessionTasks() method mapping to GET /session/:id/tasks. |
| packages/cli/src/serve/server.ts | Implements GET /session/:id/tasks route calling the bridge snapshot method. |
| packages/cli/src/serve/server.test.ts | Covers the new route and 404 mapping for missing sessions. |
| packages/cli/src/serve/index.ts | Re-exports serve task snapshot types. |
| packages/cli/src/serve/capabilities.ts | Adds session_tasks capability tag. |
| packages/cli/src/acp-integration/session/tasksSnapshot.ts | Serializes whitelisted task registries into a stable snapshot shape. |
| packages/cli/src/acp-integration/acpAgent.ts | Adds ACP extMethod handler for qwen/status/session/tasks. |
| packages/cli/src/acp-integration/acpAgent.test.ts | Ensures serialization is sanitized/whitelisted and does not leak internal fields. |
| packages/acp-bridge/src/status.ts | Adds shared serve protocol status types for session task snapshots + ext method constant. |
| packages/acp-bridge/src/bridgeTypes.ts | Extends HttpAcpBridge with getSessionTasksStatus(). |
| packages/acp-bridge/src/bridge.ts | Implements getSessionTasksStatus() via requestSessionStatus(...). |
| packages/acp-bridge/src/bridge.test.ts | Ensures tasks status requests bypass the prompt queue and handle missing sessions. |
| packages/acp-bridge/README.md | Documents the new /session/:id/tasks route as part of the lifted status slice. |
| integration-tests/cli/qwen-serve-routes.test.ts | Updates capabilities envelope expectations to include session_tasks. |
| docs/users/qwen-serve.md | Documents the new read-only session tasks snapshot route. |
| docs/developers/qwen-serve-protocol.md | Documents capability tag + wire shape and semantics for GET /session/:id/tasks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add a read-only daemon session task snapshot status method and HTTP route so clients can inspect background tasks without sending a prompt. Expose the snapshot through the TypeScript SDK and intercept /tasks in web-shell before generic slash-command forwarding. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
ac1b37b to
da3dbc3
Compare
Fix summary (58e1629)
|
|
|
||
| expect(handled).toBe(false); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
[Suggestion] handleTasksSlashCommand error path is untested. When getTasks() rejects, the .catch handler at tasksCommand.ts:117-119 calls reportError(error, 'Failed to load tasks') — but no test exercises this branch.
Consider adding a test case:
it('reports an error when getTasks rejects', async () => {
const error = new Error('network down');
const getTasks = vi.fn().mockRejectedValue(error);
const dispatch = vi.fn();
const reportError = vi.fn();
const handled = handleTasksSlashCommand({
cmd: 'tasks',
promptBlocked: true,
getTasks,
dispatch,
reportError,
});
expect(handled).toBe(true);
await vi.waitFor(() => expect(reportError).toHaveBeenCalledTimes(1));
expect(reportError).toHaveBeenCalledWith(error, 'Failed to load tasks');
expect(dispatch).not.toHaveBeenCalled();
});— qwen3.7-max via Qwen Code /review
| expect(text).not.toContain('\u001b'); | ||
| }); | ||
| }); | ||
|
|
There was a problem hiding this comment.
[Suggestion] formatTaskStatus is only tested for kind: 'shell' with status: 'running'. The agent kind has distinct branches (failed with error/'unknown error' fallback, paused + resumeBlockedReason, default passthrough). The monitor kind has completed with/without error, failed, and default with event-count pluralization (1 event vs N events). None of these are exercised.
Consider adding test cases covering at least agent with failed, agent with paused+resumeBlockedReason, and monitor with running (to verify event pluralization):
it('renders agent and monitor task kinds with status-specific formatting', () => {
const text = formatTasksSnapshot(
makeSnapshot([
{
kind: 'agent',
id: 'a-1',
label: 'reviewer: check',
description: 'check',
status: 'failed',
startTime: 1_000,
runtimeMs: 500,
isBackgrounded: true,
error: 'timeout',
},
{
kind: 'monitor',
id: 'm-1',
label: 'tail logs',
description: 'tail logs',
status: 'running',
startTime: 1_000,
runtimeMs: 2_000,
command: 'tail -f',
eventCount: 1,
lastEventTime: 2_000,
droppedLines: 0,
},
]),
);
expect(text).toContain('failed: timeout');
expect(text).toContain('running (1 event)');
});— qwen3.7-max via Qwen Code /review
Maintainer Verification Report环境: macOS Darwin 25.4.0, Node.js v22.17.0 Build
Unit Tests
E2E:
|
| 场景 | 结果 |
|---|---|
| 不存在的 session ID | ✅ HTTP 404, "No session with id ..." |
| 无认证 token | ✅ HTTP 401, "Unauthorized" |
| 已关闭的 session | ✅ HTTP 404 |
结论
本 PR 功能验证通过,可以 merge。
核心目标 — web-shell 在 streaming 期间通过独立的 status 路径查询 background tasks snapshot,绕过 prompt FIFO — 已验证正常工作。涉及的 4 个包 (acp-bridge, cli, sdk, web-shell) 全部构建通过,750+ 单元测试全部通过,HTTP 端点/SDK/认证/边界情况均按预期工作。
chiga0
left a comment
There was a problem hiding this comment.
Review Summary
PR Classification: New Feature (feat)
Scope reviewed: Full stack — tasksSnapshot serialization, acpAgent extMethod, bridge status path, server HTTP route, SDK types + client methods, web-shell /tasks command, all tests.
Overall Assessment
Clean, well-structured PR that follows existing status endpoint patterns precisely. Key design decisions are sound:
- FIFO bypass: Using
requestSessionStatus(extMethod) instead of going through the prompt queue is the correct approach — clients need to inspect tasks while a prompt is streaming. The bridge test explicitly verifies this. - Whitelist serialization:
tasksSnapshot.tsexplicitly selects fields rather than spreading raw registry entries. Internal fields (abortController,pendingMessages,outputOffset,idleTimer) are properly excluded. Verified by acpAgent.test.ts assertions. - Terminal injection defense:
sanitizeTaskTextstrips control characters (0x00-0x1F except HT/LF/CR, 0x7F-0x9F) from the formatted output — prevents ANSI escape injection in web-shell terminal. - Exhaustive switch:
formatTaskStatususesconst _exhaustive: never = taskfor compile-time exhaustiveness checking. - Testable
now:buildSessionTasksStatusacceptsnow = Date.now()parameter — enables deterministicruntimeMsassertions in tests.
Test Coverage
Comprehensive across all layers: bridge (FIFO bypass + SessionNotFoundError), server (200/404 paths), acpAgent (all 3 task kinds + serialization whitelist), tasksCommand (formatting, sanitization, slash command dispatch).
Findings (1 item)
| # | Severity | File | Issue |
|---|---|---|---|
| 1 | Nit | tasksCommand.ts:100 |
promptBlocked in handleTasksSlashCommand input interface is declared but never read |
LGTM overall — the design, implementation, and tests are solid.
This review was generated by QoderWork AI
|
|
||
| export function handleTasksSlashCommand(input: { | ||
| cmd: string; | ||
| promptBlocked: boolean; |
There was a problem hiding this comment.
[Nit] promptBlocked parameter declared but unused
The handleTasksSlashCommand input interface declares promptBlocked: boolean, and App.tsx passes it, but the function body never reads it.
Since /tasks is intentionally designed to work regardless of prompt state (it bypasses the FIFO via extMethod), this parameter is currently dead code. Consider either:
- Removing it from the interface and the call site in
App.tsxto avoid confusion - Adding a comment like
// reserved for future prompt-aware routingif it's intentionally kept for forward compatibility
This review was generated by QoderWork AI
chiga0
left a comment
There was a problem hiding this comment.
LGTM. Clean implementation with comprehensive tests.
- FIFO bypass via extMethod is the correct design choice
- Whitelist serialization properly excludes internal fields (
abortController,pendingMessages,outputOffset,idleTimer) sanitizeTaskTextprovides terminal injection defense- Test coverage across all layers (bridge, server, acpAgent, tasksCommand)
One minor nit: promptBlocked parameter in handleTasksSlashCommand is declared but unused — non-blocking.
This review was generated by QoderWork AI
Summary
GET /session/:id/tasks) backed by ACP status extMethodqwen/status/session/tasks, SDK helpers, and web-shell/taskslocal handling./tasksinterception before generic slash forwarding.Validation
/tasksin web-shell fetches a local snapshot while streaming and does not callsendPromptor enqueue./session/:id/tasks, and web-shell handler dispatches local status output./taskswhile a long prompt is streaming.npm run build && npm run typecheckprinted existing vscode companion lint warnings but exited 0.Scope / Risk
Testing Matrix
Testing matrix notes:
Linked Issues / Bugs