qwen-code/.github/assets
ChiGao b9be6c8ee8
fix(cli): echo resume command to main screen on exit (#8455)
* fix(cli): echo resume command to main screen on exit

The quit screen already shows a resume hint, but in VP mode (the
default) it is drawn on the alternate screen, which is discarded on
teardown — users never see it. Echo the command again after Ink
unmounts, when we are back on the main screen buffer, so it survives
exit and can be copied from the terminal scrollback. Only printed when
chat recording is enabled and the session file exists, so empty
sessions do not advertise a resume that has nothing to restore.

* docs(cli): add real-machine screenshots for exit resume echo

* fix(cli): make exit resume echo best-effort and sanitize session ID

* fix(cli): make exit resume echo tests Windows-safe and pin gates

The sanitization test created a real file with ESC/BEL control bytes
in its name; Win32 forbids 0x01-0x1F in file names, so the test would
fail in the merge-queue Windows job. Stub the existence gate instead.

Also rebuild the recording-disabled negative test on a full recording
config so removing the getChatRecordingService() gate actually fails
it (it previously passed via the exception path), and add coverage for
the isTTY half of the gate. The source switches to a namespace fs
import so the gate is stubbable in vitest (trustedFolders.ts
precedent); runtime behavior is unchanged.

* fix(cli): gate exit resume echo to paste-safe session IDs

* fix(cli): reject dash-leading IDs in exit resume echo (#8455)

A dash-leading session ID (loadable from disk: SESSION_FILE_PATTERN
accepts dash-leading transcript names) pasted back as `qwen --resume
-<id>` reparses as CLI flags instead of the option value, so require an
alphanumeric first character in the paste-safe gate. Also switch the
echo to writeStdoutLine, the helper the file already imports
writeStderrLine from.

* fix(cli): gate exit resume echo to canonical session IDs (#8455)

Replace the inline charset gate with isValidSessionId, the same
canonical predicate --resume routing, --session-id validation, and the
in-TUI /resume command use. The old gate was strictly looser, so a
transcript-sourced ID (e.g. via --continue) could be echoed as a hint
that --resume then misroutes to title matching. The UUID shape is
already paste-safe, making the extra sanitizeTerminalText wrap
redundant.

Also make the positive echo test locale-independent: earlier main()
tests run the real initializeI18n('auto') and leave the machine
locale's dictionary in the i18n module state, so the hardcoded
English label failed deterministically under any supported non-English
locale. Match the locale-free command part instead, and switch the
echo test fixtures to valid UUIDs so each negative test isolates its
intended gate.

* fix(cli): align exit resume echo with --resume transcript semantics (#8455)

The echo gated on fs.existsSync(sessionFile), but --resume refuses an
empty transcript: ChatRecordingService creates the JSONL file before
the first record lands, and a failed first write (or an exit that races
the flush ceiling) leaves it empty through exit, so the hinted
`qwen --resume <id>` fails with "No saved session found with ID".
Require a non-empty transcript instead of switching to
SessionService.sessionExists(): that check's SESSION_FILE_PATTERN
rejects the -agent- suffixed Arena IDs that --resume itself accepts,
which would have silently narrowed the echo's acceptance surface.

Also mirror the full SESSION_ID_REGEX — including the -agent- suffix
form — in the gemini.test.tsx isValidSessionId mock, which had drifted
narrower than the production gate, and cover the agent-suffixed shape
with a positive echo case. The now-unused node:fs existsSync mock and
its stub in the hostile-ID cases are removed: the ID gate rejects
before any file probe.

* test(cli): Harden exit resume echo gate tests (#8455)

* fix(cli): Use async stat in exit resume echo gate (#8455)

The exit-cleanup chain bounds each cleanup with a 2s withTimeout race
and a 5s overall ceiling, but fs.statSync() blocks the event loop, so
an unresponsive filesystem (dead NFS/FUSE mount) hangs exit past both
ceilings despite the surrounding try/catch. Switch to fs/promises
stat — the handler is already async, so the existing race bounds it.

---------

Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
Co-authored-by: qwen-code-autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-08-08 04:29:31 +00:00
..
exit-resume-echo fix(cli): echo resume command to main screen on exit (#8455) 2026-08-08 04:29:31 +00:00
worktree-toggle feat(web-shell): surface worktree isolation in the new-session empty state (#7365) 2026-07-21 07:38:33 +00:00