+
    jHàjK‰  ã                  óÜ   € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
HtHtHt ^ RIHt ]
'       d	   ^ RIHtHt ]P&                  ! R4      t]! 4       tRtR	 R
 lt ! R R4      tR# )a  SessionStore storage plumbing: per-profile SessionDB handle resolution and the routing-index
load/save paths (state.db gateway_routing primary, sessions.json legacy mirror). Mixin split out of
``gateway/session.py``; bound onto ``SessionStore`` via the MRO.)ÚannotationsN)ÚPath)ÚTYPE_CHECKINGÚAnyÚDictÚOptional)Úatomic_json_write)ÚSessionEntryÚSessionSourcezgateway.sessiona©  LEGACY MIRROR of the gateway routing index (the primary copy lives in the gateway_routing table in ~/.hermes/state.db). Maps messaging session keys (agent:main:<platform>:...) to active session IDs. This is NOT the session list. ALL sessions (CLI, TUI, and gateway) live in ~/.hermes/state.db and are shown by `hermes sessions list` and `/sessions`. Disable this file with `gateway.write_sessions_json: false` in config.yaml.c               ó    € V ^8„  d   QhRRRR/# )é   ÚexcÚBaseExceptionÚreturnÚbool© )Úformats   "Ú@/home/ubuntu/.hermes/hermes-agent/gateway/session_persistence.pyÚ__annotate__r   #   s   € ÷ Mñ M˜}ð M°ñ Mó    c                óN   € \        V \        4      ;'       d    R\        V 4      9   # )zCTest-isolation guard: must stay a loud failure and is never cached.zlive-system guard)Ú
isinstanceÚRuntimeErrorÚstr)r   s   &r   Ú_is_live_system_guardr   #   s!   € ä�cœ<Ó(×LÐLÐ-@ÄCÈÃHÑ-LÐLr   c                  ó  € ] tR t^(tRtR tRRL/R R lltRMR R lltRMR	 R
 lltR t	]
R 4       t]P                  R R l4       t]
R 4       tR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR  R! ltR" R# lt]R$ R% l4       tR& R' ltR( R) ltR* R+ lt]R, R- l4       tR. R/ ltR0 R1 ltR2 R3 lt R4 R5 lt!R6 R7 lt"R8 R9 lt#R: R; lt$R< R= lt%R> R? lt&R@ RA lt'RB RC lt(RD RE lt)RFRRGRH/RI RJ llt*RKt+R# )NÚSessionPersistenceMixinzWSessionStore storage plumbing: SessionDB handle resolution and routing-index load/save.c                ó`  € V P                   ;_uu_ 4        \        V4      p\        V P                  P	                  4       4       F%  w  r4VP
                  V9   g   K  V P                  V K'  	  V F  pV P                  P                  VR4       K!  	  RRR4       R#   + '       g   i     R# ; i)zBPublish committed retirement; durable fences reject delayed saves.N)Ú_lockÚsetÚlistÚ_entriesÚitemsÚ
session_idÚ_session_owner_hintsÚpop)ÚselfÚsession_idsÚtargetsÚkeyÚentryÚsids   &&    r   Úretire_runtime_sessionsÚ/SessionPersistenceMixin.retire_runtime_sessions+   s{   € à�Z�Z‹ZÜ˜+Ó&ˆGÜ" 4§=¡=×#6Ñ#6Ó#8Ö9‘
�Ø×#Ñ# wÖ.ØŸ™ cÒ*ñ :ó �Ø×)Ñ)×-Ñ-¨c°4Ö8ñ ÷ �Z�ZŠZús   •ABÁ6BÂB-	Úexpiringc               ó   € V ^8„  d   QhRR/# ©r   r   ÚNoner   )r   s   "r   r   Ú$SessionPersistenceMixin.__annotate__5   s   € ÷ 	Dñ 	DÈdñ 	Dr   c               óZ   € V P                  R4      pVf   \        R4      hV! WVR7       R# )a«  Durably retire *session_ids* in the routing index's own database (fences + route
drop in one transaction). A delete committed in another profile's store fences only that
store; the shared index lives in the launch home, so its saves need the fence here too,
keyed by the deleting profile's routing *namespace* (``retire_gateway_routes``).
Raises when the routing store cannot take the fence: the delete must not report success.Úretire_gateway_routesNz,routing store unavailable for deletion fence)Ú	namespacer.   )Ú_routing_db_methodr   )r&   r'   r5   r.   Úretires   &&$$ r   Úfence_retired_routesÚ,SessionPersistenceMixin.fence_retired_routes5   s0   € ð ×(Ñ(Ð)@ÓAˆØŠ>ÜÐMÓNÐNÙˆ{¸(×Cr   Nc               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2   @   s   € ÷ Cñ C¸tñ Cr   c                ó   € V P                   pV P                  R,          pVe   VP                  4       '       g   R# ^ RIHp Vf   RM
\        V4      pV P                  R\        P                  4      ;_uu_ 4        \        VRRR7      ;_uu_ 4       p\        P                  ! V4      pRRR4       \        X\        4      '       g    RRR4       R# / pVP                  4        F�  w  rš\        V
\        4      '       d   V
P                  R4      MRpV	P!                  R	4      '       gH   \        V\"        4      '       d2   Ve   Wµ9  g(   V! W)\        P$                  ! RV/4      4      '       d   KŒ  W¨V	&   K’  	  \'        V4      \'        V4      8  d   \)        W8R
R7       RRR4       R#   + '       g   i     EL; i  + '       g   i     R# ; i)až  Drop from an existing sessions.json every route a routing save now skips as retired
(``route_retired``, the check ``replace_gateway_routing_entries`` applies), only those naming
*session_ids* when given. Runs whatever ``write_sessions_json`` says and never creates the
file: the mirror is derived, and a retired route left in it would come back through the
next start's legacy import once a prune's fence ages out.úsessions.jsonN©Úroute_retiredÚ
_save_lockÚrú	utf-8-sig©Úencodingr#   Ú_é€  ©Úmode)Ú_routing_dbÚsessions_dirÚexistsÚ hermes_state_mutation_retirementr>   r   Ú_lazyÚ	threadingÚLockÚopenÚjsonÚloadr   Údictr"   ÚgetÚ
startswithr   ÚdumpsÚlenr   )r&   r'   ÚdbÚsessions_filer>   r(   ÚfÚdataÚkeptr)   r*   r+   s   &&          r   Ústrip_retired_mirror_routesÚ3SessionPersistenceMixin.strip_retired_mirror_routes@   sR  € ð ×ÑˆØ×)Ñ)¨OÕ;ˆØŠ:˜]×1Ñ1×3Ò3ÙÝBØ%Ò-‘$´3°{Ó3CˆØ�Z‰Z˜¤i§n¡n×5Õ5Ü�m S°;×?Õ?À1Ü—y’y “|�÷ @ä˜d¤D×)Ò)Ø÷	 6Ñ5ð
 ˆDØ"Ÿj™jžl‘
