+
    jHàjEŠ  ã                  óŒ  € R t ^ RIHt ^ RIt^ RIt^ RIHt ^ RIHt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 ]P2                  ! R4      t]! R4      tR	tR
tR R ltR R ltRJR R llt R R lt!RRR 3RR] ! ]R,           4      3RR] ! R4      3RR] ! R4      3RR] ! R4      3RR] ! R ]4      3R!R] ! R"4      3R#R]3R$R] ! R%4      3R&R] ! R'4      3R(R] ! R)]4      3R*R] ! R+]"PF                  4      3R,R] ! R-4      3R.R] ! R/4      3R0R] ! R14      3R2R] ! R3]4      3R4R] ! ]R,           4      3R5R] ! ]R6,           4      3R7R] ! ]R,           4      3R8R] ! ]R6,           4      3R9R] ! R:4      3R;R] ! R<4      33t$]%! R= ]$ 4       4      0 RKm,          t&R>]! R?R@4      ,           t'RA RB lt(RC]' RD]' RE2t)RLRF RG llt* ! RH RI4      t+R# )MzQRetention pruning, stale-session archiving and VACUUM policy mixin for SessionDB.)ÚannotationsN)ÚPath)ÚAnyÚDictÚIterableÚListÚOptionalÚTuple)ÚAUTO_VACUUM_MIN_FREELIST_RATIOÚ
_id_chunksÚ_non_continuation_child_sqlÚ_placeholdersÚ_sql_session_last_activeÚescape_likeÚemit_session_deleted)Úreport_startup_progressÚhermes_stateÚsz<(COALESCE(s.input_tokens, 0) + COALESCE(s.output_tokens, 0))z4COALESCE(s.actual_cost_usd, s.estimated_cost_usd, 0)c               ó    € V ^8„  d   QhRRRR/# )é   ÚvalueÚstrÚreturn© )Úformats   "Ú=/home/ubuntu/.hermes/hermes-agent/hermes_state_maintenance.pyÚ__annotate__r      s   € ÷ .ñ .�ð .˜ñ .ó    c                ó<   € R \        V P                  4       4       R 2# )Ú%)Ú_escape_likeÚlower)r   s   &r   Ú_liker"      s   € ØŒ|˜EŸK™K›MÓ*Ð+¨1Ð-Ð-r   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   ztuple[list[str], list]r   )r   s   "r   r   r      s   € ÷ "ñ "˜cð "Ð&<ñ "r   c                ó>   € ^ RI Hp V! V 4      w  r#V.\        V4      3# )é    )Ú_cwd_prefix_clause)Úhermes_state_sessionsr&   Úlist)r   r&   ÚclauseÚparamss   &   r   Ú_cwd_prefix_filterr+      s"   € Ý8Ù'¨Ó.�N€FØˆ8”T˜&“\Ð!Ð!r   c               ó   € V ^8„  d   QhRR/# )r   r)   r   r   )r   s   "r   r   r   "   s   € ÷ :ñ :�ñ :r   c                ó   a a€ V V3R  l# )c                ó4   <€ S.S'       d   S! V 4      .3# T .3# ©Nr   )Úvr)   Úconvs   &€€r   Ú<lambda>Ú_one.<locals>.<lambda>#   s   ø€ �v�h¯D¡ a£Ð 8Ñ9°aÐ 8Ñ9r   r   )r)   r1   s   ffr   Ú_oner4   "   s	   ù€ Ý9Ð9r   c               ó    € V ^8„  d   QhRRRR/# )r   ÚnowÚfloatr   úOptional[float]r   )r   s   "r   r   r   &   s   € ÷ ñ ˜ð  ñ r   c                ól   €  V'       d   V \        V4      ,
          # R#   \        \        3 d     R# i ; i)zKAge of a state_meta timestamp; None when unset or corrupt (= no prior run).N)r7   Ú	TypeErrorÚ
ValueError)r6   Úraws   &&r   Ú_seconds_sincer=   &   s4   € ðß#&ˆs”U˜3“ZÕÐ0¨DÐ0øÜ”zÐ"ô Úðús   ‚ Š œ ž3²3Úlast_active_beforeÚnotnonec                ó&   € \         R ,           R.W .3# )ú < ?zG(COALESCE(s.end_reason, '') != 'startup_orphan_reap' OR s.ended_at < ?))Ú_LAST_ACTIVE_SQL)r0   s   &r   r2   r2   2   s   € Ü	˜FÕ	"Ø	Rð	Tà	
ˆñ1r   Úlast_active_afterz >= ?Ústarted_beforezs.started_at < ?Ústarted_afterzs.started_at >= ?ÚsourceÚtruthyzs.source = ?Ú
title_likez.LOWER(COALESCE(s.title, '')) LIKE ? ESCAPE '\'Ú
end_reasonzs.end_reason = ?Ú
cwd_prefixÚmin_messageszs.message_count >= ?Úmax_messageszs.message_count <= ?Ú
model_likez.LOWER(COALESCE(s.model, '')) LIKE ? ESCAPE '\'Úproviderz+LOWER(COALESCE(s.billing_provider, '')) = ?Úuser_idzs.user_id = ?Úchat_idzs.chat_id = ?Ú	chat_typezs.chat_type = ?Úbranch_likez3LOWER(COALESCE(s.git_branch, '')) LIKE ? ESCAPE '\'Ú
min_tokensÚ
max_tokensz <= ?Úmin_costÚmax_costÚmin_tool_callsz#COALESCE(s.tool_call_count, 0) >= ?Úmax_tool_callsz#COALESCE(s.tool_call_count, 0) <= ?c              #  ó,   "  € T F
  w  p q!x € K  	  R # 5ir/   r   )Ú.0ÚnameÚ_s   &  r   Ú	<genexpr>r]   L   s   é € ÐF±~©¨¨q°!¤³~ùs   ‚zp.end_reason = 'compression'
