§
    ÷žyjåy  ã                   óf  — d Z ddlZddlZddlZddlZddlmZmZmZ ddl	m
Z
 ddlmZ ddlmZ  ej        e¦  «        Zeeegdf         Zeeegdf         Zeg ef         ZdZdZd	Zd
ZdZdddddddiidgddœdœdœZdZdeddddfZdefd„Z defd„Z!d edefd!„Z"d"edefd#„Z#d"edefd$„Z$d"edee         fd%„Z%d&edefd'„Z&d edee         fd(„Z'	 	 	 	 d:d"ed)ee(         d*ee         d+e)d,ee         dee         fd-„Z*dd.œd/„Z+	 d;d0ed"ed1ee         dee         fd2„Z,	 	 	 	 d:d0ed"ed*ee         d+e)d1ee         d,ee         ddfd3„Z-	 	 	 	 d:d0ed"ed*ee         d+e)d1ee         d,ee         ddfd4„Z.d5edefd6„Z/d0edefd7„Z0	 	 	 	 	 d<d0ed"ed8ee1         d*ee         d+e)d1ee         d,ee         ddfd9„Z2dS )=uã  Auto-generate short session titles from the user's opening message.

Two stages, both off the critical path:

1. **Instant** â€” a deterministic title derived from the first user message,
   written before the model is even called. Costs nothing, cannot fail, and
   means a session is named the moment it starts instead of after the first
   turn finishes (which measured p50 151s / p90 1212s on real sessions).
2. **Upgrade** â€” one small-model call that replaces the derived title with a
   proper one. Runs on a cheap/fast tier, with thinking disabled and the
   response constrained to a JSON object, so there is no reasoning preamble to
   strip and nothing to parse out of prose.

Provenance (``derived`` < ``llm`` < ``user``) is enforced by the storage layer,
so stage 2 can only ever replace stage 1, and neither can replace a name the
user typed. That ordering is the industry-standard one â€” Codex CLI encodes the
same ``custom > ai > fallback`` precedence in its session importer.
é    N)ÚAnyÚCallableÚOptional)Úcall_llm)ÚLEGACY_SUMMARY_PREFIX)Úflatten_message_textiè  é0   a‚  You name chat sessions. Given the user's opening message, write a title that lets them find this conversation again in a list.

Rules:
- 3 to 7 words, sentence case (capitalize only the first word and proper nouns).
- Name what the user wants DONE, not that they asked a question.
- Keep technical terms, filenames, numbers, and error codes exact.
- Drop filler words: the, this, my, a, an.
- No trailing punctuation, no quotes, no tool names, no 'Title:' prefix.
- Never answer the message. Name it.
- Always produce something, even for a bare greeting.
__LANGUAGE_RULE__
Good: {"title": "Fix login button on mobile"}
Good: {"title": "Postgres connection pool exhaustion"}
Good: {"title": "Friendly greeting"}
Too vague: {"title": "Code changes"}
Too long: {"title": "Investigate and fix the issue where the login button does not respond on mobile devices"}

