mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-21 16:18:55 +00:00
fix(desktop): stop button sends interrupt to wrong session + stale events re-arm busy (#66485)
Co-authored-by: Brooklyn Nicholson <brooklyn.bb.nicholson@gmail.com>
This commit is contained in:
parent
bcea5371c8
commit
11d36232c0
2 changed files with 40 additions and 10 deletions
|
|
@ -249,6 +249,15 @@ export function useGatewayEventHandler(deps: GatewayEventDeps) {
|
|||
}
|
||||
|
||||
if (busy) {
|
||||
// Don't re-arm busy from a stale session.info if the user
|
||||
// just clicked Stop (interrupted=true). The backend's
|
||||
// cooperative interrupt may not have propagated yet, so
|
||||
// running is still true in the heartbeat. The turn's
|
||||
// finally block will emit running=false to clear busy.
|
||||
if (state.interrupted) {
|
||||
return state
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
busy,
|
||||
|
|
@ -307,14 +316,27 @@ export function useGatewayEventHandler(deps: GatewayEventDeps) {
|
|||
triggerHaptic('streamStart')
|
||||
}
|
||||
|
||||
updateSessionState(sessionId, state => ({
|
||||
...state,
|
||||
busy: true,
|
||||
awaitingResponse: true,
|
||||
sawAssistantPayload: false,
|
||||
interrupted: false,
|
||||
turnStartedAt: Date.now()
|
||||
}))
|
||||
updateSessionState(sessionId, state => {
|
||||
// If the user clicked Stop (cancelRun set interrupted=true), don't
|
||||
// let a stale message.start from a chained turn (goal follow-up,
|
||||
// completion drain) or an in-flight LLM response re-arm busy.
|
||||
// The interrupt is user intent — the backend's cooperative cancel
|
||||
// may not have propagated yet, so its events are stale. The turn's
|
||||
// finally block will emit session.info with running=false to clear
|
||||
// busy for real once the agent loop actually exits.
|
||||
if (state.interrupted) {
|
||||
return state
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
busy: true,
|
||||
awaitingResponse: true,
|
||||
sawAssistantPayload: false,
|
||||
interrupted: false,
|
||||
turnStartedAt: Date.now()
|
||||
}
|
||||
})
|
||||
|
||||
if (isActiveEvent) {
|
||||
setTurnStartedAt(Date.now())
|
||||
|
|
|
|||
|
|
@ -514,7 +514,16 @@ export function usePromptActions({
|
|||
)
|
||||
|
||||
const cancelRun = useCallback(async () => {
|
||||
const sessionId = activeSessionId || activeSessionIdRef.current
|
||||
// Read from the ref, not the closure-captured `activeSessionId`. The
|
||||
// actions bag is a stable ref mutated in place (Object.assign on each
|
||||
// ContribWiring render), and ChatRoutesSurface is memoized on that stable
|
||||
// ref — so it does NOT re-render when activeSessionId changes, which means
|
||||
// the ChatView element's onCancel prop holds a stale cancelRun closure.
|
||||
// The closure's `activeSessionId` can be a previous session's id (or null
|
||||
// from a new-chat draft), sending session.interrupt to the wrong session.
|
||||
// The ref is updated via useEffect on every activeSessionId change, so it
|
||||
// always reflects the current session — same pattern submitText uses.
|
||||
const sessionId = activeSessionIdRef.current
|
||||
|
||||
const releaseBusy = () => {
|
||||
setMutableRef(busyRef, false)
|
||||
|
|
@ -589,7 +598,6 @@ export function usePromptActions({
|
|||
notifyError(stopError, copy.stopFailed)
|
||||
}
|
||||
}, [
|
||||
activeSessionId,
|
||||
activeSessionIdRef,
|
||||
busyRef,
|
||||
copy.stopFailed,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue