fix: scope session assist to session directory
This commit is contained in:
@@ -267,7 +267,7 @@ const ChatViewport = React.memo(({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<SessionRecapNote sessionId={currentSessionId} isMobile={isMobile} />
|
<SessionRecapNote sessionId={currentSessionId} directory={directory} isMobile={isMobile} />
|
||||||
|
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<StatusRowContainer />
|
<StatusRowContainer />
|
||||||
|
|||||||
@@ -4758,6 +4758,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<SessionSuggestionChip
|
<SessionSuggestionChip
|
||||||
sessionId={currentSessionId}
|
sessionId={currentSessionId}
|
||||||
|
directory={currentSessionDirectoryForSync ?? currentDirectory}
|
||||||
hidden={hasContent || newSessionDraftOpen}
|
hidden={hasContent || newSessionDraftOpen}
|
||||||
onApply={applyAssistSuggestion}
|
onApply={applyAssistSuggestion}
|
||||||
className="mb-1.5"
|
className="mb-1.5"
|
||||||
@@ -4841,6 +4842,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
|||||||
<>
|
<>
|
||||||
<SessionSuggestionChip
|
<SessionSuggestionChip
|
||||||
sessionId={currentSessionId}
|
sessionId={currentSessionId}
|
||||||
|
directory={currentSessionDirectoryForSync ?? currentDirectory}
|
||||||
hidden={hasContent || newSessionDraftOpen}
|
hidden={hasContent || newSessionDraftOpen}
|
||||||
onApply={applyAssistSuggestion}
|
onApply={applyAssistSuggestion}
|
||||||
className="mb-1.5"
|
className="mb-1.5"
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ import { useI18n } from '@/lib/i18n';
|
|||||||
|
|
||||||
interface SessionRecapNoteProps {
|
interface SessionRecapNoteProps {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
|
directory?: string;
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quiet one-paragraph recap of the agent's last reply, rendered right under
|
// Quiet one-paragraph recap of the agent's last reply, rendered right under
|
||||||
// the last message (above the reserved bottom gap). Appears only after the
|
// the last message (above the reserved bottom gap). Appears only after the
|
||||||
// 5-minute quiet window, so the layout shift happens off-screen in practice.
|
// 5-minute quiet window, so the layout shift happens off-screen in practice.
|
||||||
export const SessionRecapNote: React.FC<SessionRecapNoteProps> = React.memo(({ sessionId, isMobile }) => {
|
export const SessionRecapNote: React.FC<SessionRecapNoteProps> = React.memo(({ sessionId, directory, isMobile }) => {
|
||||||
const { visibleRecap } = useSessionAssistState(sessionId);
|
const { visibleRecap } = useSessionAssistState(sessionId, directory);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
if (!visibleRecap) {
|
if (!visibleRecap) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useI18n } from '@/lib/i18n';
|
|||||||
|
|
||||||
interface SessionSuggestionChipProps {
|
interface SessionSuggestionChipProps {
|
||||||
sessionId: string | null;
|
sessionId: string | null;
|
||||||
|
directory?: string;
|
||||||
/** The composer already has content — the suggestion must stay out of the way. */
|
/** The composer already has content — the suggestion must stay out of the way. */
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
onApply: (text: string) => void;
|
onApply: (text: string) => void;
|
||||||
@@ -20,8 +21,8 @@ const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|||||||
// One small-model-suggested follow-up message, styled like the draft starter
|
// One small-model-suggested follow-up message, styled like the draft starter
|
||||||
// chips. Tapping it fills the composer (no auto-send); the X patches the
|
// chips. Tapping it fills the composer (no auto-send); the X patches the
|
||||||
// suggestion out of the session metadata so it stays dismissed everywhere.
|
// suggestion out of the session metadata so it stays dismissed everywhere.
|
||||||
export const SessionSuggestionChip: React.FC<SessionSuggestionChipProps> = React.memo(({ sessionId, hidden, onApply, className }) => {
|
export const SessionSuggestionChip: React.FC<SessionSuggestionChipProps> = React.memo(({ sessionId, directory, hidden, onApply, className }) => {
|
||||||
const { suggestion } = useSessionAssistState(sessionId ?? '');
|
const { suggestion } = useSessionAssistState(sessionId ?? '', directory);
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { currentTheme } = useThemeSystem();
|
const { currentTheme } = useThemeSystem();
|
||||||
const [dismissing, setDismissing] = React.useState(false);
|
const [dismissing, setDismissing] = React.useState(false);
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ interface LastMessageSnapshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Narrow subscription to the last message of a session (id/role/time only). */
|
/** Narrow subscription to the last message of a session (id/role/time only). */
|
||||||
function useLastMessageSnapshot(sessionId: string): LastMessageSnapshot | null {
|
function useLastMessageSnapshot(sessionId: string, directory?: string): LastMessageSnapshot | null {
|
||||||
const store = useDirectoryStore();
|
const store = useDirectoryStore(directory);
|
||||||
const cacheRef = React.useRef<LastMessageSnapshot | null>(null);
|
const cacheRef = React.useRef<LastMessageSnapshot | null>(null);
|
||||||
|
|
||||||
const getSnapshot = React.useCallback((): LastMessageSnapshot | null => {
|
const getSnapshot = React.useCallback((): LastMessageSnapshot | null => {
|
||||||
@@ -56,10 +56,10 @@ export interface SessionAssistState {
|
|||||||
suggestion: string | null;
|
suggestion: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSessionAssistState(sessionId: string): SessionAssistState {
|
export function useSessionAssistState(sessionId: string, directory?: string): SessionAssistState {
|
||||||
const session = useSession(sessionId);
|
const session = useSession(sessionId, directory);
|
||||||
const status = useSessionStatus(sessionId);
|
const status = useSessionStatus(sessionId, directory);
|
||||||
const lastMessage = useLastMessageSnapshot(sessionId);
|
const lastMessage = useLastMessageSnapshot(sessionId, directory);
|
||||||
|
|
||||||
const isIdle = !status || status.type === 'idle';
|
const isIdle = !status || status.type === 'idle';
|
||||||
const payload = getSessionAssist(session);
|
const payload = getSessionAssist(session);
|
||||||
|
|||||||
@@ -43,7 +43,43 @@ const ASSIST_SYSTEM_PROMPT = [
|
|||||||
'You assist a user who chats with a coding agent. Based on the conversation transcript, return exactly one JSON object and nothing else — no prose, no markdown, no code fences.',
|
'You assist a user who chats with a coding agent. Based on the conversation transcript, return exactly one JSON object and nothing else — no prose, no markdown, no code fences.',
|
||||||
'Shape: {"recap": string, "suggestion": string}',
|
'Shape: {"recap": string, "suggestion": string}',
|
||||||
'recap: at most 20 words. State the substance directly — the facts, result, or conclusion, plus the next move if there is one. NEVER narrate ("The assistant explained…", "The agent did…") — write the content itself, like a note the user jotted down.',
|
'recap: at most 20 words. State the substance directly — the facts, result, or conclusion, plus the next move if there is one. NEVER narrate ("The assistant explained…", "The agent did…") — write the content itself, like a note the user jotted down.',
|
||||||
'suggestion: the next message to send in this conversation, addressed TO the agent — a concise instruction or question that moves the work forward, e.g. "Run the tests and fix failures" / "Commit this". Imperative or question form. Never explain, never offer help, never say "you can".',
|
'suggestion: write ONE immediately sendable next user message addressed TO the coding agent.',
|
||||||
|
'The suggestion should be the most useful next step after the assistant\'s latest reply. It should help the user continue productively, not inspect already-known details.',
|
||||||
|
'Prefer suggestions that ask the agent to make a concrete improvement, implement something specific, validate the latest change, explain tradeoffs, improve the current approach, or continue from the current result.',
|
||||||
|
'Rules for suggestion:',
|
||||||
|
'- Output exactly one message the user could click and send without editing.',
|
||||||
|
'- Pick one best next action yourself.',
|
||||||
|
'- Do not include alternatives, choices, slash-separated options, or "or".',
|
||||||
|
'- Do not write "Do X or Y", "Ask whether...", "Maybe...", or "You could...".',
|
||||||
|
'- Do not ask for information the assistant already provided.',
|
||||||
|
'- Do not ask to see exact code, file paths, prompt locations, or implementation internals unless the assistant did not provide them and they are necessary for the next step.',
|
||||||
|
'- Do not produce generic workflow commands like "Run tests" unless testing is clearly the next unresolved step.',
|
||||||
|
'- Do not produce meta/debug requests that merely inspect the implementation.',
|
||||||
|
'- Use imperative or question form.',
|
||||||
|
'- Keep it concise.',
|
||||||
|
'Use these examples to understand how to choose the suggestion. Do not copy their topic or wording unless the current conversation is about the same thing.',
|
||||||
|
'Example 1:',
|
||||||
|
'Assistant reply summary:',
|
||||||
|
'The assistant already identified the file where the feature is implemented, explained what context is sent to the small model, and summarized the current prompt.',
|
||||||
|
'Bad suggestion:',
|
||||||
|
'"Show me the exact runtime.js code and where the prompt is built."',
|
||||||
|
'Why bad:',
|
||||||
|
'It asks for information the assistant already provided. It repeats inspection instead of moving to an improvement or decision.',
|
||||||
|
'Good suggestion:',
|
||||||
|
'"Suggest how to improve the prompt and context so the generated suggestion is more useful."',
|
||||||
|
'Why good:',
|
||||||
|
'It naturally continues from the analysis and asks for a concrete improvement.',
|
||||||
|
'Example 2:',
|
||||||
|
'Assistant reply summary:',
|
||||||
|
'The assistant implemented a timeline dialog redesign, listed concrete UI changes, and reported that type-check and lint passed.',
|
||||||
|
'Bad suggestion:',
|
||||||
|
'"Check whether scrolling or loading older messages works without jumps."',
|
||||||
|
'Why bad:',
|
||||||
|
'It contains an alternative. A suggestion chip must be one sendable message, not a choice the user has to edit.',
|
||||||
|
'Good suggestion:',
|
||||||
|
'"Check whether scrolling and loading older messages work without jumps."',
|
||||||
|
'Why good:',
|
||||||
|
'It picks a single validation request that the user can send immediately.',
|
||||||
'Both values MUST be written in the same language as the conversation text itself. Ignore any other language preferences or personalization you may have — only the conversation text decides the language.',
|
'Both values MUST be written in the same language as the conversation text itself. Ignore any other language preferences or personalization you may have — only the conversation text decides the language.',
|
||||||
'Use double quotes for JSON strings, no trailing commas.',
|
'Use double quotes for JSON strings, no trailing commas.',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|||||||
Reference in New Issue
Block a user