Commit c0d17e2
feat(desktop): establish OpenWork on the Qwen Tauri Web Shell (#81)
* feat(desktop): bridge Electron users to Tauri updates (#8392)
* feat(desktop): bridge Electron updates to Tauri
* test(desktop): cover parseArguments validation in electron bridge manifest (#8392)
* chore(desktop): address bridge review follow-ups
---------
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
* fix(web-shell): prevent table dialog close scroll jump (#8407)
* ci: bump qwen-code-action to 05f8171 (skip redundant install, surface install errors) (#8444)
* ci: bump qwen-code-action to 05f8171
* ci: bump qwen-code-action to 05f8171
* ci: bump qwen-code-action to 05f8171
* ci: bump qwen-code-action to 05f8171
* ci: remove broken legacy scheduled PR triage workflow (#8434)
The Gemini-era scheduled PR triage workflow has been dead weight for a
long time:
- Its only business value — syncing labels from the linked issue to the
PR — never fires: gh exports closingIssuesReferences as a flat array,
so the script's '.closingIssuesReferences.nodes[0].number' jq path
always errors, the error is swallowed by 2>/dev/null, and every PR
falls into the "No linked issue found" branch. The latest production
run logged 157 "No linked issue" hits and zero label syncs, despite
many of those PRs having linked issues.
- LABELS_TO_REMOVE is computed but never applied, PRS_NEEDING_COMMENT is
never appended to, and the prs_needing_comment job output has no
consumer — the rest of the script is dead code.
- It burns 1+N API calls against every open PR every 15 minutes.
- The id-token: write permission is a leftover from the Gemini/GCP OIDC
era; nothing in the bash script uses it.
Real PR triage lives in qwen-triage.yml. Remove the workflow and its
script, drop the stale docs section describing behavior it never had,
and pin the file into the legacy-workflow regression list.
Co-authored-by: verify <verify@local>
* feat(telemetry): Track tool execution outcomes (#8180)
* feat(telemetry): track tool execution outcomes
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(telemetry): address execution-status review feedback (#8180)
- Update stale nonInteractiveToolExecutor expectations for executionStatus (red CI)
- Scope the cancelled span-status short-circuit to tool_call events so other
cancelled events carrying an error keep ERROR status
- Record loop-detection skips as UNKNOWN, not EXECUTION_DENIED, keeping the
denial metric accurate
- Assert execution_status on the resolved-with-error PostToolBatch path
- Raise tool-call observer-failure logging from warn to error
- Clarify subagent-projection exclusion and JSONL compatibility in design doc
* fix(core): address review findings 3-6 on tool execution status (#8180)
- recordToolExecutionMetrics now merges common attributes (session.id
opt-in) like every other counter in metrics.ts
- Lift TOOL_FAILURE_KIND_ATTRIBUTE / TOOL_FAILURE_KIND_CANCELLED into
telemetry/constants.ts so coreToolScheduler and session-tracing share
one definition
- Add debugLogger to runToolTelemetrySink catch (was silent)
- Replace delete-based absence in withPostToolBatchStop with conditional
spread
* fix(core): address remaining review findings on tool execution status (#8180)
- Pass the frozen executionStatus variable instead of the literal
'success' in the post-hook-stop error response, keeping the frozen
value the single source of truth (finding 4)
- Force-finalize the deferred PostToolBatch parent span in the abort
drain, since that terminal path cancels the batch hook that otherwise
owns the span; documents the invariant at the call site (finding 6)
- Comment the loop-detection guard so the permission-cancellation
exclusion from invalid-param loop detection is explicit (finding 9)
- Rename the design doc to the dated docs/design convention and note
the schedule()/handleConfirmationResponse() resolution contract
change for embedders (finding 2, doc convention)
* docs(core): note schedule() resolution contract in tool execution status design (#8180)
Record the embedder-facing behavior change that schedule() and
handleConfirmationResponse() resolve with a terminal error call rather
than rejecting, so a failing tool no longer aborts its siblings.
* fix(telemetry): address review feedback for tool execution status (#8180)
- Document the new tool_call attributes (call_id, execution_status), the
qwen-code.tool.execution.count metric, the tool.execution span attributes,
and the tool.failure_kind=cancelled span field in telemetry.md.
- Pass ToolErrorType explicitly at loop-detection skip sites instead of
inferring it from the skip message string, so copy edits cannot silently
reclassify loop skips as approval denials.
- Simplify withPostToolBatchStop response construction (drop the
destructure-and-reattach used to preserve a missing execution status).
- Add a debug breadcrumb when a PostToolBatch stop has no span to attach to,
and a one-time warning when PostToolBatch hook detection fails open.
- Drop the try/catch wrapping the pure isTelemetrySdkInitialized getter.
- Clarify the design doc invalid-combination wording and note that the
execution-failure SLI cannot be attributed to a specific tool.
- Add a regression test pinning that schedule() resolves (not rejects) when a
tool execution throws.
* fix(telemetry): address round-7 review feedback for tool execution status (#8180)
* fix(telemetry): restore type-safety fallback for executionErrorType (#8180)
* fix(telemetry): align tool execution failure outcomes
Keep Core and ACP cancellation arbitration consistent, preserve structured post-processing errors, and restore QwenLogger MCP metadata privacy.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): address review suggestions for tool execution status (#8180)
* test(core,cli): strengthen test-efficacy for tool execution status (#8180)
* fix(core): address review suggestions for tool execution status (#8180)
- Improve post-processing cancellation message to indicate the tool had
already completed, preventing silent model redo of completed work
- Remove dead !isExecutionTimeout conjunct in Session.ts PostToolUse
cancellation check (unreachable: timeout always sets toolResult.error)
- Replace construct-then-delete with destructuring in withPostToolBatchStop
- Move all failure-kind constants to telemetry/constants.ts so the full
documented vocabulary lives in one place
- Re-export StructuredToolError from tool-error.ts instead of importing
from the unrelated priorReadEnforcement module
- Add JSDoc to normalizeToolCallEvent documenting key-absent semantics
- Add ordering-safety comment to createParentAbortRace microtask guarantee
- Document endToolExecutionSpan not_started guard as defence-in-depth
- Document PostToolBatch span leak window in finalizeToolSpan
- Add design doc note about hand-placed cancellation check invariant
- Add test for unknown execution_status normalization path
- Revert unrelated generate-notices.js formatting change
* fix(core): address review feedback for tool execution status (#8180)
- Gate cancel message on executionThrew so the model sees 'User
cancelled tool execution.' when execute() rejected under abort,
reserving 'already completed' wording for post-processing cancels
- Move StructuredToolError into tool-error.ts to break the
tool-error ↔ priorReadEnforcement module cycle
- Revert unrelated Prettier reformat in generate-notices.js
* test(core): pin both tool cancellation notices; extract them as constants
afd349ca gated the cancel message on executionThrew but left the two
wordings as bare literals at four sites and added no test. That is the
exact shape the bug had: it was introduced by editing one literal and
missing the others.
Extract TOOL_CANCELLED_{BEFORE,AFTER}_COMPLETION_MESSAGE so the four
sites cannot drift, and add regression tests for both paths — a tool
interrupted mid-flight (execute() rejected under abort) must report
"User cancelled tool execution.", while a cancel after execute()
returned must report that the output was discarded. The mid-flight test
fails against the pre-afd349ca behaviour.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(core): keep MCP reconnect for a timeout on a dead transport
Classifying every `-32001` as EXECUTION_TIMEOUT skips
handleReconnectOnError, which previously recovered one real case: the
transport dies mid-request, the SDK request times out because no
response will ever arrive, and the server is already recorded
DISCONNECTED. That reconnected and retried; now it hard-fails and the
user has to retry by hand.
Divert back to the reconnect path only on positive evidence the
transport is dead. Note that getMCPServerStatus() reports DISCONNECTED
for servers it has never seen, so the guard checks for a *recorded*
DISCONNECTED — the naive comparison misroutes every timeout from a
server whose status was never registered, which broke four existing
timeout tests when tried.
A timeout on a healthy server is still EXECUTION_TIMEOUT: retrying it
after a reconnect would just double the wait. The client-side idle
timeout keeps classifying unconditionally; it is our own timer, not a
transport signal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(core): address blocking review feedback for tool execution status (#8180)
Two blocking items from the maintainer review:
1. Post-processing cancellations dropped persistedOutputFiles (and
visionBridgeNotice) along with the model-visible output, orphaning
files the tool had already spilled to disk. createCancelledResponse
now carries both, and every cancelAfterPostProcessing site passes
what it has; the settle-then-abort and hook-stop paths do the same.
2. A -32001 that lands while the parent signal is aborted is the SDK's
abort rejection or a timeout that raced with a cancel; classifying
it EXECUTION_TIMEOUT would count user cancels against the timeout
SLI. isExecutionTimeoutFailure now defers to the abort in both
catch blocks, regardless of which side settled the race first. The
two tests that pinned the opposite timeout-wins ordering are
updated to the abort-wins semantics the review asked for.
Co-Authored-By: Qwen Code <noreply@alibaba-inc.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: qwen-code-bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <253268222+qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Qwen Code <noreply@alibaba-inc.com>
* fix(core): Clarify write_file prior-read guidance (#8428)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(desktop): read Windows smoke log from LocalAppData (#8381)
* fix(desktop): read Windows smoke log from LocalAppData
* fix(desktop): validate Windows smoke log path
* test(desktop): guard smoke log fallback branch and capture ordering (#8381)
* fix(desktop): cross-check smoke appId against tauri config and fail closed on log rotation (#8381)
* fix(desktop): reset smoke log baseline on truncation and test behavior (#8381)
The Tauri app truncates the log on every startup (main.rs:
fs::write(&log_path, b"")), so the second smoke run on any non-ephemeral
Windows machine always failed with "truncated or rotated". Reset the
baseline and keep polling instead of aborting.
Extract resolveLogRoot into a tiny module so test-release.js can verify
the platform/env resolution behaviorally rather than regex-matching
source text. Hoist the duplicated tauri.conf.json parse, add logPath to
the timeout diagnostic, and note the shared-log hermeticity constraint.
* test(desktop): pin stale-log protection wiring in smoke source guard (#8381)
* test(desktop): extract sliceNewLog helper and harden ordering assertion (#8381)
* test(desktop): pin resolveLogRoot and dual readNewLog sites in smoke guard (#8381)
* fix(desktop): log path in smoke errors; warn only on real truncation (#8381)
* fix(desktop): make smoke truncation warning reachable; relax guard regexes (#8381)
* fix(desktop): rebase smoke log baseline on truncation; embed full log on timeout (#8381)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(desktop): drop dead truncation flags from smoke log reader (#8381)
* fix(desktop): isolate packaged smoke settings
---------
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Qwen Code <qwen-code@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Avoid replaying unsafe MCP tool calls (#8387)
* fix(core): Avoid replaying unsafe MCP tool calls
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): Revalidate MCP replay after reconnect
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): stop goal retries on evidence exhaustion (#8430)
Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
* ci: externalize review timeout to repository variables (#8460)
Read review timeout settings from two GitHub Actions repository
variables instead of hardcoding them, so tuning no longer requires
a code change:
QWEN_REVIEW_JOB_TIMEOUT_MINUTES (default: 360)
- review-pr job-level hard cap (was hardcoded 300)
QWEN_REVIEW_MAX_TIMEOUT_MINUTES (default: 300)
- per-review max timeout: validation ceiling, large PR auto-scale,
and fallback comment message (was hardcoded 240 in 3 places)
Constraint: QWEN_REVIEW_JOB_TIMEOUT_MINUTES must stay above
QWEN_REVIEW_MAX_TIMEOUT_MINUTES so retry + comment posting never
hit the job-level cap.
* fix(review): stand a drifted launch whose payload provably arrived (#8466)
A model asked to copy the roster's twelve blocks normalized one word in
every block's tail ("you" -> "it"). Every launch failed the verbatim
containment check, check-coverage reported the whole roster undelivered,
and the run relaunched all twelve agents -- the most expensive repair in
the pipeline, spent redelivering text the agents had already acted on.
Measured on a live run: ~10M input tokens and 17 minutes of wall clock.
The verbatim check was written when the launch prompt carried the
payload. It no longer does: the brief on disk holds the method, the
severity bar and the project rules, and the transcript records whether
the agent opened it and whether it read the diff. When both facts are on
record, a drifted launch is a delivery that happened, not one that
failed.
check-coverage now reports such launches under driftedLaunches -- a
NOTE, not a failure: ok stays true, nothing enters the posted body, and
no relaunch is owed. The rescue is injective like the verbatim matching
(one transcript, one requirement) and requires the diff read for a role
whose brief reads the diff, so every true failure -- a drift with no
brief-open, a dropped read list, a hand-written prompt with no record --
stays exactly where it was. Step 4/5 delivery classification is
unchanged: a verify/reverse-audit launch carries the findings list in
the prompt itself, and drift tolerance there would excuse a dropped
payload.
Co-authored-by: verify <verify@local>
* feat(review): Add structured Web Shell review results (#8402)
* feat(review): add Web Shell review artifacts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(web-shell): add code review artifact visual scenario (#8402)
* fix(review): address Web Shell review artifact feedback (#8402)
* save-artifact: document why paths resolve against the daemon workspace
root (QWEN_CODE_PROJECT_DIR) instead of cwd, and cover the relative-path
form the skill documents with a test where the two roots differ.
* CLI/renderer contract: the renderer hand-duplicates the findings
vocabulary and fails closed on unknown values, so name the renderer as a
second consumer beside the CLI's lists and check in a contract fixture
generated through the real pipeline (validateFindings -> buildReport ->
save-artifact) that exercises every source, severity, confidence and
outcome. Exporting the vocabulary through the SDK stays deferred: it is a
public cross-package API change beyond this PR's seam.
* resolve-anchors now validates `line` exactly like `findings` does
(positive safe integer); the two validators in one pipeline no longer
disagree. Note: an in-flight `.qwen/tmp` findings file carrying `line: 0`
fails where it previously did not.
* The renderer validates markdownReportPath (relative, no ".." segments,
.md suffix) before it becomes a readWorkspaceFile call, resets the
severity/confidence filters when switching artifacts, and surfaces
heldByMeasurement so a nonzero Held count is attributable.
* save-artifact refuses low effort structurally (choices and library guard)
instead of by prose, stats the Markdown report before reading it so a
directory reports "not a file", and the component no longer shadows the
DOM `document` global.
* The case-insensitive alias test now skips visibly on case-sensitive
filesystems instead of passing vacuously.
* Comment the kept `turnOutputs.review` key and document the JSON
companion in the user docs.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(review): address second Web Shell review artifact feedback round (#8402)
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
* ci: reduce SDK Java runner queueing (#8441)
* ci: cancel stale SDK Java pull request runs
* fix(ci): preserve SDK Java push scheduling
* ci: route trusted SDK Java jobs to ECS
* ci: simplify SDK Java runner routing
* fix(ci): provision Maven on ECS Java jobs
* fix(ci): clean SDK daemon ECS test state
* fix(ci): satisfy SDK Java lint
* fix(core): align MCP reconnect timeout test with safe replay policy (#8478)
The reconnect-on-timeout test still built its mock tools without server
trust or tool annotations, which the safe replay change now requires
before automatically replaying a connection-loss failure. Update the
fixtures the same way the surrounding reconnect tests were updated,
keeping the test's original assertion that a timeout on a known
disconnected server goes through the reconnect path.
* feat(core): support Qwen 3.8 reasoning effort (#8472)
* fix(ci): align review workflow tests with externalized timeout variables (#8486)
#8460 moved the review timeouts into the QWEN_REVIEW_JOB_TIMEOUT_MINUTES and QWEN_REVIEW_MAX_TIMEOUT_MINUTES repository variables but left the workflow-text assertions in scripts/tests/qwen-resolve-workflow.test.js pinned to the old hardcoded 240/300 values, so the workspace test suite fails (Release Quality Checks and the PR Test job).
* chore(release): v0.21.5 (#8505)
* chore(release): v0.21.5
* docs(changelog): sync for v0.21.5
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* feat(browser-ext): add alpha readiness diagnostics (#6739)
* feat(browser-ext): add alpha readiness diagnostics
* test(browser-ext): automate readiness verification
* fix(browser-ext): support current devtools adapter
* test(browser-ext): verify restored page after reconnect
* fix(browser-ext): harden release and acceptance checks
* test(browser-ext): cover onboarding transitions
* fix(browser-ext): harden alpha diagnostics
* test(cli): sync serve capabilities baseline
* feat(browser-ext): add alpha readiness diagnostics
* test(browser-ext): automate readiness verification
* fix(browser-ext): support current devtools adapter
* test(browser-ext): verify restored page after reconnect
* fix(browser-ext): harden release and acceptance checks
* test(browser-ext): cover onboarding transitions
* fix(browser-ext): finalize Chrome Web Store package
* fix(browser-ext): harden CDP diagnostics per review feedback (#6739)
* fix(browser-ext): harden CDP diagnostics per review feedback (#6739)
Distinguish the ACP child's idle placeholder (initialized: false,
discoveryState: 'not_started') from a genuinely empty server list so
the panel no longer shows a false "adapter is not connected" warning
before the first session or after the child is reaped.
Compare the tunnel endpoint's host+port against the daemon baseUrl to
detect cross-daemon shadowing (a chrome-devtools entry pointing at a
different daemon's /cdp was previously reported as connected).
Guard package-extension and symlink tests with
skipIf(process.platform === 'win32') so the Windows merge-queue gate
does not fail on missing zip.exe or privilege-dependent symlinkSync.
Also: destructure QwenCapabilityStatus lazily inside probeState so a
missing capability-status.js no longer throws before the welcome
screen renders; add the missing license header to manifest-version.js;
replace the leftover #welcome height:100vh with flex sizing; add
cross-reference comments for the shared /cdp path pattern.
Note: probeJson intentionally drops the .catch(() => ({})) fallback so
a 200 with a non-JSON body reads as unreachable; this also makes
/health stricter than before.
* fix(browser-ext): resolve CDP diagnostics review findings (#6739)
* fix(browser-ext): mirror nightly build number in manifest test oracle (#6739)
* fix(browser-ext): address alpha diagnostics review feedback (#6739)
- declare the semver dependency used by manifest-version.js so an isolated
workspace install no longer relies on root hoisting
- make artifact-scan skip the root CLI bundle metafile with a warning when it
is absent (it only exists after `cross-env DEV=true npm run bundle`), keeping
the extension metafile required, so package-level test:release no longer fails
- throttle the side panel /workspace/mcp probe to every 5th tick and reuse the
cached snapshot in between, avoiding a cross-process RPC on every 2s poll
- document the per-session CDP event fan-out and pin single-path event counts;
note that Target.getDevToolsTarget is deliberately unsupported
- guard the nightly build-number git lookup and the zip end handler
- disclose the daemon-to-model-provider page-content flow in PRIVACY.md
- drop brittle source-substring panel tests and add coverage for a
chrome-devtools server with no config args
* fix(browser-ext): improve acceptance diagnostics and honest phase naming (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(cli): stabilize flaky orphan-session transport tests (#6739)
Replace hardcoded setTimeout(40ms) + assertion with vi.waitFor() in the
session/new and session/load orphan tests. The 40ms budget is too tight
under CI parallelism, causing intermittent removeSession-not-called
failures. vi.waitFor polls until the assertion holds (default 1s timeout),
matching the pattern already used elsewhere in this file.
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(cli): restore PAGE_SESSION_ID forwarding for lazy-attach path (#6739)
The autoAttachActive gate on PAGE_SESSION_ID command forwarding broke
the cdp-ws lazy-attach path, which sends commands with PAGE_SESSION_ID
without a Target.setAutoAttach handshake. Revert the forwarding gate
to unconditional PAGE_SESSION_ID acceptance while keeping the gated
Target.attachedToTarget emission (the Critical fix).
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): make survivor tests load-bearing with log assertions (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): address review feedback on diagnostics PR (#6739)
* fix(browser-ext): resolve review findings on diagnostics tests (#6739)
- reject preview-range QWEN_CHROME_EXTENSION_BUILD_NUMBER values at the
env var boundary with a message naming the variable, value, and range
- assert the package-extension symlink test observably ran main() instead
of passing on equality alone when both runs fail identically
- add CLI-level tests proving explicit positional roots are scanned and a
clean scan exits 0, covering paths the symlink-only tests skip on Windows
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* docs: document headless Goal workflows (#8503)
* feat(core): guide recording of PR, issue, and comment URLs as artifacts (#8453)
* ci: route trusted-author fork PRs and no-checkout jobs to the ECS pool (#8502)
* ci: route trusted-author fork PRs and no-checkout jobs to the ECS pool
Fork PRs whose author has write access (OWNER/MEMBER/COLLABORATOR association) now run Linux CI on the self-hosted ECS pool instead of the saturated GitHub-hosted quota, and bot workflows that check out no code move to ECS unconditionally. Everything stays gated on the MAINTAINER_ECS_RUNNER_DISABLED kill-switch.
* ci: address review — real write-permission routing, watchdog independence, timeouts
Route the triage agent on the collaborator-permission API result computed by authorize instead of the coarse author_association, which admits org members and read-only collaborators; the two permission-gate jobs revert to the same-repo guard. Keep the fleet watchdog and the CI-failure reporter hosted so they stay independent of the pool they watch. Add missing timeouts, wipe serve-ab's reused workspace, and pin the routing logic with drift and negative-case tests.
---------
Co-authored-by: 易良 <1204183885@qq.com>
* fix(web-shell): keep pending background agents active (#8413)
* fix(web-shell): keep pending background agents active
* test(web-shell): cover pending agent activity edge cases
* fix(web-shell): preserve background agent elapsed time
* test(web-shell): require shared active tool status classification
* refactor(web-shell): centralize active tool status
* fix(web-shell): keep parallel agents clock monotonic
* refactor(web-shell): consolidate active tool checks and walkers (#8413)
Drop the duplicate hasActiveTool wrapper in favor of the adapters'
hasActiveAgents so the package keeps a single any-active predicate,
share one turn walker between turnOwnsCallId and turnHasActiveAgent so
the tool-carrying DisplayItem set is encoded once, and cover the
parallel-agents clock latch re-arm when a second wave of agents starts
after the group went fully terminal.
* fix(desktop): rotate Tauri updater signing key (#8511)
* fix(desktop): rotate Tauri updater signing key
The original minisign private key paired with the pubkey in
tauri.conf.json was lost and could not be recovered from any local
worktree or branch. Generate a fresh keypair and update the public
key so the TAURI_SIGNING_PRIVATE_KEY GitHub Secret can sign updater
artifacts for the first stable Tauri desktop release.
* chore: trigger CI rerun
* chore: rerun CI
* chore: rerun CI (retry runner)
* fix(web-shell): add explicit ::selection for message content in Firefox (#8417)
* fix(web-shell): add explicit ::selection for message content in Firefox
Firefox does not paint the default selection highlight for text whose
element chain passes through a display:contents element (the
data-user-selectable wrapper on MessageItem). The logical selection
(copy, selectionchange popup) works fine - only the visual highlight is
missing. An explicit ::selection background makes Firefox paint the
highlight where the default painting fails.
Fixes #8214
* fix: use fixed color instead of non-existent CSS variable
--selection-bg was never defined in the codebase (only
--chat-editor-selection-bg exists in App.module.css). Use a fixed
hsl(210 100% 50% / 30%) to avoid confusion.
* test(web-shell): pin ::selection rule and soften root-cause framing
Reframe the standalone.css comment and PR description as a defensive
workaround, not a confirmed root-cause fix: the data-user-selectable
wrapper is shared by user and assistant rows, and the reporter's
screenshot shows an embedding-page toolbar this package does not ship.
Add a getComputedStyle(..., '::selection') assertion to the smoke e2e so
a future cleanup cannot silently drop the rule.
* fix(web-shell): move ::selection rule to component-scoped globals.css
The defensive ::selection rule for [data-user-selectable] message content
was in standalone.css, which is only loaded by the standalone app entry
(client/main.tsx) and the e2e harness. The npm package entry
(client/index.tsx via vite.lib.config.ts) never loads standalone.css, so
embedded deployments of @qwen-code/web-shell - including the reporter of
#8214 - did not receive the rule and still saw no selection highlight.
Move it to globals.css, which is imported by App.tsx and
WebShellTranscript.tsx and therefore ships with the component-scoped
stylesheet. Verified against the lib build: the rule now appears in
dist/index.js correctly scoped under
[data-web-shell-root][data-web-shell-shadcn]. The standalone app also
loads globals.css, so the e2e smoke pin still passes.
Addresses the review finding on standalone.css:119.
* test(web-shell): pin ::selection across all rows and in the lib bundle
Address review findings on the round-3 move to globals.css:
- The smoke e2e only sampled the first [data-user-selectable] row (the
user row in this fixture). Assert the rule on every selectable row so
a future narrowing to user rows keeps assistant rows covered.
- Nothing asserted the rule survives in the npm lib bundle - the
deployment this fix exists for. Add a build-artifact test that parses
the injected component CSS in dist/index.js and pins the scoped
[data-user-selectable] ::selection rule under [data-web-shell-root].
* test(web-shell): assert ::selection on selectable wrapper rows, not descendants
Per review: querySelectorAll('[data-user-selectable] *') counts element
descendants, not the wrapper rows themselves - a single user row renders
4+ descendants, so the >=2 invariant did not actually enforce that both
roles are present. Match the [data-user-selectable] wrappers directly and
sample one descendant per row.
* test(web-shell): match ::selection lib-bundle pin by effect, not notation
Per review (R6-1): the pin matched an exact selector substring
(including the space) and an exact prop name, coupling to the current
notation. A maintainer switching 'background' to 'background-color'
(the CSS Pseudo-Elements-4 name) would fail this test with a misleading
message while the e2e pin stayed green. Match the two selector halves
independently and accept either prop name.
---------
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* refactor(core): move review skill incident narratives to DESIGN.md (#8499)
* perf(review): issue independent setup calls in one response
Measured on a real small-PR run: the stretch from parse-args to the
first agent launch took 7 minutes of wall clock, one round-trip at a
time, on calls that never needed an order — pr-context, comment-status
and the Step 2 rules load are mutually independent reads. Step 1 now
tells the orchestrator to issue all three in a single response (the
same rule Step 3 already enforces for the agent fan-out) and to page
their outputs in shared responses too. comment-status loses its
wait-for-the-context-file guard in worktree mode: learning whether
inline comments exist cost a serial round-trip, while running it on a
commentless PR just writes an empty index. Step 6's two deterministic
gates (script-lint, test-plan) get the same one-response note.
The orderings that matter are kept explicit: fetch-pr before everything
(it creates the worktree and the plan), the roster after the rules load
(it bakes the rules into every brief).
* refactor(core): move review skill incident narratives to DESIGN.md
SKILL.md is injected wholesale into the review orchestrator's context on
every /review run and re-billed on each of its turns, and ~16KB of it was
incident narrative — accounts of past dogfood failures and measurements
that justify rules but are not themselves instructions.
Move 50 such narrative blocks into a new 'Measured incidents (moved from
SKILL.md)' section of DESIGN.md (47 anchors, not loaded at runtime),
leaving every rule in place with a short
'(measured; DESIGN.md — <anchor>)' pointer. Force-bearing figures stay
inline where the number is the argument (e.g. the ~161s cold npm ci, the
41% test-code median, the PR #6457 one-of-five checklist measurement).
No instruction, gate, format, flag, threshold, or ordering changed; the
YAML frontmatter and all 35 fenced code blocks are byte-identical, and
the MUST / Do not / never imperative counts are unchanged outside the
moved narrative text (verified by script).
SKILL.md: 237,847 -> 228,266 bytes; DESIGN.md: 106,708 -> 125,184 bytes.
* fix(review): keep DESIGN.md out of the runtime bundle and pin pointers
The slim refactor left DESIGN.md shipped beside SKILL.md in
dist/bundled/, so one curious read_file of the 125 KB maintainer
document would cost more context than the refactor saves. The bundle
copy now skips DESIGN.md, and SKILL.md gains a one-line guard telling
the orchestrator the pointers are for humans auditing a rule.
Also addresses review feedback: a test pins both directions of the
SKILL.md incident-pointer mapping, the transcribed-argument narrative
keeps its referent after the move, the incidents section title loses
its changelog suffix, and Step 2 no longer asks for a base fetch that
fetch-pr already performed.
* fix(review): gate setup batching by effort and consolidate incident blocks
Address round-1 review feedback on the skill-slim PR:
- Gate the ONE-response setup batch and the comment-status call to high
and medium effort, matching Step 2's low-effort skip.
- Scope the Step 6 lint/test-plan batching to same-repo PR reviews.
- Merge same-run incident blocks (self-composed Approve into the
paraphrased roster prompt; archive verdict into the narrated-away cap),
cross-reference the roster-size and relocated-Critical tellings, and
state the #8368 path in its block plus the pointer it was missing.
- Pointer-ize the last inline QQChannel narrative and fix the
scripts-nobody-ran summary to match its block.
- Extend the DESIGN.md exclusion to copy_files.js so the transpiled
dist/src build and the published core tarball stop shipping it.
- Pin the no-read_file guard and the batch ordering constraints in
SKILL.test.ts, and fail loudly on pointers the regex cannot parse.
---------
Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
* fix(core): harden Qwen 3.8 reasoning effort wire shape (#8488)
* fix(core): harden Qwen 3.8 reasoning effort wire shape (#8472 follow-up)
Follow-up to #8472, addressing the post-merge review findings:
- Drop enable_thinking/thinking_budget after the extra_body merge whenever
reasoning_effort ships: the Token Plan preset made qwen3.8-max-preview
carry both thinking knobs, and DashScope rejects reasoning_effort
combined with thinking_budget
- Family-gate the new tool_choice=required strip clause to qwen wire
models: reasoning_effort is an opaque sampling override on non-qwen
DashScope models, and dropping forced tool selection degraded their
structured side queries
- Prefix-match the qwen3.8-max family so dated snapshots and -latest
aliases receive the selected tier instead of silently collapsing to
enable_thinking
- Log the tool_choice strip; restore the effort-config JSDoc and comment
the request-level override copy
* fix(core): family-gate DashScope thinking-knob drop (#8488)
---------
Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
* perf(review): the review-round refinements for the setup batch (#8487)
The batched-setup paragraph merged to main through #8499's squash of its
base branch, without the two review rounds that followed on #8487. This
re-lands those refinements as the delta against main's current text:
- 'ONE response' now says separate tool calls, never an &&/;-joined
Shell chain — a chain changes the failure semantics (pr-context
failing must warn-and-continue, not skip the other two) and merges
the warning: size lines the paging decisions read.
- The rules load names its ref uniformly (<remote>/<baseRefName>, the
ref fetch-pr just updated) with the baseFetchFailed carve-out spelled
in both Step 1 and Step 2, replacing the prefer-local probe language —
deciding 'does <base> exist locally' costs exactly the serial turn the
batch removes, and an unresolvable ref makes load-rules report 'no
rules found', indistinguishable from a repo that has none.
- The incremental-cache read pairs with the fetch-report read where the
read is instructed (both read_file, genuinely parallel).
- The executable-script lint's enumeration counts file reviews — they
have a tree and scriptLintGate owes them the lint; the adjacent gate
note already said so.
Co-authored-by: verify <verify@local>
* fix(review): stop the reverse-audit loop while there is still time to report (#8468)
* fix(review): stop the reverse-audit loop while there is still time to report
Measured on CI run #8368 (+1699 lines): the iterative reverse audit ran to
its 5-round cap, each round a per-chunk fan-out whose findings then went
back through verification, and the loop consumed 3.5 of the job's 4
budgeted hours. The outer GNU-timeout kill arrived while round 5's
findings were still being verified. The review died holding every
confirmed finding it had; nothing reached the pull request.
The loop's rounds are driven by the orchestrator, but every round begins
at the same place: agent-prompt building the round's prompts. So the
builder becomes the loop's clock. When the environment carries a review
deadline (QWEN_REVIEW_DEADLINE_EPOCH, exported per attempt by the review
workflow) and the remaining time is inside the reserve kept for the last
verification, compose-review and submission (default 60 minutes,
QWEN_REVIEW_DEADLINE_RESERVE_SECONDS to override), a reverse-audit round
is refused: a BUDGET line on stderr, exit code 4, no prompt built and no
record written. The message carries the exact unreviewedDimensions entry
to file, so the disclosure that caps the verdict is the CLI's text, and
Step 6 proceeds with the findings already confirmed.
Local runs have no deadline and are untouched. A malformed deadline fails
open — the outer kill still bounds the run, and a broken variable must
degrade to today's behaviour rather than wedge every budgeted review at
round 1. The verifier is deliberately not gated: the reserve exists so it
can run.
* fixup: scale the deadline reserve to the externally-chosen budget
The budget is not this workflow's to assume: it arrives from a repository
variable, a workflow input, or a /review --timeout=N comment. A fixed
60-minute reserve would consume most of a 70-minute budget and refuse the
audit loop outright on a 30-minute one. The workflow now passes a reserve
of a quarter of the attempt, floored at 10 minutes and capped at 60; the
CLI constant remains only the fallback for a caller that sets a deadline
without a reserve.
* review feedback: admit the round only if IT fits, and cap deterministically
Three findings from review, all taken:
1. The gate budgeted for the tail but not for the round it admits — the
terminal round is by construction the one that starts closest to the
boundary, so the killed-mid-verification failure survived one round
wide. The gate now requires remaining >= round + reserve, where the
round's cost is the previous round's, measured admission-to-admission
from a stamp the builder writes (one per round; a same-round rebuild
is not a round), falling back to a 30-minute constant for round 1,
which starts with the most headroom.
2. The refusal was deterministic; the disclosure that caps the verdict
was prose the orchestrator had to carry. The builder now records a
budget-stop marker beside the prompt records and compose-review
synthesizes the unreviewedDimensions entry from it — deduped against
a relayed copy — so a run that drops the sentence still cannot
approve past a truncated audit.
3. Exit code 4 is documented in the command's describe.
Also restores the Step 5 bullet the previous commit's edit displaced
(new findings merge into the cumulative list before the next round).
* review feedback: pin the budget gate's all-chunks refusal and ordering
Cover the two behaviours the review noted were only asserted on the bare
--findings form: an exhausted budget refuses the loop's real
--all-chunks round before ANY of the per-chunk records is written, and
a malformed call (--round 0) still gets its validation error first —
exit 4 is for a well-formed round the budget refuses, never a
replacement error. Also name what the code already does: reserve=0 is
the deliberate escape hatch (the gate shrinks to the round estimate
alone), and the workflow's 3600s cap mirrors DEFAULT_RESERVE_SECONDS.
* docs(review): describe the soft-deadline env vars for time-budgeted runs
The review noted the two new variables appeared in no user-facing doc;
the reserve in particular is an operator-facing knob. State what each
does, the fail-open posture, and how the refusal surfaces in the verdict.
* fix(cli): align budget-stop disclosure with the gate's refusal (#8468)
A round-1 budget refusal left no reverse-audit records, so the Step 4/5
floor reported the deliberate stop as a rogue/unlaunched audit with a
rebuild FIX the same gate deterministically rejects; the refusal's own
disclosure was swallowed by the caller-echo dedup. The floor now stands
down when the budget-stop marker exists, and compose-review renders the
disclosure structurally, bilingually, from the marker.
Also: `--role reverse-audit` requires `--round <k>` (an unlabeled
admission stamps an entry no estimate can attribute), the budget gate
runs after the plan/findings reads (a broken plan or unreadable findings
deserves its own error, and nothing is stamped ahead of a buildable
call), and the gate's admission boundary, measured-cost behaviour, and
the workflow env contract are pinned by tests.
* review: a budget stop excuses only the round it refused
The budget-stop suppression keyed on the marker's existence alone, so
every reverse-audit gap shape went silent once any round was refused —
including the shapes that describe rounds which RAN before the budget
hit. A hand-written round-1 launch is exactly as undelivered when round
3 later hits the budget, and suppressing its disclosure let 'stopped
before round 3' imply the rounds that did run were faithful.
Exactly one shape is by design under a marker: not-built — the refusal
writes no record, so an audit with no records IS the audit the gate
stopped, and its FIX (rebuild the round) would be refused by the same
gate. The suppression now names that shape and no other; a rewritten,
unlaunched or brief-unread round keeps its disclosure and its repair.
The new test pins the operative halves: the verdict stays capped, the
marker's disclosure posts, and the operator channel carries the
rewritten round's exact repair. (The posted body collapses same-subject
disclosures — both say 'reverse audit' — so the author sees the stop;
repairs are acted on from stderr, where the rewritten fix rides.)
* fix(review): fence budget state per run, and let gate errors beat budget stops
Address the round-2 review threads on the reverse-audit budget gate:
- Fence budget-rounds.json and budget-stop.json by the plan's own mtime.
Every run rewrites the plan at its Step 1 capture, so records older than
the plan belong to a previous run of the same PR: a run killed before
cleanup no longer prices the next run's rounds off stale stamps (an
hours-old stamp read as an hours-long round refused round 1 of a fresh
budget) and no longer caps a later run's verdict on a stop that did not
happen in it (R2-1, R2-2).
- Refuse a structurally unbuildable plan (no chunks[], duplicate or
non-integer ids) with its own error ahead of the budget gate, so the
same corruption gets the same diagnosis whatever the clock says, and no
budget-stop marker is written over a corrupt plan (R2-5).
- Stamp a round admitted only after its build succeeds: a build that
throws leaves no stamp, so the next round's cost is never measured from
a build that produced nothing and floored to 600s (R2-6).
- Keep the budget entry's 'reverse audit' subject out of the caller-echo
prefix filter: other reverse-audit scopes the orchestrator disclosed
(a twice-whiffed chunk from the rounds that DID run) are no longer
silently dropped in the marker's shadow; the marker's own relays stay
deduped by the phrase splice (R2-7).
- Render --round unbracketed in the reverse-audit rebuild fix — the CLI
refuses a round-less reverse-audit call, so the paste-and-run repair
must not present the flag as optional (R2-14).
- Document the deliberate one-verification overlap between the measured
round estimate and the tail reserve, at both definitions (R2-13).
- Test hardening, each assertion mutation-probed to fail its named
mutant: a reshaped relay only the marker-phrase splice dedups (R2-8);
the stamp's round label and the verifier's no-stamp invariant (R2-9);
whole-line, unit-arithmetic and reserve-cap pins on the CI wiring
contract (R2-10); the first-wins stamp survivor (R2-11); the reserve=0
escape hatch (R2-12).
---------
Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
* fix(autofix): normalize paginated fetches to one flat array per file (#8438)
* fix(autofix): normalize paginated fetches to one flat array per file
gh api --paginate emits one JSON array PER PAGE, so any PR past 100
comments/reviews/events produces a multi-document stream. The workflow
already slurps correctly in a few readers (jq -rs add, --slurpfile +
add), but more than a dozen plain-jq consumers of the WORKDIR files
mis-aggregate on a multi-doc input:
- MARKERS/REARM_AT/RED_HEAD/REARM_KEY in the scan and their LIVE_*
mirrors in prepare emit one result per page; ROUND then becomes a
multi-line string, [[ -ge ]] arithmetic fails, and the round cap
silently stops holding — on exactly the PRs (takeover, 100-round cap,
one report comment per round) that reach page two first.
- CAP_NOTICED / BASE_UPDATE_RECENT / LAST_REJECTION / PRIOR_TIMEOUTS /
the milestone census and the report-step consecutive-failure census
all degrade the same way.
- NEWEST and LIVE_NEW bind rv/rc/ic/checks POSITIONALLY (.[0]..[3]); a
two-page rv.json shifts rc/ic into the wrong slots and later
feedback is silently lost.
Fix at the fetch sites: every --paginate that lands in a WORKDIR json
file (and the report step's COMMENTS_JSON fallback) now pipes through
jq -s 'add // []', so each file holds ONE flat array. Existing
slurp-style readers are unaffected — add is idempotent over a single
array — and every plain consumer becomes correct past 100 items with no
program changes. Failure semantics are preserved: the workflow-level
bash default gives -eo pipefail, so a failed gh still fails the
pipeline exactly where it failed the bare redirect before, and the
pr-events/COMMENTS_JSON fallbacks keep their '[]' paths.
The check-runs/annotations/status-comment reads stay raw on purpose:
they aggregate per-page via --jq + slurp, line-streams, or .[][] and
were already pagination-safe.
Tests: a behavioral case runs the real MARKERS→ROUND pipeline and the
positional NEWEST program against two-page fixtures through the
normalizer, with negative controls demonstrating the pre-fix corruption
(two MARKERS lines; the page-2 review timestamp lost to slot shift).
Shape assertions pin all nine normalized fetch sites and ban raw
--paginate file redirects.
* fix(autofix): pin total --paginate occurrence count in tripwire test (#8438)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): correct gh --paginate merge model in pagination comments (#8438)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): make engage-ack ic re-fetch atomic on failure (#8438)
* test(autofix): pin atomic engage-ack re-fetch and empty-input normalization (#8438)
---------
Co-authored-by: verify <verify@local>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(web-shell): bind plan approval to its Todo revision (#8393)
* feat(web-shell): gate session workflow behind experimental setting
* feat(web-shell): bind plan approval to todo revision
* fix(cli): clear stale workflow revision on plan entry
* test(web-shell): pin revised workflow snapshot
* fix(cli): clear stale plan revisions on restore
* fix(cli): keep replayed history from rebinding plan revisions
History replay re-sends stale plan updates through Session.sendUpdate,
re-stamping activeTodoPlanRevision from finished plan cycles. Clear the
revision after every replay path (cold replayHistory and live non-bulk
loadSession) so a replayed snapshot can never bind a later exit_plan_mode
approval; reloaded sessions fall back to text-only approval until the next
live todo_write re-establishes the binding. Also drop the bulk-load restore
that could never be read before a plan-mode transition cleared it, and pin
the workflow gates and mode-entry clears with negative tests.
* test(web-shell): pin older plan revision in ChatPane approval test (#8393)
* test(cli): pin unbindable plan updates in approval revision test (#8393)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): clear Todo plan revision on history restore (#8393)
restoreHistory was the one history-resetting path that kept
activeTodoPlanRevision, so a restored snapshot could let a stale
revision bind the next exit_plan_mode approval. Clear it like the
sibling reset paths, pin the behavior with a test, and pin the
live-load clear ordering after the replayed updates.
* fix(cli): restore Todo stop guard clear on plan re-select (#8393)
The previous-mode guard added for the revision binding also skipped the
Todo Stop Guard trust clear on a redundant plan re-select; scope the
guard to the revision reset so every transition into plan clears the
stop guard as before. The replay-time revision clears now run in
finally blocks so a transport failure part-way through a replay cannot
leave a replayed binding on the live session, and the web-shell
exit-plan approval rule is unified in one predicate. Revision tests
assert through the observable qwenTodoApproval approval metadata
instead of the private field.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
* fix(core): resolve DashScope thinking-knob conflicts by family (#8488 round 2) (#8536)
Second review round on #8488:
- Honour an explicit extra_body enable_thinking: false on the qwen3.8-max
family as reasoning_effort: 'none' instead of silently deleting it and
re-enabling thinking
- Legacy qwen hybrids now drop the inert reasoning_effort override when it
conflicts with a meaningful thinking_budget, keeping the knobs the model
actually reads
- Family-gate the pipeline's enable_thinking tool_choice clause like the
reasoning_effort clause: on non-qwen models sharing the endpoint the
field is an opaque no-op (GLM reads thinking.enabled), and stripping
forced tool selection there degraded their side queries
- The tier-native disable path emits reasoning_effort: 'none' — the knob
the family reads — instead of enable_thinking: false; the
required-thinking retry trigger recognises the new shape so runtime
learning still fires
- Warn once per generator (not debug per request) when user extra_body
knobs are dropped; hoist the wire-model family predicates to
modalityDefaults.ts and share the provider's extra_body merge tail
- Tests for every behavior above (all load-bearing, verified by targeted
mutation); docs attribute the vendor rejection to thinking_budget only
and document the extra_body exceptions
* fix(cli): stop review test-efficacy tests depending on ambient tmpdir vitest (#8537)
Two tests failed on hosts where vitest resolves up-tree from os.tmpdir()
(observed on self-hosted CI where a node_modules above TMPDIR provides
one): findVitestBin's "cannot be resolved" case never threw, and
runControlMutant's "cannot run" case executed the probe for real instead
of throwing.
Make the failure conditions host-deterministic while keeping every
assertion: findVitestBin accepts an injected resolver (default
unchanged) so the MODULE_NOT_FOUND case is forced directly, and the
runControlMutant test plants a shadow vitest whose exports hide
package.json, which wins resolution from any ancestor install and makes
the run fail deterministically.
* feat(serve): add a required external tool guard provider (#8125)
* feat(serve): add required external tool guard
* fix(serve): keep guard constants off fast path closure
* test(serve): cover guard startup options
* refactor(acp-bridge): centralize external tool guard validation and ack value (#8125)
* fix(core): align MCP reconnect timeout test with safe replay policy (#8125)
The reconnect-on-timeout test still built its mock tools without server
trust or tool annotations, which the safe replay change now requires
before automatically replaying a connection-loss failure. Update the
fixtures the same way the surrounding reconnect tests were updated,
keeping the test's original assertion that a timeout on a known
disconnected server goes through the reconnect path. Mirrors the same
alignment already landed on main.
* fix(cli): alias externalToolGuard subpath for vitest source resolution (#8125)
This PR added `@qwen-code/acp-bridge/externalToolGuard` imports to cli
serve/acp modules but not the vitest source alias every other acp-bridge
subpath carries. Without it, any vitest run whose acp-bridge dist is
stale or absent fails to resolve the import and the five serve test
files die at transform time. Add the alias following the documented
convention in the config so tests read the live source.
* fix(serve): reject non-ASCII external tool guard bearer tokens
A token outside the ASCII range passed construction but made the
handshake throw ERR_INVALID_CHAR when interpolated into the
Authorization header, blocking qwen serve startup in required mode
with an unexplained error. Enforce printable ASCII (0x21-0x7E) at
validation time so the configuration fails fast with a clear message.
---------
Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* fix(desktop): codesign ripgrep and node binaries before tauri build (#8518)
* fix(desktop): codesign ripgrep and node binaries before tauri build
macOS notarization rejects the app bundle because Tauri only signs the
main binary, not the embedded ripgrep and Node.js runtime binaries under
Contents/Resources/runtime/qwen-code/. Add a pre-build codesign step
that signs all native macOS executables in the bundled runtime with the
Developer ID identity, hardened runtime, and the existing entitlements.
* fix(desktop): allow Windows build without signing certificate
The Tauri release workflow threw when WINDOWS_CERTIFICATE was missing,
blocking the entire release (including macOS). Mirror the old Electron
workflow behavior: warn and continue unsigned when no cert is configured.
Also add fallback to legacy WIN_CSC_LINK/WIN_CSC_KEY_PASSWORD secrets
so existing Electron-era credentials still work if present.
* fix(desktop): allow unsigned Windows artifacts in verify step
The 'Verify Windows signature' step threw on any non-Valid status,
including NotSigned. With no Windows code signing certificate
configured, this blocked the Windows build job, which in turn
blocked the publish job (needs: [prepare, build]).
Allow NotSigned with a warning instead of throwing, matching the
fallback behavior of the 'Import Windows certificate' step. A
genuinely invalid signature (HashMismatch, etc.) still throws.
* fix(desktop): narrow find scope and deduplicate pfx import
- Scope ripgrep codesign find to *-darwin/* paths so Linux ELF
binaries (built in the same matrix) aren't targeted.
- Unify the two pfx-import branches into a single code path to
eliminate duplicated write/import/configure logic.
* fix(desktop): guard optional Windows signing config
* fix(desktop): harden vendor signing workflow
* fix(core): reuse prompt cache for multimodal compression (#8419)
* fix(core): reuse prompt cache for multimodal compression
* test(core): remove inert media modality fixture
* fix(core): guard compression cache-sharing window
* fix(core): harden compression cache-sharing gate
* fix(core): tighten compression cache preflight
* test(core): pin lazy compression slimming
* fix(cli): preserve Qwen Review startup version in footers (#8431)
* fix(cli): preserve review startup version in footers
* fix(cli): keep review startup version dynamic in bundle
* fix(cli): reset review version after managed update
* test(cli): use indexed env access
* fix(cli): harden review footer strip and version stamping (#8431)
* fix(tests): sync qwen-resolve-workflow expectations with externalized review timeouts (#8431)
The timeout externalization in #8460 replaced the hardcoded 300/240
values in qwen-code-pr-review.yml with the QWEN_REVIEW_JOB_TIMEOUT_MINUTES
and QWEN_REVIEW_MAX_TIMEOUT_MINUTES repository variables but left
scripts/tests/qwen-resolve-workflow.test.js asserting the old literals,
so the full-profile Test job fails on any branch carrying that change.
Update the three affected assertions to pin the externalized shape.
* fix(cli): harden the review footer strip per review feedback (#8431)
The strip regex kept a 2^(N-1) partition ambiguity for same-line footer
runs (measured 5.3 s at n=20) and missed footers truncated before their
closing `_`; forged footers also survived on the body channel through
`bodyCriticals`, and the values interpolated into the footer were not
shape-validated. Guard the repeated group so an iteration cannot span
another footer's start, make the final `_` optional, strip body
Criticals per entry, refuse footer-forging model ids and non-version
stamps, refuse non-object comment entries, pin the CLI-glue test suite
against an ambient startup stamp, and cross-assert the LGTM filter
regex against the footer builder.
* fix(cli): strip review footers before ledger carryover
* fix(cli): align ledger footer regression expectation
---------
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
* fix(serve): detect lineEnding across the file, not the returned slice (#8383)
* fix(serve): detect lineEnding across the file, not the returned slice
`readText` reported `meta.lineEnding` from the slice it was about to return.
A slice holding a single CRLF line arrives as text ending in '\r' — the '\n'
was consumed as that line's terminator — so detecting on it answers 'lf'. Page
one of a cursor sequence then disagreed with page two about the same file, and
a client that trusts the first page would rewrite CRLF content as LF.
The truncation branch re-detected on the truncated slice for the same reason
and had the same flaw.
Detect on the whole decoded file once, which is what the field is describing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(serve): guard byte-truncated reads against slice-based lineEnding re-detection
* fix(core): report crlf on cursor pages resuming after a CRLF terminator (#8383)
* fix(core): count a skipped CRLF terminator on byte-truncated pages (#8383)
When a window's first line exceeds both the read-chunk size and
maxOutputBytes, the byte cut fires before the line's terminator is decoded,
and the re-snap then walks over that terminator without reading it. The next
page seeds from the pair and reports 'crlf' while the cut page reported 'lf'
— adjacent pages of one file disagreeing, the exact symptom this PR removes.
Consume the same two-byte evidence after the re-snap so the pages agree.
Also qualify the design-doc agreement guarantee to files with uniform line
endings (mixed-ending files can still flip between pages), and pin the
seed's load-bearing placement with tests: it must run on the snapped offset,
and the minimum probe offset (startOffset == 2) is now covered.
* docs: correct the lineEnding spec for byte-cursor pages (#8383)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* docs: qualify the mixed-EOL verification bullet for byte-cursor pages (#8383)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* docs: name the uniform-file line-window-vs-cursor lineEnding split (#8383)
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* perf(core): clear tool results to a low watermark to preserve prompt cache (#8464)
* perf(core): clear tool results to a low watermark to preserve prompt cache
Size-triggered microcompaction now clears oldest compactable tool results down to half the threshold instead of stopping just below it, so the conversation prefix stays stable between clearings and provider prompt caches keep matching. The recent-result budget now protects committed results only; pending results no longer consume protection slots but stay counted, uncleared, and live for file-read-cache resolution. Adds the watermark to cleanup metadata and the debug log.
Fixes #8463
* fix(core): harden size-cleanup protection against zero-char and pending refs
Review follow-up for the low-watermark change: keepRecent now selects from committed results that are actually clearable (positive, successful, uncleared output), so trailing errors, prior placeholders, and empty outputs no longer absorb protection slots. Pending refs are dropped from the keep set entirely — a pending read may be a cache-hit placeholder rather than file bytes, so it must not suppress eviction reporting; over-disarming only costs a redundant re-read. Adds regression tests for both plus the protected-saturation consecutive-trigger corner.
* qwen: address PR review feedback (#8464)
Pin the (soft-exceeded) log marker with the one-line assertion suggested by the sandboxed verification report (finding S-1): the all-protected overage test now asserts 'target 250000 (soft-exceeded)', killing the surviving mutant M4.
* qwen: address PR review feedback (#8464)
* qwen: address PR review feedback (#8464)
* qwen: address PR review feedback (#8464)
Two P1 context-integrity fixes from review: (1) media-only tool results (image/PDF reads with empty text output and bytes on functionResponse.parts) stay in the idle-path keepRecent candidates instead of being dropped by the zero-char filter; (2) only write_file results vouch for file residency in kept-path accounting — edit calls carry just old/new snippets while still setting the cache's sticky full-read flags, so a kept edit can no longer suppress eviction reporting after the full read is blanked. Regression tests for both.
* qwen: address PR review feedback (#8464)
Pin the absence of the (soft-exceeded) marker at the exact watermark boundary: clearing that lands the virtual total exactly on the watermark must not be flagged. Kills the >= and always-true mutants of the marker condition that previously surviv…1 parent 547c885 commit c0d17e2
8,233 files changed
Lines changed: 3310326 additions & 724 deletions
File tree
- .github
- ISSUE_TEMPLATE
- actions
- configure-windows-runner
- post-coverage-comment
- self-hosted-node
- verify-checkout-head
- assets
- exit-resume-echo
- worktree-toggle
- scripts
- ci
- dsw-swe-verified
- workflows
- .husky
- .qwen
- agents
- e2e-tests
- skills
- agent-reproduce-align
- references
- scripts
- agent-reproduce-feature
- references
- scripts
- autofix
- scripts
- bugfix
- ci-flaky-patrol
- codegraph
- evals
- create-issue
- deflake
- desktop-pet
- scripts
- docs-audit-and-refresh
- references
- docs-update-from-diff
- references
- e2e-testing
- references
- scripts
- feat-dev
- memory-leak-debug
- examples
- scripts
- openwork-desktop-sync
- prepare-pr
- qwen-code-claw
- repo-hygiene
- references
- scripts
- structured-debugging
- examples
- terminal-capture
- tmux-real-user-testing
- scripts
- triage
- references
- verify-pr
- specs
- .vscode
- docs-site
- scripts
- src/app
- [[...mdxPath]]
- docs
- assets
- design
- adaptive-output-token-escalation
- assets
- auth
- auto-memory
- channels
- compact-mode
- compaction-image-stripping
- ctrl-o-detail-expand
- assets
- customize-banner-area
- daemon-acp-http
- daemon-session-artifacts
- daemon-sidechannel-coordination
- daemon-transport-abstraction
- fork-subagent
- hot-reload
- performance
- prompt-cache
- prompt-suggestion
- rt-optimization
- session-crash-recovery
- session-idle-reaper
- session-recap
- session-title
- skill-nudge
- slash-command
- standalone-clipboard-native-addon/assets
- structured-output
- tool-use-summary
- tools
- usage-only-stream-memory/assets
- virtual-viewport
- vp-mouse-selection
- web-shell
- web-shell-pane-header-actions
- developers
- daemon-client-adapters
- daemon-ui
- daemon
- development
- examples
- tools
- e2e-tests
- images
- plans
- superpowers
- plans
- specs
- users
- configuration
- extension
- features
- channels
- ide-integration
- reference
- support
- verification/abort-controller-refactor
- scripts
- eslint-rules
- integration-tests
- baselines
- cli
- concurrent-runner
- examples
- toy-project
- fixtures
- idle-mcp
- mock-acp-child
- settings-migration
- hook-integration
- interactive
- sdk-typescript
- terminal-bench
- ci-tasks
- hello-world
- tests
- swe-bench-astropy-1
- terminal-capture
- scenarios
- skill-review-harness
- integrations/external-context
- examples
- src
- packages
- acp-bridge
- src
- internal
- audio-capture
- native
- src
- channels
- base
- src
- __fixtures__
- dingtalk
- src
- feishu
- src
- github
- src
- gitlab
- src
- plugin-example
- src
- qqbot
- src
- telegram
- src
- wecom
- src
- weixin
- src
- chrome-extension
- config
- docs
- public
- icons
- scripts
- src
- background
- daemon
- sidepanel
- cli
- src
- acp-integration
- service
- session
- emitters
- rewrite
- agent-view
- commands
- channel
- extensions
- examples
- agent
- agents
- commands
- commands/fs
- context
- mcp-server
- skills
- skills/synonyms
- starter
- agents
- commands/writing
- skills/synonyms
- mcp
- review
- __fixtures__
- lib
- sessions
- config
- migration
- versions
- core
- dualOutput
- export
- hooks
- i18n
- locales
- nonInteractive
- control
- controllers
- types
- io
- patches
- remoteInput
- runtime
- serve
- acp-http
- auth
- cdp-tunnel
- acceptance
- fs
- live
- routes
- server
- voice
- workspace-service
- __tests__
- services
- insight
- generators
- types
- prompt-processors
- test-commands
- tips
- startup
- test-utils
- ui
- auth
- commands
- components
- __snapshots__
- agent-view
- arena
- background-view
- extensions
- steps
- __snapshots__
- tabs
- views
- hooks
- mcp
- steps
- messages
- __snapshots__
- skills
- subagents
- create
- manage
- views
- __snapshots__
- contexts
- daemon
- editors
- hooks
- layouts
- models
- noninteractive
- selection
- state
- themes
- utils
- __snapshots__
- export
- formatters
- voice
- utils
- housekeeping
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
| 1 | + | |
| 2 | + | |
| 3 | + | |
3 | 4 | | |
4 | | - | |
5 | | - | |
6 | | - | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
7 | 9 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | | - | |
| 1 | + | |
| 2 | + | |
| 3 | + | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | | - | |
8 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
9 | 12 | | |
10 | | - | |
11 | | - | |
| 13 | + | |
| 14 | + | |
12 | 15 | | |
13 | | - | |
14 | | - | |
15 | | - | |
| 16 | + | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
19 | | - | |
20 | | - | |
| 21 | + | |
| 22 | + | |
21 | 23 | | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
| 24 | + | |
32 | 25 | | |
33 | 26 | | |
34 | 27 | | |
35 | | - | |
36 | | - | |
| 28 | + | |
| 29 | + | |
37 | 30 | | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
| 31 | + | |
| 32 | + | |
78 | 33 | | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
| 34 | + | |
| 35 | + | |
84 | 36 | | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
| 37 | + | |
92 | 38 | | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
98 | 45 | | |
99 | 46 | | |
100 | 47 | | |
101 | | - | |
102 | | - | |
| 48 | + | |
| 49 | + | |
103 | 50 | | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
| 51 | + | |
| 52 | + | |
118 | 53 | | |
119 | | - | |
120 | | - | |
| 54 | + | |
| 55 | + | |
121 | 56 | | |
122 | | - | |
123 | | - | |
| 57 | + | |
| 58 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | | - | |
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
4 | 6 | | |
5 | | - | |
| 7 | + | |
6 | 8 | | |
7 | | - | |
8 | | - | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
9 | 14 | | |
10 | | - | |
11 | | - | |
| 15 | + | |
| 16 | + | |
12 | 17 | | |
13 | | - | |
14 | | - | |
| 18 | + | |
| 19 | + | |
15 | 20 | | |
16 | 21 | | |
17 | 22 | | |
18 | | - | |
19 | | - | |
| 23 | + | |
| 24 | + | |
20 | 25 | | |
21 | | - | |
22 | | - | |
| 26 | + | |
| 27 | + | |
23 | 28 | | |
24 | 29 | | |
25 | 30 | | |
26 | | - | |
27 | | - | |
| 31 | + | |
| 32 | + | |
28 | 33 | | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
| 34 | + | |
| 35 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
0 commit comments