qwen-code/docs/design
ChiGao ce4b0cf629
feat(sdk,serve): DaemonTransport abstraction + ACP standard compliance (#5040)
* feat(sdk): DaemonTransport abstraction — pluggable transport for REST/ACP-HTTP/ACP-WS

- DaemonTransport interface with fetch + subscribeEvents
- RestSseTransport: extract current SSE logic from DaemonClient
- AcpWsTransport: WebSocket multiplexer + URL-to-JSON-RPC mapping
- AcpHttpTransport: POST /acp + session-scoped SSE
- AcpEventDenormalizer: JSON-RPC notification -> DaemonEvent
- AutoReconnectTransport: opt-in reconnect + fallback wrapper
- negotiateTransport(): auto-detect best transport via GET /capabilities
- Provider: DaemonWorkspaceProvider gains transport prop
- Server: GET /capabilities advertises supported transports
- Zero breaking changes: no transport = current REST behavior

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* docs(design): include DaemonTransport design doc in implementation PR

* fix(sdk): address 6 verification findings — bundle size, WS hang, error types, ACP compat

- Remove ACP transport class re-exports from barrel (index.ts) to avoid
  ~19.7KB browser bundle bloat; keep type-only exports
- Fix WS dial hang: reject connect promise in onerror when not yet
  connected (Node WebSocket may only fire error, not close)
- Fix parked generators: maintain _activeGenerators set, abort all on
  WS close so generators throw DaemonTransportClosedError
- Forward abort signal through AcpHttpTransport.sendRequest to fetch
- Restore DaemonHttpError in RestSseTransport (was plain Error)
- ACP endpoint compat: extract connectionId from initialize, send
  Acp-Connection-Id header, add _qwen/ prefix for vendor methods,
  preserve real HTTP status in error mapping, fetch /capabilities
  from REST endpoint for correct shape

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): address 16 review findings + CI bundle size

- CI: move negotiateTransport to separate file, extract DaemonHttpError
  to break static import chain from barrel -> DaemonClient. Browser
  bundle drops from 136KB to 115KB, under the 116KB budget.
- Route table: extract shared acpRouteTable.ts, used by both transports.
  Unify method naming (remove _qwen/ prefix inconsistency).
- Token: move from URL query to Authorization header on WS upgrade
- Error type: DaemonHttpError extracted to DaemonHttpError.ts; import
  in RestSseTransport no longer pulls in DaemonClient.
- Init retry: reset failed initPromise so next call retries
- Reconnect mutex: prevent concurrent reconnect storms
- Generator queue: cap at 256, drop-oldest
- WS init timeout: 30s default
- negotiate: clear timer on all paths, catch dispose rejection
- Headers: forward init.headers in ACP transports via mergeHeaders()
- Dead code: remove unused pendingRequests/sseAbort fields
- Provider: dispose client on unmount
- Helpers: extract matchRoute/synthesizeResponse/jsonRpcErrorToHttpStatus/
  isRecord/composeAbortSignals to shared acpTransportUtils.ts
- Package exports: add deep import paths for ACP transports
- Tests: add AcpEventDenormalizer unit tests (17 cases)

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): ESLint array-type rule — ReadonlyArray<T> → readonly T[]

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): fix 3 ACP wire bugs + bundle size + npm exports

Wire bugs (verified broken against real daemon):
1. AcpHttpTransport: read connectionId from response header + correct JSON path
2. AcpWsTransport: send token via Authorization header, not URL query
3. AcpEventDenormalizer: read params.update.sessionUpdate, not params.type

Bundle: remove negotiateTransport from barrel-reachable imports
Exports: add package.json deep import paths for ACP transports

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(sdk): comprehensive ACP transport test suite (~175 tests)

- RestSseTransport: fetch delegation, SSE subscribe, auth, timeout, signal
- AcpWsTransport: route mapping, token auth, event filtering, queue cap
- AcpHttpTransport: connectionId extraction, header injection, init retry
- AutoReconnectTransport: reconnect mutex, fallback, delegation
- negotiateTransport: capability probing, timeout, fallback
- acpRouteTable: URL→method mapping, param extraction

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): route table coverage, browser WS auth, header forwarding, capabilities type