zc.zp.idc               ó    € V ^8„  d   QhRRRR/# )r   Úcandidates_wherer   r   r   )r   s   "r   r   r   R   s   € ÷ 	%ñ 	%¨sð 	%°sñ 	%r   c                ó*   € R\          RV  R\          R2# )zTCompression ancestors of every row *candidates_where* (alias ``s``) does not select.zmWITH RECURSIVE kept(id) AS ( SELECT p.id FROM sessions c JOIN sessions p ON p.id = c.parent_session_id WHERE z@ AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.id = c.id AND zs) UNION SELECT p.id FROM kept k JOIN sessions c ON c.id = k.id JOIN sessions p ON p.id = c.parent_session_id WHERE z) SELECT id FROM kept)Ú_CONTINUATION_EDGE_SQL)r_   s   &r   Ú_continued_ancestors_sqlrb   R   s1   € ðä,Ð-ØNÐO_ÐN`ð aô -Ð-Ø#ð$ð %r   z‹WITH RECURSIVE tail(id) AS ( SELECT c.id FROM sessions c JOIN sessions p ON p.id = c.parent_session_id WHERE COALESCE(p.pinned, 0) = 1 AND zr UNION SELECT c.id FROM tail t JOIN sessions p ON p.id = t.id JOIN sessions c ON c.parent_session_id = p.id WHERE z) SELECT id FROM tailc               ó    € V ^8„  d   QhRRRR/# )r   Úaliasr   r   r   )r   s   "r   r   r   i   s   € ÷ Yñ Y˜3ð Y¨ñ Yr   c                ó"   € RV  RV  R\          R2# )zUPredicate sparing pinned rows and the unpinned continuations a pinned segment covers.z	COALESCE(z.pinned, 0) = 0 AND z.id NOT IN (Ú))Ú_PINNED_TAIL_SQL)rd   s   &r   Ú_not_pinned_sqlrh   i   s!   € à�u�gÐ1°%°¸ÔEUÐDVÐVWÐXÐXr   c                  ót  € ] tR t^ntRtR6R R lltR R ltR R ltR	R7R
R5RRRRRRRR/R R llt]	RRRRRRRR/R R ll4       t
RR/R R lltR8RR/R R llltR8RR/R R  llltR8R! R" lltRR/R# R$ lltR9R% R& lltR:R' R( lltR) R* ltR+ R, ltR- R. ltR/ R0 ltR1 R2 lt^Z^RR^]3R3 R4 lltR5tR# );ÚSessionMaintenanceMixinzKRetention pruning, stale-session archiving and VACUUM policy for SessionDB.Nc               ó    € V ^8„  d   QhRRRR/# )r   Úsessions_dirúOptional[Path]r   Úintr   )r   s   "r   r   Ú$SessionMaintenanceMixin.__annotate__q   s   € ÷  ñ  °~ð  ÐQTñ  r   c                ó  a a€ \         P                   ! 4       R,
          oVV 3R lpS P                  V4      ;'       g    . p\        S VRVR7       ^ RIHp V! S 4       V'       d   TMR F  pS P                  W4       K  	  \        V4      # )zCRemove empty TUI ghost sessions (no messages, no title, >24hr old).é€Q c                ó.  <€ V P                  R S34      P                  4        Uu. uF  q^ ,          NK  	  pp^ RIHp V! W4      p\	        V4       F"  pV P                  R\        V4       R2V4       K$  	  V'       d   SP                  V 4       V# u upi )aZ  
                SELECT id FROM sessions
                WHERE source = 'tui'
                  AND title IS NULL
                  AND ended_at IS NOT NULL
                  AND started_at < ?
                  AND NOT EXISTS (
                      SELECT 1 FROM messages WHERE messages.session_id = sessions.id
                  )
            )Úretire_prunableú"DELETE FROM sessions WHERE id IN (rf   )ÚexecuteÚfetchallÚ hermes_state_mutation_retirementrs   r   r   Ú#_delete_unreferenced_system_prompts)ÚconnÚrÚidsrs   ÚchunkÚcutoffÚselfs   &    €€r   Ú_doÚ?SessionMaintenanceMixin.prune_empty_ghost_sessions.<locals>._dot   s¢   ø€ Ø!%§¡ð 	/ð �ó	"÷ %™H›Jð	"'ó 	(ñ 	"'˜A�Q—4�4ñ 	"'ˆCð 	(õ IÙ! $Ó,ˆCÜ# Cž�Ø—‘ÐAÄ-ÐPUÓBVÐAWÐWXÐYÐ[`Öañ )çØ×8Ñ8¸Ô>ØˆJùò!	(s   ¥BÚghost_prune©Úreasonrl   ©Úcollect_retired_mediar   )ÚtimeÚ_execute_writer   Úhermes_state_mediar…   Ú_remove_session_filesÚlen)r~   rl   r   Úremoved_idsr…   Úsidr}   s   f&    @r   Úprune_empty_ghost_sessionsÚ2SessionMaintenanceMixin.prune_empty_ghost_sessionsq   sv   ù€ ä—’“˜uÕ$ˆö	ð$ ×)Ñ)¨#Ó.×4Ð4°"ˆÜ˜T ;°}ÐS_Õ`Ý<Ù˜dÔ#ß".‘;°BÒ6ˆCØ×&Ñ& |Ö9ñ 7ä�;ÓÐr   c               ó    € V ^8„  d   QhRRRR/# )r   r{   zIterable[str]r   Úsetr   )r   s   "r   r   ro   Ž   s   € ÷ rñ r mð r¸ñ rr   c           	     ód   € V Uu0 uF  q0P                  WRR7      '       g   K  VkK!  	  up# u upi )z«Ids in *ids* protected by a live turn lease / compression lock. Idle compression-ended
parents are closed, not live, so they are not guarded (prune and delete share this).T)Úallow_closed_compression_parent)Ú_write_guards_reject)r~   ry   r{   rŒ   s   &&& r   Ú_guarded_idsÚ$SessionMaintenanceMixin._guarded_idsŽ   s/   € ñ  #Óq™s˜×&?Ñ&?ÀÐkoÐ&?×&p—�™sÑqÐqùÒqs   …-¤-c               ó    € V ^8„  d   QhRRRR/# )r   rŒ   r   r   Úboolr   )r   s   "r   r   ro   “   s   € ÷ ñ ¨cð Àñ r   c                ót   € ^ RI Hp ^ RIHp  V P                  ! W3RRRRRRRR/VB  R	#   YE3 d     R# i ; i)
z‰True when a live turn lease / compression lock protects ``sid``; expired or
dead-holder guards are reclaimed and fenced as a side effect.)Ú!SessionCompressionInProgressError)ÚSessionTurnLeaseLostErrorÚcompression_lock_holderNÚturn_lease_holderÚreject_active_turn_leaseTÚreject_active_compression_lockF)r   r™   Úhermes_state_errorsrš   Ú_check_transcript_write_guards)r~   ry   rŒ   Úkwargsr™   rš   s   &&&,  r   r“   Ú,SessionMaintenanceMixin._write_guards_reject“   sk   € õ 	CÝAð	Ø×/Ò/Øñ^Ø37ð^ØKOð^à)-ð^àNRð^àV\ò^ñ
 øð 2ÐMô 	Úð	ús   Ž+ «7¶7ÚsourcesÚexclude_idsÚexclude_pinnedFÚheartbeat_staleness_secondsÚ!heartbeat_ownership_grace_secondsÚrespect_gateway_heartbeatsTc               ó8   € V ^8„  d   QhRRRRRRRRRR	R
R	RRRR/# )r   Úmax_idle_secondsr7   r£   útuple[str, ...]r¤   r¥   r—   r¦   r8   r§   r¨   r   z	list[str]r   )r   s   "r   r   ro       sV   € ÷ 9.ñ 9.Ø#(ð9.Ø3Bð9.à$ð9.à;?ð9.ð &5ð9.ð ,;ð	9.ð `dð	9.ð
 
ñ9.r   c               ó  a aaa€ \         ;QJ d    . R V 4       F  NK  	  5M! R V 4       4      pV^ 8:  g	   V'       g   . # YVr©V	'       d   V	^ 8”  g
   V^,          p	V
e   V
^ 8¼  g   T	p
\        P                  ! 4       ;q±,
          pV'       d   R\        R4       2MRpR\        R4       R2pRpV'       d   VR,          pW¹,
          V
3pR\	        V4       R	V RV 2o. VOVNVNVO5oVVVV 3R
 lpS P                  V4      ;'       g    . # )u€  Close session rows orphaned by a dead gateway process (its in-process disconnect grace timer died
with it, leaving ``ended_at IS NULL`` forever).  Rows of ``sources`` whose ``started_at`` AND
canonical last activity are both older than ``max_idle_seconds`` get
``end_reason='startup_orphan_reap'`` (the ``started_at`` predicate protects fresh compression/branch
children whose copied activity is old).  Only pass sources whose lifecycle the caller owns â€” never
messaging platforms like ``telegram`` (ending those triggers a routing loop).  ``exclude_ids`` spares
rows this process still holds.  Non-destructive: messages kept, row resumable, first-reason-wins.
With ``respect_gateway_heartbeats`` a row is reaped only when no live backend (heartbeat within
``heartbeat_staleness_seconds``, default ``2 * max_idle_seconds``) could own it: B owns S if
``B.started_at <= S.started_at + grace`` (default = staleness) â€” grace covers a migrating backend
whose sessions predate its first heartbeat, bounded so a PID-reuse respawn cannot protect rows
forever.  Disable the gate only for state.db-owned sources.  SELECT, live-lease validation and UPDATE
run in one ``BEGIN IMMEDIATE`` transaction; active leases/locks spare the row, expired guards are
removed so their owner is fenced.

See #65194.
``exclude_pinned`` is intended for broad automatic sweeps; pinned rows remain explicitly
recoverable. See #60609.
c              3  ó8   "  € T F  q'       g   K  Vx € K  	  R # 5ir/   r   )rZ   r   s   & r   r]   ÚBSessionMaintenanceMixin.sweep_orphaned_sessions.<locals>.<genexpr>¹   s   é € Ð-¡˜1«1—Q’Q£ùs   ‚	�
ú AND ÚsessionsÚ zstarted_at < ? AND rA   z| AND NOT EXISTS (SELECT 1 FROM gateway_heartbeats h WHERE h.last_heartbeat >= ? AND h.started_at <= sessions.started_at + ?)z AND source IN (rf   c                ó¢  <€ V P                  R S 2S4      P                  4       pS Uu0 uF  q"'       g   K  \        V4      kK  	  ppR V 4        Uu. uF&  pWC9  g   K  S	P                  W4      '       d   K$  VNK(  	  ppV'       g   . # V P                  R\	        V4       RS 2\
        P
                  ! 4       .VOSO54       V# u upi u upi )z.SELECT id FROM sessions WHERE ended_at IS NULLc              3  óF   "  € T F  p\        VR ,          4      x € K  	  R# 5i)ÚidN)r   )rZ   Úrows   & r   r]   ÚOSessionMaintenanceMixin.sweep_orphaned_sessions.<locals>._do.<locals>.<genexpr>Ï   s   é € Ð&FÁ¸#¤s¨3¨t­9§~ ~Ãùs   ‚!zRUPDATE sessions SET ended_at = ?, end_reason = 'startup_orphan_reap' WHERE id IN (z) AND ended_at IS NULL)ru   rv   r   r“   r   r†   )
ry   ÚrowsÚxÚexcludedrŒ   Úvictimsr¤   Úscope_paramsÚ	scope_sqlr~   s
   &     €€€€r   r   Ú<SessionMaintenanceMixin.sweep_orphaned_sessions.<locals>._doË   sß   ø€ Ø—<‘<Ð"PÐQZÐP[Ð \Ø ,ó.ß.6©h«jð á(3Ó9© 1³qœœ˜Až©ˆHÐ9Ù&FÁÔ&Fó \Ñ&F˜sØÑ-ô Ø6:×6OÑ6OÐPT×6Z÷ �sÑ&FˆGð \çØ�	à�L‰Lð!Ü!.¨wÓ!7Ð 8Ð8NÈyÈkð[ä—’“Ð6˜wÐ6¨Ñ6ô8ð ˆNùò :ùò\s   ©	C·CÁCÁ CÁ9Cr   )Útupler†   rh   r   r   r‡   )r~   rª   r£   r¤   r¥   r¦   r§   r¨   ÚsrcsÚhb_stalenessÚhb_gracer6   r}   Ú	pin_scopeÚorphan_predicateÚheartbeat_paramsr   r»   r¼   s   f$$d$$$$         @@r   Úsweep_orphaned_sessionsÚ/SessionMaintenanceMixin.sweep_orphaned_sessions    s%  û€ ÷2 ŒuÑ-¡Ó-�u‰uÑ-¡Ó-Ó-ˆØ˜qÔ ¯ØˆIØ!<�hß °Ô!1Ø+¨aÕ/ˆLØÒ$¨°Q¬Ø#ˆHÜŸš›Ð$�#Õ8ˆß=K�eœO¨JÓ7Ð8Ñ9ÐQSˆ	Ø0Ô1IÈ*Ó1UÐ0VÐVZÐ[ÐØ.0Ðß%Øð "gõ hÐà #Õ 2°HÐ=ÐØ&¤}°TÓ':Ð&;¸1¸Y¸KÀuÐM]ÐL^Ð_ˆ	ØA˜ÐA˜vÐA vÐAÐ0@ÑAˆ÷	ð 	ð ×"Ñ" 3Ó'×-Ð-¨2Ð-r   ÚarchivedÚinclude_pinnedÚlineage_tips_onlyÚinclude_openc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   rÇ   zOptional[bool]rÈ   r—   rÉ   rÊ   r   útuple[str, list]r   )r   s   "r   r   ro   Ü   s3   € ÷ -ñ -¨ð -ÐPTð -Ø/3ð-ØKOð-à*:ñ-r   c                óh  € \        V4      \        ,
          pV'       d   \        R\        V4      : 24      hV'       d   . MR.pV'       d   VP	                  R4       . p\
         Fd  w  r‰p
VP                  V4      pV	R8X  d   Vf   K$  M\        V4      '       g   K8  V
! V4      w  rÍVP                  V4       VP                  V4       Kf  	  \        V \        4      '       d   VP	                  R\        V 4       24       V'       g   VP	                  \        4       4       RP                  V4      V3# )um  Shared WHERE clause for bulk prune/archive selection (alias ``s``): ``_PRUNE_FILTERS``
AND together, only ended sessions are ever candidates, ``archived`` is tri-state
(None = both), ``*_like`` are case-insensitive substrings, the rest exact.
``lineage_tips_only`` (bulk archive) drops compression ancestors: they are archived with
their tip, never on their own age â€” matching an old ancestor would fan out over the lineage
and hide its OPEN, recently active tip (#115489). ``include_open`` lifts the ended guard
for reversible archive only â€” never for prune/delete (#139597: desktop sessions never set
``ended_at``).zQSessionMaintenanceMixin._prune_filter_where() got an unexpected keyword argument ús.ended_at IS NOT NULLz+COALESCE(s.end_reason, '') <> 'compression'r?   zs.archived = r¯   )r�   Ú_PRUNE_FILTER_NAMESr:   ÚminÚappendÚ_PRUNE_FILTERSÚgetr—   ÚextendÚ
isinstancern   rh   Újoin)rÇ   rÈ   rÉ   rÊ   ÚfiltersÚunknownÚclausesr*   r[   ÚappliesÚbuildr   Únew_clausesÚ
new_paramss   $$$$,         r   Ú_prune_filter_whereÚ+SessionMaintenanceMixin._prune_filter_whereÛ   sþ   € ô �g“,Ô!4Õ4ˆßÜð 0Ü03°G³Ñ/?ðAó Bð Bç$‘"Ð+CÐ*DˆßØ�N‰NÐHÔIØˆß$2Ñ ˆD˜5Ø—K‘K Ó%ˆEØ&-°Ô&:�Õ!ÄÀUÇÄÙ*/°«,Ñ'�Ø—‘˜{Ô+Ø—‘˜jÖ)ñ %3ô �h¤×%Ò%Ø�N‰N˜]¬3¨x«=¨/Ð:Ô;çØ�N‰Nœ?Ó,Ô-Ø�|‰|˜GÓ$ fÐ,Ð,r   Úwhole_lineagesc               ó    € V ^8„  d   QhRRRR/# )r   rà   r—   r   rÌ   r   )r   s   "r   r   ro   ü   s    € ÷ bñ bÐPTð bÐaqñ br   c               ó@  € Ved   VP                  R4      fQ   VP                  R4      f>   V^ 8  d   \        RV: R24      h\        P                  ! 4       VR,          ,
          VR&   V P                  ! R	RV/VB w  rVV'       g   WV3# V R\	        V4       R2. VOVO3# )
z¸Translate the legacy age window into the shared activity filter, then build WHERE.
``whole_lineages`` (prune) keeps a compression ancestor while any continuation after it
is unmatched.r>   rD   z"older_than_days must be >= 0, got zO: a negative retention builds a future cutoff that matches every ended session.rq   rF   z AND s.id NOT IN (rf   r   )rÓ   r;   r†   rÞ   rb   )r~   Úolder_than_daysrF   r×   rà   Úwherer*   s   &&&&$  r   Ú_prune_whereÚ$SessionMaintenanceMixin._prune_whereü   sÆ   € ð Ò'¨G¯K©KÐ8LÓ,MÒ,UØ—K‘KÐ 0Ó1Ò9Ø Ô"Ü Ø8¸Ñ8Kð LYð YóZð Zô -1¯IªI«K¸?ÈUÕ;RÕ,SˆGÐ(Ñ)Ø×0Ò0ÑJ¸ÐJÀ'ÑJ‰ˆßØ�=Ð ð �Ð*Ô+CÀEÓ+JÐ*KÈ1ÐMÐOaÐQWÐOaÐZ`ÐOaÐaÐar   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rã   r8   rF   ú
str | Nonerà   r—   r   zlist[dict[str, Any]]r   )r   s   "r   r   ro     s1   € ÷ 
Lñ 
L°_ð 
LÐU_ð 
LØ.2ð
LØJ^ñ
Lr   c               ó¢   € V P                  WWCR7      w  rVV P                  R\         RV R2V4       Uu. uF  p\        V4      NK  	  up# u upi )z¶Dry-run: sessions a matching prune/archive would touch, oldest first (``older_than_days``
= inactivity threshold: freshest of ``last_activity_at`` / latest message / ``started_at``).©rà   zRSELECT s.id, s.source, s.title, s.model, s.started_at,
                           z~ AS last_active,
                           s.ended_at, s.message_count, s.archived
                    FROM sessions s WHERE z?
                    ORDER BY last_active ASC, s.started_at ASC)rå   Ú	_read_allrB   Údict)r~   rã   rF   rà   r×   rä   r*   rµ   s   &&&$,   r   Úlist_prune_candidatesÚ-SessionMaintenanceMixin.list_prune_candidates  s|   € ð ×)Ñ)¨/À7Ð)Ój‰ˆØ%)§^¡^ðÜ,Ð-ð .+à+0¨'ð 2?ðBð DJô&Kó Lñ &K˜c”�S–	ñ &Kñ Lð 	Lùò Ls   µAÚpinned_onlyc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rã   r8   rF   rè   rï   r—   r   rn   r   )r   s   "r   r   ro     s1   € ÷ 	`ñ 	`°?ð 	`ÐS]ð 	`Ø)-ð	`ØEHñ	`r   c               ó°   € V'       d   RVR&   V P                  WV4      w  rVV'       d
   VR,          p\        V P                  RV 2V4      ^ ,          4      # )z¦Count-only :meth:`list_prune_candidates`; ``pinned_only`` counts rows carrying the pin