Reply with JSON only: {"title": "..."}z=- Write the title in the same language as the user's message.z - Write the title in {language}.Újson_schemaÚsession_titleTÚobjectÚtitleÚtypeÚstringF)r   Ú
propertiesÚrequiredÚadditionalProperties)ÚnameÚstrictÚschema)r   r
   )
)z<command-message>z</command-message>)z<command-name>z</command-name>)z<command-args>z</command-args>)z<local-command-caveat>z</local-command-caveat>)z<local-command-stderr>z</local-command-stderr>)z<local-command-stdout>z</local-command-stdout>)z<task-notification>z</task-notification>)z<system-reminder>z</system-reminder>)z<ide_opened_file>z</ide_opened_file>)z<ide_selection>z</ide_selection>z[CONTEXT COMPACTIONz[Runtime note:z[System note:z[SYSTEM]z7[System: The active model for this chat has changed to Úreturnc                  ó  — 	 ddl m}  t           | ¦   «         pi                      d¦  «        pi                      di ¦  «                             dd¦  «        ¦  «                             ¦   «         S # t
          $ r Y dS w xY w)zDReturn configured title language, or empty string to match the user.r   ©Úload_config_readonlyÚ	auxiliaryÚtitle_generationÚlanguageÚ )Úhermes_cli.configr   ÚstrÚgetÚstripÚ	Exceptionr   s    ú;/home/ragecks/.hermes/hermes-agent/agent/title_generator.pyÚ_title_languager$   ”   s�   € ð	Ø:Ð:Ð:Ð:Ð:Ð:åØ"Ð"Ñ$Ô$Ð*¨×/Ò/°Ñ<Ô<ÐBÀßŠSÐ# RÑ(Ô(ßŠS�˜RÑ Ô ñ
ô 
÷ Š%‰'Œ'ð		øõ
 ð ð ð Øˆrˆrðøøøs   ‚A-A0 Á0
A>Á=A>c                  ó"  — 	 ddl m}  ddlm}  | ¦   «         }|                     d¦  «        pi                      d¦  «        pi } ||                     d¦  «        d¬¦  «        S # t
          $ r  t                               d	d¬
¦  «         Y dS w xY w)z=Return whether automatic session title generation is enabled.r   r   )Úis_truthy_valuer   r   ÚenabledT)Údefaultz'Failed to read title_generation.enabled©Úexc_info)r   r   Úutilsr&   r    r"   ÚloggerÚdebug)r   r&   ÚconfigÚtitle_configs       r#   Ú_auto_title_enabledr0   ¢   sÂ   € ðð 	;Ð:Ð:Ð:Ð:Ð:Ø)Ð)Ð)Ð)Ð)Ð)à%Ð%Ñ'Ô'ˆØŸ
š
 ;Ñ/Ô/Ð5°2×:Ò:Ð;MÑNÔNÐTÐRTˆØˆ˜|×/Ò/°	Ñ:Ô:ÀDÐIÑIÔIÐIøÝð ð ð Ý�ŠÐ>ÈˆÑNÔNÐNØˆtˆtðøøøs   ‚A!A$ Á$&BÂBÚtextc                 óŽ  — | sdS |                       ¦   «         }t          t          t          ¦  «        dz  ¦  «        D �]}|}t          D ]ï\  }}|                     ¦   «                              |¦  «        sŒ-|                     ¦   «                              |¦  «        }|dk    r*|t          |¦  «        d…                               ¦   «         }nk|t          |¦  «        |…                               ¦   «         }||t          |¦  «        z   d…                               ¦   «         }|p|                      ¦   «         } ||k    r n|}�Œ|S )a]  Remove leading machine-authored control wrappers, including nested ones.

    Loops so ``<command-message><command-name>/work</command-name></command-message>``
    reduces to the prose the user actually typed. Unlike a refusal check, this
    still yields usable text, so a slash-command turn gets a real title instead
    of staying untitled.
    r   é   éÿÿÿÿN)r!   ÚrangeÚlenÚ_CONTROL_WRAPPERSÚlowerÚ
startswithÚfind)	r1   ÚcurrentÚ_ÚstrippedÚopen_tagÚ	close_tagÚendÚinnerÚrests	            r#   Ústrip_control_wrappersrC   ³   sE  € ð ð ØˆrØ�jŠj‰lŒl€Gå•3Õ(Ñ)Ô)¨AÑ-Ñ.Ô.ð ñ ˆØˆÝ#4ð 	ð 	ÑˆH�iØ—>’>Ñ#Ô#×.Ò.¨xÑ8Ô8ð ØØ—.’.Ñ"Ô"×'Ò'¨	Ñ2Ô2ˆCØ�bŠyˆyà#¥C¨¡M¤M N NÔ3×9Ò9Ñ;Ô;��à ¥ X¡¤¨sÐ!2Ô3×9Ò9Ñ;Ô;�Ø ¥c¨)¡n¤nÑ 4Ð 5Ð 5Ô6×<Ò<Ñ>Ô>�ð !˜M E×0Ò0Ñ2Ô2�ØØ�wÒÐØˆEØˆ‰Ø€Nó    Úuser_messagec                 ó¶   — | sdS d}	 ddl m}  || ¦  «        }n,# t          $ r t                               dd¬¦  «         Y nw xY w|�|n| }t          |¦  «        S )uÚ  Reduce a user turn to the text worth titling.

    A ``/skill`` invocation expands into a message that embeds the whole skill
    body, so feeding it to the titler verbatim titles the session after the
    *skill's* prose â€” "Kick off a task in a fresh isolated git worktree" â€” not
    after the user's request. Reuse the canonical scaffolding parser so the
    model sees ``/work â€” fix the title leak`` instead, then strip any control
    wrappers left around it.
    r   Nr   )Údescribe_skill_invocationz-Skill-scaffolding summary failed; titling rawTr)   )Úagent.skill_commandsrG   r"   r,   r-   rC   )rE   Ú	describedrG   r1   s       r#   Ú_summarize_user_messagerJ   Õ   s˜   € ð ð ØˆrØ€IðUØBÐBÐBÐBÐBÐBà-Ð-¨lÑ;Ô;ˆ	ˆ	øÝð Uð Uð UÝ�ŠÐDÈtˆÑTÔTÐTÐTÐTðUøøøà!Ð-ˆ9ˆ9°<€DÝ! $Ñ'Ô'Ð's   ˆ š&AÁAc                 ó  — t          | t          ¦  «        r|                      ¦   «         sdS t          D ],}|                      ¦   «                              |¦  «        r dS Œ-t          t          | ¦  «                             ¦   «         ¦  «        S )zæReturn whether *user_message* carries real user intent to title from.

    False for machine-authored openers (compaction handoffs, runtime notes) and
    for turns that reduce to nothing once control scaffolding is stripped.
    F)Ú