- Route table: add file/stat/list/glob/write/edit paths (all DaemonClient URLs)
- Route table: add session diagnostic routes (context, tasks, stats, rewind, language)
- Route table: add bulk sessions/delete
- WS auth: document browser limitation, Node uses headers, browser needs proxy
- Headers: forward X-Qwen-Client-Id via JSON-RPC _meta in WS transport
- DaemonCapabilities: add transports field to SDK type
- Package exports: remove unreachable deep exports, document monorepo usage
- Provider bypass: document limitation for glob/stat/list in workspace actions

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): add missing detach + hooks routes per QA doc

Cross-referenced with daemon-acp-integration-qa.md route table.
Added POST /session/:id/detach and GET /session/:id/hooks.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve,sdk): enforce ACP standard session/new — always isolated session

ACP standard mandates session/new MUST create a new isolated session.

Server-side (dispatch.ts):
- Force sessionScope='thread' on /acp session/new, ignoring client params
- REST POST /session retains 'single' default for backward compat

SDK-side (acpRouteTable.ts):
- Strip sessionScope from session/new params in ACP transports
- Document that ACP follows the standard (no extensions)

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve): ACP session/new returns standard models/modes fields

ACP standard NewSessionResponse includes optional `models` and `modes`
top-level fields alongside `configOptions`. Extract model/mode state
from configOptions and surface them as standard-shaped objects:
- models: { currentModelId, availableModels: [{id}] }
- modes:  { currentModeId, availableModes: [{id}] }

Also update test to verify sessionScope is always forced to 'thread'
(ACP standard compliance — session/new always creates isolated session).

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* feat(serve): add standard ACP methods session/set_mode, session/set_model, session/fork

Align /acp endpoint with ACP standard protocol:

- session/set_mode: dedicated method for mode changes (standard)
  Maps to bridge.setSessionApprovalMode(). Params: {modeId, sessionId}
- session/set_model: dedicated method for model changes (unstable)
  Maps to bridge.setSessionModel(). Params: {modelId, sessionId}
- session/fork: create a branched copy of an existing session
  Maps to bridge.branchSession(). Response includes configOptions,
  models, modes per ACP standard.
- session/load, session/resume: responses now include configOptions,
  models, modes (per ACP LoadSessionResponse/ResumeSessionResponse)

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve): TS2345 — pass persist: false to setSessionApprovalMode

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(webui): add dispose() to MockDaemonClient in provider tests

DaemonClient now has dispose() (called in provider cleanup effect).
Mock clients in test files need to implement it.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve): add sessionId pre-validation + remove type assertion

- session/set_mode, session/set_model: add explicit sessionId empty
  check before requireOwned (consistent with session/fork)
- session/set_model: remove `as unknown as` type assertion, pass
  proper {modelId, sessionId} matching SetSessionModelRequest

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk,serve): align route table with dispatcher + AcpHttp SSE response correlation

Route table:
- Add _qwen/ prefix to all vendor session/workspace methods
- Split workspace catch-all into granular dispatcher methods
- Fix session/branch → session/fork, model → session/set_model
- Remove routes with no dispatcher handler

AcpHttpTransport:
- Implement conn-scoped SSE stream for response correlation
- POST returns 202 (ack), real response rides SSE stream
- Map<id, {resolve, reject}> for pending request correlation

dispatch.ts:
- Remove session/set_mode, session/set_model from CONN_ROUTED_METHODS

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): bump browser bundle budget 116KB→118KB for transport abstraction

Main uses 117,753 bytes (99.1% of 116KB budget). The transport
abstraction adds ~1.5KB (DaemonTransport interface + RestSseTransport
default constructor in DaemonClient). Bump to 118KB (120,832 bytes).

Also change RestSseTransport to type-only export from barrel (class
is constructed internally by DaemonClient, not needed as a value
export for consumers).

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): fix 2 test failures — SSE error message + workspace catch-all route

- RestSseTransport: error message 'SSE response has no body' → 'No SSE body'
  (matches existing DaemonClient.test.ts assertion)
- acpRouteTable: re-add GET/POST /workspace/* catch-all after granular routes
  (AcpWsTransport.test.ts expects generic workspace path to resolve)

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(sdk): align RestSseTransport test with updated error message

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-14 02:37:06 +08:00
..
adaptive-output-token-escalation fix(core): remove unused debugResponses array and dead extractUsageFromGeminiClient (#4982) 2026-06-13 07:51:19 +08:00
auth refactor(cli): provider-first auth registry with unified install pipeline (#3864) 2026-05-08 12:19:28 +08:00
auto-memory feat(memory): managed auto-memory and auto-dream system (#3087) 2026-04-16 20:05:45 +08:00
channels docs(channels): consolidate design docs into single file 2026-04-02 11:17:37 +08:00
compact-mode feat: optimize compact mode UX — shortcuts, settings sync, and safety (#3100) 2026-04-16 09:29:24 +08:00
compaction-image-stripping feat(core): strip inline media before chat compaction summary (#4101) 2026-05-14 10:20:11 +08:00
customize-banner-area feat(cli): customize banner area (logo, title, hide) (#3710) 2026-05-07 10:17:53 +08:00
daemon-acp-http feat(core): durable cron jobs — /loop tasks that survive restarts (#5004) 2026-06-13 19:30:40 +08:00
daemon-transport-abstraction feat(sdk,serve): DaemonTransport abstraction + ACP standard compliance (#5040) 2026-06-14 02:37:06 +08:00
fork-subagent feat(core): implement fork subagent for context sharing (#2936) 2026-04-14 14:27:38 +08:00
prompt-suggestion fix(followup): prevent tool call UI leak and Enter accept buffer race (#2872) 2026-04-09 00:07:03 +08:00
rt-optimization feat(telemetry): foundation for skill-based RT optimization (P0+P1) (#4565) 2026-05-29 02:17:40 +08:00
session-idle-reaper feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
session-recap feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
session-title feat(session): auto-title sessions via fast model, add /rename --auto (#3540) 2026-04-23 20:37:05 +08:00
skill-nudge feat(memory): add autoSkill background project skill extraction (#3673) 2026-05-09 14:25:02 +08:00
slash-command feat(cli): improve slash command discovery (#3736) 2026-05-09 14:25:44 +08:00
structured-output docs: user + design docs for --json-schema structured output (#4051) 2026-05-17 23:10:34 +08:00
tool-use-summary fix(cli): add API Key option to qwen auth interactive menu (#3624) 2026-04-27 22:01:47 +08:00
virtual-viewport feat(cli): virtual viewport for long conversations on ink 7 (#4146) 2026-06-02 13:57:17 +08:00
2026-05-15-async-memory-recall-design.md fix(core): decouple auto-memory recall from main-agent request path (#4172) 2026-05-19 13:58:58 +08:00
auto-compaction-threshold-redesign.md feat(core)!: redesign auto-compaction thresholds with three-tier ladder (#4345) 2026-05-25 21:11:08 +08:00
custom-api-key-auth-wizard-prd.md docs(auth): add custom API key wizard PRD (#3583) 2026-05-13 14:04:41 +08:00
f2-mcp-transport-pool.md feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
markdown-syntax-extension.md feat(cli): expand TUI markdown rendering (#3680) 2026-05-07 16:24:13 +08:00
openrouter-auth-and-models.md refactor(cli): remove legacy qwen auth CLI subcommand, redirect to /auth TUI dialog (#3959) 2026-05-11 16:44:09 +08:00
telemetry-llm-request-timing-design.md feat(telemetry): Phase 4b — retry visibility for qwen-code.llm_request (#3731) (#4432) 2026-06-05 13:45:47 +08:00
telemetry-outbound-propagation-design.md feat(telemetry): client-side HTTP span + opt-in W3C traceparent propagation (#4384) (#4390) 2026-05-25 22:16:54 +08:00
telemetry-resource-attributes-design.md feat(telemetry): support custom resource attributes and add metric cardinality controls (#4367) 2026-05-21 13:54:37 +08:00
telemetry-subagent-spans-design.md feat(telemetry): Phase 3 — qwen-code.subagent span with concurrent isolation (#3731) (#4410) 2026-06-05 17:12:34 +08:00
workflow-tracing-gaps.md feat(telemetry): unify span creation paths for hierarchical trace tree (#4126) 2026-05-16 22:29:55 +08:00
worktree.md feat(worktree): Phase D — startup --worktree flag + symlinkDirectories + PR refs (#4381) 2026-05-27 17:04:51 +08:00