itself, not the continuations it protects (CLI reports spared pinned sessions).TrÈ   z AND COALESCE(s.pinned, 0) = 1ú&SELECT COUNT(*) FROM sessions s WHERE )rå   rn   Ú	_read_one)r~   rã   rF   rï   r×   rä   r*   s   &&&$,  r   Úcount_prune_matchesÚ+SessionMaintenanceMixin.count_prune_matches  sY   € ÷ Ø(,ˆGÐ$Ñ%Ø×)Ñ)¨/À7ÓK‰ˆßØÐ5Õ5ˆEÜ�4—>‘>Ð$JÈ5È'Ð"RÐTZÓ[Ð\]Õ^Ó_Ð_r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rã   r8   rF   rè   r   rn   r   )r   s   "r   r   ro   %  s(   € ÷ eñ e¸ð eÐXbð eØ/2ñer   c                ó  € VP                  R4      '       d   ^ # V P                  WV4      w  rERpVP                  V4      '       g   \        R4      hRV\	        V4      R  2p\        V P                  RV 2V4      ^ ,          4      # )z±Count open sessions a matching prune/archive skips (``ended_at`` guard inverted);
visibility-only. With ``include_open`` the selection already contains them: nothing is skipped.rÊ   rÎ   z0prune filter lost its ended-session safety guardzs.ended_at IS NULLNrò   )rÓ   rå   Ú
startswithÚRuntimeErrorrŠ   rn   ró   )r~   rã   rF   r×   rä   r*   Úended_guardÚ
open_wheres   &&&,    r   Úcount_open_prune_matchesÚ0SessionMaintenanceMixin.count_open_prune_matches%  s‹   € ð �;‰;�~×&Ò&ÙØ×)Ñ)¨/À7ÓK‰ˆØ.ˆØ×Ñ ×,Ò,ÜÐQÓRÐRØ)¨%´°KÓ0@Ð0AÐ*BÐ)CÐDˆ
Ü�4—>‘>Ð$JÈ:È,Ð"WÐY_Ó`ÐabÕcÓdÐdr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Ú	idle_daysr7   r¥   r—   r   rn   r   )r   s   "r   r   ro   2  s"   € ÷ ñ °ð È$ð ÐZ]ñ r   c               óL  € Ve   V^ 8  d   ^ # \         P                   ! 4       \        V4      R,          ,
          pV'       d   R\        4        2MRpV P                  RV R\         R2V P
                  V34      pV F  pV P                  V^ ,          4       K  	  \        V4      # )uU  Archive every session untouched for ``idle_days`` (freshest of ``last_activity_at`` /
latest message / ``started_at``); may archive unended sessions.  ``archived = 0`` makes
repeats no-ops; only lineage tips (``end_reason <> 'compression'``) are candidates â€” a
stale tip archives its chain via :meth:`set_session_archived`, so an old compressed-away
root with a recent continuation is never matched.  The hidden canonical Bot Chat (same
predicate as :meth:`set_session_pinned`) is exempt: only a deliberate archive may retire
it, since archiving releases its registry title to the next Bot open.ç     õ@zAND r±   z–
            SELECT s.id FROM sessions s
            WHERE s.archived = 0
              AND COALESCE(s.end_reason, '') <> 'compression'
              zd
              AND NOT (COALESCE(s.hidden, 0) <> 0 AND COALESCE(s.title, '') = ?)
              AND z7 < ?
            ORDER BY s.started_at ASC
            )r†   r7   rh   rë   rB   ÚCANONICAL_BOT_CHAT_TITLEÚ_auto_archive_lineagerŠ   )r~   rÿ   r¥   r}   Ú
pin_clauser·   rµ   s   &&$    r   Úarchive_stale_sessionsÚ.SessionMaintenanceMixin.archive_stale_sessions2  sª   € ð Ò 	¨A¤ÙÜ—’“œu YÓ/°'Õ9Õ9ˆß3A�tœOÓ-Ð.Ñ/Àrˆ
Ø�~‰~ðð ˆlð ä#Ð$ð %ðð ×/Ñ/°Ð8ó	:ˆó ˆCà×&Ñ& s¨1¥vÖ.ñ ô �4‹yÐr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   rÿ   r7   Úmin_interval_hoursrn   r¥   r—   r   údict[str, Any]r   )r   s   "r   r   ro   M  s,   € ÷ ñ ØðØ8;ðØRVðà	ñr   c                ó0  € RRR^ /p \         P                   ! 4       p \        V P                  R4      ;'       g    R4      pV'       d   WV,
          VR,          8  d   RVR&   V# \        RR	R
7       V P                  WR7      ;qtR&   V P                  R\        V4      4       V^ 8”  d   \        P                  RWq4       V#   \        \        3 d    Rp L•i ; i  \         d0   p\        P                  RT4       \        T4      TR&    Rp?T# Rp?ii ; i)z¥Idempotent, non-destructive auto-archive of sessions idle for ``idle_days``; state_meta
``last_auto_archive`` gates runs within ``min_interval_hours``. Never raises.ÚskippedFrÇ   Úlast_auto_archiveç        é  Tç      Œ@Ústate_db_auto_archive©Úphase)r¥   z=state.db auto-archive: archived %d session(s) idle >= %s daysz state.db auto-archive failed: %sÚerrorN)r†   r7   Úget_metar:   r;   r   r  Úset_metar   ÚloggerÚinfoÚ	ExceptionÚwarning)	r~   rÿ   r  r¥   Úresultr6   ÚlastrÇ   Úexcs	   &&&&     r   Úmaybe_auto_archiveÚ*SessionMaintenanceMixin.maybe_auto_archiveM  s  € ð
 #,¨U°JÀÐ!Bˆð	'Ü—)’)“+ˆCðÜ˜TŸ]™]Ð+>Ó?×FÐFÀ3ÓG�÷ ˜�
