feat: LLM intent router, direct chat execution, and Memory V2 light intervention
Wire chat intent routing with direct_chat on regular sessions, skill-selected short-circuit to Agent, memory light/heavy intervention tiers, and fix direct chat UI stuck streaming after completion. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -172,6 +172,7 @@ export function ChatPanel({
|
||||
const [uploadingImage, setUploadingImage] = useState(false);
|
||||
const [imageError, setImageError] = useState<string | null>(null);
|
||||
const [voiceStopSignal, setVoiceStopSignal] = useState(0);
|
||||
const pendingSkillRef = useRef<string | null>(null);
|
||||
const [randomPrompt] = useState(
|
||||
() => CHAT_PLACEHOLDER_PROMPTS[Math.floor(Math.random() * CHAT_PLACEHOLDER_PROMPTS.length)],
|
||||
);
|
||||
@@ -432,9 +433,11 @@ export function ChatPanel({
|
||||
return { accepted, message };
|
||||
}, [pendingImageBytes, pendingImages.length]);
|
||||
|
||||
const submitText = useCallback(async (textOverride?: string) => {
|
||||
const submitText = useCallback(async (textOverride?: string, skillIdOverride?: string) => {
|
||||
const baseText = typeof textOverride === 'string' ? textOverride : input;
|
||||
const trimmed = baseText.trim();
|
||||
const selectedChatSkill = skillIdOverride ?? pendingSkillRef.current ?? undefined;
|
||||
pendingSkillRef.current = null;
|
||||
if ((!trimmed && pendingImages.length === 0) || voiceDisabled) return;
|
||||
if (pendingImages.length > 0 && !onUploadImage) {
|
||||
setImageError('当前会话暂不支持图片发送');
|
||||
@@ -518,6 +521,7 @@ export function ChatPanel({
|
||||
await onSubmit(trimmed, imagesToSend, previewImagesToSend, {
|
||||
messageId: outgoingMessageId,
|
||||
forceDeepReasoning,
|
||||
selectedChatSkill,
|
||||
});
|
||||
uploadedImages.forEach(revokePendingImage);
|
||||
setPendingImages([]);
|
||||
@@ -823,7 +827,10 @@ export function ChatPanel({
|
||||
skills={chatSkills}
|
||||
disabled={voiceDisabled}
|
||||
onSelect={submitText}
|
||||
onPrefill={setInput}
|
||||
onPrefill={(prompt, skillId) => {
|
||||
pendingSkillRef.current = skillId ?? null;
|
||||
setInput(prompt);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!showHomeWelcome && (
|
||||
|
||||
@@ -10,8 +10,8 @@ export function ChatSkillPicker({
|
||||
}: {
|
||||
skills: ChatSkillOption[];
|
||||
disabled?: boolean;
|
||||
onSelect: (prompt: string) => void | Promise<void>;
|
||||
onPrefill?: (prompt: string) => void;
|
||||
onSelect: (prompt: string, skillId?: string) => void | Promise<void>;
|
||||
onPrefill?: (prompt: string, skillId?: string) => void;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const wrapRef = useRef<HTMLDivElement>(null);
|
||||
@@ -40,10 +40,10 @@ export function ChatSkillPicker({
|
||||
setOpen(false);
|
||||
const prompt = skill.buildPrompt(skill.skillName);
|
||||
if (skill.prefillOnly) {
|
||||
onPrefill?.(prompt);
|
||||
onPrefill?.(prompt, skill.id);
|
||||
return;
|
||||
}
|
||||
void onSelect(prompt);
|
||||
void onSelect(prompt, skill.id);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user