Skip to content

fix(ui): unblock ask_user promise when Stop is clicked (fixes #183)#411

Open
octo-patch wants to merge 1 commit intoalibaba:mainfrom
octo-patch:fix/issue-183-panel-close-stuck-on-ask-user
Open

fix(ui): unblock ask_user promise when Stop is clicked (fixes #183)#411
octo-patch wants to merge 1 commit intoalibaba:mainfrom
octo-patch:fix/issue-183-panel-close-stuck-on-ask-user

Conversation

@octo-patch
Copy link
Copy Markdown
Contributor

Fixes #183

Problem

When the agent invokes the ask_user tool and the user clicks Stop before typing an answer, the panel becomes permanently stuck:

  1. Panel.#askUser() returns a Promise and stores only its resolve callback.
  2. The LLM layer (OpenAIClient) awaits tool.execute(), which in turn awaits onAskUser()without passing the abort signal to that inner await.
  3. Clicking Stop calls agent.stop()abortController.abort(), which cancels the outgoing HTTP request but has no effect on an already-running tool execution.
  4. As a result, execute() stays blocked at await tool.execute() with status = 'running' forever.
  5. The action button never morphs from ■ (stop) to X (close), so the user cannot close the panel.

Solution

Introduce #cancelUserAnswer() which rejects the pending #askUser promise with an AbortError-shaped error (error.name = 'AbortError').

The LLM retry layer already checks rawError.name === 'AbortError' to skip retries for user aborts, so the error propagates immediately to execute()'s catch block → #onDone(false)status = 'error'. The panel button then becomes X, allowing the user to close it.

Changes in packages/ui/src/panel/Panel.ts:

  • Add #userAnswerRejecter field alongside the existing #userAnswerResolver.
  • Capture reject in #askUser().
  • Add #cancelUserAnswer() — rejects the promise, clears state, and removes temporary question cards from the history panel.
  • Call #cancelUserAnswer() in #handleActionButton() before agent.stop() when #isWaitingForUserAnswer is true.
  • Call #cancelUserAnswer() in dispose() (after removing event listeners) to prevent memory leaks if the agent is disposed while waiting for user input.
  • Clear #userAnswerRejecter in reset() and #handleUserAnswer().

Testing

  1. Start a task that triggers the ask_user tool (e.g. configure the agent so the LLM asks a clarifying question).
  2. When the question prompt appears in the panel, click ■ (Stop) without typing anything.
  3. Before fix: the button stays ■ and the panel cannot be closed.
  4. After fix: the button changes to X and clicking it closes the panel cleanly.

…#183)

When the agent invokes the ask_user tool and the user clicks Stop before
answering, the promise returned by Panel.#askUser() was never resolved or
rejected. The LLM layer awaits tool execution before checking the abort
signal, so the execute() loop stayed permanently blocked with
status='running', leaving the panel stuck on the stop button with no way
to close it.

Fix: introduce #cancelUserAnswer() which rejects the pending promise with
an AbortError-shaped error. The LLM layer recognises this as a non-retryable
user abort, propagates it to the execute() catch block, and calls
#onDone(false), transitioning status to 'error'. The panel button then
changes to X and the user can close it normally.

Also cancel any pending promise in Panel.dispose() to prevent memory leaks
when the agent is disposed while waiting for user input.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 默认的UI,当询问用户问题的时候,点击停止和关闭没有关闭pannel

1 participant