feat(desktop): establish OpenWork on the Qwen Tauri Web Shell - #81
feat(desktop): establish OpenWork on the Qwen Tauri Web Shell#81yiliang114 wants to merge 8445 commits into
Conversation
* 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>
… 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
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
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>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* 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 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>
Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
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.
…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 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: 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
…#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.
…les (#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 * 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
* 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>
…l (#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 * 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 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)
…ox (#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>
…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 (#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>
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>
… 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>
…(#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): 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>
Click an image in a user message or a pasted image in the composer to open it in the right-hand artifact panel as an image tab. Each distinct image gets its own tab (re-clicking focuses the existing one), the tab shows a hover-revealed download button like workspace image artifacts, and the composer remove button is now a small round icon that appears on hover instead of an always-visible heavy cross. Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com>
Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com>
…(#8936) Tauri's resource_dir() returns \\?\ verbatim paths on Windows, so the bundled Node executable and the cli-entry.js argument were spawned with that prefix. Node's entry-script resolution cannot handle it and dies with EISDIR before the daemon reports its listening URL, making the app unstartable regardless of workspace. Simplify both paths with dunce before spawning, complementing the workspace-path fix from #8615. Fixes #8929
…osing retry (#8884)
* fix: add structured error code to SessionNotFoundError responses
PR #8864 retried session switches while the target session is closing,
but relied on fragile string matching against the daemon's error message.
This commit:
1. Adds a `code` property to `SessionNotFoundError` — automatically set
to `'session_closing'` when the extra message mentions "closing",
otherwise `'session_not_found'`.
2. Includes `code` in the HTTP JSON response body so clients can
distinguish closing (transient) from genuinely missing sessions
without depending on error message text.
3. Updates the WebUI retry check in `DaemonSessionProvider` to use
`errorBody.code === 'session_closing'` instead of matching
`endsWith('The session is closing; retry after close completes')`.
4. Fixes an inconsistent error message in `rewindSession` that used the
short `'The session is closing'` without the retry suffix.
Closes: #8864 (follow-up)
* fix(daemon): expose session closing code
* docs(serve): document session closing codes
* fix(acp): preserve closing code after restore waits
* fix: restore class pin in bridge test and update error taxonomy
- Add toBeInstanceOf(SessionNotFoundError) alongside toMatchObject
to preserve the envelope type assertion
- Document session_closing code in 18-error-taxonomy.md
* chore: drop unrelated merge formatting
* fix(core): hash the built-in template for the stored provider version The version recorded by a provider install hashed the installed model list (built-ins plus every owned model), while the launch check that detects a pending update hashed the built-in template alone. A user owning any id outside the current built-in list — including a built-in that a release renamed away — therefore stored a version that could never equal the recomputed one, so "Update all" did not clear the prompt and it returned on every launch. Record the built-in template version instead, and route both sides through a single computeProviderTemplateVersion so the two inputs cannot drift apart again. Custom models are still carried through the update; no new field is persisted. Relates to #8504 * fix(cli): scope provider version to template updates Keep ordinary install metadata tied to the models in the install plan, while confirmed provider updates persist the detected built-in template version. * fix: restore computeProviderTemplateVersion — align all install paths The second commit (71bd8f1) reverted the core fix from 1df9c1d: it deleted computeProviderTemplateVersion and restored the old resolveProviderState that hashes the full model list (built-ins + custom). The CLI executeUpdate path was patched post-hoc, but setup wizard, VS Code companion, and ACP flows still went through the broken path — storing a version that would never match the template-only launch-time check whenever custom models were present. This commit: 1. Restores computeProviderTemplateVersion with the invariant comment. 2. Restores resolveProviderState to hash only the built-in template. 3. Removes the post-hoc version patching in executeUpdate (no longer needed). 4. Restores the comprehensive invariant tests for every built-in provider. 5. Re-exports computeProviderTemplateVersion from the core package index. * fix(core): repair duplicate and missing test imports The source-import pass left `resolveMetadataKey as resolveMetadataKeySrc` imported twice and never imported `PROVIDER_METADATA_NS`, so `tsc --build` failed on the core workspace and took Test / web-shell E2E / Post Coverage down with it. * fix(cli,core): use source import and shared helper in tests - Switch provider-config test to source import (computeProviderTemplateVersionSrc) to match the file's convention for dist-bypass imports - Replace hand-composed version computation in CLI test fixtures with computeProviderTemplateVersion to prevent drift * fix(vscode): source plan models and version from the core preset The IDE kept its own copy of the subscription plan model lists and hashed that copy for `providerMetadata.<plan>.version`. Neither matched the preset the CLI reads, so signing in from the IDE wrote a version the CLI could never reproduce — an update prompt on the next launch — and persisted model entries that were missing fields the preset carries. The copy had also fallen behind: it still listed `deepseek-v4-flash`, renamed to `deepseek-v4-flash-0731` in 0.21.8, so the IDE installed a model id that no longer exists. Derive both the template and the version from the matching core provider, found by baseUrl + envKey, and delete the duplicated definitions along with the "keep in sync" comment that admitted the drift. The test no longer pins model ids — pinning them is what let the copy drift — and asserts against the shared preset instead. * fix(providers): preserve installed model versions * refactor(providers): remove redundant version helper * fix(cli): never switch model during provider template update * test(cli): update model selection test for unconditional modelSelection deletion * test(cli): pin provider update metadata routing
Resolve conflicts: take qwen-code for most files, keep OpenWork desktop-release.yml workflow.
- Remove 36 qwen-code release/publish/bot CI workflows - Restore OpenWork branding, README, release workflow - Restore OpenWork-specific packages: messaging, session tools, viewer - Restore OpenWork assets: .agents/skills, electron resources, themes, i18n - Apply OpenWork identity to packages/desktop-shell: - tauri.openwork.conf.json overlay - Cargo.toml, package.json: name, version, description - env vars: QWEN_ → OPENWORK_ - UI strings: Qwen Code → OpenWork - Runtime: JSON startup event parsing - Tests: match JSON format - Restore server bootstrap contract: startServer() export, desktop-entry.ts
Builds OpenWork WebUI, server, and session MCP instead of qwen-code CLI and Web Shell. Stages resources under runtime/openwork/.
PR1 E2E test reportResult: PASS on macOS. Windows and Linux were not exercised locally. User flows
Verification gates
EvidenceOpenWork welcome state: Real session response: Daemon recovery state: |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
💡 Codex Reviewopenwork/packages/desktop-shell/src-tauri/src/local_control.rs Lines 347 to 348 in d043eea When Local Control proxies a browser WebSocket connection, the Web Shell offers the pair token as ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Addressed in |



What this PR does
This is PR1 of the OpenWork replatforming tracked in #80. It adopts qwen-code v0.21.10 as the repository baseline and establishes the smallest complete desktop path on Tauri 2 and the Qwen Web Shell: OpenWork branding, the original OpenWork symbol, a bundled Node/Qwen/Web Shell runtime, an authenticated loopback daemon, local workspace bootstrap, runtime recovery, and clean daemon shutdown.
The legacy Electron implementation remains available as parity evidence, while duplicate merge artifacts and incompatible legacy root workspaces are kept out of the PR1 runtime. The migration audit records every preserved, upstream-equivalent, deferred, or redesigned capability for PR2.
Why it's needed
OpenWork needs to follow the current Qwen desktop architecture without silently losing its product identity or existing behavior. Splitting the migration makes the architectural baseline independently runnable and reviewable before PR2 restores the remaining OpenWork-specific product features and release infrastructure.
Reviewer Test Plan
How to verify
Reply exactly OK, and confirm the assistant returnsOKand the session remains available in the sidebar.Evidence (Before & After)
Before: OpenWork used the legacy Electron renderer/runtime path and did not run on the qwen Tauri/Web Shell architecture.
After — OpenWork-branded Web Shell welcome state:
After — real workspace session and model response:
Recovery — bundled daemon exit is surfaced with an actionable Retry flow:
Tested on
Environment (optional)
macOS; Node.js 22+; local Tauri debug build and release app bundle; bundled qwen-code v0.21.10 runtime. Verified with the full workspace build and typecheck, ESLint, Rust format/clippy and 37 unit tests, two branding tests, release-contract checks, runtime smoke, real-session lifecycle E2E, and packaged smoke.
Risk & Scope
serve; incompatible legacy OpenWork packages are excluded from the qwen root workspace, and the updater is intentionally disabled until the PR2 release path is implemented.Linked Issues
Relates to #80.
中文说明
本 PR 做了什么
这是 #80 所跟踪的 OpenWork 架构迁移 PR1。它以 qwen-code v0.21.10 作为仓库基线,并在 Tauri 2 与 Qwen Web Shell 上建立最小但完整的桌面运行路径:OpenWork 品牌、原始 OpenWork 图标、内置 Node/Qwen/Web Shell 运行时、带鉴权的本机回环 daemon、本地工作区启动、运行时恢复,以及退出时完整清理 daemon。
旧 Electron 实现继续保留为功能对齐依据,同时重复的合并产物和不兼容的旧根工作区不会进入 PR1 运行路径。迁移审计记录了所有已保留、由上游等价覆盖、延期或需要重新设计的能力,供 PR2 继续完成。
为什么需要
OpenWork 需要跟进当前 Qwen 桌面架构,同时不能悄然丢失产品身份或既有行为。拆分迁移后,可以先独立运行和审查架构基线,再由 PR2 恢复剩余的 OpenWork 专属产品功能与发布基础设施。
Reviewer 测试计划
如何验证
Reply exactly OK,确认助手返回OK,且该会话仍可在侧边栏中访问。证据(Before & After)
Before:OpenWork 使用旧 Electron renderer/runtime 路径,尚未运行在 qwen Tauri/Web Shell 架构上。
After — OpenWork 品牌的 Web Shell 欢迎页:
After — 真实工作区会话与模型回复:
Recovery — 内置 daemon 退出后会显示可操作的 Retry 恢复流程:
已测试平台
环境(可选)
macOS;Node.js 22+;本地 Tauri debug build 与 release app bundle;内置 qwen-code v0.21.10 运行时。已通过全仓 build 和 typecheck、ESLint、Rust format/clippy 与 37 个单元测试、2 个品牌测试、release contract 检查、runtime smoke、真实会话生命周期 E2E,以及 packaged smoke。
风险与范围
serve;不兼容的旧 OpenWork package 被排除出 qwen 根工作区,updater 在 PR2 发布路径完成前有意保持禁用。关联 Issue
关联 #80。