* feat(web-shell): add managed workspace selector
Let Web Shell create and select daemon-managed workspaces without
changing ownership of existing sessions.
- Add capability-gated existing and scratch workspace registration
- Validate scratch roots, trust provenance, capacity, and shutdown races
- Serialize workspace mutations, session switching, and refresh results
- Add SDK/WebUI wiring and focused cross-package regression coverage
# Conflicts:
# packages/web-shell/client/App.tsx
# packages/web-shell/client/components/sidebar/WebShellSidebar.tsx
# Conflicts:
# packages/cli/src/serve/capabilities.ts
# packages/cli/src/serve/routes/workspace-management.ts
# packages/cli/src/serve/server.test.ts
# packages/sdk-typescript/src/daemon/DaemonClient.ts
# packages/web-shell/client/App.tsx
# packages/web-shell/client/components/dialogs/AddWorkspaceDialog.tsx
# packages/web-shell/client/components/sidebar/WebShellSidebar.tsx
* fix(web-shell): revalidate workspace before session creation
Prevent a stale workspace selection from bypassing the latest trusted
capability snapshot during lazy session creation.
- Validate the selected workspace before passing it to the daemon
- Fall back to the primary workspace when trust has been revoked
- Add a regression test for the pre-effect race window
- Remove stale branch state and clarify add-workspace ownership
* fix(web-shell): improve workspace removal feedback
Keep workspace removal controls legible and make blocked force removals
visibly inactive.
- Size the action menu independently from its narrow icon trigger
- Add a disabled affordance and suppress destructive hover styling
- Cover the removal menu width override with a regression test
* fix(web-shell): centralize existing workspace registration
Route sidebar and composer entry points through the App-owned dialog so
capability gating and workspace reconciliation remain consistent.
- Forward display names only when the daemon advertises support
- Hide and suppress persistence when registration is runtime-only
- Mark directory registrations with existing-workspace provenance
- Cover both entry points and capability combinations with tests
* fix(web-shell): address review feedback on workspace dialogs and capability docs (#7390)
- Document dynamic_workspace_registration and scratch_workspace_registration
in the conditional serve-features table so the capabilities-docs-contract
test passes.
- Gate DialogShell backdrop-click and Escape dismissal on the dismissible
prop so non-dismissible dialogs ignore both gestures.
- Surface an inline error when an added folder registers but the capability
refresh fails, mirroring the scratch recovery path.
- Add coverage for the active-session workspace switch and the add-folder
refresh-failure paths.
* fix(web-shell): address review feedback on workspace dialogs and capability docs (#7390)
---------
Co-authored-by: heyang.why <heyang.why@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
* feat(web-shell): Add sidebar customization API for branding, navigation, session actions, and footer
Add new sidebar configuration options to WebShellSidebarOptions:
- primaryNav.items: Control which built-in nav buttons are shown (newTask, plugins, scheduledTasks, goals)
- primaryNav.render(): Append custom content after built-in nav buttons
- hideProjectHeader: Hide the 'Projects' header row (search + add workspace)
- sessionActions.items: Control which session action items appear (both inline and dropdown)
- sessionActions.inlineItems: Control which items render as inline hover buttons (supports all action types with icon/text fallback)
- footer.render(): Inject custom UI elements on the left side of the footer
- Move scheduledTasks and goals from footer.items to primaryNav.items
Visibility follows a strict 'hide-first' policy: inline buttons require all three conditions (items includes + inlineItems includes + built-in capability check) to render.
* fix(web-shell): Prevent inline/dropdown duplication and add pin/archive dropdown fallback
- Critical fix 1: Dropdown items now exclude items already shown as inline buttons (added !inlineActionItems.has guard to each dropdown entry and trigger visibility).
- Critical fix 2: pin and archive now have dropdown menu entries as fallback when not configured as inline items, preventing them from becoming inaccessible.
- Narrowed inlineItems type to WebShellSidebarSessionInlineActionItem (excludes details/group which have no working inline handlers), preventing dead buttons.
- Added destructive color styling (var(--destructive)) to inline delete button when not disabled.
* fix(web-shell): Re-export new sidebar types and add visibility matrix tests
- Re-export WebShellSidebarPrimaryNavOptions, WebShellSidebarPrimaryNavItem, WebShellSidebarSessionActionsOptions, WebShellSidebarSessionActionItem, and WebShellSidebarSessionInlineActionItem from client/index.tsx so consumers can import them by name.
- Add session-action-visibility.test.ts: table-driven tests covering the items × inlineItems × capability matrix (default config, empty items/inlineItems, dedup guarantee, pin fallback to dropdown). 7 test cases, all pass.
* fix(web-shell): gate readOnly archive on sessionActionItems, fix footer null safety and docs accuracy (#7379)
---------
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
* feat(core): add opt-in built-in web_search backed by the DashScope Responses API
Claude-Session: https://claude.ai/code/session_01KwsYFzWZ6VLCxVN8MbeFXb
* fix(core): require HTTPS for the web_search backend; fail closed on unresolved agent allow-lists
Review follow-ups on #7215: the endpoint gate now rejects plaintext
endpoints (the side request carries a bearer key), and an agent allow-list
whose names resolve to no registered tool keeps its dead entries instead of
widening to the inherited toolset — an agent restricted to the unavailable
WebSearch now runs tool-less rather than gaining shell/write.
Claude-Session: https://claude.ai/code/session_01KwsYFzWZ6VLCxVN8MbeFXb
* chore(cli): remove test-leaked debug artifacts; gitignore the leaked dirs
The CLI unit-test suites write debug logs relative to the package dir
(custom/, first/, from-env/, workspace/); a merge-commit git add swept
them in. Remove them and ignore the directories until the tests are
pointed at temp dirs.
Claude-Session: https://claude.ai/code/session_01KwsYFzWZ6VLCxVN8MbeFXb
* fix(core): honor web_search's own result budget and salvage in-stream-error partials
- Override maxOutputChars (result limit + envelope headroom) so the
scheduler's global 25k threshold no longer slices results before the
tool's section-aware truncation can protect URL evidence sections.
- Route in-stream backend errors through the shared terminal-failure
tail so results streamed (and billed) before the error surface as a
partial result, matching the transport-error path.
- Strengthen gate tests: assert gate.ok before webExtractor, exercise
the https-only endpoint guard, and make the config mock disambiguate
same-id entries by baseUrl like the real Config.
* fix(cli): treat whitespace-only WEB_SEARCH_API_KEY as unset
Apply the function's set-but-empty-is-unset rule to the API key env
var like every sibling env read, and add loadCliConfig coverage for
the web search settings resolution (env precedence, empty-env
fallthrough, base-URL key selection).
* fix(core): salvage failed-terminal web_search results and name the exact endpoint disqualifier
- Route the failed/cancelled terminal paths through the shared
terminal-failure tail so search evidence streamed (and billed) before
the backend gave up is salvaged, consistent with the in-stream-error
and transport-error paths; regression test included.
- Classify base-URL gate rejections so the startup notice blames the
actual disqualifier: a plaintext-HTTP endpoint now gets an "use
https://" notice at both the env-declared and modelProviders sites
instead of the misleading "non-DashScope endpoint" text.
- Cover WEB_SEARCH in the speculation boundary-tools test and the US
regional host in the DashScope provider test — both behavioral
changes this PR introduced without direct test coverage.
* test(cli): cover web search suppression in safe and bare modes
The bareMode/safeMode guard is the escape hatch that keeps web search
(external, billed API calls) off in troubleshooting modes; assert that
an enabled settings config resolves to no web search settings under
--safe-mode and --bare.
* fix(core): parse the search model selector once for both gate paths
A selector written for the modelProviders path ("openai:<model-id>", as
the gate's own OAuth notice suggests) was sent verbatim to DashScope
when WEB_SEARCH_BASE_URL overrode the backend, failing with
InvalidParameter. Hoist the resolveModelId parse above the env branch
so both paths share one interpretation of the selector.
Also cover two review gaps: the Claude extension WebSearch tool mapping
and the ACP startup-warning emission that surfaces WebSearch
misconfiguration notices in the client log.
* fix(core): handle response.cancelled in the web_search terminal-event switch and trim gate env keys
- Add response.cancelled to the terminal-event switch so the
status === 'cancelled' handler is reachable instead of dead code
- Trim API key env vars in the gate (all three check sites), matching
the CLI-side whitespace rule from 302cf3bb7
- Add tests: cancelled with/without prior search, whitespace-only env
key rejection, schema getter month/year embedding
* fix(core): cap opened URLs, suppress failed-item progress, note retry budget (#7215)
* fix(core): reject unresolved selector on env-declared web_search path (#7215)
---------
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
* feat(core): add fork_turns to subagents
* fix(core): preserve nested agent context inheritance
* fix(core): isolate inherited subagent history
* refactor(core): scope fork_turns to fork agents
* test(core): cover zero-real-turns branch in selectForkHistory
Add a regression guard asserting selectForkHistory returns [] when a
numeric fork window finds no real user turns after the synthetic prefix
(e.g. only startup context present). This pins the
realUserTurnIndexes.length === 0 branch so a future refactor cannot
silently return the full history instead of an empty selection.
* docs(core): address fork_turns review feedback
- Explain the curated vs uncurated history split between the fork_turns
'all' and numeric paths in createForkSubagent.
- Document why includeCompressed is load-bearing in selectForkHistory.
- Gate the 'forks inherit ...' prose in the Writing-the-prompt section
behind isForkSubagentEnabled so non-interactive sessions no longer
advertise fork behavior, and lock it with description assertions.
* test(core): cover fork_turns 'all' and getHistoryForForkWindow fallback
Add two integration tests for prepareForkConfig fork-history selection:
- 'all' path: verify getHistoryShallow(true) sources the curated history
and selectForkHistory(history, 'all') seeds the fork with the full
history verbatim.
- numeric path: verify the getHistoryForForkWindow?.() ?? getHistory(true)
fallback still produces a correct bounded window (startup + latest real
turn) when getHistoryForForkWindow is unavailable.
* fix(core): use uncurated history for fork bounded-window fallback
The numeric fork_turns path falls back to geminiClient.getHistory(true)
when getHistoryForForkWindow is unavailable. Curated history coalesces
the leading startup reminder into the first real user turn, so
getStartupContextLength can no longer detect it as a pure prefix.
selectForkHistory then leaves the startup text embedded in the first
selected turn while the startupContext prefix is prepended separately,
duplicating startup context in the fork's initial messages.
Fall back to uncurated getHistory() instead, which keeps the startup
reminder as its own pure entry that selectForkHistory strips cleanly.
Update the fallback-path test to assert the uncurated call and document
why curated history is unsafe here.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(channels): exclude discrete messages from replies
* feat(serve): make ACP initialize handshake timeout configurable
Add --initialize-timeout-ms CLI flag to qwen serve, wiring it through
to BridgeOptions.initializeTimeoutMs. The ACP initialize handshake
defaults to 10 s (DEFAULT_INIT_TIMEOUT_MS); containerized deployments
where the child process needs longer can now raise the ceiling without
patching the source.
Fixes#7244
* fix(serve): wire initializeTimeoutMs to fast-path parser and embed bridge
Add the missing NUMBER_OPTIONS entry in fast-path.ts and forward
initializeTimeoutMs in the server.ts inline createAcpSessionBridge
call so the direct-embed / test path also respects the flag.
* fix(serve): address review — fast-path test, timer upper bound, revert #7223, docs (#7246)
* test(cli): add happy-path propagation test for initializeTimeoutMs (#7246)
* refactor(cli): reuse isPositiveIntegerMs for initializeTimeoutMs validation (#7246)
---------
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
* docs(design): define default background subagents
* feat(core): improve subagent delegation defaults
* docs(core): cross-reference the three background-classification sites
Add pointer comments linking the core dispatch source of truth
(AgentTool.execute) and its two UI mirrors (web-shell
isBackgroundSubAgentToolCall, desktop detectBackgroundEvents) so the
replicated top-level-agent background heuristic is not changed in
isolation. Addresses PR review feedback.
* fix(core): align background classification for fork and named-teammate launches
Address review feedback on the background-classification rule so core dispatch
and the two UI classifiers stay consistent:
- core: exclude a name-without-active-team launch from the default-background
path so it stays foreground, matching both UI classifiers (which exclude
name). Previously such a launch was backgrounded by core but tracked as
foreground by the UIs.
- web-shell and desktop classifiers: exclude subagent_type "fork" from the
default-background heuristic, mirroring core's !isForkRequested guard. A
top-level fork request with an omitted flag runs foreground in core but was
classified as background by the UIs.
- add a core dispatch test asserting a working_dir launch with an omitted
run_in_background flag stays in the foreground.
* test: cover fork/background classification and precedence per review feedback
Address unresolved review threads on PR #7048:
- Add web-shell and desktop UI classifier tests asserting an omitted-flag
`subagent_type: "fork"` launch stays in the foreground, verifying the
documented `!isForkRequested` parity with core dispatch.
- Add a core AgentTool test asserting an explicit `run_in_background: false`
overrides a subagent config with `background: true`, locking in the
`run_in_background ?? config` precedence against a `||` regression.
- Harden the Explore read-only prompt: pipelines must not send data to a
network endpoint (no curl/wget/nc), closing the `cat file | curl`
exfiltration gap.
* fix(core): restore general no-unnecessary-files guard in general-purpose prompt
Address review feedback: the rewritten general-purpose prompt dropped the
broad guard against creating unrequested files, keeping only the
documentation-specific line. Restore a general 'do not create files unless
necessary' guard so speculative utility/config files are not created.
* test(desktop): cover named-teammate foreground guard in detectBackgroundEvents
Add a desktop tool-matching test asserting a top-level Agent with a
`name` set (named teammate) stays foreground and emits no
task_backgrounded event, mirroring the web-shell classifier's
named-teammate coverage and the existing fork-exclusion test.
* test(core): cover named-teammate foreground dispatch when flag omitted
Add a core-dispatch test asserting a top-level Agent launch with `name`
set and `run_in_background` omitted stays foreground when no team is
active, guarding the `this.params.name === undefined` exclusion in
backgroundRequested directly (previously only covered by the UI
classifiers).
---------
Co-authored-by: Claude <noreply@anthropic.com>
Expose persisted active/archived/total (plus live) via a dedicated aggregate
endpoint so clients do not need to page the full session list. Counts reuse the
existing chats-dir disk scan pattern from session title search; responses mark
expensive/disk_scan so callers know not to poll.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* feat(cli): group daemon channel workers by workspace (phase 4b)
Multi-workspace `qwen serve --channel` now runs one channel worker per owning workspace instead of a single primary-bound worker. Each worker binds to its workspace's directory, daemon-workspace env marker, and effective env overlay. Channels are grouped implicitly by their configured working directory: a channel belongs to the registered workspace its resolved cwd matches, mirroring the worker's own workspace validation. Unknown, ambiguous, or untrusted targets fail fast at startup.
The pidfile and daemon status grow an additive per-workspace worker list while keeping the existing single-worker fields for older readers; single-workspace daemons stay byte-identical to before. `--channel all` stays primary-only.
Refs #6378
* fix(cli): harden multi-workspace channel workers
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): close listener after channel worker startup failure
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): restore grouped channel webhooks
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): mount runtime before channel workers start
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* codex: address PR review feedback (#6635)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(cli): strengthen channel worker edge coverage
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* chore(cli): address channel review suggestions
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>