mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
main
74
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a8eb74fdf5 |
fix(web-shell): polish files and trim preview resources (#14143)
* fix(web-shell): polish files and trim preview resources * test(cli): observe pending child Hook promises * fix(web-shell): address Files browser review findings * fix(vscode): sync Pierre modification notices * chore: ignore the repo-local .pnpm-store fallback directory pnpm falls back to a project-local store at .pnpm-store/ when the global store directory is not writable (e.g. sandboxed CI runners). It is a regenerable cache that node_modules hard-links into; its thousands of untracked files otherwise dirty the working tree and fail deterministic workspace-clean verification. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): address Pierre preview and file display review findings - getPreviewLanguage folds case on the extension only, so README.MD and config.YML highlight while whole-name detector keys (Gemfile, BUILD) keep their case-sensitive behavior - formatFileName strips the full Default_Ignorable_Code_Point class so zero-advance Hangul fillers and variation selectors cannot desync the tree search projection from the rendered name - pin the previously unwitnessed pierre-build guards: redirected Shiki subpaths count toward the 4 MiB budget, getModuleInfo may return null for redirected ids, emitted assets are skipped, and entry-module resolveId calls carry no importer - pin the theme name->module mapping with exact token colors and give the formatFileName display sites discriminating coverage with repeated-space names --------- Co-authored-by: 钉萁 <[email protected]> Co-authored-by: qwen-code[bot] <qwen-code[bot]@users.noreply.github.com> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> |
||
|
|
47463b79a7 |
chore: drop the unanchored workspace/ ignore rule that hides web-shell sources (#13155)
* chore: drop the unanchored workspace/ ignore rule that hides web-shell sources
The bare `workspace/` line under the brand-builder section matches every
directory named workspace at any depth, including the tracked source tree
packages/web-shell/client/daemon/workspace/. Its 34 files are tracked but
ignored, so a new file there never shows in git status and git add -A
skips it silently (a test in #13154 was nearly left out this way).
Nothing in the repo creates a workspace/ directory: the desktop brand
builder writes to brand-builds/, which stays ignored. The line arrived in
a squashed history import (
|
||
|
|
64dd058c9c |
ci(pnpm): install with pnpm everywhere and retire package-lock.json (#11859)
* ci(pnpm): install dependencies with pnpm in CI and release CI tests the graph release ships only if both install it the same way, so this moves both, keeping every `npm run` script and `npm publish` as they are. - ci.yml (7), release.yml (3) and release-vscode-companion.yml (2) install with `corepack pnpm install --frozen-lockfile`; release keeps --ignore-scripts plus its explicit postinstall and generate steps. - setup-node drops the npm cache and sets package-manager-cache: false, which setup-node v5+ needs once package.json declares a pnpm packageManager. - A composite action caches the pnpm store on the three always-hosted jobs; the ECS pool keeps its store on local disk. - version.js no longer lets npm reify node_modules: --no-workspaces-update on both `npm version` calls, and a package-lock-only refresh in step 9. - The notices generator, prepare-package's sharp pin and the standalone release's native package specs read the installed tree or pnpm-lock.yaml instead of package-lock.json keys. - New size baselines for the three workflows, which were already over them. * chore(vscode): regenerate NOTICES from the pnpm-installed tree Generated in CI (pnpm Worktree Smoke on ubuntu-latest) by the ported generator. Same entry set as before except the five packages npm and pnpm resolve differently: fdir 6.5.0, picomatch 4.0.5, mdurl 2.1.0, @types/node 22.20.1 and mime-db 1.52.0 (form-data's pinned copy). No entry lacks a license text or repository. * fix(pnpm): resolve undeclared zod peers to the repo's zod, as npm does The first CI run on this branch failed the export renderer's size budget: 1,930,015 bytes against 1,930,000, where the same commit built on npm's tree comes to 1,858,186. Comparing both trees' esbuild metafiles and prebuilt bundles put the whole difference in web-shell's transcript.js (+100,903 bytes pre-minify), and the added code is zod 4 plus the @modelcontextprotocol/ext-apps schemas; the sdk daemon bundle is identical. web-shell bundles ext-apps, whose zod and MCP SDK are peers, and declares no zod itself, so pnpm filled the peer with the highest locked zod (4.4.3) while npm serves the hoisted 3.25.76. Declaring zod 3.25.76 as a root devDependency makes pnpm resolve every such peer to it (resolve-peers-from-workspace-root is on by default), which is npm's hoisting behaviour. mobile-mcp keeps its own zod 4.4.3, as under npm; the npm tree does not change. * fix(pnpm): keep npm's root @types/node so root-level typechecks match The no-AK integration gate typechecks integration-tests/, which takes Node's types from the root node_modules. npm hoists @types/node 20.19.1 there, because acp-bridge and core pin it; pnpm, with no root declaration, hoisted 22.20.1, whose spawnSync overloads type process-registry.ts's stdout as string | NonSharedBuffer and fail TS2322. The same code passes on every npm-installed PR. Declaring 20.19.1 as a root devDependency puts npm's copy back where a root-level tsconfig looks. The npm tree does not change. * ci(pnpm): install with pnpm in the remaining workflows The follow-up to switching CI and release: every other workflow that installs the root workspace now uses the pinned pnpm with --frozen-lockfile, so no job tests a graph that release does not ship. - e2e, tui-parity, repo-hygiene, web-shell-visuals, serve-ab, sdk-java, windows-runner-smoke, qwen-autofix, the SDK releases, finalize-release, sync-release-to-oss, the root install in desktop-release and cd-cua-driver, and the sandbox Dockerfile. - setup-node drops the root npm cache (package-manager-cache: false); hosted jobs restore the pnpm store through the shared composite action, and qwen-autofix keeps af-012's rule that the persistent pool restores no remote cache. - serve-ab and web-shell-visuals install a separate base checkout that can predate pnpm-lock.yaml, so those installs fall back to npm ci there. - Out of scope, unchanged: packages with their own lockfile (cua-driver's TypeScript SDK, desktop-shell, live-host, mobile-mcp), and the triage lanes with npm-cache.yml and the npm audit gate, which move with the package-lock.json retirement. - New size baselines for the touched workflows; qwen-autofix stays under the 470000-byte gate. * fix(pnpm): link the workspace packages that root-level code imports npm links every workspace package into the root node_modules, so integration-tests/ and scripts/, which are not workspace packages themselves, can import @qwen-code/* by name. pnpm links a workspace package only where it is a declared dependency, and the root declared none, so the no-AK integration gate could not find @qwen-code/qwen-code-core. The root now declares the seven packages that root-level code imports at runtime as file: devDependencies: acp-bridge, channel-base, the CLI, core, sdk, web-shell and web-templates. npm already links them, so its tree does not change (letting npm re-resolve the lock reproduces the same root entry); .pnpmfile.mjs rewrites them to workspace:*, which pnpm links at the root. * ci(pnpm): move triage, the store producer and the root audit to pnpm - qwen-triage's verify and tmux-testing lanes install the PR with `corepack pnpm install --frozen-lockfile` from a restored pnpm store. The one retry clears node_modules first, because pnpm install, unlike npm ci, keeps it. - npm-cache.yml becomes pnpm-store.yml, keyed on pnpm-lock.yaml. - security-checks audits the root workspace with `pnpm audit`, which reads the lockfile, so the root install step goes. The vendored lockfiles keep npm, and the retry recognises pnpm's endpoint error too. - qwen-autofix drops the hosted-only store cache: its heavy jobs forbid local `uses: './...'` actions, and the file sits at the size gate. - cd-cua-driver: setup-node@v4 has no package-manager-cache input. - package-assets fixtures install sharp rather than listing it in package-lock.json, matching how prepare-package now resolves it. - AGENTS.md, CONTRIBUTING.md and the npm workspace guide install with pnpm. * feat(review): install pnpm repos in qwen review's build/test qwen review installed dependencies only for a repo with package-lock.json and reported a pnpm repo as unsupported, so reviews of qwen-code would lose their build and test once its npm lockfile goes. - lockfileInstaller() picks npm or pnpm from the committed lockfile. With both, `packageManager: pnpm@...` chooses pnpm; anything else keeps npm. - A pnpm repo installs with `corepack pnpm install --frozen-lockfile` through the same disk, budget and sandbox path as `npm ci`, and counts as installed once node_modules/.modules.yaml exists. - Scripts still run through `npm run`, and the npm path is unchanged. * ci(pnpm): retire the root package-lock.json pnpm-lock.yaml is now the only root lockfile. - check-lockfile keeps the pnpm integrity check and Playwright parity, which now reads pnpm importers and snapshots. The npm-vs-pnpm agreement and build-approval checks go: pnpm 11's strictDepBuilds and every CI install's --frozen-lockfile enforce what they covered. - pnpm-lock-freshness.yml goes; it regenerated pnpm-lock.yaml from package-lock.json. - Release: version.js drops its package-lock refresh, the release commit stages pnpm-lock.yaml and the SDK release stages no lockfile, so neither `git add` fails on the missing file. `npm version` in the SDK and mobile-mcp releases passes --no-workspaces-update so it cannot reinstall the root with npm. - cd-mobile-mcp installs from the root pnpm lockfile. setup-node in cd-mobile-mcp, qwen-code-pr-review and desktop-release no longer keys an npm cache on the root lockfile. - check-desktop-isolation reads pnpm importers; build.js, build_sandbox.js and preflight install with pnpm. - pnpm-lock.yaml, pnpm-workspace.yaml and .pnpmfile.mjs join the autofix supply-chain class and repo-hygiene's deny list; pnpm-lock.yaml joins autofix's generated-diff exclusions, sdk-java's path filter and the platform-sensitivity manifests. - .gitignore lists /package-lock.json so an npm install cannot commit it back. Tests and docs follow. * fix(standalone): read the node-pty versions from the pnpm lockfile * chore(vscode-ide-companion): regenerate NOTICES.txt after the lockfile swap * chore(vscode-ide-companion): regenerate NOTICES.txt from the pnpm dependency tree The committed NOTICES.txt was generated from an npm install and does not match the tree produced by the pnpm install the release pipeline now uses (659 resolved dependencies, differing @types/node and mime-db versions). Regenerating with the pinned pnpm install makes the freshness check reproducible. * fix(ci): drop the npm cache from the daily dependency audit actions/setup-node's npm cache keys off the root package-lock.json, which this branch retires, so the scheduled job would fail at Set up Node before the audit ran. No pull_request run reaches this workflow to catch it. The root tree is never installed here either: pnpm audit reads pnpm-lock.yaml directly. Matches the sibling workflows that already set package-manager-cache: false; scripts/tests/security-workflows.test.js now pins it. * chore(ci): stop banking main-side drift in the workflow size ratchet The ratchet exists to turn a workflow's growth into one reviewed line. This branch re-recorded entries for 17 workflows it never touches, several upward by 0.4-2.8 KB - drift that happened on main, banked into the manifest without review. Restore those to their merge-base values, so .size-baseline changes only for the 22 workflows this branch actually edits. The entries were not needed to go green: ci.yml passes the PR's base commit, so a file that drifted on main and is otherwise untouched earns a warning, not a failure. * fix(release): initialize standalone state before main * test(browser-use): read Playwright pin from pnpm lock * fix(ci): address pnpm migration review blockers * fix(desktop): stop reading the retired root lockfile --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
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 <[email protected]>
* 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 [email protected] snapshot keyed with @vitest/browser and
[email protected]. 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 <[email protected]>
* 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 <[email protected]>
* 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.
|
||
|
|
829385a14e |
feat(qwen-live): protocol v7 playback receipts and interactive init — M5 (#10769)
* feat(qwen-live): interactive init command with agent detection
`qwen-live init` walks a new user through setup:
- Scans PATH for supported coding agents (qodercli, qwen, gemini,
claude, codex) via `--version` probe, resolving each to an absolute
path. Native agents (qodercli/qwen/gemini) use their resolved binary
directly; adapter-based agents (claude/codex) configure npx to run
the ACP adapter, passing the detected binary via env (CODEX_PATH).
- Interactive prompts (prompts library) let the user pick a default
backend, add additional backends, enter their DashScope API key
(with env-var reuse option), and set a working directory.
- macOS: checks the Live Host app via LiveHostInstaller.refresh();
offers to install if missing. Non-macOS: informs the user the Host
is required.
- Writes ~/.qwen-live/config.json atomically (tmp + rename) with
user-only permissions (0o600).
Subcommand dispatch in index.ts: `qwen-live init` runs the wizard,
everything else starts the daemon.
* feat(qwen-live,live-host): protocol v7 playback receipts
The injector's playback window no longer guesses from byte counts;
the Host app sends real playback_started/completed events.
Protocol:
- LIVE_HOST_PROTOCOL_VERSION 6 → 7 (types.ts both copies +
live-host shared/protocol.ts)
- New Host → Daemon messages: host.playback_started, host.playback_completed
(both carry epoch for call-scoping)
- CLI coordinator copy accepts the new messages (v7 Host connecting to
built-in serve is not rejected as invalid)
- FakeHost in e2e harness bumped to v7
Daemon side (packages/qwen-live):
- Injector: replaces playbackDeadline (byte ÷ 48kB/s estimation) with
playbackInProgress boolean + playbackCompletedAt timestamp.
notePlaybackStarted/notePlaybackCompleted replace noteOutputAudio.
windowClosedForMs returns -1 while playback is active, waits
completedAt + quietGapMs after completion, returns 0 if no playback
or cleared.
- v6 forward compatibility: onOutputAudioDelta calls notePlaybackStarted
as a fallback (v6 Host never sends receipts; v7 Host does). The
fallback preserves the existing "speech after audio triggers
clearOutput" behavior without the byte-estimation wait.
- live-session: onPlaybackStarted/onPlaybackCompleted wired from
coordinator handlers to injector signals
- Coordinator: parseHostMessage + handleTextFrame dispatch the new
messages; LiveCallHandlers gains optional onPlaybackStarted/
onPlaybackCompleted; epoch-scoped (stale receipts ignored)
Host app (packages/live-host):
- audio-engine.ts: constructor gains onPlaybackStarted/
onPlaybackCompleted callbacks; play() fires started on 0→1 source
transition; source.onended fires completed on 1→0 transition
(generation-guarded so clearOutput's stop() doesn't false-fire)
- preload/index.ts: callbacks → IPC live:audio:playback-started/
playback-completed with the current epoch; play IPC payload changes
from bare Uint8Array to { audio, epoch }
- main/index.ts: IPC handlers validate epoch === daemon.getEpoch(),
call daemon.sendPlaybackStarted/sendPlaybackCompleted
- daemon-connection.ts: sendPlaybackStarted/sendPlaybackCompleted
via sendControl
Tests: injector tests rewritten for receipt-based timing (started
holds, completed + gap delivers); e2e discovery assertion bumped to v7.
* test(qwen-live): v7 parity test and playback receipt coverage
- live-host parity test: version assertion bumped 6→7, 13/13 pass
- injector tests rewritten: playback_started holds window,
playback_completed + quiet gap delivers, clearOutput reopens
immediately, speech-started returns playbackInProgress
- e2e: FakeHost protocol version v7, discovery record assertion v7
* docs(qwen-live): rewrite README with quick start, init wizard, v7, and backend table
* fix(live-host): prefix unused IPC event parameters
* fix(live-host): gate playback IPC handlers with isTrustedSender
The playback-started/completed IPC channels skipped the sender
validation every sibling channel applies — an untrusted renderer
could forge playback receipts. Added isTrustedSender(event) as the
first guard, matching the pattern in live:set-output-muted etc.
* fix(qwen-live): wire playback receipts to injector + publish qwen-live with Host release
Two fixes in one commit:
1. Playback receipt wiring (qqqys's blocking review):
- daemon.ts setHandlers now passes onPlaybackStarted/
onPlaybackCompleted to the session
- LiveSession gains notePlaybackStarted/notePlaybackCompleted
methods that resolve the active context by epoch and forward
to the injector
- The per-delta notePlaybackStarted fallback in onOutputAudioDelta
is removed — it latched playbackInProgress closed with no matching
completion, blocking all queued injections until the user spoke
- v7 Host receipts now flow end-to-end: Host audio engine →
IPC → daemon-connection → WS frame → coordinator → session →
injector
- Test updated to call session.notePlaybackStarted directly
(matching the production coordinator→session path)
2. qwen-live npm publish in live-host-release.yml:
- Added checkout + install + build + npm publish steps to the
Host release publish job
- Version aligned with the Host release version via npm version
- Published as @qwen-code/qwen-live with --provenance
- Tag: "preview" for prereleases, "latest" for stable
- Skips if the version is already published
* fix(live-host-release): omit --provenance on first npm publish
@qwen-code/qwen-live does not exist on npm yet; provenance requires
an existing package. The workflow now checks if the package exists
and omits --provenance on the first publish, adds it on subsequent
ones.
* fix(live-host-release): use single quotes in NPM_TAG expression
GitHub Actions expressions require single quotes for string literals;
double quotes cause actionlint to fail.
---------
Co-authored-by: Claude <[email protected]>
|
||
|
|
c13aa351a0 |
feat(serve): hide workspace Browse on headless daemon hosts (#9406)
* fix(serve): let channel workers reach TLS-enabled daemons
The channel worker supervisor always handed workers an http:// loopback
URL, and workers rejected any other scheme, so on a daemon started with
--tls-cert/--tls-key the worker's first capabilities fetch hit the
HTTPS-only listener as plain HTTP and died with "fetch failed" before
reporting ready ("Channel worker exited before ready (code=1)").
- Emit an https:// loopback URL for the worker when TLS is configured
- Accept https loopback in the worker's QWEN_DAEMON_URL validation
- Inject NODE_EXTRA_CA_CERTS with the daemon cert into the worker env
(merged with an operator-set value, since it accepts a single file)
* fix(serve): make the worker TLS trust injection actually establish trust
Round 1 review found three ways the CA injection this PR adds silently
fails to give channel workers a usable trust anchor (R1-1, R1-2, R1-3),
plus the diagnosability and coverage gaps around it (R1-4..R1-8).
- R1-1: `--tls-cert` was forwarded to the worker verbatim. Workers are
forked with `cwd: opts.workspace`, so a relative path resolved against
the worker's cwd instead of the daemon's, Node silently ignored the
unloadable extra cert, and every handshake failed
DEPTH_ZERO_SELF_SIGNED_CERT — the exact pre-PR symptom. Resolve once at
the source, next to the read that already validated it.
- R1-2: the merged CA bundle went to `os.tmpdir()/qwen-worker-ca-<pid>.pem`,
a path predictable from the daemon PID (CWE-377/CWE-59). A pre-planted
symlink redirected the write; a pre-planted regular file kept attacker
ownership and mode while receiving the full cert — the private key too,
for a combined PEM. Write into an `mkdtempSync` 0700 directory instead,
the same defence standalone-update.ts already uses in this tmpdir.
- R1-3/R1-4: a serving cert only anchors trust when it signed itself, and
only reaches the worker when its SANs cover the loopback host workers
dial. Neither held for the `mkcert` flow this project documents, and
boot validation checked parse/expiry/validity-window only — so the
daemon booted green, browsers connected, and every worker restart-looped
with /health still green. `describeWorkerTlsTrustGaps` names both at
boot, the way the adjacent expiry guard does. The non-self-signed check
stays quiet when the operator set NODE_EXTRA_CA_CERTS, since that value
is merged into the worker bundle and may already carry the issuing root.
- R1-5: the merge-failure `catch` dropped the operator-set
NODE_EXTRA_CA_CERTS with no diagnostic, and Node stays silent when the
remaining cert loads fine. Emit a process warning naming both paths.
- R1-6: the bundle was never cleaned up. Merged bundles are now memoized
per (operator CA, daemon cert) pair — workers respawn on every restart,
so minting a directory per spawn would leak one per restart — and
removed on daemon exit.
- R1-7/R1-8: tests for the merge-failure fallback and for the
`workerTlsCaCertPath` pass-through, plus an end-to-end test that boots
the daemon with a relative `--tls-cert` and asserts the supervisor gets
an absolute path and an https daemon URL.
Verification: every fix was mutation-checked — reverting `path.resolve`,
the mkdtemp write, the trust-gap detection, the merge-failure warning, the
group pass-through, and the bundle memoization each turns at least one new
test red. `npx vitest run src/serve/run-qwen-serve.test.ts
src/serve/channel-worker-supervisor.test.ts
src/serve/channel-worker-group.test.ts` → 403 passed. eslint and prettier
clean on the six touched files.
* test(serve): declare the worker TLS trust check's NODE_EXTRA_CA_CERTS reads
The `Test (ubuntu-latest, Node 22.x)` job failed on
|
||
|
|
a511ce4a12 |
feat(desktop-shell): restore the brand builder skill for the Tauri shell (#10164)
* feat(desktop-shell): restore the brand builder skill for the Tauri shell The Electron-era desktop-brand-builder skill was removed with packages/desktop in #9085, leaving the Tauri shell without a white-label customization path. Restore it against the new branding hooks: tauri.conf.json, the icon set, and the bootstrap UI. The bundled script is dependency-free Node, regenerates icons via tauri icon, and empties updater endpoints so branded builds never touch the official feed. * fix(desktop-shell): give brand builder scripts node globals for eslint The desktop-brand-builder skill script runs under node but lives outside every existing node-scripts glob in eslint.config.js, so lint:ci failed with 7 no-undef errors on process/console/URL in brand-create.mjs. Add the skill scripts path to that existing block. Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com> * fix(desktop-shell): complete updater identity patching and fix npx spawn on Windows - When branded build has no custom updater endpoints, also clear the official pubkey and set bundle.createUpdaterArtifacts to false so the Tauri bundler does not produce signed updater artifacts that no feed will serve. - Add shell: true to the npx spawnSync call so it works on Windows where npx is npx.cmd. Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): address review findings for brand builder skill - Add brand-builds/ to .gitignore (R1-19) - Validate logo is a file, not a directory (R1-8) - Document target-specific bundle path in SKILL.md (R1-17) Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): harden brand-create against injection and shell safety - Use function replacers in replaceAll to prevent $& pattern interpretation when appName contains dollar signs (R1-3) - Quote logo path in shell spawn to handle paths with spaces (R1-4) - Add image extension validation for logo files (suggestion R1-6) - Document silent config mutations in SKILL.md (suggestion R1-5) Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): document cross-compile workflow and guard against re-run - SKILL.md: document that build:runtime must be re-run with QWEN_DESKTOP_TARGET set before each cross-compile target, otherwise the packaged artifact contains a wrong-arch Node binary. - brand-create.mjs: detect already-branded shell-root (productName no longer 'Qwen Code Desktop') and fail early with a clear message, preventing silent stale results from non-idempotent re-runs. - SKILL.md: add explicit 'never re-run in the same clone' warning to Failure Handling section. Addresses review findings R1-16 (cross-compile docs) and idempotency guard from qwen-code-ci-bot. Co-authored-by: Qwen Code <[email protected]> * fix(desktop-shell): harden brand-create against updater crash, icon misreport, and JS injection - Keep updater pubkey as empty string instead of deleting it (the tauri-plugin-updater schema requires `pubkey: String` with no default; removing it causes deserialization failure at startup). - Report accurate icon replacement status when `tauri icon` fails on non-PNG logos (previously claimed icon.png was replaced even when it was not). - Escape single quotes in appName before injecting into bootstrap.js single-quoted string literals (an appName like "Bob's App" caused SyntaxError). - Add root `npm install` step to SKILL.md workflow (build:runtime shells out to repo root which needs cross-env and other root devDependencies). Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): eliminate shell injection in brand icon generation (R1-4) Replace shell:true npx spawn with direct Node invocation of the Tauri CLI entry point. The logo path is now passed as a plain argv element, preventing command substitution via crafted filenames ($(cmd), `cmd`). Falls back to shell-less npx when @tauri-apps/cli is not resolvable from the shell root. Adds a test verifying generateIcons contains no shell:true usage. Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): require updaterPubkey when custom updater feed is configured (R1-1) When a brand supplies updaterEndpoints, the matching signing pubkey must also be provided. Without this check, custom feeds were paired with the official pubkey, breaking update verification permanently. loadConfig now fails fast when updaterEndpoints is non-empty but updaterPubkey is missing. patchTauriConfig sets the pubkey from the brand config when endpoints are present. SKILL.md documents updaterPubkey and TAURI_SIGNING_PRIVATE_KEY usage. Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): reject appName matching pristine default to close re-run guard bypass (R3-4) When appName equals 'Qwen Code Desktop' (either explicitly or derived from brandId like 'qwen-code-desktop'), the detectAlreadyBranded guard never fires on the first run, allowing a second run to double-patch bootstrap strings. loadConfig now rejects this appName at validation time, before any files are mutated. Covers both explicit override and derived paths. Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): silence empty-block lint in brand-create-safety test Add /* best-effort cleanup */ comments inside two catch blocks used for teardown so ESLint no-empty rule is satisfied. Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): escape backslashes and newlines in appName for bootstrap.js injection brand-create.mjs only escaped single quotes before splicing appName into the single-quoted string literals of bootstrap/bootstrap.js. A free-form appName ending in a backslash (or containing a newline) produced syntactically invalid generated bootstrap.js while the script still exited 0 reporting bootstrapPatched. Derive the injected content via JSON.stringify so backslashes, newlines, and control characters are all escaped, then re-quote it for the single-quoted context. Add a safety test covering hostile appName values. Co-authored-by: Qwen-Coder <[email protected]> * fix(desktop-shell): HTML-escape appName in bootstrap/index.html, fail closed on missing updater section (R5-1, R5-8) R5-1: appName was spliced raw into bootstrap/index.html (<title>, alt attribute, <h1>, <h2>), so free-form brand names with <, > or " produced broken markup (and a </title> breakout). The index.html path now uses an HTML-escaped form while bootstrap.js keeps its JS-literal escaping. R5-8: when the target shell-root's tauri.conf.json has no plugins.updater section, the brand's validated updaterEndpoints/updaterPubkey were silently discarded while brand-create exited 0 reporting them as applied. Fail closed before any file is written, leaving the config unmutated. Tests: hostile-appName index.html fixture assertions (escaped output, no raw breakout) and a no-plugins.updater shell-root case (non-zero exit, stderr names the missing section, config byte-identical). Both new assertions go red when the corresponding fix is removed. Suite 8/8 green; prettier + eslint clean on both changed files. Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com> * fix(desktop-shell): fail on non-array updaterEndpoints in brand.json A scalar updaterEndpoints (e.g. a string typo of the JSON-array shape documented in SKILL.md) was silently coerced to [] before the updater-safety guards ran, so brand-create exited 0 while shipping the brand with in-app updates permanently disabled. Reject any non-array value with an explicit error naming the expected shape; omitted or [] still disables updates as documented. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Qwen Code <[email protected]> Co-authored-by: root <[email protected]> |
||
|
|
fb6637f0d3 |
chore(ci): Add security hygiene: CODEOWNERS for release workflows, least-privilege permissions, security checks and Scorecard (#9008)
* chore(ci): add security hygiene: CODEOWNERS for release workflows, least-privilege permissions, security checks and scorecard workflows * chore(ci): pin TruffleHog scanner version and drop invalid path input * fix(ci): close security workflow review gaps * fix(ci): fail package audit on install errors * test(ci): pin security workflow guardrails * fix(ci): pin security workflow test assertions for SHA refs, status edges, and push trigger * test(ci): pin security workflow edge guards * test(ci): pin security workflow contracts * test(ci): pin secret-scan push guard * fix(ci): quote secret-scan condition * fix(ci): audit workspace package locks directly * fix(ci): scope security checks concurrency * docs(ci): explain mobile audit skip * test(ci): link trufflehog version pin |
||
|
|
7bc71e4207 |
feat(review): add qwen-code repository context manifest (#8654)
* feat(review): add qwen-code repository context manifest Co-authored-by: Qwen-Coder <[email protected]> * fix(review): keep repo context manifest under relatedPaths budget Drop the review rule's relatedPaths glob, which resolved ~125 files and left the merged expansion four files of headroom under the 128 cap: any co-matching rule (core/skills, core/config, web-shell) pushed the sum past the cap and aborted `review repo-context` fail-closed before any agent launched. Changed files are already in the diff, and the rule's domains, tests, and configurations still attach. Add a collocated test that runs the real provider against the committed manifest — per-rule matching, web-shell expansion, and the widest co-matching combination — so a future hand-edit the strict parser rejects, or that outgrows the budget, fails in CI instead of mid-review. * fix(review): exclude local build artifacts from review context expansion The web-shell e2e relatedPaths glob expanded against the live filesystem, so developer-local Playwright artifact trees could cross the resolved-file bound and fail committed-manifest tests (and local repo-context runs) closed with no manifest change. Narrow that glob to source-only patterns and extend the provider's never-descend set with Playwright artifact directory names. Strengthen the committed-manifest tests to derive one probe per paths pattern, pin rendered payloads and paths patterns, assert each relatedPaths glob resolves, and gate the .gitignore un-ignore hunk on the manifest staying un-ignored and tracked. Co-authored-by: Qwen-Coder <[email protected]> * test(review): pin repository context manifest policy Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
cb2555c7c5 |
feat(desktop): package Web Shell as a release-ready desktop app (#8132)
* feat(desktop): add Web Shell Tauri proof of concept * feat(desktop): prepare Web Shell shell for release * fix(desktop): make release dry runs portable * fix(desktop): harden cross-platform release smoke * fix(desktop): stabilize Windows and Linux CI * fix(desktop): scope bootstrap env to daemon * fix(desktop): stabilize packaged app smoke * fix(desktop): diagnose Linux packaged startup * fix(desktop): address release readiness review * fix(desktop): address follow-up review findings * fix(desktop): address runtime review blockers * fix(desktop): gate cookie auth acceptance behind desktop bootstrap flag - Cookie→Bearer translation middleware now only active when desktopShellBootstrap is enabled - Use timing-safe comparison for bootstrap token validation * fix(desktop): replace cookie handshake with URL fragment auth - Navigate the desktop WebView to /#token=<token>; the fragment never reaches the server, so drop the desktop cookie bootstrap middleware, its cookie->bearer translation, and the related serve tests - Skip the deferred-runtime auth gate for pre-auth Web Shell routes (GET|HEAD / and /assets/*): a document navigation cannot carry an Authorization header, so the fast-path window used to answer the first desktop navigation with 401 Unauthorized until a manual reload - Poll /health?deep=true before navigating: deep health stays 503 (reason: bootstrap) until the runtime app that mounts the Web Shell is ready, so readiness can no longer race the deferred window - Run the folder picker off the main thread and only store the runtime after the WebView navigation succeeds - Enable withGlobalTauri plus a bootstrap capability so the bootstrap page can subscribe to desktop lifecycle events - Update smoke-packaged to assert the fragment contract (unauthenticated root navigation 200, no cookies minted, API routes still 401) and sync the release design doc * fix(desktop): fix Linux smoke log path, add runtime .gitkeep, correct README (#8132) * fix(desktop): close release readiness gaps * fix(cli): keep deferred serve auth gate closed when web shell unmounted (#8132) * fix(desktop): address review feedback on auth gates and runtime bundle (#8132) - Cover the method guard in isPreAuthWebShellRequest: assert unauthenticated POST to / and /assets/* is still 401 during the deferred runtime window. - Add unit tests for is_allowed_navigation covering the unset origin, set origin, and bootstrap-after-origin cases. - Drop DEV:'true' from the release bundle step so the esbuild metafile is no longer shipped as dead weight in the desktop runtime. * fix(desktop): address review feedback on runtime extraction and release workflow (#8132) - Extract .zip Node archives with unzip so Linux cross-builds for win32-x64 no longer crash on GNU tar. - Build the Windows signing config with ConvertTo-Json instead of backslash escapes, which PowerShell treats as a parse error. - Fetch the runtime Web Shell without a bearer token so the smoke test exercises the pre-auth navigation path the shell relies on. - Make GitHub release creation idempotent so a re-run after a partial publish uploads assets instead of failing on the existing tag. * fix(desktop): normalize artifact filenames to prevent updater 404s (#8132) GitHub rewrites spaces to dots when release assets are uploaded, but the updater manifest encoded spaces as %20 via encodeURIComponent. This caused every platform's auto-update URL to 404 on published releases. Replace spaces with hyphens in the Collect artifacts step for all platforms so the local filename, the manifest URL, and the published asset name agree by construction. Update test-release.js fixtures to match and assert no artifact name contains a space. * fix(desktop): address review feedback on security, lint, and code quality (#8132) * fix(desktop): address review feedback on smoke test, error UX, and window state (#8132) * fix(desktop): address review feedback on crate build, recovery UX, auth gate, and CI (#8132) * fix(desktop): address review feedback on settings race, version script, and log growth (#8132) * fix(desktop): address review feedback on retry, auth gate, and release clobber (#8132) * fix(desktop): gate commands to bootstrap origin and show native update dialog (#8132) * fix(desktop): use matches! instead of PartialEq on JoinError result (#8132) * fix(desktop): wait for deferred runtime in smoke tests and sync release flags on clobber (#8132) --------- Co-authored-by: Qwen Code Bot <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: qwen-code-ci-bot <[email protected]> Co-authored-by: Qwen Code Autofix <[email protected]> |
||
|
|
02c79beb62 |
feat(web-shell): auto-post visual previews (screenshots + flow GIFs) on PRs (#6880)
* feat(web-shell): auto-post visual previews (screenshots + flow GIFs) on PRs PRs that touch the web-shell UI now get an auto-updated comment with light/dark screenshots of key views (transcript, slash menu, model/theme dialogs, permission panel) and short GIF recordings of common flows, rendered against the existing mock daemon — no real backend, no secrets. Split into two workflows for security, since capture runs untrusted PR code: - web-shell-visuals.yml (pull_request): checks out the PR head, builds and renders it with Playwright, captures PNGs + webm, converts webm->GIF with ffmpeg, and uploads an artifact. `contents: read` only, references no secrets — fork PRs run with a read-only token and no secrets. - web-shell-visuals-publish.yml (workflow_run): downloads the artifact, binds it to its real PR by requiring the PR head SHA to equal the run's authenticated head SHA, hosts the images on a per-PR `pr-assets/*` branch (referenced by immutable commit SHA), and posts/updates one inline comment. Never checks out or runs PR code; the write token lives only here. Capture infra is self-contained in packages/web-shell (playwright.visuals.config.ts + client/e2e/visuals/*), reusing the mock daemon harness. Run locally with: `npm run test:e2e:visuals --workspace=packages/web-shell`. * fix(web-shell): guard empty gh api response in visuals publish Addresses review feedback on #6880: if `gh api` returns empty (network error / rate limit), jq on empty stdin errors and `set -e` kills the publish job. Skip gracefully instead. * fix(web-shell): address review nits on visuals capture - harness recordFlow: wrap video saveAs/delete in try/catch so a video I/O error (e.g. drive failed before navigation) can't mask the real driveError. - capture workflow: drop the unused head_sha.txt artifact field; the publish job binds to the authenticated workflow_run.head_sha, and an artifact-sourced SHA would be untrusted. * fix(web-shell): address second review round on visuals capture - context.close() in recordFlow's finally is now best-effort (try/catch) so a close/crash error can't mask the real driveError. - add a flows spec that asserts a throwing drive propagates its own error. - trigger the capture workflow on playwright.visuals.config.ts changes too. * fix(web-shell): address third review round on visuals capture - harness: log (don't silently swallow) a video save/null when drive succeeded; keep masking-suppression only when driveError is set. - publish: HTML-escape interpolated values in the comment builder (defense in depth, independent of the upstream filename sanitization); fix the stale 'single pr-assets branch' comment and key concurrency on source repo+branch so different PRs (incl. same-named fork branches) parallelize. - capture: bump checkout to v6.0.3 (repo standard); surface ffmpeg's stderr on GIF-conversion failure instead of discarding it. * fix(web-shell): harden visuals publish/capture (review round 4) Publish (privileged workflow_run): - CRITICAL: capture basename before `tr` so its trailing newline isn't turned into `_` (which broke the .png/.gif filter -> empty preview). - dedup only against the bot's OWN comment (author + marker), not any marker-bearing comment a participant can post. - bound the pr-assets branch: force-push a single orphan snapshot per run (previous snapshot GC'd) instead of appending unbounded untrusted content; this also removes the rebase/retry path. - cap EXAMINED candidates (not just accepted) before validation; tighten per-file (3MiB) and accepted-image (14) caps. - re-validate PR open + head-SHA immediately before the comment write (TOCTOU); retry the comment listing and abort rather than POST a duplicate when listing fails. - esc() the runUrl for consistency with the self-defending HTML. Capture (pull_request): - upload raw recordings as a SEPARATE artifact the publisher never downloads, so an untrusted multi-GB video can't exhaust the privileged job. - also trigger on packages/webui/src and packages/sdk-typescript/src (the visuals dev server aliases them). - create screenshots/gifs dirs before the metadata counts (defensive). Harness recordFlow: - track drive failure with an explicit boolean (handles `throw undefined`); discard the recording on failure so a failed flow leaves no bogus webm. * refactor(web-shell): extract + unit-test the visuals publish staging/comment Addresses the review's testability gap (the class of bug that let the filename sanitizer break the whole preview slip through green CI). The image validation (magic bytes, filename sanitization, examined/accepted/size caps) and the comment builder (light/dark pairing, flow labels, HTML escaping) move from inline workflow bash/node into .github/scripts/web-shell-visuals-publish .mjs, covered by web-shell-visuals-publish.test.mjs (run in ci.yml's node --test line). The publish workflow sparse-checks-out and calls the script instead. Behaviour is unchanged; it just gained a test surface. * fix(web-shell): retry the visuals asset force-push; drop stale comment Round-4 switched hosting to a force-push but left a comment referencing a 'push-retry loop' that no longer existed, and the force-push was a single call that set -e would abort on a transient failure. Add a bounded retry and correct the comment. * fix(web-shell): harden visuals publish/capture (review round 6) Script (unit-tested): - flow labels: own-property lookup so `toString.gif`/`constructor.gif` can't leak Object.prototype members into the comment. - per-kind image caps (screenshots vs gifs) so a large screenshot set can't silently starve the flow GIFs from the preview. - tests for both, plus the per-kind cap. Publish: - bind the artifact PR number to the run's authenticated head repo+branch (not just head SHA), rejecting a sibling PR that shares the same commit. - re-validate before the force-push and again right before the comment write (close the download/stage/lookup TOCTOU windows). Capture: - bound artifact contents before upload (drop oversized / excess files) so an untrusted spec can't bloat the published or video artifact. - trigger on the capture workflow file itself. - new close-trigger cleanup workflow deletes a PR's asset branch on close, so pr-assets/* refs don't accumulate without bound. - single-source the capture viewport (constants.ts) shared by config + harness. - model-switch flow asserts the daemon model request actually fired. * fix(web-shell): stricter visuals error handling (review round 7) Harness recordFlow: - when the drive SUCCEEDS, a failed context.close() or video.saveAs() (or a missing recording) now FAILS the flow instead of a swallowed console.warn — a silent pass with no .webm makes the downstream GIF step fail confusingly. A drive FAILURE still discards the partial video and rethrows the original error (unchanged). Publish: - validate_pr distinguishes a transient API failure (empty after retries -> exit 1, re-triggerable) from a genuine invalid state (closed / head mismatch -> skip), via a `gate` wrapper used at all three checkpoints. - add a 2s backoff between comment-listing retries (matching the push retry). --------- Co-authored-by: wenshao <[email protected]> |
||
|
|
24b2442875 |
feat(cli): add /learn command for user-initiated skill creation (#6440)
* feat(cli): add /learn command for user-initiated skill creation Add a /learn slash command that lets users explicitly point the agent at knowledge sources (local dirs, URLs, conversation history, or freetext) to create reusable skills. Implementation uses submit_prompt (same pattern as /remember) to let the main model gather knowledge and write a SKILL.md in the normal turn. Skills are saved under .qwen/skills/learned-skill-<name>/ with source: learned frontmatter for edit isolation from auto-skills and user-authored skills. Also includes a forked-agent path (runLearnSkillByAgent) with scoped permissions for future ACP/auto-learn scenarios, plus 29 unit tests. * fix(i18n): add zh/zh-TW translations for /learn command description CI strict-parity check requires all built-in command descriptions to have translations in zh-CN and zh-TW locales. * fix(i18n): add en.js baseline key for /learn command description The check-i18n script requires all translated keys to also exist in the English baseline locale file. * refactor(core): remove unused forked-agent infrastructure, add collision guard Address review feedback on PR #6440: - Remove ~300 lines of unused forked-agent code (runLearnSkillByAgent, createLearnSkillScopedAgentConfig, scoped permissions, system prompt, buildLearnTaskPrompt) — no callers in production code. - Make buildLearnSkillPrompt async and add listExistingSkillDirNames enumeration to prevent skill name collisions. - Revert buildAgentHistory export (no longer needed). - Trim test suite from 29 to 10 tests (dead code tests removed). * feat(config): default auto-skill to disabled With /learn available for explicit skill creation, auto-skill (background skill extraction after 20 tool calls) is no longer needed as the default experience. Users who want automatic extraction can re-enable it via memory.enableAutoSkill in settings. * feat(config): align auto-skill default to disabled across all layers The prior commit only changed the core Config constructor default. CLI config builder, ACP agent defaults, and settings schema still defaulted to true, making the core change unreachable for CLI users. Align all layers: - cli/config.ts: ?? true → ?? false - acpAgent.ts: enableAutoSkill default true → false - settingsSchema.ts: default true → false - read-file.ts: resolve stale merge conflict markers * chore: regenerate settings.schema.json for auto-skill default change * test: update tests for auto-skill default change, fix stale conflict resolution - config.test.ts / acpAgent.test.ts: update assertions for the enableAutoSkill default flip (true -> false) from the prior commit. - read-file.ts: an earlier rebase conflict resolution incorrectly kept os.tmpdir() as an allowed read root — it was removed on main. Restore the main version so read-file.test.ts's "ask for OS temp paths" case passes again, and drop the now-unused os import. * fix(core): restore dynamic ignore-file message, wrap /learn input as opaque data - read-file.ts: an earlier rebase conflict resolution incorrectly hardcoded the ignore-file error message to '.qwenignore', losing the dynamic getQwenIgnoreFileDisplayForPath() call that reports the actual file (.agentignore, .cursorignore, etc.) that blocked the read. This was the real cause of the read-file.test.ts CI failures — not a pre-existing bug as previously assumed. - learn-skill-agent.ts: wrap the /learn command's raw user input in <user_data> tags with an explicit "don't follow instructions" preamble, matching the existing <user-content> pattern in remember.ts. Since buildLearnSkillPrompt feeds submit_prompt (full tool access), unwrapped content fetched from a URL could otherwise redirect tool calls. --------- Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
5c82857fea |
Add harness infrastructure for web-shell package (#6517)
* test(web-shell): add browser and lint harness * test(web-shell): harden browser smoke harness * fix(web-shell): guard mock daemon model state * test(web-shell): remove unused scenario harness * fix(web-shell): remove stale lint disables * test(web-shell): make matchMedia stub writable * fix(web-shell): exclude tests from package typecheck * test(web-shell): tighten mock daemon route contract * Update packages/web-shell/client/e2e/utils/mockDaemon.ts Co-authored-by: qwen-code-ci-bot <[email protected]> * test(web-shell): clear stale SSE connections * ci(web-shell): gate smoke on full CI profile --------- Co-authored-by: ermin.zem <[email protected]> Co-authored-by: qwen-code-ci-bot <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: 易良 <[email protected]> |
||
|
|
1b58ede8e7 |
fix(cli): smoother live streaming preview — drop "generating more" cue, hold back partial table rows (#6340)
* fix(cli): drop redundant "generating more" cue from the live preview In non-VP mode the live markdown preview is clipped to a rendered-height budget so the frame never overflows the viewport and triggers ink's scroll-to-top full redraw. It used to append a "... generating more ..." cue (and code/math/mermaid blocks appended their own) to signal that the clipped tail was still coming. Since #6170 landed the incremental scrollback commit, that tail is streamed into <Static> in real time — clipped content is "still streaming" and reappears within a commit cycle, not "delayed output". The cue is therefore redundant noise that flickers in step with the commit cycle, so remove all four occurrences (outer preview clip, code block, mermaid block, math block). The row each cue used to occupy is reclaimed for content, so the total rendered height is unchanged: the code/math/mermaid RESERVED_LINES drop by one and the outer slice trigger switches from the (now-inlined) `clipped` flag to `keptLines < allLines.length`. The TableRenderer "… more rows streaming …" clamp is intentionally kept — an in-progress oversized table is not yet in scrollback, so that cue still carries information. Also gitignore the nested `.qwen/computer-use/` marker so the auto-generated artifact stops showing up as untracked. Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(cli): hold back the unterminated table row while streaming While a markdown table streams, the frontier line is often a half-typed row like `| a | b` with no closing `|` yet. Because TABLE_ROW_RE requires both a leading and trailing pipe, that partial line does not match, so the parser closed the table and rendered the partial as a plain text line below it — then, once the closing `|` arrived, flipped it into the table. This per-token flip changed the frame height and re-ran column autosizing on every keystroke, jittering the live table. Hold the partial row back instead: when pending, if the final line is an unterminated table row and at least one complete row already exists, skip it so `inTable` stays set and the end-of-content handler keeps rendering the accumulated rows as a live table. The row appears the moment it terminates. The `tableRows.length > 0` guard keeps the header + separator from blanking out while the very first row is still being typed. Note: this smooths the table content itself; it does not change the streaming repaint frequency, so the fixed bottom controls still repaint on each tick (that is the domain of the flicker-reduction work, e.g. #5396). Co-Authored-By: Claude Opus 4.8 <[email protected]> * docs(cli): fix two stale "generating more" cue references in comments Review follow-up: two comments still referenced the removed outer cue. - TABLE_PENDING_RESERVED_ROWS: reword "marginY 2 + the outer cue" to "marginY 2 + one row of wrapped-cell safety headroom". The reserve stays at 3 on purpose — tables under-estimate their rendered height the most (wrapped cells), so they keep one more backstop row than the other blocks; lowering it would shrink that safety margin. - pending-rendered-height PendingSliceResult.keptLines JSDoc: drop the "plus a 'more' cue" phrasing — the caller now renders nothing rather than an oversized row. Comment-only; no behaviour change. 155 tests pass. Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(cli): hold back the partial first table row too; de-dup reserve constant Review follow-up on the streaming table hold-back. - The `tableRows.length > 0` guard skipped the hold-back for the FIRST data row: a partial first row fell through to the table-closing branch, which also requires a row, so the header + separator were dropped and the partial rendered as a stray text line — the same per-token flip the change is meant to remove, just for the first row. Relax the guard to `tableHeaders.length > 0` so an unterminated first row/separator is held back too; the table is simply not drawn until its first row terminates, then pops in complete and grows one row at a time. Comment corrected to describe the actual behaviour. - Add a test for that edge case (partial first row held back, table appears once the row terminates). - De-duplicate the magic `3`: the slice-side `tableClampRows` estimate now references `TABLE_PENDING_RESERVED_ROWS` (moved to the top-of-file constants) instead of a literal, so the estimate and RenderTable's render-side `maxHeight` cap can never diverge. 157 tests pass. Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: Claude Opus 4.8 <[email protected]> |
||
|
|
ea0749ead8 |
feat(web-shell): add daemon UI support for vision model selection (#6209)
* feat(web-shell): add daemon UI support for vision model selection Add /model --vision support to the web-shell daemon UI, mirroring the existing --fast and --voice patterns. - Add 'vision' to ModelDialogMode type and aria-label - Add --vision branch to /model handler (dialog + direct set) - Add handleVisionModelSelect callback - Wire vision into ModelDialog rendering (title, onSelect) - Add visionModel to SettingsMessage SUB_DIALOG_KEYS - Add visionModel to onSubDialog callback - Update localCommands argument hint - Add model.setVision i18n keys (en/zh-CN) * feat(web-shell): add daemon UI support for vision model selection Add /model --vision support to the web-shell daemon UI, mirroring the existing --fast and --voice patterns. - Add 'vision' to ModelDialogMode type and aria-label - Add --vision branch to /model handler (dialog + direct set) - Add handleVisionModelSelect callback - Wire vision into ModelDialog rendering (title, onSelect) - Add visionModel to SettingsMessage SUB_DIALOG_KEYS - Add visionModel to onSubDialog callback - Update localCommands argument hint - Add model.setVision i18n keys (en/zh-CN) * Updated .gitignore Co-authored-by: atlarix-agent <[email protected]> * adding atlarix to gitignore * chore: remove .atlarix/.atlarixignore from tracking * chore: revert unrelated .gitignore, package-lock.json, and NOTICES.txt changes These were local environment artifacts accidentally staged alongside the /model --vision feature. Reverting to keep the PR focused. - Restore .gitignore to base state - Restore package-lock.json (fsevents peer flag, test-utils entry) - Restore NOTICES.txt (hasown version) * Updated .gitignore Co-authored-by: atlarix-agent <[email protected]> * chore: add .atlarix/ to .gitignore Prevents Atlarix workspace files from being tracked in the repo. * Updated .gitignore Co-authored-by: atlarix-agent <[email protected]> --------- Co-authored-by: Qwen3.6 Plus agent <[email protected]> |
||
|
|
ca61d7827e |
feat(channels): add identity and task lifecycle metadata (#6105)
* chore: ignore local worktrees * docs(channels): design identity and task lifecycle p0 * docs(channels): plan identity and task lifecycle p0 * feat(channels): add identity and task lifecycle metadata * fix(channels): suppress cancelled tool call lifecycle * fix(channels): cover loop lifecycle metadata * fix(channels): harden lifecycle event edges * fix(channels): finalize cancelled lifecycle before cleanup * fix(channels): address lifecycle review suggestions * fix(channels): close lifecycle cancellation races * fix(channels): separate pending cancel state * fix(channels): order clear cancellation lifecycle * fix(channels): suppress loop chunks during pending cancel * test(channels): use active session in cancel regression * fix(channels): sanitize lifecycle tool fields * fix(channels): route shared tool call lifecycle * fix(channels): preserve pending cancel intent * fix(channels): preserve responses after failed cancel * fix(channels): tighten lifecycle cancellation reasons * fix(channels): close lifecycle cancel races and validate identity config Address the outstanding review findings on #6105: - carry a typed reason on ChannelLoopSkippedError and report disabled loops as 'dropped' instead of 'timeout' - treat a turn as committed once delivery starts: /cancel re-checks deliveryStarted after the cancel RPC settles, and neither prompt path lets a late-settling cancel rewrite a delivered turn into cancelled (or follow a /clear cancellation with completed) - tag failed lifecycle events with phase: agent vs delivery - validate identity/memoryScope shape at config parse time instead of throwing an opaque TypeError on the first prompt of every session - guard onPromptStart hooks, pass job.id to loop onPromptStart/End, append the boundary block after operator instructions, gate /who + /status identity lines on configured identity/memoryScope, cache the boundary prompt, and route error logs through lifecycleError() Co-authored-by: Qwen-Coder <[email protected]> * fix(channels): keep failed terminal event when cancel settles post-delivery Self-review follow-up: once delivery started, the catch paths no longer reconcile a pending cancel — a late-resolving cancel RPC used to flip cancelled=true there, suppressing the failed emit while the /cancel handler (seeing deliveryStarted) also declined to emit, leaving a started task with no terminal lifecycle event at all. Co-authored-by: Qwen-Coder <[email protected]> * fix(channels): hold streamed chunks while a cancel is pending Chunks arriving while a /cancel RPC is in flight were pushed straight into the BlockStreamer, which can send a block on a size/paragraph threshold before the cancel resolves — leaking output a successful cancel can never recall. Hold the pending-window chunks instead: replay them (block streaming + text_chunk transcript) when the cancel fails, discard them when it succeeds. onResponseChunk stays live through the window so adapter-accumulated display state has no permanent hole on a failed cancel; adapters gate visible updates on their own stop flags. Also stop passing the loop job id to onPromptStart/onPromptEnd (and the /clear eviction path): the hook contract is inbound platform message ids, and adapters act on them — cards and reactions keyed to a fake id. Lifecycle events still carry job.id for correlation. Co-authored-by: Qwen-Coder <[email protected]> * fix(channels): defer adapter chunks while cancel is pending --------- Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
6a5ad453c8 |
[codex] Add explicit channel memory for messaging channels (#6051)
* chore: ignore local worktrees * feat(core): add channel memory store * fix(core): preserve dots in channel memory paths * fix(core): guard dot channel memory paths * feat(channels): add channel memory commands * fix(channels): defer channel memory session invalidation * feat(channels): inject channel memory into sessions * fix(channels): serialize channel memory context injection * fix(channels): harden channel memory context races * fix(channels): let queued turn retry memory context * feat(cli): wire channel memory into channel startup * docs(channels): document channel memory * fix(channels): harden channel memory handling * fix(channels): gate channel memory injection * fix(channels): serialize channel memory clears * fix(channels): tolerate channel memory read failures * fix(channels): harden channel memory review gaps * fix(channels): harden channel memory review gaps * fix(channels): protect shared channel memory * test(channels): allow group memory view test * fix(channels): block group memory writes * fix(channels): include memory in loop prompts * fix(channels): preserve session context order after merge * fix(channels): retry loop memory context after read failure --------- Co-authored-by: qwen-code-dev-bot <[email protected]> |
||
|
|
7b9e31885b |
feat(web-shell): add mobile sidebar drawer with session list (#6003)
* feat(web-shell): add mobile sidebar drawer with session list Replace the display:none behavior at viewport <=760px with an overlay drawer pattern. A hamburger menu button appears on mobile, tapping it slides the existing WebShellSidebar in as a fixed overlay with a semi-transparent backdrop. Selecting or creating a session auto-closes the drawer. Desktop layout (>=761px) is unaffected. Closes #6000 * fix(web-shell): address review feedback for mobile sidebar drawer - Use display:contents for desktop wrapper transparency (Critical: sidebar was hidden) - Fix z-index stacking so sidebar renders above backdrop in drawer - Force sidebar expand when mobile drawer is open (collapsed state) - Hide resizeHandle on mobile to prevent touch scroll conflicts - Reset drawer state on viewport resize via matchMedia listener - Add role=dialog, aria-modal, Escape key dismissal, body scroll lock - Add aria-expanded to hamburger button - Close drawer when opening Settings or resuming sessions * fix(web-shell): address second round of review feedback - Remove dead :global(.sidebar) selector (CSS Modules hash class names) - Fix Escape key capture-phase handler to not intercept sidebar inputs - Conditionally apply role=dialog/aria-modal only when drawer is open - Stop toggling collapsed prop on drawer open/close to preserve sidebar state - Add closeMobileDrawer() for bare /resume command path - Fix hamburger button vertical centering in empty chat state on mobile * fix(web-shell): fix stacking context and escape handler in mobile drawer * fix(web-shell): prevent iOS Safari background scroll when drawer is open * chore: remove accidentally committed .qwen-session and gitignore it The .qwen-session file is a developer-local session UUID generated by qwen serve. It was accidentally committed to the repo and should never be tracked. * fix(web-shell): address review feedback for mobile drawer - Don't preventDefault touchmove inside the drawer so the session list can scroll natively; only block scrolling on the page behind it. - Defer Escape to a pending tool/permission approval (reject) instead of closing the drawer when a prompt is visible. - Reuse isEditableTarget from utils/dom and only bail out for editable targets outside the drawer, so the drawer search input still closes on the first Escape. - Close the drawer before awaiting loadSession so it doesn't linger over the old transcript, matching the other session-switch paths. - Keep the drawer panel visible until the backdrop finishes fading out to avoid a one-frame flicker on close. Co-Authored-By: Qwen-Coder <[email protected]> * fix(web-shell): mobile drawer ignores collapsed rail + block backdrop scroll - collapsed: a user who collapsed the desktop sidebar got a mobile drawer that still rendered as the icon rail (no session list — the whole point of the drawer). Force the expanded layout while the drawer is open. - touchmove: the allowlist matched the outer [data-mobile-drawer] wrapper, which also contains the full-screen backdrop, so a touchmove starting on the dim backdrop skipped preventDefault and let iOS Safari scroll the page behind. Exclude the backdrop so only the panel keeps native scroll. Co-Authored-By: Qwen-Coder <[email protected]> * fix(web-shell): harden mobile drawer collapse, error path, and width cap - Hide the sidebar collapse button while the mobile drawer is open so its no-op toggle can no longer silently persist desktop collapsed state. - Close the drawer before awaiting createSession() so a failed create no longer leaves the drawer stuck open with page scroll locked. - Drop redundant width/min-width/position from .sidebar.mobileOpen and cap it with max-width:100vw so a wide persisted width can't overflow phones. Co-Authored-By: Qwen-Coder <[email protected]> --------- Co-authored-by: pomelo-nwu <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
5581424b6b |
feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777)
* feat(chrome-qwen-bridge): 🔥 init chrome qwen code bridge * chore(chrome-qwen-bridge): connect * chore(chrome-qwen-bridge): connect & them * chore(chrome-qwen-bridge): connect & them * chore(chrome-qwen-bridge): wip use chat ui * chore(chrome-qwen-bridge): wip use chat ui * wip * refactor(chrome-extension): rename chrome-qwen-bridge package to chrome-extension * feat(chrome-extension): enhance network monitoring with webRequest API Replace the existing network monitoring implementation with a more comprehensive solution that combines both webRequest and debugger APIs for broader coverage. The new implementation: - Uses chrome.webRequest.onBeforeRequest to capture all outgoing requests - Uses chrome.webRequest.onCompleted to capture completed responses - Uses chrome.webRequest.onErrorOccurred to capture failed requests - Retains debugger API integration for detailed network information - Implements memory management with maximum 1000 logs per tab - Adds proper initialization and cleanup for each tab - Ensures graceful handling of debugger attachment failures - Provides more reliable network activity capture across all tabs This enhancement significantly improves the reliability and coverage of network monitoring functionality in the Chrome extension. * refactor(chrome-extension): reorganize directory structure for better maintainability This commit reorganizes the entire Chrome extension package structure for improved maintainability and clarity: - Move all source files to `src/` directory (background, content, sidepanel) - Move build configurations to `config/` directory - Move documentation to `docs/` directory with proper categorization - Move all script files to `scripts/` directory - Move native-host specific files to appropriate subdirectories (`src`, `scripts`, `config`) - Update package.json scripts to reflect new file locations - Add comprehensive documentation files (debugging, development, architecture, API reference) - Maintain all functionality while improving project organization The reorganization separates source code from build output, centralizes documentation, and creates a clear separation of concerns making the project more maintainable and easier for developers to navigate. * feat(chrome-extension): enhance native host communication and network logging - Add troubleshooting documentation for native host setup issues - Improve native host logging to home directory with fallback to tmp - Enhance network logging in service worker with response body capture - Update scripts to properly reference host.js from correct path - Increase timeout for MCP session creation and long prompts from 3 to 5 minutes - Add getConsoleLogs functionality to sidepanel for content script capture - Improve browser-mcp-server network logs aggregation by request ID - Update icon assets and improve manifest configuration refactor(chrome-extension): consolidate host.js entry point and improve path resolution - Create unified host.js entry point that delegates to src/host.js - Improve path resolution for host scripts in installer and runner scripts - Add proper path existence checks for browser-mcp-server.js - Support running from different directory structures style(chrome-extension): improve TypeScript type safety and error handling - Add proper type definitions for message handling in side panel - Add null checks and error handling for message parsing - Improve React component callback implementations * refactor(chrome-extension): redesign build workflow * fix(chrome-extension): resolve ESLint errors in native host, service worker, and content script - Fix 'Unexpected lexical declaration in case block' by wrapping switch cases in blocks - Fix 'Unexpected constant truthiness on the left-hand side of a || expression' by using conditional patterns - Fix unused variable errors by properly using catch error parameters or adding logging - Fix 'document' and 'window' not defined errors in service worker with proper global declarations - Add eslint-disable comments where appropriate for globals used in specific contexts * feat(chrome-extension): enhance native host with browser MCP tools and event streaming - Add new browser MCP tools: browser_click, browser_click_text, browser_run_js, browser_fill_form_auto - Implement SSE (Server-Sent Events) for improved event streaming instead of long-polling - Add daemon script for running the bridge host in background - Enhance documentation with MCP notes and updated README - Add multiple executable binaries to package.json: chrome-browser-mcp, qwen-bridge-host - Improve error handling and event processing in the native host - Add debouncing mechanism for stream end events in service worker - Update timeout for MCP discovery to accommodate slower startup Co-authored-by: Claude Opus 4.5 <[email protected]> * fix(chrome-extension): use trusted cwd for MCP tool discovery The root cause of MCP tools not being recognized by Qwen CLI was that the cwd (current working directory) was defaulting to '/' (root directory). In Qwen CLI's MCP discovery logic, there's a security check: if (!cliConfig.isTrustedFolder()) { return; // Skip MCP tool discovery } The root directory '/' is not a trusted folder, so MCP tools were silently not being discovered at all. Changes: - host.js: Default to $HOME instead of process.cwd() for start_qwen - service-worker.js: Remove '/' fallback, let host.js handle default This ensures browser MCP tools (browser_read_page, browser_click, etc.) are properly discovered and available to the model. * fix(core): validate MCP entry script existence before connection Add pre-flight check to verify that stdio-based MCP server entry scripts exist on disk before attempting connection. This prevents silent failures and provides clear error messages for misconfigured MCP servers. * fix(chrome-extension): enhance native host path resolution and port config - Add QWEN_BROWSER_MCP_SERVER_PATH env override for custom installations - Expand candidate search paths for browser-mcp-server.js discovery - Add detailed logging when MCP server script is not found - Support BRIDGE_PORT env variable for HTTP API server configuration * fix(chrome-extension): improve browser MCP server reliability and debugging - Add comprehensive debug logging for bridge health checks and host spawn - Support BROWSER_MCP_NO_SPAWN env to disable automatic host.js spawning - Handle bridge unavailability gracefully with clear error messages - Add raw JSON mode fallback for clients without Content-Length framing - Capture and log host.js stdout/stderr instead of inheriting stdio - Add pre-flight bridge check at startup for better diagnostics - Handle each bridge call failure with proper error responses * chore(chrome-extension): add debug wrapper script for MCP server Add cbmcp-wrapper.sh to help diagnose MCP server invocation issues. The wrapper logs invocation details and stderr to /tmp/cbmcp.log, making it easier to debug when Qwen CLI spawns the MCP server. * docs(chrome-extension): add MCP/Bridge troubleshooting guide Document common failure scenarios when MCP bridge shows as Disconnected: - EPERM errors when spawning host.js cannot bind to port - Content-Length framing issues during MCP handshake - Step-by-step troubleshooting with flow diagrams - Manual bridge setup with BROWSER_MCP_NO_SPAWN workaround * build(chrome-extension): 浏览器插件 mcp 构建优化 * docs(chrome-extension): update docs * fix(chrome-extension): 解决CDP响应体获取和权限请求处理问题 * feat(mcp-chrome-integration): add MCP Chrome browser extension integration Add complete Chrome extension with native messaging host for MCP integration: - Chrome extension with sidepanel UI, service worker, and content script - Native server with agent engines (Claude/Codex), session management, and tool bridge - Shared packages for types, tools, and node specifications - Documentation and build scripts Co-Authored-By: Claude Opus 4.5 <[email protected]> * chore(mcp-chrome-integration): refactor * feat(chrome-extension): 切换到HTTP后端代理并更新构建配置 * refactor(mcp-chrome-integration): build * wip * wip chrome extension * chore: ignore .worktrees * docs: plan sidepanel component removal * refactor(sidepanel): remove local components * feat(chrome-extension): add native messaging ACP client and protocol support - Add ACP client for native messaging communication - Add file handler for local file access via native host - Add protocol definitions for ACP communication - Archive old documentation files - Update integration status and protocol documentation * fix(chrome-extension): use GenericToolCall from @qwen-code/webui - Replace non-existent local ToolCallCard import with GenericToolCall - Import from @qwen-code/webui package instead of local path - Fix component props to match GenericToolCall interface - Remove unused ToolCallData import * refactor(mcp-chrome-integration): split large files and fix ESLint errors - Split tools.ts (1554 lines) into 8 schema files by functionality - Split native-messaging.ts (1533 lines) into 6 modules - Split doctor.ts (1099 lines) into 8 modules - Split content-script.ts (1055 lines) into 5 modules - Add Qwen Team license headers to all new files - Remove unused tool names (SEARCH_TABS_CONTENT, SEND_COMMAND_TO_INJECT_SCRIPT, USERSCRIPT, RECORD_REPLAY) - Fix ESLint errors: no-require-imports, no-explicit-any, no-unused-vars, prefer-const - Add archive/ directory to eslint ignore patterns * 调试 MCP 工具成功 * refactor: revert unnecessary formatting changes and clean up MCP chrome-integration - Revert Node version requirement from >=22 to >=20 - Revert code formatting changes (import statements and indentation) - Archive obsolete chrome-extension implementation - Clean up outdated documentation and scripts - Reorganize MCP chrome-integration docs * docs(mcp-chrome): 新增核心文档和更新 README - 新增 02-features-and-architecture.md (27个工具完整参考) - 新增 03-design-and-implementation.md (与 hangwin/mcp-chrome 对比) - 新增 04-test-cases.md (35个测试用例) - 更新 01-installation-guide.md (Extension ID 固定方案) - 重写 README.md (对齐新文档结构) * refactor(chrome-extension): 简化 sidepanel 并移除未使用代码 - 删除未使用的 Onboarding 组件 - 删除冗余样式文件 (App.css, timeline.css) - 删除未使用的工具函数 (diffStats, diffUtils, sessionGrouping, tempFileManager, webviewUtils) - 新增 MCP 工具状态横幅显示 - 隐藏不需要的 UI 按钮 (slash command, attach, edit mode) - 移除未使用的变量 (clearToolCalls) - 更新 manifest.json 和 native-messaging-host * chore: regenerate lockfile for mcp-chrome-integration workspace npm install reconciles the lock with the merged package.json: adds the new packages/mcp-chrome-integration/app/* workspace subtree (371 deps). No existing main dependencies were removed. * chore(chrome-integration): drop dead logger + unused pino deps native-server/src/util/logger.ts was 100% commented-out dead code (even a hardcoded /Users/hang/... path) with zero importers; pino/pino-pretty were declared but never imported (logging is console.error to stderr, correct for a native-messaging host). Remove the file and both deps. * feat(chrome-integration): WIP serve-backed agent client (Phase 1 backbone) Replace the hand-rolled ACP client by driving `qwen serve` (main's maintained HTTP daemon) through the SDK's DaemonClient. New serve-agent/client.ts spawns `qwen serve --no-web` where the host used to spawn `qwen --acp`, then uses DaemonClient (REST + SSE) for session create / prompt / cancel / permission / streaming. Same public surface as AcpClient so the native host can swap in place. Compiles + typechecks against the real SDK API. NOT yet wired into native-messaging-host.ts — wiring is gated on two items that need runtime validation in a real Chrome + qwen env: - OAuth: host's onAuthenticateUpdate (in-extension authUri) maps to serve's server-side device-flow events — needs design. - permission requestId becomes string (was number) — host's permissionRequests map + handler need to follow. acp/ kept in place until the serve path is validated. Packaging follow-up: @qwen-code/sdk (→ core) as a dep of the standalone host is fine in the monorepo but needs a publish-time story (bundle or resolve from the co-installed qwen). * feat(browser-ext): daemon-direct connection foundation (Phase 1, #5626) First brick of the daemon-direct architecture from #5626: the extension talks straight to a local `qwen serve` HTTP daemon instead of a native messaging host. - daemon/config.ts: resolve { baseUrl, token } — default loopback http://127.0.0.1:4170 (auth-free), overridable via chrome.storage.local. - daemon/discovery.ts: GET /health probe so the side panel can show a "start qwen serve" hint instead of a broken chat when no daemon is up. Both typecheck clean. (Pre-existing tsc errors live only in the orphaned legacy sidepanel hooks — useWebViewMessages/useToolCalls etc. — which the DaemonSessionProvider migration removes next.) * docs(browser-ext): daemon-direct architecture spec (Phase 1+2, #5626) Concrete implementation spec: Phase 1 (side panel as daemon client, no daemon changes) and Phase 2 (browser tools as a client-hosted MCP server over the daemon WS). Phase 2 reuses the existing SdkControlClientTransport / SdkControlServerTransport pattern but moves the wire from the SDK subprocess control plane onto qwen serve's WebSocket — a new public daemon-contract surface, gated behind a capability flag (the open question in #5626). Includes the daemon-lifecycle options for #5626 Q3. * feat(browser-ext): Phase 1 — side panel as a daemon-direct client (#5626) Side panel chat now talks straight to a local `qwen serve` HTTP daemon via @qwen-code/webui's DaemonSessionProvider, replacing the native-messaging relay (background/ + content/ untouched; nativeMessaging perm kept for now). - SidePanelRoot.tsx: health gate — checkDaemonHealth(getDaemonConfig()); shows a "run qwen serve" hint + Retry when unreachable, else mounts DaemonSessionProvider around App. - App.tsx: rewritten daemon-driven — transcript/streaming/permissions/ lifecycle from the webui daemon hooks (useTranscriptBlocks, useStreamingState, usePromptStatus, usePendingPermissions, useConnection, useActions); reuses the existing webui presentational components + ChromePlatformProvider. - sidepanel/daemon/{transcriptItems,permission}.ts: adapters from daemon transcript/permission shapes to the existing UI components. - Deleted the dead legacy native-messaging chat hooks/types. Verified: sidepanel/daemon tsc clean; `npm run build` (esbuild) passes, emits the side-panel bundle with the daemon wiring. (Pre-existing tsc errors remain only in background/ + content/, out of scope until Phase 2.) Daemon contract accepted live against the worktree `qwen serve`: /health, /capabilities (advertises session_create/prompt/events/workspace_mcp), POST /session runs end-to-end to the model-auth gate. * feat(browser-ext): Phase 2 — browser tools over the daemon WS (#5626) Reverse tool channel: a WS client (the extension) hosts an MCP server (its browser tools) that the daemon's agent can call, carrying mcp_message JSON-RPC frames over the daemon WS — reusing the SdkControlClientTransport / SDK-MCP-server control-plane pattern. Gated behind capability flag `client_mcp_over_ws` (opt-in; the public-contract piece flagged in #5626). Daemon (core + cli/serve): - core/tools/client-mcp-registrar.ts: ClientMcpRegistrar — id-correlation, pending/timeout, notifications fire-and-forget, exposes the sendSdkMcpMessage(server,msg) callback McpClientManager consumes. - cli/serve/acp-http/client-mcp-ws.ts: ClientMcpWsConnection — handles mcp_register/mcp_message/mcp_unregister frames; pushes mcp_message down the WS; disposes on close. Hookup to the live agent McpClientManager is a ClientMcpServerProvider injection point (returns structured `not_wired` until the child↔parent reverse-IPC lands — see below). - capability `client_mcp_over_ws` threaded through serve options/capabilities. Extension (chrome-extension/background): - browser-tools-server.ts: minimal hand-rolled MCP JSON-RPC (initialize/tools/list/tools/call) reusing the existing tool-catalog + router + executors (MVP 6 read-first tools); WS client to the daemon /acp with mcp_register + reconnect. Wired into the service worker behind a health probe; native messaging left intact. Self-accepted (no LLM needed): 10/10 tests pass — a headless ws client registers + answers the MCP handshake over mcp_message and the daemon lists+CALLS the client-hosted chrome_read_page tool end-to-end over a real socket. Builds: core + cli + extension esbuild all green; tsc clean. NOT yet wired (out of scope here; needs acp-bridge + acpAgent reverse IPC): the parent-process WS ↔ ACP-child McpClientManager hookup — the daemon's WS lives in the parent serve process but sendSdkMcpMessage binds in the ACP child. Single injection at the mountAcpHttp call site once that IPC exists. * feat(serve): wire client-MCP-over-WS to the ACP child agent (#5626) Closes the Phase 2 gap: a client-hosted (extension) MCP server's tool calls now reach the agent's McpClientManager in the ACP CHILD, routed back to the parent's ClientMcpRegistrar and out over the daemon WS. Opt-in via QWEN_SERVE_CLIENT_MCP_OVER_WS=1 (the contract is still settling — dormant by default; this is the public-daemon-contract piece flagged in #5626). Contract additions: - ACP ext-method `qwen/control/client_mcp/message` (child→parent, called UP): {server,payload} → {payload}; notifications resolve with a synthetic ack. - runtime-MCP config flag `__clientMcpOverWs`: parent stamps it on the SDK-type add config; child KEEPS type:'sdk' (instead of stripping) so it binds an SdkControlClientTransport instead of the SDK subprocess control plane. - BridgeOptions.clientMcpSender seam + ServeAppDeps.clientMcpSenderRegistry. Round-trip: mcp_register(WS) → serve registers the connection's ClientMcpRegistrar.sendSdkMcpMessage in a process ClientMcpSenderRegistry + bridge.addRuntimeMcpServer(type:'sdk',__clientMcpOverWs) → child adds the SDK server + runs initialize/tools/list, each frame child→parent via client_mcp/message → BridgeClient looks up the sender → registrar pushes mcp_message down the WS → extension answers → child discovers the tools. Self-accepted LIVE (no LLM): integration-tests/cli/qwen-serve-client-mcp.test.ts spawns a REAL qwen serve + REAL qwen --acp child under a mock OpenAI server, a headless ws client registers + answers the MCP handshake, and the child discovers the client-hosted chrome_read_page tool over the genuine child→parent→WS channel (GET /workspace/mcp/chrome-tools/tools lists it). Verified passing locally (24.6s). +5 bridge round-trip tests; 324 acp-bridge, the 10 prior Phase-2, acpAgent 133, server+acp-http 675 all pass; builds clean. Not exercised here: a real LLM turn driving tools/call (needs creds), and a real Chrome extension as the WS client (headless ws stands in). * fix(serve): session-scope runtime MCP servers so client tool calls resolve (#5626) The reverse tool channel registered the client-hosted MCP server only on the bootstrap/workspace Config, so discovery worked but a prompt — which runs against an independent per-session Config from newSessionConfig→loadCliConfig — couldn't resolve the tool ("not found in registry"), and the reverse WS channel was never reached. Spec (docs/05) intends per-session scope. Fix (minimal, additive, guarded; normal settings-based MCP servers unaffected): - core/config.ts: add Config.getRuntimeMcpServers() (shallow copy of the private runtimeMcpServers map). - acpAgent.ts newSessionConfig: copy the bootstrap Config's runtime MCP servers into a newly-created session Config before initialize() so its discovery binds that session's sendSdkMcpMessage (register-before-session). - acpAgent.ts workspaceMcpRuntimeAdd/Remove: fan the add/remove out to every active session's McpClientManager (register-after-session), best-effort. Test: the fake model now emits the fully-qualified registered name mcp__chrome-tools__chrome_read_page (what a real model is handed); the reverse channel still forwards the bare tool name to the client server. Verified LIVE (no LLM, no creds): integration test now drives the FULL loop — model→agent→session-registry resolution→reverse client_mcp/message over the daemon WS→headless ws client returns CallToolResult→agent consumes it (tool completed)→turn_complete. 2/2 integration tests pass (confirmed locally). Regression: config 248, acpAgent 133, mcp-client-manager 101, client-mcp-ws 5 pass; builds clean. (server.test.ts has 2 pre-existing Web-Shell flakes, identical on the unmodified baseline.) Still needs a real Chrome extension (vs the headless ws stand-in) + a real model turn for true browser behavior; the protocol round-trip is proven. * chore(browser-ext): remove the dead Native Messaging stack (#5626) Daemon-direct made Native Messaging obsolete — the extension talks to `qwen serve` directly (chat over HTTP+SSE, browser tools over the daemon WS), so the entire native-host stack is dead weight. Deletes ~15.5k lines: - packages/mcp-chrome-integration/app/native-server/ — the whole native host (MCP servers, ACP client, Fastify server, doctor/register/report/postinstall, the superseded serve-agent backbone). - extension background transport: native-messaging.ts, native-connection.ts, native-message-handler.ts, native-messaging-types.ts, ui-request-router.ts (+ its test). The still-used executor types (BrowserToolArgs / RawNetworkRequest / WebSocketSession / NetworkCaptureState) move to background/browser-tool-types.ts. - service-worker rewired to daemon-direct only (drops the NativeMessaging init + the onMessage→routeUiRequest relay; keeps the browser-tools server start). - esbuild.background.config.js drops the deleted native-messaging entry point. - manifest.json drops the `nativeMessaging` permission. - package.json scripts drop every native-server/native-host reference; dev-watch no longer spawns the native host. - obsolete native-messaging docs (01-04) + scripts (diagnose/install/update) removed; README rewritten for daemon-direct. Extension esbuild build green; tsc errors dropped (84 -> 69, all pre-existing node:test/content typings). Daemon-side (cli/serve/core) untouched. * chore(browser-ext): drop orphaned content-fetch-patch.ts (#5626) * fix(serve): let browser extensions open the daemon WS reverse channel The daemon-direct Chrome extension (#5626) connects to qwen serve's /acp WebSocket to register its browser tools as a client-hosted MCP server. Three gaps blocked the real-browser path. A node WS client in the integration tests carries no browser Origin and completes the ACP handshake, so none of these surfaced until a real Chrome connected: - The WS CSRF check hard-coded loopback origins, so the extension's chrome-extension://<id> Origin was rejected with 403. Wire the existing --allow-origin allowlist into the WS upgrade check (acp-http/index.ts, server.ts) with the same match semantics as the REST allowOriginCors. - parseAllowOriginPatterns rejected chrome-extension:// because its URL.origin is the opaque "null". Rebuild the canonical origin from scheme+host for opaque-origin schemes (auth.ts), with tests. - The extension skipped the ACP initialize handshake and sent mcp_register directly, tripping the daemon's 30s initialize timeout. Send ACP initialize first and register only after the ack (browser-tools-server.ts). Also allow console.* in the extension package (no stdio in the MV3 runtime) via the eslint no-console allowlist. Verified end-to-end against a real Chrome: the daemon agent calls mcp__chrome-tools__chrome_read_page and reads the live active tab. * docs(browser-ext): Plan C (CDP tunnel) feasibility + implementation design Assess routing chrome-devtools-mcp's ready-made DevTools toolset through the extension's chrome.debugger to drive the user's real browser, instead of re-implementing each tool in the extension (Plan A). Records, with source verification against [email protected] + [email protected]: - the createCDPSession wall (why zero-change reuse fails — Target.attachToTarget is "Not allowed" for chrome.debugger; cdp-mcp throws in McpContext.from); - the patch-package fork shape (pin 1.4.0 + a ~2-site patch, not vendor/submodule); - the daemon /cdp browser-level CDP emulation + sessionId routing design; - minimal browser-level command set, reusable prior art (playwright-mcp --extension), phased steps, risks, and the Plan A fallback. Refs #5626. * feat(serve): add CDP browser-level emulator for Plan C tunnel (#5626) First component of the Plan C "CDP tunnel": a synthesis layer that fakes the browser-level CDP topology so an external puppeteer client (chrome-devtools-mcp) can connect over a future /cdp endpoint while page-domain commands are forwarded to the one real tab via the extension's chrome.debugger. Implements the exact contract a Phase 0 spike proved necessary: a tab->page two-level target tree + recursive Target.setAutoAttach (browser attaches the tab session; the tab session attaches the page session), with page-session commands routed to forwardToTab and tab events re-tagged with the page session id. The spike connected real puppeteer to a pure synthesis layer and ran page.evaluate(() => 1 + 1) === 2. 7 unit tests cover the handshake + routing. Refs #5626. * feat(serve): add CDP tunnel reverse-link, /cdp glue, and bridge registry (#5626) Plan C Phase 1 daemon core. The reverse-link forwards page-domain CDP commands to the extension over cdp_command/cdp_result frames (id-correlated, timeout) and re-tags cdp_event onto the page session; cdp-ws wires a per- puppeteer-connection emulator to the reverse-link bound to the single active extension bridge in a process-scoped registry. The emulator gains setTabInfo so the synthetic targetInfo reflects the real tab after cdp_attach. * feat(serve): wire /cdp upgrade branch and cdpTunnelOverWs flag (#5626) Adds the /cdp WebSocket upgrade branch to acp-http (reusing the loopback / host-allowlist / auth / CSRF checks) and routes inbound cdp_* frames on the extension's /acp socket to the bound reverse-link. The extension connection registers as the active CDP bridge eagerly at ACP initialize so a /cdp puppeteer client can bind immediately (avoids the attach chicken-and-egg). Feature flag cdpTunnelOverWs is wired exactly like clientMcpOverWs (env QWEN_SERVE_CDP_TUNNEL_OVER_WS=1, capability cdp_tunnel_over_ws), DEFAULT OFF — existing behaviour is unchanged when off. * feat(browser-ext): add CDP bridge over the reverse /acp socket (#5626) The extension answers cdp_attach by attaching chrome.debugger to the active tab and cdp_command via chrome.debugger.sendCommand, replying cdp_result; chrome.debugger.onEvent -> cdp_event, onDetach -> cdp_detach. Reuses the existing browser-tools-server /acp socket (routes cdp_* frames; tears the bridge down on socket close) and mutually excludes with the chrome_network_debugger_* tools (one debugger per tab). * build(deps): pin chrome-devtools-mcp 1.4.0 + puppeteer-core 25.2.0, patch McpContext (#5626) Pins the two CDP-tunnel client deps (exact, to keep the version-specific patch and puppeteer's hardcoded ExtensionTransport topology stable) and adds patches/chrome-devtools-mcp+1.4.0.patch. The patch wraps McpContext.#init's devtoolsUniverseManager.init / serviceWorkerConsoleCollector.init in try/catch so the createCDPSession wall (Target.attachToTarget -> -32000 over chrome.debugger) no longer crashes the server on startup; only performance_* / service-worker console degrade. Applied via the existing postinstall patch-package hook (same form as patches/ink+7.0.3.patch). * test(serve): add Plan C /cdp end-to-end acceptance harness (#5626) Node script that starts the real daemon with the flag on, connects a mock extension over /acp (ACP initialize + mcp_register, answering cdp_command with page-domain CDP), then puppeteer.connect to /cdp and asserts page.evaluate(() => 1 + 1) === 2 through the real daemon + emulator + reverse-link. Allowlists the harness dir in eslint's node-script globals. * fix(serve): gate CDP page commands behind attach completion (#5626) Real-Chrome testing surfaced an ordering race the mock acceptance missed: the extension's chrome.debugger.attach is async (it pops the debugger banner), but forwardToTab forwarded page-domain commands immediately, so a fast puppeteer Network.enable reached the extension before attachedTabId was set and failed "CDP tunnel not attached to a tab". The mock extension acked attach synchronously, which hid the race. Add an attach gate in CdpReverseLink: forwardToTab awaits the in-flight cdp_attach to settle (success or failure) before forwarding. Verified end-to-end against REAL Chrome — puppeteer read the live active tab (a GitHub PR page) through the tunnel: pages=1, real url/title/body returned. Refs #5626. * refactor(chrome-extension): delete Plan A side panel, content scripts, and reverse tool channel Tears out the superseded Plan A surface so the extension can become a pure CDP-tunnel pipe (chat moves to the daemon web UI, browser tooling runs as chrome-devtools-mcp over the /cdp tunnel): - src/sidepanel/ (side-panel chat UI) - src/content/ (content scripts; the CDP tunnel drives DOM/Input via chrome.debugger, no injection needed) - background reverse tool channel: browser-tools-server, browser-network-tools, network-capture-utils, browser-tool-executors, tool-catalog, tool-router, mcp-tool-result, browser-tool-types, and their tests - public/sidepanel/sidepanel.html static asset Refs #5626 * refactor(chrome-extension): rewrite service worker as minimal daemon CDP client The service worker is now the entire extension logic: probe the daemon /health, open the /acp WebSocket, send the ACP initialize handshake (the daemon closes the socket on a 30s init timeout otherwise and binds this connection as the CDP bridge at that point), then route cdp_* frames into the CDP bridge with capped backoff reconnect. No more reverse MCP tool server (chrome-tools is gone). cdp-bridge: drop the browser-network-tools import and the network-capture mutual-exclusion branch in handleAttach (the network tools are deleted, nothing to exclude); remove the now-unused isCdpTunnelAttached export. Refs #5626 * build(chrome-extension): trim manifest, build config, and deps to the CDP pipe manifest: drop content_scripts, side_panel, and the sidePanel/webRequest/cookies/ scripting/webNavigation permissions; keep only debugger/tabs/activeTab/storage plus background, key, host_permissions, icons, and action. build: background esbuild now has a single service-worker entry point (content script gone); delete the UI esbuild/postcss/tailwind configs and drop the UI build step + build:ui scripts; sync-extension no longer special-cases the gone sidepanel assets; dev-watch no longer spawns the UI watcher. deps: remove the side-panel-only deps (@qwen-code/webui, react, react-dom, markdown-it, and the @types + the postcss/tailwind/autoprefixer CSS toolchain). Refs #5626 * chore(chrome-extension): drop dead externally_connectable hook (#5626) * test(serve): add real-Chrome /cdp local verification script (#5626) * test(serve): add cdp-mcp-over-tunnel layer-C smoke check * fix(extension): keep the CDP tunnel alive with chrome.alarms MV3 service workers idle out after ~30s, so the tunnel silently dropped whenever no puppeteer client was driving it and the user had to keep the Service Worker DevTools open to hold the worker awake. Register a 30s chrome.alarms keepalive: the recurring onAlarm dispatch holds the idle timer off, and each wake of a terminated worker re-runs the top level to reconnect. * feat(serve): auto-register chrome-devtools-mcp over the CDP tunnel Plan C (#5626) last mile: when `qwen serve` runs with the CDP-tunnel flag, the agent should be able to drive the user's real browser. Rather than hand-writing browser tools, auto-register the (patched) chrome-devtools-mcp as a session MCP server pointed at this daemon's /cdp endpoint, so its 29 ready-made DevTools tools flow through the tunnel. - run-qwen-serve: forward QWEN_SERVE_CDP_TUNNEL_OVER_WS + _PORT into the spawned ACP child via childEnvOverrides (same path as the MCP budget env). - acpAgent: buildCdpTunnelMcpServer() injects the server into the top-precedence sessionMcpServers tier when the flag + port are present and the package resolves; trust left unset so tools default to 'ask' (no silent auto-approval of browser control); best-effort skip otherwise. No settings.json edit and no hand-written tools required. * fix(serve): gate CDP bridge registration to the extension (#5626) Auto-registering every /acp initialize as the CDP bridge was last-writer- wins. Once an ACP agent connects over the same /acp endpoint (web UI, Zed), it would capture the bridge and receive cdp_* frames it can't answer, stealing the tunnel from the extension. Gate registration on clientInfo.name === 'qwen-cdp-bridge'; the extension (and the acceptance mock) now identify themselves that way, while agent clients are left alone. * feat(extension): open the web UI when the toolbar icon is clicked The extension has no UI of its own (pure CDP-tunnel pipe; chat lives in the daemon web UI), so clicking the toolbar icon did nothing after the side panel was removed. Wire action.onClicked to open the daemon baseUrl in a new tab so the icon is a useful entry point instead of a dead click. * feat: host the web UI in a Chrome side panel (#5626) The extension is a pure CDP-tunnel pipe with no UI of its own, so after the side panel chat was removed the toolbar icon did nothing. Bring the side panel back as a thin host that iframes the daemon web UI (chat + tools), so the sidebar is the everyday entry point and reuses the web UI's pages/components instead of shipping a second UI in the extension. - extension: side_panel + sidePanel permission; sidepanel.html/js frames the daemon baseUrl; toolbar icon opens the panel (openPanelOnActionClick). - daemon: the Web Shell sent frame-ancestors 'none' + X-Frame-Options: DENY, which blocked the iframe. Allow framing only for chrome-extension origins explicitly passed via --allow-origin; everything else still gets DENY. * fix: prefer chrome-devtools over computer-use under the CDP tunnel + keep MV3 worker alive during attach (#5626) Two issues surfaced driving the real agent: 1. The agent picked the OS-level computer-use tool (cua-driver) for browser tasks instead of the injected chrome-devtools-mcp — heavyweight screenshot/ click loop that pegged a CPU and stalled turns. Disable computerUse when the CDP tunnel flag is on so browser automation goes through the tunnel. 2. The extension's MV3 service worker idled out *between* CDP commands (the agent pauses to think), detaching chrome.debugger and hanging the next command. Add a sub-30s keepalive while attached; the 30s alarm only covered idle reconnects, not in-flight attachments. * chore(extension): stop tracking .extension-key.pem (signing private key) The extension signing private key was committed and pushed — anyone with repo access could impersonate the extension. Stop tracking it and gitignore *.pem. Load-unpacked debugging needs only the public "key" in manifest.json, so this doesn't affect dev on any machine; the .pem is kept locally for packaging. * refactor(chrome-extension): flatten to packages/chrome-extension Drop the mcp-chrome-integration wrapper + dead native-server (daemon-direct no longer uses native messaging). The extension is now a top-level workspace at packages/chrome-extension. Updated root workspaces, eslint globs, doc-path comments, and the two node scripts' global directives. cli + extension builds and eslint verified green. * test(serve): assert cdp_tunnel_over_ws in the capability registry The cdp_tunnel_over_ws capability was added to SERVE_CAPABILITY_REGISTRY + CONDITIONAL_SERVE_FEATURES but the test's EXPECTED_REGISTERED_FEATURES and the conditional drift-insurance branch weren't updated, failing 3 registry tests. Add it to the expected list (after client_mcp_over_ws, matching registry order) and add its assertion branch (predicate accepts/rejects the cdpTunnelOverWsEnabled toggle). * fix(serve): address review comments on CDP tunnel + client-MCP wiring (#5777) - guard post-async ws.send() with readyState OPEN (daemon crash on extension disconnect, sendClientMcpAck + cdp endpoint sends) - make client-mcp-sender-registry delete() ownership-aware (cross-connection server-name collision) - type the __clientMcpOverWs runtime config flag carrier - document the CDP tunnel trust model (loopback + bridge-gated dumb pipe) * chore(cdp-tunnel): set copyright year to 2026 on files created this year The Plan C / #5626 files were authored in 2026 but carried a 2025 header. service-worker.ts (created last year under #1432) keeps 2025. * feat(chrome-ext): add side-panel onboarding gate + fix packaging The side panel framed the daemon Web Shell unconditionally and showed a static "Connecting…" line. When no daemon was reachable, or the daemon wasn't started with --allow-origin (so frame-ancestors blocks the iframe), the user was stuck on "Connecting…" with no guidance, and discovery.ts's health check was never wired in. Wire a health/capabilities gate into the panel: - probe GET /health, then GET /capabilities - down → "Start qwen serve" + the exact command - up but no `allow_origin` feat → "Allow this extension" + the command - ready → frame the Web Shell The command is built from chrome.runtime.id at runtime, so it always names this extension's real origin (dev-unpacked or published) — no need to know or hardcode the id, and no publish-first chicken-and-egg. The pure decision helpers live in onboarding-logic.js. Also: - fix `npm run package` so manifest.json sits at the zip root (the old `zip ... extension/` nested it under extension/, which the Chrome Web Store rejects) - gitignore that packaging zip; add a package README Part of #5626. PR #5777. * docs(cdp-tunnel): trim over-long Plan C comments (ponytail) Comment-only: compress multi-paragraph design rationale to intent, keep the non-obvious why (trust model, bridge gate, attach ordering) + ponytail ceilings. ~-91 lines, no code touched, build + tests green. * fix(serve): address second-round review comments on the CDP tunnel (#5777) - close the bound /cdp puppeteer socket on extension disconnect (was hanging ~170s on CDP timeout) via an onExtensionGone hook - reject a 2nd concurrent /cdp client instead of silently clobbering routeInbound - narrow extension host_permissions from <all_urls> to localhost (chrome.debugger needs no host perm; only the /health fetch needs localhost) - log safeWsSend drops under serve debug mode so a dead tunnel is diagnosable * feat(chrome-ext): polish side-panel welcome into a terminal console Replace the bare welcome with a console that matches the product (a CLI daemon): the command is the hero, typed at a `$` prompt with a blinking cursor inside a titled terminal card. Warm-charcoal / electric-lime, light + dark aware (prefers-color-scheme), staggered load-in, a pulsing "listening" status, and a reduced-motion guard. No web fonts / no inline JS (the extension CSP allows neither). No behavior change: same /health + /capabilities gate and the chrome.runtime.id-derived command. Mechanics tidied alongside the markup: visibility toggles a .hidden class (CSS owns the flex layout), the copy button updates a label span, and the command is prefilled synchronously so first paint isn't an empty prompt. PR #5777. * feat(chrome-ext): click-to-copy command + centered copy button Make the onboarding command easier to grab: the whole command row is now click-to-copy (keyboard-reachable, Enter/Space), and a centered "Copy command" button sits at the foot of the terminal card. Both flash a check-mark "Copied" confirmation; the small top-bar button is gone. No gate-logic change. PR #5777. * test(serve): cover CDP-tunnel + client-MCP regression guards Add the four focused unit tests flagged in review for load-bearing reverse-channel paths that had no coverage: - ClientMcpSenderRegistry: ownership-scoped delete — a disconnecting connection must not remove an entry a peer re-registered under the same name. - CdpTunnelRegistry: register/supersede/unregister lifecycle, inbound routing delegation, onExtensionGone-on-disconnect, and the stale-unregister guard that must not evict a newer active bridge. - CdpReverseLink: a forwarded command rejects when its per-command timer expires (not just on bulk dispose). - safeWsSend: drops (no send, no throw) on a CLOSED/CLOSING socket. safeWsSend is extracted from acp-http/index.ts into its own module so it's unit-testable in isolation; behavior unchanged. PR #5777. * fix(serve): address bot code-review findings on the CDP tunnel (#5777) - cdp-bridge: tear down listeners before re-attach (was double-registering → duplicate cdp_event frames corrupting puppeteer) - emulator: return a CDP error for an unknown session instead of fake success - registry: notify the superseded bridge so the old /cdp closes (single-puppeteer) - deps: move chrome-devtools-mcp + puppeteer-core to optionalDependencies (~26MB) - tests: entry-script validation, deliverClientMcpMessage error branches, registry supersede * fix(chrome-ext): revert side panel to welcome when the daemon stops Once the panel framed the Web Shell it stopped probing, so if the daemon later went away the iframe was left showing Chrome's localhost connection-refused page with no way back. Keep probing after framing and, after a short tolerance (2 misses, ~5s, so a transient blip doesn't nuke a live chat), clear the iframe src and show the welcome screen again. PR #5777. * fix(chrome-ext,serve): address review comments on the CDP tunnel - service-worker: redact the bearer token from the connect log, and guard connect() against a still-CONNECTING socket so a rapid reconnect can't orphan an in-flight handshake. - acpAgent: don't clobber a user-configured `chrome-devtools` MCP server with the tunnel auto-wire. - cdp-reverse-link: log dropped/unexpected inbound frames via an optional diagnostic sink instead of swallowing them silently. - name the cross-package `qwen-cdp-bridge` client-name constant on both sides instead of repeating the bare string. PR #5777. * refactor(chrome-ext): inline onboarding helpers, drop dead pollTimer onboarding-logic.js was a 65-line file (mostly JSDoc) for three trivial helpers and a constant, split out "for testability" that was never used. Fold them into sidepanel.js (decideState collapses into probeState's return; resolveBaseUrl/allowOriginCommand become a one-liner each) and delete the file + its import. Also drop `pollTimer`: the welcome-fallback change made it write-only (the only clearInterval was removed), which trips no-unused-vars. PR #5777. * fix(serve,chrome-ext): more CDP-tunnel review fixes - sidepanel: pass the bearer token through the Web Shell URL fragment so a token-gated daemon doesn't 401 every framed request. - server: throw if deps.bridge is injected without deps.clientMcpSenderRegistry (the bridge is already wired to its own sender; a fresh one would be orphaned). - cdp-browser-emulator: surface unhandled browser-level CDP commands via an optional log sink (keep the empty-result ack, with a TODO). - cdp-bridge: only treat "already attached" as ours when attachedTabId === tabId (a foreign DevTools owner now errors), and detach the previous tab on switch so Chrome drops its debug banner. - build.js: add packages/chrome-extension to the build order so root build exercises the extension bundle. PR #5777. * fix(serve,chrome-ext): harden CDP-tunnel + client-MCP reverse channel - client-mcp-ws: cap registered servers per connection (max 10) and re-check `disposed` after the provider round-trip so a WS close mid-register can't leave a zombie server; add registrar serverCount(). - acp-http: rate-limit client-MCP frames (mcp_register/unregister at the mutation tier, mcp_message at read) and cap concurrent fire-and-forget register/unregister dispatch (max 8) to stop DoS amplification. - cdp tunnel: on /cdp puppeteer disconnect send a `cdp_release` frame so the extension detaches chrome.debugger instead of leaving the tab's debug banner up until /acp dies. - acpAgent: skip chrome-devtools auto-registration (with a diagnostic) when the /cdp tunnel requires bearer auth — the ACP child can't authenticate to it. PR #5777. * fix(chrome-ext,serve): address second-round CDP-tunnel review - service-worker: only the *active* socket's close tears down the bridge — a stale daemon-forced close must not detach the new connection's debugger. - daemon config + sidepanel: fail closed on a non-loopback baseUrl so a tampered chrome.storage value can't exfiltrate the bearer token off-host (background fetch/WS bypass host_permissions). - sidepanel: reentrancy guard on tick() so overlapping slow probes don't burn the framed-miss tolerance and flash the welcome screen mid-chat. - cdp-bridge: reentrancy guard on handleAttach so overlapping cdp_attach frames can't interleave teardown and corrupt attachedTabId. - add cdp-ws.test.ts: regression cover for no-bridge reject, second-client reject, onExtensionGone fail-fast, cdp_release on dispose, and the superseded-bridge cleanup guard. PR #5777. * fix(serve,webui): address third-round CDP-tunnel review - safe-ws-send: wrap the debug-mode writeStderrLine in try/catch so a broken stderr (EPIPE on a piped/closed log) can't break the "never throw on a dead socket" contract production callers rely on. - ChromeToolCall: index-access rawInput['name'] to satisfy noPropertyAccessFromIndexSignature. PR #5777. * fix(serve,chrome-ext): address #5777 review round 4 - acp-http: send mcp_error back on an unexpected client-MCP handler rejection (register/unregister callers otherwise hang); log when the inflight cap rejects. - cdp-ws: log the happy-path cdp_release dispatch for oncall tracing. - run-qwen-serve/acpAgent: don't pass a bogus "0" CDP-tunnel port for ephemeral --port 0, and emit a stderr diagnostic when the tunnel is disabled for a missing/invalid port instead of failing silently. - cdp-bridge: handle a cdp_release that races an in-flight handleAttach so a late attach can't leave a debugger attachment with no live /cdp client. - service-worker: close the WS on an ACP initialize error so the daemon doesn't keep holding a non-functional CDP bridge. * fix(serve,chrome-ext): address pr-review findings (#5777) - client-mcp-sender-registry: gate the child-side runtime-server teardown on ownership too (Config.removeRuntimeMcpServer is not owner-scoped), so a disconnecting connection can't kill a server a later connection re-registered under the same name. (P2) - service-worker: carry the bearer token via the `qwen-bearer.*` WS subprotocol (matching the web-shell + daemon decoder) instead of a `?token=` query the daemon never reads, so a token-gated daemon no longer 401-reconnect-loops. (P3) - run-qwen-serve: advertise client_mcp_over_ws / cdp_tunnel_over_ws in the bootstrap /capabilities too, matching the runtime path. (P3) * chore(webui): remove unused ChromeToolCall component (#5777) ChromeToolCall was added in a debugging commit but is never wired into the tool-call routing (getToolCallComponent) — there's no `chrome` tool kind and the only references were barrel re-exports. Dead code; remove it. * fix(serve,chrome-ext): address #5777 review round 5 (diagnostics) - service-worker: log the WS close code/reason so failure modes aren't indistinguishable (e.g. the daemon's 1011 "no extension connected"). - acpAgent: containment-check the resolved chrome-devtools-mcp bin path so a malformed `bin` field can't escape the package dir. - cdp-tunnel-registry: log when a new extension bridge supersedes a stale one. * fix(chrome-ext,serve): address #5777 review round 6 - cdp-bridge: ack the attach (as an error) before tearing down on a release-during-attach, so the daemon's reverse link doesn't hang ~170s waiting for a cdp_attached that never arrives. - acp-http: rename isFireAndForget -> dispatchOffQueue + clarify the comment; register/unregister are dispatched off-queue but still expect a response ack, so the name no longer reads as "no response." * fix(serve,cli): address #5777 review round 7 - config: warn (stderr) when QWEN_SERVE_CDP_TUNNEL_OVER_WS overrides an explicit tools.computerUse.enabled=true, so the effective config isn't a silent surprise. - client-mcp-ws: document the intentional idempotency of handleUnregister. - cdp-reverse-link: add tests for the attach gate — forwardToTab parks behind an in-flight attach, the cdp_attach timer rejects, and the gate opens on timeout so commands don't hang. * fix(serve,core): address #5777 review round 8 - mcp-client: only treat the first stdio arg as a local entry script when it is clearly a filesystem path. A bare includes('/') also matched scoped npm package names (npx @scope/pkg), wrongly resolving them under the workspace and throwing before the runner ran. Adds a regression test. - client-mcp-sender-registry: reject shadowedSettings so a browser-hosted WS client cannot shadow a user-configured MCP server name; roll back the child-side add. - cdp-ws: close the puppeteer socket when /cdp attach fails so dispose() clears cdpBound/routeInbound, instead of a stuck tunnel until restart. * fix(serve,chrome-ext): address #5777 review follow-ups * fix(serve): satisfy CDP inbound frame guard typing * fix(serve): address CDP tunnel review feedback --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
673454839e |
feat(memory): add a git-shared team memory tier (#5886)
* feat(memory): add a git-shared team memory tier Add an opt-in third auto-memory tier — TEAM — stored in the repository at `.qwen/team-memory/` and shared with collaborators through git. The private USER and PROJECT tiers are unchanged. - Enabled via the `memory.enableTeamMemory` setting (default off) or the `QWEN_CODE_MEMORY_TEAM` env override; inactive otherwise. - The model is taught a three-tier prompt and when to route saves to the shared tier (project-wide conventions, team-wide references). - Writes to the team directory are scanned for secrets (a curated gitleaks-style ruleset) and hard-blocked, since the directory is committed to the repo and visible to everyone with repo access. - Team writes default to 'ask' permission — not auto-allowed like the private tiers — so they are proposed for confirmation in the default approval mode and surface in the git diff for review. Co-authored-by: Qwen-Coder <[email protected]> * fix(vscode): update settings schema for team memory * fix(memory): harden team memory review gaps * fix(memory): address team-memory PR review feedback - secret-scanner: AWS key suffix is base62 ([A-Z0-9]), not base32 ([A-Z2-7]) — old class missed IDs with digits 0/1/8/9; add a positive test for such a key. - paths: document that isAnyAutoMemPath deliberately excludes team memory (security-load-bearing — team writes must stay 'ask'). - extractionAgentPlanner: name the team-memory dir in the scoped deny message so a denied team write is debuggable. - prompt: spell the tier count via a number-word lookup so a future 4th tier never silently reads "two". - team-memory-secret-guard: debug-log matched rule IDs only (never the secret content) when a write is blocked. - edit: when a blocked team write's secret already exists in the on-disk file, tell the user to delete the committed secret. - docs: caveat that a directory-form gitignore (.qwen/) makes the !-reinclude a no-op; use the file-glob form. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): harden team-memory secret guard on blocked writes and notebooks - write-file: blocked team-memory secret write now returns an `error` field (INVALID_TOOL_PARAMS) so the framework treats it as a failure instead of a silent success, mirroring edit.ts. - notebook-edit: scan the serialized .ipynb that hits disk through the team-memory secret guard before writing, blocking with the same `error` field; behavior unchanged for non-team-memory paths. - store: log non-ENOENT errors in readTeamAutoMemoryIndex before re-throwing, so EACCES/EIO/ELOOP no longer vanish via config.ts's best-effort `.catch(() => null)`. - config: emit a debug diagnostic when team memory is enabled but suppressed by the untrusted-workspace trust gate. Co-Authored-By: Qwen-Coder <[email protected]> * feat(memory): auto-generate the team memory index Generate the team `MEMORY.md` index by scanning the saved memory files instead of having the model hand-maintain it. The index is derived and ordered by path, so the committed file is deterministic across machines. This removes the git merge-conflict surface a hand-edited shared index would have, keeps the index consistent with the files, and frees the model from the two-step save for the team tier. Co-authored-by: Qwen-Coder <[email protected]> * feat(memory): opt-in git sync for team memory Add an opt-in, best-effort git sync of team memory at session start so a daemon can share team memory through the remote without manual git. Off by default; enabled with `QWEN_CODE_MEMORY_TEAM_SYNC=1`. It commits the team directory (only that path), pulls fast-forward-only, then pushes. `--ff-only` is deliberate: it never creates a merge commit or a conflict — a diverged branch is skipped rather than touching the working tree. All git is best-effort and never throws, so a failure cannot break a session. Commits can be attributed to the acting user via an optional author, for cooperative per-user attribution on a shared daemon. Co-authored-by: Qwen-Coder <[email protected]> * fix(core): close team-memory secret-guard regex + test gaps - secret-scanner: add `_` to the two T3BlbkFJ-marker char classes so legacy base64url OpenAI keys with underscores in the body no longer bypass the scanner; extend the OpenAI positive samples with an underscore key body. - edit: cover the pre-existing-secret branch — seed the on-disk file with a full detectable token, assert the distinct "already exists" message and that the blocked edit leaves the file untouched. - team-paths: cover the first-ever write before .qwen/team-memory exists, exercising realpathNearestExisting walking up to an existing ancestor. Co-Authored-By: Qwen-Coder <[email protected]> * feat(core): add Google OAuth client secret and SendGrid secret-scanner rules Extend the team-memory secret scanner with two more high-confidence, distinctive-prefix credential patterns (both gitleaks rules): - gcp-oauth-client-secret: GOCSPX- prefix + 24 base64url chars, placed next to the existing GCP AIza API-key rule. - sendgrid-api-token: SG. + 22-char id + . + 43-char secret. Both have a near-zero false-positive bar, matching the curated set's existing criteria. Add a load-bearing positive sample per rule. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): warn when team memory is enabled but not git-shareable When the team tier is active, emit a one-time startup warning if its directory is git-ignored (e.g. a directory-form `.qwen/` ignore that a `!`-reinclude cannot escape) or there is no git root — so the tier never silently shares nothing. Gated on the active (trusted + enabled) tier; probes a representative file path with `git check-ignore`, and is latched so refreshHierarchicalMemory re-runs do not re-emit it. Closes the last open review thread (wenshao + qwen-code-ci-bot both flagged the silent-inert case); the docs caveat already shipped. Co-Authored-By: Qwen-Coder <[email protected]> * feat(memory): dedup team index by description; fix empty-frontmatter parse The team index now collapses entries that share a (normalized) description into one line — listing the other files via "(also: …)" — so two people saving the same shared fact don't surface as duplicates. Nothing is dropped; every file path is preserved and the grouping is deterministic (input is path-sorted). Also fix a pre-existing frontmatter bug: parseFrontmatterValue used `\s*`, which crosses newlines, so an empty value (`description:`) captured the next frontmatter line. Bound it to horizontal whitespace. Co-Authored-By: Qwen-Coder <[email protected]> * feat(memory): add memory.enableTeamMemorySync setting Team-memory git sync was env-only (QWEN_CODE_MEMORY_TEAM_SYNC). Add a first-class `memory.enableTeamMemorySync` setting mirroring the enableTeamMemory plumbing (settingsSchema, cli/core config, acpAgent type/schema/default/keys/normalizer, desktop mirror), with the env var kept as a 0/1 override. Off by default. Docs updated. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): audit hardening — resilient scan, deterministic team index From a multi-round adversarial audit of the team-memory tier: - scan: a single unreadable memory file (EACCES, or a TOCTOU delete during `git pull`) no longer rejects the whole scan and wipe every memory from the index — per-file failures are caught and skipped (debug-logged). - indexer: order the team index by code unit, not `localeCompare()`, so the committed MEMORY.md is byte-identical across machines/locales and the ff-only sync can't wedge on index churn. - scan: escape the frontmatter key before building its RegExp (latent hardening). - config: surface a skipped/failed team-memory sync at debug level instead of silently swallowing it. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): deterministic team index cap + surface diverged sync Round-2 audit follow-ups: - scan: the >200-file cap selected its subset by mtime + locale basename, which differs per machine/locale — so a large team's committed index churned and wedged the ff-only sync. Team scans now cap by code-unit path (deterministic); private tiers keep mtime-recency (never shared, so it's fine). - sync: a diverged branch made `pull --ff-only` (and the push) fail with no `skippedReason` — a silent no-op for a user who opted into sync. Now reported as `pull-failed` / `push-failed` and logged. Docs note pull/push are whole-branch, not path-scoped. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): regenerate settings schema for enableTeamMemorySync Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): harden team-memory git sync (reconcile-first, scoped push, safe failure) The opt-in team-memory git sync could publish unrelated local commits, wedge a two-writer branch, leave team files staged on commit failure, and hang session start on a credential prompt. Harden all four maintainer-flagged criticals: - Reconcile first: run `git pull --ff-only` BEFORE committing so the local team commit lands on top of upstream instead of diverging; a diverged branch is skipped cleanly as `pull-failed` rather than committing then wedging `--ff-only`. - Scoped push: push only when THIS sync created the commit and the branch was not already ahead of `@{u}` (pre-sync), via an explicit single-branch refspec (`HEAD:<merge-ref>`) — never an unqualified `git push`. New `local-ahead` skip. - Safe failure: unstage the team path when the commit fails (hook/GPG/identity) so a user's next manual `git commit` cannot sweep team files in. - Non-interactive git: force `GIT_TERMINAL_PROMPT=0` + batch-mode SSH, SIGKILL on timeout, and a shorter timeout so sync cannot block session start on a prompt. config refresh: rebuild the team index BEFORE syncing (so a fresh MEMORY.md is committed/pushed, with a re-build after a pull that brought new files) and key the shareability latch by projectRoot so a newly entered repo is re-checked. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): harden team-memory index/scan/path security Address the security/correctness criticals from PR #5886 review. Team memory is committed to the repo and loaded into every collaborator's system prompt, so its write/scan/classification paths are trust boundaries. - indexer: pass noFollow on the team MEMORY.md write and reject a symlinked team root, so a committed `.qwen/team-memory` (or index) symlink can't redirect the generated index outside the repo. - indexer: sanitize attacker-controlled frontmatter title/description (strip control/zero-width/bidi chars, collapse newlines, defang code/link markdown, cap length) before embedding into MEMORY.md, to prevent prompt injection into the shared context. - indexer: skip the rewrite when the generated index is byte-identical, avoiding mtime churn and no-op commit cycles. - scan: normalize CRLF before parsing so a Windows checkout's team files don't silently drop out of the shared index. - paths: resolve a dangling final-component symlink via lstat/readlink in realpathNearestExisting, so a `decoy -> team-memory/leak.md` (missing target) is classified as team and the secret scanner can't be bypassed. - secret-scanner: bound both quantifiers in the openai-api-key T3BlbkFJ rule ({20,512}) to remove O(n^2) ReDoS backtracking; real keys still match. - team-memory-git-status: also probe a representative topic path so a repo that re-includes the index but ignores topic files is flagged as non-shareable. - notebook-edit: run the team-memory secret scan in validateToolParamValues too, for parity with edit/write-file. Adds load-bearing tests for each fix. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): sanitize team index paths and verify whole-path containment Refines the earlier team-memory security hardening with two fixes the first pass left open: - sanitizeIndexPath now defangs the attacker-controlled relativePath rendered into the committed MEMORY.md — at BOTH the `](path)` link target and the "(also: ...)" dedup suffix. A git filename may legally contain newlines and markdown delimiters, so a raw path could inject a second physical line (e.g. "- SYSTEM:") and reopen the prompt-injection structure the patch was closing for title/description. - rebuildTeamAutoMemoryIndex now realpath-resolves the whole team root and requires it to equal repoRoot/.qwen/team-memory. The prior lstat only inspected the leaf, so a symlinked PARENT component (e.g. `.qwen -> /tmp/out`) let scan/write escape the repo undetected while the guard believed it had rejected symlinks. Also adds a regression test pinning the security-load-bearing invariant that isAnyAutoMemPath excludes team paths (so team writes stay ask-gated). Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): keep team index link targets addressable, not just safe The earlier injection fix rewrote legal filename chars (`(` `)` `[` `]` and backticks) to `_` and used that destroyed value as the ACTUAL MEMORY.md link target. A real file `feedback/a(b).md` was emitted as `](feedback/a_b_.md)`, which does not exist (or, worse, collides with a different real file) — the link was injection-safe but no longer addressable. Replace `sanitizeIndexPath` with `encodeIndexPathTarget`, a reversible percent-encoder: every char outside an addressable allowlist (alphanumerics and `/ . - _ ~`) is percent-encoded, so breakout chars become inert ASCII (newline->%0A, `(`->%28, `)`->%29, space->%20, backtick->%60, ...). The target is a single line with no `](`/`)` breakout, yet `decodeURIComponent` recovers the exact original path so the link resolves to the real file. `/` is kept literal so the path still works as a relative reference. Applied at both index sites (the main `](path)` link and the `(also: ...)` suffix). The display label (title) stays non-reversibly sanitized as before — it is not addressable. Update the path tests to assert the emitted target percent-decodes back to the real relative path, and add a `feedback/a(b).md` case. The newline+`]`/`)` injection assertions remain (mutation-verified: they fail if the encoding is dropped). Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): gate team-memory sync on the index safety check rebuildTeamAutoMemoryIndex throws when the team root is a symlink that could redirect the committed index outside the repo, but the surrounding .catch swallowed the error and syncTeamMemory ran independently — so a refused symlink/escape still got git add/commit/push'd on the out-of-repo dir, defeating the indexer's refusal. Capture whether the rebuild succeeded and only sync when it did, so a failed safety check skips sync entirely. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): harden team-memory git sync and cover execute-time secret scan Several robustness fixes to the team-memory git sync, plus a missing test: - Respect a user's GIT_SSH_COMMAND: append our non-interactive batch guards (-oBatchMode=yes -oConnectTimeout=5) onto it instead of clobbering it, so a custom identity (-i), proxy jump (-J), or non-standard port is preserved. - Skip cleanly on a detached HEAD (new `detached-head` skippedReason) instead of creating an orphaned commit that can never be pushed. A branch with no upstream still commits locally as before. - Use SIGTERM (not SIGKILL) as the git kill signal so a timeout mid-commit/-pull lets git release index.lock and clean up; the non-interactive ssh guards already bound the network-hang case. - docs(memory): correct the sync description to match the implementation (rebuild index, ff-pull BEFORE commit, push only the scoped sync commit, skip on divergence/no-upstream). - test(notebook-edit): cover the execute-time team-memory secret scan (the full-notebook backstop), distinct from the existing validate-time test. Co-Authored-By: Qwen-Coder <[email protected]> * fix(memory): split security vs operational team-sync failures; per-op git kill signal Refine the recent team-memory sync changes per review: - indexer: throw a typed TeamMemoryRootSecurityError for the symlink/escape rejection so the sync gate can tell a SECURITY refusal apart from an OPERATIONAL IO failure. config now blocks sync ONLY on the security class; an operational rebuild failure (EACCES/ENOSPC/EPERM) is logged but self-corrects on the next rebuild and no longer permanently gates legitimate team sync. The security invariant (never add/commit/push an escaping root) is unchanged. - team-memory-sync: pass a per-op kill signal to tryGit — SIGKILL for the hang-prone network/ref ops (pull/push/rev-parse/symbolic-ref; no index.lock to corrupt), SIGTERM for mutating ops (add/commit) so git can release locks and clean up. Node's execFile timeout does not escalate to SIGKILL on its own, so a child trapping SIGTERM could hang past the timeout. - team-memory-sync: thread the resolved branch into resolvePushTarget so it no longer re-spawns `git symbolic-ref` after the detached-HEAD check. - docs: fix a broken sentence in the team-memory git-sync section. - tests: cover both rebuild failure classes (security -> sync skipped; operational -> sync still runs) and a positive gate (rebuild ok + sync enabled -> sync runs); assert the typed error on the indexer symlink tests. Co-Authored-By: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
44b80da0db |
feat(memory): confirm auto-generated skills before persisting (#5616)
* feat(memory): add memory.autoSkillConfirm setting schema * feat(memory): add Config.getAutoSkillConfirmEnabled() * feat(memory): wire memory.autoSkillConfirm through cli/acp/desktop settings * feat(memory): add pending-skills staging helpers * feat(memory): stage auto-skills for confirmation in runSkillReview * feat(memory): pass autoSkillConfirm flag from client to skill review * feat(memory): skill-review subscriptions + accept/reject pending APIs * feat(cli): add skill-review dialog state to UI context * feat(cli): add SkillReviewDialog component * feat(cli): render SkillReviewDialog from DialogManager * feat(cli): wire skill-review subscription and idle dialog routing * feat(cli): show pending auto-skill review hint in footer * feat(cli): add autoSkillConfirm toggle to /memory dialog * docs(memory): document memory.autoSkillConfirm setting * fix(cli): focus and Ctrl+C-close the skill-review dialog * fix(memory): address review on auto-skill confirmation - stage only newly-created skills, never agent-edited pre-existing ones, so Discard can't delete a skill the user already confirmed - re-read pendingSkills after the await in resolvePendingSkill so concurrent Keep-all/Discard-all removes every entry, not just the last - surface accept/reject fs failures (try/catch + log + .catch) instead of silently swallowing them - remount SkillReviewDialog per task via key so its snapshot never goes stale across consecutive skill-review batches - skip redundant skillReviewPending updates with a signature compare - remove the unreachable openSkillReviewDialog action - add debug logging to the pending-skills module - ignore .qwen/pending-skills/ explicitly in .gitignore * fix(memory): address round 2 review on auto-skill confirmation - acceptPendingSkill: when the staged dir is gone, no-op only if the skill is already in the skills root; otherwise throw so resolvePendingSkill keeps it pending and logs, preventing silent data loss - fall back to the agent's systemMessage for progress text when staging yields zero pending (a pre-existing-skill edit is still a durable change) - log the no-task / no-target early returns in resolvePendingSkill - replace internal tracker references in an AppContainer comment * fix(memory): harden auto-skill confirmation for multi-batch and edge cases - parseDescription: keep an empty description empty instead of spilling onto the next YAML line - namespace staged dirs under the task id so a later same-named batch can't clobber a still-deferred earlier one - track Esc-dismissed batches in a Set (not a single value) and only mark a batch dismissed on Esc, so a partially-failed Keep-all can reopen for the unresolved skills - document the in-place updateRecord invariant the accept/reject race fix relies on - add the missing license header to pending-skills.test.ts * fix(memory): strip quoted descriptions; Ctrl+C defers skill-review dialog - parseDescription: strip a matching pair of surrounding quotes so a `description: "..."` frontmatter value isn't rendered with literal quotes - useDialogClose: Ctrl+C on the skill-review dialog now calls dismissSkillReviewDialog (records the batch as dismissed) instead of plain close, matching Esc — otherwise the idle effect immediately reopened it --------- Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
d360dd276e |
feat(serve): voice dictation over the daemon for the Web Shell (#5755)
* feat(serve): voice dictation over the daemon for the Web Shell Bring voice input to the `qwen serve` Web Shell. The browser captures the microphone, streams raw 16 kHz mono PCM to a new `/voice/stream` WebSocket, and the daemon transcribes server-side by reusing the existing CLI voice pipeline (realtime streaming + on-stop batch) — provider credentials never reach the browser, and it works whether the daemon is local or remote. Daemon: - `/voice/stream` WebSocket handler (serve/voice) reusing openVoiceStream / openQwenAsrRealtimeStream / transcribeVoiceAudio; resolves the workspace voiceModel from a ModelsConfig built off settings. - Routed through the existing ACP upgrade listener so it shares the loopback / host-allowlist / CSRF / bearer checks; concurrency-capped. - Advertises the `voice_transcribe` capability (unconditional, like auth_device_flow; the WS errors when no voice model is configured). - Relax `Permissions-Policy` to `microphone=(self)` so the same-origin shell can request the mic — an empty `microphone=()` allowlist blocked the prompt entirely. - Allowlist `voiceModel` on `/workspace/settings` so the picker can read it. Web Shell: - Mic button in the composer: click to record, click to stop -> the transcript is inserted into the input for review before sending. - `/model --voice` picker (sourced from `/workspace/providers`, since voice models are hidden from the session model list) and `/model --voice <id>`, persisted via the prompt channel like `/model --fast`. The voiceModel resolver now accepts a structural model lookup so the daemon can resolve it without constructing a full CLI Config. Co-authored-by: Qwen-Coder <[email protected]> * fix(serve): harden web voice streaming * fix(serve): clean stale voice capture resources * fix(serve): harden voice websocket errors * fix(serve): hide voice capability when token auth is configured * fix(serve): address voice review blockers * fix(web-shell): keep voice cancel available when disabled * fix(web-shell): inline the voice mic in the composer toolbar The mic was an absolute-positioned overlay anchored to the composer wrapper, so after the #5775 chat-UI restructure it floated outside the input box (clipped at the bottom-right). Render it inside ChatEditor's `toolbarRight`, just before the send button, wiring the transcript insert to the composer core — it sits inline with the `/`, `@`, and send controls like the rest of the toolbar. Restyle to a Codex-style recording experience: an idle mic that matches the other toolbar icon buttons, and a recording pill with a live waveform (driven by the RMS meter), an elapsed `M:SS` timer, and a stop control. Also forward `/voice/stream` through the vite dev proxy (`ws: true`, scoped to the exact path so it doesn't shadow the client's own `client/voice/*` source modules) so the WebSocket reaches the daemon in dev instead of silently failing against vite. Co-authored-by: Qwen-Coder <[email protected]> * feat(serve): authenticate the voice WebSocket via a bearer subprotocol Voice was suppressed on any token-configured daemon because browsers cannot set an `Authorization` header on a WebSocket, so the bearer check on the shared ACP upgrade listener always failed for the browser mic. Let the browser carry the token in `Sec-WebSocket-Protocol` as `qwen-bearer.<base64url(token)>` (the only header a browser can set on a WS). The upgrade listener now accepts the token from either the `Authorization` header (non-browser clients, unchanged) or the subprotocol, hash-compared in constant time. `handleProtocols` selects a non-secret subprotocol (or none) so the token is never echoed back in the handshake response. This aligns the voice WS with how the ACP WS will authenticate browsers. With the WS now authenticated, drop the `!tokenConfigured` / `requireAuth` suppression of the `voice_transcribe` capability — it is advertised whenever the `/voice/stream` endpoint exists. Tests updated to assert voice is advertised under a token / `--require-auth`. Co-authored-by: Qwen-Coder <[email protected]> * fix(serve): make the voice WS subprotocol handshake complete + test it Review follow-up to the bearer-subprotocol auth. Fixes a real handshake bug and closes the test gap on the security-critical path. - Handshake bug: when the browser offered only the `qwen-bearer.*` subprotocol, the daemon's `handleProtocols` selected none, and a strict WS client (the `ws` library) aborts with "Server sent no subprotocol". The web-shell now offers a non-secret marker (`qwen-ws`) alongside the bearer one, so the daemon always selects the marker — never echoing the secret — and the handshake completes for strict and lenient clients alike. - Tests: add daemon-side coverage for the subprotocol auth path (accept valid token, reject wrong/malformed, no-token loopback) and assert the secret is never echoed back; add client coverage that the bearer token is offered as `[qwen-ws, qwen-bearer.<b64url>]` and round-trips. - Drop the unreachable try/catch around `Buffer.from(_, 'base64url')` (it never throws) and correct the comment. - a11y: mark the transcribing/interim regions as `role="status"` / `aria-live`, and add a `:focus-visible` ring to the idle mic. - Refresh the stale `useVoiceCapture` doc comment (token deployments are now supported in the browser) and fix the mock typing in VoiceButton.test.tsx. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): ignore stale voice socket callbacks * test(serve): sync voice capability integration expectation * fix(serve): harden voice websocket review gaps * fix(serve): sanitize voice websocket failures * fix(serve): address voice review suggestions * fix(serve): harden voice websocket lifecycle --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
b9c5e3566b |
feat(voice): voice dictation with native capture, streaming, and biasing (#5502)
* feat(voice): voice dictation with native capture, streaming, and biasing
Add voice dictation for the prompt input:
- /voice [hold|tap|off|status] command + general.voice.{enabled,mode,language,protocol} settings; push-to-talk via Space, /model --voice to pick the model
- Native microphone capture (@qwen-code/audio-capture, miniaudio N-API) with arecord/SoX fallback, silence auto-stop, cold-start warm-up, and macOS permission query
- Batch transcription via DashScope Qwen-ASR (OpenAI-compatible chat/completions + input_audio) with language + keyterm biasing and an echo guard
- Live streaming over the DashScope realtime WebSocket (fun-asr-realtime / paraformer-realtime-v2) with interim text and an input-level waveform, behind voice.protocol=dashscope-realtime
- Rich VoiceIndicator UI (state, level meter, live partial transcript)
- Cross-platform prebuilds via prebuildify + node-gyp-build and a CI matrix
Co-authored-by: Qwen-Coder <[email protected]>
* feat(cli): route voice ASR by model
* feat(cli): polish voice realtime parity
* ci: fix voice workflow checks
* fix(cli): address voice review blockers
* fix(cli): harden voice transcription failures
* fix(cli): address voice PR review blockers
* fix(cli): harden voice review follow-ups
* fix(voice): handle realtime review blockers
* fix(cli): add voice command i18n keys
* fix(cli): address voice review blockers
* fix(cli): address voice review follow-ups
* fix(voice): harden review edge cases
* fix(voice): address release and realtime review blockers
* fix(voice): address realtime suggestion followups
* fix(voice): handle realtime review followups
* fix(voice): address realtime review blockers
* fix(voice): address review feedback
* fix(voice): address recorder review feedback
* docs(voice): document ssrf guard boundary
* fix(voice): address review feedback
* fix(voice): preserve warm recorder session safety
* fix(voice): address stream review suggestions
* fix(voice): address multi-round review findings
Realtime/streaming:
- Salvage an already-committed transcript when the WebSocket closes right
after finish() instead of rejecting the whole dictation
(qwenAsrRealtimeSession, voiceStreamSession) + regression tests.
useVoiceInput state machine:
- Single-shot finalize guard so a tap-stop racing the silence auto-stop can't
double-stop the recorder and surface a spurious failure.
- Reset mountedRef on (re)mount so StrictMode (DEBUG) can't freeze the voice UI.
- Widen the hold-mode first-press release window above common key-repeat delays.
Model selection:
- Reject ids with no ASR transport at /model --voice and in the model dialog via
a new isSelectableVoiceModel; move resolveVoiceTransport into voiceModel so the
record-time config resolver stays transport-agnostic (+ tests).
macOS mic permission:
- Surface the not-determined state in voice warmup so the first dictation isn't
silently lost behind the TCC dialog.
Native packaging:
- Make the audio-capture native install non-fatal (falls back to SoX/arecord) so
a voice-only build failure can't break npm ci.
- Download audio-capture prebuilds before building standalone release archives.
Co-authored-by: Qwen-Coder <[email protected]>
* fix(voice): address review blockers
* fix(voice): update batch recording audio level
* fix(voice): tap-mode transcript loss, stream leaks, and dead keyterm cleanup
- Tap-mode dictation submitted a stale empty buffer.text, wiping the just-
inserted transcript and sending nothing: thread the resulting prompt text
through onSubmit(text) instead of reading buffer.text back synchronously
after the async insert (useVoiceInput, InputPrompt).
- Streaming finalize leaked the WebSocket session when recorder.drain() threw:
abort the session before propagating the error.
- voiceStreamSession: reject the connect promise when 'task-finished' arrives
before 'task-started' instead of hanging forever in 'transcribing'.
- Remove dead keyterm enrichment (project/branch/recent-file paths were
unreachable after the privacy fix) and the now-inert CJK echo guard, plus the
tests that asserted that removed behavior; fix the misleading "OpenAI prompt
field" comment.
- Add the missing 'Voice Model' and macOS mic-permission i18n keys to
en/zh/zh-TW (they were falling back to English; check-i18n doesn't flag keys
absent from en).
Co-authored-by: Qwen-Coder <[email protected]>
* fix(voice): reject partial stream transcripts
* fix(cli): let dialogs consume voice keys first
* fix(voice): reject incomplete qwen realtime transcripts
* fix(voice): handle stream review blockers
* fix(voice): salvage qwen realtime transcript on close
* fix(voice): sanitize streamed transcript text
* test(audio): run audio capture tests in CI
* fix(voice): address review blockers
* fix(voice): report stream close while recording
* fix(voice): reduce keyterm echo false positives
* fix(voice): handle realtime close diagnostics
* fix(voice): address realtime review blockers
* fix(lint): allow legacy voice filenames
* chore(cli): rename voice files to kebab-case
---------
Co-authored-by: Qwen-Coder <[email protected]>
|
||
|
|
0ba245ea3d |
feat(cli): add persistent history collapse on resume with refined commands (#4085)
* feat(cli): add --quiet-restore flag to suppress history output on session resume * fix: preserve history state for /rewind while suppressing rendering * refactor: model quiet-restore as display policy with shared utilities * refactor: replace --quiet-restore with /history collapse|expand slash command * fix: persist history collapse state as user setting * fix(cli): address maintainer feedback on history collapse persistence and i18n * test(cli): fix TypeScript compilation errors in historyCommand tests * fix(cli): address maintainer review feedback on history collapse * test: fix act() warning in slashCommandProcessor.test.ts * fix: make applyCollapsePolicyAndSummary pure to avoid React batching bug * chore: revert unrelated changes to lockfile and NOTICES.txt * test: verify isRealUserTurn handles suppressOnRestore items correctly * wip(cli): preserve local history review fixes before redesign * feat(cli): refine history resume collapse commands * fix(cli): address maintainer review feedback on history collapse * test(cli): cover cold-boot collapsed resume * fix(cli): address reviewer feedback on history collapse * fix(cli): resolve rebase conflicts and missing imports * fix(cli): strip suppressOnRestore in handleRewindConfirm * fix(i18n): add Chinese translations for history collapse commands * fix: address wenshao review comments on PR #4085 - Restore restoreGoalFromHistory call in cold-boot resume path - Extract stripSuppressOnRestore to shared utility in resumeHistoryUtils - Add comment explaining historyRef pattern in slashCommandProcessor - Use historyCommand.name constant instead of string literal - Add missing i18n translation for collapse summary message - Fix pluralization in createHistoryCollapseSummaryItem * fix(i18n): add missing English translations for history collapse commands * fix: address wenshao follow-up review comments - Filter out collapse-summary items in rewind path (AppContainer.tsx) - Show info messages for collapse-on-resume/expand-on-resume commands (slashCommandProcessor.ts) - Use visibleHistory instead of uiState.history in summaryByCallId useMemo (MainContent.tsx) - Remove dead hasHistoryManager guard and optional chaining (useResumeCommand.ts) * fix: restore optional chaining for remount in useResumeCommand * test: update slashCommandProcessor tests for history command feedback changes * chore: remove generated artifact files from branch - Remove .learnings/LEARNINGS.md (local workflow artifact) - Remove .pr-body.md (PR description draft) - Remove build_output.log (build log) - Remove vscode_test_output.log (test output log) These files are unrelated to the history-collapse feature and were causing git diff --check whitespace errors. * fix: address wenshao review comments on PR #4085 - Remove stray [!tip] file from repo root - Add selfManaged flag to MessageActionReturn for explicit UI feedback control - Fix expand-now to return load_history type instead of calling loadHistory directly - Replace hardcoded isSelfManaged path check with result.selfManaged in processor - Fix MainContent merge detection to use visibleHistory.length (avoid flicker) - Refactor applyCollapsePolicyAndSummary to not mutate input array - Extract expandCollapsedHistory shared helper - Restore dialog:memory test in slashCommandProcessor.test.ts - Add stripSuppressOnRestore dedicated tests - Add visibleHistory filtering test in MainContent.test.tsx - Update historyCommand tests for new load_history return type - Fix eslint errors: remove unused imports and fix dependency array * fix: address wenshao review comments on PR #4085 - Remove stray [!tip] file from repo root - Add selfManaged flag to MessageActionReturn for explicit UI feedback control - Fix expand-now to return load_history type instead of calling loadHistory directly - Replace hardcoded isSelfManaged path check with result.selfManaged in processor - Fix MainContent merge detection to use visibleHistory.length (avoid flicker) - Refactor applyCollapsePolicyAndSummary to not mutate input array - Extract expandCollapsedHistory shared helper - Restore dialog:memory test in slashCommandProcessor.test.ts - Add stripSuppressOnRestore dedicated tests - Add visibleHistory filtering test in MainContent.test.tsx - Update historyCommand tests for new load_history return type - Fix eslint errors: remove unused imports and fix dependency array - Fix TypeScript errors: add useEffect import, fix display.kind type assertions * fix: add braces around if statement body (eslint curly rule) Fixes lint error in editorGroupUtils.ts: - Expected { after 'if' condition on line 32 * fix: address wenshao follow-up review comments on PR #4085 - Revert expand-now to use loadHistory/refreshStatic directly (no load_history return) - Remove dead selfManaged flag from MessageActionReturn and processor - Fix visibleHistory filter to also exclude collapse-summary items - Simplify applyCollapsePolicyAndSummary (return rawItems when !collapseOnResume) - Fix test assertion shapes (content→text, timestamp as separate arg) - Add mockClient for expand-now test - Update historyCommand tests for new behavior - Add expandCollapsedHistory dedicated tests - Fix MainContent filtering test to use historyItemDisplayPropsSpy * fix: address wenshao latest review comments on PR #4085 - Fix visibleHistory filter: remove collapse-summary exclusion (summary should render when all items suppressed) - Update MainContent test: assert summary item IS rendered alongside unsuppressed items - Remove dead selfManaged flag from MessageActionReturn type - Remove dead selfManaged check from slashCommandProcessor.ts - Add remount?.() to useResumeCommand error path - Remove unused 'History expanded.' translation key from en.js, zh.js, zh-TW.js - Fix expand-now test: mock action to return undefined (matching real behavior) * fix: address wenshao latest review comments on PR #4085 - Add missing i18n translations to ca.js, de.js, fr.js, ja.js, pt.js, ru.js - Simplify applyResumeDisplayPolicy: remove dead options parameter - Fix expand-now test: mock action to return undefined (matches real behavior) - Revert unrelated changes to package-lock.json and editorGroupUtils.ts * fix: address wenshao latest review comments - Add openDiffDialog to createMockActions() in slashCommandProcessor.test.ts - Add sentToModel: false to user message assertions in slashCommandProcessor.test.ts - Fix useResumeCommand.test.ts mock: spread original @qwen-code/qwen-code-core exports to include createDebugLogger - Remove dead optional chaining (addItem?., clearItems?., loadHistory?.) in useResumeCommand.ts - Simplify if (!config || !startNewSession) to if (!config) since startNewSession is required - Fix truncatedCount off-by-one in AppContainer.tsx rewind path: exclude collapse-summary from effective length - Apply collapse policy (applyCollapsePolicyAndSummary) in useBranchCommand.ts for /branch - Add settings to UseBranchCommandOptions with proper useCallback dependency * fix: add missing mockUpdateItem arg to test renderHook calls, remove stray file - Add mockUpdateItem (17th arg) to resume-direct and memory dialog test calls - Remove accidentally committed packages/core/.qwen/computer-use/installed.json - Add .qwen/computer-use/installed.json to .gitignore * fix: address review comments (type, gitignore, test, split-brain) 1. UIActionsContext: handleResume return type → Promise<void> 2. .gitignore: split corrupted merged line into .codegraph + .qwen/... 3. useBranchCommand.test: add settings to makeOptions(), add collapseOnResume test 4. useResumeCommand: reorder core-before-UI with rollback (matches branch pattern) 5. useResumeCommand.test: add getSessionId to mocks, add rollback test * fix: complete history collapse translations in 6 locale files - Added missing translation text for 'History collapsed' message - Fixed syntax errors in de, fr, ja, pt, ru, zh-TW locale files * fix: add missing history parameter in slashCommandProcessor test - Fixed parameter order in 'should skip reload when consumeSlashReloadSuppression' test - Added missing empty array for history parameter - All 58 tests now pass * merge: resolve conflicts with upstream main - docs: keep ui.history.collapseOnResume setting, adopt upstream showCitations default - fix: update rewindRecording call to include file history snapshots parameter * fix(cli): repair history collapse CI failures --------- Co-authored-by: qqqys <[email protected]> |
||
|
|
5715b3450d | docs: rewrite CLAUDE.md to point to AGENTS.md as authoritative source (#5138) | ||
|
|
be31e8493c |
perf(filesearch): move AsyncFzf index construction to a worker thread (#4621)
* perf(filesearch): move AsyncFzf index construction to a worker thread
`new AsyncFzf(allFiles, ...)` is misleadingly named — its constructor is
synchronous and dominates the @-picker's main-thread cost on workspaces
with >20k files (where the v2→v1 algorithm switch kicks in). It's the
remaining freeze source after PR #3214 moved the crawl onto async spawn-
based git ls-files / ripgrep / fdir.
This change hosts the AsyncFzf instance in a worker_threads worker so the
construction happens off the main thread; only completed find() results
cross the message channel. Below ~5k files the in-thread path is kept
because worker spawn + IPC overhead exceeds the construction cost there.
Notes:
- `FzfWorkerHandle.create()` awaits the worker `ready` event before
returning, so init errors surface at create() time rather than as a
surprise on the first find().
- Worker is `unref()`-ed so a mid-find @-picker doesn't pin process exit.
- `FileSearch` interface gains an optional `dispose?(): Promise<void>`
to release worker resources. `FileMessageHandler.clearFileSearchCache`
calls it so the long-running VS Code extension host doesn't accumulate
workers across watcher-triggered evictions.
- `installInProcessFzfTransport()` is provided for tests / sandboxed
environments that need to skip worker spawn entirely.
- Bundle gains a second esbuild build that emits `dist/fzfWorker.js`
alongside `dist/cli.js`; `prepare-package.js` ships it in the tarball.
- In `npm run dev` (tsx) the bundled worker file isn't on disk, so the
handle silently falls back to in-thread — acceptable for a dev script.
Closes the door on #3455's two open `FileIndexService` review items by
design: there is no `whenReady()` (handle awaits ready inside `create()`)
and there is no in-worker crawl (crawl stays on main, only fzf moves).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix(filesearch): address review feedback on fzf worker PR
- Fix worker thread leak in useAtCompletion: dispose previous FileSearch
instance on re-init and component cleanup
- Fix worker leak on init failure: wrap inst.ready() in try/catch with
timeout, dispose on failure
- Fix find()-after-dispose hang: reject immediately when worker is disposed
- Add limit field to FindMessage protocol to cap IPC payload before
postMessage serialization
- Add real worker_threads test coverage (spawn/find/dispose lifecycle,
concurrent finds, init failure, post-dispose rejection)
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(filesearch): address second round of review feedback
- Fix useAtCompletion cleanup: move dispose() to [cwd,config] effect so
it only fires on workspace change, not every keystroke/status transition
- Clear setTimeout in Promise.race on happy path to prevent timer leak
- Dispose FileSearch instances in FileMessageHandler on panel close
- Fix incorrect comment in prepare-package.js (resolveBundleDir, not URL)
- Add worker crash test: verify failAll rejects pending find() on exit
- Add RecursiveFileSearch.dispose() test in fileSearch.test.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* fix(core): close missing braces in formatDateForContext test
The second test case in the formatDateForContext describe block was
missing its closing `});`, causing a TS1005 parse error that broke CI
on all platforms. This was introduced upstream in
|
||
|
|
27b056b777 |
feat(skills): enforce auto-skill- directory prefix for auto-generated skills (#4839)
* feat(skills): enforce auto-skill- directory prefix for auto-generated skills Auto-generated skills created by the managed-skill-extractor agent now use a mandatory `auto-skill-` directory prefix, and `.gitignore` re-ignores `.qwen/skills/auto-skill-*/` so these transient, session-specific directories no longer surface as untracked files in `git status`. - .gitignore: add `.qwen/skills/auto-skill-*/` after the `!.qwen/skills/**` unignore rule. Git's last-rule-wins semantics re-ignore only the prefixed directories while hand-authored project skills stay tracked. Existing auto-generated skills are not renamed. - skillReviewAgentPlanner.ts: add an AUTO_SKILL_DIR_PREFIX constant and require the prefix in SKILL_REVIEW_SYSTEM_PROMPT and buildTaskPrompt(). The frontmatter `name:` stays the natural name so skills keep their normal invocation. This is a prompt-level convention only — SkillManager discovery is prefix-agnostic and `source: auto-skill` remains the file-level edit-protection marker. - add a test asserting buildTaskPrompt() emits the prefix instruction. Closes #4837 * test(skills): assert system-prompt prefix + reserve auto-skill- namespace Follow-up to reviewer verification on #4839. - Export SKILL_REVIEW_SYSTEM_PROMPT and add a unit test asserting it carries the `auto-skill-` prefix instruction. The prefix mandate lives on two independent string arrays (SKILL_REVIEW_SYSTEM_PROMPT and buildTaskPrompt); only the latter was guarded, so an edit could silently drop the mandate from the system-prompt layer. The new test closes that gap. - Extend the .gitignore comment to reserve `auto-skill-` as an auto-generated namespace, noting that a hand-authored skill using this prefix would be ignored — preventing a silent-untrack surprise. |
||
|
|
bec97f445d |
feat(skills): add agent reproduction workflows (#4118)
* chore(skills): add codex reproduce workflows * feat(agent-reproduce): implement agent reproduction workflow and supporting scripts * feat(skills): capture reference agent state diffs |
||
|
|
4dc98484fd |
feat(cli): do not append trailing space for directory completions (#4092) (#4288)
* feat(cli): do not append trailing space for directory completions (#4092) ## What 在 @路径补全和 /dir add 命令的目录补全中不再追加尾部空格。这样可以允许用户在补全目录后直接按 Tab 继续深入下一级子目录,无需先删除空格。 ## Examples - Input: `@src/com` + Tab → Output: `@src/components/` (no trailing space) - Input: `/dir add ./pac` + Tab → Output: `/dir add ./packages/` (no trailing space) - File completions still append a space (e.g., `@src/file.txt `) ## Changes - Added `isDirectory` flag to `Suggestion` and `CommandCompletionItem` interfaces - Updated `handleAutocomplete` to skip trailing space when `isDirectory === true` - Modified `getDirPathCompletions` to return `CommandCompletionItem[]` with `isDirectory: true` - Added test case for directory completion behavior * fix(cli): append trailing / to directory completions for deeper navigation * fix(cli): propagate isDirectory and fix JSDoc comment ## Comment 2: Fix JSDoc in SuggestionsDisplay Removed "(ends with /)" from isDirectory description since it was factually incorrect. ## Comment 3: Add test for isDirectory propagation - Added test suite in useSlashCompletion.test.ts to verify directory command structure - Real filesystem testing is done in directoryCommand.test.tsx * fix(cli): add comprehensive isDirectory propagation tests Added getDirPathCompletions unit tests that verify: - Directory suggestions include isDirectory: true - Directory values end with / for continued navigation - Prefix filtering preserves isDirectory flag - Comma-separated path completion works correctly - Deeply nested directories maintain isDirectory flag This closes the testing gap identified in review comment 3. * fix(cli): address wenshao feedback - lint rules, real test, cross-platform Fixes 4 new review comments from wenshao: - [Critical] Empty catch {} blocks: guarded with if (tempTestDir) + void err - [Critical] useSlashCompletion.no-op test: replaced with real integration test that verifies isDirectory propagation through toSuggestion pass-through - [Suggestion] Windows path separator: using path.sep instead of hardcoded / in both directoryCommand.tsx and related test assertions * fix(cli): remove unused import and fix Windows path separator in tests - Remove unused directoryCommand import in useSlashCompletion.test.ts (TS6133) - Replace hardcoded / regex with path.sep-aware assertions in directoryCommand.test.tsx to fix Windows CI failures Co-authored-by: Qwen-Coder <[email protected]> * Apply suggestion from @wenshao Co-authored-by: Shaojin Wen <[email protected]> * Update packages/cli/src/ui/commands/directoryCommand.test.tsx Co-authored-by: Shaojin Wen <[email protected]> * Update packages/cli/src/ui/commands/directoryCommand.tsx Co-authored-by: Shaojin Wen <[email protected]> * Update packages/cli/src/ui/commands/directoryCommand.tsx Co-authored-by: Shaojin Wen <[email protected]> * fix(cli): normalize isDirectory to explicit boolean in toSuggestion Normalize isDirectory from three-state (true/false/undefined) to explicit boolean (true/false) to prevent latent bugs in future code that might distinguish between false and undefined. Fixes review comment: isDirectory normalization is inconsistent across completion paths. Co-authored-by: Qwen-Coder <[email protected]> * Update packages/cli/src/ui/hooks/useSlashCompletion.ts Co-authored-by: Shaojin Wen <[email protected]> * chore: remove accidentally committed pr_body.md Co-authored-by: Qwen-Coder <[email protected]> * chore: add pr_body.md to .gitignore Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): remove duplicate .slice and orphaned test code from directoryCommand.tsx Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): only suppress trailing space for dir completions at end-of-line When isDirectory is true, the trailing space was suppressed unconditionally, even when the cursor is mid-line. This caused directory completions to merge directly with following text (e.g. '@src/components/something'). Now only suppress the space when the cursor is at end-of-line, allowing continued Tab navigation into subdirectories. Co-authored-by: Qwen-Coder <[email protected]> * docs(cli): document crawler path separator dependency for isDirectory check The isDirectory detection uses p.endsWith('/') which depends on the crawler in @qwen-code/qwen-code-core normalizing paths with posix '/' (fdir.withPathSeparator('/') in crawler.ts). Add a comment to make this implicit coupling explicit. Co-authored-by: Qwen-Coder <[email protected]> * test(cli): add mid-line directory completion test Verify that directory completions append a trailing space when the cursor is mid-line, preventing the completed path from merging with following text. Co-authored-by: Qwen-Coder <[email protected]> * Update packages/cli/src/ui/hooks/useCommandCompletion.test.ts Co-authored-by: Shaojin Wen <[email protected]> --------- Co-authored-by: 方磊 <[email protected].1.11> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
28a3439307 |
feat(skills): Add codegraph skill for PR review risk analysis and conflict detection (#3910)
* feat(skills): add codegraph skill for PR review risk analysis and conflict detection Add .qwen/skills/codegraph/ with PR analysis, bug analysis, schema, patterns, and eval support. Enables per-PR risk scoring (blast radius, interface changes, test coverage), cross-PR conflict detection, and automated GitHub labeling via the codegraph-ai pip package. * chore: ignore .venv and .codegraph directories Co-authored-by: Qwen-Coder <[email protected]> * docs(skills): add HF mirror and ModelScope fallback for embedding model downloads Co-authored-by: Qwen-Coder <[email protected]> * fix(skills): address PR #3910 review feedback for codegraph skill - Rename skill from codegraph-qa to codegraph for consistency - Broaden description to cover index creation and inspection - Add HuggingFace/ModelScope model download tips - Document codegraph ingest command for adding git history - Fix stray '@' in stdout Tee pattern code snippet - Remove dead reference to PRReview.md - Add repo_dir parameter to CrossPRAnalyzer examples - Update CHANGES edge types (hunk/deleted/related/new) and resolve_pr_functions internals - Add Conflict Detection Dimensions table - Replace codegraph query with PRReview Python API examples - Remove OPTIONAL MATCH limitation from schema (now supported) - Update codegraph-ai link to PyPI - Add HF model download entry to Troubleshooting table --------- Co-authored-by: pomelo-nwu <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
cb7059f54d |
feat(installer): add standalone archive installation (#3776)
* feat(installer): add standalone archive installation * fix(installer): harden standalone archive installs * fix(installer): address standalone review findings * chore(installer): clarify review followups * fix(installer): stabilize standalone script checks * chore(installer): remove internal planning docs * chore(installer): simplify standalone release review fixes * test(installer): add Windows batch install smoke * test(installer): fix Windows batch smoke quoting * test(installer): preserve Windows cmd quotes * fix(installer): use robust Windows checksum hashing * ci: narrow installer debug matrix * fix(installer): address standalone review hardening * fix(installer): avoid Windows validation parse errors * fix(installer): simplify Windows option validation * fix(installer): harden standalone review fixes |
||
|
|
7fe853a782 |
Feat/openrouter auth (#3576)
* feat(cli): add OpenRouter auth flow Co-authored-by: Qwen-Coder <[email protected]> * feat(cli): add OpenRouter model management UI Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): align OpenRouter OAuth fallback session Co-authored-by: Qwen-Coder <[email protected]> * refactor(cli): unify OpenRouter model setup flow Co-authored-by: Qwen-Coder <[email protected]> * feat(auth): update OAuth description with provider examples and i18n support - Updated OAuth option description to include provider examples (OpenRouter, ModelScope) - Added internationalization support for new description text - Updated all language files (en, zh, de, fr, ja, pt, ru) with translations Co-authored-by: Qwen-Coder <[email protected]> * docs: simplify OpenRouter design docs Co-authored-by: Qwen-Coder <[email protected]> * test(auth): fix OpenRouter OAuth mock typing Co-authored-by: Qwen-Coder <[email protected]> * test(auth): sync AuthDialog tests with new three-option main menu layout Co-authored-by: Qwen-Coder <[email protected]> Update assertions that referenced removed 'Qwen OAuth' and 'OpenRouter' options in the main/API-key views to match the refactored OAUTH / CODING_PLAN / API_KEY structure. * fix(i18n): add missing zh-TW translation for browser-based auth key Co-authored-by: Qwen-Coder <[email protected]> zh-TW.js was generated from main's en.js which had already removed this key, but the PR re-adds it in en.js. Sync zh-TW with the new translation. * feat(cli): Improve custom auth wizard with step indicators and cleaner advanced config (#3607) * feat(cli): Add custom API key auth wizard with 6-step setup flow Replace the documentation-only Co-authored-by: Qwen-Coder <[email protected]>"Custom API Key" screen with an in-terminal wizard: Protocol select → Base URL input → API Key input → Model ID input → JSON review → Save. - Add 5 new ViewLevels and render functions in AuthDialog - Implement utility functions: generateCustomApiKeyEnvKey (normalization), normalizeCustomModelIds (split/trim/dedupe), maskApiKey (display) - Implement handleCustomApiKeySubmit in useAuth with backup, env key generation, modelProviders merge, auth refresh, and user feedback - Wire handler through UIActionsContext and AppContainer - Add 18 unit tests for utilities, 4 wizard flow integration tests * feat(cli): Improve custom auth wizard with step indicators and cleaner advanced config - Add step indicators (Step 1/6 · Protocol) to each wizard screen - Remove redundant Protocol/Endpoint context from each step for focus - Redesign advanced config: add descriptions to thinking/modality toggles - Remove max tokens option; keep only thinking and modality settings - Add ↑↓ arrow navigation with Space toggle and Enter to continue - Generation config flows through review JSON and final submit Co-authored-by: Qwen-Coder <[email protected]> * test: Fix Windows CI failures in fileUtils and AuthDialog tests - fileUtils.test.ts: Mock node:child_process execFile to prevent pdftotext spawn that times out on Windows (ENOENT, 5s timeout) - AuthDialog.test.tsx: Add char-by-char typeText() helper to work around Node 24.x + ink TextInput compatibility issue on Windows Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): Reset advanced wizard state and use JSON.stringify for settings preview - Reset advancedThinkingEnabled, advancedModalityEnabled, and focusedConfigIndex when re-entering custom wizard to prevent state leakage between configurations - Replace hand-rolled JSON string concatenation with JSON.stringify for settings.json preview to properly escape special characters in model IDs and base URLs Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): harden OpenRouter OAuth callback handling Co-authored-by: Qwen-Coder <[email protected]> * test(cli): stabilize OpenRouter state mismatch test Co-authored-by: Qwen-Coder <[email protected]> * test(cli): stabilize custom auth wizard navigation Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
96bc874197 |
chore(gitignore): add .codex directory to gitignore (#3665)
🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) Co-authored-by: jinye.djy <[email protected]> |
||
|
|
e47b22806b |
feat(docs): add qwen-code skills, agents, and updated AGENTS.md (#3575)
- Add new skills: bugfix, feat-dev with structured workflows - Update existing skills: docs-audit-and-refresh, docs-update-from-diff, e2e-testing, qwen-code-claw, structured-debugging, terminal-capture - Update test-engineer agent with clearer constraints and formatting - Update qc commands: bugfix, code-review, commit, create-issue, create-pr - Reorganize .gitignore to keep qwen configs near top - Expand AGENTS.md with development commands, feature/bugfix workflows, project directories table, and code review guidelines Co-authored-by: 愚远 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
dc833d9d94 |
feat: add bugfix workflow, test-engineer agent, and debugging skills
- Add test-engineer agent for bug reproduction and verification - Add /qc:bugfix command for structured bugfix workflow - Add e2e-testing skill covering headless/interactive modes, MCP testing - Add structured-debugging skill for hypothesis-driven debugging - Simplify AGENTS.md to focus on essential commands and conventions - Add terminal-capture scenario for bugfix workflow testing - Add .qwen folder to ESLint ignore list Known limitations: The /qc:bugfix workflow and e2e-testing skill are experimental and may be unstable or consume significant tokens. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
7a6b725b0c |
feat: replace qwen-settings-config with bundled qc-helper skill
- Remove project-level qwen-settings-config skill and its references/ - Create bundled qc-helper skill at packages/core/src/skills/bundled/ that references docs/users/ for answering usage/config questions - Update copy_bundle_assets.js to copy docs/users/ into dist/bundled/qc-helper/docs/ - Update dev.js to create symlink for dev mode docs access - Add bundled docs directory verification in prepare-package.js - Revert doc-update skills (docs-audit-and-refresh, docs-update-from-diff) to main branch versions |
||
|
|
a8bc25beb9 | feat(skills): add docs audit and update helpers | ||
|
|
bca799e818 | Merge branch 'main' into feat/ask-user-question-tool | ||
|
|
a172696b86 |
Merge branch 'main' into feat/support-insight-command
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
9e4c5ee891 |
refactor: Extract web-templates package and unify build/pack workflow
Moves export-html and insight templates from cli/assets to a new dedicated web-templates package. Updates Dockerfile and build scripts to use consolidated bundle/prepare:package/pack workflow. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
bec8cbc575 | Merge branch 'main' into feat/terminal-capture | ||
|
|
ad23df4021 |
chore: exclude .qwen/commands/ and .qwen/skills/ from gitignore
- Add negation rules to preserve Qwen commands and skills directories - This allows version control for custom commands and skills in .qwen/ Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
997fcbfaed |
feat: add terminal-capture for CLI screenshot automation
- Add terminal-capture engine using node-pty + xterm.js + Playwright - Add scenario runner with TypeScript configuration - Add pre-built scenarios (/about, /context, /export, /auth) - Add Cursor skills for terminal-capture and pr-review workflow - Add motivation documentation Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
12b669d7c6 | feat: update .gitignore to include .claude and CLAUDE.md; add project context documentation in QWEN.md | ||
|
|
02db22dd78 |
chore: update eslint config and translation adjustments
Co-authored-by: Qwen-Coder <[email protected]> This commit includes several updates: - Updated eslint configuration with improved formatting and organization - Added SaveMemoryToolCall support in vscode-ide-companion - Translated Chinese comments and UI text to English in various components - Made minor code formatting improvements across multiple files |
||
|
|
878a870df2 | Merge branch 'main' of https://github.com/QwenLM/qwen-code into feat/unified-ui-for-vscode-extension | ||
|
|
e1326d9e72 |
Merge pull request #1640 from QwenLM/feat/concurrent-runner
feat: add concurrent runner for batch CLI execution |
||
|
|
021680cd70 | feat: add html template | ||
|
|
f48eec9a02 |
feat: add git diff capture and session log collection to concurrent runner
- Add git diff capture after each run, saved as diff.patch
- Add session log collection from ~/.qwen/projects/{id}/chats/
- Store session logs in outputs/{run_id}/chats/ with original filename
- Add session_id field to track chat recording UUID
- Modify cwd in session logs to actual runner working directory
- Remove stdout_file/stderr_file from top-level, keep only in prompt_results
- Rename logs folder to openai-logs
- Add File Writer task example for testing file creation
Co-authored-by: Qwen-Coder <[email protected]>
|
||
|
|
b9a0d904de | feat: add multi-modal input support (image, PDF, audio) across all content generators |