fix(chat): make agent-prompted modals actionable#76
Merged
Conversation
The Approval / Clarification / Sudo / Secret modals rendered a header
with no buttons, then click-outside closed them silently and the chat
locked: STOP button red, Send dead, recovered only by refresh.
Two layered bugs:
1. src/js/chat/gateway.js called showModal with {body, confirmText,
cancelText, onConfirm, onCancel} — an older shape. The current
modal.js takes {message, inputs, buttons} and returns a Promise.
Only ``title`` survived the mismatch, so buttons[] stayed empty and
neither callback fired on close. Ported the four helpers to await
the Promise; null result (click-outside / Cancel) sends a negative
response so the agent unblocks via chat.done/chat.error.
2. lib/tui-gateway-bridge.js sent ``approval.respond`` without
session_id; the gateway's _sess() rejected it with 4001 "session
not found" (surfaced as ⚠️ in chat). It also sent {approve, command}
where the gateway reads {choice: "approve"|"deny"}, and
``clarify.respond`` sent {text, choice} where the gateway reads
{answer}. Bridge now tracks _internalSid (set on every prompt.submit)
and translates each respond into the keys the gateway expects.
Regression-covered by test/chat-approval-modal.test.js.
Owner
✅ Tested & MergedDeploy ke staging: ✅ Test results:
Merge: Merged to Thanks @tomekpanek! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
When the agent asks for approval (or any of the four modal prompts — Approval / Clarification / Sudo / Secret), HCI shows a modal with the title text but no buttons. Clicking outside dismisses the modal but the chat locks: Send button is red STOP state, Stop button does nothing, typing works but nothing posts. Only a refresh recovers it, and the original tool call never completes.
After buttons work, picking a choice still produces
⚠️ session not found(twice) and the agent prompts the user again as if nothing was selected.Root causes
Two layered bugs that compound:
1. Modal API mismatch (
src/js/chat/gateway.js)The four agent-prompted modal helpers were written against an older
showModalcontract:The current
src/js/components/modal.jstakes:Only
titleis shared.body/confirmText/cancelText/onConfirm/onCancelare silently dropped, so the modal renders a header with an emptymodal-actionsdiv (= no buttons). Click-outside resolves the Promise tonullbut the oldonCancelcallback never fires, so the agent stays blocked on a response that the UI cannot send. The chat lock never clears becausechat.done/chat.errordoesn't arrive until the agent gets a response.2. Wrong param shape on the bridge → gateway hop (
lib/tui-gateway-bridge.js)approval.respondwas sent withoutsession_id. The gateway's handler calls_sess(params, rid)which looks upparams[\"session_id\"]in_sessions; missing key returns the 4001 "session not found" error, surfaced as the amber{ approve: true|false, command }; gateway reads{ choice: \"approve\"|\"deny\", command }.clarify.respondsent{ request_id, text, choice }; gateway's_respondreadsparams[\"answer\"], so until now the agent always saw an empty answer and followed up with "looks like you haven't picked yet."(
sudo.respondandsecret.respondalready used the keys the gateway expects.)Fix
showApprovalModal,showClarifyModal,showSudoModal,showSecretModal) to the modern{ message, inputs, buttons }shape,awaitthe Promise, and translate anullresult (click-outside / Cancel) into a negative response so the agent unblocks naturally.buttons: choices.map(c => ({ text: c, value: c }))instead of injecting<button>markup into the body._internalSid(updated on everyprompt.submitin bothchatStartandchatSend).respondApprovalnow sends{ session_id: _internalSid, choice: approve ? 'approve' : 'deny', command }.respondClarifysends{ request_id, answer: choice ?? text ?? '' }.Test plan
node --test test/chat-approval-modal.test.js— 2/2 pass (new regression test, fails on `main`)node --test test/*.test.js— no new regressionsnpm run build— clean