akseljoonas HF Staff commited on
Commit
3b6a2ca
·
1 Parent(s): 4b24bd9

Show cancelled prompt in shimmer status bar instead of chat input placeholder

Browse files
frontend/src/components/Chat/ActivityStatusBar.tsx CHANGED
@@ -32,6 +32,7 @@ function statusLabel(status: ActivityStatus): string {
32
  return base;
33
  }
34
  case 'waiting-approval': return 'Waiting for approval';
 
35
  default: return '';
36
  }
37
  }
@@ -59,7 +60,7 @@ export default function ActivityStatusBar() {
59
  animation: `${shimmer} 4s ease-in-out infinite`,
60
  }}
61
  >
62
- {label}…
63
  </Typography>
64
  </Box>
65
  );
 
32
  return base;
33
  }
34
  case 'waiting-approval': return 'Waiting for approval';
35
+ case 'cancelled': return 'What should the agent do instead?';
36
  default: return '';
37
  }
38
  }
 
60
  animation: `${shimmer} 4s ease-in-out infinite`,
61
  }}
62
  >
63
+ {label}{activityStatus.type !== 'cancelled' && ''}
64
  </Typography>
65
  </Box>
66
  );
frontend/src/components/SessionChat.tsx CHANGED
@@ -5,7 +5,7 @@
5
  * runs — processing events — but only the active session renders visible
6
  * UI (MessageList + ChatInput).
7
  */
8
- import { useCallback, useEffect, useState } from 'react';
9
  import { useAgentChat } from '@/hooks/useAgentChat';
10
  import { useAgentStore } from '@/store/agentStore';
11
  import { useSessionStore } from '@/store/sessionStore';
