Commit graph

1286 commits

Author SHA1 Message Date
rockybot2026
732f4d8a29
feat(voice): support trusted private ASR base URLs (#8350)
* feat(voice): support trusted private ASR base URLs

* fix(voice): address private endpoint review findings

* test(voice): cover private endpoint edge cases

* test(voice): pin remaining endpoint edge cases

* fix(voice): address private endpoint review feedback

* fix(voice): clarify allowlist URL and normalize IPv6

* fix(voice): harden NAT64 address validation

* fix(voice): address managed endpoint review findings

* refactor(voice): extract shared IPv6 transition unwrap ladder (#8350)

Deduplicate the IPv6-transition unwrapping sequence (mapped, compatible,
NAT64, dotted-quad) that was repeated verbatim between isPrivateNetworkIp
and isAlwaysBlockedVoiceAddress on both CLI and Desktop surfaces. A single
unwrapIpv6TransitionStep helper now yields the next canonical address (or
'blocked' for unrecognized ::ffff: forms), and each predicate recurses
through it, preserving the exact re-check semantics at every unwrap level.

* test(voice): cover allowInsecureBaseUrl wiring through desktop default transports (#8350)

* fix(voice): add allowlist hint to private-network rejection error (#8350)

* fix(voice): reject always-blocked base URLs before offering the allowlist hint (#8350)

* fix(voice): resolve exact desktop voice provider before OAuth (#8350)

* fix(voice): address review feedback for trusted private base URLs (#8350)

* fix(voice): align desktop voice resolution with CLI semantics (#8350)

* fix(voice): scope desktop fail-closed resolution to policy-bearing entries (#8350)

* fix(voice): address round-8 review findings for trusted private base URLs (#8350)

Run the invasive process-global `mock.module('ws')` suite as
voice-ws-handler.isolated.ts so the desktop package's single-process
`bun test` run no longer leaks the fake socket into unrelated ws
consumers; the existing isolated loop runs it in its own process.

Shape-guard the desktop provider scan: non-object modelProviders
elements are skipped (falling through to OAuth instead of throwing a
raw TypeError), and non-string baseUrl/envKey/settings.env values on a
voice-model entry now surface the PROVIDER_ENTRY_REMEDY remediation
error instead of crashing.

Compute the DashScope-compatible /v1 rewrite before any allowlist
match in fromExactModelProvider so the stage-1 check, the remediation
messages, and the top-level recheck all compare the same final URL and
a single allowlist entry converges for split-horizon deployments.

Extend the CLI allowlist remediation messages to state which settings
scopes honor the entry, since serve mode never shows the interactive
workspace-strip warning. Thread providerProtocol through the CLI voice
model seams (createVoiceModelSource and the daemon buildModelsConfig)
so protocol-mapped custom provider groups resolve like the rest of the
CLI model surface, and document the remaining protocol-agnostic desktop
scan in the design doc. Correct the getHomeEnvFallback comment: it
adopts the narrower getHomeEnvFallbackVars candidate set on purpose.

Add multi-record DNS answer tests on both CLI and desktop net guards so
the records.some classification is pinned against the array shape
defaultLookupHost always produces in production.

* fix(voice): address round-9 review findings for trusted private base URLs (#8350)

* fix(voice): address round-10 review findings for trusted private base URLs (#8350)

* fix(voice): classify desktop voice duplicates before ambiguity check (#8350)

* fix(scripts): compare voice guard mirrors as parse trees (#8350)

---------

Co-authored-by: rockybot2026 <265985139+rockybot2026@users.noreply.github.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-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-08-06 14:04:57 +00:00
Shaojin Wen
f4cf4268f5
fix(core): cap a streaming response's total lifetime, slim the review fan-out launch (#8602)
* fix(core): cap a streaming response's total lifetime

The stream inactivity watchdog resets on every chunk, so a drip-fed
stream — a gateway trickling keep-alive chunks, or a model crawling
through an oversized single message — kept it alive forever while the
message never completed: in issue #8597's CI review runs that meant
2.5-4.5 hours of silence ending in the outer timeout's kill.

Add a per-request lifetime cap (default 15 min) that does not reset on
chunk arrival. Tripping it throws a retryable ETIMEDOUT, so the
existing transport-continuation recovery resumes a healthy generation
the cap happened to cut. Config field streamMaxLifetimeMs; env knob
QWEN_STREAM_MAX_LIFETIME_MS; 0 disables.

* fix(review): carry findings lists as digest-named files, not inline

agent-prompt folded the cumulative findings list into every printed
verify/reverse-audit block. On a 12-14-auditor round that made the
launch one 65-82 KB assistant message — the oversized single message
whose stream generation never completed in #8597 — and cost a 5-10
minute paged relay through the orchestrator's context before every
round.

The list now goes where the brief already goes: on disk, named by the
same findings digest that keys the record; the block carries a read_file
pointer. The delivery guarantee is unchanged — a launch that drops the
read matches no record — and the retirement scheduler's echo guard
reads the list back from the file the prompt names, failing toward
auditing when it is gone.

* fix: address review feedback on stream guards and findings files

* fix: address review feedback on findings delivery and stream guards

The delivery floor counted only the brief's read receipt, so an
instruction-skipping verifier could open its brief, skip the one
instructed read of the findings file its block points at, and clear
the gate having never seen the list it ruled on. The floor now
extracts the findings pointer from the recorded prompt and requires a
successful read of it — a new findings-unread delivery shape, with gap
texts for the verify, reverse-audit, and combined steps. A failed
findings-file write also speaks on stderr instead of silently pointing
a whole round at a missing file.

Stream guards: consult the lifetime deadline at the top of the loop,
not only when the timer wins the race — a pre-buffered chunk resolves
next() as a microtask and beats setTimeout(…, 0) every time. Merge the
two ETIMEDOUT bypass blocks into one rule. New tests pin the
idle-off + lifetime-on branch, the both-guards-0 disable (config and
env), and the buffered-source shape.

Also reword the prose sites that still described the inlined list.

* fix(review): name the findings file in the verify/reverse-audit briefs

The two briefs still told the agent the findings were "listed in the
message that launched you", but since the list moved behind a
read_file pointer they find only a pointer there. The agent reads its
brief first (the block mandates it), so the stale text sent it looking
for finding blocks in the launch message — the exact findings-unread
shape the new delivery floor gates on. Point both briefs at the
.findings.md file and the required read instead.

* fix(review): address round-3 review — empty-round brief, guard hardening

The b796708 brief fix stated unconditionally that the launch message
points at a findings file, but an early reverse-audit round on a clean
review names no file (the empty branch prints '## Nothing is confirmed
yet' with no pointer) — the auditors were being sent to read a path
their prompt does not contain. The reverse-audit brief now says the
file is named when there is a list, and that an early round has none.

Also from the round-3 review:
- writeFindingsFile's failure diagnostic now uses writeStderrLineSafe:
  the catch exists to keep the build alive, so it must not throw out of
  it on EPIPE (qwen … | head).
- The retirement echo-guard resolves the findings pointer from the
  CLI's own record (never the orchestrator's pasted copy), confined to
  the plan's record dir, memoized per round — an out-of-bounds or
  unreadable file degrades to the prompt, failing toward auditing.
- Document the delivery floor's deliberate weakening: it proves the
  findings file was opened, not paged to completion (coverage.ts), and
  drop the last stale 'record folds the findings in' comment.
- withStreamInactivityTimeout returns the source untouched when both
  guards are disabled, so the invariant survives a caller refactor
  (setTimeout(Infinity) would otherwise clamp to ~1ms).
- settings.md + the config comment now say the stream guards are
  OpenAI-compatible-only, and that the 15-minute cap bounds a stream
  whose idle timeout was raised above it (raise or disable the cap to
  keep a longer window).

* fix(review): address round-4 review — memo hygiene, read-visibility, naming

The retirement echo-guard memoized a failed read's per-record fallback
under the round's shared findings pointer, so one chunk's launch prompt
would serve as a sibling chunk's findings list. Memoize only successful
reads; a miss falls back to THIS record's own prompt, uncached.

The findings block now prints the list's line count, so an agent whose
read_file truncates can see it saw a fraction rather than the whole
confirmed list — a visibility aid the delivery floor (which proves the
file was opened, not paged) does not provide.

Also from the round-4 review: rename withStreamInactivityTimeout to
withStreamGuards (it now enforces two guards), correct the constants
comment (the cap is measured from the stream's first iteration, not
its first byte), and give the two stream-guard knobs a dedicated
settings.md entry instead of burying them in the timeout paragraph.

* fix(review): address round-5 review — deterministic record walk, accurate line count

The cross-contamination regression test only discriminated under one
readdir order: readRecordedPrompts walked the record dir in filesystem
(filename-hash) order, so which chunk's fallback poisoned the shared
memo depended on the walk. Sort the directory listing — a deterministic
walk removes that whole class of order-sensitivity from a module that
reasons per-record, not just from this test (a temporary revert of the
memo fix confirms the test now fails against the buggy shape).

findingsSection printed the line count of the TRIMMED findings body
while writeFindingsFile writes the untrimmed content, so a list with
leading/trailing blank lines got a label smaller than the file the
agent actually reads — precisely the under-reading the count exists to
make visible. Count the untrimmed content.

* fix(core,review): charge the lifetime cap on upstream-wait, not delivery time

Round-2's top-of-loop deadline check made the lifetime cap measure
end-to-end delivery time: a healthy upstream that finished and buffered
its chunks was cut for the CONSUMER's slowness (a paused IDE client, a
big render), and a stream whose terminal done resolved at the boundary
was converted into a retry. The cap is now charged on accumulated
upstream-wait — the time the loop is blocked in await it.next() — so a
buffered, already-complete stream always completes and only real
upstream latency counts; the drip-fed never-completing stream spends
exactly that time waiting, so #8597's shape is still caught.

Also from the round-6 review:
- Hoist the stream-guard error branch above the thinking-tag check: a
  drip-fed gateway cutting mid-<think> surfaced the guard's ETIMEDOUT
  as a PROTOCOL_TAG_LEAK and burned the tag-leak retry budget.
- The findings line count drops the trailing newline's empty segment,
  so a 12-line list is not advertised as 13.
- settings.md: the stream guards are env/config-only (no settings.json
  key); document that streamIdleTimeoutMs: 0 embedders now also need
  streamMaxLifetimeMs: 0 to fully opt out.
- constants: a functionCall already streamed (the tool-heavy common
  case) recovers as a visible classified error, not a continuation.
- coverage.ts: drop the stale 'four shapes' counts after Delivery grew
  a fifth; pipeline: dedupe the instanceof in the guard debug log; and
  correct the both-guards-off test's comment to pin the outcome, not
  the caller mechanism.

* fix(core,review): monotonic guard clock, findings-write fallback, read-only floor

Round-6 follow-ups the previous commit left open:

- The stream guards accounted on Date.now(): an NTP step forward (or a
  long sleep) killed a healthy generation on the next iteration, and a
  backward step silently disabled the lifetime cap — the hang #8597
  exists to bound. All guard accounting is now performance.now(); the
  setTimeout it races is monotonic too, so the two agree. Vitest fakes
  performance alongside the timers, and a new test A/B-verified against
  the wall-clock shape (it fails on Date.now() accounting).

- A failed findings-file write returned the path anyway, pointing a
  whole 12-14-agent round at a file that does not exist — every agent
  burned its round, then the delivery floor failed it. writeFindingsFile
  now returns null on failure and findingsSection falls back to inlining
  the list (the pre-#8597 shape): the recorded prompt carries the list,
  the delivery check compares it verbatim, and the build stays alive.

- The findings delivery floor matched the path in ANY successful tool
  call's serialized args, so a search_file_content or list_directory
  that merely named the file cleared it without reading a line. The
  transcript parser now records read_file calls apart, and the floor
  counts only those — a mention is not an open.

* fix(core,review): round-7 leftovers — upstream-wait wording, key helper, test mock

The round-6 error message and class doc still said 'total lifetime cap'
and promised the continuation recovery unconditionally, and the
wrap-site comment still read 'aborts at maxLifetimeMs from stream
start' — all three now describe the upstream-wait semantics the guard
actually implements, and the message names the wall clock separately so
the two numbers reconcile.

The round suffix baked into findings-role record keys and the findings
file name was derived independently in three sites (findingsFileFor,
runAllChunks, the single build) — a change to how a round is spelled
would update two of three and silently fork the artifacts; roundPartOf
spells it once.

The writeFindingsFile test's module mock replaced all of stdioHelpers
(three stubs, missing the other exports) and asserted mock.calls[0][0]
with no mock reset — it now spreads importOriginal, clears mocks in
beforeEach, and matches the stderr lines with stringContaining.

* fix(review,core): round-3 follow-ups — conditional findings briefs, JSDoc placement, log labels

The verify/reverse-audit briefs told the agent its findings live in the
.findings.md file unconditionally, on the exact path (writeFindingsFile
returning null -> findingsSection inlining the list) this PR added to
work WITHOUT one; both now say 'when the message points at a findings
file, read it; on the rare write-failure fallback the list is inlined
in the message, read it there.'

roundPartOf had been inserted between findingsFileFor's JSDoc and the
function, orphaning the comment that records the one-file-per-round
and null-means-inline contracts; the helper now sits above it.

The guard debug log labelled the wall clock 'streamLifetimeMs' beside
the cap; it is now 'wallClockMs' so the two numbers reconcile the same
way the error message does.

* fix(review): anchor the findings-pointer extraction to its emitted shape

findingsPointerOf matched the FIRST read_file(file_path="….findings.md")
anywhere in the recorded prompt. On the write-failure inline fallback the
findings list occupies exactly the position the pointer would sit in, and a
finding entry there can itself quote a read_file pointer of its own (a
finding about this pipeline, which the harness produces when it reviews this
repo). The loose match then extracted the quotation as the pointer and the
readers diverged: coverage demanded a read of a path no agent was told to
read (a spurious findings-unread on a run that is already degraded), and
retirement, worse, confined-and-read an earlier round's file and flipped a
just-filed finding to an echo, retiring a chunk that had just reported — the
one direction the module's header commits to never failing. A quoted pointer
inside a findings entry is indented or embedded in prose, so anchoring to a
standalone read_file line removes it; the happy-path pointer is alone on its
own line inside its fence. A test drives an inlined list containing a
pointer-shaped line (fails under the old loose regex).

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-08-06 13:27:30 +00:00
Shaojin Wen
44747cc27b
docs: add legacy code audit (/audit) design doc (#8397)
* docs: add legacy code audit (/audit) design doc

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

* docs: revise legacy audit design with round-2 replication evidence

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

* docs: note cross-file tracer cost and budget rule in legacy audit design

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

* docs: address review feedback on legacy audit design (#8397)

* docs: wire invariant triple, personas, and event detection into audit design (#8397)

* docs: address round-3 review feedback on legacy audit design (#8397)

* docs: address round-4 review feedback on legacy audit design (#8397)

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

* docs: address round-5 review feedback on legacy audit design (#8397)

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

* docs: address round-6 review feedback on legacy audit design (#8397)

* docs: address round-7 review feedback on legacy audit design (#8397)

* docs: address round-8 review feedback on legacy audit design (#8397)

* docs: address round-9 review feedback on legacy audit design (#8397)

* docs: address round-10 review feedback on legacy audit design (#8397)

* docs: address round-11 review feedback on legacy audit design (#8397)

* docs: address round-12 review feedback on legacy audit design (#8397)

* docs: address round-13 review feedback on legacy audit design (#8397)

* docs: address round-14 review feedback on legacy audit design (#8397)

* docs: address round-15 review feedback on legacy audit design (#8397)

* docs: address round-16 review feedback on legacy audit design (#8397)

* docs: address round-17 review feedback on legacy audit design (#8397)

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
2026-08-06 13:15:12 +00:00
jinye
2ad72fd5e9
fix(cli): Bound ACP textual tool-result payloads (#8450)
* fix(cli): Bound ACP textual tool-result payloads

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

* fix(cli): Harden ACP text projection budgets

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

* perf(cli): Cap multi-block ACP projection scans

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

* test(cli): Cover ACP projection boundary guards

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-05 15:10:53 +00:00
Shaojin Wen
477859bb3f
feat(channels): support local gh authentication (#8461)
* feat(channels): support local gh authentication

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

* test(channels): align registry catalog test and visuals with optional GitHub token (#8461)

* fix(channels): address review feedback for GitHub local gh auth (#8461)

Treat a blank replacement of an optional secret as a clear so an existing GitHub channel can no longer ship an empty or whitespace-only PAT to the daemon. Reuse the shared missing-field predicate in the editor's GitHub credential validation, wrap malformed baseUrl failures in an actionable channel error, and surface sanitized gh stderr in local authentication failures.

* fix(channels): address second-round review feedback for GitHub local gh auth (#8461)

Pin the whitespace-only token gate, the bounded gh stderr sanitization, and the required-secret blank-replacement guard with mutation-resistant tests. Log the authenticated account identity on channel connect so an out-of-band gh auth switch is visible to operators. Align test secret-source fixtures with the SDK union and complete the design doc's change footprint.

* fix(channels): address third-round review feedback for GitHub local gh auth (#8461)

* fix(channels): address fourth-round review feedback for GitHub local gh auth (#8461)

* fix(channels): address fifth-round review feedback for GitHub local gh auth (#8461)

* fix(channels): address sixth-round review feedback for GitHub local gh auth (#8461)

* fix(channels): address seventh-round review feedback for GitHub local gh auth (#8461)

* fix(channels): address eighth-round review feedback for GitHub local gh auth (#8461)

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.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-08-05 14:05:00 +00:00
Shaojin Wen
a123d0030a
ci(review): prepare evidence-image tooling for GitHub-triggered reviews (#8454)
* ci(review): prepare evidence-image tooling for GitHub-triggered reviews

Reviews triggered on GitHub cannot attach images today for three
reasons; this wires the two that live in the workflow:

- Install tmux and freeze (pinned, checksum-verified) before the
  review runs. Both are optional by contract — the evidence ladder
  degrades honestly without them (png -> ans-only -> refused, recorded
  in the capture manifest) — so the step never fails the review; it
  only decides which rung the runner can reach. tmux mirrors the
  tolerant install qwen-autofix.yml already uses; freeze falls back to
  ~/.local/bin when passwordless sudo is absent.

- Pass QWEN_REVIEW_ASSETS_REPO from a repository variable to the
  review step. Publishing stays OPT-IN by design: with the variable
  unset the env is empty and publish-assets refuses (parseAssetsRepo
  trims and rejects empty), so nothing changes until a maintainer sets
  the variable. When set, evidence images land on commit-pinned
  pr-assets/<pr>-review branches — already covered by the visuals
  cleanup workflow — pushed with the same CI_BOT_PAT the step uses.

The third reason is release lag: the capture producer (capture-tui,
#8388) has to merge and ship in a release before rendering claims can
generate images on CI at all. This change is inert until then.

* fix(ci): capture-tools step review fixes — enforced tolerance, version pin, cached fallback

R1-1: the never-fails contract is now enforced twice — continue-on-error
at the YAML level (the belt) and set +e with a trailing exit 0 inside
(the suspenders); under the runner's default bash -e several statements
(mktemp, install, sudo install with an empty path) could previously
abort the step and fail the review the comment promised never to fail.

R1-6: probe the VERSION, not just the binary — on a persistent
self-hosted runner an installed freeze made any FREEZE_VERSION/SHA
bump a silent no-op; the pin now forces a refresh when the cached
binary does not match.

Cached-fallback fix: put ~/.local/bin on PATH (and GITHUB_PATH) before
the probe — a sudo-less runner otherwise re-downloads the tarball on
every review run forever.

R1-3: the step comment says capture-tui is UPCOMING (#8388, not in the
released CLI) and names qwen review drive as today's tmux consumer, so
the step cannot be mistaken for stale dead weight and deleted from
under the follow-up.

R1-4: the retention comment scopes the cleanup-workflow claim to the
same-repository designation; a fork or scratch destination manages its
own retention (docs updated to match, plus a note documenting the
repository VARIABLE a maintainer sets to enable publishing).

R1-5: the step's real bash now runs in the workflow behavioural
harness under bash -e with stubbed sudo/apt/curl/sha256sum/tar/uname:
worst-runner and checksum-reject scenarios exit 0 installing nothing,
the no-sudo happy path pins the ~/.local/bin + GITHUB_PATH pairing,
and the version-pin probe is pinned from both sides (wrong version
re-downloads, matching version skips). Real freeze/sudo on a
developer machine are shadowed so the tests are deterministic and can
never install to /usr/local/bin.

Nit: both sudo guards now check sudo -n true.

* fix(ci): capture-tools step review fixes — step-owned tool dir, anchored probe, honest failures

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

* fix(ci): capture-tools test harness — shadow tmux, don't blank its PATH dir

The harness dropped every host PATH directory that ships a tmux so the
step's apt branch would depend on the scenario, not on the machine
hosting the suite. On GitHub-hosted ubuntu runners tmux lives in
/usr/bin, so the filter blanked /usr/bin wholesale — bash, grep, mkdir,
and tar included — and execFileSync('bash') died of ENOENT: all seven
capture-tools tests failed in the Test (ubuntu-latest Node 22.x) job
while passing on tmux-less dev machines.

Replace the directory-level drop with an entry-level shadow: each
tmux-bearing directory is mirrored (symlinks) into a scratch dir minus
the tmux entry, in place, preserving PATH order and the empty-entry
stripping the old filter did. Hosts without tmux take the map through
unchanged, and Windows (no tmux in its PATH, no symlink branch) keeps
its current behavior exactly.

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

* fix(ci): capture-tools test review fixes — faithful stubs, env shape pins, missing-branch scenarios

Round-3 review findings: the harness executed several of the step's
branches but asserted nothing about them, so probe-verified mutants
(dropped tmux guard, deleted warning/degradation messages, malformed or
missing FREEZE_VERSION/FREEZE_SHA256, wrong hash variable, dropped URL
`v` prefix or curl `-L`, severed tarball paths, broken regex boundary,
leaked mktemp dir) all shipped green.

- Make the curl/sha256sum/tar stubs model their real contracts: exact
  pinned URL, pinned checksum over a file curl actually wrote, existing
  -xzf operand
- Pin FREEZE_VERSION/FREEZE_SHA256 shape in captureToolsSource
- Pin the full curl flag set and the three-site tarball path agreement
- Assert the stale-renderer warning (fires on degraded re-download,
  silent on the happy path) and the tmux-unavailable message
- Pin TMPDIR and assert the mktemp cleanup leaves it empty
- Add the two missing scenarios: tmux-present skips apt, cached version
  extending the pin with a leading digit re-downloads

Verified by 13 mutation probes: every named mutant now turns the suite
red (13/13 killed), baseline 34/34 green.

* fix(ci): capture-tools step review fixes — hash-verified cache, per-run PATH promotion

* fix(ci): capture-tools step review fixes — verified-bytes-only installs, step timeout

Review findings on the capture-tools step:

- Drop the PATH-trust branch: a freeze already on PATH was accepted on
  its own --version and executed to probe it — exactly the self-report
  the FREEZE_BIN_SHA256 comment declares attacker-controllable, from
  dirs writable between jobs on both runner classes. The checksummed
  download always runs now; the cache makes it free after the first
  run.
- Guard $tools_bin in the download branch: with mktemp failing, the
  unguarded install resolved to /freeze — harmless unprivileged, but a
  root-in-container self-hosted runner writes it and reports success
  with nothing on PATH.
- Copy-then-verify the cache: install into the fresh per-run dir FIRST,
  verify THOSE bytes, delete both copies on mismatch — the verified
  bytes are the bytes later steps execute, closing the check-then-copy
  race for free. This makes the separate pre-verify block redundant;
  it is deleted.
- Add timeout-minutes: 5 — continue-on-error bounds failure, not
  duration, and a stalled `sudo apt-get update` mirror had no other
  bound under the 300-minute job cap.
- Report block: say the resolved freeze is likely broken when its
  --version produces nothing, instead of echoing a blank line and
  calling it stale; the mismatch wording is direction-neutral now.

Tests: replace the PATH-trust scenario with a planted-PATH one (marker
outside the scenario dir proves the plant never executes), add the
mktemp-failure scenario (the install stub succeeds like root would, so
the unguarded mutant is caught) and the promoted-dir 0700 assertion;
re-anchor the two digit-boundary tests on the report's warning. 41/41
green; both fix mutants verified killed.

* fix(ci): capture-tools review fixes — stale-dir cleanup, pinned guards

Address round-5 review:
- R5-1 (Critical): the per-run qwen-review-tools.* dir under RUNNER_TEMP
  was never removed; RUNNER_TEMP survives across jobs on the shared pool,
  so every review run accumulated one dir + one Go binary, unbounded.
  'Clean stale agent state' now removes stale dirs before the install
  step creates the current run's dir, matching the qwen-triage.yml
  convention. The harness comment claiming the dirs were runner-cleaned
  is corrected.
- R5-6: the cache re-verification rejection branch now logs why it
  deletes the cached binary instead of degrading silently.
- R5-7: bump-checklist note beside the freeze pins — the harness stubs
  key on the same env values, so a transposed hash pair must be caught
  against the real release artifacts at bump time.
- R5-2/R5-3/R5-4/R5-5: four unpinned step properties now pinned (the
  if: guard, the sudo -n probe flag, install-after-context ordering, and
  the cache branch's tools_bin guard via a new mktemp-fails scenario);
  six mutation probes confirm each pin kills its mutant.

* fix(ci): capture-tools review fixes — curl budget, swept scratch dir, wiring pins

* fix(ci): capture-tools review fixes — harness mutation pins, pin-pair self-check

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

* fix(ci): capture-tools review fixes — shadow-farm cleanup, backoff budget term

* fix(ci): capture-tools review fixes — report probes only installed freeze, age-gated sweep

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-08-05 13:16:56 +00:00
易良
c73b5ed887
ci: run Windows merge queue tests on ECS (#8386)
* ci: run Windows merge queue tests on ECS

* test(channels): skip POSIX mode assertion on Windows

* ci: expose Git Bash on Windows ECS runner

* ci: scope Windows ECS tuning to self-hosted and restore full test:ci

Review feedback on the Windows ECS routing: dropping test:scripts removed the only Windows execution of 9 Windows-only install-script tests, and the job-wide PowerShell default plus narrowed test command changed the kill-switch fallback away from the known-good hosted configuration.

Restore the full npm run test:ci on both paths (bash is available: pre-installed on hosted runners, exposed via the Git Bash PATH entry on ECS) and gate every ECS-specific adjustment on runner.environment: the PowerShell setup step (now also skip_ci-guarded), TEMP/TMP/LC_ALL env writes, and the Linux-style Node setup split that fails with an actionable error naming MAINTAINER_ECS_RUNNER_DISABLED. The windows-2022 fallback is byte-for-byte the pre-ECS job again.

* test: make Windows CI suites platform-aware

* ci: add stale-checkout guard to Windows ECS test job

* test(core): compare canonical directory identity

* ci: add fork guard and review follow-ups to Windows ECS job

* test(core): exercise real directory identity change

* test(core): wait for killed lease process exit

* test(scripts): avoid cmd echo trailing spaces

* test(scripts): use unambiguous cmd echo syntax

* test(cli): avoid sidecar I/O in truncation test

* test: fix Windows script-suite gaps and unify platform gating

- Fix missed trailing-space cmd stub in package-scripts.test.js so the
  'runs prepare steps in order' assertion passes on Windows.
- Add qwen-pr-review-workflow.test.js and pr-self-report-label.test.js to
  the win32 exclude list (both test Linux-only workflows and are not
  portable to Windows).
- Replace local itPosix/describeOnNonWindows consts with vitest's built-in
  it.skipIf/it.runIf/describe.skipIf, matching the codebase idiom.

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

* test(scripts): restore Windows workflow coverage

* test(scripts): re-exclude Windows-incompatible workflow tests on win32

Re-add pr-self-report-label.test.js and qwen-pr-review-workflow.test.js to
the win32 exclude list. Both fail on a Windows runner for reasons the code
still carries: qwen-pr-review-workflow.test.js calls execFileSync('mkdir'),
which has no executable to resolve there, and pr-self-report-label.test.js
joins PATH with ':', corrupting the ';'-separated Windows PATH so its gh
stub never resolves. Excluding them restores a green Windows gate; Linux CI
remains their authoritative coverage. Document the criterion inline.

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

* ci: extract checkout-head guard into composite action, pin Windows gate (#8386)

Address review round 2: move the stale-checkout guard shared by the four CI gates into .github/actions/verify-checkout-head so the copies cannot drift, pin the Windows gate kill-switch routing and guard wiring in the script tests, re-enable lint.test.js on Windows via separator normalization and a lazy linter setup in scripts/lint.js, unify the platform skips on it.skipIf(process.platform === 'win32'), and document the queued-run behavior of the ECS kill switch.

* ci: fail fast in Windows gate environment setup (#8386)

* ci: dedupe self-hosted runner steps into actions, pin gate mutations (#8386)

* fix(ci): checkout before repository-local actions in Windows gates (#8386)

* fix(ci): configure Windows runner before bash guard

* test(ci): pin remaining shared-action wiring in script tests (#8386)

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

* fix(ci): skip zip-dependent packaging tests when zip is missing (#8386)

* fix(ci): validate full Windows smoke path

* fix(ci): match Windows smoke shell to gate and drop dead runs-on guard (#8386)

* fix(ci): make SIGTERM escalation test Windows-aware and tighten pins (#8386)

The CDP acceptance test asserted a POSIX-only SIGKILL escalation, which
fails deterministically on Windows where kill('SIGTERM') terminates the
child directly — blocking the Windows merge-queue gate. Assert the
platform-appropriate signal instead.

Also address review suggestions: probe `unzip` alongside `zip`, pin the
integration_cli guard's missing step-level `if:`, stop getWorkflowStep
at unnamed steps, pin install-script.test.js out of the win32 excludes,
add the stale-checkout guard to windows-runner-smoke.yml, pin the
Node preflight warning branch and the guard reject path contiguously,
and extend the smoke shell-parity loop to the npm cache step.

* docs(ci): clarify Windows runner trust boundary

---------

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-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
2026-08-05 12:14:42 +00:00
顾盼
a5c637b749
feat(web-shell): add native Live Voice (#7859)
* feat(web-shell): add native Live Voice

* fix(web-shell): address review feedback for Live Voice PR (#7859)

- Quote all strings in electron-builder.yml to fix yamllint CI failure
- Gate discovery publish on liveVoiceEnabledAtBoot to avoid writing
  bearer token to disk when Live Voice is disabled (M1)
- Add child identity guard to CommandMonitor stdout/stderr handlers
  to prevent stale helper output from corrupting the new buffer (M4)
- Add exponential backoff to sent-completion delivery retry (M3)
- Skip broadcastState when setCallState/setTranscript value is
  unchanged to reduce per-audio-delta overhead (H1)
- Document sent-mode completion notification in module docstring (H2)
- Remove dead protocol/nonce aliases from readDiscoveryFile
- Fix single instance lock fall-through with process.exit(0)

* fix(cli): register realtime_voice in docs contract and env guard (#7859)

* fix(web-shell): address review feedback for Live Voice PR (#7859)

* fix(cli): discard orphaned isolated dir when parent restore fails (#7859)

* fix(web-shell): address review feedback for Live Voice PR (#7859)

* fix(serve): harden live turn recovery

* fix(desktop): restore Live Host native build

* fix(live): align native host and session isolation

* fix(acp): preserve live worker continuation lineage

* fix(live): classify provider close reasons

* fix(serve): discard unused recovered conversation dirs

* fix(live): isolate authorized realtime responses

* fix(live): preserve realtime response authority

* feat(web-shell): complete Live Voice onboarding

* fix(live): persist realtime-owned dialogue

* fix(live): preserve final speech while stopping

* Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)"

This reverts commit 7110bec6b034c702bca6e28e35b93c7f70e729cd.

* Revert "fix(cli): discard orphaned isolated dir when parent restore fails (#7859)"

This reverts commit 85165f1b2ddfaa311b8be91acdd76a6f388f6204.

* Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)"

This reverts commit 9199fa633e102bb8f24e4b216d322be4323eb3fc.

* Revert "fix(cli): register realtime_voice in docs contract and env guard (#7859)"

This reverts commit 6b6b1718352ef01a98a73976b5c7c4433fd14c35.

* Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)"

This reverts commit e083779105199d26de3afd8ad00719a08efe3099.

* revert(live): remove remaining takeover behavior

* revert(live): restore pre-rollback implementation

* test(cli): align Live diagnostics env guard

* test(release): cover Live Host publication

* fix(ci): re-sign Live Host package before verification

* fix(serve): scope sent completion notifications to Live

* fix(web-shell): preserve live setup errors

* fix(live): align realtime backend speech lifecycle

* ci(live): publish Live Host independently

* test(cli): mock Live speech bridge handler

* test(release): align Live Host workflow contract

* fix(live): address release and lifecycle review findings

* fix(live): release completed call tracking

---------

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 Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
2026-08-05 08:33:22 +00:00
顾盼
67d128715e
chore(cua-driver): sync upstream v0.17.0 (#8564) 2026-08-05 07:00:36 +00:00
jinye
3084326243
fix(webui): recover complete turns after live journal truncation (#8414)
* fix(webui): recover live journal turns

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

* fix(webui): address PR review feedback (#8414)

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

* fix(webui): refresh repair pagination anchor (#8414)

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

* fix(webui): restore evicted repair side effects

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-05 02:50:58 +00:00
Shaojin Wen
1a2bb10298
fix(review): admit evidence images by content, not by name — magic-byte sniffing (#8459)
* fix(review): admit evidence images by content, not by name — magic-byte sniffing

The publish-assets allowlist was extension-based, and an extension is
a claim anyone can make: combined with a prompt-injected review run,
whatever could name a file evidence.png could host up to the size cap
of arbitrary bytes at a github.com URL through the evidence push
(raised in #8454's review as an enablement consideration).

sniffImageFormat reads the four admitted signatures (PNG, JPEG,
GIF87a/89a, RIFF+WEBP — RIFF alone is not enough, AVI and WAV share
the container prefix) and validateAssetContent rules the content
against the format the extension claims, fail-closed: an unrecognized
signature refuses even when the extension is allowed. publish-assets
applies the ruling to every file's first bytes before anything is
uploaded — all-or-nothing, same refusal contract as the other gates.

Pinned: the four signatures, truncated/empty headers, the RIFF/AVI
near-miss, extension-format mismatch, fail-closed unknown extensions,
and end-to-end: a shell script named evidence.png refuses with exit 3
and nothing pushed.

* fix(ci): re-pin review timeout tests to vars externalization (#8459)

* fix(review): single-source the asset allowlist and pin sniff depth (#8459)

Address round-1 review suggestions:

- ASSET_EXTENSIONS now derives from EXTENSION_FORMAT, so admitting a
  format is a one-place change the batch gate and the content gate
  cannot drift on; the duplicated extension extraction moves into one
  claimedExtension helper shared by both gates.
- Pin what the mutation probes showed unpinned: the GIF87a branch
  (sniff + admission), uppercase extensions at the content gate, the
  refusal message direction, the full depth of every signature
  (near-miss negatives), and the publish-time 16-byte slice end to end
  (a WEBP publishes through runPublishAssets; verified the test fails
  when the slice shrinks to 8).

* fix(review): pin every sniff check and guard the allowlist lookup (#8459)

* fix(review): pin every sniff byte and the two-gate format coupling (#8459)

* fix(review): align the two-gates comment with the pin that enforces it (#8459)

* fix(review): sharpen evidence-gate diagnostics and pins (#8459)

- Content refusals now name the JSON-quoted full path, so two same-named
  files from different directories are tellable apart (the sibling
  read-error refusal already spent the path).
- Export ASSET_HEADER_BYTES from the lib that owns the sniff depth; the
  publish call site uses it and the two-gates pin slices canonical headers
  to it, so a future longer signature fails the pin, not real publishes.
- One shared refusal builder keeps the allowlist message identical in
  validateAssetFile and validateAssetContent.
- Narrow the sniffImageFormat threat model to what magic bytes buy: binds
  the claimed type to the leading bytes, does not stop prefixed payloads.
- One-byte-off matrix becomes a labeled it.each table (failures name the
  exact corrupted byte); imports re-alphabetized.

* test(review): pin the shared extension refusal across both asset gates (#8459)

* test(review): pin lastIndexOf extension parsing for multi-dot asset names (#8459)

* fix(review): admit WEBP by its fourcc and name refused files once (#8459)

---------

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-08-05 02:23:25 +00:00
jinye
32e2741577
perf(core): clear tool results to a low watermark to preserve prompt cache (#8464)
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 / channel-plugin E2E (nightly) (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
* perf(core): clear tool results to a low watermark to preserve prompt cache

Size-triggered microcompaction now clears oldest compactable tool results down to half the threshold instead of stopping just below it, so the conversation prefix stays stable between clearings and provider prompt caches keep matching. The recent-result budget now protects committed results only; pending results no longer consume protection slots but stay counted, uncleared, and live for file-read-cache resolution. Adds the watermark to cleanup metadata and the debug log.

Fixes #8463

* fix(core): harden size-cleanup protection against zero-char and pending refs

Review follow-up for the low-watermark change: keepRecent now selects from committed results that are actually clearable (positive, successful, uncleared output), so trailing errors, prior placeholders, and empty outputs no longer absorb protection slots. Pending refs are dropped from the keep set entirely — a pending read may be a cache-hit placeholder rather than file bytes, so it must not suppress eviction reporting; over-disarming only costs a redundant re-read. Adds regression tests for both plus the protected-saturation consecutive-trigger corner.

* qwen: address PR review feedback (#8464)

Pin the (soft-exceeded) log marker with the one-line assertion suggested by the sandboxed verification report (finding S-1): the all-protected overage test now asserts 'target 250000 (soft-exceeded)', killing the surviving mutant M4.

* qwen: address PR review feedback (#8464)

* qwen: address PR review feedback (#8464)

* qwen: address PR review feedback (#8464)

Two P1 context-integrity fixes from review: (1) media-only tool results (image/PDF reads with empty text output and bytes on functionResponse.parts) stay in the idle-path keepRecent candidates instead of being dropped by the zero-char filter; (2) only write_file results vouch for file residency in kept-path accounting — edit calls carry just old/new snippets while still setting the cache's sticky full-read flags, so a kept edit can no longer suppress eviction reporting after the full read is blanked. Regression tests for both.

* qwen: address PR review feedback (#8464)

Pin the absence of the (soft-exceeded) marker at the exact watermark boundary: clearing that lands the virtual total exactly on the watermark must not be flagged. Kills the >= and always-true mutants of the marker condition that previously survived the suite.

* qwen: address PR review feedback (#8464)

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-08-04 19:00:11 +00:00
jinye
2bd5c94111
fix(serve): detect lineEnding across the file, not the returned slice (#8383)
* fix(serve): detect lineEnding across the file, not the returned slice

`readText` reported `meta.lineEnding` from the slice it was about to return.
A slice holding a single CRLF line arrives as text ending in '\r' — the '\n'
was consumed as that line's terminator — so detecting on it answers 'lf'. Page
one of a cursor sequence then disagreed with page two about the same file, and
a client that trusts the first page would rewrite CRLF content as LF.

The truncation branch re-detected on the truncated slice for the same reason
and had the same flaw.

Detect on the whole decoded file once, which is what the field is describing.

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

* test(serve): guard byte-truncated reads against slice-based lineEnding re-detection

* fix(core): report crlf on cursor pages resuming after a CRLF terminator (#8383)

* fix(core): count a skipped CRLF terminator on byte-truncated pages (#8383)

When a window's first line exceeds both the read-chunk size and
maxOutputBytes, the byte cut fires before the line's terminator is decoded,
and the re-snap then walks over that terminator without reading it. The next
page seeds from the pair and reports 'crlf' while the cut page reported 'lf'
— adjacent pages of one file disagreeing, the exact symptom this PR removes.
Consume the same two-byte evidence after the re-snap so the pages agree.

Also qualify the design-doc agreement guarantee to files with uniform line
endings (mixed-ending files can still flip between pages), and pin the
seed's load-bearing placement with tests: it must run on the snapped offset,
and the minimum probe offset (startOffset == 2) is now covered.

* docs: correct the lineEnding spec for byte-cursor pages (#8383)

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

* docs: qualify the mixed-EOL verification bullet for byte-cursor pages (#8383)

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

* docs: name the uniform-file line-window-vs-cursor lineEnding split (#8383)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.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-08-04 16:26:54 +00:00
Dragon
bf9b47e5e3
fix(core): reuse prompt cache for multimodal compression (#8419)
* fix(core): reuse prompt cache for multimodal compression

* test(core): remove inert media modality fixture

* fix(core): guard compression cache-sharing window

* fix(core): harden compression cache-sharing gate

* fix(core): tighten compression cache preflight

* test(core): pin lazy compression slimming
2026-08-04 14:48:05 +00:00
ChiGao
5631f4b112
feat(serve): add a required external tool guard provider (#8125)
* feat(serve): add required external tool guard

* fix(serve): keep guard constants off fast path closure

* test(serve): cover guard startup options

* refactor(acp-bridge): centralize external tool guard validation and ack value (#8125)

* fix(core): align MCP reconnect timeout test with safe replay policy (#8125)

The reconnect-on-timeout test still built its mock tools without server
trust or tool annotations, which the safe replay change now requires
before automatically replaying a connection-loss failure. Update the
fixtures the same way the surrounding reconnect tests were updated,
keeping the test's original assertion that a timeout on a known
disconnected server goes through the reconnect path. Mirrors the same
alignment already landed on main.

* fix(cli): alias externalToolGuard subpath for vitest source resolution (#8125)

This PR added `@qwen-code/acp-bridge/externalToolGuard` imports to cli
serve/acp modules but not the vitest source alias every other acp-bridge
subpath carries. Without it, any vitest run whose acp-bridge dist is
stale or absent fails to resolve the import and the five serve test
files die at transform time. Add the alias following the documented
convention in the config so tests read the live source.

* fix(serve): reject non-ASCII external tool guard bearer tokens

A token outside the ASCII range passed construction but made the
handshake throw ERR_INVALID_CHAR when interpolated into the
Authorization header, blocking qwen serve startup in required mode
with an unexplained error. Enforce printable ASCII (0x21-0x7E) at
validation time so the configuration fails fast with a clear message.

---------

Co-authored-by: 秦奇 <gary.gq@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>
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>
2026-08-04 14:37:13 +00:00
Shaojin Wen
6e9ecc41e4
fix(core): resolve DashScope thinking-knob conflicts by family (#8488 round 2) (#8536)
Second review round on #8488:

- Honour an explicit extra_body enable_thinking: false on the qwen3.8-max
  family as reasoning_effort: 'none' instead of silently deleting it and
  re-enabling thinking
- Legacy qwen hybrids now drop the inert reasoning_effort override when it
  conflicts with a meaningful thinking_budget, keeping the knobs the model
  actually reads
- Family-gate the pipeline's enable_thinking tool_choice clause like the
  reasoning_effort clause: on non-qwen models sharing the endpoint the
  field is an opaque no-op (GLM reads thinking.enabled), and stripping
  forced tool selection there degraded their side queries
- The tier-native disable path emits reasoning_effort: 'none' — the knob
  the family reads — instead of enable_thinking: false; the
  required-thinking retry trigger recognises the new shape so runtime
  learning still fires
- Warn once per generator (not debug per request) when user extra_body
  knobs are dropped; hoist the wire-model family predicates to
  modalityDefaults.ts and share the provider's extra_body merge tail
- Tests for every behavior above (all load-bearing, verified by targeted
  mutation); docs attribute the vendor rejection to thinking_budget only
  and document the extra_body exceptions
2026-08-04 14:27:08 +00:00
易良
52e0d1b364
feat(web-shell): bind plan approval to its Todo revision (#8393)
* feat(web-shell): gate session workflow behind experimental setting

* feat(web-shell): bind plan approval to todo revision

* fix(cli): clear stale workflow revision on plan entry

* test(web-shell): pin revised workflow snapshot

* fix(cli): clear stale plan revisions on restore

* fix(cli): keep replayed history from rebinding plan revisions

History replay re-sends stale plan updates through Session.sendUpdate,
re-stamping activeTodoPlanRevision from finished plan cycles. Clear the
revision after every replay path (cold replayHistory and live non-bulk
loadSession) so a replayed snapshot can never bind a later exit_plan_mode
approval; reloaded sessions fall back to text-only approval until the next
live todo_write re-establishes the binding. Also drop the bulk-load restore
that could never be read before a plan-mode transition cleared it, and pin
the workflow gates and mode-entry clears with negative tests.

* test(web-shell): pin older plan revision in ChatPane approval test (#8393)

* test(cli): pin unbindable plan updates in approval revision test (#8393)

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

* fix(cli): clear Todo plan revision on history restore (#8393)

restoreHistory was the one history-resetting path that kept
activeTodoPlanRevision, so a restored snapshot could let a stale
revision bind the next exit_plan_mode approval. Clear it like the
sibling reset paths, pin the behavior with a test, and pin the
live-load clear ordering after the replayed updates.

* fix(cli): restore Todo stop guard clear on plan re-select (#8393)

The previous-mode guard added for the revision binding also skipped the
Todo Stop Guard trust clear on a redundant plan re-select; scope the
guard to the revision reset so every transition into plan clears the
stop guard as before. The replay-time revision clears now run in
finally blocks so a transport failure part-way through a replay cannot
leave a replayed binding on the live session, and the web-shell
exit-plan approval rule is unified in one predicate. Revision tests
assert through the observable qwenTodoApproval approval metadata
instead of the private field.

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-08-04 14:25:50 +00:00
Shaojin Wen
ac67de2e46
fix(review): stop the reverse-audit loop while there is still time to report (#8468)
* fix(review): stop the reverse-audit loop while there is still time to report

Measured on CI run #8368 (+1699 lines): the iterative reverse audit ran to
its 5-round cap, each round a per-chunk fan-out whose findings then went
back through verification, and the loop consumed 3.5 of the job's 4
budgeted hours. The outer GNU-timeout kill arrived while round 5's
findings were still being verified. The review died holding every
confirmed finding it had; nothing reached the pull request.

The loop's rounds are driven by the orchestrator, but every round begins
at the same place: agent-prompt building the round's prompts. So the
builder becomes the loop's clock. When the environment carries a review
deadline (QWEN_REVIEW_DEADLINE_EPOCH, exported per attempt by the review
workflow) and the remaining time is inside the reserve kept for the last
verification, compose-review and submission (default 60 minutes,
QWEN_REVIEW_DEADLINE_RESERVE_SECONDS to override), a reverse-audit round
is refused: a BUDGET line on stderr, exit code 4, no prompt built and no
record written. The message carries the exact unreviewedDimensions entry
to file, so the disclosure that caps the verdict is the CLI's text, and
Step 6 proceeds with the findings already confirmed.

Local runs have no deadline and are untouched. A malformed deadline fails
open — the outer kill still bounds the run, and a broken variable must
degrade to today's behaviour rather than wedge every budgeted review at
round 1. The verifier is deliberately not gated: the reserve exists so it
can run.

* fixup: scale the deadline reserve to the externally-chosen budget

The budget is not this workflow's to assume: it arrives from a repository
variable, a workflow input, or a /review --timeout=N comment. A fixed
60-minute reserve would consume most of a 70-minute budget and refuse the
audit loop outright on a 30-minute one. The workflow now passes a reserve
of a quarter of the attempt, floored at 10 minutes and capped at 60; the
CLI constant remains only the fallback for a caller that sets a deadline
without a reserve.

* review feedback: admit the round only if IT fits, and cap deterministically

Three findings from review, all taken:

1. The gate budgeted for the tail but not for the round it admits — the
   terminal round is by construction the one that starts closest to the
   boundary, so the killed-mid-verification failure survived one round
   wide. The gate now requires remaining >= round + reserve, where the
   round's cost is the previous round's, measured admission-to-admission
   from a stamp the builder writes (one per round; a same-round rebuild
   is not a round), falling back to a 30-minute constant for round 1,
   which starts with the most headroom.

2. The refusal was deterministic; the disclosure that caps the verdict
   was prose the orchestrator had to carry. The builder now records a
   budget-stop marker beside the prompt records and compose-review
   synthesizes the unreviewedDimensions entry from it — deduped against
   a relayed copy — so a run that drops the sentence still cannot
   approve past a truncated audit.

3. Exit code 4 is documented in the command's describe.

Also restores the Step 5 bullet the previous commit's edit displaced
(new findings merge into the cumulative list before the next round).

* review feedback: pin the budget gate's all-chunks refusal and ordering

Cover the two behaviours the review noted were only asserted on the bare
--findings form: an exhausted budget refuses the loop's real
--all-chunks round before ANY of the per-chunk records is written, and
a malformed call (--round 0) still gets its validation error first —
exit 4 is for a well-formed round the budget refuses, never a
replacement error. Also name what the code already does: reserve=0 is
the deliberate escape hatch (the gate shrinks to the round estimate
alone), and the workflow's 3600s cap mirrors DEFAULT_RESERVE_SECONDS.

* docs(review): describe the soft-deadline env vars for time-budgeted runs

The review noted the two new variables appeared in no user-facing doc;
the reserve in particular is an operator-facing knob. State what each
does, the fail-open posture, and how the refusal surfaces in the verdict.

* fix(cli): align budget-stop disclosure with the gate's refusal (#8468)

A round-1 budget refusal left no reverse-audit records, so the Step 4/5
floor reported the deliberate stop as a rogue/unlaunched audit with a
rebuild FIX the same gate deterministically rejects; the refusal's own
disclosure was swallowed by the caller-echo dedup. The floor now stands
down when the budget-stop marker exists, and compose-review renders the
disclosure structurally, bilingually, from the marker.

Also: `--role reverse-audit` requires `--round <k>` (an unlabeled
admission stamps an entry no estimate can attribute), the budget gate
runs after the plan/findings reads (a broken plan or unreadable findings
deserves its own error, and nothing is stamped ahead of a buildable
call), and the gate's admission boundary, measured-cost behaviour, and
the workflow env contract are pinned by tests.

* review: a budget stop excuses only the round it refused

The budget-stop suppression keyed on the marker's existence alone, so
every reverse-audit gap shape went silent once any round was refused —
including the shapes that describe rounds which RAN before the budget
hit. A hand-written round-1 launch is exactly as undelivered when round
3 later hits the budget, and suppressing its disclosure let 'stopped
before round 3' imply the rounds that did run were faithful.

Exactly one shape is by design under a marker: not-built — the refusal
writes no record, so an audit with no records IS the audit the gate
stopped, and its FIX (rebuild the round) would be refused by the same
gate. The suppression now names that shape and no other; a rewritten,
unlaunched or brief-unread round keeps its disclosure and its repair.

The new test pins the operative halves: the verdict stays capped, the
marker's disclosure posts, and the operator channel carries the
rewritten round's exact repair. (The posted body collapses same-subject
disclosures — both say 'reverse audit' — so the author sees the stop;
repairs are acted on from stderr, where the rewritten fix rides.)

* fix(review): fence budget state per run, and let gate errors beat budget stops

Address the round-2 review threads on the reverse-audit budget gate:

- Fence budget-rounds.json and budget-stop.json by the plan's own mtime.
  Every run rewrites the plan at its Step 1 capture, so records older than
  the plan belong to a previous run of the same PR: a run killed before
  cleanup no longer prices the next run's rounds off stale stamps (an
  hours-old stamp read as an hours-long round refused round 1 of a fresh
  budget) and no longer caps a later run's verdict on a stop that did not
  happen in it (R2-1, R2-2).
- Refuse a structurally unbuildable plan (no chunks[], duplicate or
  non-integer ids) with its own error ahead of the budget gate, so the
  same corruption gets the same diagnosis whatever the clock says, and no
  budget-stop marker is written over a corrupt plan (R2-5).
- Stamp a round admitted only after its build succeeds: a build that
  throws leaves no stamp, so the next round's cost is never measured from
  a build that produced nothing and floored to 600s (R2-6).
- Keep the budget entry's 'reverse audit' subject out of the caller-echo
  prefix filter: other reverse-audit scopes the orchestrator disclosed
  (a twice-whiffed chunk from the rounds that DID run) are no longer
  silently dropped in the marker's shadow; the marker's own relays stay
  deduped by the phrase splice (R2-7).
- Render --round unbracketed in the reverse-audit rebuild fix — the CLI
  refuses a round-less reverse-audit call, so the paste-and-run repair
  must not present the flag as optional (R2-14).
- Document the deliberate one-verification overlap between the measured
  round estimate and the tail reserve, at both definitions (R2-13).
- Test hardening, each assertion mutation-probed to fail its named
  mutant: a reshaped relay only the marker-phrase splice dedups (R2-8);
  the stamp's round label and the verifier's no-stamp invariant (R2-9);
  whole-line, unit-arithmetic and reserve-cap pins on the CI wiring
  contract (R2-10); the first-wins stamp survivor (R2-11); the reserve=0
  escape hatch (R2-12).

---------

Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-08-04 13:58:15 +00:00
Shaojin Wen
874e46d734
fix(core): harden Qwen 3.8 reasoning effort wire shape (#8488)
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 / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
* fix(core): harden Qwen 3.8 reasoning effort wire shape (#8472 follow-up)

Follow-up to #8472, addressing the post-merge review findings:

- Drop enable_thinking/thinking_budget after the extra_body merge whenever
  reasoning_effort ships: the Token Plan preset made qwen3.8-max-preview
  carry both thinking knobs, and DashScope rejects reasoning_effort
  combined with thinking_budget
- Family-gate the new tool_choice=required strip clause to qwen wire
  models: reasoning_effort is an opaque sampling override on non-qwen
  DashScope models, and dropping forced tool selection degraded their
  structured side queries
- Prefix-match the qwen3.8-max family so dated snapshots and -latest
  aliases receive the selected tier instead of silently collapsing to
  enable_thinking
- Log the tool_choice strip; restore the effort-config JSDoc and comment
  the request-level override copy

* fix(core): family-gate DashScope thinking-knob drop (#8488)

---------

Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
2026-08-04 12:42:02 +00:00
Dragon
48d37cdf70
docs: document headless Goal workflows (#8503) 2026-08-04 03:30:26 +00:00
易良
20b3087aca
feat(browser-ext): add alpha readiness diagnostics (#6739)
* feat(browser-ext): add alpha readiness diagnostics

* test(browser-ext): automate readiness verification

* fix(browser-ext): support current devtools adapter

* test(browser-ext): verify restored page after reconnect

* fix(browser-ext): harden release and acceptance checks

* test(browser-ext): cover onboarding transitions

* fix(browser-ext): harden alpha diagnostics

* test(cli): sync serve capabilities baseline

* feat(browser-ext): add alpha readiness diagnostics

* test(browser-ext): automate readiness verification

* fix(browser-ext): support current devtools adapter

* test(browser-ext): verify restored page after reconnect

* fix(browser-ext): harden release and acceptance checks

* test(browser-ext): cover onboarding transitions

* fix(browser-ext): finalize Chrome Web Store package

* fix(browser-ext): harden CDP diagnostics per review feedback (#6739)

* fix(browser-ext): harden CDP diagnostics per review feedback (#6739)

Distinguish the ACP child's idle placeholder (initialized: false,
discoveryState: 'not_started') from a genuinely empty server list so
the panel no longer shows a false "adapter is not connected" warning
before the first session or after the child is reaped.

Compare the tunnel endpoint's host+port against the daemon baseUrl to
detect cross-daemon shadowing (a chrome-devtools entry pointing at a
different daemon's /cdp was previously reported as connected).

Guard package-extension and symlink tests with
skipIf(process.platform === 'win32') so the Windows merge-queue gate
does not fail on missing zip.exe or privilege-dependent symlinkSync.

Also: destructure QwenCapabilityStatus lazily inside probeState so a
missing capability-status.js no longer throws before the welcome
screen renders; add the missing license header to manifest-version.js;
replace the leftover #welcome height:100vh with flex sizing; add
cross-reference comments for the shared /cdp path pattern.

Note: probeJson intentionally drops the .catch(() => ({})) fallback so
a 200 with a non-JSON body reads as unreachable; this also makes
/health stricter than before.

* fix(browser-ext): resolve CDP diagnostics review findings (#6739)

* fix(browser-ext): mirror nightly build number in manifest test oracle (#6739)

* fix(browser-ext): address alpha diagnostics review feedback (#6739)

- declare the semver dependency used by manifest-version.js so an isolated
  workspace install no longer relies on root hoisting
- make artifact-scan skip the root CLI bundle metafile with a warning when it
  is absent (it only exists after `cross-env DEV=true npm run bundle`), keeping
  the extension metafile required, so package-level test:release no longer fails
- throttle the side panel /workspace/mcp probe to every 5th tick and reuse the
  cached snapshot in between, avoiding a cross-process RPC on every 2s poll
- document the per-session CDP event fan-out and pin single-path event counts;
  note that Target.getDevToolsTarget is deliberately unsupported
- guard the nightly build-number git lookup and the zip end handler
- disclose the daemon-to-model-provider page-content flow in PRIVACY.md
- drop brittle source-substring panel tests and add coverage for a
  chrome-devtools server with no config args

* fix(browser-ext): improve acceptance diagnostics and honest phase naming (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(cli): stabilize flaky orphan-session transport tests (#6739)

Replace hardcoded setTimeout(40ms) + assertion with vi.waitFor() in the
session/new and session/load orphan tests. The 40ms budget is too tight
under CI parallelism, causing intermittent removeSession-not-called
failures. vi.waitFor polls until the assertion holds (default 1s timeout),
matching the pattern already used elsewhere in this file.

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(cli): restore PAGE_SESSION_ID forwarding for lazy-attach path (#6739)

The autoAttachActive gate on PAGE_SESSION_ID command forwarding broke
the cdp-ws lazy-attach path, which sends commands with PAGE_SESSION_ID
without a Target.setAutoAttach handshake. Revert the forwarding gate
to unconditional PAGE_SESSION_ID acceptance while keeping the gated
Target.attachedToTarget emission (the Critical fix).

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): make survivor tests load-bearing with log assertions (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): address review feedback on diagnostics PR (#6739)

* fix(browser-ext): resolve review findings on diagnostics tests (#6739)

- reject preview-range QWEN_CHROME_EXTENSION_BUILD_NUMBER values at the
  env var boundary with a message naming the variable, value, and range
- assert the package-extension symlink test observably ran main() instead
  of passing on equality alone when both runs fail identically
- add CLI-level tests proving explicit positional roots are scanned and a
  clean scan exits 0, covering paths the symlink-only tests skip on Windows

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

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.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>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-04 03:23:47 +00:00
Dragon
1c1ee23f93
feat(core): support Qwen 3.8 reasoning effort (#8472)
Some checks are pending
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 (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 - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (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
2026-08-03 19:45:11 +00:00
Shaojin Wen
7dfc554dff
feat(review): Add structured Web Shell review results (#8402)
* feat(review): add Web Shell review artifacts

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

* test(web-shell): add code review artifact visual scenario (#8402)

* fix(review): address Web Shell review artifact feedback (#8402)

* save-artifact: document why paths resolve against the daemon workspace
  root (QWEN_CODE_PROJECT_DIR) instead of cwd, and cover the relative-path
  form the skill documents with a test where the two roots differ.
* CLI/renderer contract: the renderer hand-duplicates the findings
  vocabulary and fails closed on unknown values, so name the renderer as a
  second consumer beside the CLI's lists and check in a contract fixture
  generated through the real pipeline (validateFindings -> buildReport ->
  save-artifact) that exercises every source, severity, confidence and
  outcome. Exporting the vocabulary through the SDK stays deferred: it is a
  public cross-package API change beyond this PR's seam.
* resolve-anchors now validates `line` exactly like `findings` does
  (positive safe integer); the two validators in one pipeline no longer
  disagree. Note: an in-flight `.qwen/tmp` findings file carrying `line: 0`
  fails where it previously did not.
* The renderer validates markdownReportPath (relative, no ".." segments,
  .md suffix) before it becomes a readWorkspaceFile call, resets the
  severity/confidence filters when switching artifacts, and surfaces
  heldByMeasurement so a nonzero Held count is attributable.
* save-artifact refuses low effort structurally (choices and library guard)
  instead of by prose, stats the Markdown report before reading it so a
  directory reports "not a file", and the component no longer shadows the
  DOM `document` global.
* The case-insensitive alias test now skips visibly on case-sensitive
  filesystems instead of passing vacuously.
* Comment the kept `turnOutputs.review` key and document the JSON
  companion in the user docs.

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

* fix(review): address second Web Shell review artifact feedback round (#8402)

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-08-03 16:08:13 +00:00
jinye
0cb109f513
fix(core): Avoid replaying unsafe MCP tool calls (#8387)
* fix(core): Avoid replaying unsafe MCP tool calls

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

* fix(core): Revalidate MCP replay after reconnect

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-03 11:04:38 +00:00
jinye
d1648b3af9
feat(telemetry): Track tool execution outcomes (#8180)
* feat(telemetry): track tool execution outcomes

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

* fix(telemetry): address execution-status review feedback (#8180)

- Update stale nonInteractiveToolExecutor expectations for executionStatus (red CI)
- Scope the cancelled span-status short-circuit to tool_call events so other
  cancelled events carrying an error keep ERROR status
- Record loop-detection skips as UNKNOWN, not EXECUTION_DENIED, keeping the
  denial metric accurate
- Assert execution_status on the resolved-with-error PostToolBatch path
- Raise tool-call observer-failure logging from warn to error
- Clarify subagent-projection exclusion and JSONL compatibility in design doc

* fix(core): address review findings 3-6 on tool execution status (#8180)

- recordToolExecutionMetrics now merges common attributes (session.id
  opt-in) like every other counter in metrics.ts
- Lift TOOL_FAILURE_KIND_ATTRIBUTE / TOOL_FAILURE_KIND_CANCELLED into
  telemetry/constants.ts so coreToolScheduler and session-tracing share
  one definition
- Add debugLogger to runToolTelemetrySink catch (was silent)
- Replace delete-based absence in withPostToolBatchStop with conditional
  spread

* fix(core): address remaining review findings on tool execution status (#8180)

- Pass the frozen executionStatus variable instead of the literal
  'success' in the post-hook-stop error response, keeping the frozen
  value the single source of truth (finding 4)
- Force-finalize the deferred PostToolBatch parent span in the abort
  drain, since that terminal path cancels the batch hook that otherwise
  owns the span; documents the invariant at the call site (finding 6)
- Comment the loop-detection guard so the permission-cancellation
  exclusion from invalid-param loop detection is explicit (finding 9)
- Rename the design doc to the dated docs/design convention and note
  the schedule()/handleConfirmationResponse() resolution contract
  change for embedders (finding 2, doc convention)

* docs(core): note schedule() resolution contract in tool execution status design (#8180)

Record the embedder-facing behavior change that schedule() and
handleConfirmationResponse() resolve with a terminal error call rather
than rejecting, so a failing tool no longer aborts its siblings.

* fix(telemetry): address review feedback for tool execution status (#8180)

- Document the new tool_call attributes (call_id, execution_status), the
  qwen-code.tool.execution.count metric, the tool.execution span attributes,
  and the tool.failure_kind=cancelled span field in telemetry.md.
- Pass ToolErrorType explicitly at loop-detection skip sites instead of
  inferring it from the skip message string, so copy edits cannot silently
  reclassify loop skips as approval denials.
- Simplify withPostToolBatchStop response construction (drop the
  destructure-and-reattach used to preserve a missing execution status).
- Add a debug breadcrumb when a PostToolBatch stop has no span to attach to,
  and a one-time warning when PostToolBatch hook detection fails open.
- Drop the try/catch wrapping the pure isTelemetrySdkInitialized getter.
- Clarify the design doc invalid-combination wording and note that the
  execution-failure SLI cannot be attributed to a specific tool.
- Add a regression test pinning that schedule() resolves (not rejects) when a
  tool execution throws.

* fix(telemetry): address round-7 review feedback for tool execution status (#8180)

* fix(telemetry): restore type-safety fallback for executionErrorType (#8180)

* fix(telemetry): align tool execution failure outcomes

Keep Core and ACP cancellation arbitration consistent, preserve structured post-processing errors, and restore QwenLogger MCP metadata privacy.

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

* fix(core): address review suggestions for tool execution status (#8180)

* test(core,cli): strengthen test-efficacy for tool execution status (#8180)

* fix(core): address review suggestions for tool execution status (#8180)

- Improve post-processing cancellation message to indicate the tool had
  already completed, preventing silent model redo of completed work
- Remove dead !isExecutionTimeout conjunct in Session.ts PostToolUse
  cancellation check (unreachable: timeout always sets toolResult.error)
- Replace construct-then-delete with destructuring in withPostToolBatchStop
- Move all failure-kind constants to telemetry/constants.ts so the full
  documented vocabulary lives in one place
- Re-export StructuredToolError from tool-error.ts instead of importing
  from the unrelated priorReadEnforcement module
- Add JSDoc to normalizeToolCallEvent documenting key-absent semantics
- Add ordering-safety comment to createParentAbortRace microtask guarantee
- Document endToolExecutionSpan not_started guard as defence-in-depth
- Document PostToolBatch span leak window in finalizeToolSpan
- Add design doc note about hand-placed cancellation check invariant
- Add test for unknown execution_status normalization path
- Revert unrelated generate-notices.js formatting change

* fix(core): address review feedback for tool execution status (#8180)

- Gate cancel message on executionThrew so the model sees 'User
  cancelled tool execution.' when execute() rejected under abort,
  reserving 'already completed' wording for post-processing cancels
- Move StructuredToolError into tool-error.ts to break the
  tool-error ↔ priorReadEnforcement module cycle
- Revert unrelated Prettier reformat in generate-notices.js

* test(core): pin both tool cancellation notices; extract them as constants

afd349ca gated the cancel message on executionThrew but left the two
wordings as bare literals at four sites and added no test. That is the
exact shape the bug had: it was introduced by editing one literal and
missing the others.

Extract TOOL_CANCELLED_{BEFORE,AFTER}_COMPLETION_MESSAGE so the four
sites cannot drift, and add regression tests for both paths — a tool
interrupted mid-flight (execute() rejected under abort) must report
"User cancelled tool execution.", while a cancel after execute()
returned must report that the output was discarded. The mid-flight test
fails against the pre-afd349ca behaviour.

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

* fix(core): keep MCP reconnect for a timeout on a dead transport

Classifying every `-32001` as EXECUTION_TIMEOUT skips
handleReconnectOnError, which previously recovered one real case: the
transport dies mid-request, the SDK request times out because no
response will ever arrive, and the server is already recorded
DISCONNECTED. That reconnected and retried; now it hard-fails and the
user has to retry by hand.

Divert back to the reconnect path only on positive evidence the
transport is dead. Note that getMCPServerStatus() reports DISCONNECTED
for servers it has never seen, so the guard checks for a *recorded*
DISCONNECTED — the naive comparison misroutes every timeout from a
server whose status was never registered, which broke four existing
timeout tests when tried.

A timeout on a healthy server is still EXECUTION_TIMEOUT: retrying it
after a reconnect would just double the wait. The client-side idle
timeout keeps classifying unconditionally; it is our own timer, not a
transport signal.

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

* fix(core): address blocking review feedback for tool execution status (#8180)

Two blocking items from the maintainer review:

1. Post-processing cancellations dropped persistedOutputFiles (and
   visionBridgeNotice) along with the model-visible output, orphaning
   files the tool had already spilled to disk. createCancelledResponse
   now carries both, and every cancelAfterPostProcessing site passes
   what it has; the settle-then-abort and hook-stop paths do the same.

2. A -32001 that lands while the parent signal is aborted is the SDK's
   abort rejection or a timeout that raced with a cancel; classifying
   it EXECUTION_TIMEOUT would count user cancels against the timeout
   SLI. isExecutionTimeoutFailure now defers to the abort in both
   catch blocks, regardless of which side settled the race first. The
   two tests that pinned the opposite timeout-wins ordering are
   updated to the abort-wins semantics the review asked for.

Co-Authored-By: Qwen Code <noreply@alibaba-inc.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: qwen-code-bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <253268222+qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Qwen Code <noreply@alibaba-inc.com>
2026-08-03 10:21:44 +00:00
Shaojin Wen
72bd3dccc2
ci: remove broken legacy scheduled PR triage workflow (#8434)
The Gemini-era scheduled PR triage workflow has been dead weight for a
long time:

- Its only business value — syncing labels from the linked issue to the
  PR — never fires: gh exports closingIssuesReferences as a flat array,
  so the script's '.closingIssuesReferences.nodes[0].number' jq path
  always errors, the error is swallowed by 2>/dev/null, and every PR
  falls into the "No linked issue found" branch. The latest production
  run logged 157 "No linked issue" hits and zero label syncs, despite
  many of those PRs having linked issues.
- LABELS_TO_REMOVE is computed but never applied, PRS_NEEDING_COMMENT is
  never appended to, and the prs_needing_comment job output has no
  consumer — the rest of the script is dead code.
- It burns 1+N API calls against every open PR every 15 minutes.
- The id-token: write permission is a leftover from the Gemini/GCP OIDC
  era; nothing in the bash script uses it.

Real PR triage lives in qwen-triage.yml. Remove the workflow and its
script, drop the stale docs section describing behavior it never had,
and pin the file into the legacy-workflow regression list.

Co-authored-by: verify <verify@local>
2026-08-03 10:20:42 +00:00
易良
89b5aa7a03
feat(desktop): bridge Electron users to Tauri updates (#8392)
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 / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (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
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
* feat(desktop): bridge Electron updates to Tauri

* test(desktop): cover parseArguments validation in electron bridge manifest (#8392)

* chore(desktop): address bridge review follow-ups

---------

Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
2026-08-03 04:56:14 +00:00
tomatotomata
2ae8cd9619
feat(memory): configure background agent turn limits (#8171)
* feat(memory): configure background agent turn limits

Signed-off-by: ahmadalguydi <ahmadalgaidy@hotmail.com>

* fix(memory): honor configured skill review turn limits

Signed-off-by: ahmadalguydi <ahmadalgaidy@hotmail.com>

* test(memory): cover planner turn limit sentinels

* fix(memory): apply turn limits to all agents

* test(memory): cover remaining turn limit feedback

---------

Signed-off-by: ahmadalguydi <ahmadalgaidy@hotmail.com>
2026-08-03 03:56:43 +00:00
易良
fa938bdfac
feat(web-shell): gate session workflow behind experimental setting (#8391) 2026-08-03 03:14:45 +00:00
Shaojin Wen
186812694c
feat(review): publish evidence images to a user-designated assets repo (#8351)
* feat(review): publish-assets — evidence images for PR review comments

GitHub's API cannot attach images to review comments (the web UI's
drag-and-drop upload has no API equivalent), so a review whose evidence is a
screenshot — a TUI rendering, a before/after comparison — had no way to show
it. New `qwen review publish-assets` hosts evidence images in a
user-designated repository and hands back URLs a comment can embed.

Grew from the maintainer's manual workflow (screenshots pushed to
`pr-assets/<PR>-verify` branches over HTTPS), and inherits the shape of the
skill's only other public write (`submit`) deliberately:

- Designated destination: writes only to QWEN_REVIEW_ASSETS_REPO, an
  owner/repo the user set by hand — the reviewed repo for maintainers, a fork
  or scratch repo otherwise (fork-vs-in-repo becomes a configuration
  difference, not two code paths). A separate variable from
  QWEN_REVIEW_SCRATCH_REPO on purpose: that contract forbids PR-derived
  content, and evidence screenshots are exactly that. Unset → exit 3.
- Authorised run: the same args-file re-parse and target binding as submit,
  now extracted to a shared lib/authorization.ts so the two gates cannot
  drift (the target-binding lesson lives in one place). Since an effective
  --comment forces high effort, low/medium runs can never publish.
- Images only, capped, all-or-nothing: extension allowlist (SVG excluded — a
  script container), per-file and per-batch size caps, one refused file
  refuses the batch before anything is pushed.
- Immutable references: files land on pr-assets/<pr>-review via the Contents
  API (HTTPS via gh; no clone, no SSH), content-hash-named so re-runs are
  idempotent, and every URL is pinned to the commit — a posted comment's
  evidence cannot be changed from under it. The web-host /raw/ URL form works
  unchanged on GitHub Enterprise.
- Auditable: a manifest names every file pushed and the landing commit,
  swept by cleanup with the other review artifacts.

The findings artifact gains per-finding `assetFiles` (local evidence paths)
and `assets` (published URLs); `publish-assets --findings/--findings-out`
publishes everything referenced and weaves the URLs back in, so the comment
builder reads the artifact rather than hand-carrying URLs.

What the command cannot check is stated in SKILL.md instead: image content.
Publish only evidence the review itself produced — never a capture of the
user's own terminal, which can hold an env dump in the scrollback.

Tests: 45 files / 1394 assertions — new suites for the assets naming and
validation rules and the command's gates (refusal without designation,
refusal without authorisation, target binding, branch creation, idempotent
re-run, batch refusal, findings weaving); submit's 42 pass unchanged on the
extracted gate.

* fix(review): publish-assets round-1 self-review — six findings

Round-1 review of this branch, walked with the angles the author-side pass
does not cover:

- submit.ts kept its parseReviewArgs import after the authorization
  extraction; vitest does not typecheck, `tsc --build` does, and CI's build
  leg failed on TS6133. (The whole first CI round's failures cascade from
  this one break.)
- ensureBranch %2F-encoded the slashed ref path; GitHub's documented form is
  literal slashes and %2F routes inconsistently across endpoints — a 404
  here reads as "branch missing" and turns every re-run into a 422 on the
  create. Ref paths are now literal (the branch name is built from a
  validated integer, so interpolation is safe); the contents `?ref=` query
  VALUE keeps its encoding, which is the correct position for it.
- The authorization gate bound URL-shaped `--comment` arguments against the
  ASSETS repo, refusing legitimately authorised runs whenever the assets repo
  is a fork rather than the reviewed repo. The shared gate's repo binding is
  now optional — submit still always binds it; publish-assets binds the PR
  number (and host) alone, with a new optional --reviewed-repo to restore
  the stronger binding when the orchestrator knows the reviewed repo.
- URLs were pinned to the last PUT response's commit.sha; on an
  identical-content update that field's shape is GitHub's to decide, not
  ours to assume. The head is now read from the branch ref after the
  uploads — one extra call for independence from the response shape.
- putContent's catch-all retried EVERY failure through the exists path,
  answering a 401 with a confusing secondary error from the sha lookup; the
  retry now fires only on the 422/needs-sha shape and rethrows the rest.
- --findings without --findings-out silently skipped the URL weaving; it
  now warns, and --findings-out implies --findings.

New tests: literal-ref assertion, non-exists rethrow, URL-shaped
authorisation without assets-repo binding, --reviewed-repo mismatch refusal.
45 files / 1399 assertions green; `tsc --build` clean.

* fix(review): publish-assets round-2 — empty-findings no-op, reviewed-repo hint

Round-2 findings on this branch:

- A findings artifact carrying no assetFiles is the ORDINARY case for most
  reviews, but publish-assets answered it with exit 3 — a refusal an
  orchestrator calling the command unconditionally on every posting run
  would read as a failure to repair. It is now a no-op (exit 0,
  {published:false, count:0}); a bare --files with nothing named keeps the
  exit-3 refusal, because there the emptiness IS the caller error.
- SKILL.md's example now names --reviewed-repo for URL-target reviews, so
  the stronger authorisation binding is used where the orchestrator knows
  the reviewed repo.

44 files / 1387 assertions green; tsc --build clean.

* test(review): fix invalid two-argument expect in assets.test.ts

Round-3 sweep: vitest's expect takes one argument — the message-style second
argument was a lint error and a weak assertion both. The offending value now
rides inside the asserted object, so a regression names which shape slipped
through instead of reporting 'expected true'.

* test(review): pin the findings schema's evidence-asset validation directly

Round-4 sweep: assetFiles/assets were exercised only through publish-assets'
weaving test — the schema's own rejection paths (non-array, empty-string
entry, empty-array drop) had no direct case, so a validation regression
would have surfaced as a confusing weaving failure two layers up.

* fix(review): address all six findings from the automatic review (R1-1..R1-6)

The /review pipeline's own round-1 findings on this PR, each confirmed and
fixed:

- R1-1 (the real catch): the host-binding check sat nested inside the
  `req.repo !== undefined` guard, so a caller omitting --reviewed-repo also
  silently skipped the HOST binding — contradicting the documented "binds
  the PR number (and host) alone". The host check now stands on its own;
  a new test pins an Enterprise-host mismatch refusal with the repo binding
  absent.
- R1-2: --pr accepted whatever yargs `type:'number'` passed through (NaN,
  0, 3.5), and --user-authorized bypasses the gate that would have
  re-parsed the target — `pr-assets/NaN-review` was reachable. A Gate-0
  positive-integer check now refuses first, matching submit's sibling
  discipline.
- R1-3: the suite drove the skillArgs seam without clearing
  QWEN_CODE_SESSION_ID, so running it inside an active Qwen Code session
  spuriously failed eight tests. beforeEach now saves/clears the variable
  and afterEach restores it.
- R1-4: the 40MB aggregate cap was enforced inline and untested (a mutation
  deleting it stayed green). The per-file rules and the total cap now live
  in one pure ruling, validateAssetBatch, unit-tested with five 9MB sizes
  and no fixtures.
- R1-5: the asset_files snake_case alias was the one untested member of an
  otherwise-tested alias family; pinned.
- R1-6: the setGhHost wiring had no command-level assertion; a GHE test now
  pins both the call and the host-carrying manifest URLs.

44 files / 1397 assertions green; tsc --build and eslint clean.

* fix(review): address all ten round-2 findings from the automatic review

Round-2 of the /review pipeline on this PR: 2 Critical, 8 Suggestions,
every one confirmed against the code and fixed.

Criticals:
- The round-2 test block added for the empty-findings no-op omitted the
  QWEN_CODE_SESSION_ID save/delete/restore its two sibling blocks perform,
  so the suite spuriously failed inside an active Qwen Code session — the
  exact dogfooding environment this repo reviews from.
- The gh routing and the returned URLs read the host from two different
  sources: with --host absent, gh children inherit an operator-exported
  GH_HOST (routing at Enterprise) while rawAssetUrl defaulted to
  github.com — every returned URL a 404. One effectiveHost (flag, then
  GH_HOST env) now feeds both.

Suggestions:
- putContent's retry discriminator matched a bare `422` anywhere in
  err.message — which execFileSync fills with the full command line,
  including the PR-numbered remote path: evidence for PR #4220 would read
  a 401 as "already exists". Anchored to `HTTP 422`.
- ensureBranch's bare catch read every ref-lookup failure (401, 403
  rate-limit) as "branch missing"; only HTTP 404 takes the create path
  now, and an empty assets repo — whose default_branch resolves while its
  head ref 404s — is named as the condition it is, with the fix stated.
- Validation refusals threw (yargs exit 1, stack trace, empty stdout)
  while every other gate in the command answers exit 3 +
  {"published": false}; unreadable files and batch refusals now speak the
  same refusal language.
- The command's idempotent writes (content-hashed PUTs, a ref create
  whose duplicate is tolerated) now go through a new ghWithInputRetried —
  sharing gh()'s transient-error retry — and ghWithInput's no-retry
  docstring names the two-caller split instead of claiming a sole caller.
- parseAssetsRepo admitted dot-segment repos (`owner/..`) its docstring
  claimed were path-safe; segments now exclude `.`/`..`, mirroring
  submit's isRepo.
- stringArray accepted whitespace-only evidence paths; trim(), matching
  the sibling asString.
- The GHE test asserted setGhHost was called but not WHEN; it now asserts
  the call precedes the first API invocation.

44 files / 1403 assertions green; tsc --build and eslint clean.

* refactor(review): one refusal helper for every publish-assets gate

Round-2 of this branch's fresh review: the refuse() helper existed below
seven inline copies of the identical three-line refusal — the drift shape
where one site eventually forgets the exit code. Hoisted to the top of
runPublishAssets and used by every gate; message content unchanged where
tests pin it. 26/26; tsc clean.

* fix(review): address the round-3 review — bidirectional host binding and 14 more

The automatic review's third round on this PR: 1 Critical + 14
Suggestions, each verified and addressed.

The Critical (host binding, both halves):
- The gate's `req.host &&` guard bound the host in one direction only —
  an Enterprise-URL authorisation admitted a host-less write routed at
  github.com (or wherever GH_HOST pointed). The gate now compares the
  authorised host against the write's EFFECTIVE host, defaulting an
  absent req.host to github.com: a host is a host, not an exemption.
- Both callers fed the gate the flag rather than the route: publish-assets
  computed effectiveHost (--host ?? GH_HOST) AFTER the gate and bound
  args.host; submit bound args.host while its gh child inherited GH_HOST.
  publish-assets now resolves effectiveHost before Gate 2 and binds it;
  submit binds the same resolution.

The rest:
- pr-assets/<N>-review registered in the asset-branch cleanup workflow,
  per its own every-producer-must-be-added-here rule — a branch nothing
  deletes is permanent.
- ghWithInputRetried had been inserted between ghWithInput and its JSDoc,
  leaving the does-NOT-retry comment attached to the function that DOES
  retry; each function now carries its own doc.
- putContent's retry-path contents-GET is wrapped: when the 422 was not
  the sha-missing shape and the path does not exist, the GET's 404 no
  longer replaces the PUT error the user needs.
- stringArray treats null as absent like every sibling parser, so an
  artifact rendering "no assets" as null canonicalizes instead of
  crashing.
- Test isolation, all four describe blocks: GH_HOST save/delete/restore,
  setGhHostMock.mockReset (a sibling's persistent throwing implementation
  survives mockClear — the malformed-host test also switched to
  mockImplementationOnce), and full mock resets in the blocks that lacked
  them.
- The two regression-pin tests the review measured vacuous now
  discriminate: each fails only the one call under test and asserts the
  pipeline stopped THERE (no contents PUT after a bad create; exactly one
  gh call after a 403 lookup).
- New positive pins: a double-fired create ("Reference already exists")
  succeeds; the canonical report shape this command's own --findings-out
  writes round-trips; an Enterprise-URL authorisation refuses a host-less
  write while a github.com-URL one passes it.

Not changed: the finding that reverting the Finding-interface hunk leaves
tests green — the fields are type-level and their removal fails
tsc --build (the CI leg that caught this branch's own TS6133); a runtime
pin would duplicate what the round-trip tests already exercise.

47 files / 1495 assertions green; tsc --build 0 errors; actionlint clean
on the cleanup workflow.

* fix(review): address the round-4 review — empty-GH_HOST passthrough and four test pins

Round 4 came back COMMENTED (down from CHANGES_REQUESTED), 5 Suggestions,
0 Criticals — all five confirmed and fixed:

- An exported-but-empty GH_HOST ("" from an unset workflow var) survives
  `??`, being non-nullish: effectiveHost became "" and the gate compared
  the authorised host against "", refusing a legitimate github.com write.
  Both call sites now collapse an empty trim to undefined (`|| undefined`,
  parenthesized).
- The gate's URL-shaped repo/host binding was exercised only via
  publish-assets' suite; submit.test.ts now pins both directions of the
  host binding and the repo binding at its own call site.
- ghWithInputRetried had no retry-contract test; gh.test.ts adds the
  symmetric block to ghWithInput's does-NOT-retry pin (transient 500
  retried once then succeeds; non-transient 401 single call).
- The publish-assets mock aliased ghWithInput and ghWithInputRetried to
  one mock, hiding which variant a write used; they are two mocks now,
  and the happy path asserts the non-retrying variant is never touched.
- The Prepared interface's dead `name` field is gone.

46 files / 1476 assertions green; tsc --build and eslint clean.
2026-08-02 15:49:32 +00:00
jinye
295230a48f
fix(external-context): Dispose of Auto Recall proxy dispatcher (#8352)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-02 15:16:09 +00:00
Shaojin Wen
09d818867e
fix(review): follow output language in Tip lines and saved reports (#8370)
* fix(review): follow output language in Tip lines and saved reports

The /review skill's critical rule 2 already states that terminal output
follows the user's output language preference, but three areas lacked
explicit guidance, causing the model to output them in English even
with a Chinese output language configured:

- Follow-up Tip lines (e.g. "Tip: type post comments to ...") were
  specified as English templates with no translation instruction
- The Step 8 saved report file used English section headings and prose
- Step 6 labels (e.g. "Balanced review (effort: medium)") had no
  translation note

Add explicit output-language guidance at each point of use, with a
Chinese example for Tip lines and Chinese section heading examples for
the saved report. Command keywords (post comments, fix these issues)
stay verbatim since they are trigger phrases the user types back.

* fix(review): disambiguate findings-artifact language boundary and complete low-effort translation notes (#8370)

---------

Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
2026-08-02 11:48:58 +00:00
易良
cb2555c7c5
feat(desktop): package Web Shell as a release-ready desktop app (#8132)
* feat(desktop): add Web Shell Tauri proof of concept

* feat(desktop): prepare Web Shell shell for release

* fix(desktop): make release dry runs portable

* fix(desktop): harden cross-platform release smoke

* fix(desktop): stabilize Windows and Linux CI

* fix(desktop): scope bootstrap env to daemon

* fix(desktop): stabilize packaged app smoke

* fix(desktop): diagnose Linux packaged startup

* fix(desktop): address release readiness review

* fix(desktop): address follow-up review findings

* fix(desktop): address runtime review blockers

* fix(desktop): gate cookie auth acceptance behind desktop bootstrap flag

- Cookie→Bearer translation middleware now only active when desktopShellBootstrap is enabled
- Use timing-safe comparison for bootstrap token validation

* fix(desktop): replace cookie handshake with URL fragment auth

- Navigate the desktop WebView to /#token=<token>; the fragment never
  reaches the server, so drop the desktop cookie bootstrap middleware,
  its cookie->bearer translation, and the related serve tests
- Skip the deferred-runtime auth gate for pre-auth Web Shell routes
  (GET|HEAD / and /assets/*): a document navigation cannot carry an
  Authorization header, so the fast-path window used to answer the
  first desktop navigation with 401 Unauthorized until a manual reload
- Poll /health?deep=true before navigating: deep health stays 503
  (reason: bootstrap) until the runtime app that mounts the Web Shell
  is ready, so readiness can no longer race the deferred window
- Run the folder picker off the main thread and only store the runtime
  after the WebView navigation succeeds
- Enable withGlobalTauri plus a bootstrap capability so the bootstrap
  page can subscribe to desktop lifecycle events
- Update smoke-packaged to assert the fragment contract (unauthenticated
  root navigation 200, no cookies minted, API routes still 401) and
  sync the release design doc

* fix(desktop): fix Linux smoke log path, add runtime .gitkeep, correct README (#8132)

* fix(desktop): close release readiness gaps

* fix(cli): keep deferred serve auth gate closed when web shell unmounted (#8132)

* fix(desktop): address review feedback on auth gates and runtime bundle (#8132)

- Cover the method guard in isPreAuthWebShellRequest: assert unauthenticated POST to / and /assets/* is still 401 during the deferred runtime window.

- Add unit tests for is_allowed_navigation covering the unset origin, set origin, and bootstrap-after-origin cases.

- Drop DEV:'true' from the release bundle step so the esbuild metafile is no longer shipped as dead weight in the desktop runtime.

* fix(desktop): address review feedback on runtime extraction and release workflow (#8132)

- Extract .zip Node archives with unzip so Linux cross-builds for win32-x64
  no longer crash on GNU tar.
- Build the Windows signing config with ConvertTo-Json instead of backslash
  escapes, which PowerShell treats as a parse error.
- Fetch the runtime Web Shell without a bearer token so the smoke test
  exercises the pre-auth navigation path the shell relies on.
- Make GitHub release creation idempotent so a re-run after a partial publish
  uploads assets instead of failing on the existing tag.

* fix(desktop): normalize artifact filenames to prevent updater 404s (#8132)

GitHub rewrites spaces to dots when release assets are uploaded, but
the updater manifest encoded spaces as %20 via encodeURIComponent.
This caused every platform's auto-update URL to 404 on published
releases.

Replace spaces with hyphens in the Collect artifacts step for all
platforms so the local filename, the manifest URL, and the published
asset name agree by construction. Update test-release.js fixtures to
match and assert no artifact name contains a space.

* fix(desktop): address review feedback on security, lint, and code quality (#8132)

* fix(desktop): address review feedback on smoke test, error UX, and window state (#8132)

* fix(desktop): address review feedback on crate build, recovery UX, auth gate, and CI (#8132)

* fix(desktop): address review feedback on settings race, version script, and log growth (#8132)

* fix(desktop): address review feedback on retry, auth gate, and release clobber (#8132)

* fix(desktop): gate commands to bootstrap origin and show native update dialog (#8132)

* fix(desktop): use matches! instead of PartialEq on JoinError result (#8132)

* fix(desktop): wait for deferred runtime in smoke tests and sync release flags on clobber (#8132)

---------

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-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
2026-08-02 08:20:16 +00:00
易良
eea0a2b3b2
fix(github-channel): recover interrupted inbound tasks (#8306)
* fix(github-channel): recover interrupted inbound tasks

* fix(github-channel): make inbound recovery bounded

* test(github-channel): cover delivery-failure lifecycle and audit-hit recovery

Add a direct test for the onTaskLifecycle failed/delivery -> reply_pending
transition and a recovery test for the publication-audit match-and-remove
path. Restore the blank line between the constructor and createInitialCursor.

* fix(github-channel): preserve cancelled inbound tasks and fail closed on bookkeeping (#8306)

* fix(github-channel): harden inbound task lifecycle against partial persistence failures (#8306)

* fix(github-channel): close crash-window duplicates and make tests load-bearing (#8306)

* test(github-channel): add recovery test for suppressed audit outcome (#8306)

* fix(github-channel): persist errorCommentPosted after post, add review test coverage (#8306)

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.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-08-02 07:27:44 +00:00
Dragon
e967cc9037
docs: document compaction and image model selection (#8348)
* docs: document auxiliary model selection

* docs: align model option labels

* docs: clarify image model HTTPS requirement
2026-08-02 06:44:54 +00:00
jinye
4338120100
feat(serve): resolve and report the daemon memory budget (#8245)
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 / channel-plugin E2E (nightly) (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(serve): resolve and report the daemon memory budget

The daemon has no notion of how much memory it has. It samples its own
RSS and heap every five seconds, and polls the primary ACP child's RSS,
but there is no limit anywhere to divide those by: no cgroup read, no
heap-size limit, no ratio, no `limits.*` memory field. Every number it
reports is an absolute byte count with nothing to compare against, so
"how close to exhaustion is this daemon" cannot be answered from
`/daemon/status` at all.

Resolve one set of figures at boot and report them. Configured and
effective budgets are separate: the effective value is capped at resolved
cgroup or host memory, so an operator passing a budget larger than the
machine gets a denominator the machine can actually back, with the
discrepancy visible rather than silently resolved. A derived budget below
the documented minimum is reported as `insufficientMemory` rather than
clamped upward, which would have invented capacity that does not exist —
a 768 MB host would otherwise report a 1 GB budget and poison every ratio
computed from it.

`limits.memory` carries the static figures, including `legacyCeilingMb`:
the ceiling an ACP child receives today with no budget involved, so the
gap between current behavior and any future policy is measurable before
that policy exists. `runtime.memory` carries live counts and the advisory
per-child share at both the registered and the live child count.

Nothing here sizes a child. Dividing the pool by a workspace count is not
a sound policy on its own, and the advisory shares exist to show why: on
a 32 GB host with 25 registered workspaces and only the preheated primary
live, a registered-count divisor would cut that child from 16384 MB to
614 MB for memory no dormant workspace is holding, while the per-child
floor still lets 25 children authorise more than the pool. Registration
is not allocation; a real policy needs admission at spawn time keyed on
concurrently live children, and it needs this data to be designed
against.

Applying such a share is also a compatibility change even with no
refusals, since it alters child GC and OOM behavior — so it is not
something this change should slip in under the heading of reporting.

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

* fix(serve): report honest memory counts and guard the registered share (#8245)

* fix(serve): reuse workspace snapshots when counting active children

Counting active ACP children from `listManaged()` is right — `list()` only
returns entries in `active` state, so a workspace mid-drain, mid-replacement,
or blocked still holds a live child that `list()` drops. But taking the count
by calling `getDaemonStatusSnapshot()` again per managed runtime undoes the
existing reuse of the primary bridge's snapshot, and `getDaemonStatusSnapshot`
rebuilds the whole session array on every call.

The second pass also reads the tree at a different instant than the rest of
the response, so `activeAcpChildren` could disagree with the session and
channel figures beside it.

Reuse the snapshots already taken instead, keyed by bridge, and fall back to a
fresh call only for a managed runtime the first pass missed — which is exactly
the non-`active` entry the `listManaged()` change exists to catch.

The reuse this restores was already guarded by a test asserting one snapshot
call per bridge, but that test resolves no memory budget, and the second pass
ran only on the budget path — so it stayed green while every production
`/daemon/status` call did the work twice. Added a case that resolves a budget
and asserts the same property; it fails against the previous commit with
"expected 1 times, but got 2 times".

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

* fix(serve): address review feedback on daemon memory budget (#8245)

* fix(serve): import isValidMemoryBudgetMb in serve command (#8245)

* fix(serve): address review feedback on daemon memory budget (#8245)

* fix(serve): stub isChannelLive on serve test fake bridges (#8245)

* fix(serve): address review feedback on daemon memory budget (#8245)

- Make the stderr-gate test host-independent by pinning os.totalmem
  through a vi.mock toggle instead of reading the runner's cgroup
- Add a spawn-path constant parity test enforcing that
  getAcpMemoryArgs and legacyChildCeilingMb agree on the fraction
  and cap, converting the comment-only invariant into a test
- Narrow the mirror comment to name only the two constants that
  actually have spawn-path counterparts
- Accept availableMemorySource through the resolveDaemonMemoryBudget
  seam so the constrained path is testable end-to-end
- Report maxChildHeapMb alongside minChildHeapMb on the wire so
  clients can distinguish the 16 GB cap from a large host
- Move the listManaged/list comment to the computation it describes
- Add a cross-reference at the opts literal for the late-assigned
  daemonMemoryBudget field

* fix(serve): address review feedback on daemon memory budget (#8245)

* fix(serve): address review — split fraction constant, sharpen parity test, deduplicate error, populate bootstrap memory (#8245)

* fix(serve): address review — document maxChildHeapMb, make parity test order-independent (#8245)

* fix(serve): address review — split parity test into two files to avoid cold re-import timeout (#8245)

* fix(serve): address review — correct session count, compatibility scope, bootstrap memory docs (#8245)

* fix(serve): address review — align help text framing, document default cap, fix stale comment (#8245)

* fix(serve): address review — add positive memory-budget validation test, correct activeAcpChildren docs (#8245)

* fix(serve): address review — document childRssBytes stale tail after watcher detach (#8245)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen Autofix <qwen-autofix@users.noreply.github.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-08-02 04:19:31 +00:00
Dragon
2584764930
fix(core): reuse prompt cache during chat compression (#8339)
* fix(core): reuse prompt cache during chat compression

* fix(core): preserve request config during compression

* fix(core): skip compression after cancellation

* fix(core): accept complete cached summaries

* fix(core): harden compression cache sharing

* fix(core): keep cache helper out of ACP bundle

* test(core): cover effort budget cap

* test(core): cover non-streaming tool call detection

* test(core): cover compression cache gates

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-08-02 03:42:02 +00:00
Dragon
4c6e2518a3
docs: refresh architecture overview (#8325)
* docs: refresh architecture overview

* docs: complete architecture package table
2026-08-02 02:45:00 +00:00
qwen-code-dev-bot
8d6d2ab56a
feat(cli): /summary supports custom export path (#8116)
* feat(cli): /summary supports custom export path (#8113)

`/summary` now accepts an optional path argument, matching `/export`'s
behavior. When a path is provided, the summary is saved there instead
of the default `.qwen/PROJECT_SUMMARY.md`.

- `/summary` → saves to `.qwen/PROJECT_SUMMARY.md` (unchanged)
- `/summary docs/summary.md` → saves to `docs/summary.md`
- `/summary /absolute/path/summary.md` → saves to absolute path
- `/summary docs/` → saves to `docs/PROJECT_SUMMARY.md`

If the path is a directory (existing or ending with `/`), the default
filename `PROJECT_SUMMARY.md` is appended. Parent directories are
created automatically.

* fix(cli): summary custom path dir detection and i18n key (#8116)

* test(cli): assert relative display path in summary tests (#8116)

* fix(cli): summary path containment, early validation, and mkdir hardening (#8116)

* fix(cli): defer summary mkdir to save time so failed generation leaves no empty dir (#8116)

* fix(cli): normalize path separators in summary test and assert file content (#8116)

* test(cli): assert directory permission mode in summary test (#8116)

* fix(cli): resolve symlinks in summary path containment check (#8116)

* fix(cli): reject broken symlinks escaping project root in /summary (#8116)

* fix(cli): guard /summary overwrite and expand tilde in path (#8116)

* fix(cli): re-validate appended default filename for symlink escape in /summary (#8116)

* fix(cli): harden /summary symlink chain walk, file mode, and overwrite guard (#8116)

* fix(cli): address review feedback on /summary custom path (#8116)

- Fix CRLF false-negative in overwrite guard by normalizing line endings
- Allow overwriting empty pre-created files (zero-length bypass)
- Detect trailing separator on existing file and report clearly
- Log chmod failures via debugLogger matching exportCommand convention
- Add comment explaining mkdir mode asymmetry
- Update docs: /summary usage table and custom-path welcome-back note
- Add i18n key for trailing-separator error in all 9 locales
- Add tests for CRLF, empty file, and trailing separator cases

* fix(cli): address review feedback on /summary custom path (#8116)

- Skip symlink-escape check for the default .qwen/ target so a
  symlinked .qwen/ directory (shared team config, overlay mounts)
  keeps working, and the check no longer runs after the LLM call
- Re-run the overwrite guard immediately before writing to close
  the TOCTOU window across the slow generation step
- Determine isDefaultTarget by comparing the resolved path against
  the default so `/summary .qwen/` gets the same 0o700 permissions
- Only chmod 0o600 on file creation; preserve existing permissions
  on regeneration
- Return empty content in interactive-mode errors to avoid double
  rendering (failInteractive already adds the error to history)
- Tighten the overwrite-guard regex to require `**Update time**: `
  after the Summary Metadata heading, preventing false positives
- Fix the realpathNearestExisting comment to document the missing
  containment-during-walk guard vs export/stats copies
- Add tests: symlink cycle, default target with symlinked .qwen,
  TOCTOU overwrite guard, explicit .qwen/ permissions, chmod
  preservation, interactive error content, regex false-positive

* fix(cli): address review feedback on /summary custom path (#8116)

* test(cli): cover post-LLM symlink re-check and interactive error UI (#8116)

* fix(cli): address review feedback on /summary custom path (#8116)

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-08-02 02:19:31 +00:00
Dragon
74dc5547f1
docs: complete TUI keyboard shortcut reference (#8327) 2026-08-01 17:24:19 +00:00
Shaojin Wen
dbb0349351
feat(review): borrow recall, a fix loop, and a size-derived budget from Claude /review (#8315)
* feat(review): borrow recall, a fix loop, and size-derived budget from Claude /review

Three changes, from a comparison of this skill against Claude Code's
`/code-review`. The orchestration half of that comparison went the other way —
nothing there has the worktree isolation, the transcript-backed coverage gate, or
the single computed verdict — so what is borrowed is what it does better:
how much it surfaces, what a finding *is*, and what a small diff costs.

Recall
------
The Exclusion Criteria are a filter on what KIND of thing is a finding. Read as a
confidence bar — which is how an agent under "silence is better than noise" reads
them — they license dropping anything half-believed, and that drop is invisible:
no later stage sees a candidate that was never filed. Every stage after the
finders removes wrong findings; none can add a missing one. Each finder brief now
carries the counterweight explicitly, and the Step 4 verifier deliberately does
not get it.

Code quality was one agent holding six unrelated checks — the shape this skill
already refuses for invariant agents, on measured evidence (PR #6457: one agent
with an eight-item checklist found 1 of 5 defects; the same model split three ways
found all 5). Split into 3a reuse/duplication, 3b altitude/abstraction fit, 3c
consistency/clarity. 12 -> 14 agents in 3A.

Low was one undirected pass capped at 8, and its only alternative was a
nine-subagent fan-out. It is now an angle rotation in one context — line-by-line,
removed behaviour, language pitfalls, wrapper routing, reuse/dead code, sibling
consistency, then a gap sweep — dedup-only, no re-judging, cap 10. Still zero
subagents.

--fix and findings as data
--------------------------
`--fix` is `--comment` reflected and gated on the opposite target: `--comment`
writes to a pull request, `--fix` writes to a working tree, so a PR review (whose
tree is the ephemeral worktree Step 9 deletes) ignores it with a warning. An
effective `--fix` floors the effort at medium — editing the user's files on an
unverified finding is the same mistake as posting one.

New `qwen review findings` canonicalizes the findings into a JSON artifact the
terminal report, the saved report and the review JSON all read, instead of three
transcriptions of one list. With `--outcomes` it merges the fixer's ledger and
REFUSES one that does not account for every finding: a fixer that applies six of
nine and reports six has not lied about any of them, it has silently shortened
the list.

Size-derived budget
-------------------
New `plan.budget`, computed from srcDiffLines the way the topology gate is and
recorded in the plan rather than passed as a flag, so every reader sees one
number. Scopes the low tier's angle count and sweep, the Agent 8 ceiling (0 below
80 source lines — "one domain dominates" is a judgement, and one made about forty
lines finds a dominant domain every time), and the verify shard width. It never
scales a dimension away: that is the roster's answer and the roster reads effort.

Not included: per-model prompt routing. Claude's table exists because it was
measured per model family; shipping an invented mapping into this skill is the
kind of change its own review would flag.

Tests: 39 files, 1215 assertions.

* docs(review): align counts and level descriptions left stale by the 3a/3b/3c split

Round-1 self-review findings on this branch:

- SKILL.md medium tier still named 'quality (Agent 3)'; the Step-1 low bullet
  and Step 3C heading hardcoded six angles though plan.budget scales them 3-6;
  and no fallback was stated for a plan written by an older CLI that carries no
  budget field (falls back to the flat pre-budget behaviour — more coverage,
  never less).
- DESIGN.md still labelled the 12-agent roster '(current)', kept the ten-lens /
  crosses-twelve topology arithmetic beside the updated fourteen-agent copy,
  keyed the re-gating cost row to the 12-agent roster, and described low as one
  pass ≤8 and medium as unverified inline angles — both contradicting the
  SKILL.md this PR ships. The LLM-call-budget and Fork-Subagent sections were
  still summed for 12 agents.
- findings.ts: validateFindings accepted outcome but dropped outcomeNote, so
  the canonical artifact did not round-trip — a skipped finding fed back
  through --input kept its outcome and lost its reason.

Tests: 40 review files green, including two new round-trip cases.

* docs(review): one id per finding across the cache ledger and the findings artifact

The rebase onto #8218 left two id schemes for one finding: the incremental
cache's cross-round ledger names findings R<round>-<n>, while the findings
artifact accepted any unique id. Same defect, two names, and the outcome
ledger and next round's report could no longer be joined. The artifact now
uses the R-ids whenever the run writes the cache ledger.

Conflict resolutions from the rebase itself: review.ts keeps both new
subcommands (test-delta from main, findings from this branch); the
documentation-parity check #8218 added to the old Agent 3 brief lands in 3c,
the consistency slice that owns sibling-parity checks.

* test(cli): add findings to the pinned review subcommand surface

review.test.ts pins the exact subcommand list and sits one directory above
the review/ glob the branch's local runs used, so the new findings
subcommand never met it until CI. Ubuntu was the only matrix leg that ran.

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-01 15:26:58 +00:00
Dragon
8673151ebd
docs: document skill learning and live reload (#8298) 2026-08-01 15:23:08 +00:00
Heyang Wang
9223de3517
docs(web-shell): organize design documents by feature area (#8304)
Group the existing Web Shell design materials under a dedicated directory
so related specifications and supporting assets are easier to navigate.

- Move Web Shell design documents into the web-shell directory
- Keep pane header action screenshots alongside their design notes
- Normalize selected filenames to use the web-shell spelling

Co-authored-by: heyang.why <heyang.why@alibaba-inc.com>
2026-08-01 14:32:20 +00:00
qwen-code-dev-bot
ac4799feca
fix(cli): add ui.mouseTracking setting to restore right-click and URL clicks (#8198)
* fix(cli): add ui.mouseTracking setting to restore right-click and URL clicks

VP mode (default since 0.21.1) enables SGR mouse tracking (?1002h)
which captures ALL mouse events including right-click and left-click
on OSC 8 hyperlinks. The terminal can no longer handle these
natively — right-click context menus and clickable URL links stop
working.

Add a `ui.mouseTracking` boolean setting (default: true) that gates
SGR mouse tracking in useMouseEvents. When set to false, mouse
tracking is disabled entirely, restoring terminal-native right-click
and OSC 8 hyperlink handling. The trade-off is that mouse wheel
scrolling and scrollbar dragging in the VP viewport stop working;
keyboard scrolling (Up/Down/PgUp/PgDn) still works.

Users who need right-click or URL clicking can set:
  "ui": { "mouseTracking": false }

Fixes the regression reported in #8003 (right-click broken) and
the follow-up report (URL link clicks broken) after 0.21.1.

* fix(cli): regenerate mouseTracking schema and clarify setting scope (#8198)

* fix(cli): scope ui.mouseTracking to the TUI and document it (#8198)

* fix(cli): handle hyperlink clicks in VP mode via onContentClick

When SGR mouse tracking is active (VP mode default), the terminal
cannot handle OSC 8 hyperlink clicks natively. Add an onContentClick
callback to ScrollableList that fires on left-press in non-scrollbar
areas. MainContent implements this callback by extracting hyperlink
regions from the clicked item's text (parsing markdown links and
bare URLs with visual column tracking) and opening the URL at the
click position via the platform's open/xdg-open/start command.

This allows URL clicks to work in VP mode without disabling mouse
tracking, so mouse wheel scrolling and scrollbar dragging continue
to work simultaneously.

New file: packages/cli/src/ui/utils/hyperlink-click.ts
- extractHyperlinkRegions(): parses markdown links and bare URLs
  from raw text, computing visual column positions
- findUrlAtColumn(): maps a click column to a URL
- openUrl(): opens a URL via the platform's default handler

* fix(cli): remove broken hyperlink-click feature from VP mode (#8198)

Remove the hyperlink-click feature (hyperlink-click.ts, handleContentClick,
onContentClick) which had three critical defects identified in review:

1. Command injection on Windows: openUrl used child_process.exec with shell
   string interpolation, where single-quote escaping is ineffective against
   cmd.exe metacharacters (&, |, ^, ;).

2. Non-functional coordinate mapping: handleContentClick used raw 1-based
   SGR terminal screen coordinates directly as item indices into
   allVirtualItems, without the scroll-offset and frame-anchor transforms
   that every other mouse consumer applies.

3. Wrong visual-column accounting: extractHyperlinkRegions counted raw
   markdown characters (including **bold**, *italic*, ~~strikethrough~~
   markers) that InlineMarkdownRenderer strips, causing column drift.

The ui.mouseTracking setting (the PR's core purpose) is unaffected and
remains fully functional. A properly designed click-to-open feature can
be added in a follow-up with correct coordinate transforms, renderer-aware
column parsing, and openBrowserSecurely for safe URL launching.

* docs(cli): cross-reference ui.mouseTracking in useTerminalBuffer descriptions (#8198)

* fix(cli): document mouseTracking tradeoffs and hide showScrollbar in serve mode (#8198)

* docs(cli): clarify mouseTracking tradeoffs in shortcuts and settings (#8198)

Address review feedback: note that mouse-wheel scrolling requires ui.mouseTracking in the keyboard-shortcuts table, the tmux trackpad section, and troubleshooting; add the missing ui.showScrollbar row to the settings reference; and pin the mouseTracking schema fields (requiresRestart: false) in settingsSchema.test.ts.

* fix(cli): make mouseTracking restart-scoped and gate mouse affordances (#8198)

ui.mouseTracking declared requiresRestart: false but nothing propagates a
settings write to the refcounted SGR mouse mode: the SettingsContext value
is a single LoadedSettings created outside React with no change emitter, so
a subscriber that never re-renders (e.g. a memoized completed thought block)
keeps ?1002h active and right-click stays broken even with the setting off.
Mark it requiresRestart: true, matching ui.useTerminalBuffer, and drop the
runtime-toggle test whose own comment admitted it did not prove the flip
lands.

Add a shared useMouseTrackingEnabled hook and route the useMouseEvents gate
and the mouse-dependent affordances through it — the history-item "click to
expand" hint, the selection-list hover controller, and prompt
click-to-position — so they read one source and no longer advertise a dead
interaction when mouseTracking is disabled.

* test(cli): add component-level mouseTracking affordance gate tests (#8198)

* fix(cli): address review feedback on mouseTracking PR (#8198)

- Rewrite BaseSelectionList mouse test to mock RowMouseController and
  assert mount/unmount directly, pinning the component-level gate
  instead of the downstream useMouseEvents escape writes
- Add troubleshooting entry for right-click/links/selection symptoms
- Add useTerminalBuffer pairing advice to settings.md mouseTracking row

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
2026-08-01 14:15:42 +00:00
Dragon
e9db70a072
feat(cli): add TUI image display tool (#8217)
* feat(cli): add TUI image display tool

* fix(cli): report terminal image rendering failures

* fix(cli): disable Kitty placeholders in Warp

* fix(cli): constrain terminal image previews

* fix(core): preserve fork image tool cache prefix

* fix(cli): address display_image review feedback (#8217)

Add the missing DisplayImage zh/zh-TW locale entries (and en for parity)
that broke the i18n guard test, detect chafa via a PATH lookup instead of a
synchronous probe render during display_image execution, and reject
truncated PNGs by reading the full 24-byte header before reporting success.

* fix(web-shell): add display_image to tool display contract (#8217)

* test(cli): cover display image validation edges

* fix(cli): harden chafa executable lookup (#8217)

Resolve chafa through the mermaid renderer's hardened findExecutable and
spawn the resolved path, so a project-local node_modules/.bin/chafa is
never executed unless the user opted in. display_image is a
non-prompting Read tool, so the previous bare-name PATH resolution
allowed arbitrary code execution from a malicious repository.

Also add review-requested coverage: isTerminalImageDisplay routing, the
truncated-PNG rejection branch, the chafa stderr fallback, and the Kitty
re-emit dedup guard.

* fix(cli): address TUI image display review feedback (#8217)

- Cache rendered terminal images (bounded LRU keyed on path, mtime, size,
  shape, and renderer) so a terminal resize or a restored session no longer
  re-reads the file or re-spawns chafa for every visible image.
- Reject cmd.exe metacharacters in the model-supplied path before spawning
  chafa through a shell, closing a command-injection surface on Windows
  .cmd/.bat shims.
- Bound a failing chafa's stderr to a capped first line before rendering it
  into permanent scrollback.
- Enforce the main-agent-only display_image ban inside the tool execution
  (isInForkExecution) and fail resolveForkExecutionAllowedTools closed when
  display_image is advertised without a concrete allowlist.
- Move the fileName computation to its use site, drop misleading awaits on the
  synchronous renderer, and add Kitty/Ghostty detection plus renderer cache,
  truncation, and shell-metacharacter tests.

* fix(cli): preserve color in chafa image fallback

* feat(cli): render images directly in Warp

* fix(cli): disable native images in Warp

* test(cli): cover chafa shell path guard

* fix(cli): skip re-transmitting Kitty image payload on remount (#8217)

---------

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>
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>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
2026-08-01 12:39:52 +00:00
易良
bd85599139
feat: visualize ordinary-session plan execution (#7580)
* feat: visualize ordinary session plan execution

* fix(web-shell): complete plan workflow rendering

* fix: preserve authoritative plan state

* fix(core): isolate teammate todo state

* fix: reject ambiguous empty plan snapshots

* test(web-shell): cover workflow dialog wiring

* feat(web-shell): preview workflow before execution

* feat(web-shell): inspect workflow step details

* feat(web-shell): preserve session workflow history

* test: align failing CI tests with plan-isolation behavior

SubAgentTracker: the emitter now guards subagent TodoWrite results
(tool-call-emitter emitResult early-returns on subagentMeta), so a
subagent todo no longer promotes into a session-level plan. Flip the
stale assertion to expect no plan emission, mirroring the dedicated
guard test in tool-call-emitter.test.ts.

HistoricalPlanExecution: the pagination fixture's onLoadOlderHistory
returned Promise<void>, but PlanExecutionHistoryProvider requires
Promise<boolean> and throws 'Unable to load earlier session history'
on a falsy resolution. Production wires loadOlderHistory (resolves
true after layout); return true in the fixture to model a successful
load.

* refactor: simplify session plan execution workflow

* fix(web-shell): preserve split plan approval workflow

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-08-01 10:48:30 +00:00
ytahdn
554c5e44ba
feat(web-shell): support mutable default mid-turn messages (#8229)
* feat(web-shell): support mutable default mid-turn messages

* fix(serve): register mid-turn removal telemetry route

* test(serve): update telemetry route totals

* fix(test): add session_mid_turn_message_mutation to expected features list

* fix(webui): forward clientId on cross-session mid-turn removal (#8229)

- Forward the session clientId in the cross-session removeMidTurnMessage
  branch so the bridge's exact-originator match can succeed; without it the
  removal resolved to an undefined originator and could never remove the
  message stamped at enqueue.
- Strip a misaligned/malformed messageIds from mid_turn_message_injected in
  asKnownDaemonEvent instead of rejecting the whole event, mirroring the
  sidechannel parser so a buggy daemon can't silently lose the injection
  signal.
- Log a mid-turn removal miss in the bridge like the enqueue/pending-removal
  siblings, to make removal races diagnosable from daemon logs.

* fix(web-shell): exclude annotations from mid-turn path and harden idle cleanup (#8229)

* fix(web-shell): add container-type to .queuedPrompts so @container query applies (#8229)

* fix(web-shell): harden mid-turn dedupe and capability gate per review (#8229)

- removeInjectedFromQueue now matches by id first (position-independent)
  and falls back to text only when no id match exists, so two same-text
  sends can't remove the wrong row and double-deliver.
- Thread canMutateMidTurn into useQueuedPrompts and gate the mid-turn
  delete/edit mutation on it, so the keyboard path can't hit a DELETE
  route the daemon doesn't advertise.
- asMidTurnMessageInjectedData omits a malformed messageIds key instead
  of leaving a present undefined, matching the sidechannel parser.
- Narrow MidTurnQueueItem.midTurnState, document the load-bearing effect
  order, and make clearQueuedPrompts return false on a no-op clear.

* fix: harden mid-turn removal per review (log escape, cross-session client id) (#8229)

- Escape the caller-controlled messageId (and sessionId) in the mid-turn
  removal-miss stderr line to prevent log injection (CWE-117).
- Forward the target session's persisted client id on cross-session mid-turn
  removal so the bridge's exact-originator match no longer rejects valid
  removals after a session switch with per-session client ids.
- Strengthen tests: distinct-id independence for two queued messages, deferred
  removal proving the composer waits for daemon removal, and the active-turn
  delete failed-action flag.

---------

Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com>
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 Bot <qwen-code-bot@users.noreply.github.com>
2026-08-01 10:41:29 +00:00
Shaojin Wen
6f8ad2b4a5
feat(review): Include CLI version in attribution (#8294)
* feat(review): Include CLI version in attribution

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

* test(review): Decouple footer test from package version

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-01 09:40:57 +00:00
Dragon
e569734a1e
feat(skills): add auto-skill curator (#7846)
* feat(skills): add auto-skill curator

* fix(i18n): translate /curator command descriptions for zh and zh-TW

The strict-parity locale coverage test failed because the new /curator
command and its status/run/restore subcommands fell back to English
descriptions in zh-CN and zh-TW. Add Simplified and Traditional Chinese
translations for the curator command descriptions and its user-facing
output strings.

* fix(i18n): add English source keys for /curator command

The strict-parity i18n check failed because zh and zh-TW defined the
new /curator command keys while en.js (the source of truth) did not,
producing extra-key parity errors. Add the matching English source
entries so all supported locales share the same key set.

* fix(skills): align curator lifecycle safeguards

* fix(skills): harden curator trust and name guards

* fix(skills): guard curator mutations by workspace trust

* test(skills): cover curator stale-to-active reactivation path

* test(skills): cover curator rollback and restore-collision paths

Add coverage for two previously untested error paths in the auto-skill
curator:
- restoreArchivedAutoSkill refusing to overwrite an existing active
  directory, leaving both the reused directory and the archived copy
  intact.
- runAutoSkillCurator rolling back an archive rename when the post-move
  state persistence fails, returning the skill to the live library and
  leaving nothing stranded in the archive (new isolated test file that
  mocks atomicWriteJSON to fail once).

* test(cli): cover curator command errors and stacked auto-skill usage

- Add mockRejectedValue error-path tests for the /curator status, run,
  restore, and pin commands, asserting each surfaces messageType 'error'
  and that a failed run/restore skips skill-discovery refresh.
- Add positive stacked auto-skill tests to both the non-interactive and
  interactive slash-command paths, asserting recordAutoSkillUsage is
  called once per successful stacked skill carrying project skillDetail.

* fix(skills): reject control-byte auto-skill directory names

isManagedDirectoryName only checked the auto-skill- prefix and basename,
so a crafted directory whose name embeds ANSI/control bytes was treated as
a managed skill and its name printed verbatim by the non-interactive
/curator output (which, unlike the TUI, does not run escapeAnsiCtrlCodes),
enabling terminal control-sequence injection from a cloned repo.

Require the directory name to match SKILL_NAME_PATTERN. A managed dir is
always auto-skill-<name> where <name> passes validateSkillName and the
prefix chars are within the same charset, so this never rejects a
legitimately generated directory (including Unicode skill names, which an
ASCII-only guard would wrongly drop) while excluding ESC/control bytes.

Add a regression test covering a crafted directory with a valid manifest
name so only the directory-name guard can exclude it.

* fix(skills): guard curator state reads and clarify restore errors

Align the curator state read path with the noFollow/lstat guards every
write already uses: refuse a symlinked or non-regular-file state file
(which could otherwise be followed to an external path, /dev/zero, or a
FIFO, causing OOM or a boot hang in untrusted workspaces) and cap the
read size. Also distinguish a present-but-ineligible archived skill from
a missing one in restore error messages.

* fix(skills): close curator TOCTOU reads and preserve seeding baseline

Address review feedback on the auto-skill curator:

- readManagedSkill previously read the manifest via Promise.all([lstat,
  lstat, readFile]); the readFile ran concurrently with the lstat guards,
  so a symlinked SKILL.md pointing at /dev/zero could start an unbounded
  read before the guard rejected it. Read the manifest with O_NOFOLLOW +
  an fstat size bound instead (shared readRegularFileNoFollow helper),
  refusing symlinks atomically and bounding the read.
- readState had the same lstat->readFile TOCTOU window; the O_NOFOLLOW
  read closes it while keeping the existing friendly error messages.
- First-run seeding overwrote firstSeenAt/lastActivityAt with now even
  when recordAutoSkillUsage had already created a record, resetting the
  inactivity clock. Preserve an existing baseline (like useCount/pinned/
  lastUsedAt), while a brand-new skill still gets a fresh now baseline.

Adds regression tests for the seeding-baseline preservation and for
refusing a symlinked manifest.

* fix(core): harden auto-skill curator per review feedback (#7846)

- Resolve node:fs constants lazily so importing the curator does not crash tests that mock node:fs without a constants export.

- Preserve the original error via cause when a rollback also fails.

- Apply the skill-name charset guard to archived directory names reserved in the review-agent task prompt.

* test(core): cover curator restore rollback and re-read guard (#7846)

* fix(core): record auto-skill usage on re-invocation (#7846)

* fix(skills): preserve curator read failures

* fix(skills): harden curator lifecycle guards

* test(skills): cover curator safety paths

* fix(skills): address curator review findings (#7846)

- Ignore future manifest mtimes in lastActivityMs so a bogus timestamp
  cannot make a skill permanently un-curatable
- Skip archived status entries whose directory is also live, preventing
  contradictory double-listing in /curator status
- Check the weekly interval before acquiring the cross-process lock in
  maybeRunAutoSkillCurator so most boots skip the lock entirely
- Use handle.readFile() instead of a single handle.read() to avoid
  silent truncation on short reads

* fix(cli): localize curator messages

* test(skills): cover curator usage safeguards

* fix(skills): address curator review feedback (#7846)

- Isolate per-skill rename failures so a transient error no longer
  aborts the whole pass and prevents state persistence (boot-loop fix)
- Make usage recording fire-and-forget (void instead of await) since
  it is already best-effort and nothing consumes the result
- Skip state file creation when no auto-skills exist
- Prune dead records whose directory exists in neither root
- Sanitize user-supplied directory names in error messages
  (JSON.stringify) to close the ANSI control-sequence echo path
- Split reserved skill names into active/archived lists in the
  review-agent task prompt
- Make collision output actionable with remediation guidance
- Fix rollbackMoves mutating its argument (moved.reverse → copy)
- Add null guard to isMissing for non-object rejections
- Add locale keys for skippedErrors output (9 locales)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Qwen Autofix <qwen-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
2026-08-01 07:37:20 +00:00
易良
2bbd82f14b
fix(cli): complete image routing across entry points (#7206)
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 / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (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(cli): complete image routing across entry points

resolve local image @ references consistently in TUI, ACP, and non-interactive entry points
validate aliases and canonical targets against workspace, ignore, MIME, and file-identity boundaries
route image turns through the full-turn agent or vision bridge while failing closed on unsupported/fallback paths
integrate with tool-result vision bridge from #7484: @ image routing and tool-result image routing coexist with \0 suffix priority convention

Refs #6988

* test(cli): complete session config fixture

* fix(cli): close image routing review gaps

* test(core): fix readManyFiles config mock cast

* fix(cli): address image routing review feedback

* fix(cli): address image routing review comments

* test(core): cover validated file read safeguards

* chore: trim review followup scope

* fix(core): preserve custom fs text reads

* fix(core): harden validated file snapshots

* fix(core): resolve validated attachment review findings

* fix(core): share file display label with errors

* fix(core): unify snapshot cleanup through finally block (#7206)

* fix(cli): accept repeated full-turn model override

* test(cli): align drain prompt id expectation

* fix(cli): continue directory loop on workspace/ignore check failure (#7206)

The multi-root workspace directory loop used break on workspace-boundary
and canonical-ignore check failures, preventing resolution from remaining
directories. Switch to continue and defer the ignoredByReason push until
after the loop so a path that resolves from a later directory does not
leave a stale entry in the ignored-paths report.

* fix(cli): cover validated image routing edge cases

* test(core): type snapshot growth read mock

* fix(core): bound validated text reads

* test(cli): align large at-file read expectation

* fix(readManyFiles): key FileReadCache by canonical path, add size pre-check

* test(cli): deflake InstalledTab activation warning render sync (#7206)

* test(cli): cover full-turn model override rejection

* fix(core): keep unbounded validated text reads handle-bound

* fix(core): per-file error boundary and scan budget for validated reads (#7206)

* fix(core): surface size errors for validated files too large to snapshot (#7206)

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.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>
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-bot@users.noreply.github.com>
2026-08-01 02:40:44 +00:00