isinstancer   r!   Ú_MACHINE_PREFIXESÚlstripr9   ÚboolrJ   )rE   Úprefixs     r#   Úis_titleable_user_messagerQ   ì   sŽ   € õ �l¥CÑ(Ô(ð °×0BÒ0BÑ0DÔ0Dð ØˆuÝ#ð ð ˆØ×ÒÑ Ô ×+Ò+¨FÑ3Ô3ð 	Ø�5�5ð	åÕ'¨Ñ5Ô5×;Ò;Ñ=Ô=Ñ>Ô>Ð>rD   c                 ó¶  — t          | ¦  «        }|sdS t          d„ |                     ¦   «         D ¦   «         d¦  «        }|sdS d                     |                     ¦   «         ¦  «        }t          |¦  «        t          k    rT|dt          …         }|                     d¦  «        }|t          dz  k    r
|d|…         }|                     d¦  «        dz   }|pdS )uz  Build an instant title from the user's message. No model, never fails.

    This is what the user sees within milliseconds of sending their first
    message. It is intentionally dumb â€” first meaningful line, trimmed to a
    word boundary â€” because its job is to beat the model to the screen, not to
    beat it on quality. The model's title replaces it moments later.
    Nc              3   óf   K  — | ],}|                      ¦   «         ¯|                      ¦   «         V — Œ-d S ©N©r!   ©Ú.0Úlns     r#   ú	<genexpr>zderive_title.<locals>.<genexpr>  s7   è è € ÐFÐF ¸2¿8º8¹:¼:ÐF�—’‘”ÐFÐFÐFÐFÐFÐFrD   r   Ú r3   u	    ,.;:â€”-u   â€¦)	rJ   ÚnextÚ
splitlinesÚjoinÚsplitr6   ÚMAX_DERIVED_TITLE_CHARSÚrfindÚrstrip)rE   r1   ÚlineÚcutÚspaces        r#   Úderive_titlere   ú   sÞ   € õ # <Ñ0Ô0€DØð Øˆtõ ÐFÐF d§o¢oÑ&7Ô&7ÐFÑFÔFÈÑKÔK€DØð ØˆtØ�8Š8�D—J’J‘L”LÑ!Ô!€DÝ
ˆ4�y„yÕ*Ò*Ð*ØÐ+Õ+Ð+Ô,ˆà—	’	˜#‘”ˆØÕ*¨aÑ/Ò/Ð/Ø�f�u�f”+ˆCØ�zŠz˜+Ñ&Ô&¨Ñ.ˆØˆ<�4ÐrD   Úcontentc                 ó´  — | sdS |                       ¦   «         }t          j        d|t          j        ¦  «        }|r'|                     d¦  «                              ¦   «         }	 t          j        |¦  «        }t          |t          ¦  «        rBt          | 	                    d¦  «        t          ¦  «        r|d                               ¦   «         S n# t          t          f$ r Y nw xY wt          j        d|¦  «        }|ru	 t          j        d|                     d¦  «        › d�¦  «                              ¦   «         S # t          $ r* |                     d¦  «                              ¦   «         cY S w xY w	 ddlm}  |d	|¦  «                              ¦   «         }n,# t           $ r t"                               d
d¬¦  «         Y nw xY wt'          d„ |                     ¦   «         D ¦   «         d¦  «        }|                     ¦   «                              d¦  «        r|dd	…                               ¦   «         }|                      d¦  «                              ¦   «         S )a  Pull the title out of a model response.

    The JSON schema makes the object shape the expected case, but not every
    provider honors ``response_format``; fall back through a loose JSON scan
    and finally to first-line prose so a non-compliant provider still titles.
    r   z^```(?:json)?\s*(.*?)\s*```$é   r   z""title\"\s*:\s*"((?:[^"\\]|\\.)*)"ú"r   )Ústrip_think_blocksNz/strip_think_blocks unavailable for title outputTr)   c              3   óf   K  — | ],}|                      ¦   «         ¯|                      ¦   «         V — Œ-d S rT   rU   rV   s     r#   rY   z&_extract_title_text.<locals>.<genexpr>8  s7   è è € ÐDÐD˜r¸¿º¹¼ÐD�—’‘
”
ÐDÐDÐDÐDÐDÐDrD   útitle:é   ú"')r!   ÚreÚmatchÚDOTALLÚgroupÚjsonÚloadsrL   Údictr    r   Ú
ValueErrorÚ	TypeErrorÚsearchÚagent.agent_runtime_helpersrj   r"   r,   r-   r[   r\   r8   r9   )rf   ÚrawÚfencedÚparsedrp   rj   s         r#   Ú_extract_title_textr}     sR  € ð ð ØˆrØ
�-Š-‰/Œ/€CåŒXÐ5°s½B¼IÑFÔF€FØð &Ø�lŠl˜1‰oŒo×#Ò#Ñ%Ô%ˆðÝ”˜C‘”ˆÝ�f�dÑ#Ô#ð 	+­
°6·:²:¸gÑ3FÔ3FÍÑ(LÔ(Lð 	+Ø˜'”?×(Ò(Ñ*Ô*Ð*øøÝ�	Ð"ð ð ð Øˆðøøøõ ŒIÐ;¸SÑAÔA€EØð *ð	*Ý”:Ð3 %§+¢+¨a¡.¤.Ð3Ð3Ð3Ñ4Ô4×:Ò:Ñ<Ô<Ð<øÝð 	*ð 	*ð 	*Ø—;’;˜q‘>”>×'Ò'Ñ)Ô)Ð)Ð)Ð)ð	*øøøðWØBÐBÐBÐBÐBÐBà Ð   sÑ+Ô+×1Ò1Ñ3Ô3ˆˆøÝð Wð Wð WÝ�ŠÐFÐQUˆÑVÔVÐVÐVÐVðWøøøå
ÐDÐD S§^¢^Ñ%5Ô%5ÐDÑDÔDÀbÑ
IÔ
I€CØ
‡y‚y�{„{×Ò˜hÑ'Ô'ð Ø�!�"�"Œg�mŠm‰oŒoˆØ�9Š9�UÑÔ×!Ò!Ñ#Ô#Ð#s7   Á#A*C ÃC#Ã"C#Ã><D; Ä;1E/Å.E/Å3$F Æ&GÇ Gc                 óÂ  — d                      | pd                     ¦   «         ¦  «        }|                     d¦  «                             ¦   «         }|                     ¦   «                              d¦  «        r|dd…                              ¦   «         }|                     d¦  «        }|sdS t          |¦  «        dk    r|dd	…                              ¦   «         d
z   }|S )zFNormalize a model-produced title, or None when nothing usable remains.rZ   r   rn   rl   rm   Nz.!,;:éP   éM   z...)r]   r^   r!   r8   r9   ra   r6   )r1   r   s     r#   Ú_clean_titler�   >  sÅ   € à�HŠH�d�j˜b×'Ò'Ñ)Ô)Ñ*Ô*€EØ�KŠK˜ÑÔ×$Ò$Ñ&Ô&€EØ‡{‚{�}„}×Ò Ñ)Ô)ð "Ø�a�b�b”	—’Ñ!Ô!ˆà�LŠL˜Ñ!Ô!€EØð ØˆtÝ
ˆ5�z„z�B‚€Ø�c�r�c”
×!Ò!Ñ#Ô# eÑ+ˆØ€LrD   ÚtimeoutÚfailure_callbackÚmain_runtimeÚruntime_validatorc           
      óÐ  — t          ¦   «         st                               d¦  «         dS |�T	  |¦   «         st                               d¦  «         dS n,# t          $ r t                               dd¬¦  «         Y nw xY wt	          | ¦  «        dt
          …         }|                     ¦   «         sdS t          ¦   «         }|rt           	                    |¬¦  «        nt          }t                               d|¦  «        }d	|d
œd|d
œg}		 t          d|	dd||dt          i¬¦  «        }
|
j        d         j        j        pd}t%          t'          |¦  «        ¦  «        S # t          $ r~}t                               d|¦  «         t                               dd¬¦  «         |�:	  |d|¦  «         n,# t          $ r t                               dd¬¦  «         Y nw xY wY d}~dS d}~ww xY w)u¬  Generate a session title from the user's opening message.

    Runs on the ``title_generation`` auxiliary task, which resolves to a
    small/fast model tier. Thinking is disabled and the response is constrained
    to ``{"title": "..."}`` so there is no preamble or reasoning to strip.

    Titles come from the user's message alone â€” every surveyed implementation
    that titles well (Claude Code, OpenCode, Cursor, OpenClaw) does the same.
    Waiting for the assistant is what made this slow, and it bought nothing:
    the user's opening message already states the intent worth naming.

    ``failure_callback`` is invoked with ``(task, exception)`` when the
    auxiliary call raises â€” the caller typically wires this to
    ``AIAgent._emit_auxiliary_failure`` so the user sees a warning instead
    of silently accumulating untitled sessions.

    ``runtime_validator`` is called right before the LLM request. If it
    returns False (e.g. the user's model was switched since the background
    thread captured its runtime snapshot), the call is skipped silently â€”
    no request is sent, so a stale title request can't reload a model the
    runtime already unloaded (#19027).
    ú<Auto-title skipped: auxiliary.title_generation.enabled=falseNz:Title generation skipped: runtime validator returned Falsez*Title runtime validator raised; proceedingTr)   )r   Ú__LANGUAGE_RULE__Úsystem)Úrolerf   Úuserr   é@   g333333Ó?Úresponse_format)ÚtaskÚmessagesÚ
max_tokensÚtemperaturer‚   r„   Ú
extra_bodyr   r   zTitle generation failed: %szTitle generation tracebackútitle generationz(Title generation failure_callback raised)r0   r,   r-   r"   rJ   ÚMAX_TITLE_INPUT_CHARSr!   r$   Ú_LANGUAGE_RULE_PINNEDÚformatÚ_LANGUAGE_RULE_MATCH_USERÚ_TITLE_PROMPT_TEMPLATEÚreplacer   Ú_TITLE_RESPONSE_FORMATÚchoicesÚmessagerf   r�   r}   Úwarning)rE   r‚   rƒ   r„   r…   Úuser_snippetr   Úlanguage_ruleÚpromptr�   Úresponserf   Úes                r#   Úgenerate_titler£   M  sc  € õ: Ñ Ô ð Ý�ŠÐSÑTÔTÐTØˆtàÐ$ð	VØ$Ð$Ñ&Ô&ð Ý—’ÐYÑZÔZÐZØ�tðøõ ð 	Vð 	Vð 	Vå�LŠLÐEÐPTˆLÑUÔUÐUÐUÐUð	Vøøøõ +¨<Ñ8Ô8Ð9OÕ:OÐ9OÔP€LØ×ÒÑÔð ØˆtåÑ Ô €Hð ð	'Õ×$Ò$¨hÐ$Ñ7Ô7Ð7å&ð õ $×+Ò+Ð,?ÀÑOÔO€Fð  fÐ-Ð-Ø LÐ1Ð1ð€Hð
ÝØ#Øð ØØØ%Ø)Õ+AÐBð

ñ 

ô 

ˆð Ô" 1Ô%Ô-Ô5Ð;¸ˆÝÕ/°Ñ8Ô8Ñ9Ô9Ð9øÝð 
ð 
ð 
õ 	�ŠÐ4°aÑ8Ô8Ð8Ý�ŠÐ1¸DˆÑAÔAÐAØÐ'ðXØ Ð Ð!3°QÑ7Ô7Ð7Ð7øÝð Xð Xð XÝ—’ÐGÐRV�ÑWÔWÐWÐWÐWðXøøøàˆtˆtˆtˆtˆtøøøøð
øøøsN   ®$A Á&A>Á=A>ÄAE Å
G%Å'9G Æ!F.Æ-G Æ.&GÇG ÇGÇG Ç G%)Údedupec                óÞ   ‡ ‡‡‡— t          ‰ dd¦  «        Šˆˆ ˆˆfd„}	  ||¦  «        S # t          $ r8 t          ‰ dd¦  «        }|r|€‚  ||¦  «        }|r||k    r‚  ||¦  «        cY S w xY w)uN  Persist a title at *source* authority, recovering from name collisions.

    The write goes through ``set_auto_title`` (precedence check + write in one
    transaction) so a manual ``/title`` set while generation was in flight is
    never overwritten. ``ValueError`` means the name is taken by an unrelated
    session (the unique-title index); rather than leave the session untitled
    (#50537), append a ``#N`` suffix via ``get_next_title_in_lineage``.

    ``dedupe=False`` re-raises that collision instead. The derived title is the
    one write on the turn's critical path, and it is also the one that collides
    constantly â€” it is a slice of the user's own words, and people open sessions
    with "hi" and "help me debug this". Scanning the lineage for the next free
    "hi #N" is a widening scan, run inline, for a name the model replaces a
    second later. The background stage picks the collision back up, so nothing
    is lost by declining it here.

    Returns the title actually persisted, or None when a higher-authority
    title already held the row (nothing was written).
    Úset_auto_titleNc                 ó  •— ‰�. ‰‰| ‰¬¦  «        st                                d‰‰¦  «         d S | S t          ‰dd ¦  «        }|� |‰| ¦  «        r| nd S ‰                     ‰| ¦  «        }|du rt	          d‰› d�¦  «        ‚| S )N©ÚsourcezDSkipping %s title: a higher-authority title already holds session %sÚset_auto_title_if_emptyFzsession z not found when storing title)r,   r-   ÚgetattrÚset_session_titleÚRuntimeError)Ú	candidateÚ	legacy_fnÚokÚauto_fnÚ
session_dbÚ
session_idr©   s      €€€€r#   Ú_setz$_persist_session_title.<locals>._set»  sÀ   ø€ ØÐØ�7˜: y¸Ð@Ñ@Ô@ð Ý—’ð!à˜Jñô ð ð
 �tØÐå˜JÐ(AÀ4ÑHÔHˆ	ØÐ Ø ) 	¨*°iÑ @Ô @ÐJ�9�9ÀdÐJØ×)Ò)¨*°iÑ@Ô@ˆØ�ˆ;ˆ;ÝÐS¨*ÐSÐSÐSÑTÔTÐTØÐrD   Úget_next_title_in_lineage)r«   rv   )	r²   r³   r   r©   r¤   r´   Únext_title_fnÚdedupedr±   s	   `` `    @r#   Ú_persist_session_titler¸   ¥  sÊ   øøøø€ õ( �jÐ"2°DÑ9Ô9€Gðð ð ð ð ð ð ð ð&	Øˆt�E‰{Œ{ÐøÝð ð ð Ý 
Ð,GÈÑNÔNˆØð 	˜Ð.ØØ�- Ñ&Ô&ˆØð 	˜' UÒ*Ð*ØØˆt�G‰}Œ}ÐÐÐðøøøs   Ÿ
* ª?A,Á+A,r³   Útitle_callbackc                 óZ  — | r|sdS 	 t          |¦  «        sdS t          |¦  «        }|sdS t          | ||dd¬¦  «        }|r<|�:	  ||d¦  «         n,# t          $ r t                               dd¬¦  «         Y nw xY w|S # t          $ r  t                               dd¬¦  «         Y dS w xY w)	a&  Write the derived title synchronously. Cheap enough to run inline.

    Returns the title written, or None when nothing was written (no usable
    text, or the session already carries a title of at least ``derived``
    authority). Never raises: a titling failure must not affect the turn.
    NÚderivedF)r©   r¤   zInstant-title callback failedTr)   zInstant title failed)rQ   re   r¸   r"   r,   r-   )r²   r³   rE   r¹   r   Ú	persisteds         r#   Úapply_instant_titler½   Ú  s  € ð ð ˜Zð ØˆtðÝ(¨Ñ6Ô6ð 	Ø�4Ý˜\Ñ*Ô*ˆØð 	Ø�4Ý*Ø˜
 E°)ÀEð
ñ 
ô 
ˆ	ð ð 	M˜Ð3ðMØ�˜y¨)Ñ4Ô4Ð4Ð4øÝð Mð Mð MÝ—’Ð<Àt�ÑLÔLÐLÐLÐLðMøøøàÐøÝð ð ð Ý�ŠÐ+°dˆÑ;Ô;Ð;Øˆtˆtðøøøs?   ˆB  ™B  ¬B  ÁA ÁB  Á&A;Á8B  Á:A;Á;B  Â &B*Â)B*c           	      ób  — 	 t          | ||||||¬¦  «         dS # t          $ rŠ}t                               d|¦  «         t                               dd¬¦  «         |�:	  |d|¦  «         n2# t          $ r t                               dd¬¦  «         Y nw xY wY d}~dS Y d}~dS Y d}~dS d}~ww xY w)	u8  Generate and store the model title for a session.

    Called on a background thread. Silently skips if:
    - session_db is None
    - the session already carries an ``llm`` or ``user`` title
    - title generation fails
    - runtime_validator returns False (model was switched)

    Never lets an exception escape: this is a daemon-thread target, and an
    escaping exception would spray a raw traceback into the user's terminal
    via the default threading excepthook. The canonical trigger is the
    post-``hermes update`` stale-module window, where this function's lazy
    imports read NEW source from disk while already-cached modules
    (``agent.portal_tags`` etc.) are still the OLD version â€” the resulting
    ImportError repeats on every auto-title attempt until the long-running
    process restarts.
    ©rƒ   r„   r¹   r…   zeAuto-title failed (harmless; if this started after an update, restart the running Hermes process): %szAuto-title tracebackTr)   Nr“   z"Auto-title failure_callback raised)Ú_auto_title_sessionr"   r,   r�   r-   )r²   r³   rE   rƒ   r„   r¹   r…   r¢   s           r#   Úauto_title_sessionrÁ   ü  s3  € ð4RÝØØØØ-Ø%Ø)Ø/ð	
ñ 	
ô 	
ð 	
ð 	
ð 	
øõ ð Rð Rð Rõ 	�Šð6àñ	
ô 	
ð 	
õ
 	�ŠÐ+°dˆÑ;Ô;Ð;ØÐ'ðRØ Ð Ð!3°QÑ7Ô7Ð7Ð7øÝð Rð Rð RÝ—’ÐAÈD�ÑQÔQÐQÐQÐQðRøøøð (Ð'Ð'Ð'Ð'Ð'à7Ð7Ð7Ð7Ð7Ð7àQÐQÐQÐQÐQÐQøøøøðRøøøs8   ‚ š
B.¤9B)ÁA+Á*B)Á+&BÂB)ÂBÂB)Â)B.c                 óô  — | r|sdS 	 t          | dd¦  «        }|� ||¦  «        }|�|dk    rdS n|                      |¦  «        rdS n# t          $ r Y dS w xY wddlm}	 ddlm}
 |}	 |                      |¦  «        p|}n# t          $ r Y nw xY w |
|¦  «          |	| |¦  «         t          ||||¬¦  «        }d}|st          |¦  «        }d}|sdS 	 t          | |||¬	¦  «        }|€dS t                               d
|¦  «         |�<	  |||¦  «         dS # t          $ r  t                               dd¬¦  «         Y dS w xY wdS # t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w)u9   Body of :func:`auto_title_session` â€” see its docstring.NÚget_session_title_sourcer»   r   )Úset_accounting_context)Úset_conversation_context)rƒ   r„   r…   Úllmr¨   z Auto-generated session title: %szAuto-title callback failedTr)   z&Failed to set auto-generated title: %s)r«   Úget_session_titler"   Úagent.aux_accountingrÄ   Úagent.portal_tagsrÅ   Úget_conversation_rootr£   re   r¸   r,   r-   )r²   r³   rE   rƒ   r„   r¹   r…   Ú	source_fnÚexisting_sourcerÄ   rÅ   Úconversation_idr   r©   r¼   r¢   s                   r#   rÀ   rÀ   0  sl  € ð ð ˜Zð Øˆð	Ý˜JÐ(BÀDÑIÔIˆ	ØÐ Ø'˜i¨
Ñ3Ô3ˆOØÐ*¨À)Ò/KÐ/KØ�øØ×)Ò)¨*Ñ5Ô5ð 	ØˆFøøÝð ð ð Øˆˆðøøøð <Ð;Ð;Ð;Ð;Ð;Ø:Ð:Ð:Ð:Ð:Ð:à €OðØ$×:Ò:¸:ÑFÔFÐTÈ*ˆˆøÝð ð ð ØˆðøøøàÐ˜_Ñ-Ô-Ð-ð Ð˜: zÑ2Ô2Ð2åØØ)Ø!Ø+ð	ñ ô €Eð €FØð 	õ ˜\Ñ*Ô*ˆØˆØð 	ØˆFðBÝ*¨:°zÀ5ÐQWÐXÑXÔXˆ	ØÐØˆFÝ�ŠÐ7¸ÑCÔCÐCØÐ%ðJØ�˜y¨&Ñ1Ô1Ð1Ð1Ð1øÝð Jð Jð JÝ—’Ð9ÀD�ÑIÔIÐIÐIÐIÐIðJøøøð &Ð%øõ
 ð Bð Bð BÝ�ŠÐ=¸qÑAÔAÐAÐAÐAÐAÐAÐAÐAøøøøðBøøøsd   ˆ&A	 °A	 Á	
AÁAÁ)B Â
BÂBÃE Ã,E Ä
D Ä&EÄ>E ÅEÅE Å
E7ÅE2Å2E7rœ   c                 óò   — t          | t          ¦  «        r|                      d¦  «        dk    rdS |                      d¦  «        }t          t          |t          ¦  «        r|nt          |¦  «        ¦  «        S )u=  Whether a history entry is a question a person actually asked.

    Hermes persists a lot of machinery under ``role="user"`` â€” compaction
    handoffs, model-switch markers, background-process notices â€” because strict
    OpenAI-compatible providers reject a system message that isn't first.
    Counting those as turns is what made a session that merely *opened* with one
    look like it was already past the point where titling applies.

    A multimodal turn is judged on its text, so "here's a screenshot, fix the
    login" counts as the real question it is.
    rŠ   r‹   Frf   )rL   ru   r    rQ   r   r   )rœ   rf   s     r#   Ú_is_real_user_turnrÏ   |  ss   € õ �g�tÑ$Ô$ð ¨¯ª°FÑ(;Ô(;¸vÒ(EÐ(EØˆuØ�kŠk˜)Ñ$Ô$€Gå$Ý˜g¥sÑ+Ô+ÐNˆˆÕ1EÀgÑ1NÔ1Nñô ð rD   c                 óþ   — t          | dd¦  «        }t          |¦  «        sdS 	 t           ||¦  «        pd¦  «                             ¦   «          S # t          $ r! t
                               d|d¬¦  «         Y dS w xY w)u‰  Whether the session still carries no title of any provenance.

    Titling normally reads the opening message and nothing else, but an opener
    isn't always titleable: an image with no caption, a compaction handoff, a
    bare slash command. Those sessions stayed nameless for life â€” the same guard
    that stops us re-titling on every turn also stopped us ever trying again.
    This reopens the question on later turns, and only while the answer is still
    missing, so a named session asks nothing and pays nothing.

    Answers False when it can't tell: an unreadable title is not a reason to
    start spending a model call per turn.
    rÇ   NFr   zUntitled check failed for %sTr)   )r«   Úcallabler   r!   r"   r,   r-   )r²   r³   Úgetters      r#   Ú_session_is_untitledrÓ   ‘  s—   € õ �ZÐ!4°dÑ;Ô;€FÝ�FÑÔð ØˆuðÝ�v�v˜jÑ)Ô)Ð/¨RÑ0Ô0×6Ò6Ñ8Ô8Ð8Ð8øÝð ð ð Ý�ŠÐ3°ZÈ$ˆÑOÔOÐOØˆuˆuðøøøs   ¤,A Á'A<Á;A<Úconversation_historyc           	      óŠ  — | r|r|sdS t          d„ |pg D ¦   «         ¦  «        }|dk    rt          | |¦  «        sdS t          |¦  «        sdS t          ¦   «         st                               d¦  «         dS t          | |||¦  «         t          j        t          | ||f||||dœdd¬¦  «        }	|	 
                    ¦   «          dS )	aÄ  Title a session from its opening message: instant, then upgraded.

    Call this at the START of a turn, before the model is invoked. The derived
    title is written inline (sub-millisecond) and the model upgrade is forked
    onto a daemon thread, so nothing here is on the critical path.

    Only acts on the session's opening exchange, and only when the message
    carries real user intent (machine-authored compaction handoffs are skipped).
    Nc              3   ó8   K  — | ]}t          |¦  «        ¯d V — ŒdS )rh   N)rÏ   )rW   Úms     r#   rY   z#maybe_auto_title.<locals>.<genexpr>È  s1   è è € ÐZÐZ˜qÕDVÐWXÑDYÔDYÐZ˜ÐZÐZÐZÐZÐZÐZrD   rh   r‡   r¿   Tz
auto-title)ÚtargetÚargsÚkwargsÚdaemonr   )ÚsumrÓ   rQ   r0   r,   r-   r½   Ú	threadingÚThreadrÁ   Ústart)
r²   r³   rE   rÔ   rƒ   r„   r¹   r…   Úuser_msg_countÚthreads
             r#   Úmaybe_auto_titlerâ   ¨  s	  € ð& ð ˜Zð ¨|ð Øˆõ ÐZÐZÐ%9Ð%?¸RÐZÑZÔZÑZÔZ€NØ˜ÒÐÕ"6°zÀ:Ñ"NÔ"NÐØˆå$ \Ñ2Ô2ð Øˆõ Ñ Ô ð Ý�ŠÐSÑTÔTÐTØˆå˜
 J°¸nÑMÔMÐMåÔÝ!Ø˜* lÐ3à 0Ø(Ø,Ø!2ð	
ð 
ð Øðñ ô €Fð ‡L‚L�N„N€N€N€NrD   )NNNNrT   )NNNNN)3Ú__doc__rs   Úloggingro   rÝ   Útypingr   r   r   Úagent.auxiliary_clientr   Úagent.context_compressorr   Úagent.message_contentr   Ú	getLoggerÚ__name__r,   r   ÚBaseExceptionÚFailureCallbackÚTitleCallbackrO   ÚRuntimeValidatorr”   r_   r˜   r—   r•   rš   r7   rM   r$   r0   rC   rJ   rQ   re   r}   r�   Úfloatru   r£   r¸   r½   rÁ   rÀ   rÏ   rÓ   Úlistrâ   © rD   r#   ú<module>rò      s\  ððð ð& €€€Ø €€€Ø 	€	€	€	Ø Ð Ð Ð Ø *Ð *Ð *Ð *Ð *Ð *Ð *Ð *Ð *Ð *à +Ð +Ð +Ð +Ð +Ð +Ø :Ð :Ð :Ð :Ð :Ð :Ø 6Ð 6Ð 6Ð 6Ð 6Ð 6à	ˆÔ	˜8Ñ	$Ô	$€ð ˜C Ð/°Ð5Ô6€ð ˜#˜s˜ TÐ)Ô*€ð ˜B ˜HÔ%Ð ð
 Ð ð
 Ð ð-ð ð* \Ð Ø:Ð ð àØàØ" V¨XÐ$6Ð7Ø ˜	Ø$)ð	
ð 
ð	ð 	ðð Ð ð&Ð ð$ ØØØØð >ðÐ ð$˜ð ð ð ð ð˜Tð ð ð ð ð" ð ¨ð ð ð ð ðD(¨#ð (°#ð (ð (ð (ð (ð.?¨Cð ?°Dð ?ð ?ð ?ð ?ð˜sð  x°¤}ð ð ð ð ð6&$ ð &$¨ð &$ð &$ð &$ð &$ðR�sð ˜x¨œ}ð ð ð ð ð"  $Ø26ØØ48ðUð UØðUà�eŒ_ðUð ˜Ô/ðUð ð	Uð
  Ð 0Ô1ðUð ˆc„]ðUð Uð Uð Uðp MQð 2ð 2ð 2ð 2ð 2ðr /3ð	ð àðð ðð ˜]Ô+ð	ð
 ˆc„]ðð ð ð ðL 37ØØ.2Ø48ð1Rð 1Ràð1Rð ð1Rð ˜Ô/ð	1Rð
 ð1Rð ˜]Ô+ð1Rð  Ð 0Ô1ð1Rð 
ð1Rð 1Rð 1Rð 1Rðp 37ØØ.2Ø48ðIBð IBàðIBð ðIBð ˜Ô/ð	IBð
 ðIBð ˜]Ô+ðIBð  Ð 0Ô1ðIBð 
ðIBð IBð IBð IBðX ð ¨ð ð ð ð ð*°ð ¸ð ð ð ð ð6 ,0Ø26ØØ.2Ø48ð;ð ;àð;ð ð;ð # 4œ.ð	;ð
 ˜Ô/ð;ð ð;ð ˜]Ô+ð;ð  Ð 0Ô1ð;ð 
ð;ð ;ð ;ð ;ð ;ð ;rD   