mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-01 04:02:35 +00:00
23 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5f4b60d2a0
|
fix(cua): align input and session repairs with 0.27.0 (#15784)
* fix(cua): correct background input and pending native cleanup * chore(cua): prepare aligned 0.26.1 release * fix(cua): discover live accessory apps outside install roots * fix(cua): Return pending actions when popup windows hide * fix(cua): Avoid unbound Windows input value probes * fix(cua): Check macOS class factories on the class * fix(cua): Verify app text insertion through accessibility * fix(cua): Route macOS editing keys to their AX hosts * fix(cua): Preserve owner windows and public refusal recovery * docs(cua): Complete the 0.26.1 repair release scope |
||
|
|
4d050906e5
|
feat(cua): enable default cursor and live macOS PiP (#15669)
* feat(cua): enable native cursor and live macOS previews * fix(cua): preserve focus in standalone UI hosts * feat(cua): polish floating preview cards * fix(cua): stabilize PiP hover and modal interactions * test(node-repl): use a lint-compatible status assertion * fix(cua): retain the preview while a newer frame is pending * fix(cua): use an auxiliary fullscreen role for the preview * fix(cua): preserve worker lifetime and preview controls |
||
|
|
249ab50d84
|
fix(node-repl): let the kernel exit promptly on shutdown (#15160)
* fix(node-repl): let the kernel exit promptly on shutdown The kernel reads its input pipe through a file stream, and a pending read on that pipe keeps process.exit() from completing until the pipe reaches EOF. Since the manager began waiting 2.5 s for a graceful exit before signalling, every reset and disposal cost about 3 seconds and ended in SIGTERM, which also slowed the node-repl suite from ~35 s to ~3.8 min in CI. End the pipe right after the shutdown frame so the kernel can exit as soon as its cleanup finishes. * fix(node-repl): stop session processes when the kernel exits on its own Ending the kernel's input pipe lets it exit within milliseconds, so the group signals that used to reach processes the session started without detaching were no longer sent: they kept running after reset, disposal and a cancellation timeout. When the kernel exits on its own, signal its process group the way the signalled shutdown did: SIGTERM, then SIGKILL if anything is still in the group after 500 ms. The group keeps its ID while any member lives, and a kernel that was already gone before termination began is left alone. Tests start a child from session code that holds a socket to the test, so its exit is seen even when nothing reaps it, and cover reset, disposal, a timed-out cancellation barrier and a child that ignores SIGTERM. The idle-kernel test now checks that no signal reaches a running kernel rather than that no signal is sent at all. * fix(node-repl): stop leftover session processes after a SIGTERM exit too The process group stop ran only when the kernel exited within the first grace period. A kernel whose own cleanup outlasts it (a slow Computer Use disposal, say) exits during the SIGTERM wait instead, so neither the group stop nor the SIGKILL ran, and a session process that ignores SIGTERM kept running. Before the pipe was closed that kernel could not finish exiting, so the old path always reached SIGKILL. Run the group stop whenever the kernel exits before the hard stop. A test blocks Computer Use disposal past the grace period and checks that a SIGTERM-ignoring child is still stopped. * test(node-repl): pin the SIGTERM step of the leftover-process sweep The slow-cleanup case was named for a kernel that exits on SIGTERM, but the kernel ignores SIGTERM once shutdown has started; it exits when the fixture's cleanup ends. Rename it and assert the group got SIGTERM while the kernel was still running, so a kernel that exits in the first wait no longer passes for this path. Add a case whose session process handles SIGTERM by writing a marker and exiting, so replacing the SIGTERM-then-SIGKILL sweep with a bare SIGKILL fails a test. |
||
|
|
a791e47a54
|
feat(cua): streamline computer use and port native reliability fixes (#14846)
* feat(cua): preload the JavaScript computer use runtime * refactor(cua): remove the native Wayland backend * feat(cua): move app computer use into the driver * feat(cua): preserve background delivery and port platform fixes * feat(cua): add explicit desktop input and browser runtime facade * feat(cua): package the unified 0.26.0 computer use runtime * fix(cua): finalize cross-platform native reliability * fix(cua): isolate runtime releases from CLI versioning * fix(cua): keep Electron out of root workspace installs * fix(cua): synchronize consumers and async cleanup tests * fix(browser): migrate smoke to preloaded cua runtime * docs(cua): document MCP setup for Qwen Code Claude Code and Codex |
||
|
|
2cfd390650
|
feat(computer-use): let a remote session use your desktop through a node_repl relay (#11799)
* docs(plans): add remote computer use plan for a local Mac Plan for letting a Qwen Code session on a headless Linux dev box operate the user's local Mac through computer use: the principle, the existing pieces, three options, and a Mac companion that joins the reverse tool channel. * docs(plans): add handoff and slice-0 verification brief for remote computer use Add a handoff document for the session or agent that continues this work, and a real-Mac verification brief for slice 0. Update the plan with a newly found pitfall: on macOS the driver's LaunchServices auto-launch does not forward environment variables, so an auto-launched daemon never enables the HTTP MCP endpoint. * docs(plans): replace the driver relay with a node_repl relay for remote computer use Rereading main shows the bundled computer-use skill never talks to the qwen-cua-driver daemon: ComputerUse.create() embeds the driver inside the node_repl process, and a standalone daemon rejects trusted sessions from any connection that is not its embedding parent (serve.rs:787, error 77). That invalidates the v1 plan's option B and makes options A and C relay the wrong layer (the raw tool surface meant for external agents). The plan now relays node_repl itself over the reverse tool channel, keeps the skill's App API intact, and lists the one skill change this needs (read references with read_file instead of inside node_repl). The verification brief is rewritten around a ten-minute fact check on a real Mac plus the slice-1 prototype acceptance; the handoff document follows. * docs(plans): make the Mac relay a qwen subcommand, not a standalone app Maintainer decision: the Mac-side relay for remote computer use is a qwen subcommand (qwen mac-bridge) run from a terminal. That matches how local computer use already works, where qwen launches node_repl from the terminal and the TCC grants are recorded against the terminal, so it adds no install step and no new permission identity. The plan drops the signed-app, bundle-id and menu-bar requirements, makes Ctrl-C the local stop control, and demotes the signed host to an optional slice that is off by default. The handoff document records the decision so the next session does not reopen it, and the verification brief uses the subcommand throughout. * docs(plans): name the relay qwen bridge and drop the macOS-only framing The relay is for any machine with a graphical session, not for macOS: it only moves MCP frames, the SDK reports the connected platform, and the skill already carries per-platform references. macOS is the platform verified in this round because the TCC facts were read from its code path. The subcommand is qwen bridge, one new top-level command in packages/cli/src/commands/bridge.ts; the plan file is renamed from mac-companion to desktop-relay and the handoff and verification brief follow. * feat(computer-use): let a remote session use this desktop through a node_repl relay A Qwen Code session on a headless machine can now drive the desktop the user is sitting at. That computer lends its own node_repl (with @qwen-code/cua-sdk and the embedded driver) to one remote session: - `node-repl-mcp desktop-relay install` puts the runtime under ~/.qwen/desktop-relay and registers a launchd socket on 127.0.0.1:47821 in inetd mode, so nothing runs until something connects. - The Web Shell gains a "Use this computer" footer entry. It hands the daemon URL, session and token to the relay, which asks for approval in a native dialog, starts node_repl and registers it for that session over the daemon's reverse tool channel. A dropped connection ends the relay; control never resumes without a fresh approval. - Terminal sessions reach the same socket through an SSH RemoteForward; their first tools/call asks for approval. One node_repl serves the daemon's several MCP clients: the relay answers repeated initialize requests from the first result, rewrites request ids and turns replies above the 10 MB frame cap into errors. The skill needs no change; main already reads references with read_file when node_repl cannot. Not built, typechecked or tested locally; CI and the real-Mac brief in docs/verification/remote-computer-use/ are the verification. * chore(deps): record ws in the pnpm lockfile for node-repl-mcp The pnpm lockfile is generated from package-lock.json by `corepack pnpm import`; the previous commit updated only the npm one, so frozen installs failed on the node-repl importer's new ws and @types/ws specifiers. Both resolve to the versions the cli importer already records. * fix(node-repl): drop the console override in the desktop relay agent The repo lints packages with no-console, and the override guarded against output nothing on the agent path produces. * fix(node-repl): restore the desktop relay agent entry point The previous commit spliced runtime.ts at stale offsets and left it unparseable. This applies the intended change to the last good copy: no console override and no unused format import. * fix: harden remote computer-use relay * fix(node-repl): forward unambiguous cancellations through the desktop relay Stopping a turn in the remote session sends notifications/cancelled for the running tools/call. The relay dropped every cancellation because the reverse channel does not say which multiplexed MCP client sent it, so a node_repl cell kept driving the desktop after the user had stopped the agent. Forward a cancellation, with its request id rewritten, when exactly one pending request carries that id; still drop it when none or several do. * docs(computer-use): refresh the remote desktop relay handoff for another machine Switch the verification setup to the pnpm install main now uses, add a check that stopping a turn stops a running desktop cell, and add a troubleshooting guide that walks the chain from browser to launchd to daemon to macOS permissions. The handoff records the current state, the two decisions still open, and why SSH workspaces do not replace this. * docs(computer-use): record the Linux-side verification results for the desktop relay The Mac-side end-to-end run is still blocked: this machine has no GUI and no reachable macOS host. What could be verified here is recorded instead - the relay's real HTTP surface over a genuine fd-0 socket with launchd's inetd activation emulated (B.1-B.4 plus the edge cases curl cannot express), and a code-level walk of the cancellation chain the 2026-09-23 fix relies on. Two new findings: a failing osascript is indistinguishable from an explicit Deny and leaves nothing in agent.log, and launchd's default ThrottleInterval may serialize these short-lived per-connection processes, so it should be measured first on the Mac. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(web-shell): gate desktop relay and surface browser permission * fix(computer-use): keep desktop relay usable after cancellation * fix(computer-use): complete remote desktop relay acceptance * fix(node-repl): harden desktop relay consent, lifecycle, and cancellation paths (#11799) - skill: probe the desktop tool with `in` so the code-mode tools proxy does not abort the script before the fallback runs - agent: bound the raw JSON-RPC buffer (64 KiB, matching the HTTP path) and retire tools/call cancelled while the consent dialog is open - launchd: roll back the plist when bootstrap fails; confirm bootout actually unloaded the agent before deleting its plist - consent: log dialog failures to stderr instead of reporting them as a human denial with no trace - mcp-child-relay: forward an ambiguous-id cancellation to every match instead of dropping it, clamp relayed yield_time_ms under the daemon's 30 s frame budget, and never let a throwing child reject handle() - docs: replace the overlay merge recipe with a three-way merge, scope the tccutil reset guidance, and align the cancellation check with the channel timeout Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(node-repl): bound desktop relay connection failures * fix(web-shell): preserve active desktop relay revocation * docs(computer-use): correct setup and redact verification details * fix(desktop-relay): close review security gaps * fix(desktop-relay): harden process ownership and prewarm * fix(desktop-relay): pin the setup command to a published node-repl version DESKTOP_RELAY_INSTALL_COMMAND, packages/node-repl/README.md and docs/users/features/computer-use.md all handed out `npx -y @qwen-code/node-repl-mcp@latest desktop-relay install`. The published `latest` (0.1.6) has no `desktop-relay` subcommand and its entrypoint never reads argv, so it connects a stdio MCP server and blocks on stdin: zero output, nothing installed, no launchd socket. The Web Shell panel then re-serves that same string behind a Copy button in exactly the `missing` phase the command produced, so the loop has no exit. Pin the spec to the package version the way the repo already does for this package (packages/core/src/skills/bundled/browser-use/skill.test.ts), and bump packages/node-repl 0.1.6 -> 0.1.7 in this PR so the publish carries the relay. Without the bump, cd-cua-driver.yml rejects the release with "already exists with different integrity" and the pinned version would still lack the subcommand. Sync the three version lines the release validate-version gate reads (computer-use SKILL.md, browser-use SKILL.md, computer-use user guide) plus the hard pin in scripts/tests/release-workflow.test.js. desktop-relay-client.test.ts now fails when the constant drifts from packages/node-repl/package.json or reintroduces `@latest`. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(desktop-relay): stop reporting relay state that was never confirmed Five review findings shared one cause: the Web Shell panel and the launchd teardown each asserted a state they had not actually confirmed. - deriveDesktopRelayStatus let another session's live relay mask this session's connect failure, because input.error was only consulted after the other-session return. The error now wins over other-session; a relay that is live for this session still outranks it, since that is the truth. - awaitingApproval and error were component-global while the probe was scoped by sessionId and daemonUrl, so switching session or daemon showed the previous one's pending dialog or stale error. Both are reset when either changes. - retainLiveDesktopRelayProbe kept a live observation for any non-ready probe without limit, so a relay that vanished stayed "connected" for as long as the panel was mounted. Retention now expires after three inconclusive probes, which still covers a transient blip. - disconnectDesktopRelay resolved void and never read the response status, so a revocation that never reached the relay was indistinguishable from one that did, and disconnect() cleared the probe unconditionally. It now reports success only on an ok response, and the panel keeps its last observation until the relay confirms, leaving Disconnect retryable. - currentUid() returned 0 under sudo, so uninstall queried gui/0 while the agent was loaded in the invoking user's domain. The existing "still loaded" guard in uninstallLaunchAgent therefore could not see the agent and deleted the plist out from under it. SUDO_UID now supplies the domain; Number() rather than parseInt() rejects a malformed value instead of truncating it into a wrong uid. Addresses review findings R3-5, R3-6, R3-7, R2-10 and R3-14. Each has a regression test that was verified to fail when its fix is reverted. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(desktop-relay): cancel stale approvals and bound registration * fix(desktop-relay): keep revocation available when unreachable * fix(desktop-relay): serialize consent and allow cold runtime startup --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
bcea19780f
|
fix(node-repl): widen the bounded-cancellation test margin under runner load (#12884)
The "recovers from ... within a bounded cancellation interval" cases armed the host-side exec timeout at 200ms. The kernel crosses two await points (the prepended dynamic import and the case's own 1ms timer) before it reaches the blocking payload, and the vm evaluate watchdog is released at the first suspension, so only the host can bound the wait. When a loaded shared runner delayed the kernel past those 200ms, the cancel frame was handled while the kernel still idled at an await point: the kernel cancelled cleanly and survived, so the run reported status "timeout" with kernelReplaced=false and the replacement assertion failed. Raise the timeout margin for the host-driven timeout variants to 2000ms and the case budget to 20000ms so the kernel is reliably inside the blocked payload before the cancel frame arrives. The assertions and the cancellation-boundary behavior under test are unchanged. |
||
|
|
b7543aeb1b
|
feat(browser-use): add Chrome Native Messaging relay (#11242)
* feat(browser-use): add Playwright browser SDK
* docs(browser-use): document browser use architecture
* fix(browser-use): make browser input actions reliable
* feat(browser-use): preserve screenshot coordinate metadata
* fix(browser-use): recover stale sockets across VM realms
* fix(browser-use): bound locator action retries
* fix(browser-use): keep page input in the background
* fix(browser-use): reject ambiguous input arguments
* fix(browser-use): validate locator composition identity
* fix(browser-use): recover socket locks across VM realms
* fix(browser-use): preserve input results across navigation
* fix(browser-use): capture screenshots through bounded CDP frames
* fix(browser-use): separate input and navigation waits
* feat(browser-use): add Chrome native relay
* fix(chrome-extension): avoid foreground tabs and stale cursor markup
* fix(chrome-extension): require Chrome 125 for flat sessions
* feat(browser-use): add product integration
* test(browser-use): add managed Chrome smoke coverage
* feat(node-repl): expose structured image metadata
* test(browser-use): preserve complete smoke reports
* feat(browser-use): bundle skill and runtime with CLI
* fix(browser-use): install pinned Playwright in fresh workspaces
* feat(browser-use): require explicit skill opt-in
* feat(browser-use): make bundled skill available by default
Keep explicit skill disables authoritative, update documentation and tests, and use the latest Node REPL package in setup instructions.
* fix(ci): build browser-use before Live Host publish
Build the bundled browser runtime before core in the Live Host npm dependency chain, and cover that prerequisite order with a regression test.
* fix(browser-use): output screenshot metadata explicitly
* fix(node-repl): preserve image metadata outside prose budget
* fix(browser-use): address review round on workspace wiring and runtime correctness
- register the workspace in .pnpmfile.mjs, pnpm-lock.yaml, and the
unit-vitest-configs registry, with the shared-pool timeout ternary and
off-Linux unhandled-error exemption (fixes the red Install/Test checks)
- map Playwright's 'warning' console type to the contract's 'warn' level
- reject captures whose pixel dimensions contradict the css-pixels
coordinate contract instead of certifying rescaled images
- keep one unattachable derived tab from failing tabs.list while still
propagating a lost session
- clear the cached dialog when Chrome resolves it out of band or the page
navigates, guarded by dialog identity so a newer dialog survives
- synthesize a parseable Browser.getVersion (numeric Chrome major, host
platform token) so Playwright enables the right keyboard editing commands
- keep YAML-quoted snapshot keys and link props in interactive listings and
stop claiming subtrees that filtering removed
- always release the mouse button when a drag gesture fails
* fix(dev): build browser-use runtime before startup
* fix(browser-use): isolate cleanup and correct smoke checks
* fix(browser-use): resolve managed Chrome through Playwright
* fix(browser-use): harden native relay and tab lifecycle
* fix(browser-use): drain host output and persist restored tab state
* fix(browser-use): scope dialog handles and clean navigation waiters
* fix(browser-use): preserve input outcomes and release modifiers
* fix(browser-use): drain session teardown and release debugger attachments
* fix(browser-use): correct locator validation and evaluation
* fix(dev): reuse prebuilt Browser Use runtime
* fix(browser-use): address review round on locator, bridge, and screenshot correctness
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(browser-use): require installed extension before native host setup
* fix(browser-use): address review round on schema probe bound and regex validation
* fix(browser-use): start transport before Chrome preflight
* fix(browser-use): exclude staged runtime from code checks
* fix(core): declare Browser Use build dependency
* fix(browser-use): preserve successful navigation and dialog input
* fix(browser-use): harden input drain and tab registration cleanup
* fix(browser-use): keep sessions bound to one Chrome profile
* fix(browser-use): bound commands and validate evaluation JSON
* fix(browser-use): reuse unchanged Native Host registration
* fix(browser-use): wait for fresh extension installation
* fix(browser-use): address review round on screenshot origin, dialog scope, and bridge privacy
- screenshot: tolerate settle-probe failure (measure the ratio from the
captured pixels, re-capture on HiDPI), measure layout after the settle
wait, and reject screencast frames whose scroll offsets disagree with
the published origin
- session: clear the cached dialog only on main-frame navigation;
abort tab attachment when the tab is removed mid-attach
- errors: classify crashes and timeouts by Playwright's structured error
name instead of page-influenced message text
- locator.press: release chord modifiers when a later token is rejected;
cap locator.type at the 60k characters its per-character budget can
deliver inside the 120s ceiling
- snapshot: keep AI-mode [cursor=pointer] generic nodes in the
interactive listing
- bridge: derive the fallback socket directory as an env-independent
per-user 0700 subdirectory on all POSIX hosts and verify ownership
before binding
* fix(browser-use): close review-critical gaps in error mapping, budgets, and downloads
- errors: classify by Playwright error name first; text phrases match only
the start of the first message line so page-controlled text (log tails,
quoted markup, page-thrown values) cannot pick the reported code, and a
TimeoutError name wins over crash-shaped page text
- transport: cdp.send bridge requests default to 130s so operations up to
the 120s schema ceiling report their own deadline; other requests keep
the 30s default
- transport: the /tmp fallback socket directory is a per-user leaf directly
under the sticky root (no shared intermediate a first mover could own);
ensureSocketDirectory re-verifies the leaf after creation and walks
ancestors up to the sticky root, requiring each to be owned by this user
or root and not group/other-writable
- runtime: withModifiers no longer rewrites a completed action into a
failure when a modifier release fails; chord failure cleanup releases the
chord's own tokens (including non-modifiers) instead of four fixed names
- playwright: enable CDP download events on tab attach (Playwright's
noDefaults connect leaves them off, so waitForEvent('download') could
never resolve); locator.downloadMedia races the caller deadline, aborts
the in-page fetch at it, resolves media contained in a located wrapper
before an ancestor link, restricts URL schemes, and names CORS rejections
distinctly; locator.allTextContents waits for the first match within the
caller budget and still resolves [] when nothing attaches
- screenshot: full-page captures get a 16 Mi-pixel budget (1920x3000 pages
now succeed) and the encoded byte ceiling drops to the 4 MiB model image
limit so captures are not dropped at the model boundary
- snapshot: interactiveOnly promotes only a terminal [cursor=pointer]
attribute (page text quoting the marker no longer promotes the line), and
truncation skips over-long lines instead of discarding the rest
- schemas: reject empty string/regex matchers and exact:true combined with
a regex matcher (Playwright silently drops exact for regexes)
- tests: the SDK evaluate fixture now mirrors production serialization;
POSIX socket tests carry win32 gates; posix.join keeps the derived socket
path a pure function of uid and platform on every host
* fix(browser-use): close review criticals in error mapping, chords, downloads, and snapshots
- errors: classify Playwright's genuine strict-mode violation, which
crosses CDP as the raw exception description and reaches the client
behind an "Error: " layer ("locator.click: Error: strict mode
violation: ... resolved to N elements:"); requiring the element count
keeps a page-thrown lookalike from forging LOCATOR_NOT_UNIQUE
- runtime-helpers: on a failed chord press, release the tokens
Playwright actually derived ('+' separates only after a non-empty
token, so a standalone '+' is a literal key) rather than the caller's
array elements; an element embedding '+' previously released nothing
and left modifiers held. locator.press uses the same tokenizer
- locator.downloadMedia: resolve an ordered candidate list (element,
media descendants, anchor descendants, closest ancestor) and keep the
first candidate that exposes a URL, so a wrapping anchor no longer
beats the image it contains and a source srcset or an unloaded
video's source child can supply the URL
- snapshot: unwrap Playwright's single-quoted YAML key before the
[cursor=pointer] suffix test, so clickable nodes whose accessible
name forces key quoting stay in the interactive listing
- docs: align the Input completion contract with the shipped modifier
cleanup semantics (cleanup failures are discarded; only the action's
own error propagates) in both languages
* fix(browser-use): close round-13 review criticals in errors, srcset, evaluateAll, snapshots, and screenshot origin
* fix(browser-use): point the pnpm importer at a vitest snapshot that exists
Merging main brought in #11625, which regenerated pnpm-lock.yaml and
dropped the vitest@3.2.7 snapshot keyed with @vitest/browser and
supports-color@8.1.1. The packages/browser-use importer still referenced
that key, so every Install leg failed with
ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY. Re-key the importer to the snapshot
its sibling packages already use; nothing else in the lockfile changes.
* fix(browser-use): close round-14 criticals in error classification, dialog ordering and screenshot ratio
R13-2: the strict-mode branch of sanitizeOperationError was the only
text-phrase branch not gated on the evaluate channel, so a page-thrown
primitive "strict mode violation: ... resolved to 2 elements:" picked
LOCATOR_NOT_UNIQUE. On that channel only Playwright's "Error: "-wrapped
form classifies now; other channels keep the unwrapped form.
R14-1: Playwright hands a dialog over on a later turn than the bridge
reports its CDP events (a microtask in CRSession plus the in-process
dispatcher's setImmediate), so an open+close pair delivered in one chunk
left a phantom dialog that blocked the tab. Each tab now traces dialog
openings and closes in bridge order and drops a delivered dialog the
bridge already reported closed; a close with no traced opening (a dialog
open before the tab was attached) is never charged to a later dialog, and
the microtask clear stays for the delivered case.
R14-2: window.devicePixelRatio is page script, and a spoofed value became
an unbounded clip.scale while the pixel re-measure only ran when the
probe failed. The probed ratio is now trusted only within 0.25..8, and the
measure-and-recapture correction runs whenever the first fallback capture
disagrees with the requested CSS region, deriving the real ratio from
Chrome's own pixels (measured / (width * scale)).
Also resolve the temp root in the ensureSocketDirectory tests: the
ancestor sweep rejects symlinks, and macOS reaches os.tmpdir() through
/var -> /private/var, so the tests failed locally on macOS.
* fix(browser-use): tear down accepted peers on start failure and pin the transport's untested paths
The esbuild banner declared require/__filename/__dirname/process bindings
the bundle never uses, so any bundled module declaring one of them built
green but failed to load; drop it (and the unused .wasm loader) and import
dist/index.js after bundling so an unloadable bundle fails the build.
When start() fails after listen (chmod, handshake), a peer validated in
between kept server.close() waiting forever; destroy and clear the
accepted sockets first, mirroring stop(). net.Server has no
closeAllConnections(), so that is the whole fix.
Tests now cover the QWEN_BROWSER_USE_SOCKET_PATH override, a hello with a
mismatched protocol version or extension id, onConnectionChange on the
real transport, an in-flight request when the extension disconnects and
the configured request budget, the ok:false error-code mapping,
FrameDecoder split-frame reassembly and its length ceiling, the
start-failure teardown, and all five schema refine predicates.
* fix(browser-use): mirror the contract bounds in the SDK and state the documented limits
nth() now rejects indexes outside the contract's -10000..10000 before
dispatch, and all() fails closed when the plan already has 32 steps or
the match count exceeds what nth() can address, instead of handing back
locators that only fail on first use. Both evaluate script builders
normalise method-shorthand and class-method sources the way Playwright
does (compile-only vm.Script probe, then a function prefix), so those
well-typed callbacks no longer die as a page-side SyntaxError.
providerTab clamps title and url to the claimTab schema's 20000-char cap
so the documented openTabs() -> claimTab() round trip survives long URLs,
and orderOpenTabs() establishes the http(s)-only, newest-first listing
the model documentation promises. downloadMedia derives the file name
from the URL path only for http(s) sources and leaves blob:/data: names
to the browser.
The model-facing documentation now states the 1s read default, that
dom_cua.scroll's x/y are wheel deltas rather than coordinates, and that
a cross-origin resource without CORS headers cannot be downloaded from
the page origin; the design docs record that boundary as follow-up work.
Tests pin every one of these, plus buildLocator's frame, combinator,
filter and positional branches, the file-chooser SDK surface, and the
claimTab argument guards.
* fix(browser-use): keep background tab registration from moving the selection
registerTab selected every tab it registered, and two of its callers are
background paths (derivedTabTracked, syncDerivedTabs via tabs.list), so
a read-only tabs.list could move tabs.selected onto a popup. Selection is
now set only by newTab and claimTab from the info they return.
Also: tabs.get is allowed while a dialog is open, matching tabs.list and
tabs.selected, which already return the dialog-safe TabInfo; the
auxiliary-button click moves Playwright's tracked cursor before sending
the CDP button events so a later wheel lands where the click did; the
dead providerTabId lookup is removed; openTabs applies orderOpenTabs.
Tests: the runtime test file mocks node:timers so vi.useFakeTimers()
actually controls the input drain bound (the 250ms test drops to ~0ms),
and new cases pin the drain clearTimeout, the registration probe's
session detach, promptText pass-through, tabs.get under a dialog, the
openTabs/claimTab fail-closed guards and ordering, popup adoption from
derivedTabTracked, the abandoned navigation waiter's swallowed rejection,
the uploadFiles path guards, and the screencast fallback and byte budget
on the unconstrained capture path.
* test(browser-use): pin the element bindings the runtime gives evaluate scripts
The SDK emits scripts that read the free variables `element` and
`elements`, and the runtime binds them when it compiles the script inside
the page; no test ran either page-side callback, so swapping the binding
name on one side kept the suite green while every locator.evaluate would
throw a ReferenceError in production. Run the real callbacks with fake
elements so the contract is pinned from both ends.
* fix(browser-use): fail closed on page-controlled strict-mode text and prefer contained media over anchor hrefs
* fix(browser-use): bound page title reads and download a located anchor's file over its icon
* fix(browser-use): correct media downloads and frame composition
* fix(browser-use): bound locator.count and allTextContents by the caller deadline
* fix(browser-use): preserve native snapshots for DOM CUA
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(browser-use): classify a closed tab from the tab's own state
The pinned playwright-core client renders a closed target as a plain Error:
only TimeoutError sets `name`, so the `name === 'TargetClosedError'` branch
in sanitizeOperationError never fired, and because evaluate-channel text
fails closed by design, a tab that closed or crashed while a page command
ran was reported as OPERATION_FAILED instead of the documented STALE_TAB.
Decide tab-gone in the dispatcher from `tab.stale` and `page.isClosed()`,
which the page observers keep current, before the text classifier runs.
Drop the dead name branch and replace the test that fabricated the name
with one built the way the client builds the error, plus dispatch-level
cases for both signals that fail without the dispatcher check.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(browser-use): restore the kernel realm process binding and land the round-5 review batch
Criticals from review round 5, each reproduced before fixing:
- The bundled skill imports the staged runtime inside the node_repl kernel,
whose untrusted vm context has no `process` global and denies the
`node:process` specifier.
|
||
|
|
b8fea284fd
|
fix(cua): make Linux input delivery adaptive (#11876) | ||
|
|
ba0ddcd0b7
|
fix(cua): preserve Linux observations and REPL diagnostics (#11829)
* fix(cua): preserve Linux observations and REPL diagnostics * fix(node-repl): bound unresponsive cancellation |
||
|
|
6a211b4652
|
chore(release): bump node repl to 0.1.4 (#11771)
Some checks failed
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Lint & Static (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / web-shell E2E Smoke (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (no-AK, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (ubuntu-22.04) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (windows-2022) (push) Blocked by required conditions
E2E Tests / Build for E2E (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/1 (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/1 (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 1/2 (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 2/2 (push) Blocked by required conditions
E2E Tests / E2E Interactive - OpenTUI renderer (bun) (push) Blocked by required conditions
E2E Tests / channel-plugin E2E (nightly) (push) Blocked by required conditions
E2E Tests / cron-interactive E2E (nightly) (push) Blocked by required conditions
E2E Tests / web-shell Browser Regression (push) Waiting to run
pnpm Worktree Smoke / Install (macos-latest) (push) Waiting to run
pnpm Worktree Smoke / Install (ubuntu-latest) (push) Waiting to run
pnpm Worktree Smoke / Install (windows-latest) (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
npm cache producer / Save npm cache (push) Has been cancelled
|
||
|
|
e6647b3a3b
|
fix(cua): bound observations and reduce model round trips (#11735)
* fix(cua): retain bounded observation baselines and cap text * perf(cua): reduce avoidable computer-use round trips * perf(node-repl): avoid repeated runtime rules in tool discovery * fix(cua): recognize attached sheets in exact-target validation * fix(cua): expose known refusal categories in app errors * perf(cua): Combine macOS bootstrap with initial app observation * perf(cua): Prefer exact selection for field replacement * perf(cua): Lead field edits with verified text selection * chore(cua): reconcile local optimizations with updated App routing |
||
|
|
2eaed75010
|
fix(cua): address tool failures and efficiency in OSWorld runs (#11683)
* fix(cua): address tool failures and efficiency in OSWorld runs * docs(cua): consolidate efficiency bugfix design notes |
||
|
|
4ec9f63d0a
|
feat(core): add CodeModeOnly tool execution (#10607)
* feat(core): add CodeModeOnly tool execution * fix(core): preserve CodeModeOnly execution semantics * fix(core): drop the tool_search reminder in CodeModeOnly CodeModeOnly hides `tool_search` and binds every deferred tool — schema included — into the `exec` description, so progressive discovery has nothing left to do. The session prelude still announced those tools as "reachable via `tool_search`", pointing the model at a tool that is never declared and re-billing the text on every cached prefix. Worse, tools already callable as `tools.web_fetch(...)` looked gated behind a lookup step that does not exist. The subagent and fork-resume callers already opted out, but the main-session prelude sites did not, so the gate now lives where the prelude is built rather than in each caller. * chore: refresh settings schema * chore: refresh companion notices * fix: register exec tool metadata * fix(cli): localize exec tool display name * fix(core): stop offering ToolSearch lookups in CodeModeOnly CodeModeOnly hides tool_search and binds every deferred schema into the exec description, so text that tells the model to look a tool up describes a surface it cannot reach. Return an empty deferred summary from the registry itself - this also covers the fork-resume reminder that bypassed the earlier call-site gate - and point the image zoom hint at tools.zoom_image. * feat(core): teach the system prompt CodeModeOnly's tool surface In CodeModeOnly the model can only call exec, so the tool guidance and the worked examples described a surface it does not have: bare tool names it cannot call, an instruction to issue several tool calls in one response, and examples in a direct tool-call syntax that does not exist there. Give "Using Your Tools" a code-mode branch that routes every tool through tools.<name>, states which tools are direct controls instead, and replaces multi-call parallelism with batching inside one exec program. Swap the four model-family example sets for one shared exec set, since the syntax differences they exist for do not apply. The mechanics of exec itself stay in its tool description; the prompt carries only policy. * test: include code mode host in standalone fixture * fix(core): return code mode media as multimodal output * feat(core): support image tool results in code mode * fix(core): carry deferred tool schemas in the exec description CodeModeOnly hides tool_search and never surfaces a nested call as a history functionCall, so a signature collapsed to Record<string, unknown> could never be filled in later: parameter names were reachable only by guessing, or by reading them off validation errors one at a time. Deferred tools keep their registry state; only the generated signature gains the schema. * fix(node-repl): restore the bin exec bit after rebuild npm chmods dist/index.js when it links the bin at install time, but the build deletes that exact file and tsc re-emits it as 0644. After any rebuild node_modules/.bin/node-repl-mcp therefore points at a non-executable target and spawning it fails with EACCES (exit 126) before the shebang is ever read. OR the exec bits into the emitted mode instead of setting 0o755, so a restrictive umask is preserved rather than widened. * fix(cli): pin patched Ink version * feat(cli): add collapsible tool call details * fix(dev): use CLI tsconfig outside repository * fix(cli): preserve expanded tool details after completion * fix(cli): fail headless runs on model API errors * fix(core): expose shell timeout bounds in code mode * feat(core): add code mode timeout helpers * feat(cli): expose code mode in settings * fix(mcp): preserve structured results for model and exec consumers * fix(core): preserve code mode output and truncate inline * feat(core): port scoped context tools to code mode * fix(build): synchronize pnpm lockfile and capability docs * fix(core): recover from rejected Responses encrypted reasoning |
||
|
|
5014a091ab
|
chore(release): bump node repl to 0.1.3 (#11299) | ||
|
|
5b470befa0
|
ci: make the Prettier lane a real gate (#11117)
* chore: format the 35 files CI had been silently rewriting
`scripts/lint.js --prettier` and the release workflow's `Format Project`
step both run `prettier --write`, which reformats in place and exits 0.
Neither is followed by a dirty-tree check, so the rewrites are discarded
when the job ends and these files have stayed unformatted on `main`.
This is the output of `prettier --write` over exactly those files, so the
next commit can turn the check into a real gate without failing on a
backlog. No behaviour change: every hunk is whitespace, quoting or
wrapping.
Verified with prettier 3.6.1, the version pinned in package-lock.json.
`prettier --check .` over the whole repo reports these 35 and nothing
else.
Refs #11109
* fix(ci): make the Prettier lane a real gate
`runPrettier()` ran `prettier --write .`. Prettier exits 0 after
reformatting, and no caller checks the tree afterwards, so the lane
passed on unformatted code and CI threw the rewrites away — the 35 files
in the previous commit are what it had been silently fixing and
discarding.
ESLint does not cover the gap: eslint.config.js loads
eslint-config-prettier, which disables formatting rules rather than
enforcing them. With this change the only enforcement point outside the
husky pre-commit hook actually enforces something.
`npm run lint:all` now reports unformatted files instead of fixing them;
`npm run format` is the fix. Prettier's own failure message says so.
Refs #11109
* fix(release): drop Format Project, it writes and discards
The step ran `npm run format` (`prettier --write .`) and nothing in
release.yml inspected the tree afterwards — no `git diff --exit-code`, no
`git status --porcelain` anywhere in the file. The rewrites died with the
job, so the only failure it could produce was Prettier crashing on an
unparsable file, which the Run Lint step reports anyway.
It cost 13m58s on a quiet host and 20m45s on a contended one, against a
30-minute cap that `quality_static` hit twice today (runs 33957952281 and
33963757913, both cancelled mid-lint with nothing failing). The remaining
steps are `npm ci` (1m44s) and `lint:ci` (~8m20s), so the job now has real
headroom instead of finishing 24 seconds over the wall.
Formatting is now gated where it belongs: the previous commit makes the
PR-CI Prettier lane `--check`.
Refs #11109
* fix(ci): keep prettier --check out of .qwen/ and the pre-paint theme script
Two things the `--write` → `--check` flip broke that `--write` had hidden.
`.qwen/` is gitignored as `.qwen/*` and holds machine-written state —
pending-skills/, auto-skill-*/, learned-skill-*/,
computer-use/installed.json. Prettier does not read .gitignore, so it was
walking in and silently reformatting whatever a contributor had there;
under `--check` that turns into a failed `npm run lint:all` on files git
does not even track. CI never saw it (fresh checkout, tracked files only),
which is exactly why it went unnoticed. Adding `.qwen/` to
.prettierignore also drops the one tracked file under it —
skills/find-simplifications/SKILL.md, force-added past .gitignore — from
the previous formatting commit, since a directory the gate should not
police should not be reformatted for it either.
The inline theme script in web-shell's index.html is restored to its
original single line behind `<!-- prettier-ignore -->`. It is a
render-blocking script in <head> that applies the stored theme before
first paint; the expanded form ships ~600 more bytes on the critical path
and buys no readability the comment above it does not already give. A
change about CI hygiene should not be reshaping that.
Refs #11109
* fix(ci): keep prettier --check out of vendored packages/mobile-mcp
eslint.config.js has ignored `packages/mobile-mcp/**` since it landed —
"vendored mobile-next/mobile-mcp; has own eslint config" — and
.prettierignore already carries its sibling `packages/cua-driver/`, listed
one line above with the same rationale. mobile-mcp was simply missed.
`--write` hid that: it reformatted the vendored package's own eslint
config to our house style every run and threw the result away. The
formatting commit in this PR would have made that permanent, widening the
diff against upstream on the next sync for a package this repo has already
decided not to lint. That file is reverted here and the directory is
ignored.
Same class as the .qwen/ fix in the previous commit: the gate reaching
somewhere the repo had already decided it should not.
Refs #11109
* fix(ci): mirror eslint's remaining global ignores in .prettierignore
`--check` makes .prettierignore load-bearing in a way `--write` never was:
under `--write` a file prettier should not touch was reformatted and the
result discarded, so nobody noticed; under `--check` it fails the lane.
These three are the rest of eslint.config.js's global ignores, added for
the reasons it already states. `scripts/codemod/fixtures/` is the one that
matters: those files are "intentionally non-idiomatic ink input/output",
so the obvious response to a `--check` failure there — reformat to house
style — would corrupt the fixtures the codemod tests assert against. The
trap is not live today (all three currently satisfy prettier), which is
precisely why it is worth closing before someone meets it.
Refs #11109
* chore: format converter.test.ts, which drifted onto main since this branch
This branch merged main 32 commits after its base and `prettier --check .`
found one new offender. It is the same gate failing the same way: nothing
enforces formatting, so `--write` reformatted this file on every CI run
since it landed and threw the result away.
Worth noting as evidence rather than noise — the backlog this PR fixes was
not a one-off from before someone got careless; it accrues continuously,
and it accrued again while the PR was open.
Refs #11109
* Revert "fix(release): drop Format Project, it writes and discards"
This reverts the release.yml half of this PR. #11195 removes that step on
its own, in one file and four lines, and two PRs racing on the same
deletion helps nobody.
The reasoning and the evidence are unchanged and now live on #11195: the
dry-run release dispatched from this branch with the step removed
(34022919438) went 18/18 green in 65m43s, quality_static finished in
7m00s against main's 8m57s, and Run Lint was 4m52s vs 4m43s — nothing
downstream ever read what the step produced.
Dropping release.yml from this PR also widens who can review it. It was
the only file here owned by @pomelo-nwu and @wenshao alone; what remains
under CODEOWNERS is /packages/core/, whose owner list is six people. The
Prettier gate no longer waits on the two maintainers who must sign off on
release workflow changes.
Refs #11109, #11195
* fix(ci): make the gate resolve the file set npm run format resolves
Two review findings, one root cause: the gate was checking a different set
of files than the tree it is supposed to police.
**The `.qwen/` ignore rested on a false premise (R1-1, @chiga0).** Prettier
3.6's `--ignore-path` already defaults to `[.gitignore, .prettierignore]`,
so every machine-written path that entry's comment named —
`pending-skills/`, `auto-skill-*/`, `learned-skill-*/` — was outside the
gate before it via `.gitignore:32`. What the entry actually excluded is the
corpus `.gitignore` deliberately re-includes: `.qwen/commands/**`,
`skills/**`, `agents/**`, `team-memory/**`, `review-context.json` — 57
tracked, hand-authored files. The gate then passed because the surface had
been narrowed, not because the tree was formatted, and contributors editing
those docs could ship them unformatted with both enforcement points silent.
The entry is dropped and the first commit's formatting of
`skills/find-simplifications/SKILL.md`, reverted alongside it, is re-landed.
**The gate and the remedy used different CLIs (R1-2).** `runPrettier()` ran
the classic `prettier --check .` while `npm run format` runs
`prettier --experimental-cli --write .`. The classic CLI reads only the
repo-root `.gitignore`; the experimental one also honours nested ones, and
this repo has 23 of those. Verified on a scratch tree: given
`pkg/.gitignore` containing `lib/`, classic reports `pkg/lib/nested.js` and
experimental skips it. So the lane could go red on a locally built package's
output that `npm run format` provably cannot clear. The gate now uses the
same CLI as the remedy it names.
Refs #11109
* fix(ci): let the pre-commit hook cover what the gate now checks, and pin it
**The hook was narrower than the gate (R1-5).** `lint-staged` formatted only
`*.{js,jsx,ts,tsx}` and `*.{json,md}`, but the gate checks every extension
Prettier has a parser for. Of the 35 files the first commit formatted, 29
(83%) are in classes the hook never touched — 13 `.yml`, 12 `.mjs`, 3
`.css`, 1 `.html`. Left alone, the backlog this PR just cleared would
re-accumulate, and now as a red CI step on someone else's PR rather than as
silent drift. `.mjs`, `.cjs`, `.yml`, `.yaml`, `.css` and `.html` join the
Prettier-only group; ESLint's globs are unchanged, since the gap is about
formatting coverage, not lint coverage.
**Nothing pinned the flag (R1-4).** `scripts/tests/lint.test.js` exercised
only the linter-directory helpers, and `ci-platform-lanes.test.js` asserts
that the `Run Prettier` step exists and is profile-gated, not what it runs.
A refactor, or a well-meant "make lint auto-fix again", could restore the
silent pass with every suite green. Two assertions now pin it: that
`runPrettier()` checks rather than writes, and that it uses the same
Prettier CLI as `npm run format`, so the two cannot drift apart again the
way R1-2 found them.
Refs #11109
* docs: correct the instructions and comments the --check flip falsified
**Three tracked instructions to automation agents now say the opposite of
what the code does (R1-9), and one is fed to a CI lane today** —
`.github/workflows/qwen-triage.yml` re-pins `verify-pr` from the base commit
and hands it to the verifying agent.
- `verify-pr/SKILL.md` told agents never to run `scripts/lint.js` with no
arguments because it would rewrite the working tree under their A/B
harness. That is no longer why. The prohibition is kept — it is still
right — but re-grounded in what the no-arg form actually does now
(`setupLinters()` wipes the linter temp dir and re-downloads three pinned
binaries), and `--prettier` joins the enumerated non-mutating checks it
was the only flag missing from.
- `find-simplifications/SKILL.md` and its `references/land.md` told the
simplification agent that a formatting diff "carries zero signal" because
CI runs `--write`. An agent believing that would skip `npm run format`
after a deletion changed line shape — on the very extensions the hook does
not cover — and go red on formatting it was told could not matter.
- `update-ecs-runner-qwen-workflow.test.js`'s comment justified parsing two
matrix layouts by "CI runs `prettier --write .` before this suite". CI no
longer rewrites anything; the two layouts are still worth parsing, so the
comment is re-grounded in the checked-in form versus older branches.
**The index.html rationale overstated its own number (R1-8, @chiga0).** It
claimed the expanded script ships "~600 more bytes"; measured against this
repo's Prettier 3.6.1 and its `tabWidth: 2`, it is +258 source and +8
gzipped here, +287/+20 by the reviewer's method — either way roughly half
what was claimed, and "~600" is what the same expansion costs at
`--tab-width 4`. Since the prose ships verbatim inside the built
`index.html`, the fix is to drop the contested figure and keep the comment
short. The `<!-- prettier-ignore -->` guard itself stays: it is load-bearing.
Refs #11109
* chore: format classify-profile.test.mjs, drifted again after commit 1
Third instance in this PR's lifetime, and the sharpest: commit `
|
||
|
|
d50b0283ef
|
fix(ci): keep worker tests responsive under ECS contention (#10813)
* test(ci): stabilize contention-sensitive worker tests Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(node-repl): bound native addon builds Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(node-repl): terminate stalled addon build trees Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(node-repl): spawn native builds in a process group Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
2b8f73c1e9
|
fix(cua): harden Node REPL and Windows UIAccess (#10666) | ||
|
|
bc186116c6
|
fix(ci): align non-Linux Vitest unhandled-error handling across unit suites (#10443)
* fix(ci): extend the vitest RPC-timeout exemption to every unit suite (#10438) * refactor(ci): collapse duplicated vitest exemption rationale to witness pointer (#10438) * refactor(ci): extend vitest rationale pointer consolidation to remaining unit configs (#10438) |
||
|
|
b89ba11ede
|
fix(cua): harden Computer Use sessions and instructions (#10319) | ||
|
|
3a46420d59
|
fix: repair the Windows test lane's standing failures (#10064)
* fix: repair the Windows test lane's standing failures
Fixes the eight failure clusters that keep Test (windows-latest) red on
the ECS nightly lane — three product bugs and five test bugs.
Product:
- extension-git-client: GIT_CONFIG_GLOBAL was os.devNull, whose win32
spelling ('\\.\nul') Git for Windows rejects with "Invalid argument",
failing every restricted-env git command on Windows. Use the literal
'/dev/null', which git's compat layer maps on every platform.
- sessionArtifacts: the pre-open/post-open identity check compared
numeric dev/ino. NTFS file ids are 64-bit and lose precision above
2^53 as Numbers, so two files created close together can round to the
same ino and defeat the swap (TOCTOU) detection. Compare bigint stats.
- dws-event-stream: clearing a stale stderr error on a healthy stdout
line was gated on the child still being alive, but on Windows the pipe
routinely drains only after exitCode is set, so the final healthy line
never cleared it and close reported the stale error. A clean (code 0)
exit now certifies recorded errors as stale at close.
Tests:
- daemon-git-worktree-guard: fixtures pasted native Windows paths into
command strings unquoted; on the bash lane the tokenizer consumes the
backslashes (C:\t\repo -> C:trepo), so resolvable targets tested as
unresolvable ones and cd shifts silently failed in place. Interpolate
through cmdPath(), which spells them with forward slashes on win32 —
what bash preserves and Windows resolves. Assertions keep the native
spelling; the guard canonicalizes targets through realpath.
- review/lib/worktree: git marks a linked worktree's .git gitfile hidden
on Windows and CREATE_ALWAYS on a hidden file fails with EPERM;
rewrite gitfiles via unlink-then-write.
- config: compare getCwd() against path.resolve(targetDir) — the
constructor resolves it, so the POSIX fixture comes back
drive-qualified on win32.
- SessionMessageHandler: complete the pathToFileURL shim's mirror case —
parse POSIX-spelled fixture paths with { windows: false } on win32 so
expected file:// URLs read the same on every host.
- node-repl.semantics: dispose the kernel before removing its cwd (a
process's cwd cannot be removed on Windows) and retry the removal
across the child's asynchronous teardown.
* fix: witness the Windows-lane fixes per review feedback (#10064)
- Document why the workspace-status identity fstat cannot be folded into
the Number stat (bigint stats truncate mtimeMs and return BigInt fields).
- Add a clean-exit fixture + test proving the dws consumer reports the
code-0 process error instead of a stale stderr error.
- Add an injected-stats test detecting swaps whose file ids differ only
above 2^53, so a numeric identity revert cannot ship green.
- Route the remaining unquoted/eval'd env-var assignment payloads in the
worktree guard tests through cmdPath so they stay resolvable on the
win32+bash lane.
* test(acp-bridge): consolidate the duplicated forwarding lstat spy (#10064)
---------
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
|
||
|
|
f9f5f2fcb6
|
fix(node-repl): make package verification idempotent (#10133) | ||
|
|
37cedea5b2
|
feat(computer-use): replace built-in tools with bundled skill (#9856) | ||
|
|
be891657f7
|
refactor(node-repl)!: deliver the persistent Node REPL as a standalone MCP server (#9499)
* feat(core): add persistent Node REPL runtime * fix(core): align Node REPL phase-one contract * fix(core): align Node REPL module compatibility * fix(core): remove unused Node REPL host broker * refactor(node-repl)!: deliver as a standalone MCP server, revert core tools Replaces the three built-in `packages/core` node_repl tools with a self-contained MCP server package, `@qwen-code/node-repl-mcp`. Why --- Issue #9333 triage accepted the runtime only "for exploration" and gated it on an open maintainer decision: built-in core tool vs MCP-server-first. Reversing OpenAI Codex 0.149.0 settled the shape — it ships code mode as a standalone host with an in-process fallback, and its real-Node `js_repl` (not the restricted V8 `exec`) is the analogue this roadmap needs, because stage 3 (#9335) imports cua-driver's N-API addons, which only real Node can load. Delivering out-of-core also keeps a security-relevant subsystem out of the maintainer-gated `packages/core` until its value is proven. What changed ------------ - New `packages/node-repl`: the kernel, module loader, cell transform, protocol and kernel manager, ported and now dependency-free (local `debug-log`/`win-path`/`tokenizer` replace core utils; `output-adapter` emits MCP content blocks in place of `result-converter`). - Reverted every `packages/core` change, the ~11 packaging files that existed only to ship the runtime assets into six distribution layouts, and the two design/plan docs describing the core delivery. - Deleted the trusted-package/sha256 layer: it was empty and unreachable in production (`module-loader.mjs` 882 -> 483 lines). - Wired the package into `scripts/build.js` and the root `vitest.config.ts`. Net effect: `packages/core` is untouched relative to main; the tool is opt-in via `mcpServers` instead of registered unconditionally for every user. Correctness fixes made while porting ------------------------------------ - Stack line numbers were wrong and drifted with binding count (source line 4 reported as 87). The prelude is now one physical line and the cell compiles with `lineOffset: -1`. - Top-level `var` nested in blocks/try/switch/loops was silently dropped; collection now walks the statement subtree, pruning at function boundaries. Verified against real Node for 16 constructs. - A binding named `nodeRepl` permanently broke the output channel; such cells are now rejected. Ordinary globals stay shadowable, matching plain Node. - Errors thrown by imported modules lost their class, `code` and stack. - A throwing frame handler could discard buffered protocol frames. - A hoisted `var` assigned before a throw is now kept, as Node does. - Unhandled rejections settling after a cell no longer vanish. - Live sandbox timers are capped, so one runaway loop cannot saturate the session's event loop. - Image MIME types are matched case-insensitively on both input paths. - Binding sort no longer depends on host locale collation. - The published bin lacked a shebang and mis-detected its entry point, and the server plus its kernel leaked on every host disconnect. Tests: 146 across 14 files, including a compiled N-API addon fixture, 100 consecutive cells, 10 concurrent isolated kernels, stack-line fidelity, and hoisting semantics. Three smoke scripts cover the adapter, the MCP wire and process lifecycle; the packed tarball was installed into a clean project and driven end to end. Refs #9333 * fix(node-repl): pin zod to the hoisted 3.x so the workspace build type-checks `packages/node-repl` declared `zod ^4.1.13`, so `npm ci` installed a nested zod 4.4.3 for it while `@modelcontextprotocol/sdk` resolved the hoisted zod 3.25.76. Two zod type identities in one compilation made every `registerTool` input schema unassignable (`Type 'ZodString' is not assignable to type 'AnySchema'`), failing `npm run build` for this workspace — and with it the install step of every CI job that builds workspaces. The SDK accepts `^3.25 || ^4.0`, so pin the hoisted 3.x line and drop the now-stale nested lockfile entry. One deduped zod, no behaviour change. This only reproduced with a lockfile-driven install; the local tree had no nested copy, which is why the build passed locally and failed in CI. * fix(lint): lint package .mjs node scripts with the node env The eslint "scripts we run with node" override matched `packages/*/scripts/**/*.js` but not `.mjs`, nor a package-root `build.mjs`. `packages/node-repl` is `type: module`, so its `build.mjs` and `scripts/*.mjs` were linted as browser code and `eslint .` failed with `'process' is not defined` / `'console' is not defined` / `'setTimeout' is not defined`. The pre-commit hook only lints `*.{js,jsx,ts,tsx}`, so `.mjs` files are not checked locally — this only surfaced in the root CI lint step. Add `packages/*/scripts/**/*.mjs` and `packages/*/build.mjs` to the override, matching the existing `.js` entries. Generic, additive, no behaviour change. * fix(node-repl): address round-3 review findings (resolution, error identity, image bound) Triaged the bot's round-3 critical findings against the ported code and fixed the three that were genuine defects here; each has a regression test. - R3-5 (resolution): a symlinked `<cwd>/node_modules` — the norm under pnpm, monorepo hoisting and shared CI caches — was silently dropped, so the documented zero-config `await import('pkg')` failed with "cannot resolve from 0 module roots" while plain Node resolved it. The implicit cwd root was applying a re-link self-comparison that only makes sense for registered roots (which carry a registration-time canonical baseline). Follow the symlink for the implicit root, but skip it entirely when the cwd node_modules is itself a registered root, so the registered root's revocation guard is not undermined. - R3-3 (error identity): an error thrown by a host builtin inside imported code (e.g. `fs.readFileSync` → ENOENT) was rewrapped message-only, dropping `code`/`errno`/`syscall`/`cause`/`stack` — so `catch (e) { if (e.code === 'ENOENT') }`, a ubiquitous Node idiom, silently took the wrong branch. Carry those fields onto the realm-wrapped error. - R3-6 (image bound): image-frame `mimeType` was unbounded — only `data.length` counted against the raw image budget — so a malformed/forged frame could retain a huge string that also got interpolated into a notice and tokenized. Bound the MIME at frame ingestion (a real image MIME is a few dozen chars). - R3-4 is by design (the runtime is not a security boundary), but the tool description recommended `createRequire` without noting it is not subject to the process denial or module-root containment the import path enforces; the description now says so. - R3-1 / R3-2 (the `.mjs` lint failure) were already fixed in an earlier commit. Suite: 148 tests (added symlinked-cwd resolution and host-builtin error-code regressions). The symlink fix initially defeated the registered-root revocation test; the registered-root deferral above resolves both. * docs(node-repl): note top-level function/class re-declaration needs a fresh name Round-3 review R3-12/R3-33: re-declaring an existing top-level function or class in a later cell is a link-time SyntaxError (they persist as `let`, which the prelude re-declares), whereas a plain Node REPL accepts it. A correct fix needs declaration-kind tracking the manager does not carry today; until then the tool description's rerun guidance ('prefer var') is corrected, since a function cannot be made rerunnable via var. --------- Co-authored-by: Claude <noreply@anthropic.com> |