Skip to content

[pull] main from QwenLM:main - #540

Merged
pull[bot] merged 17 commits into
bit-cook:mainfrom
QwenLM:main
Sep 2, 2026
Merged

[pull] main from QwenLM:main#540
pull[bot] merged 17 commits into
bit-cook:mainfrom
QwenLM:main

Conversation

@pull

@pull pull Bot commented Sep 2, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

yiliang114 and others added 17 commits September 2, 2026 11:38
…es (#10803)

* fix(ci): stop stable releases from disabling per-workspace test retries

The workspace shards passed --retry=0 on stable releases, and a command
line option outranks the Vitest config, so the release lane switched off
the retry packages/sdk-typescript sets for itself while every other lane
kept it. Omit the flag instead of zeroing it, and refresh the stale.yml
comment that still described the nightly release as running at 00:00 UTC.

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

* test(ci): preserve empty retry arguments in assertion

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…ed package early (#10784)

* fix(release): raise the package size budget and stop hiding sandbox build failures

The docker sandbox build in `integration_docker` has been failing every
release since 0.22.3-release-7df5ac68. The build itself is fine; what fails is
`npm run prepare:package` inside the Dockerfile's builder stage:

    Error: Prepared package unpacked size 101897869 bytes exceeds 100663296 bytes

`assertPreparedPackageSize`'s 96 MiB budget was set in #6691 when the prepared
package was ~80.6 MiB. It has grown with ordinary feature work since —
96,802,417 bytes at 0.22.1, 98,582,313 at the 08-31 nightly, 101,897,869 now,
with the OpenTUI migration batches carrying two UI stacks at once — and it
crossed the ceiling. Raise the budget to 112 MiB, the same "headroom, not
target" call #6688 and #6691 made before it. vendor/ripgrep (24 MiB, five
platform binaries) and web-shell/assets (18 MiB, mostly shiki grammars,
mermaid and cytoscape) are the two places worth trimming if we would rather
bring the package down than keep raising the ceiling; that is a separate
change.

The reason a one-line size error cost a 20-minute release run to diagnose is
the other half of this commit:

- `build_sandbox.js` sent the image build's output to /dev/null unless VERBOSE
  was set, so the failure surfaced as an execSync stack trace with
  `stdout: null` and no way to tell what broke without rebuilding by hand.
  `e2e.yml` had already worked around this by setting VERBOSE; the release
  workflow had not. Now CI streams the output, and a quiet local build keeps
  it and prints the tail when the build fails.

- Nothing ran `prepare:package` before the publish job, so its guards were
  first exercised by the Dockerfile — the slowest consumer in the pipeline.
  `quality_build` now runs bundle + prepare:package after uploading its build
  artifact, which reports the same failure in minutes without touching what
  downstream jobs consume.

* fix(release): close sandbox output review gaps

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…eases (#10814)

* feat(release): add gated bun/OpenTUI preview flavor to standalone releases

Phase 2 validation needs a real archive that boots the OpenTUI renderer,
so the standalone pipeline gains an opt-in second flavor instead of
replacing the classic Node.js one:

- create-standalone-package.js derives archive names from a shared
  standaloneArchiveName() helper; bun archives carry the
  -opentui-preview suffix, and bun shims default QWEN_TUI_RENDERER to
  opentui (explicit user overrides still win).
- build-standalone-release.js accepts --include-opentui-preview to
  package both flavors in one run, with per-flavor publisher checksums
  and a flavor-aware output assertion.
- verify-installation-release.js accepts the same flag so a gated
  release directory/URL and the OSS upload list both expect the preview
  archives.
- release.yml and sync-release-to-oss.yml thread the flag behind a
  vars.OPENTUI_PREVIEW_RELEASE_ENABLED gate (off by default, byte-identical
  behavior when unset); .size-baseline re-pinned in the same PR.
- Also fix a latent crash in copyOpenTuiAddon's warn-only degradation
  path, which asserted against a lib/node_modules directory it never
  created.

* feat(release): add a thin installer for the gated opentui-preview archives

The preview flavor is not reachable through the hosted installer chain,
which only packages assets for stable releases, so testers had to download
and unpack the archive by hand. This adds a scratch-directory installer
pair that resolves the platform target, verifies the release SHA256SUMS,
and unpacks into ~/.qwen-preview without touching PATH or an existing
classic installation. A local archive can be installed directly so the
path is testable before a gated release exists.
)

Release run 33576013293 (nightly 77d41f4) failed with 211 test files and
9480 tests passing and the job still exiting 1:

    Vitest caught 1 unhandled error during the test run.
    Error: write after end
     ❯ wt.write       node_modules/tar/node_modules/minipass/src/index.ts:547
     ❯ wt.[process]   node_modules/tar/src/pack.ts:352
     ❯ wt.[jobDone]   node_modules/tar/src/pack.ts:378
    This error originated in "src/extension/archive-safety.test.ts"
    The latest test that might've caused the error is "rejects a hard link
    even when it points inside the archive root"

packages/core/vitest.config.ts sets dangerouslyIgnoreUnhandledErrors to false
on Linux, so an unhandled error fails a CI run whose tests all passed — and it
is ignored on macOS, which is why this never appears locally. The log carries
no FAIL line, so the release notification is indistinguishable from a real
test failure.

tar's [PROCESS] finalizes the archive when the queue drains, and [JOBDONE] has
a branch only hard links reach: stat.nlink > 1 re-processes the pending link
job and re-enters [PROCESS]. Under contention that second pass can reach the
finalization branch after the pack already ended, and minipass throws from a
stream nothing awaits — an uncaught exception, so `await tar.c(...)` cannot
catch it.

This fixture is the only place in the repo that packs a hard link; the other
fs.link callers never hand the link to tar, and symlink fixtures do not take
the PENDINGLINKS path. Crafting the two headers keeps the archive shape tar
itself produces (typeflag '1', linkname pointing at the original, verified by
dumping a real packed archive) and drops the test's dependency on fs.link, so
it now runs on Windows too.

With the cores saturated the parent commit hit this 3 times in 28 runs; this
commit survived 32. On an idle machine the parent commit survived 30 runs, so
the contention is what makes it visible.
…#10760)

* fix(release): cap Vitest workers in the quality_scripts lane (#10755)

The 2026-09-02 nightly failed on the quality aggregate because the
quality_scripts lane (added by the release-sharding split, #10619) ran
the scripts suite with vitest's default worker count — one worker per
host core — on the shared ECS pool. On a 64-core runner executing
several pool jobs at once, the suite's ESLint instances and bash
replays spawned dozens of heavy subprocesses in parallel and 5-8 tests
per run blew through the 30s test timeout non-deterministically
(reproduced twice on the failing SHA: 30s timeouts across the ESLint
boundary guards and workflow replays, plus a spawn-latency race in the
upload-stall test). The sibling workspace_tests lane and the main CI
gate already bound their vitest workers on ECS (#10667); the new lane
never inherited the bound.

Apply the same tunable per-process bound to the lane and pin it in the
release contract test next to the workspace lane's copy. Also deflake
the upload-stall test: its 400ms kill bound raced child-spawn latency
under pool load, losing attempt log lines and reading 1 or 2 attempts
instead of 3; the bound is now 2s — still far under the shim's 60s
hang, so the kill semantics are unchanged, and the worst-case run stays
inside the test's own 30s budget.

* refactor(release): alias the Vitest worker bound across release test lanes (#10755)

Review of the worker-cap fix noted that the Run Script Tests step
carried a byte-identical copy of the Run Workspace Tests env block, so
any future tuning (bumping the '4' default, adding another pool/thread
variable) would need two synchronized edits, and editing one step while
missing the other would silently restore the unbounded-worker timeout
on the untouched lane — caught only reactively by a red contract test.

Anchor the block at its first occurrence (&vitest_worker_bound) and
alias it into the scripts lane, the same mechanism this file already
uses for &release_test_env across six jobs. Reshape the contract
assertions to pin the structure: the bound's four variables at the
anchor definition and the bare alias at the consumer, so dropping
either side turns the test red. Net five lines removed.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* feat(channels): expose BTW through agent bridges

* test(web-shell): remove duplicate language bindings

* fix(cli): gate channel BTW by daemon capability

* docs(channels): document the btw capability gate

The daemon-worker facade exposes `bridge.btw` only when the daemon
advertises `session_btw`, but the base README documented that gating
pattern for `shellCommand` alone. An adapter author hitting a silently
absent `bridge.btw` had nowhere to look it up.

Extend the Runtime modes sentence and the optional-methods note to cover
both methods and name their capabilities, and add `btw?` to the
`ChannelAgentBridge` interface sketch in source order.

* docs(channels): clarify BTW plugin capability

---------

Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
* fix(web-shell): keep manual title across clear

* fix(acp-bridge): reject an empty session displayName on metadata update

An empty displayName only cleared the live bridge entry: the sessionTitle
persist runs for truthy names, so no tombstone reached the transcript.
The persisted manual custom_title record then resurfaced through
mergeLiveSessionSummary (live.displayName ?? existing.displayName) with
manual provenance, and the /clear carry renamed the successor session
back to the deleted name before persisting it there as manual.

Reject empty or whitespace-only names in updateSessionMetadata, the
choke point shared by the REST metadata routes, the SDK client, and the
daemon-MCP session_update_metadata tool, mirroring the rejection the
workspace-scoped metadata route already applies. All internal callers
pass generated non-empty names, and every UI rename path trims and
rejects empty input client-side.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtj23z78au

* fix(web-shell): keep title provenance when metadata events echo the same name

A `session_metadata_updated` event that echoes the unchanged displayName
without an explicit titleSource — the bridge's pr-only publish when a PR is
bound — reset the connection's provenance to undefined, wiping the 'manual'
provenance the `/clear` carry reads. Preserve the prior provenance for an
unchanged-name echo; only a changed name of unknown provenance resets it.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtjeyxvebk

* fix(web-shell): discard an armed /clear carry when other paths bind a session

Two session-binding paths bypassed the carry's consume/discard network while
pendingManualTitleRef was armed: the shrink-fold landing of the first split
pane on the sessionless chat connection, and resolveSessionForWorkspace's
direct createSession (Commit-dialog flow). A cleared manual title could then
resurface on the bound session's successor and persist as user-authored
provenance. Discard the armed carry at both sites, mirroring
loadSidebarSession.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtjeyxvebk

* fix(serve): preserve automatic title provenance

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

* test(serve): type automatic title metadata

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

* fix(web-shell): keep clear recovery after rename failure

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Batch 7 added a dedicated OpenTUI leg to e2e.yml. It never ran on its own
PR — the workflow triggers only on push to main, nightly and dispatch — so
its first executions were on main, where it fails deterministically: the
same four interactive files have failed across three consecutive main
commits.

The failures are not test fragility. Each one is a parity gap the renderer
still has, and the leg did its job by surfacing them:

- the approval-mode indicator is never drawn, so a user running --yolo
  cannot see which mode they are in;
- the submit seam drops submittedPrompt, so the UserPromptSubmit hook input
  loses submitted_prompt and external-context auto-recall silently no-ops;
- the live turn bypasses the query-preparation pipeline, so @file expansion
  does not work under this renderer at all.

Removing the leg keeps main green without deleting any test: the ink leg
still runs every one of these files, so nothing regresses silently. The
renderer matrix stays wired in — the e2e legs keep pinning their renderer,
and the OpenTUI side remains runnable locally through
npm run test:integration:interactive:opentui:sandbox:none. Restoring the CI
leg is gated on closing the three gaps, tracked in #8662.

The design doc claimed the build/CI batch "runs the OpenTUI e2e leg green
on CI"; that named a mechanism this commit removes, so both mentions now
match the tree.
…ublish (#10827)

Co-authored-by: Claude <noreply@anthropic.com>
Refresh standalone session options whenever a cleared draft re-enters the deferred state, while preserving lazy session creation.

Refs #10821

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

* fix(goal): stop exporting the unreachable propose_goal decline message

PROPOSE_GOAL_NOT_APPROVED_MESSAGE is written as if the model reads it. It
does not. A declined dialog resolves as ToolConfirmationOutcome.Cancel,
and coreToolScheduler settles the call as cancelled without ever entering
execute(); what the model receives is the scheduler's own "[Operation
Cancelled] Reason: User did not allow tool call".

The guard itself is worth keeping as a defence against a host that one
day runs execute() after a cancelled confirmation, so a decline can never
fall through to parking an approval. It is now module-private with a
comment saying why, since nothing outside the module should assert on a
string the model cannot receive. What actually stops the model from
re-proposing is the tool description, which already says so.

The test that pinned the message now pins the real path -- a cancelled
dialog parks nothing -- and a second test keeps the defensive branch
honest without depending on the exact wording.

The documentation sentence that made the same wrong claim is corrected in
the Goal docs sync PR, which already rewrites that paragraph.

* test(goal): pin the propose_goal decline contract

Three gaps this PR left in what holds the decline behavior.

The refusal assertion matched only the shared 'The Goal was not set'
prefix, which PROPOSE_GOAL_NO_TURN_MESSAGE also begins with, so the two
refusal branches were indistinguishable: returning the no-turn message
from the not-approved guard kept the suite green while handing the model
a string that invites the re-proposal the decline forbids. Match the
'the user did not approve it' fragment instead, which occurs only in the
decline message.

The tool description's decline clause was asserted nowhere, and dropping
the exported constant made that description the only thing left telling
the model not to re-propose. Pin the clause next to the existing name and
permission checks, reusing the fragment the bundled goal-draft skill test
already pins so the two copies cannot drift apart silently.

Drop 'parks nothing when the dialog is cancelled': it never called
execute(), and its assertions were a strict subset of the test below it,
which runs the same cancelled confirmation and then executes. Its comment
described the scheduler path it did not exercise; that rationale now sits
on the surviving test, pointing at the scheduler test that does cover it.
* fix(ci): stop heartbeat mint-skip test racing loop startup (#10523)

The test slept a fixed 2.5s and then required the mint-failure log
line, but the loop sleeps a full interval before its first tick, so
bash startup plus the tick's forks land after that budget on a loaded
runner — reddening the helper-tests lane with no product defect. Poll
the skip line itself (the file's existing waitFor shape) and keep all
three assertions.

* fix(ci): gate heartbeat mint-skip test on the second skip line (#10523)

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

* fix(ci): carry observed log state into the mint-skip gate failure (#10523)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.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>
* fix(dws): isolate direct message ingestion

* fix(dws): harden concurrent direct dispatch

* fix(dws): address concurrent dispatch review

* fix(dws): preserve direct message progress

* fix(dws): scope the direct replay cap to replay dispatches

The replay cap measured queuedDirectMessages.size, which in followup
mode also counts the live per-conversation backlog: entries are added at
schedule time and removed only when their own turn finishes, so N chained
messages hold N entries while one turn runs. A single conversation's
backlog of 16 could therefore keep every poll from replaying any parked
failed direct message of any other conversation, and parked entries have
no other redelivery surface.

Count only replay-started dispatches against the cap, tracked in a map
cleared on disconnect alongside queuedDirectMessages so replay-alone
concurrency still stays at or below the cap across polls.

Also add the missing test witnesses for the disconnect and tail cleanup
introduced earlier in this branch: the conversation-tail reset on
disconnect, the identity-guarded tail release in scheduleDirectMessage,
and failed ambient parking when a capacity wait is released by disconnect.

---------

Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
* fix(web-shell): preserve slash command attachments

* fix(web-shell): align attachment command resolution

* fix(web-shell): preserve attachments for slash-like text

---------

Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com>
* feat(serve): establish workspace runtime ownership

* feat(serve): add workspace-scoped MCP management

* fix(serve): address workspace MCP review findings

* fix(serve): address follow-up MCP review findings

* test(serve): stabilize MCP drain race coverage

---------

Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com>
Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
* feat(channels): add BTW side questions

* fix(channels): address review round 1 on BTW side questions

Critical:
- Deliver the /btw answer to the chat/thread captured from the asking
  envelope instead of the session's creation-time router target, which
  froze the destination for sessions shared across chats or threads
- Keep an acknowledged /btw current when SessionRouter promotes the
  live target to a group mid-flight; the promotion changes neither the
  conversation nor the delivery destination, while the named-task
  branch keeps the stricter target comparison

Suggestions:
- Move the shared-session authorization gate ahead of the usage,
  length, and attachment validation so non-members no longer receive
  validation answers
- Move the bridge capability check ahead of session resolution so an
  unsupported /btw no longer creates and persists a session
- Declare btw on ChannelAgentBridge as an optional capability instead
  of a module-private intersection type
- Describe /btw in the /help command list and the channel docs

Tests: cross-chat delivery under sessionScope single, mid-flight group
promotion, foreign-session response rejection, unauthorized bare and
oversized /btw, no-session-creation on unsupported bridges, and the
described /help entry.

* fix(channels): address review round 2 on BTW side questions

* fix(channels): close BTW review gaps

* fix(channels): close BTW delivery review gaps

* fix(channels): let /btw reach the agent when no bridge answers it

No ChannelAgentBridge in this tree implements the optional btw capability
yet, so the unsupported-bridge gate was taken on every shipped connection
and /btw refused. At the merge base /btw was not a locally handled
command: it fell through to the agent, which serves it as its own slash
command and answered. Landing the interception before any producer
therefore replaced a working answer with a permanent refusal.

Intercept only when the bridge can answer out of band. Without the
capability /btw falls through unchanged, so this PR no longer depends on
the bridge half landing first.

The /help de-duplication needed the same treatment: `btw` is registered
locally whether or not the bridge supports it, so the filter hid the
agent's entry even when that entry was the only working one.

Tests: the fail-closed case is replaced by one asserting the /btw text
reaches bridge.prompt, plus a case keeping the agent entry visible in
/help. Also adds the session-scoped de-duplication case — the existing
one runs with no active session, so it never reached
getAgentCommandsForSession and a filter narrowed to the non-session
branch stayed green while a live conversation listed /btw twice.

---------

Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
@pull pull Bot locked and limited conversation to collaborators Sep 2, 2026
@pull pull Bot added the ⤵️ pull label Sep 2, 2026
@pull
pull Bot merged commit 4f21287 into bit-cook:main Sep 2, 2026
0 of 2 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants