Commit graph

7353 commits

Author SHA1 Message Date
Shaojin Wen
2deefbf9c7
test(web-shell): add mermaid, split-view + sidebar visual scenarios (#6964)
* test(web-shell): add a mermaid diagram visual scenario

Add a `mermaid diagram` scenario to the visuals suite so the preview
covers the Mermaid rendering surface — an assistant message with a
mermaid fenced flowchart. It renders the real MermaidBlock (async
mermaid import, injected <svg>) in light and dark, waiting on the
rendered SVG so the capture is never the "rendering…" placeholder.

This is the surface the diagram zoom/pan work (#6881) enriches, so once
the before/after preview lands it gives that PR a real before/after
target instead of an unrelated canned screenshot.

* test(web-shell): add a split-view (+ maximize) visual scenario

Add a `split view` scenario: enter the two-pane split via the `?split=a,b`
deep link, then maximize one pane (#6951). Captures the tiled state (both
panes, with the maximize controls) and the maximized state (one pane
filling, restore control) in light and dark, driving the real SplitView
against the mock daemon serving two sessions.

* test(web-shell): add a sidebar attention-badge visual scenario

Add a `sidebar attention` scenario: four sessions in distinct states —
waiting-on-permission, waiting-on-user-question, running, idle — so the
sidebar renders #6956's "Waiting for approval" / "User input needed"
attention pills. Renders in light and dark; asserts on session names
(present with or without the pills) so the frame is the same shape on
main and the PR, letting the before/after preview surface the pills.

* test(web-shell): derive the split view's second session from the scenario list

Addresses a review suggestion: the split view test hardcoded the
'previous-session' id, which only worked because it is in
createWebShellDaemonScenario's default sessions list. Derive the second
pane's session from the scenario's own list instead (and throw a clear
error if absent), so a future rename/removal of that default surfaces as
a self-explaining failure rather than a confusing SSE connection timeout.

* test(web-shell): tidy split copy and mermaid width in visual scenarios

Address review nits on the visual scenarios:
- Split scenario: the mock replays the same events into both panes, so
  "Here is the first pane of the split." read wrong in the second pane.
  Use pane-neutral copy ("Here are the two sessions, side by side.").
- Mermaid scenario: the flowchart's rightmost node clipped at the code-block
  edge at the 1280px capture viewport. Shorten the node labels (same nodes and
  flow) so the whole diagram fits with margin.

Re-ran both scenarios (light + dark) locally: 4/4 pass, and confirmed in the
captures that the diagram no longer clips and the neutral copy reads correctly
in both panes.

* test(web-shell): capture split-view restore and assert all sidebar sessions

Address review nits on the visual scenarios:
- Split view: after maximize, click "Restore pane" and capture the restored
  tiled layout, asserting the maximize control returns on both panes — so a
  regression in the restore path is caught, not just the tiled and maximized
  states.
- Sidebar attention: assert all four session names render (not just the two
  waiting ones). The running session is also the loaded one, so its name also
  shows in the main view — scope the running/idle checks to the sidebar
  landmark so the match stays unambiguous.

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-16 00:58:28 +00:00
Shaojin Wen
7c1a93477c
perf(review): scope Agent 7's build/test to the workspaces the diff changed (#6955)
* perf(review): scope Agent 7's build/test to the workspaces the diff changed

Agent 7's brief runs `npm run build` then `npm test` with a 120s deadline. On this
repo a cold full build is 125s, so the mandated command cannot finish inside the
mandated deadline — measured across the harness's own transcripts, 71 `npm run
build` timeouts, each verifying nothing before the model spent more turns ruling
the timeout environmental and improvising a narrower command.

`qwen review build-test` replaces it. It installs if needed, then builds only the
workspaces the diff touches (from the plan's files[] and the root package.json
workspaces) plus their dependents and dependencies, deps-first — for a leaf PR, a
handful of packages instead of all of them. When a compile fails on `TS2307:
Cannot find module '@scope/pkg'` naming a workspace, it adds that package and
retries, so a tsconfig `paths` edge into another package's sources (which
package.json never declares) self-corrects rather than being modelled. A command
that runs out of time is reported as infrastructure, never as a Critical against
the PR; a build failure in a file the diff did not touch is reported as
pre-existing.

Agent 7's brief (in lib/agent-briefs.ts) now leads with build-test and falls back
to the maven/gradle/cargo/go/pytest precedence only when it reports
`toolchain: unsupported`, so non-npm repos are unchanged. buildRoleBrief injects
the concrete command with absolute --plan/--worktree paths, beside the
test-efficacy probe it already injects.

The install moves out of Step 1 (nothing before Agent 7 needs node_modules, and
running it there blocked the whole fan-out) into build-test, with
QWEN_SKIP_PREPARE=1 so `npm ci` does not trigger this repo's prepare hook — which
builds and bundles every workspace, ~190s, wasted before a scoped build. npm ci
drops from ~161s to ~27s. And todo_write is banned during a review: this document
is the plan, and each call is a whole model turn (measured at 179-377s per run of
restating steps already written down).

`isWorkspaceMember` moves from test-efficacy.ts to the shared lib/workspaces.ts
(re-exported to keep its callers), since build-test needs the same glob walk.

* test(review): add build-test to the expected subcommand roster

* fix(review): guard the build-test prompt and treat every timeout as infrastructure

Addresses the /review findings on this PR:

- agent-prompt.ts: the build-test command block is now guarded on `pr !== undefined`,
  matching the adjacent test-efficacy block. Without it, `report.prNumber` being
  absent resolved `--out` to `qwen-review-pr-undefined-build-test.json` — a report the
  agent writes and downstream never finds.
- build-test.ts: a timed-out `npm ci` now aborts instead of building against the
  partial `node_modules` a timeout leaves behind (distinct from a `prepare` failure,
  which leaves a complete tree). A timed-out test command now produces an
  infrastructure note rather than "correlate this failure with the diff — a failure is
  a Critical", which contradicted the brief. The handler's triple `argv` cast is
  consolidated.

Tests: absolute-path + no-`undefined` assertions for the build-test command block, and
install-timeout / test-timeout infrastructure-note coverage.

* fix(review): survive the tool timeout, partial installs, local mode, and Windows

Addresses the review on this PR:

- **Tool timeout (High).** build-test runs install + builds + tests in one process;
  the agent's default 120s `run_shell_command` timeout would kill it — the failure
  this PR fixes, one level up. The welded command block now tells the agent to
  invoke it with `timeout: 600000` (the shell tool's max).
- **Partial `node_modules` (Medium).** The install gate is now npm's completeness
  marker (`node_modules/.package-lock.json`), not bare directory existence, so a
  partial tree left by a timeout (here or from the outer kill) triggers a reinstall
  instead of a build against half a tree. A timed-out install also removes the
  partial tree before aborting.
- **Local reviews (Medium).** Agent 7 runs on local/file reviews too, which have no
  worktree or PR number. The build-test block now emits there as well, scoped to the
  project root, so the brief's "run build-test, below" is no longer a mandate with no
  command.
- **Windows merge queue (Medium).** The real-spawn fixtures used POSIX
  `touch`/`test -f`/`sleep`, which the Windows `test_windows` job (cmd.exe) fails on;
  rewritten with portable `node -e`, plus explicit per-test timeouts.

Smaller points: an unmodeled workspace glob (`**`, inner `*`, `foo-*`) now returns
`toolchain: "unsupported"` instead of a false "nothing to build"; a widened-away
command is dropped from `timedOut` as well as `build[]`; the `ok` docstring is
corrected; timeout detection prefers spawnSync's authoritative `ETIMEDOUT`.

* fix(review): abort widening on a build timeout, and close the round-2 review gaps

Addresses the second review pass:

- **Widening loop ignored a timeout (Critical).** A build killed at the deadline
  leaves partial output that can contain a `Cannot find module` line; the loop read
  that as a too-small build set and retried under another full deadline, up to the
  attempt cap — 4× the wall clock for what is infrastructure, not a graph gap. A
  timed-out build now aborts at once, the way the install path already does.
- **Widening scanned trimmed output.** `unresolvedWorkspaceDeps` reads the per-command
  output, which was head+tail only; a `Cannot find module` in the omitted middle
  (a long tsc log) ended widening early and surfaced a real gap as a false build
  failure. `trimOutput` now rescues module-resolution error lines from the middle, so
  the report stays bounded and the widening signal survives.
- **Unquoted `--workspace`.** A workspace dir with a space or shell metacharacter would
  split under `shell: true`; the build and test commands now quote it.
- **Handler had no error boundary.** A missing/invalid plan threw a raw stack trace as
  the whole of Agent 7's result; the handler now prints the descriptive message and
  exits non-zero.

Coverage the review asked for: the widening attempt-cap exhaustion, a build timeout
mid-widening, negated-workspace exclusion from the build set, the `changedFilesFrom`
error path, and `readWorkspaceGlobs`' object form.

* test(review): drive build-test through the exec seam, not real npm

The new build/test scoping tests spawned real `npm run` processes. Under the full
suite's parallelism that hung CI (each npm start is slow, and a `node -e`
setTimeout fixture leaked past the process kill), and the fixtures were POSIX-only.

These tests are about which packages get built, in what order, and how a result is
classified — not about npm's own workspace resolution. Driving them through the
injectable `exec` seam makes them deterministic, instant, leak-free and
platform-independent. No production code changes; the suite drops from a hang to
~6s.

* fix(review): support single-package and non-npm repos in build-test

Addresses the follow-up review's findings.

- **Single-package npm repos (no `workspaces`) had no build/test path (regression).**
  build-test now treats a workspace-less `package.json` with a build/test script as a
  single root package: it installs, builds `npm run build` and tests `npm test` (no
  `--workspace`), keeping the deadline and timeout-as-data for the most common repo
  shape. Only when the root has no build/test script does it report `unsupported` —
  and the brief's fallback now installs dependencies first, since build-test's own
  install runs only on the npm path.

- **yarn/bun workspace repos got a false "install failed".** `workspaces` is also
  yarn/bun syntax, but they write no `package-lock.json`, so the completeness marker
  was never present and `npm ci` fail-fasted over a usable tree. Install now runs only
  when a root `package-lock.json` exists; a non-npm tree that is already present is
  trusted (the build is the authoritative signal).

- **Per-command deadline lowered 600s → 300s**, kept strictly below the 600 000 ms tool
  timeout the brief welds, so a single hung command's own deadline fires — and the
  report lands — before the outer shell kill would discard it. (A giant PR whose
  commands sum past the tool ceiling remains an acknowledged follow-up; a dynamic
  cumulative budget is the full fix.)

Smaller points: the install-timeout `rmSync` is wrapped (best-effort, `maxRetries`) so
a race with orphaned grandchildren can't replace the report; the local-mode `.`
worktree fallback is gated on `pr === undefined` so a PR-mode report never builds the
user's checkout (help text corrected); the now-unreachable widen-path `timedOut` filter
is removed; and `changedFilesFrom` rejects a non-object plan with a descriptive error.

* fix(review): close the generic-repo false-green and false-Critical gaps

The verification review found three ways the generic (non-this-repo) surface
re-opened the two failure modes this command is built to prevent. All three now
hand off or self-correct instead.

- **F1 (false green).** A changed dir the workspace globs map to a non-package — a
  nested package listed before a `*` that also claims its parent segment, or a loose
  file under a `packages/*` base — was dropped from the build set silently: zero
  commands, `ok: true`, "Everything passed". Any affected dir not in the package map
  now trips the `unsupported` handoff (naming the dir), never a bare green.

- **F2 (false Critical).** A review worktree is cold. A yarn/bun/pnpm repo (same
  `workspaces` field, no `package-lock.json`) got no install on any path — build-test's
  install is npm-gated and the brief's fallback never fired — so the build failed with
  `Cannot find module` inside the PR's own files, steered toward a Critical. A non-npm
  repo with no installed tree now hands off, naming the tool (`yarn install
  --frozen-lockfile`, etc.) to install with first.

- **F3 (mis-order terminal-fail).** When both the needer and an undeclared-needed
  package are changed and the alphabet orders the needer first, the compiler named an
  in-set package, `missing` came out empty, and the run terminal-failed though the
  corrected order builds green. The widening filter is now `!built.has(dir)` rather
  than `!set.includes(dir)`, so an in-set-but-unbuilt package re-seeds into
  `alsoBuild` (which sorts first) and fixes the order; the attempt cap still bounds it.

Tests for all three, matching the review's runnable repros. The non-blocking notes
(cumulative-budget overrun, incremental report write, array-args spawn) remain for the
dynamic-budget follow-up.

* test(review): tighten the widening-cap assertions

Per review: assert exactly three widenings (`toBe(3)`, not `>= 3`) so an
over-widening regression is caught, and assert `rep.test` is empty — the
exhaustion branch returns before the test loop, so a refactor that reordered the
test loop above that return would otherwise pass undetected.
2026-07-16 00:58:11 +00:00
qwen-code-dev-bot
506ce0a1a4
fix(test): widen model-response timeouts in SDK E2E tests for CI stability (#6979) (#6985)
* fix(test): widen model-response timeouts in SDK E2E tests for CI stability (#6979)

The 'should handle control responses when stdin closes before replies'
test armed 30 s boundedPromise timeouts for all four phases of a
two-turn model interaction. On shared Linux CI runners a single model
round-trip routinely exceeds 30 s, exhausting every retry.

The permission-control suite used a flat TEST_TIMEOUT = 30 000 ms
across 23 real-API test cases, and two tests hardcoded 40 000 ms
timeouts instead of the shared constant.

- Raise all four boundedPromise timeouts to 60 s in the stdin-close
  test so each phase can absorb a slow model round-trip.
- Make TEST_TIMEOUT CI-aware (60 s on CI, 30 s locally) and route the
  two hardcoded 40 s values through it for consistency.

* fix(test): use CI-conditional phase timeouts in abort-and-lifecycle E2E (#6985)

Apply the same CI-conditional pattern (60s on CI, 30s locally) used by
permission-control.test.ts to the stdin-close test's four boundedPromise
calls. Local developers no longer wait the full CI-grade 60s per phase.

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-16 00:06:26 +00:00
ytahdn
19fc52aa93
feat(daemon): add stateless generation SSE (#6947)
* feat(daemon): add stateless generation SSE

* test(integration): expect session generation capability

* fix(daemon): address generation review findings

* fix(daemon): harden generation regressions

* fix(daemon): preserve generation error events

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-16 00:00:08 +00:00
qwen-code-dev-bot
bc33a66689
chore: update default model to qwen3.7-max (#6978)
Update MAINLINE_CODER_MODEL from qwen3.5-plus to qwen3.7-max and
refresh the Qwen OAuth coder-model description to reflect the new
model generation.

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-15 23:51:13 +00:00
qqqys
859095bc98
feat(channels): support natural memory references (#6952)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
* feat(channels): plan targeted memory intents

* fix(channels): harden targeted memory planner

* fix(channels): bound serialized memory manifest

* feat(channels): resolve natural memory references

* fix(channels): harden memory intent routing

* docs(channels): explain natural memory references

* fix(channels): type resolved memory intent dispatch

* fix(channels): route Chinese preference filters
2026-07-15 17:46:23 +00:00
易良
5919968763
fix(cron): add deterministic test seam for cron-interactive E2E (#6987)
* feat(cron): add deterministic test seam for cron-interactive E2E

Add forceFireJob(id) method and QWEN_CODE_TEST_CRON_FAST env var to
CronScheduler. When enabled, newly created session-only jobs auto-fire
after 5s (configurable via QWEN_CODE_TEST_CRON_DELAY_MS) instead of
waiting up to 60s for the wall-clock minute boundary.

This removes the timing-flakiness from cron-interactive.test.ts
without changing any production behavior (seam is env-gated and
inactive by default).

Refs #6982

* test(cron): use QWEN_CODE_TEST_CRON_FAST seam, reduce timeouts to 30s

Enable the CronScheduler test seam in cron-interactive tests via
QWEN_CODE_TEST_CRON_FAST=1 in makeEnv(). This makes newly created
cron jobs auto-fire after 5s instead of waiting for the wall-clock
minute boundary.

Reduce all waitForScreen timeouts from 90s to 30s since the fire is
now deterministic (~5s after job creation + model round-trip).

Refs #6982
2026-07-15 17:39:15 +00:00
易良
d1787a2280
ci: quarantine cron-interactive from push E2E to nightly-only (#6986)
cron-interactive.test.ts is inherently timing-flaky: it relies on
wall-clock cron fire (*/1 at minute boundary) + real model latency,
with a 90s waitForScreen window. On slower macOS runners this
occasionally exceeds the timeout, turning push CI red and triggering
wasteful autofix attempts (~50min).

Changes:
- Exclude cron-interactive from push-triggered Linux and macOS jobs
  via vitest --exclude
- Add new cron-interactive-nightly job: runs only on schedule/
  workflow_dispatch, with continue-on-error so flakes are visible
  but do not fail the workflow
- Mirrors the existing web-shell-browser-regression pattern

Coverage is preserved: cron regressions are still caught by nightly
runs within ~24h. The root fix (injectable clock seam in
CronScheduler) is tracked separately in #6487/#6982.

Closes #6982
2026-07-15 17:18:28 +00:00
Shaojin Wen
0d136bd132
feat(review): prove Step 4 (verify) and Step 5 (reverse audit) actually ran (#6965)
* feat(review): prove Step 4 (verify) and Step 5 (reverse audit) actually ran

`check-coverage` proves Step 3 was done, from the harness's own transcripts. But it
runs at Step 3D — before verify and reverse audit exist — so its roster never reaches
them, and a run could skip Step 4 or Step 5 wholesale, or launch agents that never
opened their brief, and nothing would see it. That is the same silent-omission failure
the coverage gate was built for, one pipeline stage later.

Their count is not in the plan (verify shards on the finding count; the reverse audit
loops until it goes dry), so there is no exact roster to check. What there is is a
floor, and `compose-review` is the place to check it: it runs only at high effort —
the only effort at which verify and reverse audit run at all — and it already
recomputes coverage from the transcripts on the way to the verdict.

`verificationGaps` asks two questions of the same records:

- **Reverse audit** — required on every high-effort review, because it is the pass
  that looks for what Step 3 missed, and a verdict that never ran it cannot certify
  the diff complete, least of all a clean one. At least one auditor must have run and
  opened its brief (3A records it under `reverse-audit`, 3B under
  `reverse-audit--chunk-N`).
- **Verify** — required once the review has findings, because an unverified finding
  must not become a public blocker. Keyed to inline findings: a review that confirmed
  nothing has nothing to verify, and deterministic `[build]`/`[test]` findings are
  pre-confirmed and skip verification by design.

A gap is named in `unreviewedDimensions` and caps the verdict exactly like a dimension
nobody reviewed — an Approve drops to Comment, the gap is disclosed in the body, the
findings still post. The highest-value catch is a clean, zero-finding review that
never ran its reverse audit and would otherwise have approved. Nothing is passed in
and nothing can turn it off: the proof is the intersection of the prompt the CLI
recorded building and the harness's transcript of an agent that ran it and read the
brief — two artifacts, neither authored by the orchestrator.

Tests: verificationGaps over real transcript/record fixtures (reverse audit ran /
skipped / built-but-brief-unread / 3B per-chunk key; verify required-when-findings /
not-when-clean / brief-unread), and compose-review integration (the cap reaches the
verdict and the body).

* fix(review): tighten the Step 4/5 gate from its own review

Six findings from the /review skill's pass over this PR, all addressed.

The verify floor keyed on inline findings alone, so a non-deterministic Critical
that could not be anchored — a body Critical — posted under REQUEST_CHANGES without
ever demanding a verifier. It now counts body Criticals too, minus the deterministic
`[build]`/`[test]` ones, which are pre-confirmed and skip verification by design (and
carry their source tag, so they are recognisable). A review whose only finding is a
real unanchorable Critical is now told it was not verified; one whose only finding is
a build failure is not — that disclosure would be false.

The brief-open check matched the brief path as a bare substring, the trap
`parseTranscript` already learned to avoid for the diff path: `…/x.brief.md.bak`
would have counted as `…/x.brief.md`. Both this file's brief checks — the Step 4/5
one and the roster's — now match the whole quoted JSON value.

The per-chunk reverse-audit key was recognised by a regex hardcoding the
`--chunk-<n>` shape; it now keys off the role name and the universal `--` separator,
so a change to how the suffix is spelled does not silently drop every per-chunk key
and cap a correct review.

`verificationGaps` now runs in its own try, so a read failure there reports itself
rather than wearing the coverage block's "cannot show the diff was read" message
over a coverage result that succeeded a line above it.

Tests: the fixture helper records prompts under the percent-encoded key, matching
production; a `launch: false` verify case covers the transcript-matching half of the
floor that the `opensBrief: false` case does not; and two compose-review cases pin
the body-Critical verify rule (a non-deterministic one demands a verifier, a
`[build]` one does not).

* docs(review): correct the verify-floor comment and cover the .bak brief arm

Two non-blocking notes from the verification review.

The `verificationGaps` doc comment still said the verify floor was "keyed to INLINE
findings" — one commit stale. It is `opts.postsFindings`, decided by the caller,
which now counts non-deterministic body Criticals and excludes deterministic
`[build]`/`[test]` ones. The comment now says that.

The `.bak` tightening of the brief-open check had a regression test for the Step 4/5
arm but not the Step 3 roster arm they share. Added one: a roster agent that opened
`<brief>.bak` is not credited with opening the brief.
2026-07-15 16:19:12 +00:00
顾盼
53ce8a2a40
fix(cua-driver): harden MCP tool reliability (#6968)
* fix(cua-driver): harden MCP tool reliability

* fix(cua-driver): address reliability review findings

* fix(cua-driver): keep config snapshots coherent

* fix(cua-driver): reject empty AX container trees

* chore(cua-driver): prepare v0.7.2 release
2026-07-15 15:40:31 +00:00
BaboBen
f5bdba724e
fix(wecom): prevent requireMention from disabling group chat (#6948)
* fix(wecom): trust group callback mention scope

Fixes #6939

* docs(wecom): clarify mention-scoped behavior
2026-07-15 15:38:21 +00:00
ytahdn
03e796ec9b
feat(web-shell): show sessions awaiting user action (#6956)
* feat(web-shell): show sessions awaiting user action

* test(web-shell): cover question count fallback

* fix(web-shell): clarify pending input state

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-07-15 14:40:10 +00:00
qwen-code-dev-bot
c8a784bbf5
fix(test): widen second-turn phase timeouts in stdin-close E2E test (#6966) (#6973)
The 'should handle control responses when stdin closes before replies'
test armed 15 s timeouts for canUseToolCalled and inputStreamDone —
both measuring the second turn, which requires a full model round-trip
(API request + model thinking + tool_use response). On shared Linux CI
runners the second-turn latency routinely exceeds 15 s, failing all
three retry attempts.

Raise both to 30 s, matching the existing firstResult and secondResult
budgets, so every phase in the two-turn test carries the same headroom.

Co-authored-by: Qwen Autofix <autofix@qwen-code.dev>
2026-07-15 14:01:46 +00:00
Shaojin Wen
38429bc100
fix(web-shell): show workspace chip tooltip on narrow composer (#6958)
* fix(web-shell): show workspace chip tooltip on narrow composer

The composer's workspace chip surfaced its full cwd only through a native
`title` attribute, unlike the sibling git-branch and model chips which use a
styled Radix tooltip. On a narrow (split-screen / mobile) composer the chip
ellipsizes or collapses to an icon, so the workspace is discoverable only on
hover — and a native `title` is inconsistent and never fires on touch.

Give WorkspaceIndicator the same Radix tooltip as GitBranchIndicator (with the
full cwd as content), completing the documented "mirrors GitBranchIndicator"
intent. Its visually-hidden tooltip mirror also exposes the cwd to screen
readers, which the native `title` did not do reliably.

* test(web-shell): assert the workspace tooltip renders on hover

Address review feedback: the WorkspaceIndicator tests checked the
`data-web-shell-workspace-title` hook but never opened the tooltip, so a
regression rendering the short name (or nothing) in the Radix `TooltipContent`
would have gone unnoticed. Open the tooltip via a `pointermove` (jsdom has no
`PointerEvent`; Radix opens on mouse move after `delayDuration`) and assert the
portalled `[role="tooltip"]` shows the full cwd — and, in compact mode, assert
the `workspaceChipCompact` icon-only class is actually applied.

* test(web-shell): guard the compact chip before asserting on it

Move the `if (!chip)` null guard above the assertions in the compact-mode test
so a failure to render surfaces the descriptive "workspace chip was not
rendered" error instead of an opaque `expect(undefined)` throw from the
optional-chained access. Matches the first test in the file.

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-15 12:32:17 +00:00
jinye
7a1b182cd1
feat(cli): Add archived session export (#6911)
* feat(cli): add archived session export

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

* codex: address PR review feedback (#6911)

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

* codex: address PR review feedback (#6911)

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

* codex: address PR review feedback (#6911)

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

* codex: address PR review feedback (#6911)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-15 12:21:07 +00:00
Shaojin Wen
96225b03c4
ci(web-shell): stop visual previews firing on SDK-only PRs (#6959)
The visual-preview capture workflow triggered on
packages/sdk-typescript/src/**, but the previews render against a mock
daemon — the SDK's transport/client layer is stubbed at the network
boundary and its changes can't move a pixel in the rendered scenarios.
So #6911 (a pure-backend PR that only added a DaemonClient data-layer
method) still got the five canned screenshots re-posted as noise.

Drop the SDK trigger. The web-shell client imports no runtime code from
the SDK root and only type-imports DaemonClient, so no real UI coverage
is lost: genuine web-shell UI PRs (#6881, #6951) still trigger via
packages/web-shell/client/**. Confirmed by simulating the path predicate
against each PR's changed-file list.

Co-authored-by: wenshao <wenshao@example.com>
2026-07-15 12:13:43 +00:00
Shaojin Wen
93ccdf4070
feat(web-shell): maximize a single split pane (#6951)
* feat(web-shell): maximize a single split pane

Add a per-pane maximize/restore toggle to the split view. Clicking it makes
one pane fill the whole split and hides the others; the hidden panes stay
mounted so their sessions keep streaming (a purely visual solo). Restore via
the header button or Escape — Escape defers to the composer, the add-session
picker, and open dialogs so it never steals their key.

The toggle only appears with 2+ panes, adding a session exits maximize to
reveal the new pane, and the maximize is dropped whenever its pane leaves the
set or the split shrinks to a single pane.

* refactor(web-shell): use lucide icons for the maximize toggle; cover switch + picker-Escape

Address review on #6951:
- Swap the hand-written Maximize2/Minimize2 SVG paths for the named lucide-react
  components, per the web-shell icon convention (README) and matching DialogShell.
- Add tests for moving maximize between panes (guards the toggle's switch branch)
  and for Escape closing the add-session picker without un-maximizing (guards the
  pickerOpen deferral).

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-15 12:10:29 +00:00
Shaojin Wen
fa1c402c66
feat(review): build the Step 4 verifier and Step 5 reverse-audit prompts in code (#6942)
Some checks failed
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Python / Classify PR (push) Has been cancelled
SDK Python / SDK Python (3.10) (push) Has been cancelled
SDK Python / SDK Python (3.11) (push) Has been cancelled
SDK Python / SDK Python (3.12) (push) Has been cancelled
* feat(review): build the Step 4 verifier and Step 5 reverse-audit prompts in code

The last change moved Step 3's agent prompts into code — the diff path, the brief,
the roster, the delivery check — because a prompt the orchestrator retypes is one
that drifts, and dogfooding proved every drift. Step 4 (verify) and Step 5 (reverse
audit) were left composing their prompts from prose. They carry the densest
methodology in the skill, and it is the methodology most costly to drop:

- the verifier's one-way, quote-the-contradiction bar on rejecting a Critical, and
  its documented-intent gate — the exact rule a run skipped when it auto-posted a
  false "this PR now leaks AWS/GitHub tokens" Critical over a rationale three lines
  up in the diff;
- the reverse auditor's gaps-only focus and substantive receipt.

Both are now `qwen review agent-prompt --role verify` / `--role reverse-audit` —
built in code, written to a brief file the agent reads, so the launch prompt the
orchestrator carries is short and the method cannot be paraphrased away. The
verifier is a new brief kind (`output: 'verdicts'`): it gets the Exclusion Criteria
but not the finding format, because it rules on findings rather than filing them. A
Step 3B reverse auditor takes `--chunk <id>` so it reads one chunk's range, not the
whole 5 800-line diff — the range that made it the most context-starved agent in the
pipeline.

The orchestrator still supplies the one input that changes per launch — the shard's
findings for the verifier, the cumulative finding list for the auditor — above the
verbatim brief, exactly as a dimension agent gets its one-line change summary.

NOT in this change, and called out so it is not mistaken for done: these agents run
after Step 3D, so the coverage gate's roster does not reach them. Whether the
verifier and auditor actually ran and read their briefs is not yet checked from the
transcripts the way Step 3's agents are. That check is the next step.

* fix(review): scope a per-chunk reverse auditor's brief to its one chunk

Dogfooding this PR, the /review skill's own reverse-audit agent found a defect
in it. A Step 3B reverse auditor is launched `--role reverse-audit --chunk N` so
it reads one chunk's range, not the whole diff — that scoping is the whole point:
a reverse auditor handed a 5 800-line diff is the most context-starved agent in
the pipeline, on exactly the PRs where the reverse audit matters most. The launch
prompt scoped correctly. The brief did not: `buildRoleBrief` gave every diff-reading
role the full `diffReadingBlock`, which emits a read for every chunk in the plan and
says "walk it chunk by chunk". The agent is told its brief is authoritative and that
nothing in the launch message replaces it — so it would read the whole diff the
`--chunk` design exists to spare it. The brief and the launch prompt disagreed on the
one thing the feature is about.

`diffReadingBlock` now takes an optional chunk id and, when given one, reads that
chunk alone — the same range the launch prompt reads — and drops the "walk it chunk
by chunk" instruction. `buildRoleBrief` threads the chunk through, exactly as an
invariant agent's brief is already scoped to its one file.

Which role may be launched per-chunk is now declared on the brief (`acceptsChunk`),
not hardcoded in the command guard as `role !== 'reverse-audit'`. A new per-chunk
role is a data change in agent-briefs, and the guard reads the same field the brief
builder does. Tests added for the fix and the coverage the review flagged: the
scoped brief reads one chunk not all; the handler accepts the one legal role+chunk
combo and keys its record `reverse-audit--chunk-N`; a non-existent chunk id is
rejected by name rather than emitting an unusable read.

* fix(review): finish the acceptsChunk story and de-duplicate the diff-window math

A second reverse-audit pass — the /review skill run on the previous commit — found
four gaps in it. All four are addressed here.

The guard was made data-driven (`!BRIEFS[role]?.acceptsChunk`) but its error message
still said "only for reverse-audit". If a second role ever sets `acceptsChunk`, the
guard would allow it while the message denied it. The message now names the set it
read from the briefs, so it cannot drift from what the guard enforces.

The record key derived from `--file` with no guard on it, while `--chunk` was guarded.
`--role reverse-audit --chunk 14 --file foo.ts` was accepted and keyed
`reverse-audit--foo.ts` — a file the agent never reads, colliding with and masking a
real file-keyed record. `--file` is the invariant agent's one scoping input; it is now
rejected on any role that is not an invariant agent, and on `--whole-diff`, closing the
asymmetry with the `--chunk` guard.

The 1-based-line-range → `{offset, limit}` arithmetic (`startLine - 1`,
`endLine - startLine + 1`) was written out at five sites. An off-by-one fix, or a
change in how `read_file` windows, would have had to land in all five. It is now one
`diffWindow(startLine, endLine)` helper the five call.

Tests: the message names the derived set; `--file` is rejected on a non-invariant role
and on `--whole-diff`; and the verify role — the one new role whose full handler path
was untested — is driven end-to-end through the handler, keyed `verify`, with the
verdict branch of its brief (Exclusion Criteria, no finding format).
2026-07-15 12:02:29 +00:00
yuanyuanAli
60a9ec2a4e
feat(web-shell): add zoom, pan and drag controls to Mermaid diagrams (#6881)
* feat(web-shell): add zoom and pan controls to Mermaid diagrams

* fix(web-shell): bind Mermaid drag events to window and address review nits

- Move mousemove/mouseup listeners from wrapper div to window via
  useEffect, preventing drag cancellation when cursor exits the
  container during fast pans (Critical from review)
- Clamp Y-axis offset to ±1500px to prevent dragging into the
  overflow-y: hidden clipped region; X-axis remains unclamped
  since overflow-x: auto provides native horizontal scrolling
- Remove unused wrapperRef (dead code)
- Merge duplicate handleZoomReset/handleDoubleClick into shared
  resetZoomAndPan callback
- Add missing title attribute to zoom reset button for tooltip
  accessibility parity with zoom-in/zoom-out buttons

* fix(web-shell): address review round 2 — drag state, max-width, code change reset

- resetZoomAndPan now clears dragRef and isDragging, preventing stuck
  grab cursor when user alt-tabs or loses focus during an active drag
- Add window blur listener in drag useEffect to cancel drag on focus
  loss (alt-tab, browser notification, DevTools stealing focus)
- Add flowchart.useMaxWidth: false in mermaid.initialize so the CSS
  max-width: none rule actually takes effect (Mermaid v11 stamps an
  inline max-width via useMaxWidth:true by default, overriding CSS)
- Reset zoom and offset when code prop changes (diagram regenerated)
  via a dedicated useEffect keyed on code; theme-only toggles preserve
  the user's current zoom/pan state

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-15 11:10:36 +00:00
qwen-code-dev-bot
bad7d6ba10
fix(cli): isolate submit tests from inherited QWEN_CODE_SESSION_ID (#6940) (#6944)
The submit.test.ts posting-gate tests pass a test-only skillArgs path,
which authorization() honours only when currentSessionId() is empty.
CI sets QWEN_CODE_SESSION_ID, so every skillArgs-based test read the
session-scoped path instead — which never existed — and the gate
refused every post.

Save and clear QWEN_CODE_SESSION_ID in beforeEach, restore in
afterEach, matching the pattern the session-id test already uses in
its own try/finally.

Co-authored-by: Qwen Autofix <autofix@qwen-code.dev>
2026-07-15 10:31:13 +00:00
morluto
89ab15d2f1
fix(core): roll back failed continuation attempts (#6921) 2026-07-15 08:36:17 +00:00
ytahdn
14993b1cf3
feat(daemon): add immutable session source metadata (#6932)
* feat(daemon): add session source metadata

* test(daemon): update baseline capabilities

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-07-15 07:35:41 +00:00
Shaojin Wen
35c9186441
fix(review): prove the diff was read, build every agent's prompt, and compute the verdict (#6892)
* fix(review): prove coverage on both topologies, and check the prompt survived the trip

Three defects, all measured against the harness's own transcripts of real /review
runs against QwenLM/qwen-code PRs #6766 (Step 3B) and #6579 (Step 3A).

1. Step 3A reviews were told nobody had read them.

   Coverage was attributed by one question: did an agent whose launch prompt says
   `chunk N of M` make a successful tool call? No Step 3A prompt says that — there
   every dimension agent walks the whole diff — so no chunk was ever attributed to
   anyone. Run against a real 3A review whose twelve agents each opened the diff,
   walked both chunks and filed findings, check-coverage returned

     Coverage: 0/2 chunk(s) reviewed. 16 agent(s) ran; 16 did work
     ERROR: 2 chunk(s) were not reviewed — 1, 2. Nobody read those lines.

   in one breath. compose-review runs the same computation on the way to the
   verdict, so a flawless small-PR review was capped away from Approve and the
   body it would have POSTED to the pull request said nobody had read it. Step 3A
   is the topology most pull requests get. The only reason it never blew up is
   that check-coverage lived inside Step 3B and was never reached from 3A — two
   bugs cancelling.

   Coverage is now the intersection of two things the harness wrote down: the
   lines each agent was pointed at (its launch prompt) and the fact that it opened
   the diff (a successful tool call naming the diff file). Topology-blind. It also
   no longer credits a chunk to an agent on the strength of any successful call —
   a glob for test files was enough.

2. The whole-diff agents were still launched blind.

   agent-prompt built the territory agents' prompts and left the other half of the
   fan-out to prose. All three whole-diff agents of the 3B run — cross-file tracer,
   test-coverage matrix, build & test — got a prompt naming no diff file at all.
   The test-coverage matrix was told to "Read the diff chunks" and given no path to
   read them from; it read the post-change source instead, which on a deletion
   shows it nothing. These agents own the classes a chunk agent is structurally
   blind to, and the gate could not see it: it only asked that question of agents
   whose prompt said `chunk N of M`.

   `agent-prompt --whole-diff` now builds their diff-reading block too.

3. The prompt the CLI built was rewritten on the way to the agent.

   The 3B run invoked agent-prompt correctly for all five chunks and then
   paraphrased what it printed: the delivered prompt dropped the rule against
   reciting a stock sentence, dropped the half-read warning, replaced the project's
   review rules with a three-sentence summary of its own, and invented an
   instruction that was never in the original. Nothing could see it, because a
   paraphrase keeps the diff path. So agent-prompt records what it emitted, at a
   path derived from the plan that the caller is never given; check-coverage reads
   it back against the launch prompt the harness recorded. Replayed against that
   run's real transcripts, all five chunk agents are now named.

Verified end to end: a fresh /review of #6829 (3A) calls `agent-prompt
--whole-diff`, passes it verbatim, and Step 3D reports 2/2 chunks reviewed,
12 agents, 12 did work — a gate that path could not reach before, and could not
have passed if it had.

* fix(review): build every agent's prompt, from a roster the plan derives

Two more failures, both measured against the harness's own transcripts of real
/review runs.

1. Agent 0 was never launched, and nothing could tell.

   The skill says issue fidelity runs on every PR review. Dogfooded on #6766, it
   did not run — and every check passed, because every check asks a question of an
   agent that RAN. An agent that does not run leaves no transcript to ask. An
   omission is invisible precisely because it is an omission.

   So `check-coverage` now derives a roster from the plan — which the caller does
   not write — and names every required agent that never ran, with the exact
   `agent-prompt` call that builds it. The plan already knew everything the roster
   turns on: the topology, whether the diff deletes anything, which files were
   rewritten heavily enough to need invariant agents, whether there is a worktree
   to build in and a pull request to check an issue against.

   `agent-prompt --role <role>` builds all of them: 0, 1a, 1b, 1c, 2, 3, 4, 5,
   6a/6b/6c, 7, the test matrix, and the three invariant agents per heavy file.
   The briefs move out of SKILL.md and into code, because a brief the orchestrator
   retypes is a brief that drifts.

2. A 4 652-character prompt is not a thing an orchestrator will paste twelve times.

   With the briefs welded into the launch prompt, the first dogfood delivered
   **2 893** characters of one: it kept the head, added a preamble of its own, and
   cut nineteen hundred characters out of the middle. The delivery check caught it
   — and the run then read the check's exit-3, concluded "the agents clearly did
   their job", skipped `compose-review`, and filed an **Approve it had written
   itself**. A gate that always fails is a gate that gets talked around.

   So the brief goes where the diff already goes: on disk, read by the agent that
   needs it. The launch prompt drops to ~500-800 characters — it names the role,
   points at the brief file, and lists the diff reads — and whether the agent
   actually read its brief stops being a hope and becomes a tool call the harness
   wrote down (`unreadBriefs`).

Verified end to end. A fresh /review of #6847 built all twelve role prompts, and
against the harness's transcripts: 12 of 12 delivered **byte-for-byte verbatim**,
12 of 12 **opened their brief**, 3-19 successful tool calls each — Agent 0 among
them. Step 3D: `1/1 chunks reviewed, 12/12 agents did work`, no errors.

* fix(review): the verdict is computed, not carried

`compose-review` has computed the event and the body since the C/S table stopped
being prose. The skill then told the orchestrator to "copy event/body verbatim
into the review JSON" — a transcription, into a document the model writes, of a
decision the CLI had already made. That is the exact anti-pattern `submit`'s own
header repudiates, and it left two ways for a run to author its own verdict:

  - **The terminal.** Step 6's verdict was composed by the model, from prose
    rules. Dogfooded, a run read the coverage check's refusal, concluded that
    "the agents clearly did their job", never called `compose-review` at all, and
    printed `Review complete — Approve` on a review whose gate had just refused.

  - **The wire.** `submit` took `{event, body}` as fields. Nothing stopped a run
    that had skipped the computation from posting the conclusion it preferred.

So `submit` composes. It takes the findings — the inline comments and the states
Step 6 established — and derives everything that follows, including how many
blockers there are: `criticalsInline` and `suggestionsInline` are counted off the
`**[Critical]**` / `**[Suggestion]**` prefixes of the comments actually attached,
not accepted as numbers beside them. (A number beside a list is a number that can
disagree with the list, and one did: the breaching run posted a body reading
"Suggestions are inline" next to an empty `comments` array and a summary claiming
`0 Suggestion inline`.) A payload carrying `event`/`body` is refused rather than
silently overruled — the caller was trying to author a verdict.

Two body checks are deleted, not weakened: a body that promises inline comments it
does not carry, and a body whose footer is preceded by a literal `\n`. Both were
checks on a string the caller built. The caller no longer builds it.

`compose-review` now prints the verdict line itself, and Step 6 prints that. There
is one place a verdict exists; skipping the command does not get you a different
one, it gets you none.

Verified: a payload with `event: APPROVE` and an unreviewed dimension is refused
at the wire; the same findings without a verdict compose to `COMMENT`. A fresh
/review of #6788 called `compose-review`, was told `Verdict: Comment`, and showed
the user "Comment — downgraded from Approve (CI failing: route)" — the presubmit
downgrade applied by code, on a run that did not post.

* fix(review): make Step 3B carryable, and stop the delivery check crying wolf

Dogfooding the Step 3B path — the one topology none of this had been run against
— found two defects, and the second is the more important of the two.

1. Eighty-seven kilobytes of chunk prompts, in one response.

   The briefs moved onto disk for the dimension agents and not for the territory
   agents. Measured on PR #6606 (5 511 diff lines, 17 chunks): 17 chunk launch
   prompts of ~5 149 characters each — **87 546 characters** the orchestrator was
   expected to paste unedited. At a twelfth of that load it had already cut
   nineteen hundred characters out of a single prompt.

   Chunk agents get the same split: the brief on disk, and a launch prompt that
   carries only what cannot live anywhere else — `chunk N of M`, which attributes
   the territory, and the `offset`/`limit`, which are the lines coverage proves
   were delivered. 87 546 → 14 789 characters. And `check-coverage` now asks the
   territory agents the same question it asks the others: did you open your brief?

2. The delivery check failed a correct run — all nine agents of it.

   It was a substring test: the built prompt had to appear in the launch prompt,
   contiguously. That is a stricter claim than the skill makes, and both of the
   differences it fired on were legitimate. The orchestrator had inserted **the
   one-sentence summary of the change that the skill explicitly tells it to add**,
   which breaks contiguity by construction — and it had reflowed a hard-wrapped
   sentence onto one line, which changes not one character of meaning.

   This is the failure this skill keeps re-learning, and this time it was ours: a
   gate that fires on a correct run is a gate that gets talked around, and there is
   a dogfood transcript of a model doing exactly that. The rule the check enforces
   is now the rule the skill states — **you may add; you may not remove, alter, or
   reorder** — over whitespace-collapsed lines, in order.

Verified against the harness's transcripts of a real Step 3B review of #6766: nine
agents (five chunks, issue fidelity, cross-file tracer, test matrix, build & test),
9/9 delivered intact, 9/9 opened their brief, 6-22 successful tool calls each.
`check-coverage`: 5/5 chunks, every list empty, exit 0.

* feat(review): path-scoped rules; and move the briefs out of the skill, where they were never reaching the agents

Two changes, and the second found a hole the first would not have.

1. Rules that attach to a path, not to a dimension.

   The nine dimensions are domain-blind by design — "find security bugs" is a lens,
   not a syllabus — and that holds until a file's failure modes are not guessable
   from reading it. A GitHub Actions workflow is the clearest case: it is YAML, so it
   reads as configuration, and the reviewer who treats it as configuration misses
   every one of its attack classes. Nothing in this review knew to ask whether a
   `pull_request_target` job checks out the contributor's head — which is the
   difference between a CI file and a remote code execution with the repository's
   write token. This repo runs `qwen-autofix.yml`, which posts to pull requests.

   `agent-prompt` now appends a checklist for such a file to the brief of every
   code-reviewing agent **whose territory actually contains one**. Scoped, because a
   rule that fires on every review is a rule that gets skimmed. `/review` runs on
   other people's repositories, so the calibration matters as much as the content:
   the blockers are the six that are unambiguously wrong; the two that shade into
   taste (SHA-pinning, `permissions:`) are Suggestions, exempt the conventions almost
   everyone keeps, and are scoped to lines the diff touches. No style rules — a
   linter owns those, and the Exclusion Criteria already forbid them.

2. The briefs move out of SKILL.md — and three things turned out never to have
   reached an agent at all.

   The briefs have been built in code since the roster landed, and SKILL.md still
   carried 38 KB of the same prose. Duplication is drift, and a 178 KB skill is
   ~45 000 tokens in the orchestrator's context on every review — which is itself a
   cause of the failure this whole line of work has been chasing. The skill now keeps
   what each agent is *for* (a table) and drops what it is *sent* (the command's copy
   is the one that arrives).

   Doing that surfaced what the code briefs were missing, because the deleted prose
   had to go somewhere:

   - **The Exclusion Criteria had never reached an agent.** The skill states them at
     the end of the document and tells the orchestrator to "apply" them. The agents
     do not read the document. The single largest precision control in this review
     has been governing nobody, in every run, since it was written.
   - **Nor had the anchor rules.** Agents were asked for a snippet and never told
     what makes one resolvable: prefer added lines, a removed line cannot be anchored
     at all, a bare `}` matches everywhere. `resolve-anchors` was downstream of a
     snippet nobody had given the rules to produce.
   - **Nor the severity calibration.** `SEVERITY`'s own comment warns that a chunk
     agent owns test coverage with nothing to calibrate it and will file "zero test
     coverage" as Critical — and then did not include the calibration.

   All three are in the briefs now. And two degradations the orchestrator used to be
   told to add by hand — and can no longer add, because it does not write these
   prompts — are applied by the builder: in cross-repo lightweight mode there is no
   tree, so 1b and 1c report at `Confidence: low` rather than asserting a
   re-establishment is missing. A false Critical blocks a merge.

   Step 3C (the medium-effort inline pass) now *loads* the briefs it needs rather
   than carrying them: same text as the high-effort agents get, read when that level
   actually runs instead of sitting in every review's context.

SKILL.md: 171 178 → 153 656 bytes.

* fix(review): scope an invariant agent to its own file, and let a blocker's blast radius be part of the blocker

Two corrections, both from dogfooding the paths that had never been run.

1. An invariant agent was being handed the whole chunk plan.

   It owns one heavily-rewritten file. Its brief says so, and gives it that file's
   own slice of the diff. Its *launch prompt* listed every chunk in the review —
   on PR #6457, all twenty-one reads of a 6 149-line diff, for an agent whose job
   is one file.

   The wasted reading is the smaller half. Coverage is computed from the ranges in
   the launch prompt, so an invariant agent was being credited with having read
   **every chunk in the review**. One of them could have masked twenty missing chunk
   agents. It now gets exactly its file's `diffRange`, and nothing else.

2. `permissions: write-all` on a job that runs untrusted code is not a Suggestion.

   The path rule said it was. Dogfooded against a planted vulnerability, the security
   agent read that and escalated anyway: "grants maximum token scope to a job that
   processes untrusted contributor code, amplifying the RCE above". It was right and
   the flat rule was too coarse. A broad token on a privileged job is not a separate
   recommendation — it is how far the blocker reaches, and it belongs in that finding,
   at Critical. On an ordinary job it stays a Suggestion.

* fix(review): the six findings this skill filed against its own pull request

The repository's own `/review` bot reviewed #6892 — this change reviewing the code
that changes it — and filed six Suggestions. Every one of them is real, and two are
fail-open holes in the gates this pull request exists to build. They are fixed here,
each with a test.

- **`submit` accepted `state: null`.** `=== undefined` is not `== null`, so the
  structural check passed it; `compose`'s `?? {}` then collapsed it to an empty state
  and would have posted a review whose footer named no model and whose caps came from
  nowhere.

- **`wasDeliveredVerbatim` was vacuously true for an empty `built` prompt.** A
  zero-byte record is what a partial write leaves behind — and `recordPrompt` swallows
  its write errors by design, so this is reachable. `readRecordedPrompts` stores it as
  `''`, not `undefined`, so the "no prompt was built" guard did not catch it, and the
  loop's body never executed. The roster would have credited a required role to
  whichever transcript it looked at first. It now fails closed.

- **A chunk read across two pages got no credit.** The check asked for a *single*
  range containing the chunk, and reads of 1-200 and 201-400 are two — so it
  contradicted the paging instruction the same review had just given, on exactly the
  oversized chunks where paging is not optional. Ranges are coalesced first.

- **Agent 7 was handed relative paths it could not resolve.** `worktreePath` and the
  plan path are repo-relative in the report, and Agent 7's working directory *is* the
  worktree — so `--worktree .qwen/tmp/review-pr-6457` resolved to
  `<worktree>/.qwen/tmp/review-pr-6457`, which does not exist. This was already
  visible and nobody had read it: in the 29-agent dogfood run, Agent 7 spent its time
  running `find … -name "*6457*fetch*"`, hunting for a plan it had been handed a path
  to. Absolute now.

- **`removePromptRecord` was dead code with a comment claiming a caller it did not
  have.** `cleanup.ts` sweeps the prompt directory by prefix instead. Deleted.

- **`--dry-run` omitted `cappedBy`.** The point of a dry run is to see what would be
  posted; `"event": "COMMENT"` with no reason leaves the reader to guess why the
  Approve went away.

* docs(review): purge the stale event/body payload examples from Step 7

The reviewer caught a real contradiction it filed as Critical: submit.ts now
refuses a payload carrying `event`/`body` (those are computed from `state` and
the attached comments), but Step 7's main-path 'Build the review JSON' examples
still showed `"event": "REQUEST_CHANGES"` / `"body"` and routed the verdict
through a copy-it-verbatim step. An orchestrator following the unchanged
instructions would have built exactly the payload submit rejects.

The correct `{commit_id, comments, state}` shape existed lower in the section (the
no-findings branch), added when submit took over composition — but the main-path
examples and the compose-review-then-transcribe bullets above them were never
reconciled. They are now: one payload shape, no verdict in it, `state` handed to
submit, and the inline counts derived from the comments rather than supplied.

Found by the repository's own /review on #6892.

* fix(review): the three findings from the third self-review

The repository's /review passed #6892 (no blockers) and filed three Suggestions.
All three are real; two are contradictions this PR itself introduced.

- **verdictLine printed a dangling colon.** When a would-be Approve was taken away
  by a presubmit downgrade ALONE — no cap state, `cappedBy` empty, `downgraded`
  true — the code joined the empty array and produced 'an Approve was NOT
  available:  — downgraded by a presubmit check', a colon over nothing. It now
  collects the reasons (a cap and a downgrade are both reasons, either can be the
  only one) and prints the clause only when there is a reason to. The function had
  no test; it has six now, including this case.

- **Step 3D said 'six failures' and listed seven, while check-coverage reports
  eight.** The count drifted as failure classes were added, and the uncoverable-chunk
  class had no bullet at all. Now 'eight', with the missing bullet written.

- **`submit --review` help still advertised `event` / `body`** as payload fields,
  which the same command now refuses. Updated to `commit_id / comments / state`.

Found by the repository's own /review on #6892 — the third pass, the one that
turned CHANGES_REQUESTED into no-blockers.

* fix(review): a heavy file in a Step-3A diff must not demand invariant agents

From a human review of #6892 (doudouOUC). `heavy` is decided independently of
topology (lib/heavy.ts): a ~300-line source file with ~120 changed lines clears
the rewrite-ratio branch while srcDiffLines stays under 500 — a Step 3A review.
The invariant-agent loop in requiredAgents ran in both topologies, so it added
invariant-a/b/c to the roster of a 3A review that never launches them; check-coverage
then reported them as missingRoles and exit-3'd, and compose-review capped the
verdict — an otherwise-complete small PR, falsely blocked.

Gate the loop on isTerritoryFanOut. Step 3A's dimension agents each walk the whole
diff, so one already sees both ends of a rewritten file; invariant agents are a 3B
mechanism for when the diff is carved into territories and no single agent holds
the whole file. roster.test.ts now pins the 3A-heavy case.

Also, same review: merge() in coverage.ts copied its first tuple and pushes copies,
so it no longer mutates a tuple owned by rec.diffReads (harmless today, pure now).

* fix(review): a downgraded Request changes must not read as a plain Comment

Fifth self-review, one behavioural finding among five (the rest are test/doc).

verdictLine printed 'Comment — downgraded by a presubmit check' for BOTH a
Suggestion-only Comment the presubmit moved and a REQUEST_CHANGES it moved down to
Comment. The second is a review with confirmed Criticals posted inline, and
'Comment — downgraded' reads to an operator as 'nothing blocking'. It could not
tell them apart from baseEvent alone — a cap may already have softened the RC
before the downgrade ran — so ComposeReviewResult now carries downgradedFrom, and
verdictLine says 'Request changes, downgraded to Comment … (the blockers are still
posted)' for that case. Six verdictLine cases now, including this one.

Also from the same review, all confirmed:
- agent-prompt.test.ts: the describe block named a function that was renamed
  (buildRolePrompt -> buildRoleBrief), and the mode-rejection it.each covered 2 of
  the invalid combinations, not the role-mode ones; now covers all five and drops
  the stale 'two modes' wording.
- SKILL.md Step 7: the review-JSON example used a /* */ comment inside a ```json
  fence (not valid JSON); switched to ```jsonc with a // comment.

* fix(review): eight review-round fixes atop the Step-3A invariant resolution

Follows 7c499d193, which resolved the doudouOUC roster finding (a heavy file in
a Step-3A diff must not demand invariant agents — gate the loop on the
topology) and the merge() purity nit. This carries the rest of the same round:

- `roster.ts` requires Agent 0 only for a positive PR number. `!== undefined`
  let `null`/`0`/`''` through. Note the reviewer's suggested `typeof === 'number'`
  is wrong for this codebase — `fetch-pr` writes the number as a *string* — so
  the guard accepts a numeric string too, or every real PR review would lose
  Agent 0. A table test pins both directions.
- `transcripts.ts` matches the diff path as a whole JSON string value, so
  `…/diff.txt.bak` no longer counts as reading `…/diff.txt`. It also documents
  why FIFO is right for a chronological transcript.
- `agent-prompt.ts` scopes path rules to `--file` only for invariant roles — a
  whole-diff reviewsCode agent passed `--file` would otherwise lose the rules
  for every other file — and guards each chunk element in `diffReadingBlock`
  like `chunkFrom`, so a corrupted chunk errors legibly instead of emitting
  `offset=NaN`.
- `compose-review.ts` stops double-wrapping `cov.missingRoles` /
  `cov.rewrittenPrompts`, which coverage.ts already writes self-explanatory.
- `agent-briefs.ts` JSDoc said "Two do not" read the diff; only Build & Test
  does not.
- The agent-prompt size-bound test now covers `test-matrix`.

The empty-prompt guard, the paged-read coverage, the verdictLine dangling-colon
and the submit help text were all already handled by earlier commits on the
branch; those threads are answered without a code change.
2026-07-15 06:49:23 +00:00
Shaojin Wen
02c79beb62
feat(web-shell): auto-post visual previews (screenshots + flow GIFs) on PRs (#6880)
* feat(web-shell): auto-post visual previews (screenshots + flow GIFs) on PRs

PRs that touch the web-shell UI now get an auto-updated comment with
light/dark screenshots of key views (transcript, slash menu, model/theme
dialogs, permission panel) and short GIF recordings of common flows,
rendered against the existing mock daemon — no real backend, no secrets.

Split into two workflows for security, since capture runs untrusted PR code:

- web-shell-visuals.yml (pull_request): checks out the PR head, builds and
  renders it with Playwright, captures PNGs + webm, converts webm->GIF with
  ffmpeg, and uploads an artifact. `contents: read` only, references no
  secrets — fork PRs run with a read-only token and no secrets.
- web-shell-visuals-publish.yml (workflow_run): downloads the artifact,
  binds it to its real PR by requiring the PR head SHA to equal the run's
  authenticated head SHA, hosts the images on a per-PR `pr-assets/*` branch
  (referenced by immutable commit SHA), and posts/updates one inline
  comment. Never checks out or runs PR code; the write token lives only here.

Capture infra is self-contained in packages/web-shell
(playwright.visuals.config.ts + client/e2e/visuals/*), reusing the mock
daemon harness. Run locally with:
`npm run test:e2e:visuals --workspace=packages/web-shell`.

* fix(web-shell): guard empty gh api response in visuals publish

Addresses review feedback on #6880: if `gh api` returns empty (network
error / rate limit), jq on empty stdin errors and `set -e` kills the
publish job. Skip gracefully instead.

* fix(web-shell): address review nits on visuals capture

- harness recordFlow: wrap video saveAs/delete in try/catch so a video
  I/O error (e.g. drive failed before navigation) can't mask the real
  driveError.
- capture workflow: drop the unused head_sha.txt artifact field; the
  publish job binds to the authenticated workflow_run.head_sha, and an
  artifact-sourced SHA would be untrusted.

* fix(web-shell): address second review round on visuals capture

- context.close() in recordFlow's finally is now best-effort (try/catch)
  so a close/crash error can't mask the real driveError.
- add a flows spec that asserts a throwing drive propagates its own error.
- trigger the capture workflow on playwright.visuals.config.ts changes too.

* fix(web-shell): address third review round on visuals capture

- harness: log (don't silently swallow) a video save/null when drive
  succeeded; keep masking-suppression only when driveError is set.
- publish: HTML-escape interpolated values in the comment builder (defense
  in depth, independent of the upstream filename sanitization); fix the
  stale 'single pr-assets branch' comment and key concurrency on source
  repo+branch so different PRs (incl. same-named fork branches) parallelize.
- capture: bump checkout to v6.0.3 (repo standard); surface ffmpeg's stderr
  on GIF-conversion failure instead of discarding it.

* fix(web-shell): harden visuals publish/capture (review round 4)

Publish (privileged workflow_run):
- CRITICAL: capture basename before `tr` so its trailing newline isn't
  turned into `_` (which broke the .png/.gif filter -> empty preview).
- dedup only against the bot's OWN comment (author + marker), not any
  marker-bearing comment a participant can post.
- bound the pr-assets branch: force-push a single orphan snapshot per run
  (previous snapshot GC'd) instead of appending unbounded untrusted content;
  this also removes the rebase/retry path.
- cap EXAMINED candidates (not just accepted) before validation; tighten
  per-file (3MiB) and accepted-image (14) caps.
- re-validate PR open + head-SHA immediately before the comment write
  (TOCTOU); retry the comment listing and abort rather than POST a duplicate
  when listing fails.
- esc() the runUrl for consistency with the self-defending HTML.

Capture (pull_request):
- upload raw recordings as a SEPARATE artifact the publisher never downloads,
  so an untrusted multi-GB video can't exhaust the privileged job.
- also trigger on packages/webui/src and packages/sdk-typescript/src (the
  visuals dev server aliases them).
- create screenshots/gifs dirs before the metadata counts (defensive).

Harness recordFlow:
- track drive failure with an explicit boolean (handles `throw undefined`);
  discard the recording on failure so a failed flow leaves no bogus webm.

* refactor(web-shell): extract + unit-test the visuals publish staging/comment

Addresses the review's testability gap (the class of bug that let the
filename sanitizer break the whole preview slip through green CI). The image
validation (magic bytes, filename sanitization, examined/accepted/size caps)
and the comment builder (light/dark pairing, flow labels, HTML escaping) move
from inline workflow bash/node into .github/scripts/web-shell-visuals-publish
.mjs, covered by web-shell-visuals-publish.test.mjs (run in ci.yml's
node --test line). The publish workflow sparse-checks-out and calls the
script instead. Behaviour is unchanged; it just gained a test surface.

* fix(web-shell): retry the visuals asset force-push; drop stale comment

Round-4 switched hosting to a force-push but left a comment referencing a
'push-retry loop' that no longer existed, and the force-push was a single
call that set -e would abort on a transient failure. Add a bounded retry and
correct the comment.

* fix(web-shell): harden visuals publish/capture (review round 6)

Script (unit-tested):
- flow labels: own-property lookup so `toString.gif`/`constructor.gif` can't
  leak Object.prototype members into the comment.
- per-kind image caps (screenshots vs gifs) so a large screenshot set can't
  silently starve the flow GIFs from the preview.
- tests for both, plus the per-kind cap.

Publish:
- bind the artifact PR number to the run's authenticated head repo+branch
  (not just head SHA), rejecting a sibling PR that shares the same commit.
- re-validate before the force-push and again right before the comment write
  (close the download/stage/lookup TOCTOU windows).

Capture:
- bound artifact contents before upload (drop oversized / excess files) so an
  untrusted spec can't bloat the published or video artifact.
- trigger on the capture workflow file itself.

- new close-trigger cleanup workflow deletes a PR's asset branch on close, so
  pr-assets/* refs don't accumulate without bound.
- single-source the capture viewport (constants.ts) shared by config + harness.
- model-switch flow asserts the daemon model request actually fired.

* fix(web-shell): stricter visuals error handling (review round 7)

Harness recordFlow:
- when the drive SUCCEEDS, a failed context.close() or video.saveAs() (or a
  missing recording) now FAILS the flow instead of a swallowed console.warn —
  a silent pass with no .webm makes the downstream GIF step fail confusingly.
  A drive FAILURE still discards the partial video and rethrows the original
  error (unchanged).

Publish:
- validate_pr distinguishes a transient API failure (empty after retries ->
  exit 1, re-triggerable) from a genuine invalid state (closed / head mismatch
  -> skip), via a `gate` wrapper used at all three checkpoints.
- add a 2s backoff between comment-listing retries (matching the push retry).

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-15 06:48:52 +00:00
易良
ae5516b90d
fix(web-shell): restore portal root hook import (#6934)
Resolves #6933
2026-07-15 05:48:15 +00:00
jinye
9cb09f4e2e
fix(core): Classify shell timeouts as tool errors (#6864)
* fix(core): classify shell timeouts as tool errors

Refs #6863

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

* codex: address PR review feedback (#6864)

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

* fix(core): Report no output for sed timeouts

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

---------

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-07-15 05:34:39 +00:00
顾盼
24b2442875
feat(cli): add /learn command for user-initiated skill creation (#6440)
* feat(cli): add /learn command for user-initiated skill creation

Add a /learn slash command that lets users explicitly point the agent at
knowledge sources (local dirs, URLs, conversation history, or freetext)
to create reusable skills.

Implementation uses submit_prompt (same pattern as /remember) to let the
main model gather knowledge and write a SKILL.md in the normal turn.
Skills are saved under .qwen/skills/learned-skill-<name>/ with
source: learned frontmatter for edit isolation from auto-skills and
user-authored skills.

Also includes a forked-agent path (runLearnSkillByAgent) with scoped
permissions for future ACP/auto-learn scenarios, plus 29 unit tests.

* fix(i18n): add zh/zh-TW translations for /learn command description

CI strict-parity check requires all built-in command descriptions to
have translations in zh-CN and zh-TW locales.

* fix(i18n): add en.js baseline key for /learn command description

The check-i18n script requires all translated keys to also exist in the
English baseline locale file.

* refactor(core): remove unused forked-agent infrastructure, add collision guard

Address review feedback on PR #6440:

- Remove ~300 lines of unused forked-agent code (runLearnSkillByAgent,
  createLearnSkillScopedAgentConfig, scoped permissions, system prompt,
  buildLearnTaskPrompt) — no callers in production code.
- Make buildLearnSkillPrompt async and add listExistingSkillDirNames
  enumeration to prevent skill name collisions.
- Revert buildAgentHistory export (no longer needed).
- Trim test suite from 29 to 10 tests (dead code tests removed).

* feat(config): default auto-skill to disabled

With /learn available for explicit skill creation, auto-skill (background
skill extraction after 20 tool calls) is no longer needed as the default
experience. Users who want automatic extraction can re-enable it via
memory.enableAutoSkill in settings.

* feat(config): align auto-skill default to disabled across all layers

The prior commit only changed the core Config constructor default.
CLI config builder, ACP agent defaults, and settings schema still
defaulted to true, making the core change unreachable for CLI users.

Align all layers:
- cli/config.ts: ?? true → ?? false
- acpAgent.ts: enableAutoSkill default true → false
- settingsSchema.ts: default true → false
- read-file.ts: resolve stale merge conflict markers

* chore: regenerate settings.schema.json for auto-skill default change

* test: update tests for auto-skill default change, fix stale conflict resolution

- config.test.ts / acpAgent.test.ts: update assertions for the
  enableAutoSkill default flip (true -> false) from the prior commit.
- read-file.ts: an earlier rebase conflict resolution incorrectly kept
  os.tmpdir() as an allowed read root — it was removed on main. Restore
  the main version so read-file.test.ts's "ask for OS temp paths" case
  passes again, and drop the now-unused os import.

* fix(core): restore dynamic ignore-file message, wrap /learn input as opaque data

- read-file.ts: an earlier rebase conflict resolution incorrectly hardcoded
  the ignore-file error message to '.qwenignore', losing the dynamic
  getQwenIgnoreFileDisplayForPath() call that reports the actual file
  (.agentignore, .cursorignore, etc.) that blocked the read. This was the
  real cause of the read-file.test.ts CI failures — not a pre-existing bug
  as previously assumed.
- learn-skill-agent.ts: wrap the /learn command's raw user input in
  <user_data> tags with an explicit "don't follow instructions" preamble,
  matching the existing <user-content> pattern in remember.ts. Since
  buildLearnSkillPrompt feeds submit_prompt (full tool access), unwrapped
  content fetched from a URL could otherwise redirect tool calls.

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-15 05:30:38 +00:00
chinesepowered
d92c3b27ba
fix(webui): route useLocalStorage functional updates through prev state (#6905)
* fix(webui): route useLocalStorage functional updates through prev state

setValue applied a functional updater to the closed-over storedValue instead
of React's previous state, so two setValue(fn) calls batched in one render
both derived from the same base — the first update was lost and the
stale-derived value was persisted to localStorage. Route the update through
the setStoredValue(prev => …) form and persist the committed value. Adds a
jsdom regression test for the batched-update case.

* refactor(webui): persist via effect and guard against throwing updaters

Address review:
- Restore error handling around the functional updater: a throwing
  updater is caught and leaves state unchanged (previously it could
  propagate through render and crash the tree).
- Move the localStorage write into an effect so the state updater stays
  pure (StrictMode-safe), keeping a first-run baseline ref so the initial
  value is never written on mount.
- Add tests for the no-mount-write contract, invalid-JSON hydration
  fallback, and state updating when setItem throws.
2026-07-15 05:03:04 +00:00
易良
808a0e8e1f
fix(core): sanitize standalone closing thinking tags (#6854)
* fix(core): sanitize standalone closing thinking tags

* fix(core): validate sanitized tool calls

* refactor(core): simplify protocol tag sanitization

* fix(core): restrict protocol tag recovery finish

* fix(core): close protocol tag recovery gaps

* fix(core): preserve protocol recovery semantics

* fix(core): close protocol recovery review gaps

* fix(core): harden protocol tag recovery

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-15 04:57:48 +00:00
jinye
ca5019968a
fix(web-shell): harden non-primary archive actions (#6912)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-15 04:57:07 +00:00
morluto
41157205c1
fix(config): reject fractional session and tool-call limits (#6920)
* fix(config): reject fractional session and tool-call limits

* fix(config): validate persisted session turn limits
2026-07-15 04:56:52 +00:00
pomelo
389a1f9ceb
feat(cli): change default approval mode from default to auto (#6899)
* feat(cli): change default approval mode from default to auto

The default approval mode required manual confirmation for every tool
call, producing dozens of confirmation prompts per task. Auto mode uses
a three-layer filter (workspace edits, read-only allowlist, LLM
classifier) to auto-approve safe operations while still guarding risky
ones.

Untrusted folders are still forced to default mode for safety.

Closes #6898

* fix(cli): keep manual approval in safe and bare modes

Restricted modes (safe/bare) strip permissions, allowlists, MCP servers
and hooks to provide a maximally restrictive session. The new AUTO
default fallback was silently downgrading them to the LLM classifier,
contradicting their lockdown intent. Restore DEFAULT (manual approval)
for these modes while keeping AUTO as the default for normal sessions.

Explicit --approval-mode and --yolo flags still take effect, since they
are resolved before the fallback.

* chore(cli): regenerate settings schema for auto default

Regenerate the VS Code settings schema so the tools.approvalMode default
matches the new auto value (fixes the "settings schema is up-to-date" CI
check). Also add coverage for the serve-mode approval fallback when no
approval mode is configured.

* test(cli): update SettingsDialog snapshots for auto approval default

The settings schema now defaults tools.approvalMode to auto, so the
SettingsDialog renders "Auto" instead of "Ask permissions" for the Tool
Approval Mode field. Regenerate the affected snapshots (10 updated).

* test(core): pin DEFAULT baseline in agent-override tests

These tests exercise createApprovalModeOverride isolation and the
DEFAULT→AUTO rule strip/restore transitions, so they implicitly relied
on the Config constructor defaulting to DEFAULT. Now that the default is
AUTO, pin the baseline explicitly so the tests no longer depend on the
constructor default.

---------

Co-authored-by: pomelo.lcw <pomelo.lcw@alibaba-inc.com>
2026-07-15 04:55:32 +00:00
易良
1f6466a50f
fix(core): handle unsigned Claude thinking from proxies (#6893)
* fix(core): handle unsigned Claude thinking from proxies

* docs: remove redundant issue design note

* test(core): cover empty Anthropic thinking signatures

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-15 04:55:12 +00:00
ytahdn
f88a8aa6fc
feat(web-shell): use popovers for composer controls (#6877)
* feat(web-shell): use popovers for composer controls

* fix(web-shell): address popover review feedback

* fix(web-shell): update popover regression coverage

* fix(web-shell): address popover review feedback

* fix(web-shell): stabilize toolbar label collapse

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-07-15 04:54:47 +00:00
han
c6fc0fd6d2
fix(core): include skill results in microcompaction (#6788)
Some checks are pending
E2E Tests / web-shell Browser Regression (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
2026-07-15 03:55:05 +00:00
C0d3N1nja97342
0957e18ea2
fix(cli): apply FETCH_TIMEOUT_MS to /update version check and log fetchInfo results (#6857) (#6887)
* fix(cli): apply FETCH_TIMEOUT_MS to /update version check and log fetchInfo results (#6857)

The FETCH_TIMEOUT_MS = 2000 constant in updateCheck.ts was defined but
never wired up. update-notifier's fetchInfo() takes no timeout option,
so slow/unreachable registries (corporate proxies, offline networks,
scoped .npmrc mirrors without auth) would either hang the check or fall
back to whatever update-notifier internally decides — sometimes a stale
configstore cache, reported by users as '/update reports up-to-date on
0.19.9 when 0.19.10 is available.'

Race fetchInfo() against a bounded timer via Promise.race and surface a
new UpdateCheckTimeoutError when it fires, so '/update' returns the
existing 'error' status instead of silently reporting 'up to date.'

Also log the fetchInfo return value under the UPDATE_CHECK debug tag so
the next round of reports can distinguish 'registry returned the wrong
version' from 'we compared incorrectly' without adding more speculation.

Refs #6857

* fix(cli): carry dist-tag on UpdateCheckTimeoutError and cover nightly timeout paths

Address bot review on #6887:

- `UpdateCheckTimeoutError` now takes an optional `distTag` argument that
  is threaded through by `fetchInfoWithTimeout` and appended to the
  message. The nightly path fires `nightly` and `latest` fetches
  concurrently via `Promise.all`; without a dist-tag on the error, an
  oncall reading logs cannot tell which registry endpoint stalled (e.g.
  a corporate proxy that lets `nightly` through but blocks `latest`).
  The tag also lands on the error instance as a public `distTag` field
  so callers can branch on it programmatically.

- Add two regression tests for the nightly `Promise.all` timeout path:
  a single stalled dist-tag (asserts Promise.all propagates the timeout
  and names the exact tag) and both stalled (full outage — asserts we
  still surface a typed error with a valid tag). The non-nightly test
  now also asserts the message contains `for latest`.
2026-07-15 03:54:35 +00:00
morluto
2132a6142b
fix(mcp): require trust for read-only auto-approval (#6924) 2026-07-15 03:53:13 +00:00
C0d3N1nja97342
1a56193bd5
feat(cli): add general.notificationMode to silence per-approval notifications (#6898) (#6922)
* feat(cli): add general.notificationMode to silence per-approval notifications (#6898)

Users driving many tool approvals in a single task report getting "几十次
弹窗" per session because the current `general.terminalBell` toggle
fires on both WaitingForConfirmation transitions AND long-task idle.
The reporter (and the triage) explicitly wanted a task-complete-only mode.

Add a new `general.notificationMode` enum setting with two values:

- `all` (default): fire on every approval prompt AND on task completion —
  identical to the pre-#6898 behavior. Legacy configs and unrecognized
  values (typos, future keys) also fall back to this to avoid silently
  suppressing notifications.
- `task-complete`: suppress the per-approval notification. The long-task
  idle notification still fires, so users who leave the terminal
  unfocused still learn when a task finishes.

Gate the WaitingForConfirmation branch in `useAttentionNotifications` on
the mode. Register the setting in the acp agent's typed metadata and in
the workspace-settings TUI allowlist so it plumbs through the same paths
as the existing `terminalBell`.

Four regression tests pin: task-complete suppresses approval, task-
complete still fires task-completion, explicit `all` is a no-op vs
unset, and an unknown value falls back to `all`.

* chore(cli): regenerate settings.schema.json for general.notificationMode

CI's 'Check settings schema is up-to-date' step runs
`npm run generate:settings-schema` and diffs — the new enum setting
added in the previous commit was missed. Regenerated.
2026-07-15 03:50:38 +00:00
Aria Zhao
95b7d750ae
fix(cli): don't mutate cached trusted-folders config on preview trust check (#6900)
loadTrustedFoldersWithOverrides() mutated the module-cached
LoadedTrustedFolders singleton via `folders.user.config = trustConfig`.
Callers pass an override to preview trust status for a tentative config
(useTrustModify's updateTrustLevel builds it "to check the new trust
status without writing"), so the unconfirmed config leaked into every
subsequent loadTrustedFolders() read and could be persisted to disk on
the next setValue().

Return a fresh LoadedTrustedFolders with a spread-copied config for the
override case instead of mutating the singleton. Adds a regression test
asserting the cached config is unchanged after a preview override check.

Fixes #6831
2026-07-15 03:46:25 +00:00
jinye
4f4387cf57
feat(core): add PDF vision bridge fallback (#6846)
* feat(core): add PDF vision bridge fallback

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

* codex: address PR review feedback (#6846)

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

* codex: address PR review feedback (#6846)

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

* codex: fix CI failure on PR #6846

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

* codex: address PR review feedback (#6846)

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

* codex: address PR review feedback (#6846)

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

* fix(cli): harden vision bridge output handling

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

* fix(cli): correct export sanitizer test typing

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

* fix(core): disclose selected vision endpoint before egress

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-15 03:42:14 +00:00
BaboBen
4b802ca5f9
feat(channels): support DingTalk webhook delivery to direct messages (#6891)
* docs(channels): design DingTalk webhook DM delivery

* docs(channels): translate DingTalk webhook DM design

* feat(channels): support DingTalk webhook direct messages

* fix(channels): isolate DingTalk webhook DM targets

* fix(channels): handle DingTalk direct delivery failures

* fix(channels): reject malformed DingTalk responses
2026-07-15 03:07:30 +00:00
dreamWB
1f056b7fdc
feat(web-shell): expose session controls to hosts (#6906)
* feat(web-shell): expose session controls to hosts

* fix(web-shell): harden embedded session navigation
2026-07-15 02:31:59 +00:00
chinesepowered
04515e8cae
fix(vscode-companion): don't let a non-boundary @ suppress / completion (#6902)
* fix(vscode-companion): don't let a non-boundary @ suppress / completion

An @ before the cursor unconditionally took priority over /, so an @ that
is not at a word boundary (e.g. inside an email like foo@bar.com) was
selected, failed the boundary check, and short-circuited before the / was
ever considered. Typing 'contact foo@bar.com /he' therefore showed no
slash-command completion.

Extract the trigger resolution into a pure resolveCompletionTrigger() that
only treats an @ as a trigger when it is at a word boundary, otherwise
falling through to a valid /. Adds unit tests for the resolver.

* test(vscode-companion): cover newline @ boundary and non-boundary /

Address review: add coverage for the two untested branches in
resolveCompletionTrigger — a newline as an @ word boundary, and a /
that is not at a word boundary (with no valid @) resolving to null.

* test(vscode-companion): cover @-over-/ priority at word boundaries

Address review: add a test pinning the central design decision — when
both @ and / are at valid word boundaries, @ wins. Without it, swapping
the branch order would pass every other test.
2026-07-15 01:24:33 +00:00
chinesepowered
e096692622
fix(vscode-companion): accurate image-size messages and formatFileSize units (#6904)
* fix(vscode-companion): accurate image-size messages and formatFileSize units

Two small fixes in the paste-image flow:

- The "too large" error hardcoded "Maximum size is 10MB" while the check
  uses MAX_IMAGE_SIZE; derive the message from the constant so it can't drift.
- formatFileSize used ['B','KB','MB','GB'] with an unclamped unit index, so a
  >= 1 TB value read sizes[4] === undefined and rendered "… undefined". Add
  'TB' and clamp the index to the array bounds.

Exports formatFileSize and adds unit tests (including the TB regression).

* refactor(vscode-companion): drop self-evident clamp comment in formatFileSize

Address review: the Math.min(..., sizes.length - 1) clamp is self-evident;
per AGENTS.md, comments default to none unless the why is non-obvious.
2026-07-15 01:18:59 +00:00
morluto
3d06f962df
fix(core): preserve display output for malformed tool results (#6925) 2026-07-15 01:18:04 +00:00
易良
2a57e8b276
fix(test): isolate WeCom temporary files across concurrent CI jobs (#6908)
* fix(test): isolate WeCom temporary files

* test(wecom): preserve real temp root guard
2026-07-15 01:03:08 +00:00
C0d3N1nja97342
82ba1d3e2d
fix(cli): keep exit_plan_mode plan visible inside the pending viewport clamp (#6867) (#6882)
* fix(cli): keep exit_plan_mode plan visible inside the pending viewport clamp (#6867)

The exit_plan_mode confirmation dialog rendered its plan body via
MarkdownDisplay with isPending={false}, which skipped fitPendingSlice.
The full plan then rendered inside MainContent's maxHeight +
overflow="hidden" wrapper (re-added by #6421 as an Ink backstop for the
scroll-to-top lock), and Ink clipped the bottom — silently dropping the
tail of a long plan and, in narrower terminals, the option buttons.

Add an opt-in enforceHeightBudget flag on MarkdownDisplay so callers
that render inside a bounded parent can share the streaming path's
rendered-height slice. Pass enforceHeightBudget from
ToolConfirmationMessage's plan body. Committed non-pending renders
(transcript, tool result markdown, PlanSummaryDisplay) keep the default
uncapped behavior.

Fixes #6867

* fix(cli): render truncation cue when exit_plan_mode plan is clipped

Address bot review on #6882: when `enforceHeightBudget && !isPending`
and the pre-slice actually dropped lines, render a dim single-line cue
naming the count of dropped source lines. Without a visible cue, a
model-authored plan could hide dangerous steps past the viewport budget
and users would approve them blind — the streaming path deliberately
omits the cue because the tail is still on its way, but a complete plan
inside the confirmation dialog has no such promise.

The cue is gated so it only appears in the exact scenario it's needed:
enforceHeightBudget on (only exit_plan_mode opts in today), non-streaming,
and something actually got dropped. Three regression tests pin each gate.
2026-07-15 01:02:28 +00:00
callmeYe
441006b0e1
feat(scripts): add local PR verification gate (#6873)
* feat(scripts): add settings schema check mode

* feat(scripts): add local PR verification runner

* fix(scripts): harden local PR verification

* docs: document local PR verification gate

* fix(scripts): isolate local verification tools

* fix(scripts): scope PR formatting checks

* fix(scripts): skip symlinked PR paths

* fix(scripts): preserve verification gate integrity

* fix(scripts): canonicalize verification temp paths

* fix(scripts): stabilize local PR verification

* fix(scripts): clear built-in test credentials

* fix(scripts): enforce isolated test environment

* fix(scripts): serialize local verification tests

* fix(scripts): address PR verification review

* fix(scripts): preserve review git wrapper environment

* refactor(scripts): avoid step helper shadowing

* fix(scripts): distinguish forwarded child signals

* fix(scripts): preserve relayed signal exit codes
2026-07-15 00:58:17 +00:00
易良
d4c15f05c5
feat(ci): add automated PR failure patrol (#6766)
* feat(ci): add stale failure patrol

* fix(ci): harden failure patrol

* refactor(ci): simplify flaky rerun patrol

* docs(ci): clarify flaky patrol skill boundary

* feat(ci): patrol stale PR failures

* fix(ci): prefilter failed PRs

* fix(ci): isolate patrol classification

* fix(ci): revalidate stale patrol actions

* fix(ci): verify main before branch update

* fix(ci): classify all stale PR failures

* fix(ci): bound patrol batches

* fix(ci): persist patrol failure state

* fix(ci): harden patrol state transitions

* fix(ci): harden stale failure patrol

* fix(ci): continue patrol after expired logs

* fix(ci): tighten patrol guardrails

* fix(ci): preserve failure context in patrol logs

* fix(ci): harden stale patrol closeout

* fix(ci): paginate patrol marker comments

* fix(ci): harden patrol action guards

* test(ci): cover patrol guard rails

* fix(ci): harden patrol marker parsing

* fix(ci): address patrol review followups

* test(ci): cover patrol review edges

* fix(ci): record patrol rerun marker first

* fix(ci): harden patrol review edge cases

* fix(ci): tighten stale failure patrol markers

* refactor(ci): simplify flaky rerun patrol (2838→1258 lines)

- Remove classification guards from actOnDecision (confidence check,
  action enum validation, boundedReason, update_branch multi-guard chain)
- Move classification rules to SKILL.md prompt
- Delete 40 source-code text matching tests, keep 20 behavior tests
- Merge identity job into classify, remove SHA verification
- Change scan sort order from oldest-first to newest-first
- Remove unused functions: writeSkillInputs, failureKey, boundedReason,
  canAct, skillCandidate, mainRunSucceeded

* fix(ci): show gh stderr in top-level error output

When gh CLI returns non-zero exit, execFile rejects with an error whose
.stderr contains the actual GitHub API diagnostic. Previously only one
of stderr or message was shown; now both are printed.

* fix(ci): address patrol review findings

* fix(ci): make stale patrol actions recoverable

* refactor(ci): simplify flaky rerun patrol

* fix(ci): close flaky patrol review gaps

* fix(ci): restore PR failure patrol actions

* fix(ci): harden failure patrol scanning

* fix(ci): address patrol review follow-ups

* fix(ci): harden patrol parsing and coverage

* fix(ci): classify failures against PR changes

* fix(ci): harden patrol script input handling

* fix(ci): remove unsafe auto branch update

* test(ci): exercise patrol action limit

* fix(ci): bind patrol actions to current evidence

* fix(ci): count patrol actions per PR

* fix(ci): redact quoted secret labels

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-15 00:51:11 +00:00
qqqys
fb0239eab4
feat(channels): add structured channel memory management (#6860)
* feat(core): add structured channel memory document

* fix(core): preserve legacy channel memory whitespace

* feat(core): structure channel memory storage

* fix(core): close channel memory races

* test(core): cover channel memory read failures

* feat(channels): parse channel memory item intents

* test(channels): cover memory intent precedence

* feat(channels): manage structured channel memory

* fix(channels): address structured memory review

* feat(cli): wire structured channel memory

* docs(channels): document structured channel memory

* fix(core): harden channel memory persistence

* fix(core): preserve channel memory uniqueness

* fix(channels): prioritize channel memory item updates
2026-07-15 00:44:15 +00:00