Commit graph

7952 commits

Author SHA1 Message Date
Qwen Code Bot
eea3f77059 fix(web-shell): reset specular proximity on pointer leave and test WebGL cleanup (#8098) 2026-07-30 15:36:11 +00:00
Qwen Code Autofix
fb57db97c3 fix(web-shell): paint static dot grid and idle animation loops (#8098) 2026-07-30 14:09:43 +00:00
qwen-code-ci-bot
4c63938297 fix(web-shell): correct specular angle wrap and dot field pointer baseline (#8098) 2026-07-30 12:37:45 +00:00
Qwen Code Autofix
5812e25ec5 fix(web-shell): localise specular highlight, fix rotation direction, add idle bail-out (#8098) 2026-07-30 11:15:09 +00:00
qwen-code-dev-bot
26f57ecf97 fix(web-shell): refresh DPR on resize and cover animation a11y guards
Re-read devicePixelRatio inside each resize so the specular composer
effect and the new-session dot field keep a correctly sized backing
store when the page zoom or display scale factor changes. Add
component-level coverage for the prefers-reduced-motion guards and for
the typewriter replay after the empty editor loses focus.
2026-07-30 09:21:18 +00:00
钉萁
2676ee0dbf feat(web-shell): enhance composer animations 2026-07-30 14:24:35 +08:00
qwen-code-dev-bot
8dc29b3583
fix(integration): scale interactive read-then-write waits with env timeout (#7942) (#7943)
* fix(integration): scale interactive read-then-write waits with env timeout (#7942)

* fix(integration): drop hardcoded tool-call timeouts in favor of env default (#7942)

* fix(integration): omit redundant getDefaultTimeout() args from waitForText (#7942)

---------

Co-authored-by: Qwen Autofix <qwen-autofix@alibaba-inc.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
2026-07-29 02:16:13 +00:00
qqqys
2ae9e1f869
feat(core): bound image reads for reliable zoom (#7911)
* feat(core): return bounded image overviews

* fix(core): catch all ImageViewError codes in image overview path (#7911)

* fix(core): use decode_failed for corrupt canonical images, rename misleading test (#7911)

* fix(core): include expected formats in decode_failed message (#7911)

* fix(core): keep absolute path out of image overview error displays (#7911)

* fix(core): fall back to inline bytes when image overview cannot render (#7911)

* fix(core): label image overview render failures accurately (#7911)

---------

Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
2026-07-29 01:21:11 +00:00
qqqys
5ce832e497
feat(core): connect Goal v3 to the turn engine (#7895)
* feat(core): integrate Goal turn engine

* test(core): preserve Goal tool isolation in agent overrides

* fix(core): pause Goal at Stop hook cap

* fix(core): keep Goal recovery from blocking sessions

* fix(core): degrade failed Goal migration writes

* fix(core): reset loop detector in Goal stop hook continuation (#7895)

The goal-runtime stop hook continuation path was missing
loopDetector.reset(prompt_id) before recursing, causing tool calls
to accumulate across iterations and trip TURN_TOOL_CALL_CAP after
a handful of healthy iterations. The non-goal stop hook path already
had this reset.

Also simplifies the redundant conditional in Turn.run() — the two
near-identical sendMessageStream calls are collapsed into one since
sendMessageStream already handles an undefined goalContext internally.

* fix(core): align turn.test.ts assertion with unified sendMessageStream call (#7895)

* fix(core): preserve error cause in GoalPersistenceUnavailableError (#7895)

---------

Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: qwen-code-bot <qwen-code-bot@users.noreply.github.com>
2026-07-29 00:33:23 +00:00
qwen-code-ci-bot
3144046c6b
chore(release): v0.21.1 (#7958)
* chore(release): v0.21.1

* docs(changelog): sync for v0.21.1

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-29 00:25:30 +00:00
qqqys
0c0ca5fed0
feat(autofix): defer suggestions after five change rounds (#7913)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
* feat(autofix): defer suggestions after five change rounds

* test(autofix): execute deferred jq queries against fixture data (#7913)

* fix(autofix): scrub control markers from deferred feedback reports (#7913)

* test(autofix): execute actionable reviews and issue-level filters against fixtures (#7913)

---------

Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-07-28 17:41:49 +00:00
Shaojin Wen
5b6714b9fb
fix(ci): give each job its own proxy wrapper directory (#7951)
The PR review job and the containerised triage jobs all built their gh/git
proxy wrappers in one fixed directory under RUNNER_TEMP. On the shared
self-hosted runner that path outlives a job, and the triage containers write
it as root through the RUNNER_TEMP bind mount. Once that happened, the review
job — running as the unprivileged runner user — could neither overwrite the
wrapper nor remove the root-owned directory holding it, so every review
scheduled on that runner died with EACCES while writing the wrapper, before
the review itself had started.

Each run now creates a private wrapper directory and removes it on exit, and
the container jobs keep theirs on the container's own disk so they stop
leaving root-owned state on the host.
2026-07-28 17:22:35 +00:00
Shaojin Wen
ddf4b8875d
fix(triage): make the build-process guard diagnosable and zombie-aware (#7858)
* fix(triage): make the build-process guard diagnosable and zombie-aware

The first real /verify run to reach the agent step (job 30267953352) was
stopped by the guard I added to stop detached lifecycle children from
outliving their step:

    ::error::Processes owned by the build user survived; refusing to start
             the agent.

That is all it printed. No pid, no state, no command line — so a genuine
threat and a harmless leftover were indistinguishable, including to the
person who wrote it. The control had no observability of its own.

Two changes, both in the verify and tmux lanes:

- name the survivors. The error now lists each one as pid, state and
  command line, so the next occurrence can actually be diagnosed.
- disregard zombies. A zombie has already exited and released everything
  except its exit status: it cannot re-plant an artifact or touch the
  agent's inputs, and it cannot be killed either — so counting one means
  the check can never clear, no matter how many times SIGKILL is sent.
  The container runs no init to reap orphans, so they are expected here.

The platform difference is the reason the old form could hang: Linux
pgrep reports defunct processes ("Defunct processes are reported." —
pgrep(1), procps-ng), while macOS pgrep does not list them at all
(verified directly: ps shows our zombie, pgrep does not). So this failure
mode was unreachable in local replay and only appears on the runner.

That same difference shapes the tests. The behavioural arm constructs a
real zombie, confirms it survives SIGKILL, and shows the state filter
drops it — but it cannot discriminate the old implementation from the new
one on macOS, because pgrep never saw the zombie there in the first place.
So the portable guarantee is asserted structurally: the filter must read
process state and exclude Z, and must not be a bare pgrep. Mutation-
verified 3/3 — removing the state filter, dropping the survivor names, or
reverting the message each turn one test red.

Also gives both proxy-watchdog tests an explicit timeout. They stream 20
chunks at 200 ms — 4 s before the stall arm even starts — so they cannot
fit vitest's 5 s default and were timing out on main.

* fix(triage): tolerate zero-process exit in build guard (#7858)

---------

Co-authored-by: wenshao <wenshao@example.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-07-28 17:13:40 +00:00
Shaojin Wen
25e357a279
feat(triage): make the verify report readable in Chinese (#7918)
* feat(triage): make the verify report readable in Chinese

The scope disclaimer under the verify headline has been bilingual since
the lane shipped. The verdict above it was not, and neither was the
assertion count — so a Chinese reader of the unfolded comment got the
caveat ("advisory evidence, not a review") and not the conclusion.

Three changes, all on the parts a reader reaches first:

- every headline carries a Chinese twin, across all eight arms —
  merge-ready / findings / blocked / inconclusive from the agent, and
  completed / fail / timeout / infra-error from the process outcome;
- the assertion count renders in both languages, off the same validated
  object, so an inconsistent assertions.json still suppresses both and
  the comment cannot grow a number no gate checked;
- in the report itself, 中文摘要 moves from last item to second, right
  after the verdict. The whole report is already inside a <details> on
  the PR, so leaving the Chinese summary at the bottom meant expanding
  that fold and scrolling the entire English report — about 90 lines on
  a real one — to reach the one section written for that reader. It
  stays collapsed, so it costs everyone else exactly one line.

The headline test pins the pairing as a bijection, not as containment:
asserting only that each arm renders some Chinese leaves a single
hardcoded string — or one that echoes the English — passing every arm.
Mutation-verified 4/4: dropping the Chinese headline, collapsing all
arms onto one Chinese string, dropping the Chinese assertion sentence,
and moving 中文摘要 back to the end each turn one test red.

* fix(ci): correct cross-reference direction and cover all verdict branches in bilingual headline test (#7918)

* fix(ci): narrow bilingual headline comment to match actual scope (#7918)

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

---------

Co-authored-by: wenshao <wenshao@example.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>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-28 17:13:23 +00:00
易良
bbaf13d456
fix(release): pin channel-base dep to exact version during release bump (#7953)
The channel adapters depend on @qwen-code/channel-base via a caret range
(^0.21.0). A prerelease bump such as 0.21.1-preview.0 does not satisfy
that range, so the per-workspace npm version reifies replaced the
workspace link with the stale registry package, and the release build
compiled the adapters against outdated types (missing
PollingChannelBase), failing the publish job.

Pin the adapters' channel-base dependency to the exact new version
during scripts/version.js, refresh the install, and drop the stale
nested copies npm leaves under the adapters.
2026-07-28 16:58:28 +00:00
carffuca
54d3997add
feat(web-shell): suggest BTW for side questions (#7935)
* test(web-shell): define composer intent suggestions

* feat(web-shell): suggest btw for side questions

* test(web-shell): cover pasted image suggestion gating

* fix(web-shell): block btw suggestions for inline tags

* test(web-shell): verify inline attachment snapshots

* feat(web-shell): refine BTW intent suggestions
2026-07-28 15:55:09 +00:00
易良
bfd4c8e519
fix(scripts): slim release-note model prompts and log request timing (#7941) 2026-07-28 15:32:56 +00:00
Shaojin Wen
b6b55c598c
feat(triage): surface the sandboxed lanes on the CI path (#7917)
Measured on 2026-07-28 across 22 open PRs: of the 16 whose AUTHOR had
write access and that already carried a triage comment, exactly 1
mentioned `/verify`. The lane it recommends has produced real evidence
on four PRs (#7829 2565 assertions, #7821 1347, #7830 27, #7881 17), so
the gap is not that the lane is useless — it is that almost nobody is
told it exists.

The instruction was there the whole time. It was a conditional clause
inside a section headed "2c. Real-Scenario Testing — local invocation
ONLY" whose first words are "Never in unattended CI." An agent running
in CI reasonably skips that section, and the assembly order at the end
of Stage 2 only allotted 2c a slot "when one was driven locally" — so
on the CI path there was no place for the recommendation to go even if
it had been read.

Split the CI-path half out into its own section, 2b-bis, sited
immediately after the CI-evidence step it follows from: 2b tells you the
suite is green, and cannot tell you the suite pins the change. It is a
required element of the Stage 2 comment when the central claim is
behavioural, with two explicit skip conditions — nothing behavioural to
settle, or the author lacks write (both lanes execute the author's code,
so recommending them on an external contributor's PR is a guaranteed
denial). It must name the specific unsubstantiated claim, because a bare
"you could run /verify" is noise and noise is why the line got skipped.

Stage 1e carried the same dead pointer, and worse: on the high-risk
paths — the strongest triage-time signal in the skill, 10 of 31 reverted
PRs against 5 of 60 controls, p = 0.006 — it recommended tmux alone and
never named /verify. The PRs most likely to be reverted were the ones
never offered the lane that proves a change is load-bearing. 1e now
points at 2b-bis and names both lanes.

The fix is positional, so the tests are positional: asserting that the
file mentions `/verify` would have passed throughout the entire period
the recommendation was dead. Mutation-verified 4/4 — moving 2b-bis back
below the local-only heading, dropping it from the assembly order,
dropping the author-write carve-out, and reverting 1e to the tmux-only
pointer each turn a test red. Prose assertions are whitespace-normalised
and shown to survive a maximal re-wrap, since prettier reflows this file.

Co-authored-by: wenshao <wenshao@example.com>
2026-07-28 14:28:39 +00:00
Shaojin Wen
dade3ab334
feat(web-shell): add git branch picker, commit dialog, and create PR flow (#7731)
* feat(web-shell): add git branch picker, commit dialog, and create PR flow

Add an IntelliJ-style branch picker popover to the web shell git
workspace, accessible from the branch chip in both the composer toolbar
and sidebar. The picker provides search-filtered branch listing (local,
remote, tags, recent), branch checkout, new branch creation, pull, push,
and a commit view integrated into the existing GitDialog.

The commit view reuses the diff panel (expandable file diffs with syntax
highlighting, fullscreen support) and adds a commit message textarea with
Commit / Commit and Push buttons. When a session is available (or one is
auto-created), the commit message and PR title/body are generated via the
model using session side-queries (btwSession), giving the agent full
conversation context for accurate generation.

The Create PR flow provides an inline form with auto-detected base branch
and model-generated title/description, backed by a new daemon route that
shells out to gh pr create.

New daemon routes:
- GET  /workspaces/:workspace/git/branches
- POST /workspaces/:workspace/git/checkout
- POST /workspaces/:workspace/git/branch
- POST /workspaces/:workspace/git/push
- POST /workspaces/:workspace/git/pull
- POST /workspaces/:workspace/git/commit
- POST /workspaces/:workspace/github/prs/create
- GET  /workspaces/:workspace/github/default-branch

* fix(web-shell): resolve correct workspace session for AI generation

The commit message and PR title/body generation now resolves the
most recent session for the target workspace via listWorkspaceSessions,
rather than using the globally active connection.sessionId which may
belong to a different workspace or session. Falls back to creating a
new session only when no sessions exist for the workspace.

Also improves the PR body editor with an Edit/Preview toggle using
the existing Markdown component, and updates the generation prompt
to follow the project PR template structure from AGENTS.md.

* fix(web-shell): stabilize session resolver prop to prevent infinite re-generation

Pass resolveSessionForWorkspace as a stable useCallback reference
instead of an inline arrow function. The inline function created a
new reference on every App render, causing the GitDialog useEffect
to abort and restart generation in an infinite loop.

* fix(web-shell): group remote branches by remote name and add PR target branch dropdown

Remote branches in the branch picker are now grouped by remote
(origin, upstream, etc.) with sub-headers, making fork workflows
clear. The PR create form's base branch field is now a select
dropdown populated from the workspace's branch list, grouped by
remote with optgroup labels, instead of a free-text input.

* fix(web-shell): use ref for session resolver to prevent effect re-run abort

When resolveSessionForWorkspace creates a new session, it updates
connection.sessionId in the provider, which changes the useCallback
reference, which triggers the useEffect to re-run and abort the
in-flight btwSession generation. Store the callback in a ref so the
effect never depends on its identity.

* fix(web-shell): resolve session per workspace, not from global active session

The commit/PR generation effects used connection.sessionId directly
without checking if it belongs to the target workspace. When opening
the commit dialog from a sidebar workspace different from the active
session's workspace, the wrong session was used for generation,
producing incorrect content. Now always routes through
resolveSessionForWorkspace(workspaceCwd) which checks workspace
membership before reusing the active session.

Also replaces 'PR' with 'Pull Request' / '合并请求' in all UI strings
and adds error logging to the generation catch blocks.

* fix(web-shell): retry btwSession with fresh session when stale session detected

When listWorkspaceSessions returns a session that no longer exists in
the daemon's memory (e.g. after daemon restart), btwSession fails with
'No session with id ...'. The generation effects now catch this error,
force-create a new session via resolveSessionForWorkspace(cwd, true),
and retry the btwSession call once. Both btwWithRetry and
resolveSessionForWorkspace are stored in refs to avoid useEffect
dependency chain aborts.

* fix(web-shell): base PR generation on branch diff, not working tree

PR title/body generation now fetches the commit log between the
resolved base branch and HEAD (git log <base>..HEAD) plus any
uncommitted changes, instead of only the working tree diff. The
base branch is resolved inside the effect's promise chain (not
from state) to avoid stale values and dependency warnings. Also
adds range parameter support to fetchGitLog and workspaceGitLog.

* feat(web-shell): replace PR base branch select with searchable popover

The native <select> for the PR target branch is replaced with a
custom searchable popover (BranchSelect) that shows a search input
and a filtered branch list grouped by remote. The default selection
is the target repository's main branch (resolved via
getDefaultBranch). Supports filtering by typing in the search box.

* fix(web-shell): show full remote ref in branch select (origin/main)

Branch select now stores and displays full remote refs like
origin/main instead of stripped names. getDefaultBranch returns
the full ref (origin/main) instead of stripping the prefix.
When creating the PR, the remote prefix is stripped for the
gh pr create --base flag (origin/main → main).

* fix(web-shell): stop pointer propagation in branch picker list to prevent popover dismiss

Radix Popover's outside-click detection was incorrectly firing when
clicking section headers (Recent/Local/Remote/Tags) inside the popover
content, causing the popover to close immediately. Adding
onPointerDown stopPropagation on the list container prevents pointer
events from reaching Radix's document-level handlers.

* fix(web-shell): use onPointerDownOutside guard for branch picker popover

The previous stopPropagation approach failed because Radix Popover
uses capture-phase document listeners for outside-click detection,
which fire before bubble-phase stopPropagation. The correct fix is
onPointerDownOutside on PopoverContent: when Radix incorrectly fires
the outside handler for a click that is actually inside the content
(can happen with portal containers), we check contentRef.contains()
and preventDefault to keep the popover open.

* fix(web-shell): stop click propagation on branch picker popover content

Root cause: the ChatEditor composer container has onClick that calls
core.focus(), stealing focus from the popover. React synthetic events
bubble through the React tree (not DOM tree), so portaled popover
clicks reach the container handler. Radix then detects focus-outside
and dismisses the popover.

Fix: onClick stopPropagation on PopoverContent, matching the existing
pattern in GitModePopover and ToolbarPopover which already have this
fix with an explanatory comment.

* docs: add PR verification screenshots for branch picker feature

* fix(web-shell): mock useWorkspace in tests for BranchPickerPopover

BranchPickerPopover calls useWorkspace() which requires
DaemonWorkspaceProvider context. The existing WorkspaceSection and
ChatEditor tests didn't provide this context, causing 5 test failures.
Added vi.mock with importActual to preserve other exports while
providing a mock useWorkspace. Also updated the git chip click test
to reflect that clicking now opens the branch picker popover instead
of directly calling onOpenGitDiff.

* fix: harden git write paths against argument injection

Address review feedback on the web-shell git surface:

- Reject option/pathspec injection in git checkout ref and branch start
  point (isValidCheckoutRef), and terminate `git checkout` argv with `--`.
- Drop `git log` range values that start with `-` and terminate the argv
  with `--` so a range can never be reinterpreted as `--output=<file>`.
- Fix getDefaultBranch always falling back to origin/main: the promisified
  exec lacked `encoding: 'utf8'`, so stdout was a Buffer and .trim() threw.
- Parameterize ghErrorMessage so `gh pr create` timeouts name the right
  command and duration; sanitize workspace paths in PR-create errors.
- GitDialog: guard doCommit against double-submit (button + keyboard), and
  strip only a known remote prefix from the PR base so local branches with
  "/" are not mangled; use theme tokens for commit button/success colors.
- BranchPickerPopover: guard checkout/new-branch behind busyAction, reset
  inline-input text on reopen, and hide the commit action when unavailable.

Adds regression tests for the checkout/branch validation and the git log
range guard.

* fix(web-shell): address review feedback on branch picker PR (#7731)

- Fix Commit+Push error masking: split try/catch so push failure
  reports alongside the successful commit SHA
- Replace hardcoded screenshot path with captureScreenshot harness
- Replace silent if-isVisible skip with explicit assertion in visual test
- Add focus-visible style for search input accessibility
- Fix CSS specificity for active PR tab hover state
- Add viewChanges to actionsVisible search filter
- Wrap toggleSection in useCallback to avoid unnecessary re-renders
- Add windowsHide: true to getDefaultBranch subprocess
- Fix trailing slash handling in mockDaemon git action routing
- Add git methods to top-level client mock in tests
- Remove dead branchPicker.commitSuccess i18n key
- Remove dead .actionShortcut CSS class
- Show generation failure feedback in commit message placeholder

* fix(web-shell): address review feedback on branch picker PR (#7731)

- Add workspace trust checks to bound git branch routes
- Use workspace-scoped client in BranchPickerPopover (fixes wrong-workspace mutation)
- Add branch name validation and -- terminator to gitCreateBranch
- Filter refs/remotes/*/HEAD from branch listings
- Force LC_ALL=C for reflog parsing (non-English locale fix)
- Narrow 'could not resolve' error regex to avoid DNS false positives
- Add range validation to git log (reject path traversal)
- Fix commit+push error i18n (dedicated key instead of concatenation)
- Add i18n for BranchSelect component strings
- Fix commit tab ARIA attributes
- Add onBranchChanged callback to handlePush
- Add btw to mockDaemon isDaemonPath regex
- Add workspace_github_prs to visuals spec capabilities
- Add -- terminator regression test
- Remove docs/pr-assets/ from repo

* fix(web-shell): address review feedback on branch picker PR (#7731)

* fix(cli): reject dash-prefixed branch name with 400 in branch route (#7731)

* fix(web-shell): address review feedback on git branch picker (#7731)

Security:
- Clear GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILE from git
  subprocess env to prevent repository redirection
- Add strict mutation gate to all POST git branch routes
- Add generation guard to qualified write routes
- Fail closed on invalid ?cwd= in mutation routes (resolveContainedCwdOrFail)
- Reject wrong-typed startPoint, fetchOnly, rebase, and PR options with 400

Correctness:
- Filter remote symbolic refs (origin/HEAD) by %(symref) instead of /HEAD
  name suffix, preserving valid branches like feature/HEAD
- Add git rev-parse --git-dir probe so non-git dirs get 404 instead of
  empty available:true
- Push preserves existing upstream; only adds --set-upstream when unset,
  resolving the remote from branch config or the sole configured remote
- git commit -a replaced with git add -A + git commit so untracked files
  displayed in the UI are included
- Always pass --body to gh pr create to prevent interactive prompts
- getDefaultBranch returns null instead of fabricating origin/main
- Memoize workspaceByCwd client in BranchPickerPopover to fix infinite
  render loop
- Move sessionId to a ref in GitDialog effects to prevent self-abort
- Bound commit-message prompt to fit /btw 4096-char limit
- Mark all platforms as unverified in PR template (no fabricated )
- Guard PR auto-fill effect against wiping user edits on reconnect

Accessibility:
- Add tabIndex and onKeyDown to commit-mode tab span
- Add aria-label to BranchSelect trigger and search input

Cleanup:
- Remove dead CSS (.prInputSmall, .prSelect)
- Remove 9 unused i18n keys
- Add ^ to git log range validation regex
- Add busyAction guard to handlePush/handlePull
- Add unit tests for gitCommit, gitPull, and route input validation

* fix(web-shell): address review feedback on git branch picker PR (#7731)

- Change commit tab from <span> to <button> for keyboard accessibility
- Move setCommitMsg('') to success-only branches so the message is
  preserved when push fails after a successful commit
- Add mutate middleware and generationGuard to PR creation route,
  matching all other POST mutation routes
- Set genFailed when session resolution returns undefined so the user
  sees the failure indicator instead of a silent empty textarea
- Make PR number nullable when URL regex does not match instead of
  returning a misleading 0
- Add LC_ALL=C and LANG=C to gitEnv() so for-each-ref upstream track
  parsing is locale-independent
- Validate setUpstream and force as booleans in handlePush, matching
  the existing validation in handlePull
- Add missing workspaceCwd and available fields to test mocks
- Use stable data-web-shell-git-branch attribute in e2e selector

* fix(web-shell): address R5 review feedback on git branch picker PR (#7731)

- Classify git errors on stdout+stderr instead of err.message to fix
  false-positive no_upstream on every push failure and dead
  nothing_to_commit classifier
- Sanitize workspace paths and cap error message length in sendGitError
- Fix remote branch checkout to strip remote prefix so git DWIM creates
  a local tracking branch instead of detaching HEAD
- Restore keyboard accessibility on composer branch chip (span → button)
- Trim startPoint in handleCreateBranch before forwarding to git
- Return bare branch name from getDefaultBranch (strip remote prefix)
- Fix i18n shortcut hint to show ⌘/Ctrl+Enter for cross-platform
- Update aria-label to reflect git management menu, not just changes
- Add available: true to mockDaemon gitDiff default payload
- Add regression tests: upstream preservation, sole remote resolution,
  strengthened fetch-only with divergent remote commit
- Add aria-expanded assertion to sidebar picker test

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

* fix(web-shell): address R6 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R7 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R8 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R9 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R10 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R11 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R12 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R13 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R14 review feedback on git branch picker PR (#7731)

* fix(web-shell): address R15 review feedback on git branch picker PR (#7731)

- Validate localName derived from remote-tracking ref to prevent option
  injection (e.g. origin/-f → git checkout -f)
- Add gitCwd prop to BranchPickerPopover and pass it to all git SDK calls
  so worktree sessions target the correct directory
- Add symlink-escape and non-existent-path tests for resolveContainedCwdOrFail
- Pin initial branch name in makeRepo() with git init -b master
- Add gitPull merge and rebase integration tests

* fix(web-shell): address git branch picker review feedback (#7731)

* fix(web-shell): address R6 review feedback on git branch picker PR (#7731)

* fix(web-shell): address review feedback on git branch picker PR (#7731)

- Strip GIT_CONFIG_GLOBAL/SYSTEM/NOSYSTEM in gitEnv to prevent
  inherited config redirection (consistent with extension/github.ts)
- Pass gitCwd to workspaceGitBranches in GitDialog loadPrBranches
  so worktree sessions fetch branches from the correct repository
- Add aria-expanded to collapsible branch section headers
- Add happy-path tests for PR create (201) and default-branch (200)
  routes, including the null fallback to origin/main

* fix(cli): add sendGenerationClosedError to POST routes and cover untested branches (#7731)

* fix(web-shell): address review feedback on branch picker and PR creation (#7731)

- Refresh branch list after push/pull to avoid stale ahead/behind counts
- Add pre-flight check for unpushed branches before PR creation
- Fix base branch prefix stripping when branch list is unavailable
- Cap PR body file list at MAX_SUMMARY_CHARS to bound model prompt size
- Add qualified route tests: trust guard, input validation, cwd containment

* fix(web-shell): hoist MAX_SUMMARY_CHARS to module scope for PR body generation (#7731)

* fix(web-shell): address review feedback for git branch picker (#7731)

- Hoist onOpenCommit to useCallback to fix App.test.tsx prop stability test
- Keep commit tab visible after navigating away (startedInCommit ref)
- Add onClick handler to commit tab for navigation back to commit view
- Fix branch-prefix strip mangling local branch names containing '/'
- Update sessionIdRef after force-creating a stale session replacement
- Pin core.hooksPath in test makeRepo for reliable rollback tests
- Add test asserting --force-with-lease is used for force pushes

* fix(web-shell): target the worktree for sidebar commits and harden git actions (#7731)

Scope the sidebar commit dialog to the active session's worktree checkout
(matching the composer path) so linked-worktree sessions commit to the right
checkout, guard PR creation against a double-click race, and surface an error
when an invalid branch name is submitted. Adds focused coverage for the branch
picker action wiring and the git branch route validation paths.

* fix(web-shell): address review feedback for git branch picker (#7731)

---------

Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
2026-07-28 14:25:11 +00:00
易良
dc2f61d910
feat(channels): dispatch GitHub notifications by reason (#7826)
* feat(channels): dispatch GitHub notifications by reason

Route each GitHub notification by notification.reason into one of five
lanes, instead of dispatching every new comment regardless of trigger:

- mention: only dispatch comments that actually @ the bot (noise reduction)
- review_requested (PR): fetch PR meta via pulls.get and dispatch a
  review-specific prompt, even with no new comments
- assign: fetch issue meta and dispatch a triage-specific prompt
- author/comment: aggregate the window's new comments into one check-and-
  respond prompt
- other reasons: generic fallback (current behavior)

Add cursor dedup via dispatchedComments (by comment node_id) and
dispatchedNotifications (by notification id), surviving a
markNotificationsAsRead failure that leaves the cursor un-advanced.

Closes #7807

* fix(channels): mark review_requested/assign envelopes as mentioned

GroupGate defaults to requireMention: true, which silently drops
isMentioned:false envelopes as 'mention_required'. The review_requested
and assign lanes are explicit directed triggers — the bot was asked to
review or assigned — equivalent to a mention, so set isMentioned: true
so they pass the gate instead of being inert on the documented default
config.

Addresses review Critical on #7826.

* fix(channels): resolve github routing review comments

* fix(channels): dedupe github meta lane comments

* fix(channels): conditional assign framing for PR threads

The assign route already detected PR threads to use pulls.get, but the
trigger framing text always read 'assigned to this issue' even for PRs.
Make it conditional so PR assignments read 'assigned to this pull request'.

* fix(channels): dedup meta lane dispatch inputs

* fix(channels): simplify GitHub reason dispatch

* fix(channels): respect mention gate for github aggregate lane

* fix(channels): truncate aggregate comment bodies by code points

Match the code-point-aware truncation already used for meta-lane bodies
so a supplementary-plane emoji at the MAX_COMMENT_CHARS boundary is not
split into a lone surrogate.

* fix(channels): harden GitHub dispatch failures, event window, and framing (#7826)

- Classify deleted/transferred subjects (404/410) as terminal so a single
  dead notification is logged and skipped instead of wedging the batch's
  mark-read and cursor advance every poll.
- Widen the review_requested/assign event search to the newest ~100 events
  by merging the preceding page when the last page is partial, instead of
  inspecting only the last page (which can hold a single event).
- Move the aggregate lane's untrusted-data warning to the head of the prompt
  text so it precedes the comment text it describes (metadata is appended
  after text by ChannelBase).
- Add regression tests: permanent-failure two-poll advance, terminal 404
  no-retry, multi-page event search, prompt caps, and the no-actor guard.

* fix(channels): drop lastReadAt filter in findMetaTrigger, add review coverage (#7826)

* fix(github): keep aggregate and meta windows bounded

* fix(channels): apply windowSince lower bound in findMetaTrigger (#7826)

* fix(channels): bound retry wedge, compute aggregate isMentioned, fix pairing pre-filter (#7826)

* fix(github): record dispatch before handler

* fix(github): persist skipped notifications

* fix(github): close dispatch retry loss cases

* test(github): cover cursor trim and meta floor validation

* fix(github): simplify notification reason dispatch

* fix(github): preserve batched dispatch comments

* fix(github): restore direct event dedup

* fix(github): preserve directed mention context

* fix(github): keep review fixes scoped

* fix(github): preserve delayed direct triggers

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
2026-07-28 14:05:21 +00:00
qqqys
1ada1b1fcd
feat(web-shell): manage Channel pairing requests (#7909)
Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com>
2026-07-28 13:41:42 +00:00
callmeYe
63166bd544
feat(web-shell): honor voice hold mode (#7839)
* feat(web-shell): honor voice hold mode

* fix(web-shell): finalize held voice after connecting

* fix(web-shell): buffer voice while connecting

* test(web-shell): cover voice buffer overflow

* test(web-shell): add negative test for mouse click guard in hold mode (#7839)

* test(web-shell): add negative test for mouse click guard in hold mode (#7839)

* fix(web-shell): add recording-state pointercancel test and fix buffer error message (#7839)

* test(web-shell): add tap-mode pointer and non-primary button guard tests (#7839)

* fix(web-shell): clear start timeout in deferred-stop path (#7839)

---------

Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
2026-07-28 13:32:02 +00:00
Shaojin Wen
6fb63b73e1
fix(review): recover the resolved effort when --effort is not re-threaded (#7855)
* fix(review): recover the resolved effort when --effort is not re-threaded

The capture commands (capture-local, plan-diff, fetch-pr) record the review
effort as plan.effort, which every downstream consumer — roster, check-coverage,
compose-review — reads. They learn it from --effort <level>, and the skill asks
the orchestrator to pass the level parse-args resolved. But that is a value the
model must copy from one file into a flag, and it does not reliably happen: a
`/review --effort medium` local run had the orchestrator omit the flag, so
plan.effort was absent and the roster safe-expanded to the FULL set — the user
asked for the reduced medium roster and silently got every agent (6a/6b/6c
included).

Close the gap deterministically. A new resolveEffort() prefers an explicit
--effort, and otherwise reads the level parse-args already wrote to its
conventional report (.qwen/tmp/qwen-review-parse-args.json). When neither is
available it returns undefined and the roster fail-safes to the full set exactly
as before, so a missing report never reduces coverage, and a malformed level is
ignored rather than trusted.

* refactor(review): dedupe effort resolution per review feedback

- Share one EFFORT_LEVELS set: export it from parse-args and import it in
  effort.ts, so a new level cannot be added to one set but not the other.
- Collapse the three identical effort-spread IIFEs into planEffortField().
- Isolate CWD in plan-diff.test.ts (matching capture-local.test.ts) so
  resolveEffort's CWD-relative report read cannot pick up a stale file and
  fail the "omits effort" case.

* test(review): pin PARSE_ARGS_REPORT value and plan-diff effort fallback (#7855)

* test(review): dedupe seed helper, cover fetch-pr effort, trace resolution (#7855)

* fix(review): spread planEffortField last for consistent effort precedence (#7855)

---------

Co-authored-by: verify <verify@local>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
2026-07-28 13:15:36 +00:00
qqqys
20f9f08ce5
feat(channels): expose loop tools in daemon sessions (#7891)
* feat(channels): expose loop tools in daemon sessions

* fix(serve): preserve fast-path bundle boundary

---------

Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com>
2026-07-28 13:07:36 +00:00
Shaojin Wen
c7de1d7b7c
perf(ci): cut the E2E suite from ~40min to ~24min (#7798)
* perf(ci): shard the E2E suite and stop building it twice per job

A full E2E run took about 40 minutes, twice the median gap between merges on main, so the post-merge suite could never keep up with the merge rate.

Two thirds of that was avoidable work. Every job installed dependencies with the default `prepare` behaviour, which builds and bundles the whole workspace, and then ran explicit build and bundle steps that did it all a second time — about 3.5 minutes on Linux and 5 on macOS. The docker leg additionally let the sandbox script re-run install, build, bundle and pack on the host before building the image, none of which the image consumes: its Dockerfile rebuilds and packs inside its own builder stage.

The remaining cost is the test run itself, which is now split across shards: three for each Linux sandbox mode and two for macOS. Vitest assigns files to shards by path hash, so the long tail of slow sdk-typescript files spreads out rather than clustering in one shard; replaying the last green run's per-file timings through that algorithm puts the slowest shard at about 6 minutes against the 16 to 25 minutes each leg used to spend. The matrix no longer fails fast either, because a cancelled sibling shard would hide most of the suite and report an incomplete failure set.

Expected wall clock per run drops from about 40 minutes to about 24, with the sandbox image build — 13 minutes, now paid once per docker shard — as the largest remaining item and the obvious next target via Docker layer caching.

* fix(ci): set QWEN_SANDBOX=docker on the sandbox image build step (#7798)

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

* fix(ci): quote the sandbox env value so yamllint passes

The value added for the image build step was unquoted, which the repository's
quoted-strings rule rejects, and note why the variable is needed at all.

* fix(ci): stream docker build output in the sandbox image step (#7798)

* fix(ci): skip duplicate build in sandbox image npm ci (#7798)

The Dockerfile builder stage runs npm ci followed by explicit build and
bundle steps. npm ci triggers the prepare script, which builds and
bundles the whole workspace — duplicating the two steps that follow.
Set QWEN_SKIP_PREPARE=1 on the npm ci so prepare only generates
git-commit.ts, matching the same fix already applied to the host CI
install steps.

---------

Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
2026-07-28 12:56:34 +00:00
qwen-code-dev-bot
ce55360958
fix(ci): restore workspace ownership in cleanup to prevent EACCES (#7931)
The verify and tmux jobs chown the workspace to node:node for the
build step, but the end-of-job cleanup never restores ownership.
On self-hosted runners, the next job's checkout step (running as
the runner user) fails with EACCES when trying to delete node-owned
files in .qwen/ and node_modules/.

Add a chown back to the runner user (detected via stat on
$RUNNER_TEMP) at the end of both "Clean up runner workspace" steps.
Only runs when the runner UID is non-root (i.e. on self-hosted
runners where the runner user differs from root).

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>
2026-07-28 12:54:41 +00:00
ZevGit
ad1b5f3de1
fix(cli): default to virtualized terminal history (#5738)
* fix(cli): default to virtualized terminal history

* fix(cli): remove redundant alternate screen exit handler

* fix(cli): keep non-interactive output off VP mode

* fix(cli): stabilize VP tests in CI environments

* test(cli): resolve SDK daemon source in vitest

* fix(cli): normalize CI env checks for VP mode

* fix(cli): keep default VP mouse interactions enabled

* fix(cli): align VP mouse behavior with runtime state

* fix(cli): stabilize virtual viewport runtime state

* test(cli): cover virtual viewport fallbacks

* docs(cli): clarify virtual viewport requirements

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-28 12:51:55 +00:00
Shaojin Wen
a68c4cb28b
fix(web-shell): report intended workspace to host when starting a new chat (#7910)
* fix(web-shell): report intended workspace to host when starting a new chat

Clearing a session leaves connection.workspaceCwd pointing at the previous session's workspace. The onSessionIdChange notification read that stale value, so starting a new chat in workspace A routed the host back to the old workspace (e.g. one with a running task) and the composer showed the wrong workspace. With no active session, report the workspace picked for the next session instead.

* fix(web-shell): reuse activeWorkspaceCwd for the no-session host report (#7910)

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-07-28 12:15:42 +00:00
chinesepowered
b7c7872159
fix(core): count the per-server and per-tool always-allow outcomes as approvals (#7869)
`isApproveOutcome` is documented as the single source of truth for "the
user said yes", shared by the CLI scheduler and the ACP Session so the two
cannot drift. It listed every proceed_* outcome except
`proceed_always_server` and `proceed_always_tool`.

Those two are deprecated but live. web-shell's ToolApproval sends them,
and `background-tasks.ts`, `telemetry/tool-call-decision.ts`, the ACP
Session and `agent.ts` all treat them as approvals. This function was the
only place that did not.

Both consumers use it for the same thing: in AUTO mode, when a call fell
back to a manual prompt because denialTracking was armed, an approval
clears the counters so later calls return to classifier flow. A user who
approved with the "always allow for this server" or "for this tool" button
did not clear them, so the session kept prompting after the user had
granted the broadest permission of all.

The test now enumerates the proceed_* values from the enum instead of
hand-listing them -- the hand-written list is what drifted, while claiming
to cover "every proceed_* outcome".
2026-07-28 12:05:56 +00:00
chinesepowered
945ea1d032
fix(core): track quotes inside a command substitution in splitCommands (#7870)
Quote tracking was gated on `substitutionDepth === 0`, so quotes inside a
`$(...)` body were invisible to the parser. A `)` inside those quotes then
closed the substitution early, and the body's own closing quote -- now
seen at depth 0 -- flipped the parser into "in quote" state, which
swallowed every separator to the end of the line.

  splitCommands(`echo $(echo ')') ; rm -rf /tmp/pwned`)
    -> ["echo $(echo ')') ; rm -rf /tmp/pwned"]     one segment
  getCommandRoots(same)
    -> ["echo"]                                     rm is gone

The trailing command did not merely stay joined to the first, it vanished
from the roots. shell.ts uses these segments to decide which sub-commands
need confirmation, and to locate the git commit / gh pr create segment for
attribution, which silently no-ops on a mis-split.

Gating the closing paren on the surrounding quote state is not enough,
because `"$(...)"` puts a double quote around the whole substitution and
that quote belongs to the outer command. Save the enclosing quote state on
`$(` and restore it on the matching `)`, so each body is quoted
independently of its surroundings.

splitCommands had no direct unit tests; this adds them, including the
nesting and quoting shapes that must keep behaving as they do.
2026-07-28 12:05:07 +00:00
ComplexSimply
13059e0796
fix(core): short-circuit the flush wait when the output stream cannot flush (#7905)
* fix(core): short-circuit the flush wait when the output stream cannot flush

Review follow-up on the settle flush wait:

- A destroyed stream (autoDestroy after an earlier EIO/ENOSPC write
  error) or an already-finished one emits neither 'finish' nor 'error',
  and .end() on it is a silent no-op — the settle stalled for the full
  10s timeout with nothing left to flush, and an abortAll() landing in
  that window (/clear, shutdown) would mark the entry cancelled.
  Short-circuit before arming the timer. writableFinished, not
  writableEnded: the latter is already true mid-flush.

- Hoist clearTimeout into runOnce so the synchronous-throw path no
  longer leaves the timer armed to log a misleading flush-timeout
  warning for a shell that settled long ago.

- Drop the vacuous duplicate-'finish' assertion (once semantics already
  drained the handler; the guard is pinned by the 'error' emit and the
  timeout test's late finish) and cover both short-circuit states with
  deferred-stream tests asserting the immediate transition and that
  .end() is never called.

* test(core): pin the mid-flush wait and the throw-path timer disarm

Closes the two coverage gaps from the verification review on #7905 —
both mutations previously survived the full suite:

- guard mutated to include writableEnded (short-circuit mid-flush, the
  exact truncation the flush wait prevents) — killed by a deferred
  stream with { writableEnded: true, writableFinished: false }
  asserting the transition still waits for 'finish';
- hoisted clearTimeout dropped from runOnce — killed by a throwing
  end() plus fake timers asserting no flush-timeout warning fires
  after the timeout elapses.

---------

Co-authored-by: ComplexSimply <rudy.arrowsong@gmail.com>
2026-07-28 11:56:24 +00:00
Shaojin Wen
43c35e533b
fix(ci): keep the post-merge E2E signal on main alive (#7795)
* fix(ci): stop cancelling in-progress E2E runs on main

Merges land on main roughly every 18 minutes (median) while a full E2E run takes about 40, so cancelling the in-flight run on every push starved the suite: across the last 100 push runs, 67 were cancelled and only 25 ever reported a result. The nightly regression was the only reliable signal, and each cancelled run still burned roughly 10 minutes on three runners before dying.

Turning cancellation off for main hands the coalescing to GitHub's concurrency queue, which keeps at most one pending run per group and cancels the previously pending one. The queue therefore collapses to the newest tree on its own while the in-flight run always finishes, so every result covers the batch of commits merged since the last one — bisect that range when it goes red. Dev branches keep cancelling superseded runs, where only the latest push matters.

* fix(ci): dedupe main CI failure issues by failing test

The autofix issue for a red main was deduped on the commit SHA, so a standing failure opened a brand new issue on every merge: one broken E2E test on 2026-07-26 produced six duplicate issues in twelve hours, each pointing the autofix agent at an unrelated commit.

The failing tests are now identified from the logs of the failed jobs and used as the dedupe key, with one marker per test so a failure set that grows still matches the issue that already tracks part of it. Later commits hitting the same failure are appended to that issue as recurrences (bounded, newest first) instead of opening another one, and notes written by a human or the agent are preserved when the machine-owned trailer is refreshed. Runs with no identifiable test — an install or build break — keep the previous per-commit behaviour.

* fix(ci): keep the failure analysis out of the bot-PAT job

Identifying the failing tests means running a helper from the repository, and the workflow deliberately checked out nothing so that the job holding the bot PAT could never execute repository code — an invariant its own test enforces.

Rather than weaken it, the work is split: a read-only job checks out the tree, reads the failed run's logs, finds any issue that already tracks the failure and renders the title and body, handing both over as outputs. The job with the PAT keeps checking out nothing and only writes what it was given. The invariant test now asserts that separation — the PAT job runs no repository code and holds only issue write — instead of banning checkout everywhere in the file.

* fix(ci): harden main CI failure dedupe per review (#7795)

- Match the `[run <id>]` link text instead of the run URL when deduping
  recurrences: `/301` is a substring of `/3010`, so the URL match silently
  deleted an unrelated run's line.
- Rebuild the machine-owned "## Also failing" section from the live failure
  set on every merge so a test that has since been fixed drops out instead of
  being listed forever.
- Assert the analyze checkout is SHA-pinned with persist-credentials disabled
  and pin the failed-job log-download paths in the workflow test.
- Add an e2e workflow test guarding the cancel-in-progress expression that
  keeps in-progress runs on main from being cancelled.

* fix(ci): bound the issue body and randomize the heredoc delimiter (#7795)

* fix(ci): test the runCli --existing merge path (#7795)

* fix(ci): address review feedback on e2e signal PR (#7795)

- Assert the full cancel-in-progress expression including && so a
  mutation to || is caught by the e2e-workflow test
- Filter the capped-summary line from missingTests so it is not
  rendered as a fake bullet under Also failing

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
2026-07-28 11:54:52 +00:00
VitaliBabkin
0afb48b1ea
fix(safe-mode): preserve caller-supplied top-tier MCP servers (#7827)
* fix(safe-mode): preserve caller-supplied top-tier MCP servers

Safe mode is meant to distrust LOCAL/ambient state (settings.json,
extensions, project .mcp.json) so a user can isolate which local
customization is misbehaving. It was also unconditionally dropping
topTierMcpServers -- the caller-supplied servers from an ACP
session/new's mcpServers field or --mcp-config -- which are an
explicit, per-invocation argument, not ambient local state.

The real gate turned out to be in packages/core/src/config/config.ts's
Config.getMcpServers() (the accessor mcp-client-manager.ts actually
reads for discovery), not just the mcpServers assembly in
loadCliConfig -- fixed both so the raw field and the accessor agree.
Also guarded the hot-reload path (hot-reload.ts) with the same
bare/safe-mode check, so a live settings.json edit can't smuggle local
servers into an already-running bare/safe-mode session.

Fixes #7819

* docs: clarify safe-mode's MCP servers note distinguishes local vs caller-supplied

Per CONTRIBUTING.md guideline 5 (docs for user-facing changes).

* fix(safe-mode): apply allowedMcpServers to top-tier servers, skip pendingMcpServers I/O under safe mode

Address Copilot's review of PR #7827:

- getMcpServers() now runs the safe-mode top-tier map through the same
  allowedMcpServers filter as the non-safe-mode path -- safe mode is
  not an exemption from a session's own --allowed-mcp-server-names
  upper bound (High severity finding, confirmed real).

- Re-added safeMode to pendingMcpServers' skip condition in
  loadCliConfig. Functionally a no-op either way (top-tier servers are
  never gated, #4615), but skips getMcpApprovals' local file read
  entirely under safe mode instead of doing a no-op read -- safe mode
  shouldn't touch local/ambient state at all, not even harmlessly
  (Medium severity finding).

* fix(safe-mode): actually run MCP discovery for surviving top-tier servers

Config.getMcpServers() reporting a top-tier server as configured isn't
enough on its own -- something has to actually connect to it and
register its tools with the model. That's a separate gate in
initialize(): startMcpDiscoveryInBackground() was skipped outright
whenever isSafeMode() was true, written back when getMcpServers()
always returned {} under safe mode (so skipping discovery was a
harmless no-op). Left unpatched after the earlier fix, a caller-supplied
top-tier server survives getMcpServers() but is never actually
discovered/connected -- confirmed live against a real ACP session
before this commit: the agent reported the tool as "not configured"
even though Config.getMcpServers() already returned it.

Found by actually running the fix end-to-end against a live ACP
session (qwen --acp --safe-mode + a real stdio MCP fixture server)
instead of relying on unit tests of Config in isolation. After this
commit the same live session correctly discovers and calls the
caller-supplied tool.

Checks getMcpServers() (not topTierMcpServers directly) so the
allowedMcpServers filter still applies -- no discovery is kicked off
if the only top-tier server present is filtered out.

* fix(safe-mode): also run MCP discovery for surviving bare-mode top-tier servers

The discovery-kickoff gate in Config.initialize() special-cased safe
mode (skip only when there's nothing to discover) but left bare mode's
half of the same guard unconditional (!this.getBareMode()), even
though loadCliConfig feeds top-tier MCP servers into bare mode's
mcpServers assembly exactly the way it does safe mode's
topTierMcpServers field. A bare-mode session with a caller-supplied
server (qwen --bare --mcp-config, or ACP session/new under bare mode)
had that server reported as configured by getMcpServers() but never
actually connected/discovered — the same stranded-server regression
already fixed for safe mode in this PR, just the bare-mode twin of it.

Found by an automated review pass on PR #7827 after the safe-mode fix
had already landed. Added the same three-case regression coverage
(present / nothing supplied / filtered out by allowedMcpServers)
mirroring the existing safe-mode tests, using mcpServers (not
topTierMcpServers) since bare mode's "local sources dropped" guarantee
lives entirely in the CLI-layer assembly, not a core-level short-circuit.

* refactor(safe-mode): simplify the bare/safe discovery-gate condition

(!bare || has) && (!safe || has) reduces by distributivity to
!(bare || safe) || has, so factor the has-servers check into a single
hasMcpServers computed once, instead of calling getMcpServers() (which
allocates and filters) twice.

Suggested by an automated review pass on PR #7827, commit 25ddf8b.
No behavior change — same 23 discovery-gate tests pass unmodified.

* fix(safe-mode): stop reading settings.mcp.allowed/excluded under safe mode

allowedMcpServers/excludedMcpServers assembly in loadCliConfig() only
guarded the settings-sourced branch with `!bareMode`, missing `!safeMode`
— so a local settings.json mcp.allowed/excluded list (LOCAL/ambient state,
same category as settings.mcpServers itself, which safe mode already
drops) was still read under safe mode. Combined with getMcpServers()'s own
allowedMcpServers filter (added earlier in this PR for the
--allowed-mcp-server-names case), a settings.json mcp.allowed list
narrower than the caller's own top-tier servers would silently filter
them back out — defeating the guarantee this PR exists to provide, via
the filter's source rather than the mcpServers map directly.

The argv.allowedMcpServerNames branch is unaffected: that's an explicit
per-invocation argument, not local state, so it still applies under safe
mode same as topTierMcpServers itself.

Found by an automated review pass (doudouOUC, CHANGES_REQUESTED) on PR
#7827. Regression test confirmed red before the fix (session-supplied
server silently filtered out) and green after. Full targeted vitest run
(packages/cli config/: 1018 passed, same 3 pre-existing Windows-only
extension-file-watcher failures as before, unrelated), tsc --noEmit,
eslint, prettier --check all clean.

* fix(safe-mode): stop reading settings.mcp.allowed/excluded on hot-reload too

Same class of bug as the previous commit's loadCliConfig fix, found by an
automated review pass on the SAME PR: recomputeMcpGating (hot-reload.ts)
reads settings.merged.mcp.allowed/excluded unconditionally, with no
bare/safe guard of its own. registerMcpHotReload's existing bare/safe
guard only covered the servers map (`next`), not the admission lists
computed right after it — so a live settings.json edit narrowing
mcp.allowed during an already-running safe/bare session would flow
straight into setAllowedMcpServers and silently filter the caller's
top-tier server out of getMcpServers() mid-session. Same stranded-server
outcome as the boot-time bug, reached through the gating list's SOURCE
instead of the mcpServers map.

Fix: under bare/safe mode, skip recomputeMcpGating entirely and build the
gating directly from only the CLI --allowed-mcp-server-names bound
(explicit, per-invocation, not local state — same treatment as
topTierMcpServers itself); excluded/pending are irrelevant once nothing
but the never-gated top-tier servers can be present.

Regression tests (safe mode + bare mode) confirmed red before the fix
(setAllowedMcpServers called with the settings-sourced list) and green
after (called with the CLI bound, undefined here). Full targeted vitest
run (packages/cli config/: 1020 passed, same 3 pre-existing Windows-only
extension-file-watcher failures as before this PR touched anything,
unrelated), tsc --noEmit, eslint, prettier --check all clean.

* fix(safe-mode): stop reading settings.mcp.allowed/excluded on ACP reload too

Third instance of the same bug class found by an automated review pass on
this PR: reloadWorkspaceMcpDiscovery (packages/cli/src/acp-integration/
acpAgent.ts) — the ACP control-endpoint reload path (workspaceMcpReload),
distinct from registerMcpHotReload's settings-file-watcher path fixed in
the previous commit — called assembleMcpServers(settings.merged.mcpServers,
...) and recomputeMcpGating(settings, ...) unconditionally, per live Config
in liveConfigs, with no bare/safe guard. A workspaceMcpReload request
against an already-running safe/bare session would fold local
mcpServers/mcp.allowed/excluded back in, silently stranding or filtering
the caller's own top-tier server mid-session — same outcome as the two
prior fixes, reached through a third independent reload path.

Fix: per-config (liveConfigs holds a Set of potentially differently-moded
Configs — the base config, active session configs, and the discovery
config), skip assembleMcpServers/recomputeMcpGating under bare/safe mode
and build servers/gating directly from that config's own
getTopTierMcpServers()/getCliAllowedMcpServerNames() — same treatment as
the other two fixes.

Regression test (packages/cli/src/acp-integration/acpAgent.test.ts)
confirmed red before the fix (settings-sourced 'local' server leaked
into reinitializeMcpServers alongside the caller's 'probe') and green
after. Also added getBareMode/isSafeMode mocks (defaulting false) to the
two pre-existing Config-shaped mocks in this describe block that didn't
have them — reloadWorkspaceMcpDiscovery now calls these unconditionally
per config, which would otherwise throw "not a function" against any
mock missing them, even for a normal-mode test. Full targeted vitest run
(packages/cli/src/acp-integration/acpAgent.test.ts: 318 passed), tsc
--noEmit, eslint, prettier --check all clean.

* test(safe-mode): add bare-mode counterpart for the workspaceMcpReload guard

Suggested by an automated review pass on PR #7827: the previous commit's
regression test for reloadWorkspaceMcpDiscovery only exercised
isSafeMode: true, leaving the bare-mode half of
config.getBareMode() || config.isSafeMode() unverified at this layer —
unlike the hot-reload.ts tests, which already cover both modes for both
the servers map and the admission lists. A future change narrowing that
guard to isSafeMode() only would go undetected here.

Confirmed red before the fix (temporarily reverted acpAgent.ts to the
prior commit): settings-sourced 'local' leaked into reinitializeMcpServers
alongside the caller's 'probe', same as the safe-mode case. Green with
the fix restored. Full targeted vitest run (acp-integration/acpAgent.test.ts:
319 passed), tsc --noEmit, eslint, prettier --check all clean.
2026-07-28 11:29:53 +00:00
jinye
788e5cd3a8
feat(core): add ARMS session user ID (#7921)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-28 11:10:38 +00:00
qwen-code-dev-bot
f7fd831003
fix(cli): remove redundant 'Read file' prefix from @mention tool card (#7902)
* fix(cli): remove redundant "Read file" prefix from @mention tool card

The @mention file-read tool card set its description to
"Read file <name>", which duplicated the display name ("Read")
when rendered as "{displayName} {description}", producing
"Read Read file README.md".

Change the description to "@<name>" — matching the @mention
syntax the user typed, and consistent with how normal read_file
tool calls show just the filename in their description.

* test(cli): assert @mention tool card description format (#7902)

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
2026-07-28 11:00:12 +00:00
qwen-code-dev-bot
0be42b782d
fix(cli): add polling fallback for git branch name display (#7830)
* fix(cli): add polling fallback for git branch name display (#7828)

fs.watch on .git/logs/HEAD is unreliable on NFS, FUSE, Docker overlay,
and some Linux filesystems — events can be silently dropped with no
recovery path, leaving the footer branch name stale indefinitely.

Added a 5-second polling fallback in useGitBranchName that calls
resolveBranchName and updates the state only when the value changes.
The timer is unref'd so it doesn't keep the process alive. The
existing fs.watch mechanism is preserved for immediate updates when
it works correctly.

* test(cli): cover the git branch polling fallback (#7830)

* refactor(cli): use idiomatic timer.unref?.() in branch poller (#7830)

* fix(cli): order concurrent branch refreshes by generation (#7830)

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-28 10:58:31 +00:00
jinye
4703cc5432
fix(serve): Release managed session writer locks on shutdown (#7812)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
* fix(serve): release managed writer locks on shutdown

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

* fix(serve): address shutdown review feedback

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

* fix(serve): release writer locks after flush failures

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

* fix(serve): harden managed shutdown recovery

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-28 10:18:35 +00:00
Dragon
555389b4d4
perf(core): add early Anthropic cache breakpoint on the stable system prefix (#7912)
The Anthropic request previously used 3 of the 4 allowed cache_control
breakpoints: last tool, end of system, last user message. Because the
system prompt ends with volatile tails (git status, auto-memory), the
scope:'global' entry on the system end almost never hit across sessions
— only the tools breakpoint did.

Split the outgoing system prompt at the stable → context / volatile
layer boundary that GeminiClient already assembles: the client records
the gitStatus/autoMemory-free prefix on Config, and the converter emits
it as its own text block with an early breakpoint (scope:'global' when
enabled), demoting the system-end breakpoint to the per-session shape.
New sessions and in-session memory saves now re-bill only the small
volatile tail instead of the whole system prompt. The converter matches
the prefix via startsWith and fails open to the previous single-block
layout (subagent prompts, stale prefix), so the wire shape never
regresses. This fills all 4 breakpoints.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 10:18:20 +00:00
ChiGao
6f739c7a46
fix(cli): hide stale sticky todos from previous turns (#7900)
* fix(cli): hide sticky todo panel when a new turn starts

PR #7062 hid the sticky panel when streamingState is Idle, but the panel
resurfaces with stale todos from a previous turn as soon as the user sends
a new message (state → Responding). This is confusing — the user sees
in-progress indicators for work that already finished in an earlier turn.

Add a turn-boundary check in getStickyTodos: if a user message exists
after the todo snapshot in history, the snapshot belongs to a previous
turn and the sticky panel returns null.

Fixes #7061

* test(cli): pin sticky-todo turn boundary for local slash commands (#7061)

---------

Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
2026-07-28 10:12:14 +00:00
ChiGao
0bafd0db21
fix(core): auto-retry transient network errors during API calls (#7898)
* fix(core): retry on transient network errors in defaultShouldRetry and API-call predicate

The retry classification layer (classifyRetryError) already recognizes
transport errors (ECONNRESET, ETIMEDOUT, etc.) as retryable, but neither
defaultShouldRetry nor the inline shouldRetryOnError in geminiChat.ts
consulted the classification for transport codes. TCP-level errors carry
no HTTP status, so they fell through every predicate and propagated as
raw [API Error: terminated (cause: read ECONNRESET)].

Wire the existing classifyRetryError transport detection into both
retry decision points:

1. defaultShouldRetry in retry.ts — append a transport-kind check after
   the existing rate-limit / 5xx predicates, preserving all current
   behavior (including bounded retry for fail-fast quota 429s).
2. Inline shouldRetryOnError in geminiChat.ts makeApiCallAndProcessStream —
   add the same transport-kind check so the API-call-level retryWithBackoff
   wrapper covers network errors.

The stream-level transport retry (gated on !streamYieldedChunk) already
exists and is unchanged.

Closes #7831

* fix(core): classify SDK-wrapped transport errors nested in the cause chain (#7898)

---------

Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
2026-07-28 10:05:57 +00:00
ChiGao
7f99a1f298
fix(cli): patch ink to clear staticNode on indirect subtree removal (#7816)
When a component containing <Static> is unmounted (e.g. transcriptFreeze
toggling <App/> off), the reconciler removes the ancestor and
freeRecursive() frees the static node Yoga WASM memory. The existing
identity check (staticNode === removeNode) only catches direct removal
of <Static> itself -- removing an ancestor leaves a dangling staticNode
reference. The next render then calls getComputedWidth() on freed WASM
memory, crashing with RuntimeError: memory access out of bounds.

Add clearStaticNodeIfContained() which walks up the parent chain from
staticNode to detect ancestor removal, and null out removeNode.yogaNode
after cleanup so stale JS references short-circuit on ?.yogaNode.

Fixes #6820

Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
2026-07-28 10:05:30 +00:00
顾盼
9461aa860d
fix(core): bridge tool-result images for text-only models (#7484)
* fix(core): bridge tool-result images for text-only models

* test(vision-bridge): pin tool-result full-turn guards and surface bridge errors (#7484)

* test(cli): cover drain-item model override conflict rejection (#7484)

* test(cli): cover stop-hook full-turn model persistence

* fix(core): disclose tool image routing

* fix(cli): type restored vision notices

* test(cli): fix stop-hook vision fixture

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
2026-07-28 08:47:40 +00:00
chinesepowered
fceb75533a
fix(core): render a thought part's reasoning instead of the boolean flag (#7866)
`Part.thought` is a boolean flag and the reasoning lives in `part.text`.
`partToString` declared it as `thought?: string` in a local cast and
interpolated the flag, so every thought part rendered verbose as the
literal `[Thought: true]` and the reasoning was dropped.

The local cast was the only thing making that compile -- the SDK types it
as `thought?: boolean`, `createOpenAIReasoningThoughtPart` builds
`{ text, thought: true }`, and every other consumer reads the text and
tests the flag for truthiness. Drop the cast and follow the same shape.

Testing `!== undefined` also caught a part carrying `thought: false`,
which is an ordinary part: it rendered as `[Thought: false]` rather than
as its own text. Use truthiness, matching the rest of the codebase.

Two existing tests asserted the old output and are corrected. Both were
built from shapes the SDK never emits: `{ thought: 'thinking' }`, which
only type-checks through `as unknown as Part`, and a bare `{ thought:
true }` expected to print its own flag.
2026-07-28 08:18:14 +00:00
chinesepowered
2554d1e186
fix(core): apply maxDepth to flat-format memory imports (#7851)
* fix(core): apply maxDepth to flat-format memory imports

* fix(core): track import depth per file so a shallower route can re-expand

* docs(core): trim the historical narrative from the depth-guard comment

Per AGENTS.md comments default to none and exist to explain a non-obvious
why. The first paragraph does that -- it states which semantics the guard
matches. The account of what the old code did wrong belongs in the commit
message, where it already is, not inline where it can go stale.

* fix(core): name the file in the depth-limit warning, correct a stale comment

The summary comment still said the flat path deduplicates with a set; it
has used a depth-keyed map since the previous commit, and the whole point
of that map is that a shallower route may re-expand a file a set would
have dismissed.

memoryDiscovery processes memory files concurrently, so several chains can
hit the limit at once and every warning read identically. normalizedPath
is already in scope.

* fix(core): announce a re-expanded flat import only on its first visit

Letting a shallower route re-expand a file made onFileImported fire twice for
it: once from the truncated deep route and again from the shallow one, with a
different parentFilePath. The Set this replaced skipped the child outright, so
the callback ran once per file. Nothing downstream de-duplicates --
notifyInstructionsLoaded in memoryDiscovery forwards every call to the
consumer -- so one loaded file was reported as two.

The file itself is still emitted into the flat output once, so gate the
notification on the first visit to match. The test is the re-expansion fixture
with a callback attached: x.md was announced twice before this commit.

* fix(core): budget flat imports from the depth already spent

The flat path started its own counter at 0 and ignored importState.currentDepth,
so a caller entering with budget already spent got the whole limit over again
in flat mode while tree mode gave it only what was left. Now that flat enforces
the limit at all, the two counters have to share an origin.

Every caller today enters flat mode at depth 0 -- memoryDiscovery passes 0, and
the internal recursion at the tree branch is unreachable from flat -- so this is
a no-op in practice; it removes the discrepancy rather than any live symptom.

Test enters at currentDepth 2 of maxDepth 3 and asserts both formats stop at the
same file. Before this commit flat expanded [0,1,2] where tree gave [0].
2026-07-28 08:18:03 +00:00
chinesepowered
91233da409
fix(core): keep Draft 4 boolean exclusive bounds in toOpenAPI30 (#7782)
Step 3 rewrites a numeric exclusiveMinimum/exclusiveMaximum into the
Draft 4 boolean form, but step 6 then skipped both keys unconditionally.
A schema that already used the boolean form lost the flag, silently
relaxing "> 10" into ">= 10", and the function stopped being idempotent
- feeding its own output back in dropped the flag it had just written.

Skip the key only when the value is a number, the one form step 3
consumes. MCP servers declaring draft-04 send the boolean form directly,
so this reached the wire through the OpenAI and Anthropic converters.
2026-07-28 08:17:20 +00:00
chinesepowered
567a834477
fix(core): read the stash reflog from the common git dir (#7774)
* fix(core): read the stash reflog from the common git dir

* fix(core): harden the commondir read the same way the HEAD read is

`resolveCommonGitDir` reads `<gitDir>/commondir` with a plain `readFile`,
while `gitDirect.ts` reads that same file through `readFirstLineNoFollow`,
which opens with `O_NOFOLLOW | O_NONBLOCK` and bounds the read. The two
reads had different protections against the same hostile input.

That matters because `getGitWorkingTreeStatus` polls unattended: a
`commondir` named pipe planted in a cloned repository would hang the
`readFile` forever and pin a libuv thread-pool slot, and a symlink would
be followed out of the repository. The adjacent `countStashEntries`
already guards against exactly this hazard for the reflog it reads.

Move `readFirstLineNoFollow` to `gitUtils.ts` and point both callers at
it. It cannot be exported from `gitDirect.ts` because `gitDirect.ts`
already imports `gitDiff.ts` — that would close an import cycle.
`gitUtils.ts` imports nothing but node builtins, so it can be shared by
both.
2026-07-28 08:17:03 +00:00
jinye
b3873571aa
fix(daemon): harden Todo Stop Guard continuations (#7821)
* fix(daemon): harden Todo Stop Guard continuations

Linearize Guard continuation ownership across bridge consumers, preserve queued input across failure paths, and tighten session lifecycle and lineage handling.

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

* fix(daemon): yield to event loop in waitForActiveTurnsToSettle (#7821)

* fix(channels): discard retired ACP sessions

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

* fix(cli): preserve superseded continuation state

Preserve unsent continuation results across prompt supersession and cancellation, and keep closing sessions from restoring stale FIFO priority.

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-07-28 06:51:40 +00:00
jinye
05f854b146
fix(test): Restore first-output benchmark measurement validity and correct its artifact schema (#7820)
* fix(test): restore first-output benchmark measurement validity

Anchor the post-session dwell to SSE readiness so a slow connect cannot
silently reduce a dwell scenario to an immediate-prompt run, isolate the
runner in its own serial vitest config, decide the Phase 1 prototype gate
on the paired bootstrap CI instead of a bare difference of two P50s, and
normalize every invalid timing rather than only the first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(test): correct first-output benchmark artifact schema and simplify (#7825)

Drop the bundle git commit, which resolved HEAD of whatever repository
happened to contain the bundle directory rather than the revision it was
built from; the harness commit and bundle hash already record provenance
correctly. Rename the prompt-shape config field, which held a description
of the prompt rather than the prompt itself, and bump the artifact schema
for both field changes.

Also remove an unreachable AB/BA balance check, fold a duplicated success
predicate into one, parse the comparison-only dwell after the mode check
so single mode reports the accurate error, and document the two median
definitions, the compile-cache path lifetime, and the actual buffer
overflow and cold/warm attribution semantics.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* fix(test): correct copyright years to 2026 (#7820)

* fix(test): reuse metricForOrdinal in coldWarmProviderDeltas (#7820)

* fix(test): strengthen benchmark test fixtures and align config with Vitest defaults (#7820)

* test(integration): cover prototype-gate input validation guards (#7820)

* fix(integration): summarize sseReadyToPromptMs metric and clarify gate error (#7820)

* test(integration): pin prototype-gate artifact shape for empty deltas (#7820)

* fix(integration): fail loudly on missing SSE timestamp; document sseReadyAt (#7820)

Replace the non-null assertion on the dwell anchor with an explicit guard so a
future path that resolves SSE readiness without recording a timestamp fails as
harness_error instead of silently degrading into an immediate-prompt run that
still reports its configured dwell. Also define sseReadyAt in the timestamp
table and note that each metric's bootstrap seed is positional, so inserting or
reordering a metric shifts later seeds and makes artifacts incomparable.

* test(integration): cover findInvalidTimings in the fast CI suite (#7820)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: qwen-code-autofix[bot] <qwen-code-autofix[bot]@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-bot <qwen-code-bot@users.noreply.github.com>
2026-07-28 06:51:03 +00:00
qqqys
2db663bec8
feat(web-shell): add Channel configuration flows (#7893)
* feat(web-shell): add Channel configuration flows

* fix(web-shell): hide invalid secret clear action

---------

Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com>
2026-07-28 05:58:26 +00:00
qqqys
090b213ad8
feat(web-shell): add native workspace folder picker (#7849)
* feat(web-shell): add native workspace folder picker

* fix(serve): harden native directory picker and add coverage (#7849)

Add a 5-minute timeout to each native picker subprocess and to the
webui action so a dismissed dialog cannot leave an orphaned GUI process,
and distinguish a headless Linux "cannot open display" failure from a
deliberate zenity cancellation. Cover pickNativeDirectory's platform
branches, the route's 501/500 error paths, and the dialog's picker
failure path with focused tests.

* fix(serve): log directory picker failures to daemon stderr (#7849)

* fix(serve): abort directory picker on client disconnect and stagger timeouts (#7849)

* test(webui): add unit tests for pickWorkspaceDirectory action (#7849)

* fix(serve): set UTF-8 console encoding for PowerShell picker and treat timeout kills as cancels (#7849)

* fix(serve): abort directory picker on response close, not request close (#7849)

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Qwen Code CI <qwen-code-ci@users.noreply.github.com>
2026-07-28 05:58:13 +00:00