14 Commits
Author SHA1 Message Date
顾盼 a791e47a54 feat(cua): streamline computer use and port native reliability fixes (#14846)
* feat(cua): preload the JavaScript computer use runtime

* refactor(cua): remove the native Wayland backend

* feat(cua): move app computer use into the driver

* feat(cua): preserve background delivery and port platform fixes

* feat(cua): add explicit desktop input and browser runtime facade

* feat(cua): package the unified 0.26.0 computer use runtime

* fix(cua): finalize cross-platform native reliability

* fix(cua): isolate runtime releases from CLI versioning

* fix(cua): keep Electron out of root workspace installs

* fix(cua): synchronize consumers and async cleanup tests

* fix(browser): migrate smoke to preloaded cua runtime

* docs(cua): document MCP setup for Qwen Code Claude Code and Codex
2026-10-21 14:03:31 +08:00
Shaojin Wen e912d4f118 fix(ci): cap the prepare build heap and share the self-hosted Node preflight (#14827)
* fix(ci): cap the prepare build heap and share the self-hosted Node preflight

`prepare` (scripts/prepare.js) re-enters `npm run build`, which sets
`NODE_OPTIONS=--max-old-space-size=4096` for `packages/cli`'s `tsc --build`.
The per-package build script inherits the heap cap from its caller's
NODE_OPTIONS, so an install-path `prepare` ran the same compile with the
default heap and OOMed on the ECS pool where `npm run build` does not
(#14823). Set the same cap on prepare's build invocation and preserve any
incoming NODE_OPTIONS.

`sdk-java.yml`'s daemon E2E lane carried its own copy of the self-hosted
Node check, which only fails when Node is missing, so a too-old Node
reached the build and OOMed first. Point it at the shared
self-hosted-node action the other pool lanes use, which fails fast with the
provisioning message instead.

* Revert "fix(ci): cap the prepare build heap and share the self-hosted Node preflight"

This reverts commit 3b66282e3a.

* fix(build): give pnpm package builds a heap cap that reaches each node process

The OOM in #14823 lands in `packages/cli`'s `tsc --build`, which runs as its
own `node` process started by `node ../../scripts/build_package.js`. The
heap cap the root build script sets in NODE_OPTIONS covers the root process
and whatever inherits the environment, but every package script spawns a
fresh `node`, so the compile ran at 4096 MB and died with the heap full
(4032 MB reported, 4128 MB reserved).

pnpm's `nodeOptions` puts the cap in every package script's NODE_OPTIONS, so
the flag reaches the `node` that compiles no matter which entry point (npm
build, prepare, install) starts the build. 6 GB leaves headroom over the
observed 4 GB ceiling.
2026-10-20 13:35:19 +08:00
qqqysandqqqys c4f0acdef8 feat(extensions): add bounded generated HTML and JavaScript views (#14338)
Co-authored-by: qqqys <[email protected]>
2026-10-19 13:09:33 +00:00
ef38ad7753 feat(web-shell): add read-only Excel artifact previews (#13664)
* feat(web-shell): add read-only Excel artifact previews

* fix(vscode): refresh notices for Excel preview dependencies

* fix(web-shell): bound Excel merge loading and preserve preview content

* fix(web-shell): bound Excel preview work and preserve recovery

* fix(web-shell): harden Excel preview values and recovery

* fix(web-shell): include bundled ExcelJS dependency licenses

* fix(web-shell): guard column expansion and complete ExcelJS notices

* fix(web-shell): complete Excel color fallback and review guards

* test(web-shell): follow artifact filename labels in Excel previews

---------

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-10-13 02:43:10 +00:00
qqqysandqqqys 6775f24927 feat(extensions): add test failure report explorer (#13854)
* feat(extensions): add test failure report explorer

* fix(extensions): bound report snapshots before serialization

* fix(extensions): bound test report validation

---------

Co-authored-by: qqqys <[email protected]>
2026-10-11 12:58:55 +00:00
612a552959 feat(channels): add IMAP and SMTP email channel (#12939)
* feat(channels): add IMAP and SMTP email channel

* test(channels): include email in managed channel catalog expectations

---------

Co-authored-by: destire-mio <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-10-03 02:54:25 +00:00
Shaojin Wen 692a5d7446 feat(managed-agent): Add the public API contract and contract tests (#12808)
* feat(managed-agent): Add the public API contract and contract tests

Move the reviewed Managed Agent OpenAPI into the server module as the
single source for the public and WebShell routes (v1.13), and check the
server and the WebShell client against it.

- A contract test compares the mapped Spring routes, the API records,
  and the real requests, responses, declared headers and SSE frames of
  every partial route with the spec. Current differences are recorded
  as expected failures that the later Stage D slices remove; a fixed
  gap that stays listed fails as well.
- The WebShell client types are generated from the spec without its
  planned surface, and a test fails when the committed types are stale.
- Rename, unarchive, archive and delete, which the server already
  serves, are recorded as partial routes.

Part of #12793.

* test(managed-agent): Cover archived reads and WebShell first input in the contract

Address review feedback on the contract test and its callers.

- The real-traffic scenario now reads an archived Session through get
  and list, and creates a WebShell Session with its first message. That
  records three more gaps: archived Sessions read back with status
  "archived", which the contract's enum lacks, and the WebShell create
  request carries the same "text" input type as the other D2 lines.
- A property that appears in several oneOf or allOf members counts as
  planned only when every member marks it planned.
- Replace the deprecated JsonNode.fields() calls.
- The managed-progress e2e spec builds a Session with agentId and
  events with data, as the generated types require.
- The design note records the gaps that the gap file cannot hold:
  undeclared error responses, the full-page has_more semantics,
  workspace-bound Sessions, and the merge order with W0d.

* test(managed-agent): Reject unknown gap categories and quiet the validator

Address the sandboxed verification of the contract test.

- Every line of the known-gaps file must start with a category that a
  test filters on. A line with any other prefix was never compared, so
  it could not be reported as resolved; it now fails every contract
  test and names the line.
- The validator treats keywords outside JSON Schema as annotations
  without logging, which removes the nine "Unknown keyword" warnings
  that loading the whole OpenAPI document produced on every run.
- The design note no longer says that no route changes status: archive
  and delete move from planned to partial, and none becomes implemented.

* docs(managed-agent): State exactly what openapi-typescript reads from supports-color

The override comment and the design note said the generator reads only
stdout.hasBasic, which 7.2.0 provides. It reads stdout and its hasBasic
flag; 7.2.0 exports the same stdout, false without a TTY, and the
truthiness check on stdout is what keeps colours off there.
2026-09-27 04:08:41 +00:00
易良andQwen-Coder 73aa65a4b4 chore(desktop): rename packages/desktop-shell to packages/desktop (#12653)
* chore(desktop): rename packages/desktop-shell to packages/desktop

The Electron-era packages/desktop was removed in #9085 and the OpenWork
downstream has forked, so the short name is free and the shell suffix is
the only thing left of the transition. Rename the package directory and
every load-bearing reference: workspace negations (npm + pnpm), the ci.yml
crate filter, desktop-release.yml, the isolation guard, lint/ignore configs,
scripts tests, CLI workspace-fixture tests, and living docs.

User-facing identity is untouched: the crate stays qwen-code-desktop,
productName stays "Qwen Code Desktop", the bundle identifier, updater
feed, release tag scheme, and the legacy electron-bridge asset names are
all unchanged. check-desktop-isolation now guards both path names so the
old one cannot re-enter a workspace set. Dated design docs under
docs/design/ keep their historical paths deliberately.

Refs #8596

* docs(design): follow the rename in the undated notification design notes

The web-shell-browser-notification-details pair is a living doc without a
date prefix, so it falls under the PR's update rule rather than the
historical-record exception.

* docs(desktop): stop citing paths the rename invalidated

Three prose sites and one CI comment still described the pre-rename world,
or contradicted themselves once `packages/desktop` named both the removed
Electron app and the living Tauri shell.

- ci.yml `desktop_shell`: the comment this branch added credited the
  Cargo.toml guard with keeping "branches from either side of the rename"
  green. It does not. A pre-rename head's files sit under
  `packages/desktop-shell`, which the changed-files filter no longer
  matches, so the job skips before the guard ever runs. Say that, and say
  what the guard does cover (a head the filter matched because ci.yml
  itself changed, with no crate at the new path).
- docs/design/web-shell-user-message-links.md: "desktop-shell routing" /
  "desktop-shell clicks" -> "desktop-host", the wording this branch already
  chose in LinkifiedText.tsx for the same rule.
- docs/design/telemetry-runtime-client-attribution-design.md: repoint the
  `runtime.rs` citation. It backs the live `QWEN_CODE_DESKTOP=1` claim, and
  the other document of record for that invariant -- the comment in
  acp-channel-fallback.ts -- was already repointed by this branch, so the
  two disagreed. Only the path token changes; the doc's dated findings are
  untouched.
- brand-create.mjs / SKILL.md / src-tauri/src/main.rs: name the historical
  referent as historical (the Electron `packages/desktop` removed in PR
  9085) instead of letting it read as a self-reference. In main.rs the
  citation `packages/desktop/packages/shared/src/config/storage.ts` now
  looked like a subpath of the file's own package. Rewritten, not deleted:
  the `QWEN_DEFAULT_WORKSPACE_DIR` clause next to it is the only in-code
  documentation of that override and still resolves.

Deliberately not swept: `scripts/check-desktop-isolation.js`'s
`packages/desktop-shell` tripwire, ci.yml's #8132 history comment,
`docs/design/9152-architecture-invariant-classification.md` (names both
spellings and is accurate), and the date-prefixed
`docs/design/2026-07-31-desktop-web-shell-release.md`.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmug1iw422v

* test(scripts): pin the desktop crate path and the isolation guard's coverage

Both invariants fail green today, and this rename is the second time the
crate directory has moved.

`scripts/check-desktop-isolation.js` gained `'packages/desktop'` in
`nativePrefixes` -- the only production-logic line in this branch -- and
nothing read it. Deleting that line leaves the guard's output and exit code
byte-identical, because `isNativeLocation('packages/desktop')` then matches
no remaining prefix; a later PR that drops `!packages/desktop` from the root
workspace list would re-admit the package with CI green.

The `desktop_shell` job names the crate directory in five places (the
changed-files filter, the Cargo.toml guard and its `::notice::` text, the
rust-cache `workspaces:`, and two `working-directory:` values). Nothing
asserted they agree with each other or with a directory that exists, so the
next drift reports a skipped job as a passed one.

The new suite parses both instead of re-running them, and is superset-shaped
on purpose: `nativePrefixes` carries a fourth entry (the
`packages/desktop-shell` tripwire) that nothing negates, and an exact-match
assertion would invite deleting it. The npm/pnpm mirror of the negation list
is already pinned by package-scripts.test.js, so this reads package.json only.

Verified by mutation, each applied alone and then restored: reverting the
filter alternative or either `working-directory` to `packages/desktop-shell`,
dropping `'packages/desktop'` from `nativePrefixes`, and dropping
`!packages/desktop` from package.json all turn this suite red. Today no other
test does.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmug1iw422v

* test(scripts): pin what the desktop-isolation tests actually claim

Four review findings on scripts/tests/desktop-isolation.test.js, each
verified by mutating the thing it claims to pin, before and after.

- The comment above the local `isNativeLocation` copy claimed a change to
  the script's rule "has to be made here too". Nothing reads the rule back
  from the producer -- `nativePrefixesFromSource` parses the array literal
  only -- and narrowing the script's rule to prefix equality leaves the
  suite green. Describe the copy instead of claiming coupling that does not
  exist.
- `still carries the Cargo.toml existence guard` matched only the guard's
  condition text. Flip the body's `changed=false` to `changed=true`, or move
  it after `fi`, and the suite stayed green at 5/5 while the job runs every
  gated step against a crate that is not there. Assert the assignment sits
  inside the guard's own `if ... fi` body.
- Both on-disk checks keyed on the bare directory, so git-ignored residue
  under `packages/desktop-shell` (`node_modules`, which survives a branch
  switch with an empty `git status`) reddened two tests on a tree with no
  defect and blamed the root workspace set. Key on `package.json`, which is
  what `packages/*` requires to make a directory a workspace member; a real
  re-entry still reddens both.
- The `startsWith(filterAlternative)` assertion compared a path built from
  `crateDir` -- `filterAlternative` with one trailing slash stripped -- back
  against `filterAlternative`, so it could not fail for any input. Dropping
  the trailing slash from the changed-files filter in ci.yml left it green.
  Replace it with the property that makes the alternative a path prefix
  rather than a bare string prefix.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmugedurp3j

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-09-25 05:41:25 +00:00
易良andQwen-Coder 64dd058c9c ci(pnpm): install with pnpm everywhere and retire package-lock.json (#11859)
* ci(pnpm): install dependencies with pnpm in CI and release

CI tests the graph release ships only if both install it the same way, so
this moves both, keeping every `npm run` script and `npm publish` as they are.

- ci.yml (7), release.yml (3) and release-vscode-companion.yml (2) install
  with `corepack pnpm install --frozen-lockfile`; release keeps
  --ignore-scripts plus its explicit postinstall and generate steps.
- setup-node drops the npm cache and sets package-manager-cache: false,
  which setup-node v5+ needs once package.json declares a pnpm
  packageManager.
- A composite action caches the pnpm store on the three always-hosted jobs;
  the ECS pool keeps its store on local disk.
- version.js no longer lets npm reify node_modules: --no-workspaces-update on
  both `npm version` calls, and a package-lock-only refresh in step 9.
- The notices generator, prepare-package's sharp pin and the standalone
  release's native package specs read the installed tree or pnpm-lock.yaml
  instead of package-lock.json keys.
- New size baselines for the three workflows, which were already over them.

* chore(vscode): regenerate NOTICES from the pnpm-installed tree

Generated in CI (pnpm Worktree Smoke on ubuntu-latest) by the ported
generator. Same entry set as before except the five packages npm and pnpm
resolve differently: fdir 6.5.0, picomatch 4.0.5, mdurl 2.1.0,
@types/node 22.20.1 and mime-db 1.52.0 (form-data's pinned copy). No entry
lacks a license text or repository.

* fix(pnpm): resolve undeclared zod peers to the repo's zod, as npm does

The first CI run on this branch failed the export renderer's size budget:
1,930,015 bytes against 1,930,000, where the same commit built on npm's
tree comes to 1,858,186. Comparing both trees' esbuild metafiles and
prebuilt bundles put the whole difference in web-shell's transcript.js
(+100,903 bytes pre-minify), and the added code is zod 4 plus the
@modelcontextprotocol/ext-apps schemas; the sdk daemon bundle is identical.

web-shell bundles ext-apps, whose zod and MCP SDK are peers, and declares
no zod itself, so pnpm filled the peer with the highest locked zod (4.4.3)
while npm serves the hoisted 3.25.76. Declaring zod 3.25.76 as a root
devDependency makes pnpm resolve every such peer to it
(resolve-peers-from-workspace-root is on by default), which is npm's
hoisting behaviour. mobile-mcp keeps its own zod 4.4.3, as under npm; the
npm tree does not change.

* fix(pnpm): keep npm's root @types/node so root-level typechecks match

The no-AK integration gate typechecks integration-tests/, which takes
Node's types from the root node_modules. npm hoists @types/node 20.19.1
there, because acp-bridge and core pin it; pnpm, with no root declaration,
hoisted 22.20.1, whose spawnSync overloads type process-registry.ts's
stdout as string | NonSharedBuffer and fail TS2322. The same code passes on
every npm-installed PR.

Declaring 20.19.1 as a root devDependency puts npm's copy back where a
root-level tsconfig looks. The npm tree does not change.

* ci(pnpm): install with pnpm in the remaining workflows

The follow-up to switching CI and release: every other workflow that
installs the root workspace now uses the pinned pnpm with
--frozen-lockfile, so no job tests a graph that release does not ship.

- e2e, tui-parity, repo-hygiene, web-shell-visuals, serve-ab, sdk-java,
  windows-runner-smoke, qwen-autofix, the SDK releases, finalize-release,
  sync-release-to-oss, the root install in desktop-release and
  cd-cua-driver, and the sandbox Dockerfile.
- setup-node drops the root npm cache (package-manager-cache: false); hosted
  jobs restore the pnpm store through the shared composite action, and
  qwen-autofix keeps af-012's rule that the persistent pool restores no
  remote cache.
- serve-ab and web-shell-visuals install a separate base checkout that can
  predate pnpm-lock.yaml, so those installs fall back to npm ci there.
- Out of scope, unchanged: packages with their own lockfile (cua-driver's
  TypeScript SDK, desktop-shell, live-host, mobile-mcp), and the triage
  lanes with npm-cache.yml and the npm audit gate, which move with the
  package-lock.json retirement.
- New size baselines for the touched workflows; qwen-autofix stays under
  the 470000-byte gate.

* fix(pnpm): link the workspace packages that root-level code imports

npm links every workspace package into the root node_modules, so
integration-tests/ and scripts/, which are not workspace packages
themselves, can import @qwen-code/* by name. pnpm links a workspace
package only where it is a declared dependency, and the root declared
none, so the no-AK integration gate could not find
@qwen-code/qwen-code-core.

The root now declares the seven packages that root-level code imports at
runtime as file: devDependencies: acp-bridge, channel-base, the CLI,
core, sdk, web-shell and web-templates. npm already links them, so its
tree does not change (letting npm re-resolve the lock reproduces the
same root entry); .pnpmfile.mjs rewrites them to workspace:*, which pnpm
links at the root.

* ci(pnpm): move triage, the store producer and the root audit to pnpm

- qwen-triage's verify and tmux-testing lanes install the PR with
  `corepack pnpm install --frozen-lockfile` from a restored pnpm store.
  The one retry clears node_modules first, because pnpm install, unlike
  npm ci, keeps it.
- npm-cache.yml becomes pnpm-store.yml, keyed on pnpm-lock.yaml.
- security-checks audits the root workspace with `pnpm audit`, which reads
  the lockfile, so the root install step goes. The vendored lockfiles keep
  npm, and the retry recognises pnpm's endpoint error too.
- qwen-autofix drops the hosted-only store cache: its heavy jobs forbid
  local `uses: './...'` actions, and the file sits at the size gate.
- cd-cua-driver: setup-node@v4 has no package-manager-cache input.
- package-assets fixtures install sharp rather than listing it in
  package-lock.json, matching how prepare-package now resolves it.
- AGENTS.md, CONTRIBUTING.md and the npm workspace guide install with pnpm.

* feat(review): install pnpm repos in qwen review's build/test

qwen review installed dependencies only for a repo with package-lock.json
and reported a pnpm repo as unsupported, so reviews of qwen-code would
lose their build and test once its npm lockfile goes.

- lockfileInstaller() picks npm or pnpm from the committed lockfile. With
  both, `packageManager: pnpm@...` chooses pnpm; anything else keeps npm.
- A pnpm repo installs with `corepack pnpm install --frozen-lockfile`
  through the same disk, budget and sandbox path as `npm ci`, and counts
  as installed once node_modules/.modules.yaml exists.
- Scripts still run through `npm run`, and the npm path is unchanged.

* ci(pnpm): retire the root package-lock.json

pnpm-lock.yaml is now the only root lockfile.

- check-lockfile keeps the pnpm integrity check and Playwright parity,
  which now reads pnpm importers and snapshots. The npm-vs-pnpm agreement
  and build-approval checks go: pnpm 11's strictDepBuilds and every CI
  install's --frozen-lockfile enforce what they covered.
- pnpm-lock-freshness.yml goes; it regenerated pnpm-lock.yaml from
  package-lock.json.
- Release: version.js drops its package-lock refresh, the release commit
  stages pnpm-lock.yaml and the SDK release stages no lockfile, so neither
  `git add` fails on the missing file. `npm version` in the SDK and
  mobile-mcp releases passes --no-workspaces-update so it cannot reinstall
  the root with npm.
- cd-mobile-mcp installs from the root pnpm lockfile. setup-node in
  cd-mobile-mcp, qwen-code-pr-review and desktop-release no longer keys an
  npm cache on the root lockfile.
- check-desktop-isolation reads pnpm importers; build.js, build_sandbox.js
  and preflight install with pnpm.
- pnpm-lock.yaml, pnpm-workspace.yaml and .pnpmfile.mjs join the autofix
  supply-chain class and repo-hygiene's deny list; pnpm-lock.yaml joins
  autofix's generated-diff exclusions, sdk-java's path filter and the
  platform-sensitivity manifests.
- .gitignore lists /package-lock.json so an npm install cannot commit it
  back. Tests and docs follow.

* fix(standalone): read the node-pty versions from the pnpm lockfile

* chore(vscode-ide-companion): regenerate NOTICES.txt after the lockfile swap

* chore(vscode-ide-companion): regenerate NOTICES.txt from the pnpm dependency tree

The committed NOTICES.txt was generated from an npm install and does not
match the tree produced by the pnpm install the release pipeline now uses
(659 resolved dependencies, differing @types/node and mime-db versions).
Regenerating with the pinned pnpm install makes the freshness check
reproducible.

* fix(ci): drop the npm cache from the daily dependency audit

actions/setup-node's npm cache keys off the root package-lock.json, which this branch retires, so the scheduled job would fail at Set up Node before the audit ran. No pull_request run reaches this workflow to catch it. The root tree is never installed here either: pnpm audit reads pnpm-lock.yaml directly.

Matches the sibling workflows that already set package-manager-cache: false; scripts/tests/security-workflows.test.js now pins it.

* chore(ci): stop banking main-side drift in the workflow size ratchet

The ratchet exists to turn a workflow's growth into one reviewed line. This branch re-recorded entries for 17 workflows it never touches, several upward by 0.4-2.8 KB - drift that happened on main, banked into the manifest without review.

Restore those to their merge-base values, so .size-baseline changes only for the 22 workflows this branch actually edits. The entries were not needed to go green: ci.yml passes the PR's base commit, so a file that drifted on main and is otherwise untouched earns a warning, not a failure.

* fix(release): initialize standalone state before main

* test(browser-use): read Playwright pin from pnpm lock

* fix(ci): address pnpm migration review blockers

* fix(desktop): stop reading the retired root lockfile

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-09-20 02:11:25 +00:00
JabrailandShaojin Wen 535dfd9a11 feat(web-shell): add PWA installability and Android development shell (#11722)
* feat(web-shell): add installable PWA support

* fix(build): skip incomplete Web Shell assets when bundling

* feat(web-shell): declare browser support matrix and compat fallbacks

* feat(web-shell): make the Web Shell installable as a PWA

Serves /manifest.webmanifest and /sw.js pre-auth, registers the worker
deferred to load, and replaces the earlier service-worker.js draft with
sw.js as a Vite entry (PNG icons included).

* feat(mobile-shell): add Android WebView shell skeleton

WebView shell around the daemon-served Web Shell (no second UI), profile
tuples of URL/token/display name with #token= fragment delivery, native
WebView version check at the Chrome 107 floor, and a foreground-service
skeleton for SSE/notifications.

* chore(web-shell): drop superseded service worker draft files

* test(web-shell): cover service worker routing and lifecycle

* chore(web-shell): align @playwright/test with main lockfile

* chore: register mobile-shell importer in pnpm lockfile

* test(web-shell): parse sidebar stylesheet with postcss for @supports nesting

* chore: register mobile-shell in pnpm rewrite set

* fix(web-shell): address PWA and Android acceptance blockers

* docs(mobile-shell): make remaining native acceptance limits explicit

* fix(web-shell): address fourth PWA review round

* fix(mobile-shell): restore web view on back

---------

Co-authored-by: Shaojin Wen <[email protected]>
2026-09-17 22:23:58 +00:00
易良andQwen-Coder c666ec1a0a fix(ci): restore Windows test baseline (#11787)
* fix(live): restore Windows filesystem compatibility

* fix(ci): restore Windows test baseline

* fix(ci): keep release-age excludes internal and re-arm two witnesses

- pnpm-workspace.yaml: drop '@tybys/[email protected].4' from
  minimumReleaseAgeExclude. scripts/tests/package-scripts.test.js pins
  that list to internal, version-independent entries. The exclusion is
  also inert: minimumReleaseAge is not configured anywhere in the repo,
  and pnpm-lock.yaml records `overrides` but not minimumReleaseAgeExclude
  -- so the '@tybys/wasm-util': '0.10.4' override stays, because dropping
  it would desync the lockfile and fail --frozen-lockfile.
- web-shell-brand.test.ts: build the swapped-file fake as BigIntStats.
  Production compares bigint fields with a bare `!==`, so a number-backed
  fake tripped `dev` first and the perturbed inode was never reached;
  removing the perturbation now turns the test red again.
- run-qwen-serve.test.ts: '0' is unresolvable on Windows (getaddrinfo
  ENOTFOUND 0), so the wildcard case keeps '0.0.0.0' and witnesses the
  socket rather than a spelling: the operator typed port 0, so the report
  must carry the ephemeral port the listener actually got. Spelling
  normalisation stays pinned platform-independently in
  remote-quickstart.test.ts.
- artifact-snapshots.ts: revert the readdir hoist to main. It is
  behaviourally inert (the catch rethrows ENOENT on the operationId path
  either way), has no test and no Windows link.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmu07e78o2w

* fix(ci): restore the load-bearing release-age exclude, keep the witness fixes

The previous commit dropped '@tybys/[email protected].4' from
minimumReleaseAgeExclude to satisfy scripts/tests/package-scripts.test.js:390,
on the reasoning that the entry was inert because minimumReleaseAge is not
configured in this repo. That reasoning was wrong and CI measured it: the gate
is enforced by pnpm itself, not by repo config, so removing the exclude failed
'Install frozen pnpm worktree dependencies' on all three platforms with

  Lockfile failed supply-chain policy check (1975 entries)
  @tybys/[email protected].4 was published at 2026-09-13T08:00:13.464Z,
  within the minimumReleaseAge cutoff (2026-09-12T20:03:50.286Z)

(run 34779568628; Install was green at 6099768ffb, which carries the entry).
Restoring the line makes pnpm-workspace.yaml byte-identical to that head again.

The three other changes from that commit are kept, since they are independent
of the exclude and were green in run 34779568625 (Test 18m58s, Lint & Static
12m36s, Integration no-AK 7m8s): the web-shell-brand.test.ts BigIntStats fake
that re-arms the inode witness, the run-qwen-serve.test.ts socket witness
replacing the tautological '0.0.0.0' assertion, and the artifact-snapshots.ts
revert to main.

This leaves the package-scripts.test.js assertion red exactly as it was before
this round. That is now a measured conflict rather than an open question: the
repo test pins the exclude list to internal, version-independent entries, while
pnpm needs a version-pinned exemption for a package published the same day.
Resolving it means either dropping the '@tybys/wasm-util': '0.10.4' override and
regenerating both lockfiles so an older, age-compliant version resolves, or
relaxing the invariant to admit external version-pinned excludes. Both are
supply-chain policy calls for the author/maintainer, so neither is made here.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmu07e78o2w

* fix(ci): pin @tybys/wasm-util to the age-compliant 0.10.3 instead of exempting it

Test (ubuntu-latest, Node 22.x) failed at scripts/tests/package-scripts.test.js:390
(run 34781999892), which pins minimumReleaseAgeExclude to internal,
version-independent entries, while the previous head carried
'@tybys/[email protected].4' there to satisfy pnpm's minimumReleaseAge gate. The two
looked mutually exclusive, but only because the pin chose the newest release.

@node-rs/jieba-wasm32-wasi pulls in @napi-rs/[email protected], which accepts
^0.10.3, and 0.10.3 was published 2026-06-23 -- well outside the 24h window that
rejected 0.10.4 (published 2026-09-13T08:00:13Z). So the pnpm override now pins
0.10.3, the exclude entry is dropped, and both lockfiles follow: package-lock.json
must lock the same version or scripts/check-lockfile.js's "pnpm lockfile against
package-lock.json" agreement gate fails, which also reddens the two check-lockfile
fixture branches in package-scripts.test.js because they copy the real lockfiles.

The pin lives on the pnpm side only, mirroring how the typescript pins already
diverge; npm has no release-age gate, so no package.json override is added and the
nested @tailwindcss/oxide-wasm32-wasi copy of @tybys/wasm-util is left alone. Once
0.10.4 ages out of the window, a maintainer can drop the override from both sides if
nothing else needs the pin.

Verified locally: pnpm 11.24.0 `install --lockfile-only` reports "Lockfile passes
supply-chain policies (1975 entries)"; scripts/tests/package-scripts.test.js 49
passed / 1 skipped (was 3 failed); scripts/tests/check-lockfile.test.js 14 passed;
prettier --check clean.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmu0ce8qb32

* fix(ci): handle expected Windows socket reset

* fix(ci): make Windows regression tests portable

* fix(ci): make script tests portable on Windows

* test: re-arm four witnesses the review round found vacuous or over-gated

Each of these was measured, not argued; the mutation named in every case is
the one that used to leave the suite green.

- no-follow-open.test.ts: add the >2^53 identity case. Removing
  `{ bigint: true }` from all four production stat call sites left 17/17
  green, so the Windows-precision conversion this PR exists for was
  unwitnessed. The fixture uses 2^60 offsets, where the double spacing is
  256 and both ids collapse to one value; 2^53+1 / 2^53+2 do NOT collapse
  (2^53+2 is exactly representable), so those constants cannot pin it.
  Two-arm measured: intact 18/18 pass, mutated exactly 1 fails.
- web-shell-brand.test.ts: stat the planted regular file with
  `{ bigint: true }`. Production compares BigIntStats with a bare `!==`, so
  the number-backed fake tripped `dev` on a type mismatch alone and would
  have refused the correct file too. Probed both arms: pre-fix
  `typeof expectedStat.dev === 'number'` with the `dev` clause true,
  post-fix both sides bigint with `dev` false and the refusal driven by a
  real inode mismatch.
- conversation-workspace.test.ts: move the `inodeVerifiable` gate below the
  two `toBe(identity)` assertions. They are inode-independent, so gating
  them deleted a witness the Windows lane was green on. Modelled an
  unverifiable-inode host: the test reports skipped, not failed, which is
  only reachable once both assertions have passed.
- worktree.test.ts: split `localFilterCommands` in two. The fail-closed
  branch is platform-free by construction and the base Windows log shows it
  passing there; only the `a\nb` fixture is Windows-impossible, so only that
  half keeps the gate.

Co-authored-by: Qwen-Coder <[email protected]>

* ci: say why the post-redirect autocrlf line is not a duplicate

Both workflows now run `git config --global core.autocrlf false` twice: once
before the checkout and once inside the test step, where HOME/USERPROFILE are
redirected to a freshly created empty directory. The second line therefore
writes a different global config than its twin, and on ci.yml's hosted
windows-2022 fallback the pre-checkout step never runs at all because it is
gated on `runner.environment == 'self-hosted'`.

It was the only one of the three occurrences with no comment, so deduping it
looks free and silently returns CRLF conversion to every git operation the
suites perform. Replaying no-ak-integration-ci.test.js's own assertions with
the second occurrence deleted gives identical indices and still passes, so no
test notices the removal.

Comments only. The ordering assertions search for the FIRST occurrence and
none of the added text matches a needle they look for; the 14 assertions in
no-ak-integration-ci.test.js still pass.

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-09-14 11:17:19 +00:00
易良 f71a4e797e chore(pnpm): scope build approvals, pin the ACP zod peer, add a freshness lane (#11797)
Follow-ups left open when #11625 merged: its three unresolved review threads,
the deferred finding in #11681, and #10524.

- allowBuilds decisions now honour the whole key the way pnpm does: a bare
  name covers every version, an exact `name@version` (or a `||` union) covers
  only those, and a range or source-like key decides nothing. Reducing a key
  to its name let an approval scoped to one version cover the next (R1-10).
- Pin `@agentclientprotocol/sdk>zod` to 3.25.76 so a pnpm worktree runs the
  zod the npm tree installs. Under zod 4 the SDK's own `instanceof z.ZodError`
  test stops matching and a malformed request answers -32602 instead of
  -32603; both versions are npm-locked, so the agreement gate cannot see the
  split (R1-1).
- Cover web-shell's shipped client sources with the undeclared-import rule;
  it is the one published package whose sources are not under src/. Measured
  cost today: zero violations (R2-6).
- Add a path-filtered workflow that regenerates pnpm-lock.yaml with
  `corepack pnpm import` and fails when the committed file differs, which is
  the freshness half set membership cannot cover (#11681).
- setup-worktree: accept only an executable file on PATH, so a corepack left
  without its exec bit reports the actionable error instead of EACCES
  (#10524).
- Name the esbuild divergence an accepted parity gap, and record what
  `fsevents: false` costs a macOS worktree.
2026-09-14 04:45:19 +00:00
1e768979c5 chore(pnpm): gate pnpm-lock on package-lock and declare hoisted imports (#11625)
* chore(pnpm): gate pnpm-lock on package-lock and declare hoisted imports

- Lockfile check: every pnpm-lock version must be locked in package-lock
  (one documented npm-lock gap, failing if it goes stale), and every
  dependency npm runs an install script for needs an explicit allowBuilds
  decision (fsevents: false records current behaviour).
- Regenerate pnpm-lock with `pnpm import`, which is deterministic and drops
  18 versions npm never locked; document it as the regeneration step.
- Enforce import/no-extraneous-dependencies on shipped sources and declare
  the 15 imports that only resolved through hoisting, at already-locked
  versions.
- Run the pnpm smoke workflow when package-lock.json changes.
- Add the Stage 2 verification brief.

Refs #10444

* chore(pnpm): regenerate vscode NOTICES and pin the new smoke path

- NOTICES.txt: core's new OpenTelemetry declarations make the generator
  visit six Apache-2.0 entries in a different order. Same 655 entries,
  same text; only their order changes (6 header lines).
- package-scripts test: expect package-lock.json in the pnpm smoke paths.

* fix(pnpm): close lockfile-gate gaps found in review

- fail closed when pnpm-lock.yaml has no packages section instead of
  printing the strongest success line over an unread graph
- match git/file: dependency keys against npm's resolved field, whose
  pnpm key is the source rather than a version
- count an allowBuilds entry only when its value is boolean, so pnpm's
  own 'set this to true or false' placeholder no longer passes the gate
- drop the inert package-lock.json trigger from the pnpm smoke workflow;
  no step in that job reads it, and npm/pnpm agreement is already gated
  by ci.yml's Check lockfile step on the full profile
- stage-2 evidence recipe: cover nested workspace roots in the reflink
  arm, sample peak worktree count at most 5 minutes apart, run the
  parity matrix in a throwaway workflow, and give the npm baseline the
  same skip guards the pnpm arm has
- drive every gate branch with fixture-based negative tests

* docs(verification): correct pnpm stage-2 measurement recipe

Quiesce the df window, name the worktree sampler's working directory and forbid host cron, and register the throwaway workflow's size baseline so the parity run's test:ci does not fail under both installers.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmty6iqs0zv

* docs(verification): fix stage-2 recipe's dispatch and macOS fs probe

Step 2 now pushes the scratch branch before gh workflow run --ref (the ref is
resolved server-side), notes the workflow token scope, and gives the merge-to-
default / pull_request fallback when a branch-only dispatch is refused. Step 1
probes the checkout's own volume with diskutil info "$(pwd)" instead of the
sealed read-only / system volume. Pushing, dispatching, and posting the results
comment are marked operator actions.

Co-authored-by: Qwen-Coder <[email protected]>

Patrol-Run: qwen-pr-closeout/jmty8nwk9zy

* test(lockfile): cover stale-gap and alias arms of check-lockfile

Add two failure-branch cases: a knownNpmLockGaps entry that leaves the pnpm
graph (the !pnpmVersions.includes arm), and an aliased install script reported
under its real package name (the npmPackageName details.name arm). Both were
untested and stayed green if the arm was deleted.

Co-authored-by: Qwen-Coder <[email protected]>

Patrol-Run: qwen-pr-closeout/jmty8nwk9zy

* fix(lockfile): close the empty-env seam and name the overrides remedy

- `CHECK_LOCKFILE_ROOT` was read with `??`, so an exported-but-empty variable made `root` the empty string and every path in this gate cwd-relative: it would then report on whatever lockfiles sit in that directory as if they were the repository's, and pass quietly when the cwd happens to be the repo root. Truthiness keeps the fixture seam and closes the empty case.
- The version-agreement failure named exactly one remedy, `corepack pnpm import`, while pnpm-workspace.yaml carries eight `overrides` (three of them pin typescript) that no npm-side regeneration can reproduce. The message now names that layer, so a still-red gate points at what actually decides the value.

Verified: `node scripts/check-lockfile.js` exits 0 on the committed tree, prettier and eslint are clean on the file, and both suites that execute the gate pass.

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: qwen-code-ci-bot <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-09-12 14:47:44 +00:00
3ecfaffdf8 perf(dev): add pnpm worktree bootstrap foundation (#10449)
* perf(dev): add pnpm worktree bootstrap foundation

Co-authored-by: Qwen-Coder <[email protected]>

* fix(dev): harden worktree bootstrap process handling

Co-authored-by: Qwen-Coder <[email protected]>

* perf(dev): prefer cache-only worktree installs

Co-authored-by: Qwen-Coder <[email protected]>

* fix(dev): preserve cached install cancellation

Co-authored-by: Qwen-Coder <[email protected]>

* fix(build): harden pnpm worktree bootstrap

Validate pnpm lock updates in releases and exercise real installs and builds across supported hosts. Preserve npm release compatibility and keep dependency-only setup from rewriting npm-layout notices.

Co-authored-by: Qwen-Coder <[email protected]>

* chore: record merge attribution

Record the required attribution for the Stage 1 merge without rewriting published history.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(dev): keep worktree bootstrap clean

Use a bootstrap-scoped notice guard because nested npm lifecycle commands replace npm_lifecycle_event. This preserves explicit notice generation while preventing dependency setup from rewriting tracked output.

Co-authored-by: Qwen-Coder <[email protected]>

* ci: track pnpm smoke workflow size

Register the new workflow in the repository size ratchet as required by the main CI gate.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(ci): drop stale importers from the new pnpm lockfile

The lockfile was generated before the WebShell cutover (#9811) removed
@qwen-code/webui from web-shell and the tailwind tooling plus
@qwen-code/webui from vscode-ide-companion, so frozen-lockfile
installs fail on all three smoke platforms with
ERR_PNPM_OUTDATED_LOCKFILE. Drop the five stale importer entries so
the lockfile matches the current package.json manifests; verified with
pnpm 11.24.0 install --frozen-lockfile --lockfile-only.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(ci): refresh pnpm lockfile after merging main

The merge of origin/main added remend@^1.3.1 to packages/cli/package.json
without updating pnpm-lock.yaml, breaking the pnpm Worktree Smoke workflow
frozen-lockfile install. Regenerated with pnpm install.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmtid3rse9h

* fix(scripts): resolve the path variable case-insensitively on Windows

A spread of process.env is an ordinary object, and native Windows shells
expose the path variable as `Path`, so `env.PATH` was undefined there and
findOnPath never located corepack — the offline-first Corepack bootstrap
silently degraded to npx on exactly the hosts it exists for.

Co-authored-by: Qwen-Coder <[email protected]>

* test(scripts): pin the worktree bootstrap guards against mutation

- Split the fallback log on /\r?\n/ like the sibling cmd.exe mocks, so the
  assertion holds on the Windows lane's CRLF output.
- Add a win32 variant bootstrapping with the native `Path` casing to pin
  the case-insensitive lookup.
- Assert the smoke workflow's fail-fast flag, the install-before-clean
  step order, and the no-build guard via a substring on the raw job text.
- Extend the pnpmfile rewrite fixture to devDependencies and
  optionalDependencies, which the committed lockfile already uses.
- Assert the notice-skip guard by effect (writeFile never called) and add
  the flag-absent companion test with I/O stubbed.

Co-authored-by: Qwen-Coder <[email protected]>

* chore(deps): sync pnpm-lock.yaml with qwen-live ACP backend deps

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmtjyz3tdco

* fix(dev): stop patch-package from corrupting the pnpm store

The root postinstall runs patch-package, which rewrites files under
node_modules in place. With pnpm's default import method those files are
hard links into the content-addressable store, so the patch rewrites the
store entry too and its contents stop matching the sha512 it is filed
under. Every subsequent fresh worktree then fails its --offline stage
with ERR_PNPM_NO_OFFLINE_TARBALL, silently falls back to the registry
(exit 0), and the fallback's postinstall corrupts the store again, so the
offline path never hits. The advertised offline timing was only reachable
on a same-tree reinstall, which needs no relinking.

Set packageImportMethod to clone-or-copy: a copy-on-write clone where the
filesystem supports it, a plain copy elsewhere. Either way patch-package
edits only the worktree's own copy and the store entry stays intact, so
later worktrees resolve entirely from the store.

Verified on an isolated fixture store: under the default method the
node_modules file and its store entry share an inode, and an in-place
edit moves the store file's sha512 off its address, after which a fresh
offline install fails with ERR_PNPM_NO_OFFLINE_TARBALL; under
clone-or-copy the same edit leaves the store hash unchanged and the fresh
offline install reports reused 1, downloaded 0.

Also add pnpm-lock.yaml to .prettierignore. prettier reflows it into a
shape pnpm does not emit, so `npm run format` and `pnpm install` fight
over the file; .yamllint.yml already ignores it for the same reason.

Both facts are pinned by tests in scripts/tests/package-scripts.test.js.

Claude-Session: https://claude.ai/code/session_012797rgiteWJxLT9TLkKq8G

* fix(ci): refresh pnpm-lock.yaml for qwen-live prompts deps

Main added prompts/@types/prompts (and the ACP sdk) to
packages/qwen-live/package.json after this branch generated its
lockfile, so the pnpm Worktree Smoke frozen-lockfile install failed
with ERR_PNPM_OUTDATED_LOCKFILE. Regenerated with pnpm 11.24.0
(--lockfile-only); frozen-lockfile and supply-chain policy checks pass.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmtlc9sozfg

* fix(pnpm): close review findings on worktree bootstrap

- Declare @qwen-code/qwen-code-core in vscode-ide-companion so the pnpm
  linker materializes it; regenerate both lockfiles and assert the link in
  the smoke workflow (R5-1)
- Pin @types/node 20.19.1 in packages/core so the pnpm layout compiles
  against the same types npm hoists (D6-1)
- Accept and pin the corepack +sha512 integrity suffix on packageManager;
  strip it for the npx fallback spec (D6-2)
- Cover every workspace member in the pnpmfile rewrite set, mirror the npm
  channel list in pnpm-workspace.yaml, and cross-check both in tests
- Run setup-worktree.js installs with cwd pinned to the checkout, gate the
  smoke clean-check on git status --porcelain, stop post-merge runs from
  cancelling each other, and extend check-lockfile.js to pnpm-lock.yaml

* merge(main): realign pnpm workspace set and lockfiles

Main removed the webui and cua-driver packages; drop both from the pnpm
rewrite set and regenerate the pnpm lockfile against the merged manifests
(picks up playwright, react-markdown, and the other main-side dependency
changes) so the frozen bootstrap matches what the PR merge ref will run.

* style(ci): quote smoke workflow scalars per yamllint

* fix(dev): require Corepack for pnpm bootstrap

* fix(dev): close pnpm bootstrap review blockers

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: yiliang114 <[email protected]>
2026-09-08 03:07:40 +00:00