@@ -24,8 +24,6 @@ export default function SessionChat({ sessionId, isActive, onSessionDead }: Sess
24
  const { isConnected, isProcessing, activityStatus, updateSession } = useAgentStore();
25
  const { updateSessionTitle } = useSessionStore();
26
 
27
- const [wasCancelled, setWasCancelled] = useState(false);
28
-
29
  const { messages, sendMessage, stop, status, undoLastTurn, approveTools } = useAgentChat({
30
  sessionId,
31
  isActive,
@@ -57,11 +55,11 @@ export default function SessionChat({ sessionId, isActive, onSessionDead }: Sess
57
  return () => document.removeEventListener('visibilitychange', onVisible);
58
  }, [isActive, sessionId]);
59
 
60
- // Wrap stop to track cancellation
61
  const handleStop = useCallback(() => {
62
  stop();
63
- setWasCancelled(true);
64
- }, [stop]);
65
 
66
  // SDK status is the ground truth — if it's streaming/submitted, agent is busy
67
  const sdkBusy = status === 'streaming' || status === 'submitted';
@@ -71,8 +69,7 @@ export default function SessionChat({ sessionId, isActive, onSessionDead }: Sess
71
  async (text: string) => {
72
  if (!text.trim() || busy) return;
73
 
74
- setWasCancelled(false);
75
- updateSession(sessionId, { isProcessing: true });
76
  sendMessage({ text: text.trim(), metadata: { createdAt: new Date().toISOString() } });
77
 
78
  // Auto-title the session from the first user message
@@ -114,9 +111,7 @@ export default function SessionChat({ sessionId, isActive, onSessionDead }: Sess
114
  placeholder={
115
  activityStatus.type === 'waiting-approval'
116
  ? 'Approve or reject pending tools first...'
117
- : wasCancelled
118
- ? 'What should the agent do instead?'
119
- : undefined
120
  }
121
  />
122
  </>
 
5
  * runs — processing events — but only the active session renders visible
6
  * UI (MessageList + ChatInput).
7
  */
8
+ import { useCallback, useEffect } from 'react';
9
  import { useAgentChat } from '@/hooks/useAgentChat';
10
  import { useAgentStore } from '@/store/agentStore';
11
  import { useSessionStore } from '@/store/sessionStore';
 
24
  const { isConnected, isProcessing, activityStatus, updateSession } = useAgentStore();
25
  const { updateSessionTitle } = useSessionStore();
26
 
 
 
27
  const { messages, sendMessage, stop, status, undoLastTurn, approveTools } = useAgentChat({
28
  sessionId,
29
  isActive,
 
55
  return () => document.removeEventListener('visibilitychange', onVisible);
56
  }, [isActive, sessionId]);
57
 
58
+ // Wrap stop to show cancelled shimmer
59
  const handleStop = useCallback(() => {
60
  stop();
61
+ updateSession(sessionId, { activityStatus: { type: 'cancelled' } });
62
+ }, [stop, updateSession, sessionId]);
63
 
64
  // SDK status is the ground truth — if it's streaming/submitted, agent is busy
65
  const sdkBusy = status === 'streaming' || status === 'submitted';
 
69
  async (text: string) => {
70
  if (!text.trim() || busy) return;
71
 
72
+ updateSession(sessionId, { isProcessing: true, activityStatus: { type: 'thinking' } });
 
73
  sendMessage({ text: text.trim(), metadata: { createdAt: new Date().toISOString() } });
74
 
75
  // Auto-title the session from the first user message
 
111
  placeholder={
112
  activityStatus.type === 'waiting-approval'
113
  ? 'Approve or reject pending tools first...'
114
+ : undefined
 
 
115
  }
116
  />
117
  </>
frontend/src/store/agentStore.ts CHANGED
@@ -50,7 +50,8 @@ export type ActivityStatus =
50
  | { type: 'thinking' }
51
  | { type: 'tool'; toolName: string; description?: string }
52
  | { type: 'waiting-approval' }
53
- | { type: 'streaming' };
 
54
 
55
  /** State that is tracked per-session (each session has its own copy). */
56
  export interface PerSessionState {
@@ -222,7 +223,7 @@ export const useAgentStore = create<AgentStore>()((set, get) => ({
222
  // Apply the processing→idle side effect
223
  const processingCleared = 'isProcessing' in updates && !updates.isProcessing;
224
  if (processingCleared) {
225
- if (updated.activityStatus.type !== 'waiting-approval') {
226
  updated.activityStatus = { type: 'idle' };
227
  }
228
  }
@@ -300,7 +301,7 @@ export const useAgentStore = create<AgentStore>()((set, get) => ({
300
 
301
  setProcessing: (isProcessing) => {
302
  const current = get().activityStatus;
303
- const preserveStatus = current.type === 'waiting-approval';
304
  set({ isProcessing, ...(!isProcessing && !preserveStatus ? { activityStatus: { type: 'idle' } } : {}) });
305
  },
306
  setConnected: (isConnected) => set({ isConnected }),
 
50
  | { type: 'thinking' }
51
  | { type: 'tool'; toolName: string; description?: string }
52
  | { type: 'waiting-approval' }
53
+ | { type: 'streaming' }
54
+ | { type: 'cancelled' };
55
 
56
  /** State that is tracked per-session (each session has its own copy). */
57
  export interface PerSessionState {
 
223
  // Apply the processing→idle side effect
224
  const processingCleared = 'isProcessing' in updates && !updates.isProcessing;
225
  if (processingCleared) {
226
+ if (updated.activityStatus.type !== 'waiting-approval' && updated.activityStatus.type !== 'cancelled') {
227
  updated.activityStatus = { type: 'idle' };
228
  }
229
  }
 
301
 
302
  setProcessing: (isProcessing) => {
303
  const current = get().activityStatus;
304
+ const preserveStatus = current.type === 'waiting-approval' || current.type === 'cancelled';
305
  set({ isProcessing, ...(!isProcessing && !preserveStatus ? { activityStatus: { type: 'idle' } } : {}) });
306
  },
307
  setConnected: (isConnected) => set({ isConnected }),