Ð%7¸$Õ%>Ô>Ø$(��yÑ!Ø�ô $ EÐ1HÕIØ,0×,GÑ,GÈ	Ð,GÓ,qÐqˆH˜jÑ)à�M‰MÐ-¬s°3«xÔ8Ø˜!Œ|Ü—‘ØSÐU]ôð ˆøô' œzÐ*ô Ø’ðûô  ô 	'Ü�N‰NÐ=¸sÔCÜ! #›hˆF�7�OØˆûð	'úsL   ˆC žC »C ÁC Á
C Á%AC ÃCÃC ÃCÃC ÃDÃ&$DÄDc               ó0   € V ^8„  d   QhRRRRRRRRR	R
RR/# )r   rã   r8   rF   rè   rl   rm   Úexclude_active_write_guardsr—   Údeleted_idsúOptional[list]r   rn   r   )r   s   "r   r   ro   l  s:   € ÷ .ñ .¨oð .ÈJð .Ø%3ð.ØY]ð.à$2ð.àILñ.r   c                ód  a aaaa€ VP                  R4      '       d   \        R4      hS P                  WVRR7      w  oo. oVVV VV3R lpS P                  V4      pV'       d   \	        S SRVR7       ^ RIHp	 V	! S 4       S F  p
S P                  W:4       K  	  Ve   VP                  S4       V# )	aâ  Delete ended sessions inactive for ``older_than_days`` (an explicit ``started_before`` /
``last_active_before`` overrides it; None = no implicit bound) matching the filters.
Children outside the window are orphaned (parent NULLed), not cascade-deleted.  With
*sessions_dir*, transcript files are removed outside the DB transaction.
``exclude_active_write_guards`` (automatic maintenance) skips rows under a live turn lease
or compression lock while expired/dead holders are reclaimed and fenced.  A compression
ancestor is deleted only together with every continuation after it (``whole_lineages``).
``deleted_ids`` (the owning gateway) receives the removed ids, so it can retire their
in-memory routes and live sessions after the commit.rÊ   zJprune_sessions() never deletes open sessions: include_open is archive-onlyTrê   c                ó2  <€ V P                  R S 2S4      pVP                  4        Uu0 uF  q"R,          kK  	  ppS'       d   VS
P                  W4      ,          p^ RIHpHp V! V \        V4      4      pV! V 4       V'       g   ^ # \        V4       Fa  p\        V4      pV P                  RV R2V4       V P                  RV R2V4       V P                  RV R2V4       S	P                  V4       Kc  	  S
P                  V 4       \        V4      # u upi )z"SELECT s.id FROM sessions s WHERE r´   )Úprune_retired_tombstonesrs   zIUPDATE sessions SET parent_session_id = NULL WHERE parent_session_id IN (rf   z*DELETE FROM messages WHERE session_id IN (rt   )ru   rv   r”   rw   r%  rs   Úsortedr   r   rÔ   rx   rŠ   )ry   Úcursorrµ   Úsession_idsr%  rs   r|   Úphr   r‹   r~   rä   Úwhere_paramss   &       €€€€€r   r   Ú3SessionMaintenanceMixin.prune_sessions.<locals>._do~  s  ø€ Ø—\‘\Ð$FÀuÀgÐ"NÐP\Ó]ˆFØ06·±Ô0AÓBÑ0A¨˜tŸ9˜9Ñ0AˆKÐBß*Ø˜t×0Ñ0°ÓCÕC�ßbÙ)¨$´°{Ó0CÓDˆKÙ$ TÔ*ßÙä# KÖ0�Ü" 5Ó)�Ø—‘ÐhÐikÐhlÐlmÐnÐpuÔvØ—‘ÐIÈ"ÈÈQÐOÐQVÔWØ—‘ÐAÀ"ÀÀQÐGÈÔOØ×"Ñ" 5Ö)ñ 1ð ×4Ñ4°TÔ:Ü�{Ó#Ð#ùò! Cs   ©DÚpruner‚   r„   )	rÓ   r;   rå   r‡   r   rˆ   r…   r‰   rÔ   )r~   rã   rF   rl   r   r!  r×   r   Úcountr…   rŒ   r‹   rä   r*  s   f&&&f&,    @@@r   Úprune_sessionsÚ&SessionMaintenanceMixin.prune_sessionsl  s®   ü€ ð �;‰;�~×&Ò&ô ÐiÓjÐjØ"×/Ñ/°ÈÐaeÐ/ÓfÑˆˆ|Ø!#ˆ÷	$ñ 	$ð& ×#Ñ# CÓ(ˆßÜ   {¸7ÐQ]Õ^Ý<Ù˜dÔ#ÛˆCØ×&Ñ& |Ö9ñ àÒ"Ø×Ñ˜{Ô+Øˆr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Únamesr«   Úfail_msgr   r   r"  r   )r   s   "r   r   ro   œ  s!   € ÷ 	ñ 	 ?ð 	¸cð 	Ànñ 	r   c                óŽ  €  V P                  4       ;_uu_ 4       pV P                  f    RRR4       R# V Uu. uF5  p\        VP                  RV 24      P	                  4       ^ ,          4      NK7  	  upuuRRR4       # u upi   + '       g   i     R# ; i  \
         d!   p\        P                  Y%4        Rp?R# Rp?ii ; i)z_Integer PRAGMAs over the existing connection (never a byte probe); None + debug log on failure.NzPRAGMA )Ú	_read_ctxÚ_connrn   ru   Úfetchoner  r  Údebug)r~   r1  r2  ry   r[   r  s   &&&   r   Ú_page_pragmasÚ%SessionMaintenanceMixin._page_pragmasœ  s    € ð	Ø—‘×!Ô! TØ—:‘:Ò%Ø÷ "Ñ!ñ W\Ó\ÑV[Èdœ˜DŸL™L¨7°4°&Ð)9Ó:×CÑCÓEÀaÕHÖIÑV[Ñ\÷ "Ò!ùò ]÷ "×!Ð!ûô ô 	Ü�L‰L˜Ô'Ýûð	úsO   ‚B šBªB ´B¸;B Á3BÁ5
B Â BÂB	ÂB ÂB ÂCÂ$B?Â?Cc               ó   € V ^8„  d   QhRR/# )r   r   zOptional[int]r   )r   s   "r   r   ro   §  s   € ÷ Añ A Mñ Ar   c                ó`   € V P                  RR4      pVf   R# V^ ,          V^,          ,          # )a4  ``page_count * page_size``: main-file size once the WAL is checkpointed in.  Prefer
over ``os.path.getsize`` when reporting a VACUUM: in WAL mode the rewrite lands in
``-wal`` and the checkpoint is refused while another connection holds a read-mark, so
a stat() delta understates the win and can go negative.z"Could not read logical DB size: %sN)Ú
page_countÚ	page_size©r8  ©r~   Úvaluess   & r   Úlogical_size_bytesÚ*SessionMaintenanceMixin.logical_size_bytes§  s4   € ð
 ×#Ñ#Ð$?ÐAeÓfˆØ’~ˆtÐ@¨6°!­9°v¸aµyÕ+@Ð@r   c               ó   € V ^8„  d   QhRR/# )r   r   r8   r   )r   s   "r   r   ro   ¯  s   € ÷ 
]ñ 
] ñ 
]r   c                ó€   € V P                  RR4      pVf   R# V^ ,          ^ 8”  d   V^,          V^ ,          ,          # R# )uÉ  Reclaimable fraction (``freelist_count / page_count``) gating VACUUM in
:meth:`maybe_auto_prune_and_vacuum`; None = fall back to the time throttle.

``PRAGMA freelist_count / PRAGMA page_count`` read over the existing connection (never a byte-level
probe of the live file â€” see ``sqlite_safe_read``). This is what VACUUM would actually give back; it
is the gate :meth:`maybe_auto_prune_and_vacuum` uses to decide whether a full rewrite pays off
(#54189).
z!Could not read freelist ratio: %sNr  )r<  Úfreelist_countr>  r?  s   & r   Ú_freelist_ratioÚ'SessionMaintenanceMixin._freelist_ratio¯  sE   € ð ×#Ñ#Ð$DÐFiÓjˆØ’~ˆtÐ\ÀVÈAÅYÐQRÄ]¨F°1­I¸¸q½	Õ,AÐ\ÐX[Ð\r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úmoder   r2  r   ÚNoner   )r   s   "r   r   ro   »  s!   € ÷ (ñ ( Cð (°3ð (¸4ñ (r   c                	ó¢   €  V P                   P                  R V R24       R#   \         d!   p\        P	                  Y#4        Rp?R# Rp?ii ; i)zPRAGMA wal_checkpoint(rf   N)r5  ru   r  r  r7  )r~   rI  r2  r  s   &&& r   Ú_try_checkpointÚ'SessionMaintenanceMixin._try_checkpoint»  sB   € ð	(Ø�J‰J×ÑÐ!7¸°v¸QÐ?Ö@øÜô 	(Ü�L‰L˜×'Ò'ûð	(ús   ‚# £A®A	Á	Ac               ó   € V ^8„  d   QhRR/# )r   r   rn   r   )r   s   "r   r   ro   Á  s   € ÷ ñ ˜ñ r   c                ó  € V P                  4        ^ p V P                  4       pV P
                  ;_uu_ 4        V P                  4        V P                  f   V P                  RR7       V P                  RR4       V P                  P                  R4       V P                  RR	4       V P                  4        RRR4       V#   \         d!   p\        P	                  RT4        Rp?LÄRp?ii ; i  + '       g   i     T# ; i)
uJ  VACUUM to reclaim space after large deletes (SQLite never shrinks on its own).
Takes an exclusive lock â€” callers must ensure no other writers are active.  FTS5
segments are merged first (:meth:`optimize_fts`) so their pages are reclaimed too;
returns the number of FTS indexes optimized (0 on merge failure / no FTS). A quarantined
handle (corrupt image, replaced file, lost WAL generation) raises before any rewrite: a
VACUUM reads every page and commits the result back, turning contained damage into an
amplified one (#105670). Same guard ``_execute_write`` applies to every write.z%FTS optimize before VACUUM failed: %sNÚwrite)ÚcontextÚPASSIVEz1WAL checkpoint (PASSIVE) before VACUUM failed: %sÚVACUUMÚTRUNCATEz1WAL checkpoint (TRUNCATE) after VACUUM failed: %s)Ú_raise_if_db_corruptÚoptimize_ftsr  r  r  Ú_lockÚ_raise_if_db_replacedr5  Ú_reopen_after_close_lockedrL  ru   Ú_record_db_file_identity)r~   Ú	optimizedr  s   &  r   ÚvacuumÚSessionMaintenanceMixin.vacuumÁ  sà   € ð 	×!Ñ!Ô#Øˆ	ð	IØ×)Ñ)Ó+ˆIð �Z�Z‹ZØ×&Ñ&Ô(Ø�z‰zÒ!Ø×/Ñ/¸Ð/Ô@ð × Ñ  Ð,_Ô`Ø�J‰J×Ñ˜xÔ(à× Ñ  Ð-`Ôað ×)Ñ)Ô+÷ ð Ðøô ô 	IÜ�N‰NÐBÀC×HÑHûð	IúçŽZð Ðús#   ”C ¸B C0ÃC-ÃC(Ã(C-Ã0D	c               ó4   € V ^8„  d   QhRRRRRRRRRRR	R
RR/# )r   Úretention_daysrn   r  r\  r—   rl   rm   Úmin_vacuum_interval_daysÚmin_vacuum_freelist_ratior7   r   r	  r   )r   s   "r   r   ro   Þ  sQ   € ÷ lñ lØ!ðlØ<?ðlØNRðlà$ðlàGJðlð $)ðlð 
ñ	lr   c           	     ó”  € ^ RI HpHp RRR^ R^ RR/p	Ve   V^ 8  d   \        P	                  RV4       R	V	R&   V	# V! V P
                  4      p
V
f   R	V	R&   V	#  \        P                  ! 4       p\        W°P                  R
4      4      pVe   WÂR,          8  d   R	V	R&   V	V! V
4       # \        RRR7       V P                  WR	R7      ;V	R&   p\        RRR7       V P                  \        V4      R,          V P                  R	RR7      p\        V4      V	R&   \        W°P                  R4      4      pVRJ ;'       g    WõR,          8¬  pV'       Ed   V^ 8”  Ed   V'       Ed   T P                  4       ;V	R&   p^ RIHpHp V! V P
                  4      V! V P
                  V R7      ,           pV'       dM   \        V4      V	R&   \        P'                  R\        V4      RP)                  R VR,           4       4      4       MoVe   VV8”  d@    \        RRR7       V P+                  4        R	V	R&   V P-                  R\/        V4      4       M%\        P'                  RVR ,          VR ,          4       V P-                  R
\/        V4      4       V'       g   V^ 8”  d3   \        P3                  R!\        V4      YÑV	R,          '       d   R"MR#4       T! T
4       T	#   \0         d!   p\        P	                  RT4        Rp?LŒRp?ii ; i  \0         d/   p\        P	                  R$T4       \/        T4      T	R%&    Rp?LlRp?ii ; i  T! T
4       i ; i)&a%  Idempotent startup auto-maintenance (never raises): prune inactive sessions, reap stale open
state-owned rows, optional VACUUM.  Runs at most once per ``min_interval_hours``; VACUUM has its own
``min_vacuum_interval_days`` throttle and also requires ``freelist_count / page_count`` >
``min_vacuum_freelist_ratio`` so a small prune on a dense multi-GB database never triggers a full
rewrite.  Stale-open reconciliation: cron/kanban/subagent/one-shot CLI rows never set ``ended_at``
when their process dies and prune only deletes ended rows, so after pruning, open rows from
:attr:`_AUTO_PRUNE_STALE_OPEN_SOURCES` older than ``retention_days`` are closed
(``startup_orphan_reap``); they stay resumable and age from their close.  Returns ``{"skipped",
"pruned", "closed", "vacuumed"}`` plus ``"freelist_ratio"`` when a VACUUM was considered and
``"error"`` on failure.

Records the last run timestamp in state_meta so subsequent calls within ``min_interval_hours``
no-op. Designed to be called once at startup from long-lived entrypoints (CLI, gateway, cron
scheduler). See #54189.
When *sessions_dir* is provided, on-disk transcript files (``.json`` / ``.jsonl`` /
``request_dump_*``) for pruned sessions are removed as part of the same sweep (issue #3015).
Messaging and UI sources are never touched here. See #54189.
)Ú_release_auto_maintenance_lockÚ"_try_acquire_auto_maintenance_lockr  FÚprunedÚclosedÚvacuumedNz«state.db auto-maintenance skipped: sessions.retention_days=%r is outside the allowed range (a whole number of days >= 0); set sessions.auto_prune: false to disable pruningTÚlast_auto_pruner  r  Ústate_db_auto_pruner  )rã   rl   r   Ústate_db_auto_sweepr  )rª   r£   r¥   r¨   Úlast_vacuumrq   Úfreelist_ratio)Úforeign_state_db_holdersÚin_process_state_db_holders)ÚexcludeÚvacuum_skipped_holderszestate.db auto-maintenance: skipping VACUUM, %d other process(es) hold the store or a WAL sidecar (%s)z, c              3  ó4   "  € T F  w  rV R V 2x € K  	  R# 5i)Ú:Nr   )rZ   ÚpidÚtargets   &  r   r]   ÚFSessionMaintenanceMixin.maybe_auto_prune_and_vacuum.<locals>.<genexpr>/  s   é € Ð/aÑU`ÁkÀc°3°%°q¸¸Õ0AÓU`ùs   ‚:Né   NÚstate_db_auto_vacuumzstate.db VACUUM failed: %sz_state.db auto-maintenance: skipping VACUUM, only %.1f%% of pages reclaimable (threshold %.0f%%)g      Y@zgstate.db auto-maintenance: closed %d stale open session(s), pruned %d session(s) inactive for %d days%sz	 + VACUUMr±   z$state.db auto-maintenance failed: %sr  )Úhermes_state_repairrc  rd  r  r  Údb_pathr†   r=   r  r   r.  rÅ   r7   Ú_AUTO_PRUNE_STALE_OPEN_SOURCESrŠ   rF  Úhermes_state_holdersrm  rn  r7  rÖ   r\  r  r   r  r  )r~   r_  r  r\  rl   r`  ra  rc  rd  r  Úmaintenance_lockr6   Úsince_prunere  rf  Úsince_vacuumÚ
vacuum_dueÚratiorm  rn  Úholdersr  s   &&&&&&&               r   Úmaybe_auto_prune_and_vacuumÚ3SessionMaintenanceMixin.maybe_auto_prune_and_vacuumÞ  s0  € ÷. 	kØ"+¨U°H¸aÀÈ1ÈjÐZ_Ð!`ˆØÒ! ^°aÔ%7ô �N‰Nðiàô ð !%ˆF�9ÑØˆMÙ=¸d¿l¹lÓKÐØÒ#Ø $ˆF�9ÑØˆMðE	=Ü—)’)“+ˆCÜ(¨¯m©mÐ<MÓ.NÓOˆKØÒ&¨;ÈdÕ9RÔ+RØ$(��yÑ!Øñ@ +Ð+;Õ<ôu $ EÐ1FÕGØ(,×(;Ñ(;Ø .Ðgkð )<ó )mð mˆF�8Ñ˜vä# EÐ1FÕGØ×1Ñ1Ü!& ~Ó!6¸Õ!@Ø×;Ñ;ÈDØ+0ð 2ó ˆFô
  # 6›{ˆF�8Ñô *¨#¯}©}¸]Ó/KÓLˆLØ%¨Ð-×aÐa°Ð\aÕAaÑ1aˆJßˆv˜& 1�*¯¨Ø37×3GÑ3GÓ3IÐI�Ð'Ñ(¨5÷Ká3°D·L±LÓAÙ8¸¿¹ÈtÔTõU�çÜ7:¸7³|�FÐ3Ñ4Ü—L‘Lð6ä˜G› d§i¡iÑ/aÐU\Ð]_ÖU`Ó/aÓ&aõcð ’] eÐ.GÔ&GðJô 0°Ð=SÕTØŸ™œØ-1˜˜zÑ*ØŸ™ m´S¸³XÕ>ô —L‘Lð "Rà!&¨¥Ð0IÈEÕ0QôSð �M‰MÐ+¬S°«XÔ6ß˜ !œÜ—‘ð Jä ›K¨ÐPVÐWa×PbÔPbÁÐhjôlñ +Ð+;Ô<Øˆøô% %ô JÜŸ™Ð'CÀS×IÑIûðJûô ô 	'ä�N‰NÐAÀ3ÔGÜ! #›hˆF�7ŽOûð	'ûñ
 +Ð+;Õ<úsq   ÁAL Â(B(L ÅL Å"AL Æ2AL È
=K ÉBL ËK>ËK9Ë4L Ë9K>Ë>L ÌL:Ì$L5Ì0L= Ì5L:Ì:L= Ì=
Mr   r/   )ÚtuiÚdesktopÚsubagent)NN)rv  é   T)éZ   NNFN)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r�   r”   r“   rÅ   ÚstaticmethodrÞ   rå   rí   rô   rü   r  r  r.  r8  rA  rF  rL  r\  r
   r‚  Ú__static_attributes__r   r   r   rj   rj   n   s  † ÙU÷ õ:rõ
ð9.ØEcð9.à')ð9.àBGð9.ð 8<ð9.ð >Bð	9.ð gk÷	9.ðv ð-¸$ð -ÐW\ð -Ø6;ð-ØRWö-ó ð-ð@bÐW\÷ bñ$
LØ5:÷
Lð 
Lñ	`Ø05÷	`ð 	`÷eðÐQU÷ ÷6÷>.õ`	õAõ
]õ(õð< %'À"ÐUYØ'+ÈRØ+I÷ló lr   rj   r/   >   rÇ   rÈ   rÉ   )r   ),r�  Ú
__future__r   Úloggingr†   Úpathlibr   Útypingr   r   r   r   r   r	   Úhermes_state_commonr
   r   r   r   r   r   r    r   Úhermes_startup_watchdogr   Ú	getLoggerr  rB   Ú_TOKENS_SQLÚ	_COST_SQLr"   r+   r4   r=   r   r!   rÒ   Ú	frozensetrÏ   ra   rb   rg   rh   rj   r   r   r   Ú<module>rš     sW  ðÙ Wå "ã Û Ý ß =× =÷÷ ñ õ <ð 
×	Ò	˜>Ó	*€á+¨CÓ0Ð ØL€ØB€	õ.õ"÷:õð ˜9ñ 'ð ð ˜)¡TÐ*:¸WÕ*DÓ%EÐFØ�y¡$Ð'9Ó":Ð;Ø�i¡Ð&9Ó!:Ð;Øˆx™˜nÓ-Ð.Ø�8™TÐ"SÐUZÓ[Ð\Ø�8™TÐ"4Ó5Ð6Ø�8Ð/Ð0Ø�Y¡Ð%;Ó <Ð=Ø�Y¡Ð%;Ó <Ð=Ø�8™TÐ"SÐUZÓ[Ð\Ø�™4Ð MÈsÏyÉyÓYÐZØ�™$˜Ó/Ð0Ø�™$˜Ó/Ð0Ø�(™DÐ!2Ó3Ð4Ø�H™dÐ#YÐ[`ÓaÐbØ�9™d ;°Õ#8Ó9Ð:Ø�9™d ;°Õ#8Ó9Ð:Ø�™D ¨WÕ!4Ó5Ð6Ø�™D ¨WÕ!4Ó5Ð6Ø�y¡$Ð'LÓ"MÐNØ�y¡$Ð'LÓ"MÐNð5€ñ8  ÑF±~ÓFÓFÒI|Õ|Ð ð :Ñ<WÐX\Ð^dÓ<eÕeÐ õ	%ð<à<RÐ;Sðà4Ð5Ø+ð,Ð ÷Y÷
\ó \r   