
    Pmj+                        d Z ddlZddlmZmZmZ dZdefdZd Z	dee         fdZ
	 	 	 d#d
edeee                  dedee         def
dZdefdZdddddddddieddddddd
gddZddlmZmZ  ej        dded  ed!"           dS )$a^  
Clarify Tool Module - Interactive Clarifying Questions

Allows the agent to present structured multiple-choice questions or open-ended
prompts to the user. In CLI mode, choices are navigable with arrow keys. On
messaging platforms, choices are rendered as a numbered list.

Supports both single-select (radio) and multi-select (checkbox) modes via the
``multi_select`` parameter.

The actual user-interaction logic lives in the platform layer (cli.py for CLI,
gateway/run.py for messaging). This module defines the schema, validation, and
a thin dispatcher that delegates to a platform-provided callback.
    N)ListOptionalCallable   returnc                    | dS t          | t                    r|                                 S t          | t                    r[dD ]V}|                     |          }t          |t                    r*|                                r|                                c S WdS t          | t
          t          f          r1d                    d | D                                                       S t          |                                           S )u  Coerce a single choice into its user-facing display string.

    The schema declares choices as bare strings, but LLMs sometimes emit
    dict-shaped choices like ``[{"description": "..."}]``. A naive ``str(c)``
    turns the whole dict into its Python repr — ``{'description': '...'}`` —
    which then leaks onto every surface that renders the choice (CLI panel,
    Discord buttons, Telegram numbered list) AND is returned verbatim as the
    user's answer. Normalising here, at the one platform-agnostic entry point,
    fixes the whole class in one place instead of per-adapter.

    Dict unwrap order is the canonical LLM tool-call user-facing keys:
    ``label`` → ``description`` → ``text`` → ``title``. ``name`` and ``value``
    are deliberately excluded — they're component-shaped fields that could
    carry raw enum values or short identifiers, not human-readable labels. A
    dict with none of the canonical keys is dropped (returns ""), since a
    garbage label is worse than no choice at all.
    N )labeldescriptiontexttitle c              3   4   K   | ]}t          |          V  d S N_flatten_choice).0xs     8/home/thesage/.hermes/hermes-agent/tools/clarify_tool.py	<genexpr>z"_flatten_choice.<locals>.<genexpr>7   s*      66q**666666    )
isinstancestrstripdictgetlisttuplejoin)ckeyvs      r   r   r      s    $ 	yr!S wwyy!T < 	! 	!Cc

A!S!! !aggii !wwyy   r!dE]## ?xx66A66666<<>>>q66<<>>r   c                    ddl d}	                     |           }|j        }d|v p,t          fd|                                D                       }n# t
          t          f$ r d}Y nw xY w|r | |||          S  | ||          S )ub  Invoke the platform callback, passing multi_select if supported.

    Uses signature inspection (not a ``TypeError`` retry) to decide whether
    the callback accepts the ``multi_select`` keyword — a retry-on-TypeError
    approach would re-invoke a *compatible* callback that raised TypeError
    internally, potentially prompting the user twice.
    r   NFmulti_selectc              3   B   K   | ]}|j         j        j        k    V  d S r   )kind	ParameterVAR_KEYWORD)r   pinspects     r   r   z#_invoke_callback.<locals>.<genexpr>I   sA       8
 8
89AFg'338
 8
 8
 8
 8
 8
r   )r$   )r*   	signature
parametersanyvalues	TypeError
ValueError)callbackquestionchoicesr$   accepts_multisigparamsr*   s          @r   _invoke_callbackr7   ;   s     NNNM	))&&0 
C 8
 8
 8
 8
=C]]__8
 8
 8
 5
 5
 z"    
  Fx'EEEE8Hg&&&s   AA A-,A-c                    t          | t                    rd | D             S t          |                                           }|                    d          rL	 t          j        |          }t          |t                    rd |D             S n# t
          j        $ r Y nw xY wd |                    d          D             S )u  Parse a multi-select response into a list of cleaned choice strings.

    Handles three forms:
      - Already a list  →  stringify + strip each element
      - JSON array      →  parse and strip
      - Comma-separated →  split, strip, drop empties
    c                     g | ]D}t          |                                          #t          |                                          ES  r   r   )r   rs     r   
<listcomp>z0_parse_multi_select_response.<locals>.<listcomp>_   s9    GGG1AGAGGGr   [c                     g | ]D}t          |                                          #t          |                                          ES r:   r;   )r   r)   s     r   r=   z0_parse_multi_select_response.<locals>.<listcomp>h   s9    III1#a&&,,..IAIIIr   c                 ^    g | ]*}|                                 |                                 +S r:   )r   r   ss     r   r=   z0_parse_multi_select_response.<locals>.<listcomp>m   s-    ;;;!;AGGII;;;r   ,)	r   r   r   r   
startswithjsonloadsJSONDecodeErrorsplit)raw_responserawparseds      r   _parse_multi_select_responserL   V   s     ,%% HGGGGGG
l


!
!
#
#C ~~c 	Z__F&$'' JIIIIIIJ# 	 	 	D	 <;syy~~;;;;s   4B B! B!Fr2   r3   r$   r1   c                 |   | r|                                  st          d          S |                                  } |et          |t                    st          d          S d d |D             D             }t	          |          t
          k    r|dt
                   }|sd}|t          d          S 	 t          || ||          }n)# t          $ r}t          d|           cY d}~S d}~ww xY w|r|t          |          }n!t          |                                           }t          j        | ||dd	
          S )a  
    Ask the user a question, optionally with multiple-choice options.

    Args:
        question:     The question text to present.
        choices:      Up to 4 predefined answer choices. When omitted the
                      question is purely open-ended.
        multi_select: When True, the user can select multiple choices
                      (checkboxes).  The ``user_response`` in the output JSON
                      will be a list of strings instead of a single string.
                      Has no effect when ``choices`` is omitted.
        callback:     Platform-provided function that handles the actual UI
                      interaction.  Signature:
                      ``callback(question, choices, multi_select=False) -> str``.
                      The optional ``multi_select`` keyword is passed so the
                      platform can render checkboxes instead of radio buttons.
                      Injected by the agent runner (cli.py / gateway).

    Returns:
        JSON string with the user's response.
    zQuestion text is required.Nz"choices must be a list of strings.c                     g | ]}||S r:   r:   rA   s     r   r=   z clarify_tool.<locals>.<listcomp>   s    IIIqI1IIIr   c              3   4   K   | ]}t          |          V  d S r   r   )r   r    s     r   r   zclarify_tool.<locals>.<genexpr>   s*      CCaq11CCCCCCr   z8Clarify tool is not available in this execution context.zFailed to get user input: )r2   choices_offereduser_responseF)ensure_ascii)r   
tool_errorr   r   lenMAX_CHOICESr7   	ExceptionrL   r   rE   dumps)r2   r3   r$   r1   rI   excrQ   s          r   clarify_toolrY   p   s   6  88>>++ 86777~~H '4(( 	DBCCC JICC7CCCIIIw<<+%%l{l+G 	GTUUU>'(G\RR > > ><s<<========>  2+4\BBL))//11:"&  	   s   3C 
C,C'!C,'C,c                      dS )z>Clarify tool has no external requirements -- always available.Tr:   r:   r   r   check_clarify_requirementsr[      s    4r   clarifyu]  Ask the user a question when you need clarification, feedback, or a decision before proceeding. Supports three modes:

1. **Single-select multiple choice** — provide up to 4 choices. The user picks one or types their own answer via a 5th 'Other' option.
2. **Multi-select multiple choice** — set multi_select=true. The user can select multiple options via checkboxes. user_response will be a list of selected choices.
3. **Open-ended** — omit choices entirely. The user types a free-form response.

CRITICAL: when you are offering options, put each option ONLY in the `choices` array — NEVER enumerate the options inside the `question` text. The UI renders `choices` as selectable rows; options written into the question string render as dead prose the user can't pick. Right: question='Which deployment target?', choices=['staging', 'prod']. Wrong: question='Which target? 1) staging 2) prod', choices=[].

Use this tool when:
- The task is ambiguous and you need the user to choose an approach
- You want post-task feedback ('How did that work out?')
- You want to offer to save a skill or update memory
- A decision has meaningful trade-offs the user should weigh in on

Do NOT use this tool for simple yes/no confirmation of dangerous commands (the terminal tool handles that). Prefer making a reasonable default choice yourself when the decision is low-stakes.objectstringu   The question itself, and ONLY the question (e.g. 'Which deployment target?'). Do NOT embed the answer options here — pass them as separate elements in `choices`.)typer   arrayr_   a  REQUIRED whenever you are presenting selectable options: each distinct option is its own array element (up to 4). The UI renders these as pickable rows and auto-appends an 'Other (type your answer)' option. Omit this parameter entirely ONLY for a genuinely open-ended free-text question.)r_   itemsmaxItemsr   booleanzWhen true, the user can select MULTIPLE options (like checkboxes). The user_response will be a list of selected choices. When false (default), single selection (radio). Has no effect when choices is omitted (open-ended question).)r2   r3   r$   )r_   
propertiesrequired)namer   r,   )registryrS   c                     t          |                     dd          |                     d          |                     dd          |                    d                    S )Nr2   r	   r3   r$   Fr1   )r2   r3   r$   r1   )rY   r   )argskws     r   <lambda>rk     sU    |*b))##XXne44
##	 %  %  % r   u   ❓)rf   toolsetschemahandlercheck_fnemoji)NFN)__doc__rE   typingr   r   r   rU   r   r   r7   rL   boolrY   r[   CLARIFY_SCHEMAtools.registryrg   rS   registerr:   r   r   <module>rw      s     + + + + + + + + + +
 #    B' ' '6<$s) < < < <8 $(#'	@ @@d3i @ @ x 	@
 	@ @ @ @FD     	C0  !G    (+'S  "S +
 
>  LC" "5= =B 0 / / / / / / /  	% %
 (
     r   