Ë
    ãß²j‰=  ã                   ó   — d Z ddlZddlZddlmZmZmZmZ dZdZ	dZ
dZdZd	efd
„Zdee   d	ee   fd„Zded	efd„Zded	efd„Zd„ Zdefd„Zd	ee   fd„Zdefd„Zded	eee      fd„Zd	efd„Zd	efd„Zd:dee   dededee   d	ef
d„Zdee   ded	efd „Z	 	 d;dedeee      d!ed"eee      d#ee   d	efd$„Zd	efd%„Z d&d'e	› d(e› d)�d*d"d+d,e	d-d*d.d/id+d.d/ied0œd.d1id2œdgd3œd4œid"gd3œd5œZ!dd6l"m#Z#m$Z$  e#jJ                  d&d&e!d7„ e d8¬9«       y)<zÊClarify tool: structured multiple-choice / open-ended questions to the user.
Schema, validation and a thin dispatcher; the UI lives in a platform-provided
callback (cli.py, gateway/run.py, tui_gateway).é    N)ÚDictÚListÚOptionalÚCallableé   é   zrThe user did not provide a response within the time limit. Use your best judgement to make the choice and proceed.z(Recommended)z8Clarify tool is not available in this execution context.Úreturnc                 óD  ‡ ‡— t        ‰ t        «      r‰ j                  «       S t        ‰ t        «      rt	        ˆ ˆfd„dD «       d«      S t        ‰ t
        t        f«      r&dj                  d„ ‰ D «       «      j                  «       S ‰ €dS t        ‰ «      j                  «       S )aa  Coerce one choice to display text. LLMs sometimes emit dict-shaped choices and ``str(c)``
    would leak the repr onto every surface and back as the answer; unwrap order ``label`` >
    ``description`` > ``text`` > ``title`` (``name``/``value`` excluded: raw component enums,
    not labels). No match -> "" and dropped: no choice beats a garbage label.c              3   ó    •K  — | ]E  }t        ‰j                  |«      xŠt        «      r"‰j                  «       r‰j                  «       –— ŒG y ­w©N)Ú
isinstanceÚgetÚstrÚstrip)Ú.0ÚkÚcÚvs     €€ú7/home/ubuntu/.hermes/hermes-agent/tools/clarify_tool.pyú	<genexpr>z"_flatten_choice.<locals>.<genexpr>   s>   øè ø€ ò F 1Ü"¨¯©¨a« = 1´#Ô6¸1¿7¹7¼9ð —W‘W—Yñ Fùs   ƒAA)ÚlabelÚdescriptionÚtextÚtitleÚ Ú c              3   ó2   K  — | ]  }t        |«      –— Œ y ­wr   ©Ú_flatten_choice)r   Úxs     r   r   z"_flatten_choice.<locals>.<genexpr>   s   è ø€ Ò6¨qœ¨×*Ñ6ùó   ‚)r   r   r   ÚdictÚnextÚlistÚtupleÚjoin)r   r   s   `@r   r   r      s‹   ù€ ô
 �!”SÔØ�w‰w‹yÐÜ�!”TÔÜô FÐ(Qô FØGIóKð 	Kä�!”dœE�]Ô#Ø�x‰xÑ6°AÔ6Ó6×<Ñ<Ó>Ð>Ø�ˆ2Ð.¤ A£§¡£Ð.ó    Úchoicesc                 ó´   — | rt        | d   «      j                  «       nd}t        | «      dk  s|t        |«      k7  r| S |› dt        › �gt        | dd «      z   S )z™Suffix the first choice (schema says best-first) with RECOMMENDED_LABEL; idempotent,
    and a lone choice is left untouched (nothing to prefer it over).r   r   é   r   é   N)r   r   ÚlenÚstrip_recommendedÚRECOMMENDED_LABELr$   )r(   Úfirsts     r   Úmark_recommendedr0   #   sb   € ñ (/ŒC�˜‘
‹O×!Ñ!Ô#°B€EÜ
ˆ7ƒ|�aÒ˜5Ô$5°eÓ$<Ò<ØˆØˆg�QÔ(Ð)Ð*Ð+¬d°7¸1¸2°;Ó.?Ñ?Ð?r'   r   c                 óÜ   — t        | «      j                  «       }|j                  «       j                  t        j                  «       «      r!|dt        t        «        j                  «       S |S )zSRemove the recommendation label so presentation never leaks into ``user_response``.N)r   r   ÚcasefoldÚendswithr.   r,   )r   Ústrippeds     r   r-   r-   ,   sX   € ä�4‹y�‰Ó €HØ×ÑÓ×#Ñ#Ô$5×$>Ñ$>Ó$@ÔAØÐ1œ3Ô0Ó1Ð1Ð2×8Ñ8Ó:Ð:Ø€Or'   Únamec                 ó¸   — 	 t        j                  | «      j                  }||v xs  t        d„ |j                  «       D «       «      S # t        t        f$ r Y yw xY w)z­Signature-inspect (never a TypeError retry, which could re-prompt the user) whether
    ``callback`` takes ``name`` or ``**kwargs``; non-introspectable callables are legacy.Fc              3   ój   K  — | ]+  }|j                   t        j                  j                  k(  –— Œ- y ­wr   )ÚkindÚinspectÚ	ParameterÚVAR_KEYWORD)r   Úps     r   r   z!_accepts_kwarg.<locals>.<genexpr>;   s%   è ø€ Ò bÈQ §¡¬7×+<Ñ+<×+HÑ+HÕ!HÑ bùs   ‚13)r9   Ú	signatureÚ
parametersÚ	TypeErrorÚ
ValueErrorÚanyÚvalues)Úcallbackr5   Úparamss      r   Ú_accepts_kwargrE   4   s\   € ðÜ×"Ñ" 8Ó,×7Ñ7ˆð �6ˆ>ÒbœSÑ bÐRX×R_ÑR_ÓRaÔ bÓbÐbøô ”zÐ"ò Ùðús   ‚A ÁAÁAc                 óB   — t        | d«      r | |||¬«      S  | ||«      S )z@Invoke the platform callback, passing multi_select if supported.Úmulti_select)rG   )rE   )rC   Úquestionr(   rG   s       r   Ú_invoke_callbackrI   >   s(   € ä�h Ô/Ù˜ '¸ÔEÐEÙ�H˜gÓ&Ð&r'   Úrawc                 ó€   — 	 t        j                  | «      }t        ||«      r|S dS # t         j                  $ r Y yw xY w)zJ``json.loads(raw)`` when it decodes to an instance of ``kind``, else None.N)ÚjsonÚloadsÚJSONDecodeErrorr   )rJ   r8   Úparseds      r   Ú_json_asrP   E   sD   € ðÜ—‘˜C“ˆô   ¨Ô-ˆ6Ð7°4Ð7øô ×Ñò Ùðús   ‚' §=¼=c                 óP  — | }t        |t        «      sOt        |«      j                  «       }|j	                  d«      rt        |t        «      nd}|€|j                  d«      }|D �cg c]5  }t        |«      j                  «       sŒt        |«      j                  «       ‘Œ7 c}S c c}w )zRParse a list / JSON array / comma-separated reply into stripped non-empty strings.ú[Nú,)r   r$   r   r   Ú
startswithrP   Úsplit)Úraw_responseÚitemsrJ   Úrs       r   Ú_parse_multi_select_responserY   N   sx   € à€EÜ�eœTÔ"Ü�%‹j×ÑÓ ˆØ'*§~¡~°cÔ':”˜œdÔ#ÀˆØˆ=Ø—I‘I˜c“NˆEØ$)Ö<˜q¬S°«V¯\©\­^ŒC�‹F�L‰L�NÒ<Ð<ùÒ<s   Á&B#ÂB#Úmultic                 ój   — |r"t        | «      D �cg c]  }t        |«      ‘Œ c}S t        | «      S c c}w )zGStrip presentation (the label, multi-select JSON) from a locked answer.)rY   r-   )rJ   rZ   rX   s      r   Ú_clean_answerr\   Y   s0   € áPUÔ*FÀsÓ*KÖL QÔ˜aÕ ÒLÐqÔ[lÐmpÓ[qÐqùÒLs   �0c                 óX   — d„ | D «       D �cg c]  }|sŒ|‘Œ	 }}|dt          xs dS c c}w )zSFlatten, drop empties, cap at MAX_CHOICES; None when nothing survives (open-ended).c              3   ó2   K  — | ]  }t        |«      –— Œ y ­wr   r   )r   r   s     r   r   z!_clean_choices.<locals>.<genexpr>`   s   è ø€ Ò?°!œ?¨1×-Ñ?ùr!   N)ÚMAX_CHOICES)r(   ÚsÚcleaneds      r   Ú_clean_choicesrb   ^   s4   € á?°wÔ?ÖE�QÂ1ŠqÐE€GÐEØ�<”KÐ Ò( DÐ(ùò Fs   Œ'”'c                 ó`   — | d u xs) t        | t        «      xr | j                  «       t        k(  S r   )r   r   r   ÚTIMEOUT_RESPONSE)rJ   s    r   Ú_is_timeoutre   d   s)   € Ø�$ˆ;ÒTœ: c¬3Ó/ÒS°C·I±I³KÔCSÑ4SÐTr'   c                 óú  — t        | t        «      sy| syt        | «      t        kD  rddt        › d�fS g }t	        | «      D �]5  \  }}t        |t
        «      rd|i}t        |t        «      s
dd|› d�fc S t        |j                  d«      xs d	«      j                  «       }|s
dd|› d
�fc S |j                  d«      }|�%t        |t        «      s
dd|› d�fc S t        |«      }|j                  d|› �t        |j                  d«      xs d	«      j                  «       xs d||rt        t        |«      «      nd|rt        |«      ndt        |j                  d«      «      xr t        |«      dœ«       �Œ8 |dfS )ao  Validate the ``questions`` batch param -> ``(normalized, error)``; an empty list gives
    ``(None, None)`` (fall back to the single-question path). Entries carry ``qid`` (stable
    wire id ``q<index>`` surfaces key answers by; the model's ``id`` is unvalidated text, only
    echoed), ``question``, decorated ``choices``, bare ``choices_offered``, ``multi_select``.)Nz/questions must be an array of question objects.)NNNzquestions supports at most z items.rH   z
questions[z&] must be an object with a 'question'.r   z"].question must be non-empty text.r(   z].choices must be a list.ÚqÚidrG   )Úqidrh   rH   r(   Úchoices_offeredrG   )r   r$   r,   ÚMAX_QUESTIONSÚ	enumerater   r"   r   r   rb   Úappendr0   Úbool)Ú	questionsÚ
normalizedÚindexÚitemr   r(   s         r   Ú_normalize_questionsrs   j   s†  € ô
 �i¤Ô&ØFÙØÜ
ˆ9ƒ~œÒ%ØÐ2´=°/ÀÐIÐIÐIØ€JÜ  Ó+ó O‰ˆˆtÜ�dœCÔ Ø Ð%ˆDÜ˜$¤Ô%Ø˜: e WÐ,RÐSÐSÒSÜ�4—8‘8˜JÓ'Ò-¨2Ó.×4Ñ4Ó6ˆÙØ˜: e WÐ,NÐOÐOÒOØ—(‘(˜9Ó%ˆØÐÜ˜g¤tÔ,Ø˜z¨%¨Ð0IÐJÐJÒJÜ$ WÓ-ˆGØ×ÑØ�u�g�;¤c¨$¯(©(°4«.Ò*>¸BÓ&?×&EÑ&EÓ&GÒ&OÈ4Ð]aÙ:AÔ'¬¨W«Ô6ÀtÙ07œt Gœ}¸TÜ  §¡¨.Ó!9Ó:ÒL¼tÀG»}ñ	Nö 	OðOð$ �tÐÐr'   rp   ÚanswersÚ	timed_outÚnoticec                 ó  — g }| D ]R  }|j                  |d   «      }|j                  i |d   rd|d   ini ¥|d   |d   |rt        ||d   «      nddœ¥«       ŒT d|i}|rd	|d
<   |rt        |«      |d<   t	        j
                  |d¬«      S )aE  Batch result JSON; unanswered -> "". The top-level ``timed_out`` flag (present only when
    true) tells the agent whether blanks are deliberate skips or the user walking away; ``notice``
    (surface-supplied, only beside ``timed_out``) says WHY the wait ended, so an undeliverable
    prompt never reads as user inactivity.ri   rh   rH   rj   rG   r   ©rH   rj   Úuser_responseÚ	responsesTru   rv   F©Úensure_ascii)r   rm   r\   r   rL   Údumps)rp   rt   ru   rv   rz   ÚentryrJ   Úresults           r   Ú_batch_resultr€   ‹   sÀ   € ð
 €IØò XˆØ�k‰k˜% ™,Ó'ˆØ×Ñð WØ&+¨D¢k��e˜D‘kÑ"°rðWà˜jÑ)¸eÐDUÑ>VÙJMœ]¨3°°nÑ0EÔFÐSUòWõ 	XðXð "-¨iÐ 8€FÙØ"ˆˆ{ÑÙÜ" 6›{ˆF�8ÑÜ�:‰:�f¨5Ô1Ð1r'   rH   c                 óä  — i }d}d}t        |d«      r� ||d| ¬«      }t        |«      }t        |t        «      rt	        |t
        «      }t        |t
        «      rIt        |j                  d«      xs i «      }t        |j                  d«      «      }|j                  d«      }t        | |||«      S | D ]0  }t        ||d   |d	   |d
   «      }t        |«      rd} n
|||d   <   Œ2 t        | ||«      S )a  Dispatch a validated batch. Batch-capable callbacks (``questions`` kwarg) get the
    whole list once and reply ``{"answers": {qid: raw}, "timed_out"?}`` as a dict or JSON
    string (the tui_gateway bridge only carries strings); any other falsy/unparseable reply
    is a cancel-all (mirrors the single-question skip). Legacy callbacks are looped per
    question: an empty answer is a skip, a timeout (``None`` or the sentinel) means the user
    walked away so the loop aborts instead of pestering them; earlier answers are kept.FNro   )ro   rt   ru   rv   rH   r(   rG   Tri   )
rE   re   r   r   rP   r"   r   rn   r€   rI   )rp   rC   rH   rt   ru   rv   rJ   r~   s           r   Ú
_run_batchr‚   Ÿ   sý   € ð €GØ€IØ€FÜ�h Ô,Ù�x °Ô<ˆÜ Ó$ˆ	Ü�cœ3ÔÜ˜3¤Ó%ˆCÜ�cœ4Ô Ü˜3Ÿ7™7 9Ó-Ò3°Ó4ˆGÜ˜SŸW™W [Ó1Ó2ˆIØ—W‘W˜XÓ&ˆFÜ˜Z¨°)¸VÓDÐDØò $ˆÜ˜x¨¨zÑ):¸EÀ)Ñ<LÈeÐTbÑNcÓdˆÜ�sÔØˆIÙØ #ˆ��e‘Òð$ô ˜ W¨iÓ8Ð8r'   rG   ro   rC   c           	      ó¢  — |�Wt        |«      \  }}|rt        |«      S |r:|€t        t        «      S 	 t        ||t	        | xs d«      j                  «       «      S | r| j                  «       st        d«      S | j                  «       } |�&t        |t        «      st        d«      S t        |«      }|€t        t        «      S |�t        |«      nd}	 t        || ||«      }	t        j                  | |t        |	|xr |du«      dœd¬«      S # t        $ r}t        d|› �«      cY d}~S d}~ww xY w# t        $ r}t        d|› �«      cY d}~S d}~ww xY w)	aõ  Ask one question (``question``/``choices``/``multi_select``) or a batch (``questions``
    wins when non-empty). ``callback(question, choices, multi_select=False) -> str`` is
    platform injected (batch-capable ones also take ``questions=``). Returns result JSON.

    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. questions:    Up to 5 independent questions asked as
    one batch (issue #18450). When present (non-empty), the single ``question``/``choices``/``multi_select``
    parameters are ignored and the result JSON is ``{"responses": [...]}`` (plus ``"timed_out": true`` when
    the user stopped answering partway). callback:     Platform-provided function that handles the actual UI
    interaction. Batch-capable platforms additionally accept a ``questions`` keyword and receive the
    normalized list in one call; platforms without it are looped one question at a time. Injected by the
    agent runner (cli.py / gateway).
    Nr   zFailed to get user input: u�   No question provided. Pass questions=[{question: '...', choices?: [...], multi_select?: bool}, ...] â€” a single question is a one-entry array.z"choices must be a list of strings.rx   Fr{   )rs   Ú
tool_errorÚ_UNAVAILABLEr‚   r   r   Ú	Exceptionr   r$   rb   r0   rI   rL   r}   r\   )
rH   r(   rG   ro   rC   rp   ÚerrorÚexcÚshownrV   s
             r   Úclarify_toolrŠ   ¼   so  € ð" ÐÜ0°Ó;Ñˆ
�EÙÜ˜eÓ$Ð$ÙØÐÜ!¤,Ó/Ð/ðFÜ! *¨h¼¸HºNÈÓ8K×8QÑ8QÓ8SÓTÐTñ ˜8Ÿ>™>Ô+Üð 2ó 3ð 	3ð �~‰~Ó€HØÐÜ˜'¤4Ô(ÜÐBÓCÐCÜ  Ó)ˆØÐÜœ,Ó'Ð'à)0Ð)<Ô˜WÔ%À$€Eð>Ü'¨°(¸EÀ<ÓPˆô �:‰: 8ÀÜ(5°lÀLÒDhÐU\ÐdhÐUhÓ(iñkà#(ô*ð *øô) ò FÜ!Ð$>¸s¸eÐ"DÓEÕEûðFûô$ ò >ÜÐ6°s°eÐ<Ó=Õ=ûð>ús;   ²'D	 ÃD- Ä		D*ÄD%ÄD*Ä%D*Ä-	EÄ6E	ÅEÅ	Ec                   ó   — y)z>Clarify tool has no external requirements -- always available.T© rŒ   r'   r   Úcheck_clarify_requirementsr�   ï   s   € àr'   Úclarifyz‘Ask the user one or more questions when you need a decision, clarification, or feedback before proceeding. Pass every question in `questions` (1-uö    entries) â€” a single question is a one-entry array, and several INDEPENDENT questions belong in ONE call (one form beats a chain of clarify calls; if one answer would change another question, ask separately). Per question: single-select (up to u�   choices â€” put your recommended option FIRST, the UI marks it '(Recommended)' and auto-appends an 'Other' free-text row), multi-select (multi_select=true), or open-ended (omit choices). Options go ONLY in `choices`, never enumerated inside the question text (choices render as pickable rows; options written into the question are dead prose the user can't click). Result: {responses: [...]} in question order (plus timed_out=true, and a notice saying why, if the user stopped part-way or the prompt could not be delivered). Prefer deciding low-stakes questions yourself; don't use this for dangerous-command confirmation (the terminal tool handles that).ÚobjectÚarrayr+   zÎThe question(s). Each: question text (options excluded), optional choices (recommended first; omit for free-text), optional multi_select. Responses come back in question order with the question text echoed.ÚtypeÚstring)r‘   rW   ÚmaxItemsÚboolean)rH   r(   rG   )r‘   Ú
propertiesÚrequired)r‘   ÚminItemsr“   r   rW   )r5   r   r>   )Úregistryr„   c           	      ó¼   — t        | j                  dd«      | j                  d«      | j                  dd«      | j                  d«      |j                  d«      ¬«      S )	NrH   r   r(   rG   Fro   rC   )rH   r(   rG   ro   rC   )rŠ   r   )ÚargsÚkws     r   ú<lambda>rœ   6  sK   € œ|Ø—‘˜* bÓ)Ø—‘˜Ó#Ø—X‘X˜n¨eÓ4Ø—(‘(˜;Ó'Ø—‘˜
Ó#ô %€ r'   u   â�“)r5   ÚtoolsetÚschemaÚhandlerÚcheck_fnÚemojir   )NFNN)&Ú__doc__r9   rL   Útypingr   r   r   r   r_   rk   rd   r.   r…   r   r   r0   r-   rn   rE   rI   rP   rY   r\   r$   rb   re   r%   rs   r"   r€   r‚   rŠ   r�   ÚCLARIFY_SCHEMAÚtools.registryr˜   r„   ÚregisterrŒ   r'   r   ú<module>r§      se  ðñ3ó Û ß 1Ó 1à€Ø€ðNÐ ð $Ð ØI€ð/˜#ó /ð@˜d 3™ið @¨D°©Ió @ð˜Cð  Có ðc 3ð c¨4ó cò'ð8�#ó 8ð=°$°s±)ó =ðr˜dó rð
)˜Dð ) X¨d°3©iÑ%8ó )ðU˜ó Uð uó ñB2˜d 4™jð 2°4ð 2ÀDð 2ÐRZÐ[^ÑR_ð 2Ðknó 2ð(9˜4 ™:ð 9¸3ð 9À3ó 9ð: [`ØX\ñ0*˜3ð 0*¨°$°s±)Ñ)<ð 0*ÐSWð 0*Ø$ T¨$¡ZÑ0ð0*ØCKÈHÑCUð0*Øadó0*ðf Dó ð ð	à*˜Oð , ð !,˜}ð 
-9ð	9ð& àØØØ)ð;ð %à%+¨XÐ$6à$+Ø&,¨hÐ%7Ø(3ñ$ð
 *0°Ð(;ñ#ð ", ññð
ð@ !�MñE#ñ+9€÷x 0à €× Ñ Ø	ØØñ%ð (Ø
ör'   