�Ü1;¸EÄ4×1HÒ1H�e—i‘i Ô-Èd�Ø—N‘N 3×'Ò'¬z¸#¼s×/CÒ/CÈÒH[Ð`cÔ`rÙ,¨R´d·j²jÀ,ÐPSÐATÓ6U×VÔVØ %˜“Iñ	 +ô
 �4‹yœ3˜t›9Ô$Ü! -¸EÕB÷ 6Ñ5ß?×?Ð?ú÷ 6×5Ð5ús8   Á;F<ÂF(	Â)F<ÃAF<Ä)AF<Å0.F<Æ(F9Æ3	F<Æ<G	c               ó   € V ^8„  d   QhRR/# )r   Údb_pathúOptional[Path]r   )r   s   "r   r   r2   Z   s   € ÷ [ñ [¸ñ [r   c                ó®   aa€ ^ RI Hp ^ RIHo Ve   \	        V4      M\	        V! 4       4      oVV3R lpV P
                  P                  SV\        R7      # )aù  SessionDB for the active profile scope. ``db_path`` pins the store; otherwise
``_default_db_path()`` follows the context-local HERMES_HOME (resolved per call so
multiplexed profiles reach their own store). Handles are cached per path; failed opens enter
a bounded backoff during which callers keep using the JSONL fallback.

Resolving here rather than once in ``__init__`` is the whole fix for #88532: it lets the scoping
that the multiplexed inbound path already performs actually reach session storage.
)Ú_default_db_path)Úacquirec                 ó|   <€  S! S4      #   \          d&   p \        T 4      '       g   \        R T  24       h Rp ? ii ; i)zL[gateway] Warning: SQLite session store unavailable, falling back to JSONL: N)Ú	Exceptionr   Úprint)Úerc   Úpaths    €€r   Ú_openÚHSessionPersistenceMixin._open_session_db_for_active_scope.<locals>._openh   sB   ø€ ðÙ˜t“}Ð$øÜô Ü,¨Q×/Ò/ÜÐhÐijÐhkÐlÔmØûðús   ƒ ‹;– 6¶;)Únon_cacheable)Úhermes_staterb   Úhermes_state_registryrc   r   Ú_db_handle_cacherS   r   )r&   r_   rb   ri   rc   rh   s   &&  @@r   Ú!_open_session_db_for_active_scopeÚ9SessionPersistenceMixin._open_session_db_for_active_scopeZ   sJ   ù€ õ 	2Ý1à 'Ò 3Œt�GŒ}¼Ñ>NÓ>PÓ9Qˆö	ð ×$Ñ$×(Ñ(¨¨uÔDYÐ(ÓZÐZr   c                ó$   € \        V R\        4      # )zKReturn the explicitly pinned DB (``store._db = x``), else ``_DB_UNPINNED``.Ú
_db_pinned)ÚgetattrÚ_DB_UNPINNED©r&   s   &r   Ú
_pinned_dbÚ"SessionPersistenceMixin._pinned_dbr   s   € ä�t˜\¬<Ó8Ð8r   c                óZ   € V P                  4       pV\        J d   V P                  4       # T# )a%  The SessionDB for the active profile scope, or a pinned override. Assigning ``store._db``
pins that value for every subsequent read (tests install a fake or disable the DB with
``store._db = None``); unpinned, each read resolves the scope so a multiplexed profile's
writes reach its own store.)rv   rt   ro   )r&   Úpinneds   & r   Ú_dbÚSessionPersistenceMixin._dbv   s,   € ð —‘Ó"ˆØ;AÄ\Ó;Qˆt×5Ñ5Ó7Ð]ÐW]Ð]r   c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2   €   s   € ÷  ñ  ˜Dñ  r   c                	ó   € Wn         R # ©N)rr   )r&   Úvalues   &&r   rz   r{      s   € àŽr   c                óÌ   € V P                  4       pV\        Jd   V# \        V RR4      p Vf   V P                  # V P	                  VR,          R7      #   \
         d     R# i ; i)u	  The one store that owns the routing index, whatever scope is active. ``_entries`` is one
flat dict holding every profile's keys, so it must persist to ONE file (``_routing_home``),
not whichever profile is scoped â€” otherwise a mid-turn rewrite and the unscoped startup
load see different copies and crash markers under a secondary profile go unrecovered. A
pinned handle still wins; bare test instances lacking the handle cache report no DB.

