+
    iHàj>|  ã                   óô  € R t ^ RIt^ RIt^ RIt^ RIHt ^ RIHtHtH	t	H
t
Ht ^ RIHtHtHtHtHtHtHt ^ RIHtHt ^ RIHt ^ RIHtHt ^ RIHt  ^ R	I!H"t"H#t#H$t$H%t%H&t&H't' ^ R
I(H)t) ]PT                  ! R4      t+R2t,0 R3mt-R4t.Rt/R R lt0R R lt1R R lt2R R lt3R R lt4R R lt5R R lt6R R lt7R t8R R lt9R  R! lt:R" R# lt;R$ R% lt<R5R& R' llt=R( R) lt>R* R+ lt?R, R- lt@R. R/ ltA ! R0 R14      tBR# )6zÑDurable transcript persistence for ``AIAgent`` (mixin; MRO-resolved from ``run_agent``): SQLite flush
with intrinsic ``_DB_PERSISTED_MARKER`` dedup, ephemeral-scaffolding filtering, explicit
trajectory export.N)Únullcontext)ÚAnyÚDictÚListÚOptionalÚTuple)ÚCOMPRESSED_SUMMARY_METADATA_KEYÚ_DB_PERSISTED_MARKERÚContextCompressorÚ_newest_checkpoint_carrierÚ_strip_persistence_markersÚdrop_shadowed_checkpointsÚuser_originated_turn_view)ÚforwardÚforward_static)Úsanitize_context)Ú_is_multimodal_tool_resultÚ_multimodal_text_summary)Úsave_trajectory)ÚDB_ROW_SNAPSHOTÚMERGED_TURN_PREFIXÚREPAIR_BOOKKEEPING_FIELDSÚTOOL_CALL_UIDÚcopy_identity_fieldsÚtool_call_uid_from_history)Úsync_flushed_message_markersÚ	run_agentÚ"_persist_after_admission_interruptc                ó0   € V ^8„  d   QhR\         R\        /# ©é   ÚmsgÚreturn)r   Úbool)Úformats   "Ú>/home/ubuntu/.hermes/hermes-agent/agent/session_persistence.pyÚ__annotate__r&   4   s   € ÷ añ a¤3ð a¬4ñ aó    c                ó¸   a € \        S \        4      ;'       dC    \        ;QJ d#    V 3R l\         4       F  '       g   K   R# 	  R# ! V 3R l\         4       4      # )z`True when ``msg`` is internal recovery scaffolding that must never reach the durable transcript.c              3   óF   <"  € T F  pSP                  V4      x € K  	  R # 5i©N©Úget)Ú.0Úflagr!   s   & €r%   Ú	<genexpr>Ú,_is_ephemeral_scaffolding.<locals>.<genexpr>6   s   øé € Ð(`ÑC_¸4¨¯©°¯¨ÓC_ùs   ƒ!TF)Ú
isinstanceÚdictÚanyÚ_EPHEMERAL_SCAFFOLDING_FLAGS©r!   s   fr%   Ú_is_ephemeral_scaffoldingr6   4   s=   ø€ ä�cœ4Ó ×`Ð`§S£SÔ(`ÕC_Ó(`§S¤SÐ`¢SÐ` SÔ(`ÕC_Ó(`Ó%`Ð`r'   c                ó0   € V ^8„  d   QhR\         R\         /# ©r    Ú
session_idr"   )Ústr)r$   s   "r%   r&   r&   ?   s   € ÷ iñ i´ð i¼ñ ir'   c           	     óZ  € \        T ;'       g    R4      P                  4       p\        P                  ! RRV4      P                  R4      R,          ;'       g    RpV'       d	   W!8X  d   V# V R\        P
                  ! VP                  RRR	7      4      P                  4       R
,           2# )u´   Path-safe component for a (possibly untrusted ``X-Hermes-Session-Id``) ID: non ``[A-Za-z0-9_-]`` â†’ ``_``,
capped, plus a content hash when changed so distinct IDs cannot collide.Ú z[^\w-]Ú_z._:Né`   NÚsessionzutf-8Úsurrogatepass)Úerrors:Né   N)r:   ÚstripÚreÚsubÚhashlibÚsha256ÚencodeÚ	hexdigest)r9   ÚrawÚ	sanitizeds   &  r%   Ú _safe_session_filename_componentrL   ?   sŽ   € ô ˆj×Ð˜BÓ
×
%Ñ
%Ó
'€CÜ—’�y # sÓ+×1Ñ1°$Ó7¸Õ<×IÐIÀ	€Iß
ˆyÔØÐØˆ[˜œ'Ÿ.š.¨¯©°GÀO¨Ó)TÓU×_Ñ_ÓaÐbeÕfÐgÐhÐhr'   c                óH   € V ^8„  d   QhR\         R\        R\        R\        /# ©r    r!   ÚcontentÚoverrider"   )r2   r   r#   )r$   s   "r%   r&   r&   I   s(   € ÷ ñ ¤Dð ´3ð Ä#ð Ì$ñ r'   c                ó¸   € VRJ;'       dP    V P                  \        4      '       * ;'       d.    \        V\        4      '       * ;'       g    \        V\        4      # )zÿMay the persist override replace ``content``? A plain-text override must not replace native image/audio
blocks (a list override is the clean multimodal payload and does), nor a message MERGED with a compaction
summary (overwriting would drop the summary).N)r,   r   r1   Úlist)r!   rO   rP   s   &&&r%   Ú_override_replaces_contentrS   I   sU   € ð
 	˜Ð÷ 	Jð 	JØ—‘Ô7Ó8Ô8÷	Jð 	Jä˜G¤TÓ*Ô*×HÐH¬j¸Ä4Ó.Hðr'   c                óH   € V ^8„  d   QhR\         R\        R\        R\        /# rN   ©r2   r   )r$   s   "r%   r&   r&   T   s(   € ÷ ñ ¤Tð ´Cð Ä3ð Ì3ñ r'   c                ó0  € V P                  \        4      p\        V\        4      '       dk   \        V\        4      '       dU   \        V\        4      '       d?   W8X  g   VP	                  VR,           4      '       d   V'       d   VR,           V,           # T# V# )z¹Replace only the absorbed current turn, leaving unanswered history intact. The join matches
``_merge_consecutive_users``; a row that no longer holds the prefix takes the plain override.z

)r,   r   r1   r:   Ú
startswith)r!   rO   rP   Úprefixs   &&& r%   Ú_content_with_turn_overriderY   T   sn   € ð �W‰WÔ'Ó(€FÜ�'œ3×Ò¤J¨x¼×$=Ò$=Ä*ÈVÔUX×BYÒBYØÔ × 2Ñ 2°6¸Fµ?× CÒ Cß19�6˜F•? XÕ-ÐE¸vÐEØ€Or'   c          
      ój   € V ^8„  d   QhR\         R\        R\        R\        \        \        3,          /# )r    r!   rO   Úapi_contentr"   )r2   r   Útuple)r$   s   "r%   r&   r&   ^   s5   € ÷ 
 ñ 
 ¬ð 
 ¼ð 
 Ì#ð 
 ÔRWÔX[Ô]`ÐX`ÕRañ 
 r'   c                ó¢   € \        V RR4      p\        WV4      '       d/   Vf   \        V\        4      '       d	   W$8w  d   Tp\	        WV4      pW#3# )uL  ``(content, api_content)`` as the current turn's user row is written: the persist override is the
clean transcript, the live content is what the wire sent â€” so when they differ and nothing else was
injected, the live bytes ARE the sidecar. Shared by the flush and the turn-start stamp so the stamp
matches the row the flush wrote.Ú_persist_user_message_overrideN)ÚgetattrrS   r1   r:   rY   )Úagentr!   rO   r[   rP   s   &&&& r%   Údurable_user_row_contentra   ^   sQ   € ô
 �uÐ>ÀÓE€HÜ! #°×9Ò9ØÒ¤:¨g´s×#;Ò#;ÀÔ@SØ!ˆKÜ-¨c¸HÓEˆØÐÐr'   c                ó0   € V ^8„  d   QhR\         R\        /# r   rU   )r$   s   "r%   r&   r&   k   s   € ÷ #ñ #œtð #¬ñ #r'   c                óú   € V P                  \        4      '       dQ   \        V 4      fD   \        P                  ! V P                  R4      4      R8X  g   V P                  R4      '       g   R# V P                  R4      # )z–Standalone handoffs are hidden so they never occupy the active user slot in retry/undo dispatch;
merge-into-tail carriers keep their prior visibility.rO   Ú
standaloneÚ!_compressed_summary_has_user_turnÚhiddenÚdisplay_kind)r,   r   r   r
   Úclassify_summary_contentr5   s   &r%   Ú_summary_display_kindri   k   s_   € ð 	�‰Ô/×0Ò0Ü% cÓ*Ò2ä×6Ò6°s·w±w¸yÓ7IÓJÈlÔZØ—7‘7Ð>×?Ò?ñ Ø�7‰7�>Ó"Ð"r'   c                ó0   € V ^8„  d   QhR\         R\         /# )r    rO   r"   )r   )r$   s   "r%   r&   r&   z   s   € ÷ +ñ +œcð +¤cñ +r'   c           	     óÔ  € \        V 4      '       d   \        V 4      # \        V \        4      '       g   V # V  Uu. uF‰  p\        V\        4      '       g   K  VP                  R4      R8X  g   VP                  R4      \        9   g   KM  VP                  R4      R8X  d   \        VP                  RR4      4      M
\        V4      NK‹  	  ppV'       d   RP                  V4      # R# u upi )um   Text-only DB projection: multimodal envelopes â†’ summary; part lists keep text, images â†’ ``[screenshot]``.ÚtypeÚtextr<   Ú
N)
r   r   r1   rR   r2   r,   Ú_IMAGE_PART_TYPESr:   Ú_image_markerÚjoin)rO   ÚpÚtxts   &  r%   Ú_durable_contentrt   z   sÄ   € ä! '×*Ò*Ü'¨Ó0Ð0Ü�gœt×$Ò$Øˆñ óáˆAÜ�aœ×ô 	Pà$%§E¡E¨&£M°VÔ$;¸q¿u¹uÀV»}ÔPaÑ?aô 	P !§%¡%¨£-°6Ô"9ŒˆA�E‰E�&˜"ÓÔ¼}ÈQÓ?OÒOÙð ð ÷
 !ˆ4�9‰9�S‹>Ð* dÐ*ùòs   ¹C%Á-C%ÂA C%c                ó0   € V ^8„  d   QhR\         R\        /# )r    Úpartr"   )r2   r:   )r$   s   "r%   r&   r&   ˆ   s   € ÷ dñ dœð d¤ñ dr'   c                óÆ   € V P                  R4      pV P                  R4      ;'       g*    \        V\        4      '       d   VP                  R4      MRpVR9   d   RV R2# R# )z›``[screenshot]`` at the image's position; a caller-chosen ``detail`` rides along so a replay
(``agent.image_replay``) rebuilds the part the live turn sent.Ú	image_urlÚdetailNz[screenshot detail=Ú]z[screenshot])ÚlowÚhighÚauto)r,   r1   r2   )rv   Úurlry   s   &  r%   rp   rp   ˆ   s^   € ð �(‰(�;Ó
€CØ�X‰X�hÓ×YÐY¼ÀCÌ×9NÒ9N C§G¡G¨HÔ$5ÐTX€FØ.4Ð8OÔ.OÐ   ¨Ð*ÐcÐUcÐcr'   c                ó<   € \        V RR4      pVf   \        4       # T# )a  Close and turn-start persistence can run on separate CLI threads: one critical section.

``__init__`` always creates ``_session_persist_lock``; only ``object.__new__``-built test stubs lack it
(they run unlocked, matching the historical ``if persist_lock is None`` branch).
Ú_session_persist_lockN)r_   r   )r`   Úlocks   & r%   Ú_persist_lockr‚   �   s$   € ô �5Ð1°4Ó8€DØ šLŒ;‹=Ð2¨dÐ2r'   c                ó€   € V ^8„  d   QhR\         \        \        \        3,          ,          R\        R\        R\        /# )r    ÚhistoryÚqueryr`   r"   ©rR   r2   r:   r   r#   )r$   s   "r%   r&   r&   š   s6   € ÷ !ñ !¤4¬¬S´#¨X­Õ#7ð !Äð !ÌCð !ÔTXñ !r'   c                óŠ  a € \        V 3R l\        \        S 4      ^,
          RR4       4       R4      pVe   S V,          P                  R4      V8w  d   R# \        ;QJ d&    R S V^,           R  4       F  '       d   K   RM	  RM! R S V^,           R  4       4      '       g   R# S V,          pS VR1 RV\
        &   WBn        R# )u  Re-stage the transcript's unanswered tail row as THIS turn's user message; True when adopted.

A dispatcher's re-run of a failed delivery turn resumes the DM its first attempt already persisted
instead of appending it again. Rows loaded from the store are born durable (``_rows_to_conversation``),
so handing the tail row back as ``agent._pending_cli_user_message`` makes ``_stage_turn_user_message``
reuse it as this turn's user dict and the flush writes no second row. The one user is the peer-DM
lane's re-run (``resume_unanswered_turn`` in ``gateway.platforms.api_server``, #115325): in process it
calls this on the agent it just built for the re-run; under the session authority the admission
carries ``resume_unanswered`` and ``gateway.run_turn_runner`` calls it on the admitted history
when its tail is that bare user row (a tool suffix there keeps the completed work in view).
The owner-side Bot Chat retry (``gateway.session_bot._maybe_retry``) does not adopt: its failed
turn is already closed by the durable failed-turn boundary, which this function declines by design.

The DM is not always the literal tail: a turn that died mid-way persisted its tool scaffolding â€” assistant
``tool_calls`` rows and their ``tool`` results â€” behind the DM before the failure text was built, and the
dispatcher retries that too. The DM is still unanswered while nothing after it is a plain assistant reply,
so it is adopted and the failed attempt's scaffolding leaves the in-memory transcript: the re-run starts
the turn over from the DM (the rows stay in the DB as the record of the failed attempt; the re-run's
answer lands after them as a valid continuation). Anything else declines â€” no user row at the tail, or a
different text there â€” so a person's deliberate re-send of the same text is never swallowed.
c              3   ó¤   <"  € T FE  p\        SV,          \        4      '       g   K"  SV,          P                  R 4      R8X  g   KA  Vx € KG  	  R# 5i)ÚroleÚuserN©r1   r2   r,   )r-   Úir„   s   & €r%   r/   Ú(adopt_unanswered_turn.<locals>.<genexpr>°   sC   øé € ð VÑ:�aÜ˜g a�j¬$×/ô Ø4;¸AµJ·N±NÀ6Ó4JÈfÑ4T÷ ’Ó:ùs   ƒA§AÁ
ANrO   Fc              3   óä   "  € T Ff  p\        V\        4      ;'       dJ    VP                  R 4      R8H  ;'       g.    VP                  R 4      R8H  ;'       d    VP                  R4      x € Kh  	  R# 5i)r‰   ÚtoolÚ	assistantÚ
tool_callsNr‹   )r-   Úrows   & r%   r/   r�   ´   s}   é € ð -Ù+�3ô ˜#œtÓ$÷  Cð  C¨#¯'©'°&«/¸VÑ*C÷  +Bð  +BÈÏÉÐPVËÐ[fÑHf÷  IAð  IAÐkn×krÑkrÐsó  lAô  CÛ+ùs   ‚A0žA0ºA0ÁA0Téÿÿÿÿ)ÚnextÚrangeÚlenr,   Úallr	   Ú_pending_cli_user_message)r„   r…   r`   ÚidxÚtails   f&&  r%   Úadopt_unanswered_turnr›   š   s¿   ø€ ô, ô Vœ5¤ W£°Õ!1°2°rÔ:ó VØW[ó]€Cà
‚{�g˜c•l×&Ñ& yÓ1°UÔ:Ùß‹3ñ -Ø! #¨¥' (Ñ+ó-�3�3Š3ñ -Ø! #¨¥' (Ñ+ó-÷ -ò -áØ�3�<€DØ�‘ˆØ!%€DÔ	ÑØ&*Ô#Ùr'   c                ó$   € V ^8„  d   QhR\         /# ©r    r"   )Úset)r$   s   "r%   r&   r&   À   s   € ÷ <ñ <¤ñ <r'   c                óÜ   € \        V RR4      p\        V RR4      V8H  pV'       d   V P                  ^ 8w  d   \        V RR4      MRpWn        \        V\        4      '       d   V# \	        4       # )z�One-shot ``_flushed_db_message_ids`` seed (same session, after a non-empty flush); the scan translates
it to markers and the flush clears it.r9   NÚ_flushed_db_message_session_idÚ_flushed_db_message_ids)r_   Ú_last_flushed_db_idxr    r1   rž   )r`   Úcurrent_session_idÚsame_sessionÚseed_idss   &   r%   Ú_db_flush_seed_idsr¦   À   sj   € ô ! ¨°dÓ;ÐÜ˜5Ð"BÀDÓIÐM_Ñ_€LßBNÐSX×SmÑSmÐqrÔSrŒw�uÐ7¸Ô>Ðx|€HØ+=Ô(Ü! (¬C×0Ò0ˆ8Ð;´c³eÐ;r'   c                óF   € V ^8„  d   QhR\         \        ,          R\        /# ©r    Úmessagesr"   )rR   r2   Úint)r$   s   "r%   r&   r&   Ê   s   € ÷ ñ ¬$¬t­*ð ¼ñ r'   c                ó¶   € ^ p\        \        V RR4      ;'       g    RV4       F1  w  r4WCJg   VP                  \        4      '       g    V# V^,          pK3  	  V# )zPSkip the identity-matched, still-marked prefix of the previous flush's snapshot.Ú_db_flush_scan_prefixN© )Úzipr_   r,   r	   )r`   r©   Ú
scan_startÚprevÚcurs   &&   r%   Ú_db_flush_scan_startr²   Ê   s]   € à€JÜœ Ð(?ÀÓF×LÐLÈ"ÈhÖW‰	ˆØ‹? #§'¡'Ô*>×"?Ò"?ØàÐð 	�a�Š
ñ Xð Ðr'   c                ó^   € V ^8„  d   QhR\         R\        R\         \        \        3,          /# )r    r!   Úis_current_turn_userr"   )r2   r#   r:   r   )r$   s   "r%   r&   r&   Ô   s)   € ÷ *ñ *œdð *¼$ð *Ä4ÌÌSÈÅ>ñ *r'   c                óº  € VP                  RR4      pVP                  R4      p\        VP                  R4      \        4      '       d   VP                  R4      MRpVP                  R4      pV'       d+   VR8X  d$   \        WWE4      w  rE\	        V RR4      pVf   TMTpWT8X  d   RpVfT   VR9   dM   \        V\        4      '       d7   V'       d/   \        V4      P                  4       VP                  4       8w  d   TpRTR\        V4      R	VP                  R	4      ;'       g    VR
8X  d   VP                  R4      MRR\        VP                  R4      \        4      '       d
   VR,          MRRVP                  R4      RVP                  R4      RVP                  R4      RVP                  R4      /\         Uu/ uF  qˆVP                  V4      bK  	  upCR\        VP                  \        4      4      RTRTR\        V4      RVP                  R4      RVP                  R4      ;'       g    VP                  R4      R\        VP                  R4      4      /Cp	\        VP                  R4      \        4      '       d   VR,          V	R&   \        W4       \        VP                  \        4      \        4      '       d   V\        ,          V	\        &   V	# u upi )zUBuild the session-db row for ``msg``, applying the persist override to THIS row only.r‰   ÚunknownrO   r[   NÚ	timestamprŠ   Ú_persist_user_message_timestampÚ	tool_namer�   Únamer‘   Útool_call_idÚeffect_dispositionÚtoken_countÚfinish_reasonÚ_compressed_summaryrg   Údisplay_metadataÚplatform_message_idÚ
message_idÚobservedÚ_row_id)rŠ   r�   )r,   r1   r:   ra   r_   r   rC   rt   rR   Ú_ROW_REASONING_KEYSr#   r   ri   rª   r   r   )
r`   r!   r´   r‰   rO   r[   r·   Úov_timestampÚkr’   s
   &&&       r%   Ú_db_flush_rowrÈ   Ô   s`  € à�7‰7�6˜9Ó%€DØ�g‰g�iÓ €Gä,6°s·w±w¸}Ó7MÌs×,SÒ,S�#—'‘'˜-Ô(ÐY]€KØ—‘˜Ó$€Iß ¨¤Ü7¸ÀGÓYÑˆÜ˜uÐ&GÈÓNˆØ!-Ò!5‘I¸<ˆ	ØÔØˆð 	Ò Ð(=Ô =Ä*ÈWÔVY×BZÒBZ×_fÜ˜WÓ%×+Ñ+Ó-°·±³Ô@àˆð 	��iÔ!1°'Ó!:Ø�S—W‘W˜[Ó)×ZÐZÀÈÄ¨c¯g©g°f¬oÐUYØ¬:°c·g±g¸lÓ6KÌT×+RÒ+R�c˜,Ö'ÐX\Ø˜Ÿ™ Ó/Ð1EÀsÇwÁwÐOcÓGdØ�s—w‘w˜}Ó-¨ÀÇÁÈÓ@Xðõ #6Ó
