mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +00:00
feat(cli): add session recap with /recap and auto-show on return (#3434)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* feat(cli): add session recap with /recap and auto-show on return
Users often open an old session days later and need to scroll through
pages to remember where they left off. This change adds a short
"where did I leave off" recap — a 1-3 sentence summary generated by
the fast model — so they can resume without re-reading the history.
Two triggers:
- /recap: manual slash command.
- Auto: when the terminal has been blurred for 5+ minutes and gets
focused again (uses the existing DECSET 1004 focus protocol via
useFocus). Gated on streamingState === Idle so it never interrupts
an active turn. Only fires once per blur cycle.
The recap is rendered in dim color with a chevron prefix, visually
distinct from assistant replies. A new `general.showSessionRecap`
setting controls the auto-trigger (default on). /recap works
independent of the setting.
Implementation notes:
- generateSessionRecap uses fastModel (falls back to main model),
tools: [], maxOutputTokens: 300, and a tight system prompt. It
strips tool calls / responses from history before sending — tool
responses can hold 10K+ tokens of file content that drown the recap
in irrelevant detail. The 30-message window respects turn boundaries
(slice never starts on a dangling model/tool response).
- Output is wrapped in <recap>...</recap> tags; the extractor returns
empty (skips render) if the tag is missing, preventing model
reasoning from leaking into the UI.
- All failures are silent (return null) and logged via a scoped
debugLogger; recap is best-effort and must never break main flow.
- /recap refuses to run while a turn is pending.
* fix(cli): abort in-flight recap when showSessionRecap is disabled
If the user disables showSessionRecap while an auto-recap LLM call is
already in flight, the previous code returned early without aborting.
The pending .then would still pass its idle/abort guards and append the
recap, producing an unwanted message after the user has opted out.
Abort the controller and clear it eagerly so the resolved promise no
longer adds to history.
* fix(cli): gate /recap and auto-recap on streaming idle state
Two related issues from review:
1. /recap was only refusing when ui.pendingItem was set, but a normal
model reply runs with streamingState === Responding and a null
pendingItem. Invoking /recap mid-stream would generate a recap from
a partial conversation and insert it between the user prompt and
the assistant reply.
2. useAwaySummary cleared blurredAtRef before checking isIdle, so if
focus returned during a still-streaming turn (after a >5min blur)
the recap was permanently dropped — there was no later retry when
the turn became idle, because isIdle was not in the effect deps.
Fixes:
- Expose isIdleRef on CommandContext.ui (mirrors btwAbortControllerRef
pattern). Plumb it from AppContainer through useSlashCommandProcessor.
- recapCommand now refuses when isIdleRef.current is false OR
pendingItem is non-null.
- useAwaySummary preserves blurredAtRef on the !isIdle bail and adds
isIdle to the effect deps, so the trigger re-evaluates when the
current turn finishes.
- Brief blurs (< AWAY_THRESHOLD_MS) still reset blurredAtRef.
Also seeds isIdleRef in nonInteractiveUi and mockCommandContext so the
new field has a sensible default outside the interactive UI.
* docs: document /recap command, showSessionRecap setting, and design
- User docs: add /recap to the Session and Project Management table in
features/commands.md and a dedicated subsection covering manual use,
the auto-trigger, the dim-color rendering, and the fast-model tip.
- User docs: add general.showSessionRecap row to the configuration
settings reference.
- Design doc: docs/design/session-recap/session-recap-design.md covers
motivation, the two trigger paths, the per-file architecture, prompt
design with the <recap> tag and three-tier extractor, history
filtering rationale (functionResponse can be 10K+ tokens), the
useAwaySummary state machine, the isIdleRef gating for /recap, model
selection, observability, and out-of-scope items.
* fix(core): exclude thought parts from session recap context
filterToDialog kept any non-empty text part, but @google/genai's Part
type also marks model reasoning with part.thought / part.thoughtSignature.
That hidden chain-of-thought was being fed to the recap LLM and could
get summarized as if it were user-visible dialogue.
Drop parts where either flag is set. Update the design doc's
History 过滤 section to call this out alongside the existing
tool-call/response rationale.
* docs(session-recap): correct debug-logging guidance, fill in state machine, sharpen UX wording
Audit of the session recap docs against the implementation found three
issues worth fixing:
- Design doc claimed debug logs were enabled via a QWEN_CODE_DEBUG_LOGGING
env var. That var does not exist; debug logs are written to
~/.qwen/debug/<sessionId>.txt by default, gated by QWEN_DEBUG_LOG_FILE.
Replace with the accurate path + opt-out behavior, and tell the reader
to grep for the [SESSION_RECAP] tag.
- Design doc's useAwaySummary state machine table was missing the
isFocused && blurredAtRef === null path (taken on first render and
right after a brief-blur reset). Add the row.
- User doc's "Refuses to run ... failures are silent" line conflated the
inline-error refusal with silent generation failures, and "(when the
conversation is idle)" used internal jargon. Split the two cases and
spell out what "idle" means, including the wait-then-fire behavior
when focus returns mid-turn.
* docs(session-recap): correctly describe /recap vs auto-trigger failure modes
The previous wording said "Generation/network failures are silent — the
recap simply does not appear", but recapCommand returns a user-facing
info message ("Not enough conversation context for a recap yet.") in
exactly that path, and also returns inline messages for the
config-not-loaded and busy-turn guards.
Only the auto-trigger path is truly silent (it just skips addItem when
generateSessionRecap returns null). Split the two paths in the doc so
the manual command's "always responds with something" behavior is
distinguished from the auto-trigger's no-op-on-failure behavior.
* docs(session-recap): align prompt-rules section with the actual prompt
Two doc-vs-code mismatches in the design doc's "System Prompt" section,
caught with the same lens as yiliang114's failure-mode review:
- The bullet list claimed RECAP_SYSTEM_PROMPT forbids "推测用户意图"
and "用 'you' 称呼用户". Those rules existed in an early draft but
were dropped when the <recap> tag rules were added; the current
prompt has no such restrictions. Replace with the actual rules and
add a "与 RECAP_SYSTEM_PROMPT 一一对应" marker so future edits stay
in sync.
- The doc said systemInstruction "覆盖" the main agent prompt. True
for the agent prompt portion, but GeminiClient.generateContent
internally calls getCustomSystemPrompt which appends user memory
(QWEN.md / 自动 memory) as a suffix. Spell that out — the final
system prompt is recap prompt + user memory, which is actually
useful project context for the recap.
* docs(session-recap): translate design doc to English
The repo convention for docs/design is English (7 of 8 existing files;
auto-memory/memory-system.md is the only Chinese one). The first version
of this design doc followed the auto-memory example, which turned out
to be the wrong sample.
Translate to English while preserving the existing structure, the
state-machine table, the prompt-vs-doc 1:1 alignment, the
QWEN_DEBUG_LOG_FILE description, and the failure-mode notes added in
prior commits.
* fix(cli): drop empty info return from /recap interactive success path
The interactive success path inserts the away_recap history item
directly via ui.addItem and then returned `{type: 'message',
messageType: 'info', content: ''}`. The slash-command processor's
'message' case unconditionally calls addMessage, which adds another
HistoryItemInfo with empty text. The empty info renders as nothing
(StatusMessage early-returns null), but it still bloats the in-memory
history list and shows up in /export and saved sessions.
Return void on the interactive success path and on the abort path so
the processor's `if (result)` check skips the message-handler branch
entirely. Widen the action's return type to `void | SlashCommandActionReturn`
to match (same shape as btwCommand).
This commit is contained in:
parent
528fcfcff8
commit
60a6dfc14c
19 changed files with 702 additions and 4 deletions
|
|
@ -24,6 +24,7 @@ These commands help you save, restore, and summarize work progress.
|
|||
| `/summary` | Generate project summary based on conversation history | `/summary` |
|
||||
| `/compress` | Replace chat history with summary to save Tokens | `/compress` |
|
||||
| `/resume` | Resume a previous conversation session | `/resume` |
|
||||
| `/recap` | Show a 1-3 sentence "where you left off" summary | `/recap` |
|
||||
| `/restore` | Restore files to state before tool execution | `/restore` (list) or `/restore <ID>` |
|
||||
|
||||
### 1.2 Interface and Workspace Control
|
||||
|
|
@ -156,7 +157,58 @@ The `/btw` command allows you to ask quick side questions without interrupting o
|
|||
>
|
||||
> Use `/btw` when you need a quick answer without derailing your main task. It's especially useful for clarifying concepts, checking facts, or getting quick explanations while staying focused on your primary workflow.
|
||||
|
||||
### 1.7 Information, Settings, and Help
|
||||
### 1.7 Session Recap (`/recap`)
|
||||
|
||||
The `/recap` command generates a short "where you left off" summary of the
|
||||
current session, so you can resume an old conversation without scrolling
|
||||
back through pages of history.
|
||||
|
||||
| Command | Description |
|
||||
| -------- | ------------------------------------------------ |
|
||||
| `/recap` | Generate and show a 1-3 sentence session summary |
|
||||
|
||||
**How it works:**
|
||||
|
||||
- Uses the configured fast model (`fastModel` setting) when available, falling
|
||||
back to the main session model. A small, cheap model is enough for a recap.
|
||||
- The recent conversation (up to 30 messages, text only — tool calls and tool
|
||||
responses are filtered out) is sent to the model with a tight system prompt.
|
||||
- The recap is rendered in dim color with a `❯` prefix so it stands apart
|
||||
from real assistant replies.
|
||||
- Refuses with an inline error if a model turn is in flight or another command
|
||||
is processing. If there is no usable conversation, or the underlying
|
||||
generation fails, `/recap` shows a short info message instead of a recap —
|
||||
the manual command always responds with something.
|
||||
|
||||
**Auto-trigger when returning from being away:**
|
||||
|
||||
If the terminal is blurred for **5+ minutes** and gets focused again, a recap
|
||||
is generated and shown automatically (only when no model response is in
|
||||
progress; otherwise it waits for the current turn to finish and then fires).
|
||||
Unlike the manual command, the auto-trigger is fully silent on failure: if
|
||||
generation errors or there is nothing to summarize, no message is added to
|
||||
the history. Controlled by the `general.showSessionRecap` setting
|
||||
(default: `true`); the manual `/recap` command always works regardless of
|
||||
this setting.
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
> /recap
|
||||
|
||||
❯ Refactoring loopDetectionService.ts to address long-session OOM caused by
|
||||
unbounded streamContentHistory and contentStats. The next step is to
|
||||
implement option B (LRU sliding window with FNV-1a) pending confirmation.
|
||||
```
|
||||
|
||||
> [!tip]
|
||||
>
|
||||
> Configure a fast model via `/model --fast <model>` (e.g.
|
||||
> `qwen3-coder-flash`) to make `/recap` fast and cheap. Set
|
||||
> `general.showSessionRecap` to `false` to opt out of the auto-trigger
|
||||
> while keeping the manual command available.
|
||||
|
||||
### 1.8 Information, Settings, and Help
|
||||
|
||||
Commands for obtaining information and performing system settings.
|
||||
|
||||
|
|
@ -171,7 +223,7 @@ Commands for obtaining information and performing system settings.
|
|||
| `/copy` | Copy last output content to clipboard | `/copy` |
|
||||
| `/quit` | Exit Qwen Code immediately | `/quit` or `/exit` |
|
||||
|
||||
### 1.8 Common Shortcuts
|
||||
### 1.9 Common Shortcuts
|
||||
|
||||
| Shortcut | Function | Note |
|
||||
| ------------------ | ----------------------- | ---------------------- |
|
||||
|
|
@ -181,7 +233,7 @@ Commands for obtaining information and performing system settings.
|
|||
| `Ctrl/cmd+Z` | Undo input | Text editing |
|
||||
| `Ctrl/cmd+Shift+Z` | Redo input | Text editing |
|
||||
|
||||
### 1.9 CLI Auth Subcommands
|
||||
### 1.10 CLI Auth Subcommands
|
||||
|
||||
In addition to the in-session `/auth` slash command, Qwen Code provides standalone CLI subcommands for managing authentication directly from the terminal:
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue