Summary
SSEOutputHandler.confirm_tool_execution resets _confirm_result = False and replaces _confirm_event on entry. If the HTTP handler calls resolve_tool_confirmation(True) before confirm_tool_execution runs its reset, the approval is clobbered.
Location
src/gaia/ui/sse_handler.py:690-745
def confirm_tool_execution(self, ...):
confirm_id = str(uuid.uuid4())
self._confirm_event = threading.Event() # ← overwrites any event resolve_tool_confirmation created
self._confirm_result = False # ← discards any prior approval
...
def resolve_tool_confirmation(self, approved: bool) -> bool:
if self._confirm_event is None:
self._confirm_event = threading.Event() # ← creates orphan event
self._confirm_result = approved
self._confirm_event.set()
return True
Reachability
Low. The intended order is:
confirm_tool_execution emits permission_request SSE event.
- Frontend renders modal, user clicks.
resolve_tool_confirmation is called.
So step 3 can only realistically run after step 1 has done its resets. The bug needs an out-of-order HTTP call (e.g., a replay or a speculative client) to manifest.
Suggested Fix
Either (a) create the event in __init__ and never replace it, only .clear() it; or (b) make resolve_tool_confirmation a no-op when _confirm_event is None (drop the event-creation branch).
(b) is one line:
def resolve_tool_confirmation(self, approved: bool) -> bool:
if self._confirm_event is None:
return False # no pending request
self._confirm_result = approved
self._confirm_event.set()
return True
The HTTP endpoint at src/gaia/ui/routers/chat.py:180-196 already 404s on missing handler; this just tightens the internal contract.
Impact
- Low severity — requires an unusual client/proxy replay.
- Fix is trivial and pure cleanup; safe to bundle with other SSE hardening.
Summary
SSEOutputHandler.confirm_tool_executionresets_confirm_result = Falseand replaces_confirm_eventon entry. If the HTTP handler callsresolve_tool_confirmation(True)beforeconfirm_tool_executionruns its reset, the approval is clobbered.Location
src/gaia/ui/sse_handler.py:690-745Reachability
Low. The intended order is:
confirm_tool_executionemitspermission_requestSSE event.resolve_tool_confirmationis called.So step 3 can only realistically run after step 1 has done its resets. The bug needs an out-of-order HTTP call (e.g., a replay or a speculative client) to manifest.
Suggested Fix
Either (a) create the event in
__init__and never replace it, only.clear()it; or (b) makeresolve_tool_confirmationa no-op when_confirm_event is None(drop the event-creation branch).(b) is one line:
The HTTP endpoint at
src/gaia/ui/routers/chat.py:180-196already 404s on missing handler; this just tightens the internal contract.Impact