6Ñ"5˜Qˆc�g‰g�a‹jŠ=Ñ"5Ñ
6ðð 	œt C§G¡GÔ,KÓ$LÓMØ�Y ¨{ØÔ-¨cÓ2Ð4FÈÏÉÐPbÓHcà˜sŸw™wÐ'<Ó=×VÐVÀÇÁÈÓAVØ”D˜Ÿ™ Ó,Ó-ñ€Cô �#—'‘'˜)Ó$¤c×*Ò*Ø˜Y�ˆˆI‰ô ˜Ô"Ü�#—'‘'œ/Ó*¬C×0Ò0Ø"¤?Õ3ˆŒOÑØ€Jùò 7s   Æ;Kc                ó~   € V ^8„  d   QhR\         \        ,          R\        \         \        ,          ,          R\        /# )r    r©   Úconversation_historyÚreplay_history)rR   r2   r   r#   )r$   s   "r%   r&   r&     s2   € ÷ &"ñ &"¤t¬D¥zð &"ÌÔRVÔW[ÕR\ÕI]ð &"Ü&*ñ&"r'   c           	     óú  € \        V 4      pT;'       g    .  Uu0 uF&  p\        V\        4      '       g   K  \        V4      kK(  	  pp\	        V RR4      p\	        V RR4      p. p	. p
/ p\        \        W4      \        V4      4       EFj  pW,          p\        V\        4      '       d-   \        V4      '       g   VP                  \        4      '       d   KP  \        V4      V9   pV'       d	   V'       d   \        V4      V9   d'   VP                  \        4      '       g   RV\        &   K¤  \	        V RR4      '       d2   V'       g*   RVR&   / VP                  R	4      ;'       g    / CR
R/CVR	&   VP                  R4      R8X  d4   VP                  \        4      '       g   \        WV4      ;pe	   Wý\        &   T	P                  \        YW|8H  ;'       g    WØJ 4      4       V
P                  V4       EKm  	  Wš3# u upi )zÄScan for un-flushed messages; returns ``(rows, msgs)`` to write in one transaction. ``replay_history``
(session-row heal) writes the history prefix again instead of stamping it as already durable.Ú_persist_user_message_idxNr˜   TÚ_mute_notification_replyFrf   rg   rÀ   Únotification_categoryÚ
diagnosticr‰   r�   )r¦   r1   r2   Úidr_   r•   r²   r–   r6   r,   r	   Ú"_PERSIST_AFTER_ADMISSION_INTERRUPTr   r   ÚappendrÈ   )r`   r©   rÊ   rË   r¥   ÚitemÚhistory_idsÚov_idxÚpending_cli_messageÚ
batch_rowsÚ
batch_msgsÚtool_uid_ownersÚmsg_idxr!   Ú
is_historyÚtool_uids   &&&&            r%   Ú_db_flush_collectrÞ     sÂ  € ô " %Ó(€HØ)=×)CÐ)CÀÐ)CÓ_Ñ)C Ì
ÐSWÔY]×H^”8”2�d–8Ñ)C€KÐ_Ü�UÐ7¸Ó>€Fô " %Ð)DÀdÓKÐØ')€JØ€JØ€OÜÔ-¨eÓ>ÄÀHÃ×NˆØÕˆô ˜#œt×$Ò$Ô(AÀ#×(FÒ(FÈ#Ï'É'ÔRf×JgÒJgÙä˜“W Ñ+ˆ
ç§´2°c³7¸hÔ3FØ—'‘'Ô<×=Ò=Ø(,ˆCÔ$Ñ%ÙÜ�5Ð4°e×<Ò<ÇZð #+ˆC�ÑØ&t¨#¯'©'Ð2DÓ*E×*KÐ*KÈÐ&tÐNeÐgsÑ&tˆCÐ"Ñ#Ø�7‰7�6‹?˜fÔ$¨S¯W©W´]×-CÒ-Cô 7°xÈ/ÓZÐZ�ÒgØ%-”MÑ"Ø×Ñœ-¨°FÑ4E×4cÐ4cÈÐIcÓdÔeØ×Ñ˜#×ñ1 Oð2 Ð!Ð!ùòC `s
   šG8·G8c                ó¤   € V ^8„  d   QhR\         \        \        \        3,          ,          R\         \        ,          R\         \        ,          RR/# )r    rØ   rÙ   r©   r"   N)rR   r2   r:   r   )r$   s   "r%   r&   r&   *  sA   € ÷ ,ñ ,¤t¬D´´c°­NÕ';ð ,ÌÌdÍð ,Ô_cÔdhÕ_ið ,Ðnrñ ,r'   c                ó  € V'       g   R# V P                   P                  V P                  T\        V RR4      \        V RR4      \        V RR4      ;'       g    RR7       \	        W!4       \        VR4      ^ 8¼  d   \        V4       R# R# )z]One transaction for the turn's new rows: on failure nothing lands and no markers are stamped.NÚ_active_compression_lock_holderÚ!_active_session_turn_lease_holderÚ&_active_session_turn_lease_ttl_secondsg     Àr@)r9   r©   Úcompression_lock_holderÚturn_lease_holderÚturn_lease_ttl_secondsÚcodex_reasoning_items)Ú_session_dbÚappend_messages_batchr9   r_   r   r   r   )r`   rØ   rÙ   r©   s   &&&&r%   Ú_db_flush_writerê   *  sˆ   € çÙØ	×Ñ×+Ñ+Ø×#Ñ#¨jÜ '¨Ð/PÐRVÓ WÜ! %Ð)LÈdÓSÜ& uÐ.VÐX]Ó^×gÐgÐbgð	 ,ô ô ! Ô8Ü! *Ð.EÓFÈ!ÔKô 	" (Ö+ñ	 Lr'   c                ó$   € V ^8„  d   QhR\         /# r�   )r#   )r$   s   "r%   r&   r&   <  s   € ÷ ñ ¬dñ r'   c                óÚ  € V P                   p V P                  P                  V4      pT'       d   Y!8X  d   R#  T P                  P                  T4      pTe   TP                  R4      e   R# \        P                  RY!4       T\        4       ^ uT n         T n	        T n
        RT n        R#   \         d"   p\        P                  RY4        Rp?R# Rp?ii ; i  \         d    Rp L”i ; i)zžAdopt the live continuation of a compression-closed session. Same-id tip = no continuation; a tip
whose row is missing or already ended is not adopted either.z(compression tip lookup failed for %s: %sNFÚended_atzJAdopted live compression tip %s for closed session %s; retrying flush onceT)r9   rè   Úget_compression_tipÚ	ExceptionÚloggerÚwarningÚget_sessionr,   rž   r¡   r¢   Ú_compression_adoption_failed)r`   Úold_idÚtipÚtip_excÚtip_rows   &    r%   Ú_db_flush_adopt_compression_tiprø   <  sÛ   € ð ×Ñ€FðØ×Ñ×3Ñ3°FÓ;ˆ÷ �#”-ÙðØ×#Ñ#×/Ñ/°Ó4ˆð ‚˜'Ÿ+™+ jÓ1Ò=ÙÜ
‡N�NÐ_ÐadÔmØRUÔWZÓW\Ð^_ÐO€EÔ�eÔ3°UÔ5OØ).€EÔ&Ùøô ô Ü�‰ÐAÀ6ÔSÝûðûô ô ØŠðús(   ŽB+ ºC Â+CÂ6CÃCÃC*Ã)C*c                óF   € V ^8„  d   QhR\         \        ,          R\        /# r8   )r   r:   r#   )r$   s   "r%   r&   r&   S  s   € ÷ ñ ´(¼3µ-ð ÄDñ r'   c                óœ   €  V P                   P                  V4      RJ #   \         d"   p\        P	                  RY4        Rp?R# Rp?ii ; i)zVTrue only when ``session_id``'s row is confirmed absent; a failed lookup is not proof.Nz$session row lookup failed for %s: %sF)rè   rò   rï   rð   rñ   )r`   r9   Ú
lookup_excs   && r%   Ú_db_flush_session_row_gonerü   S  sE   € ðØ× Ñ ×,Ñ,¨ZÓ8¸DÐ@Ð@øÜô Ü�‰Ð=¸zÔVÝûðús   ‚ ŸAªAÁAc                ó¸   € V ^8„  d   QhR\         R\        \        \        \        3,          ,          R\
        R\        \        ,          R\        \        ,          /# )r    ÚerØ   Úadoption_budgetr©   r"   )rï   rR   r2   r:   r   rª   r   )r$   s   "r%   r&   r&   \  sN   € ÷ Eñ Eœyð E´d¼4ÄÄSÀ½>Õ6Jð EÔ]`ð EÜ#¤D�zðEÜ.6´s­mñEr'   c                óÒ  € RV n         ^ RIHpHpHpHp ^ RIHp	 V! V4      V n        V P                  R8X  Ed   V^ 8:  g   \        W P                  4      '       g   R# \        V4       V P                  V n        \        4       V n        ^ V n        RV n        V P#                  4        V P$                  p
V P                   '       g8   V
'       d0   \        W
4      '       d   RV n         V P#                  4        W n        V P                   '       g$   \&        P)                  R\+        V RR4      4       R# \&        P)                  R\+        V RR4      4       R	# \-        WV34      '       dc    T! \+        V RR
4      ;'       g    R
V UUUu. uF4  pVP/                  4        UUu/ uF  w  rÍV\0        9  g   K  WÍbK  	  uppNK6  	  uppp4       \-        W4      '       d"   V^ 8”  d   \5        V 4      '       d   R# RV n        \&        P)                  RV4       R#   Y n        i ; iu uppi u upppi   \2         d2    \&        P)                  RT P                  \+        T RR4      RR7        L�i ; i)a¾  Classify a failed flush and name the one retry the caller should take, or None to fail closed.

``"adopted"``: a compression-closed session moved onto its live tip. ``"healed"``: the session row was
deleted under the live agent and has been recreated (unparented for a delegate child whose parent went
with it); flush markers are reset so the caller replays the full in-memory transcript. Either retry is
taken at most once (``adoption_budget``).N)ÚStateDbCorruptErrorÚStateDbReplacedErrorÚclassify_persistence_errorÚdivert_session_transcript_jsonl)ÚCompressionSessionClosedErrorÚsession_row_missingFzRSession DB row for %s is missing and could not be recreated; will retry next flushr9   zaSession DB row for %s was removed under the live agent; recreated it and replaying the transcriptÚhealedr<   z,JSONL divert failed after state.db %s for %sT)Úexc_infoÚadoptedz$Session DB append_message failed: %s)r¬   Úhermes_stater  r  r  r  Úhermes_state_errorsr  Ú_last_persistence_error_causerü   r9   r   Ú_session_row_replay_pendingrž   r¡   r¢   Ú_session_db_createdÚ_ensure_db_sessionÚ_parent_session_idrð   rñ   r_   r1   Úitemsr   rï   rø   ró   )r`   rþ   rØ   rÿ   r©   r  r  r  r  r  Ú	parent_idÚrrÇ   Úvs   &&&&&         r%   Ú_db_flush_failedr  \  s+  € ð #'€EÔ÷ Dó  DÝAÙ*DÀQÓ*G€EÔ'Ø×*Ñ*Ð.CÕCð ˜aÔÔ'AÀ%×IYÑIY×'ZÒ'ZÙÜ" 8Ô,ð -2×,<Ñ,<ˆÔ)Ü(+«ˆÔ%Ø%&ˆÔ"Ø$)ˆÔ!Ø× Ñ Ô"Ø×,Ñ,ˆ	Ø×(×(Ð(¯YÔ;UÐV[×;gÒ;gð (,ˆEÔ$ð5Ø×(Ñ(Ô*à+4Ô(Ø×(×(Ð(ô �N‰NÐoÜ" 5¨,¸Ó=ô?áÜ�‰ÐzÜ˜u l°DÓ9ô	;áÜ�!Ð,?Ð@×AÒAð	sá+¬G°E¸<ÈÓ,L×,RÐ,RÐPRá6@õ-BÙ6@°ð @A¿w¹w¼yÔ-o¹y±t°qÈAÔUnÑLn¬d¨aªd¹yÕ-oÙ6@ó-BôCô �!×3Ò3ð ˜QÔÔ#BÀ5×#IÒ#IÙØ-1ˆÔ*Ü
‡N�NÐ9¸1Ô=Ùøð9 ,5Õ(üó  .pùô -Bøäô 	sÜ�N‰NÐIØ ×>Ñ>ÄÈÈ|Ð]aÓ@bÐmqð ö sð	súsN   Ã%H Å'H* Å=H* ÆH#ÆHÆ1HÆ7H#Æ?	H* ÈHÈH#È#H* È*9I&É%I&c                   ó  a € ] tR tRt o RtV 3R lR ltRV 3R lR lltV 3R lR	 lt]! R
R4      t	RV 3R lR llt
RV 3R lR lltV 3R lR lt]! RR4      t]! R
R4      tV 3R lR lt]! R
R4      t]! R
R4      tRtV tR# )ÚSessionPersistenceMixini¥  zCSession DB flush and trajectory persistence (see module docstring).c                ó4   <€ V ^8„  d   QhRS[ S[,          RR/# ©r    r©   r"   N©rR   r2   )r$   Ú__classdict__s   "€r%   r&   Ú$SessionPersistenceMixin.__annotate__¨  s   ø€ ÷ 5ñ 5¹TÁ$½Zð 5ÈDñ 5r'   c                óÚ  € \        V RR4      p\        V RR4      p\        V RR4      p\        V RR4      pVe   Vf   Vf   Vf   R# ^ Tu;8:  d   \        V4      8  d   M MW,          MRp\        V\        4      '       d   VP	                  R4      R8X  g   R# \        WfP	                  R4      V4      '       d   \        WfP	                  R4      V4      VR&   Ve   WFR	&   Ve   WVR
&   R# R# )z‹Rewrite the current-turn user message in place: some paths send an API-only variant that must not
leak into transcripts or resumed history.rÍ   Nr^   r¸   Ú!_persist_user_message_platform_idr‰   rŠ   rO   r·   rÁ   )r_   r–   r1   r2   r,   rS   rY   )Úselfr©   r™   rP   r·   Úplatform_idr!   s   &&     r%   Ú$_apply_persist_user_message_overrideÚ<SessionPersistenceMixin._apply_persist_user_message_override¨  sá   € ô �dÐ7¸Ó>ˆÜ˜4Ð!AÀ4ÓHˆÜ˜DÐ"CÀTÓJˆ	Ü˜dÐ$GÈÓNˆØŠ;˜8Ò+°	Ò0AÀkÒFYÙØ  CÖ7¬#¨h«-×7ˆhŽm¸TˆÜ˜3¤×%Ò%¨#¯'©'°&«/¸VÔ*CÙÜ% c¯7©7°9Ó+=¸x×HÒHÜ8¸¿g¹gÀiÓ>PÐRZÓ[ˆC�	‰NØÒ Ø(�ÑØÒ"Ø)4Ð%Ó&ñ #r'   Nc                óT   <€ V ^8„  d   QhRS[ S[,          RS[ S[,          R,          /# )r    r©   rÊ   Nr  )r$   r  s   "€r%   r&   r  »  s*   ø€ ÷ &ñ &©©d­ð &É4ÑPTÍ:ÐX\ÕK\ñ &r'   c                ó"  € ^ RI Hp \        V 4      ;_uu_ 4        V P                  V4       Wn        V P                  W4       V P                  e   V P                  P                  4        V! V 4       RRR4       R#   + '       g   i     R# ; i)u¬  Save to SQLite on any exit path. Trailing empty-response scaffolding is dropped from
the live list; the persist override is applied to the DB row only.

The persist user-message *override* is NOT applied here â€” it is resolved inside
``_flush_messages_to_session_db`` and written only to the DB row, never mutating the live message
list used by the API call (#48677 is thus closed for every persist caller, not just this one).
)Únote_turn_persistedN)Úagent.agent_runtime_helpersr%  r‚   Ú)_drop_trailing_empty_response_scaffoldingÚ_session_messagesÚ_flush_messages_to_session_dbrè   Úflush_token_counts)r  r©   rÊ   r%  s   &&& r%   Ú_persist_sessionÚ(SessionPersistenceMixin._persist_session»  sl   € õ 	DÜ˜4× Õ ð ×:Ñ:¸8ÔDØ%-Ô"Ø×.Ñ.¨xÔNà×ÑÒ+Ø× Ñ ×3Ñ3Ô5Ù Ô%÷ !× × Ò ús   šAA=Á=B	c                ó4   <€ V ^8„  d   QhRS[ S[,          RR/# r  r  )r$   r  s   "€r%   r&   r  Ð  s    ø€ ÷ 	ñ 	Á$ÁtÅ*ð 	ÐQUñ 	r'   c                ó\   a€ R V3R llpV! RR4      '       d   SP                  4        K!  R# )a  Pop empty-response retry scaffolding from the tail. The
assistant(tool_calls) / tool rows before it stay: they were saved before the tools ran, so
dropping them from the live history only makes the model repeat a side effect the durable
transcript already records.c                ó0   € V ^8„  d   QhR\         R\        /# )r    Úkeysr"   )r:   r#   )r$   s   "r%   r&   ÚWSessionPersistenceMixin._drop_trailing_empty_response_scaffolding.<locals>.__annotate__Õ  s   € ÷ 	pñ 	pœð 	p¤ñ 	pr'   c                  óÜ   <€ \        S4      ;'       dZ    \        SR,          \        4      ;'       d;    \        ;QJ d    V3R lV  4       F  '       g   K   R# 	  R# ! V3R lV  4       4      # )é   c              3   óT   <"  € T F  pSR,          P                  V4      x € K  	  R# 5i)r3  Nr“   r+   )r-   rÇ   r©   s   & €r%   r/   ÚbSessionPersistenceMixin._drop_trailing_empty_response_scaffolding.<locals>.tail.<locals>.<genexpr>Ö  s(   øé € ÐLoÑjnÐefÈXÐVXÍ\×M]ÑM]Ð^_×M`ÐM`Ójnùs   ƒ%(TFr“   )r#   r1   r2   r3   )r0  r©   s   *€r%   rš   ÚOSessionPersistenceMixin._drop_trailing_empty_response_scaffolding.<locals>.tailÕ  sM   ø€ Ü˜“>×oÐo¤j°¸"µ¼tÓ&D×oÐoÏËÔLoÑjnÓLoÏÌÐoÊÐoÈÔLoÑjnÓLoÓIoÐor'   Ú_empty_recovery_syntheticÚ_empty_terminal_sentinelN)Úpop)r  r©   rš   s   &f r%   r'  ÚASessionPersistenceMixin._drop_trailing_empty_response_scaffoldingÐ  s/   ø€ ÷
	pð 	pñ Ð.Ð0J×KÒKØ�L‰LŽNñ Lr'   zagent.agent_runtime_helpersÚrepair_message_sequencec                óV   <€ V ^8„  d   QhRS[ S[,          RS[S[ S[,          ,          /# )r    r©   rÊ   )rR   r2   r   )r$   r  s   "€r%   r&   r  Ý  s0   ø€ ÷ _ñ _±d¹4µjð _ÑX`ÑaeÑfjÕakÕXlñ _r'   c                óˆ   € \        V 4      ;_uu_ 4        V P                  W4      uuRRR4       #   + '       g   i     R# ; i)z=Serialize direct and turn-boundary session flushes per agent.N)r‚   Ú&_flush_messages_to_session_db_unlocked)r  r©   rÊ   s   &&&r%   r)  Ú5SessionPersistenceMixin._flush_messages_to_session_dbÝ  s*   € ä˜4× Õ Ø×>Ñ>¸xÓ^÷ !× × Ó ús	   ”0°A	c                ó\   <€ V ^8„  d   QhRS[ S[,          RS[S[ S[,          ,          RS[/# )r    r©   rÊ   Ú_adoption_budget)rR   r2   r   rª   )r$   r  s   "€r%   r&   r  â  s7   ø€ ÷ 'sñ 'sÙ™T�
ð'sÙ:BÁ4ÉÅ:Õ:Nð'sÙilñ'sr'   c                óò  € \        V RR4      '       g   V P                  '       g   R# . p V P                  '       g   V P                  4        \        V RR4      V P                  8H  p\        WW%4      w  rF\        WWa4       RV n        \        4       V n	        \        V4      V n        VR,          V n        R#   \         d5   p\        YYCT4      pTf    Rp?R# T P                  Y^ R7      u Rp?# Rp?ii ; i)ul  Persist un-flushed messages to SQLite. Dedup is the intrinsic ``_DB_PERSISTED_MARKER`` on each written
dict â€” not positional slices (drift after sequence repair) nor an ``id(msg)`` set (address reuse). The
persist override touches the written row only. A compression-closed session adopts its live tip and
retries exactly once.

Deduplicates via an intrinsic ``_DB_PERSISTED_MARKER`` stamped on each written message dict, so
repeated calls (from multiple exit paths) only write truly new messages â€” preventing the
duplicate-write bug (#860) without relying on positional slices that can drift after
message-sequence repair, and without a retained ``id(msg)`` set that CPython could alias onto a
freed-then-reused address (#50372). The ``_flushed_db_message_ids`` attribute is now only a one-shot
seed (translated to markers, then cleared each flush), not a persisted set.
Ú_persist_disabledFNr  :NNNT)rA  )r_   rè   r  r  r9   rÞ   rê   r  rž   r¡   r–   r¢   r¬   rï   r  r>  )	r  r©   rÊ   rA  rØ   ÚreplayrÙ   rþ   Úretrys	   &&&&     r%   r>  Ú>SessionPersistenceMixin._flush_messages_to_session_db_unlockedâ  sã   € ô" �4Ð,¨e×4Ò4¸D×<L×<LÐ<LÙØ+-ˆ
ð	sØ×+×+Ð+Ø×'Ñ'Ô)ä˜TÐ#@À$ÓGÈ4Ï?É?ÑZˆFÜ%6°tÐG[Ó%dÑ"ˆJÜ˜D¨jÔCØ/3ˆDÔ,ä+.«5ˆDÔ(Ü(+¨H«ˆDÔ%à)1°!­ˆDÔ&ÙøÜô 	sÜ$ T¨jÈHÓUˆEØŠ}Ýà×>Ñ>¸xÐpqÐ>ÓrÕrûð	sús)   «B7 ½A8B7 Â7C6ÃC1ÃC1Ã+C6Ã1C6c                óF   <€ V ^8„  d   QhRS[ S[,          RS[ S[,          /# r¨   r  )r$   r  s   "€r%   r&   r    s#   ø€ ÷ ñ ¹4Á½:ð É$ÉtÍ*ñ r'   c                ó°   € \        \        V4      ^,
          RR4       F&  pW,          P                  R4      R8X  g   K!  VRV u # 	  VP                  4       # )zcMessages before the last assistant turn (rollback point for a malformed final answer); all if none.r‰   r�   Nr“   )r•   r–   r,   Úcopy)r  r©   rŒ   s   && r%   Ú"_get_messages_up_to_last_assistantÚ:SessionPersistenceMixin._get_messages_up_to_last_assistant  sK   € ä”s˜8“} qÕ(¨"¨bÖ1ˆAØ�{�‰˜vÓ&¨+Ö5Ø  �|Ò#ñ 2ð �}‰}‹Ðr'   zagent.system_promptÚformat_tools_for_system_messageÚconvert_to_trajectory_formatc                óR   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          RS[RS[/# )r    r©   Ú
user_queryÚ	completedr†   )r$   r  s   "€r%   r&   r    s5   ø€ ÷ Dñ D©©d±3¹°8­nÕ)=ð DÉ3ð DÑ[_ñ Dr'   c                ó~   € V P                   '       g   R# V P                  WV4      p\        W@P                  V4       R# )z+Save conversation trajectory to JSONL file.N)Úsave_trajectoriesÚ_convert_to_trajectory_formatÚ_save_trajectory_to_fileÚmodel)r  r©   rO  rP  Ú
trajectorys   &&&& r%   Ú_save_trajectoryÚ(SessionPersistenceMixin._save_trajectory  s1   € à×%×%Ð%ÙØ×7Ñ7¸ÈiÓXˆ
Ü  ¯Z©Z¸ÖCr'   Úextract_api_error_contextÚdump_api_request_debug)r¬   r¡   r¢   r(  r  r*   )Nr3  )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r!  r+  r'  Ú_forwardÚ_repair_message_sequencer)  r>  rJ  Ú _format_tools_for_system_messagerS  rW  Ú_forward_staticÚ_extract_api_error_contextÚ_dump_api_request_debugÚ__static_attributes__Ú__classdictcell__)r  s   @r%   r  r  ¥  s¦   ø‡ € ÙM÷5ð 5÷&&ò &÷*	ð 	ñ  (Ð(EÐG`ÓaÐ÷_ò _÷
'sò 's÷Rð ñ (0Ð0EÐGhÓ'iÐ$Ù$,Ð-JÐLjÓ$kÐ!÷Dð Dñ "1Ð1NÐPkÓ!lÐÙ&Ð'DÐF^Ó_Ör'   r  )r7  r8  Ú_thinking_prefillÚ_verification_stop_syntheticÚ_pre_verify_syntheticÚ_kanban_stop_syntheticÚ_dropped_toolcall_nudge>   Úimagerx   Úinput_image)Ú	reasoningÚreasoning_contentÚreasoning_detailsrç   Úcodex_message_items)F)Cr_  rF   ÚloggingrD   Ú
contextlibr   Útypingr   r   r   r   r   Úagent.context_compressorr   r	   r
   r   r   r   r   Úagent.lazy_forwardr   r`  r   rc  Úagent.memory_contextr   Úagent.tool_dispatch_helpersr   r   Úagent.trajectoryr   rT  Úagent.message_metadatar   r   r   r   r   r   Úagent.transcript_repairr   Ú	getLoggerrð   r4   ro   rÅ   rÒ   r6   rL   rS   rY   ra   ri   rt   rp   r‚   r›   r¦   r²   rÈ   rÞ   rê   rø   rü   r  r  r­   r'   r%   Ú<module>r~     sê   ðñó ã Û 	Ý "ç 3Õ 3÷÷ ñ ÷ VÝ 1ç \Ý H÷ ÷  õ Að 
×	Ò	˜;Ó	'€ð Ð ò :Ð à}Ð Ø%IÐ "õaõiõõõ
 õ#õ+õdò3õ!õL<õõ*÷Z&"õR,õ$õ.õE÷Rx`ó x`r'   