* 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>
* feat(serve): Add cursor-paged transcript replay endpoint
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): Bound transcript replay indexing
Limit transcript index builds to bounded snapshots and surface oversized transcript errors as 413 responses. Give transcript status calls a dedicated timeout and update the capabilities integration baseline.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Validate transcript cursors
Sign transcript cursors so forged snapshot sizes cannot bypass the index cache, and keep hasMore tied to persisted record availability when replay conversion returns a partial page.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Lazy-init transcript cursor secret
Avoid generating the transcript cursor HMAC key while importing the core barrel so unrelated tests with narrow crypto mocks can load core without requiring randomBytes. Keep the VS Code companion crypto mock partial so it only replaces the auth-token UUID behavior it asserts on.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): Address transcript replay review suggestions
Mark bounded replay truncation frames as having a transcript endpoint, sanitize paged transcript replay conversion errors, and remove the core reader's incomplete pre-encoded cursor field so cursors are only emitted after replay continuation state is merged.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): Stabilize transcript replay pagination
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Avoid quadratic transcript line scanning
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Mark transcript history gaps
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Address transcript reader review comments
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): Address transcript replay review feedback
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): align transcript cursor preflight errors
Return transcript snapshot conflicts for cursor pagination when the active JSONL can no longer be found during route preflight. Add route-level and integration coverage for full transcript paging, and document the boolean fullTranscriptAvailable SDK contract.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(cli): Cover paged dangling tool call replay
Add a HistoryReplayer.replayPage regression test that carries a dangling tool call through pendingToolCalls and finalizes it on a later page.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Bound transcript index cache bytes
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix: Address transcript replay review follow-ups
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): Preserve pending tool calls on transcript replay errors
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* codex: address PR review feedback (#6525)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* codex: fix CI failure on PR #6525
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): warm transcript-replay tools leniently
The read-only transcript-replay Config sets skipSkillManager, but Config.initialize() still runs toolRegistry.warmAll({ strict: true }), which constructs SkillTool whose constructor throws when no SkillManager exists. The throw escaped the replay try/catch and surfaced as JSON-RPC -32603, so GET /session/:id/transcript returned HTTP 500 for every persisted session.
Add a lenientToolWarmup initialize option and set it for the replay Config so tools that cannot construct under the deliberately-skipped subsystems are logged and skipped instead of aborting initialize(). Replay only needs optional tool_call metadata and ToolCallEmitter already falls back to the recorded tool name, so buildable tools keep full title/kind. This supersedes the narrower excludeTools:[Skill] guard, which is removed.
* fix(core): invalidate transcript index cache on in-place rewrites
An in-place transcript rewrite that keeps the inode and byte length (e.g. rsync --inplace or a redaction pass) reused a stale cached index, because makeCacheKey() keyed only on path:dev:ino:size. readSegmentRecords then found each recorded offset parsing to a different uuid and dropped it, so GET /session/:id/transcript answered 200 with an empty events array instead of the documented 409.
Include the file mtime in the index cache key so a fresh read after a same-size rewrite rebuilds the index, and raise SessionTranscriptSnapshotUnavailableError (-> 409) on a uuid mismatch or missing fragment instead of silently returning a short/empty transcript. Also make the qwen-serve docs explicit that at the default --channel-idle-timeout-ms 0 each page rebuilds the index (O(snapshotSize)).
* codex: address PR review feedback (#6525)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* qwen: fix CI failure on PR #6525
The Run ESLint step failed on vitest/valid-expect in packages/acp-bridge/src/bridge.test.ts: the getSessionTranscriptPage timeout test stores expect(request).rejects.toBeInstanceOf(BridgeTimeoutError) and awaits it only after advancing the fake timers (a deliberate deferred await so the pending timeout rejection has a handler before it fires). Auto-fixing would add an inline await and deadlock the test, so scope-disable the rule on that assignment with a rationale. lint:ci and the affected test pass.
* qwen: address PR review feedback (#6525)
Withhold nextCursor on a mid-page transcript replay error. When collectHistoryReplayUpdatesPage catches a replayError partway through a page, records after the failed one are dropped and pendingToolCalls reflect partial state; still emitting nextCursor advanced the client past the dropped records and carried corrupted pendingToolCalls forward (phantom in-progress tool calls on later pages). Now nextCursor is withheld whenever replay.replayError is set — the page is already flagged partial + replayError, so the client stops instead of paginating with corrupted cursor state. Update the handler test to assert no cursor is issued on a replay error.
* qwen: address PR review feedback (#6525)
Log when parseTranscriptReplayState drops malformed pending tool calls from a replay cursor. Previously rawPending.filter(isPendingReplayToolCall) silently discarded entries that no longer matched the shape (e.g. a cursor from a newer daemon or corrupted in transit), turning a version-mismatch/corruption into a hard-to-diagnose 'tool never completed' artifact on later pages. Now emit a debug warning with the dropped/total counts; behavior is otherwise unchanged.
* fix(serve): address transcript review feedback
Dispose superseded replay configs, preserve structured resolution errors, sanitize multi-workspace failures, and expand transcript replay coverage across unit and real-daemon integration paths.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* qwen: address transcript review feedback (#6525)
- [Critical] Map a missing transcript session to HTTP 404: the child throws a raw resourceNotFound (ENOENT without a cursor) that fell through sendBridgeError to 500. bridge.getSessionTranscriptPage now translates it to SessionNotFoundError, mirroring the load/resume path, with a bridge test.
- Dedup the untrusted-session-owner 403 onto the shared sendUntrustedWorkspaceResponse so the response format/message stay consistent across session routes (route logging + context preserved).
- Add coverage for parseTranscriptReplayState's non-object replay branch (cursor replay=garbage) -> empty pendingToolCalls + default cumulativeUsage.
- Document that cursorHmacKeys are cached for the daemon lifetime (external key rotation requires a restart).
* qwen: adopt transcript review suggestions (#6525)
- Add a handler test that a mid-page replay error preserves already-emitted events (events>=1) alongside partial+replayError and withholds the cursor.
- Add a two-call handler test for the cross-page cumulativeUsage round-trip: page 1 folds the bumped usage into the encoded cursor; page 2 decodes and propagates it into the replay context.
- Log (not silently drop) a superseded structured error in the multi-workspace transcript resolution fallback.
* qwen: clean up transcript test fixtures to fix no-AK CI flake (#6525)
The transcript-paging integration suite wrote ~6 persisted chats/*.jsonl sessions into the daemon's project dir and never removed them. Because vitest runs a file's suites sequentially, those leftover sessions widened a pre-existing race in the later 'PATCH /session/:id/metadata > updates displayName' test (a freshly-created session can exist on disk but not yet appear in the listWorkspaceSessions page), making it fail deterministically in the no-AK smoke run. Add an afterAll to the transcript suite that removes the project chats/ dir, restoring a clean session list for subsequent suites. Verified: full no-AK suite now passes 43/43 across repeated runs.
* qwen: harden transcript reader test timestamps + assert page fields (#6525)
The record() helper derived the ISO timestamp seconds from text.length, producing invalid values (e.g. 00:00:013) once a record's text reached 10+ chars — harmless today only because no test asserted startTime. Replace it with a monotonic base+offset timestamp (always valid, strictly increasing). Also assert the previously-unchecked required SessionTranscriptRecordPage fields (sessionId, filePath, startTime, lastUpdated); the strict-ISO checks on startTime/lastUpdated guard against the timestamp-helper class of bug.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
* feat(cli): List archived and organized sessions for non-primary workspaces
Trusted non-primary workspaces can now use archiveState=archived, view=organized, and group filters on the workspace session list routes, closing the remaining Phase 2b listing gap for the multi-workspace daemon. The listing engine was already workspace-scoped; a phase guard was the only thing rejecting these queries on non-primary workspaces, and the persisted/live selection is forced to the persisted store for organized and archived views. Untrusted workspaces are still refused, and legacy primary routes are unchanged.
Refs #6378.
* qwen: address PR review feedback (#6631)
Add a test for the view=organized&archiveState=archived combination on a trusted non-primary workspace: a pinned archived session sorts first and no live summary is merged into the archived view.
* qwen: address PR review feedback (#6631)
Log the requested view/archiveState/group in the session-list failure path, add a defensive guard so persisted-only options can never silently reach the live path, and cover the organized opaque-cursor pagination round-trip for a non-primary workspace.
---------
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>