Merge pull request #2776 from QwenLM/feat/enhance-btw-command

feat(cli): enhance /btw side question with improved prompt and Ctrl+C/D cancel
This commit is contained in:
Shaojin Wen 2026-04-03 19:17:12 +08:00 committed by GitHub
parent 61bc80fe19
commit e855229453
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 351 additions and 57 deletions

View file

@ -14,6 +14,7 @@ import { MessageType } from '../types.js';
import type { HistoryItemBtw } from '../types.js';
import { t } from '../../i18n/index.js';
import type { GeminiClient } from '@qwen-code/qwen-code-core';
import type { Content } from '@google/genai';
function makeBtwPromptId(sessionId: string): string {
return `${sessionId}########btw-${Date.now()}`;
@ -26,6 +27,24 @@ function formatBtwError(error: unknown): string {
});
}
// Keep only the most recent history messages to limit token usage for side
// questions. MAX_BTW_HISTORY_MESSAGES caps the number of history Content
// entries included as context before the /btw question is appended.
const MAX_BTW_HISTORY_MESSAGES = 20;
function trimHistory(history: Content[]): Content[] {
if (history.length <= MAX_BTW_HISTORY_MESSAGES) {
return history;
}
// Slice from the end, ensuring we start on a 'user' message so the
// alternating user/model pattern is preserved.
const sliced = history.slice(-MAX_BTW_HISTORY_MESSAGES);
if (sliced[0]?.role === 'model' && sliced.length > 1) {
return sliced.slice(1);
}
return sliced;
}
/**
* Helper to make the ephemeral generateContent call and extract the answer.
* Uses a snapshot of the current conversation history as context.
@ -37,8 +56,13 @@ async function askBtw(
abortSignal: AbortSignal,
promptId: string,
): Promise<string> {
const history = geminiClient.getHistory();
const history = trimHistory(geminiClient.getHistory(true));
// Side-question guidance sent as a user message (not a system instruction).
// Inspired by Claude Code's design:
// - Emphasizes direct answering without tools
// - Clarifies the isolated nature of the side question
// - Prevents the model from promising actions it can't take
const response = await geminiClient.generateContent(
[
...history,
@ -46,7 +70,23 @@ async function askBtw(
role: 'user',
parts: [
{
text: `[Side question - answer briefly and concisely, this is a "by the way" question that doesn't need to be part of our main conversation]\n\n${question}`,
text: `[This is a side question - answer directly and concisely.
IMPORTANT:
- You are a separate, lightweight agent spawned to answer this one question
- The main conversation continues independently in the background
- Do NOT reference being interrupted or what you were "previously doing"
CRITICAL CONSTRAINTS:
- You have NO tools available - you cannot read files, run commands, search, or take any actions
- This is a one-off response in a single turn
- You can ONLY provide information based on what you already know from the conversation context
- NEVER say things like "Let me try...", "I'll now...", "Let me check...", or promise to take any action
- If you don't know the answer, say so - do not offer to look it up or investigate
Simply answer the question directly with the information you have.]
${question}`,
},
],
},