Reading it through ``_db`` made that file whichever profile happened to be scoped at the time: a
whole-index rewrite during one profile's turn copied every other profile's routing rows into that
profile's store, and startup â€” which runs unscoped â€” then loaded a different copy than the one the
last writer produced. See #66887.
Ú_routing_homeNústate.db©r_   )rv   rt   rs   rz   ro   re   )r&   ry   Úhomes   &  r   rH   Ú#SessionPersistenceMixin._routing_dbƒ   si   € ð —‘Ó"ˆØœÓ%ØˆMÜ�t˜_¨dÓ3ˆð	ØŠ|Ø—x‘x�Ø×9Ñ9À$ÈÕBSÐ9ÓTÐTøÜô 	Úð	ús   «A »A ÁA#Á"A#c               ó    € V ^8„  d   QhRRRR/# )r   Úsession_keyúOptional[str]r   r   )r   s   "r   r   r2   ›   s   € ÷ Hñ H°-ð HÀMñ Hr   c                óˆ   € \        V P                  RR4      '       g   R# V P                  V4      pV'       d   VR8X  d   R# T# )u  The non-default profile that owns *session_key*, or None (ambient store is authoritative:
multiplexing off, or legacy ``agent:main``). Deliberately does NOT cover "that profile has
no directory" â€” ownership and resolvability are separate questions for ``_db_for_key``.Úmultiplex_profilesFNÚdefault)rs   ÚconfigÚ_profile_from_session_key)r&   r‡   Úprofiles   && r   Ú_named_profile_for_keyÚ.SessionPersistenceMixin._named_profile_for_key›   s?   € ô �t—{‘{Ð$8¸%×@Ò@ÙØ×0Ñ0°Ó=ˆß" g°Ô&:ˆtÐGÀÐGr   c               ó    € V ^8„  d   QhRRRR/# )r   r‡   rˆ   r   r`   r   )r   s   "r   r   r2   ¤   s   € ÷ ñ °ð À>ñ r   c                ó,  € V P                  V4      pVf   R# V P                  pW#9   d	   W2,          # Rp ^ RIHpHp V! V4      '       d   \        V! V4      4      pTe   YCT&   T#   \         d#   p\        P                  RY4       Rp Rp?L2Rp?ii ; i)zZHERMES_HOME of the profile owning *session_key*, or None (no named owner or
unresolvable).N)Úget_profile_dirÚprofile_existsz)Could not resolve profile home for %r: %s)	r�   Ú_profile_home_cacheÚhermes_cli.profilesr“   r”   r   re   ÚloggerÚdebug)r&   r‡   rŽ   Úcacher„   r“   r”   r   s   &&      r   Ú_profile_home_for_keyÚ-SessionPersistenceMixin._profile_home_for_key¤   s–   € ð ×-Ñ-¨kÓ:ˆØŠ?ÙØ×(Ñ(ˆØÔØ•>Ð!Ø#ˆð	ßKÙ˜g×&Ò&Ü™O¨GÓ4Ó5�ð ÒØ!�'‰NØˆøô ô 	Ü�L‰LÐDÀkÔWØ�Dûð	ús   µA& ÁA& Á&BÁ1BÂBc               ó   € V ^8„  d   QhRR/# )r   r‡   rˆ   r   )r   s   "r   r   r2   »   s   € ÷ &ñ & }ñ &r   c                óâ  € V P                  4       pV\        Jd   V# V P                  V4      pVfU   \        V RR4      pVe   \        V P                  RR4      '       g   V P
                  #  V P                  VR,          R7      # V P                  V4      pVf   \        P                  RW14       R#  V P                  VR,          R7      #   \         d     R# i ; i  \         d     R# i ; i)u(  The SessionDB holding *session_key*'s rows, whatever scope is active (the owning profile
is encoded in the key). ``_db`` follows the ambient HERMES_HOME that only the inbound message
path installs; unscoped background work (expiry watcher) would otherwise write profile rows
into the ROOT store until the stale-route self-heal drops a live conversation.

Background work runs unscoped while operating on every profile's keys out of the single process-wide
``_entries`` dict â€” ``_session_housekeeping_watcher`` is the clearest case â€” so it reads and writes the
ROOT store for rows that actually live under ``profiles/<name>/state.db``. The two writers then
drift apart on the same logical session until the routing index disagrees with the row and the
#54878 self-heal drops a live conversation (#66887).
Nr�   rŠ   Fr‚   rƒ   zggateway.session: profile %r has no resolvable home (key %r); refusing to fall back to the ambient store)rv   rt   r�   rs   rŒ   rz   ro   re   rš   r—   Úwarning)r&   r‡   ry   rŽ   Úrouting_homer„   s   &&    r   Ú_db_for_keyÚ#SessionPersistenceMixin._db_for_key»   sü   € ð —‘Ó"ˆØœÓ%ØˆMØ×-Ñ-¨kÓ:ˆØŠ?ô # 4¨¸$Ó?ˆLØÒ#¬7°4·;±;Ð@TÐV[×+\Ò+\Ø—x‘x�ðØ×=Ñ=ÀlÐU_ÕF_Ð=Ó`Ð`ð ×)Ñ)¨+Ó6ˆØŠ<ô �N‰Nð,Ø-4ôCñ ð	Ø×9Ñ9À$ÈÕBSÐ9ÓTÐTøô ô Úðûô ô 	Úð	ús$   Á-C Â4C ÃCÃCÃC.Ã-C.c               ó    € V ^8„  d   QhRRRR/# )r   r#   rˆ   r   r   )r   s   "r   r   r2   ã   s   € ÷ Sñ S°Mð SÀmñ Sr   c                ó  € V'       g   R#  \        V P                  P                  4       4       F"  pVP                  V8X  g   K  VP                  u # 	  \        V RR4      ;'       g    / P                  V4      #   \
         d     L3i ; i)zúThe routing key that owns *session_id*, or None. The published index is authoritative;
``_session_owner_hints`` covers the window where ownership is proven but routing not yet
published. Deliberately lock-free: several callers already hold ``_lock``.Nr$   )r    r!   Úvaluesr#   r‡   re   rs   rS   )r&   r#   r*   s   && r   Ú_owner_key_for_session_idÚ1SessionPersistenceMixin._owner_key_for_session_idã   s~   € ÷ Ùð	Ü˜dŸm™m×2Ñ2Ó4Ö5�Ø×#Ñ# zÖ1Ø ×,Ñ,Ò,ñ 6ô
 ˜Ð4°dÓ;×AÐA¸r×FÑFÀzÓRÐRøô ô 	Ùð	ús   Œ4A; ÁA; ÁA; Á;B	ÂB	c               ó   € V ^8„  d   QhRR/# )r   r#   rˆ   r   )r   s   "r   r   r2   ñ   s   € ÷ Lñ L¨]ñ Lr   c                ój   € V'       g   V P                   # V P                  V P                  V4      4      # )z„The SessionDB holding *session_id*'s row (owner from the index or a pre-published hint;
unknown ids fall back to the ambient store).)rz   r    r¥   )r&   r#   s   &&r   Ú_db_for_session_idÚ*SessionPersistenceMixin._db_for_session_idñ   s,   € ÷ Ø—8‘8ˆOØ×Ñ × >Ñ >¸zÓ JÓKÐKr   c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2   ø   s   € ÷ 0ñ 0 dñ 0r   c                óH   € R R lpV P                   P                  V4       R# )a
  Close every SessionDB handle this store opened (one per path). Closing only ``store._db``
would strand secondary profiles' handles with their WAL lock held ('database is locked' on
restart). Drained under the lock, closed outside it; a pinned handle is the pinner's.c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   ÚBSessionPersistenceMixin.close_all_db_handles.<locals>.__annotate__ü   s   € ÷ 	Sñ 	S˜$ñ 	Sr   c                ó‚   € ^ RI Hp  V! V 4       R#   \         d"   p\        P	                  RT4        Rp?R# Rp?ii ; i)é    )Úrelease_or_closez-SessionDB close error during handle sweep: %sN)rm   r±   re   r—   r˜   )rW   r±   r   s   &  r   Ú_closeÚ<SessionPersistenceMixin.close_all_db_handles.<locals>._closeü   s7   € Ý>ðSÙ  Ö$øÜô SÜ—‘ÐLÈc×RÒRûðSús   ˆ ’>�9¹>N)rn   Ú	close_all)r&   r²   s   & r   Úclose_all_db_handlesÚ,SessionPersistenceMixin.close_all_db_handlesø   s   € õ	Sð 	×Ñ×'Ñ'¨Ö/r   c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2     s   € ÷ )ñ ) ñ )r   c                óˆ   € V P                   ;_uu_ 4        V P                  4        RRR4       R#   + '       g   i     R# ; i)z4Load sessions index from disk if not already loaded.N)r   Ú_ensure_loaded_lockedru   s   &r   Ú_ensure_loadedÚ&SessionPersistenceMixin._ensure_loaded  s#   € à�Z�Z‹ZØ×&Ñ&Ô(÷ �Z�ZŠZús	   •0°A	c               ó    € V ^8„  d   QhRRRR/# )r   r‡   r   r   úOptional[SessionEntry]r   )r   s   "r   r   r2   
  s   € ÷ .ñ .¨ð .Ð1Gñ .r   c                óX   € V P                  4        V P                  P                  V4      # )zALoad the index and return the entry for *session_key*. Lock held.)r¹   r!   rS   )r&   r‡   s   &&r   Ú_entry_lockedÚ%SessionPersistenceMixin._entry_locked
  s#   € à×"Ñ"Ô$Ø�}‰}× Ñ  Ó-Ð-r   c               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   r2     s   € ÷ *ñ * ñ *r   c                ó¨   €  \        \        V P                  4      P                  4       4      #   \         d    \        T P                  4      u # i ; i)z~Namespace for this store's gateway_routing rows: the resolved sessions_dir, so stores
with different dirs never share entries.)r   r   rI   Úresolvere   ru   s   &r   Ú_routing_scopeÚ&SessionPersistenceMixin._routing_scope  sF   € ð	*Ü”t˜D×-Ñ-Ó.×6Ñ6Ó8Ó9Ð9øÜô 	*Ü�t×(Ñ(Ó)Ò)ð	*ús   ‚+. ® AÁAc               ó   € V ^8„  d   QhRR/# )r   Únamer   r   )r   s   "r   r   r2     s   € ÷ 4ñ 4 sñ 4r   c                ón   € \        V P                  ;'       g    RVR4      p\        V4      '       d   V# R# )zHBound ``_routing_db.<name>`` if the handle exists and has it, else None.N)rs   rH   Úcallable)r&   rÈ   Úmethods   && r   r6   Ú*SessionPersistenceMixin._routing_db_method  s3   € ä˜×)Ñ)×1Ð1¨T°4¸Ó>ˆÜ! &×)Ò)ˆvÐ3¨tÐ3r   c               ó   € V ^8„  d   QhRR/# rÂ   r   )r   s   "r   r   r2     s   € ÷ hñ h¸cñ hr   c                	óB   € R \        V RV 4       RT;'       g    R RV 2# )zgateway_chat_labels:r   Ú:Ú )rs   )ÚplatformÚscope_idÚchat_ids   &&&r   Ú_chat_labels_meta_keyÚ-SessionPersistenceMixin._chat_labels_meta_key  s0   € à%¤g¨h¸ÀÓ&JÐ%KÈ1ÈXÏ^È^ÐY[ÐL\Ð\]Ð^eÐ]fÐgÐgr   c               ó    € V ^8„  d   QhRRRR/# )r   Úsourcer
   r   r   r   )r   s   "r   r   r2      s   € ÷ ñ ¨ð ¸4ñ r   c                óø   € V P                  R4      pVf   R# V! V P                  VP                  VP                  VP                  4      \
        P                  ! VP                  VP                  .4      4       R# )a  Persist *source*'s ``chat_name`` / ``chat_topic`` as the chat's current labels, at the
moment they are observed; True once written. A session origin cannot answer "what is this
chat called now": it is written when its session is created, which can be after a newer
observation (a delayed first turn), and a reset inherits it under a fresh ``created_at``.

False when there is no database handle right now (open failed, backoff, JSONL fallback):
nothing was written, so the caller must not treat these labels as recorded.Úset_metaFT)	r6   rÔ   rÑ   rÒ   rÓ   rP   rU   Ú	chat_nameÚ
chat_topic)r&   r×   Úsetters   && r   Úrecord_chat_labelsÚ*SessionPersistenceMixin.record_chat_labels   sb   € ð ×(Ñ(¨Ó4ˆØŠ>ÙÙˆt×)Ñ)¨&¯/©/¸6¿?¹?ÈFÏNÉNÓ[Ü�zŠz˜6×+Ñ+¨V×->Ñ->Ð?Ó@ô	Bár   c               ó   € V ^8„  d   QhRR/# )r   r   zOptional[tuple]r   )r   s   "r   r   r2   /  s   € ÷ %ñ %¸/ñ %r   c                ó¼   € V P                  R4      pVf   \        R4      hV! V P                  WV4      4      pV'       g   R# \        P                  ! V4      w  rgWg3# )zÖThe ``(chat_name, chat_topic)`` last recorded for a chat, or None when none was. Raises
when the store cannot be read, including when there is no database handle right now:
"unreadable now" is not "never labelled".Úget_metaNzsession database unavailable)r6   ÚLookupErrorrÔ   rP   Úloads)r&   rÑ   rÒ   rÓ   ÚgetterÚrawrÚ   rÛ   s   &&&&    r   Úchat_labelsÚ#SessionPersistenceMixin.chat_labels/  s[   € ð ×(Ñ(¨Ó4ˆØŠ>ÜÐ<Ó=Ð=Ù�T×/Ñ/°ÀGÓLÓMˆßÙÜ $§
¢
¨3£Ñˆ	ØÐ$Ð$r   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r2   <  s   € ÷ ñ ¨4ñ r   c                ó:  € V P                  R4      pVf   R#  V! V P                  4       R7      P                  4        F*  w  r#V P                  W#4      pVf   K  W@P                  V&   K,  	  R#   \
         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)zvLoad state.db routing entries into ``_entries``; False when there is no loader or the
load failed (warned). Lock held.Úload_gateway_routing_entriesNF©ÚscopeTz1gateway.session: state.db routing load failed: %s)r6   rÅ   r"   Ú_routing_entry_from_jsonr!   re   r—   rž   )r&   Úloaderr)   Ú
entry_jsonr*   rg   s   &     r   Ú_load_routing_rows_lockedÚ1SessionPersistenceMixin._load_routing_rows_locked<  s�   € ð ×(Ñ(Ð)GÓHˆØŠ>Ùð	Ù#)°×0CÑ0CÓ0EÔ#F×#LÑ#LÖ#N‘�Ø×5Ñ5°cÓF�ØÔ$Ø).—M‘M #Ó&ñ $Oñ øÜô 	Ü�N‰NÐNÐPQÔRÝûð	ús   ™<A. ÁA. Á.BÁ9BÂBc               ó$   € V ^8„  d   QhRRRRRR/# )r   r)   r   rï   r   r½   r   )r   s   "r   r   r2   M  s"   € ÷ 	ñ 	 cð 	°sð 	Ð?Uñ 	r   c                ó  € ^ RI Hp  \        P                  ! V4      p\	        V\
        4      '       d   VP                  ! V4      #  R#   \        \        \        3 d"   p\        P                  RY4        Rp?R# Rp?ii ; i)zBParse one gateway_routing row; None (with a warning) when invalid.©r	   z%Skipping invalid routing entry %r: %sN)Úgateway.sessionr	   rP   rã   r   rR   Ú	from_dictÚ
ValueErrorÚKeyErrorÚ	TypeErrorr—   rž   )r)   rï   r	   Ú
entry_datarg   s   &&   r   rí   Ú0SessionPersistenceMixin._routing_entry_from_jsonL  ss   € õ 	1ð	LÜŸš JÓ/ˆJÜ˜*¤d×+Ò+Ø#×-Ò-¨jÓ9Ð9ð ,ñ øô œH¤iÐ0ô 	LÜ�N‰NÐBÀC×KÐKÙûð	Lús   ˆ=A	 Á	B ÁA;Á;B c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2   X  s   € ÷ ,ñ , tñ ,r   c                óX  € V P                   '       d   V P                  4        R# V P                  P                  RRR7       V P	                  4       pT;'       d    \        V P                  4      pV P                  V4      pRV n         Wn        V'       d   RMV P                  4       V n
        V'       d=   V'       d5   V P                  4        \        V RR4      '       g    V P                  4        V P#                  4        R#   \         d    \        P!                  RRR7        L6i ; i)a®  Load the routing index (lock held). state.db ``gateway_routing`` is primary;
sessions.json is the legacy import for keys the DB lacks (persisted on the next _save).

Read order (#9006 follow-up): the ``gateway_routing`` table in state.db is the primary source;
sessions.json is the legacy import path for pre-migration installs (its entries are folded in for
keys the DB doesn't have, then persisted to the DB on the next _save).
NT)ÚparentsÚexist_okÚ_write_sessions_jsonzBgateway.session: could not strip retired routes from sessions.json)Úexc_info)Ú_loadedÚ#_reconcile_recovered_routing_lockedrI   Úmkdirrð   r   r!   Ú_import_legacy_sessions_jsonÚ_routing_db_loadedÚ_entries_as_dictsÚ_routing_fallback_baselineÚ_savers   r\   re   r—   rž   Ú_prune_stale_sessions_locked)r&   Údb_load_succeededÚdb_had_entriesÚmirror_retireds   &   r   r¹   Ú-SessionPersistenceMixin._ensure_loaded_lockedX  sò   € ð �<�<ˆ<Ø×4Ñ4Ô6ÙØ×Ñ×Ñ¨°tÐÔ<Ø ×:Ñ:Ó<ÐØ*×BÐB¬t°D·M±MÓ/BˆØ×:Ñ:¸>ÓJˆØˆŒØ"3Ôß2C©$È×I_ÑI_ÓIaˆÔ'ß×/ð �J‰JŒLÜ˜4Ð!7¸×>Ò>ðxØ×4Ñ4Ô6ð 	×)Ñ)Ö+øô !ô xÜ—N‘NÐ#gÐrv�NÖwðxús   Ã"D Ä"D)Ä(D)c               ó    € V ^8„  d   QhRRRR/# )r   r  r   r   Úintr   )r   s   "r   r   r2   x  s   € ÷ &ñ &¸4ð &ÀCñ &r   c           
     ó  € ^ RI Hp V P                  R,          p^ pVP                  4       '       g   V#  \	        VRRR7      ;_uu_ 4       p\
        P                  ! V4      pRRR4       ^ pXP                  4        Fæ  w  r‰VP                  R4      '       g   W€P                  9   d   K.  \        V	\        4      '       g-   \        P                  RV\        V	4      P                  4       Kp   ^ R	IHp
 V P$                  pVe8   V
! W¸\
        P&                  ! R
V	R
,          /4      4      '       d   V^,          pK¾  VP(                  ! V	4      V P                  V&   V^,          pKè  	  V'       d(   V'       d    \        P1                  RYw^8X  d   RMR4       V#   + '       g   i     EL>; i  \*        \,        \.        3 d#   p\        P                  RYŒ4        Rp?EKb  Rp?ii ; i  \2         d   p\5        RT 24        Rp?T# Rp?ii ; i)zƒLegacy import: sessions.json fills only keys the DB lacks. Lock held. Returns how many
mirror entries it skipped as retired routes.rô   r<   r@   rA   rB   NrD   z8Skipping invalid session entry %r: expected dict, got %sr=   r#   z%Skipping invalid session entry %r: %sz\gateway.session: imported %d legacy sessions.json entr%s missing from state.db routing tableÚyÚiesz,[gateway] Warning: Failed to load sessions: )rõ   r	   rI   rJ   rO   rP   rQ   r"   rT   r!   r   rR   r—   rž   ÚtypeÚ__name__rK   r>   rH   rU   rö   r÷   rø   rù   Úinfore   rf   )r&   r  r	   rX   ÚretiredrY   rZ   Úimportedr)   rú   r>   rW   rg   s   &&           r   r  Ú4SessionPersistenceMixin._import_legacy_sessions_jsonx  s¿  € õ 	1Ø×)Ñ)¨OÕ;ˆØˆØ×#Ñ#×%Ò%ØˆNð	FÜ�m S°;×?Õ?À1Ü—y’y “|�÷ @àˆHØ#'§:¡:¦<‘�à—>‘> #×&Ò&¨#·±Ô*>ÙÜ! *¬d×3Ò3Ü—N‘NØRÐTWÜ˜ZÓ(×1Ñ1ô3ñ ð
TÝNØ×)Ñ)�BØ’~©-¸ÄÇÂØ)¨:°lÕ+CÐDóBF÷ +Gò +Gà 1�˜Ù Ø)5×)?Ò)?À
Ó)K�D—M‘M #Ñ&Ø •M’Hñ# $0÷( ŸNÜ—‘ð-Ø.6È1¼}¹ÐRWôYð
 ˆ÷; @×?Ð?ûô* #¤H¬iÐ8ô TÜ—N‘NÐ#JÈC×SÓSûðTûô ô 	FÜÐ@ÀÀÐD×EÐEØˆûð	Fúss   µG& ÁFÁ"B
G& Ã-AF+Ä8G& Ä:(F+Å"G& Å.G& Å6G& ÆF(	Æ"	G& Æ+G#ÇGÇG& ÇG#Ç#G& Ç&H
Ç1HÈH
c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2      s   € ÷ ñ ¨dñ r   c                ót  € V P                   '       g   R# . p^ p V P                   P                  4        F™  w  r4V P                  V4      pVf   K  VP                  VP                  4      pVe   VP                  R4      f   KP  V P                  W4V4      pVR8X  d   VP                  V4       K|  Vf   K‚  WpP                   V&   V^,          pK›  	  T F  pT P                   T K  	  T'       g	   T'       d   T P                  4        R# R#   \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)zÑRemove routing entries whose session has ended in state.db (startup, lock held). Stale ==
``end_reason IS NOT NULL``; rows absent from the DB are kept; a ``None`` DB handle is a
no-op; DB errors are non-fatal.NÚ
end_reasonÚprunez@gateway.session: stale-entry pruning skipped due to DB error: %s)r!   r"   r    Úget_sessionr#   rS   Ú_stale_entry_verdictÚappendre   r—   rž   r	  )	r&   Ú
stale_keysÚrecovered_keysr)   r*   rW   ÚrowÚverdictr   s	   &        r   r
  Ú4SessionPersistenceMixin._prune_stale_sessions_locked   s  € ð �}�}ˆ}ÙØˆ
Øˆð	Ø"Ÿm™m×1Ñ1Ö3‘
�ð ×%Ñ% cÓ*�Ø’:ÙØ—n‘n U×%5Ñ%5Ó6�Ø’; #§'¡'¨,Ó"7Ò"?ÙØ×3Ñ3°CÀÓD�Ø˜gÔ%Ø×%Ñ% cÖ*ØÔ(Ø)0—M‘M #Ñ&Ø" aÕ'’Nñ 4ó$ ˆCØ—‘˜cÒ"ñ çŸØ�J‰JŽLñ (øô ô 	Ü�N‰NÐ]Ð_bÔcÝûð	ús   šBD Â6D ÄD7ÄD2Ä2D7c               ó   € V ^8„  d   QhRR/# )r   r)   r   r   )r   s   "r   r   r2   À  s   € ÷ 9ñ 9¨ñ 9r   c                ó¶  € ^ RI Hp V P                  V4      pVeY   WRP                  8X  d   R# ^ RIpVP	                  V4      pWWn        \
        P                  RWP                  VR,          V4       V# RpVP                  e%    V P                  WP                  V! 4       RR7      pVež   VP                  VP                  8w  dƒ   \
        P                  RWP                  VR,          VP                  4       VP                  '       d<   V P                  VP                  4      VP                  8X  d   VP                  Vn        V# Ve+   \
        P                  R	VP                  WR,          4       R# \
        P                  R
WP                  VR,          4       R#   \         d-   p	\
        P                  RYP                  T	4        Rp	?	R# Rp	?	ii ; i)zjFor a routing entry whose row has ended: ``"prune"``, a replacement entry (repoint), or
None (keep as-is).)Ú_nowNzugateway.session: repointing stale sessions.json entry %r from ended %s (end_reason=%r) to its local receipt target %sr  T)r‡   r×   ÚnowÚraise_on_lookup_errorzRgateway.session: recovery lookup failed for stale sessions.json entry %r -> %s: %szfgateway.session: repointing stale sessions.json entry %r from ended %s (end_reason=%r) to recovered %szdgateway.session: reopened ended session %s for sessions.json entry %r (end_reason=%r); keeping routezfgateway.session: pruning stale sessions.json entry %r -> %s (end_reason=%r); left by a crashed gatewayr  )Úgateway.session_lifecycler(  Ú_local_receipt_targetr#   Úcopyr—   rž   ÚoriginÚ_recover_session_from_dbre   r˜   Ú
prompt_pinÚ_compression_tip_for_session_idr  )
r&   r)   r*   r#  r(  Úlocal_targetr-  Ú	repointedÚrecovered_entryr   s
   &&&&      r   r  Ú,SessionPersistenceMixin._stale_entry_verdictÀ  s½  € õ 	3Ø×1Ñ1°%Ó8ˆØÒ#ð ×/Ñ/Ô/ÙÛØŸ	™	 %Ó(ˆIØ#/Ô Ü�N‰NðAØBE×GWÑGWØ�LÕ! <ô1ð ÐØˆØ�<‰<Ò#ðØ"&×"?Ñ"?Ø #¯L©L¹d»fÐ\`ð #@ó #b�ð Ò&¨?×+EÑ+EÈ×IYÑIYÔ+YÜ�N‰Nð2Ø36×8HÑ8HÈ#ÈlÕJ[Ø×*Ñ*ô,ð ××ÐØ×4Ñ4°U×5EÑ5EÓFÈ/×JdÑJdÔdð .3×-=Ñ-=�Ô*Ø"Ð"ð Ò&Ü�K‰Kð1Ø27×2BÑ2BÀCÈ\ÕIZô\ñ Ü�‰ð Ø!$×&6Ñ&6¸¸LÕ8Iô	Kñ øôC ô ä—‘ðØ!×#3Ñ#3°Sô:õ ûðús   Â#F! Æ!GÆ,!GÇGc               ó   € V ^8„  d   QhRR/# )r   r   údict[str, Any]r   )r   s   "r   r   r2   û  s   € ÷ Nñ N >ñ Nr   c                ó†   € V P                   P                  4        UUu/ uF  w  rWP                  4       bK  	  upp# u uppi )z1Serializable snapshot of ``_entries``. Lock held.)r!   r"   Úto_dict)r&   r)   r*   s   &  r   r  Ú)SessionPersistenceMixin._entries_as_dictsû  s4   € à7;·}±}×7JÑ7JÔ7LÔMÑ7L©¨�—]‘]“_Ò$Ñ7LÒMÐMùÓMs   ž=c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2   ÿ  s   € ÷ Eñ E�tñ Er   c                óB   € V P                   ! V P                  4       !   R# )z;Persist the routing index while the caller holds ``_lock``.N)Ú_persist_routing_dataÚ_snapshot_routing_lockedru   s   &r   r	  ÚSessionPersistenceMixin._saveÿ  s   € à×"Ò" D×$AÑ$AÓ$CÔDr   c               ó   € V ^8„  d   QhRR/# )r   r   r  r   )r   s   "r   r   r2     s   € ÷ (ñ (°ñ (r   c                óL   € \        V R^ 4      ^,           V n        V P                  # )zëBump and return the shared routing counter (lock held). Full snapshots AND single-entry
fast saves MUST allocate from this one counter: the stale-write protection is a total order
over serialization times and silently breaks otherwise.Ú_routing_generation)rs   rB  ru   s   &r   Ú_next_routing_generation_lockedÚ7SessionPersistenceMixin._next_routing_generation_locked  s'   € ô $+¨4Ð1FÈÓ#JÈQÕ#NˆÔ Ø×'Ñ'Ð'r   c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2   
  s   € ÷ /ñ /°Tñ /r   c                óF  € \        V RR4      p\        V RR4      '       g   Vf   R# V P                  R4      pVf   R#  V! V P                  4       R7      pT P                  4       pTP                  4        Fj  w  rgT P                  Yg4      pTf   K  Ya9  d   T P                  P                  Yh4       K?  Ye9  d   KG  YV,          Y,          8X  g   K\  Y€P                  T&   Kl  	  RT n        RT n        R#   \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)	z<Merge authoritative rows after a fallback-only startup load.r  Nr  Frê   rë   z;gateway.session: recovered state.db routing load failed: %sT)rs   r6   rÅ   re   r—   rž   r  r"   rí   r!   Ú
setdefaultr  r  )	r&   Úbaselinerî   Údurabler   Úcurrentr)   rï   Údurable_entrys	   &        r   r  Ú;SessionPersistenceMixin._reconcile_recovered_routing_locked
  s  € ä˜4Ð!=¸tÓDˆÜ�4Ð-¨u×5Ò5¸Ò9IÙØ×(Ñ(Ð)GÓHˆØŠ>Ùð	Ù 4×#6Ñ#6Ó#8Ô9ˆGð ×(Ñ(Ó*ˆØ&Ÿ}™}ž‰OˆCØ ×9Ñ9¸#ÓJˆMØÒ$ÙØÔ"ð —‘×(Ñ(¨Ö<ØÔ#ÙØ• ¥Ö.Ø%2—‘˜cÓ"ñ  /ð #'ˆÔØ*.ˆÖ'øô# ô 	Ü�N‰NÐXÐZ]Ô^Ýûð	ús   ¿C4 Ã4D Ã?DÄD c               ó   € V ^8„  d   QhRR/# )r   r   ztuple[dict[str, Any], int]r   )r   s   "r   r   r2   '  s   € ÷ Pñ PÐ*Dñ Pr   c                ób   € V P                  4        V P                  4       V P                  4       3# )z:Capture immutable routing data and a monotonic generation.)r  r  rC  ru   s   &r   r>  Ú0SessionPersistenceMixin._snapshot_routing_locked'  s+   € à×0Ñ0Ô2Ø×%Ñ%Ó'¨×)MÑ)MÓ)OÐOÐOr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rZ   r7  Ú
generationr  r   r1   r   )r   s   "r   r   r2   ,  s!   € ÷ (,ñ (,¨.ð (,Àcð (,Èdñ (,r   c                ó~  € V P                  R\        P                  4      ;_uu_ 4        V\        V R^ 4      8:  d    RRR4       R# \        V RR4      pV'       d=   VP	                  4        F(  w  pw  rVWR8”  g   K  \
        P                  ! V4      W&   K*  	  RpV P                  R4      pVe†    T! VP	                  4        U	U
u/ uF  w  ršV	\
        P                  ! V
4      bK  	  up
p	V P                  4       R7      pRpV'       d,   VP	                  4        U	U
u/ uF  w  ršW›9  g   K  WšbK  	  pp	p
\        V R
R4      '       g	   V'       g    V P                  V4       W n        V'       d;   VP	                  4        U	UUu. uF  w  p	w  rÞWÒ8:  g   K  V	NK  	  uppp	 F  pW4 K  	  RRR4       R# u up
p	i u up
p	i   \         d!   p\        P                  R	T4        Rp?L±Rp?ii ; i  \         d*   pT'       g   h \        P                  RT4        Rp?L»Rp?ii ; iu uppp	i   + '       g   i     R# ; i)zASerialize all whole-index writers through one durable write lock.r?   Ú_persisted_routing_generationNÚ_fast_persisted_entriesFÚreplace_gateway_routing_entriesrë   Tz1gateway.session: state.db routing save failed: %sr   zKgateway.session: sessions.json mirror save failed after state.db commit: %s)rL   rM   rN   rs   r"   rP   rã   r6   rU   rÅ   re   r—   rž   Ú_save_sessions_jsonrS  )r&   rZ   rQ  Úfast_persistedr)   Úrevisionrï   Údb_savedÚreplacerÚkÚvr  r   ÚrevrD   s   &&&            r   r=  Ú-SessionPersistenceMixin._persist_routing_data,  sñ  € à�Z‰Z˜¤i§n¡n×5Õ5ØœW TÐ+JÈAÓNÔNØ÷ 6Ñ5ô
 % TÐ+DÀdÓKˆNßØ3A×3GÑ3GÖ3IÑ/�CÑ/˜(ØÖ,Ü$(§J¢J¨zÓ$:˜›	ñ 4Jð ˆHØ×.Ñ.Ð/PÓQˆHØÒ#ð]Ù&ÀTÇZÁZÄ\Ô'RÁ\¹T¸Q¨¬4¯:ª:°a«=Ò(8Á\Ò'RÐZ^×ZmÑZmÓZoÔp�GØ#�H÷ Ø15·±´ÔR±©¨ÀÑAQ¤ ¢±˜ÑRô �tÐ3°T×:Ò:Ç(ð	#Ø×,Ñ,¨TÔ2ð 2<Ô.÷ Ø1?×1EÑ1EÔ1GÕ]Ñ1G¡+ !¡X cÈ3ÑK\ŸA˜AÑ1GÖ]�CØ&Ò+ñ ^÷K 6Ñ5ùó (Sùó
  SøÜ ô ]Ü—N‘NÐ#VÐX[×\Ñ\ûð]ûô
 !ô #ß#Øô —N‘NðØ!÷#ñ #ûð#üô ^÷K 6×5Ð5ús§   ©H+Á0H+Á<3H+Â0F?Ã"F3Ã'!F?Ä	F?Ä
F9
Ä+F9
Ä1F?Ä4H+ÅG-Å!"H+ÆH$ÆH$ÆH+Æ3F?Æ?G*Ç
G%Ç H+Ç%G*Ç*H+Ç-H!Ç8HÈH+ÈH!È!
H+È+H<	c               ó    € V ^8„  d   QhRRRR/# )r   rZ   r7  r   r1   r   )r   s   "r   r   r2   V  s   € ÷ wñ w¨ð w¸4ñ wr   c                óT   € \        V P                  R,          R\        /VCRR7       R# )zLWrite the legacy sessions.json mirror of the routing index (atomic + fsync).r<   Ú_READMErE   rF   N)r   rI   Ú_SESSIONS_JSON_README)r&   rZ   s   &&r   rV  Ú+SessionPersistenceMixin._save_sessions_jsonV  s&   € ä˜$×+Ñ+¨oÕ=À	ÔK`Ð?iÐdhÐ?iÐpu×vr   c               ó   € V ^8„  d   QhRR/# r0   r   )r   s   "r   r   r2   Z  s   € ÷ 5ñ 5˜tñ 5r   c                ó®   € V P                   ;_uu_ 4        V P                  4       w  rRRR4       V P                  XX4       R#   + '       g   i     L$; i)zESnapshot latest state under ``_lock`` and persist after releasing it.N)r   r>  r=  )r&   rZ   rQ  s   &  r   Ú_save_entriesÚ%SessionPersistenceMixin._save_entriesZ  s9   € à�Z�Z‹ZØ#×<Ñ<Ó>ÑˆD÷ à×"Ñ" 4¨Ö4÷ �Zús   •AÁA	rú   Ú	lock_heldFc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r‡   r   rú   zOptional[dict[str, Any]]rh  r   r   r1   r   )r   s   "r   r   r2   `  s,   € ÷ +!ñ +!Øð+!Ø/Gð+!àð+!à$(ñ+!r   c               ón  € V'       d   \         P                  ! 4       MV P                  pT;_uu_ 4        V P                  P	                  V4      pVf    RRR4       R# Ve   \        V4      MVP                  4       p\        P                  ! V4      V P                  4       r‡RRR4       V P                  R4      p	V	e´    V P                  R\        P                  4      ;_uu_ 4        \        V R^ 4      X8¼  d    RRR4       R# V P                  R\
        4      p
V
P	                  V4      pVe   V^ ,          V8¼  d    RRR4       R# V	! VXV P                  4       R7       W‡3W¡&   RRR4       R# VeF   V;_uu_ 4        V P'                  4       pRRR4       \        V4      XV&   V P)                  VX4       R# V P+                  4        R#   + '       g   i     EL4; i  + '       g   i     R# ; i  \          d!   p\"        P%                  RY4        Rp?L©Rp?ii ; i  + '       g   i     Lš; i)uÅ  Persist ONE routing entry via UPSERT â€” the per-turn fast path (a full rewrite fsyncs a
multi-MB sessions.json). The key -> session_id mapping never changes here: structural
transitions use the full rewrite (which also refreshes the sessions.json mirror; it may lag
in metadata only). The revision comes from the shared routing generation counter; under
``_save_lock`` the upsert is skipped if a full snapshot or a newer fast save of this key
already persisted (the reverse case lives in ``_persist_routing_data``). No DB or a failed
upsert falls back to the full rewrite. ``entry_data`` persists a candidate BEFORE it is
published to the live entry (failure-atomic transitions); the fallback carries it too.NÚsave_gateway_routing_entryr?   rS  rT  rë   zagateway.session: single-entry routing save failed for %r (%s); falling back to full index rewrite)Ú
contextlibÚnullcontextr   r!   rS   rR   r9  rP   rU   rC  r6   rL   rM   rN   rs   rÅ   re   r—   rž   r  r=  rf  )r&   r‡   rú   rh  Úguardr*   Ú
serializedrï   rX  ÚsaverrW  Ú	persistedr   Úfallback_datas   &&$$          r   Ú_save_entryÚ#SessionPersistenceMixin._save_entry`  sÇ  € ÷ -6”
×&Ò&Ô(¸4¿:¹:ˆßŠUØ—M‘M×%Ñ% kÓ2ˆEØŠ}Ø÷ ‰Uð .8Ò-Cœ˜jÔ)ÈÏÉËˆJä#'§:¢:¨jÓ#9¸4×;_Ñ;_Ó;a˜÷ ð ×'Ñ'Ð(DÓEˆØÒð?Ø—Z‘Z ¬i¯n©n×=Õ=Ü˜tÐ%DÀaÓHÈHÔTØ÷ >Ñ=ð &*§Z¡ZÐ0IÌ4Ó%P�NØ .× 2Ñ 2°;Ó ?�IØ Ò,°¸1µÀÔ1IØ÷ >Ñ=ñ ˜+ z¸×9LÑ9LÓ9NÕOØ3;Ð2H�NÑ/÷ >ñ ð
 Ò!ç’Ø $× 6Ñ 6Ó 8�÷ ä)-¨jÓ)9ˆM˜+Ñ&Ø×&Ñ& }°hÖ?à×ÑÖ ÷? �UˆUú÷ >Ö=ñ ûÜô ?Ü—‘ð,Ø-8÷?ñ ?ûð?ú÷ —úsl   ´!GÁAGÃ(G6 Ã)G"Ã=G6 Ä:G"ÅG6 ÅG"Å)G6 ÆH$ÇG	Ç"G3	Ç-G6 Ç3G6 Ç6H!ÈHÈH!È$H4	)rr   r  rS  r  r  rB  r   r~   ),r  Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r,   r8   r\   ro   rv   Úpropertyrz   rÜ   rH   r�   rš   r    r¥   r©   rµ   rº   r¿   rÅ   r6   ÚstaticmethodrÔ   rÝ   ræ   rð   rí   r¹   r  r
  r  r  r	  rC  r  r>  r=  rV  rf  rs  Ú__static_attributes__r   r   r   r   r   (   s9  † Ùaò9ð	DÀr÷ 	D÷C÷4[ò09ð ñ^ó ð^ð 	‡Z�Zô ó ð ð ñó ðõ.Hõõ.&õPSõLõ0õ)õ
.õ
*õ4ð
 ôhó ðhõõ%õð  ô	ó ð	õ,õ@&õPõ@9õvNõEõ(õ/õ:Põ
(,õTwõ5ð+!ØJNð+!à÷+!ó +!r   r   )rx  Ú
__future__r   rl  ÚloggingrP   rM   Úpathlibr   Útypingr   r   r   r   Úutilsr   rõ   r	   r
   Ú	getLoggerr—   Úobjectrt   rb  r   r   r   r   r   Ú<module>rƒ     sg   ðñDõ #ã Û Û Û Ý ß 5Ó 5Ý #çß;ð 
×	Ò	Ð,Ó	-€ñ ‹x€ðEð õM÷
c	!ó c	!r   