Ë
    âß²joh ã                   ó  — U d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlZddlZddlmZ ddlmZ ddlmZ ddlmZmZ ddlmZmZmZmZmZmZm Z m!Z!m"Z" ddl#m$Z%m&Z'm(Z)m*Z+m,Z- dd	l.m/Z/ dd
l0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z= ddl>m?Z?m@Z@mAZAmBZBmCZCmDZDmEZE ddlFmGZGmHZHmIZI ddlJmKZK ddlLmMZMmNZN ddlOmPZP ddlQmRZR ddlSmTZT ddlUmVZV ddl.ZWddlXZYddlZm[Z[ ddl\m]Z]m^Z^m_Z_m`Z`maZambZbmcZcmdZdmeZemfZfmgZgmhZhmiZi ddljmkZk ddllmmZm ddlnmoZompZpmqZqmrZr ddlsmtZtmuZumvZv ddlwmxZx ddlymzZz ddl{m|Z| ddl}m~Z~ ddlm€Z€ ddl�m‚Z‚ 	 ddlƒZƒ e�j
                  e†«      Z‡dZˆeˆfd e‰d!eŠd"eŠfd#„Z‹d"eŠfd$„ZŒd"eŠfd%„Z� G d&„ d'eŽ«      Z� G d(„ d)eŽ«      Z�d*e‰d"e‘fd+„Z’ e“d,d-h«      Z” e!d.«      Z• e«       d/z  xZ–Z—d0Z˜d1\  Z™Zšd"efd2„Z›d3Zœd4Z�e ed5f   ežd6<   d7ed"dfd8„ZŸd3d9œd7ed:e‘d"dfd;„Z d<Z¡d=ee‰ef   d"e‘fd>„Z¢d?eee‰ef      d"eee‰ef      fd@„Z£ e	�jH                  dA«      Z¥d=ee‰ef   d"e‘fdB„Z¦d?eee‰ef      d"eee‰ef      fdC„Z§dDZ¨dEZ©dFZªdGZ«	 d«d3dHœdIe‰dJe‘d"e‰fdK„Z¬ e­«       Z®e­e‰   eždL<    e�j^                  «       Z°dMZ±e�jd                  dNk(  Z³d"e‘fdO„Z´dPe‰d"dQfdR„Zµd7ed"ee eŠe‰f      fdS„Z¶ G dT„ dUeKeMe‚eVePeRe€e~e|ezexekemeT«      Z· G dV„ dW«      Z¸ddXlm¹Z¹ ddlZddlºZºddl»Z»ddl¼Z¼dZ½dZ¾i dYdZ“d[d\“d]d^“d_d`“dadb“dcdd“dedf“dgdh“didj“dkdl“dmdn“dodp“dqdr“dsdt“dudv“dwdx“dydz“i d{d|“d}d~“dd€“d�d‚“dƒd„“d…d†“d‡dˆ“d‰dŠ“d‹dŒ“d�dŽ“d�d�“d‘d’“d“d”“d•d–“d—d˜“d™dš“d›dœ“¥d�dždŸd d¡d¢d£d¤d¥d¦d§d¨d©œ¥Z¿dª„ ZÀy# e„$ r dZƒY �Œ#w xY w)¬a-  SQLite state store for Hermes Agent: session metadata, message history, model
config, FTS5 search. WAL mode (concurrent readers + one writer); compression
splits sessions via parent_session_id chains; sessions are source-tagged
('cli', 'telegram', ...). Batch-runner / RL trajectories live elsewhere.
é    N)Údeque)Úcontextmanager)ÚPath)Úget_hermes_homeÚmkdir_under_hermes_home)	ÚAnyÚCallableÚDictÚIteratorÚListÚOptionalÚTupleÚTypeVarÚcast)ÚTITLE_SOURCE_DERIVEDÚTITLE_SOURCE_LLMÚTITLE_SOURCE_USERÚescape_likeÚstat_db_file_identity)Úread_only_db_uri)Ú_DELETED_WAL_GENERATION_MSGÚ_DISK_IO_ERROR_MARKERÚ_STATE_DB_CORRUPT_MSGÚ_STATE_DB_GENERATION_KEYÚ_STATE_DB_REPLACED_MSGÚDeletedWalGenerationErrorÚ!SessionCompressionInProgressErrorÚStateDbCorruptErrorÚStateDbReplacedErrorÚ_is_no_more_rowsÚclassify_persistence_errorÚis_malformed_db_errorÚis_malformed_schema_error)Ú_STATE_DB_GUARD_BYPASS_ENVÚ_in_test_contextÚ_is_production_state_dbÚ_real_platform_state_rootÚ_register_test_instanceÚ_set_last_init_errorÚget_last_init_error)Ú_READ_POOL_MAXÚ_proc_fd_targetsÚ_read_budget_for)ÚSessionSessionsMixin)ÚSessionFtsSetupMixinÚload_fts5_cjk_extension)ÚSessionPortabilityMixin)ÚSessionTelegramTopicsMixin)ÚSessionProfileRepairMixin)ÚSessionSchemaMixin)Úlog_write_lock_holders)Ú_connect_tracked_dbÚ_fd_is_truly_unlinkedÚ_prepare_connection_retirementÚ_read_sqlite_application_idÚ_stat_sqlite_sidecar_identityÚ_watched_sqlite_sidecar_pathsÚ!has_invalid_sqlite_header_preopenÚis_zeroed_state_dbÚquarantine_cross_process_lockÚquarantine_invalid_state_dbÚRetiredGenerationCaptureErrorÚcapture_retired_wal_generationÚrefuse_deleted_wal_generation)ÚSessionMessagesMixin)ÚSessionRewindMixin)Ú_WAL_INCOMPAT_MARKERSÚ_on_disk_journal_modeÚapply_database_pragmasÚapply_wal_with_fallback)Ú_claim_repair_attemptÚpreflight_db_writabilityÚrepair_state_db_schema)ÚSessionTitlesMixin)ÚSessionUsageMixin)ÚSessionMaintenanceMixin)ÚSessionGatewayMixin)ÚSessionCompressionMixin)ÚSessionSearchMixini N  ÚkeyÚfallbackÚreturnc                 ó´   — 	 ddl m}  |«       j                  d«      xs i j                  | «      }|€|S t        |«      }|dk\  r|S |S # t        $ r |cY S w xY w)za``sessions.<key>`` from config.yaml (lazy import: circular at load), else *fallback*; 0 disables.r   )Úload_config_readonlyÚsessions)Úhermes_cli.configrV   ÚgetÚintÚ	Exception)rR   rS   rV   ÚvalueÚlimits        ú1/home/ubuntu/.hermes/hermes-agent/hermes_state.pyÚ_configured_transcript_limitr_   V   se   € ðÝ:Ù%Ó'×+Ñ+¨JÓ7Ò=¸2×BÑBÀ3ÓGˆØˆ=ØˆOÜ�E“
ˆØ š
ˆuÐ0¨Ð0øÜò ØŠðús   ‚2A	 µA	 ÁA	 Á	AÁAc                  ó   — t        d«      S )NÚmax_resume_messages©r_   © ó    r^   Úresolved_max_resume_messagesre   c   ó   € Ü'Ð(=Ó>Ð>rd   c                  ó   — t        d«      S )NÚmax_export_messagesrb   rc   rd   r^   Úresolved_max_export_messagesri   g   rf   rd   c                   ó2   ‡ — e Zd Zedfdededefˆ fd„Zˆ xZS )ÚSessionResumeTooLargeErrorzacross its lineageÚmessage_countr]   Úscopec                 ó^   •— ||c| _         | _        || _        t        ‰| �  d|› d|› d�«       y )Nz+This session is too long to reload safely (z messages; limit z’). Start a fresh chat and use `hermes sessions export` to keep a copy, or raise the limit with `hermes config set sessions.max_resume_messages 0`.)rl   r]   rm   ÚsuperÚ__init__)Úselfrl   r]   rm   Ú	__class__s       €r^   rp   z#SessionResumeTooLargeError.__init__l   sG   ø€ ð *7¸Ð&ˆÔ˜DœJØˆŒ
Ü‰ÑØ9¸-¸ÐHYÐZ_ÐY`ð aGð Gõ	
rd   )Ú__name__Ú
__module__Ú__qualname__Ú_MAX_SAFE_MESSAGESrZ   Ústrrp   Ú__classcell__©rr   s   @r^   rk   rk   k   s+   ø„ à/AÐPdñ	
Ø ð	
Ø),ð	
ØJM÷	
ñ 	
rd   rk   c                   ó0   ‡ — e Zd Zefdededefˆ fd„Zˆ xZS )ÚSessionExportTooLargeErrorÚ
session_idrl   r]   c                 ób   •— |||c| _         | _        | _        t        ‰| �  d|› d|› d|› �«       y )Nz	session 'z' has at least z1 active messages; safe in-memory export limit is )r|   rl   r]   ro   rp   )rq   r|   rl   r]   rr   s       €r^   rp   z#SessionExportTooLargeError.__init__y   sE   ø€ Ø:DÀmÐUZÐ7ˆŒ˜Ô+¨T¬ZÜ‰ÑØ˜
�| ?°=°/ð B.Ø.3¨Wð6õ	
rd   )rs   rt   ru   rv   rw   rZ   rp   rx   ry   s   @r^   r{   r{   x   s%   ø„ ØI[ñ 
 3ð 
°sð 
À3÷ 
ñ 
rd   r{   Úholderc                 óš  — t        j                  d| xs d«      }|rt        |j                  d«      «      nd}|dk  s|t	        j
                  «       k(  ryt        �	 t        j                  |«       S t        j                  dk(  ry	 t	        j                  |d«       y# t        $ r Y yw xY w# t        $ r Y yt        t        f$ r Y yw xY w)a.  True only when a ``pid=<n>`` lock holder's local PID is provably gone.
    Reclaim on kernel proof only: unstructured/same-process holders (another
    thread's live lease) and any probe doubt keep the lease until TTL expiry
    (PID reuse must never steal a live lease; a wrongly-kept one self-heals).z(?:^|:)pid=(\d+)(?::|$)Ú é   r   FÚntT)ÚreÚsearchrZ   ÚgroupÚosÚgetpidÚpsutilÚ
pid_existsr[   ÚnameÚkillÚProcessLookupErrorÚOSErrorÚOverflowError)r~   ÚmatchÚpids      r^   Ú(_compression_lock_holder_process_is_deadr‘   �   sÃ   € ô
 �I‰IÐ0°&²,¸BÓ?€EÙ!&Œ#ˆe�k‰k˜!‹nÔ
¨A€CØ
ˆa‚x�3œ"Ÿ)™)›+Ò%ØÜÐð	Ü×(Ñ(¨Ó-Ð-Ð-ô
 
‡w�w�$‚ØðÜ
�‰��QŒð
 øô ò 	Ùð	ûô ò ÙÜ”]Ð#ò Ùðús*   ÁB ÂB. Â	B+Â*B+Â.	C
Â9C
Ã	C
ÚautoÚcustomÚTústate.dbç      N@)é   gš™™™™™©?c                  óB   — t         t        k7  rt         S t        «       dz  S )z¸Default state DB path at CALL time: a re-pointed ``DEFAULT_DB_PATH`` wins, else
    ``get_hermes_home()`` is resolved fresh (a runtime HERMES_HOME redirect works regardless of import).r•   )ÚDEFAULT_DB_PATHÚ_IMPORT_DEFAULT_DB_PATHr   rc   rd   r^   Ú_default_db_pathr›   ´   s    € ô .Ô1HÒHŒ?ÐlÌoÓN_ÐblÑNlÐlrd   Frc   .Ú _STATE_DB_GUARD_EXTRA_DENY_ROOTSÚdb_pathc           
      ó  — t         s-t        j                  j                  t        «      s
t        «       sy	 t        | «      j                  «       j                  «       }t        «       fD �cg c]  }|€Œ|‘Œ	 }}t        D ]9  }	 |j                  t        |«      j                  «       j                  «       «       Œ; |D ]'  }t        ||«      sŒt        d|› d|› dt        › d�«      ‚ y# t        $ r Y yw xY wc c}w # t        $ r Y Œˆw xY w)aL  Raise before any connection/mkdir/pragma/byte probe when a pytest-context process
    (env OR ancestry) resolves a production DB.

    Env alone is not enough: a child spawned with a rebuilt environment loses ``PYTEST_*`` and
    ``HERMES_HOME`` together, which is precisely the state in which it writes to production (#82770).
    NzAlive-system guard: test attempted to open production state.db at z (under real Hermes root u  ). Tests must run against a temporary HERMES_HOME â€” pass an explicit tmp db_path or let the hermetic conftest redirect HERMES_HOME. If this test genuinely needs the live database, mark it with @pytest.mark.live_system_guard_bypass â€” or, for a spawned child process, export z=1 in its environment.)Ú_STATE_DB_GUARD_BYPASSr†   ÚenvironrY   r$   r%   r   Ú
expanduserÚresolver[   r'   rœ   Úappendr&   ÚRuntimeError)r�   ÚresolvedÚrÚrootsÚextraÚroots         r^   Ú_ensure_test_isolationrª   Á   s  € õ ¤§¡§¡Ô0JÔ!KÔScÔSeØðÜ˜“=×+Ñ+Ó-×5Ñ5Ó7ˆô 3Ó4Ð6ÖH�1¸!¹-ŠQÐH€EÐHÜ1ò ˆð	Ø�L‰Lœ˜e›×/Ñ/Ó1×9Ñ9Ó;Õ<ðð
 ò ˆÜ" 8¨TÕ2ÜðØ'˜jÐ(AÀ$Àð H)ô
 *DÐ(Dð E#ð#ó	ð 	ñøô ò ÙðüâHøô ò 	Ùð	ús/   ¶'C' Á*C6Á2C6Â6C;Ã'	C3Ã2C3Ã;	DÄD©Úcreate_mainr¬   c                ó*  — t         j                  dk(  ry| }|r¥t         j                  t         j                  z  t         j                  z  }t        t         d«      r|t         j                  z  }t        t         d«      r|t         j                  z  }	 t        j                  ||d«      }t        j                  |«       || j                  | j                  dz   «      | j                  | j                  dz   «      fD ]n  }	 t        j                  |«      }t        j                   |j"                  «      rŒ9t        j$                  |j"                  «      sŒYt        j&                  |d«       Œp y# t        $ r Y Œºt        $ r Y yw xY w# t        $ r Y Œ–w xY w)u4  Create/tighten a writable state database and its sidecars to 0600.

    SQLite otherwise creates ``state.db``, ``-wal``, and ``-shm`` according to
    the process umask (commonly 0644 under 0022). Read-only SessionDB
    attachments never call this helper and remain observational.

    Existing files are tightened with ``chmod(2)`` on the path: opening the
    file and closing that descriptor would drop every POSIX ``fcntl`` lock the
    process holds on its inode â€” including the locks of an already-open SQLite
    connection to the same database. A lock-losing close in one process lets a
    sibling's connection take the shared-memory DMS exclusively at its own
    close, checkpoint, and unlink the sidecars while long-lived holders
    (gateway, desktop ``hermes serve``) keep using the deleted inodes.
    r‚   NÚ
O_NOFOLLOWÚ	O_CLOEXECi€  z-walz-shm)r†   rŠ   ÚO_WRONLYÚO_CREATÚO_EXCLÚhasattrr®   r¯   ÚopenÚcloseÚFileExistsErrorÚIsADirectoryErrorÚ	with_nameÚlstatÚFileNotFoundErrorÚstatÚS_ISLNKÚst_modeÚS_ISREGÚchmod)r�   r¬   Ú	main_pathÚflagsÚfdÚpathÚsts          r^   Ú_secure_state_db_filesrÅ   â   sK  € ô 
‡w�w�$‚Øà€IÙô —‘œbŸj™jÑ(¬2¯9©9Ñ4ˆÜ”2�|Ô$Ø”R—]‘]Ñ"ˆEÜ”2�{Ô#Ø”R—\‘\Ñ!ˆEð		Ü—‘˜ E¨5Ó1ˆBô �H‰H�RŒLð 	Ø×Ñ˜'Ÿ,™,¨Ñ/Ó0Ø×Ñ˜'Ÿ,™,¨Ñ/Ó0ðò ˆð	Ü—‘˜$“ˆBô �<‰<˜Ÿ
™
Ô#àÜ�|‰|˜BŸJ™JÔ'ØÜ
�‰��uÕñ%øô ò 	ÙÜ ò 	ñ ð	ûô$ !ò 	Ùð	ús*   ÂE- Ã?FÅ-	FÅ8FÆFÆ	FÆF)z:Review the conversation above and update the skill libraryz;Review the conversation above and consider saving to memoryÚmsgc                 óÖ   — t        | t        «      r| j                  d«      dvry| j                  d«      }t        |t        «      xr# |j	                  «       j                  t        «      S )z„Persisted harness prompt (older builds wrote the forked curator's turns
    into real sessions; replaying them hijacks the session).Úrole>   ÚuserÚsystemFÚcontent)Ú
isinstanceÚdictrY   rw   ÚlstripÚ
startswithÚ_REVIEW_HARNESS_PREFIXES©rÆ   rË   s     r^   Ú%_is_background_review_harness_messagerÒ   $  sT   € ô �cœ4Ô  C§G¡G¨F£OÐ;MÑ$MØØ�g‰g�iÓ €GÜ�gœsÓ#Ò]¨¯©Ó(8×(CÑ(CÔD\Ó(]Ð]rd   Úmessagesc                 óÆ   — | s| S g }d}d}| D ]Q  }t        |«      r| }d}Œ|r)d}t        |t        «      r|j                  d«      dk(  rd}Œ?d}|j	                  |«       ŒS |S )zZDrop harness messages and the curator-mode assistant reply that immediately followed each.FTrÈ   Ú	assistant)rÒ   rÌ   rÍ   rY   r£   )rÓ   ÚoutÚskip_next_assistantÚprevious_was_harnessrÆ   s        r^   Ú _strip_background_review_harnessrÙ   -  s‰   € áØˆØ "€CØÐØ ÐØò ˆÜ0°Ô5ð ';Ð":ÐØ#'Ð ØÙØ"'ÐÜ˜#œtÔ$¨¯©°«¸KÒ)GØ',Ð$ØØ$ÐØ�
‰
�3�ðð €Jrd   z^\[[A-Za-z_][A-Za-z0-9_.-]*\]$c                 ó  — t        | t        «      r%| j                  d«      dk7  s| j                  d«      sy| j                  d«      }t        |t        «      xr, t	        t
        j                  |j                  «       «      «      S )z™Assistant tool-call turn whose content is a bare ``[marker]`` (an older
    conversation_loop persisted a local template's marker as the final response).rÈ   rÕ   Ú
tool_callsFrË   )rÌ   rÍ   rY   rw   ÚboolÚ_STALE_TOOL_CALL_MARKER_REÚ	fullmatchÚstriprÑ   s     r^   Ú"_is_stale_tool_call_marker_messagerà   I  sg   € ô �cœ4Ô  C§G¡G¨F£O°{Ò$BÈ#Ï'É'ÐR^ÔJ_ØØ�g‰g�iÓ €GÜ�gœsÓ#Òc¬Ô-G×-QÑ-QÐRY×R_ÑR_ÓRaÓ-bÓ(cÐcrd   c                 óx   — d}t        t        | «      D ]  }d|d<   |dz  }Œ |rt        j                  d|«       | S )z‹Blank stale ``[marker]`` assistant content (replaying it teaches the model
    to keep emitting it); tool_call/result pairing stays intact.r   r€   rË   r�   zMCleared %d stale tool-call marker message(s) while restoring session (#78148))Úfilterrà   ÚloggerÚinfo)rÓ   ÚrepairedrÆ   s      r^   Ú_strip_stale_tool_call_markersræ   R  sP   € ð €HÜÔ8¸(ÓCò ˆØˆˆI‰Ø�A‰‰ðñ Ü�‰Ø[Ð]eô	
ð €Ord   z/Sessions will not be saved until this is fixed.zC If the database lives on a network drive, move it to a local disk.zUthe session database could not be opened; it may be on a network or unsupported drivezbMove it to a local disk (`hermes {profile_arg}doctor` shows where it is), then start Hermes again.)ÚdetailsÚprefixrç   c                óŽ  ‡— ddl m}  |«       }t        «       Š‰s| › dt        › d|› d�S ddlm}  |‰«      }|j                  |j                  d}}}t        ˆfd„t        D «       «      r2|j                  d	k(  rt        t        j                  d
|«      }}nt        }| › d|› dt        › d|› |› �}	|rddlm}
 |	d |
‰«      › �z  }	|	S )uS  User-facing one-liner: ``<prefix>: <gloss>. <consequence> <action>[ network hint]``.

    The cause table lives in ``hermes_state_user_copy`` so CLI, gateway and TUI agree. Chat
    surfaces (gateway, TUI) get the one-liner; ``details=True`` (the CLI banner) appends a
    ``Details: <raw cause>`` line for the raw SQLite text. Network filesystems (NFS/SMB/FUSE/ZFS)
    cannot host SQLite's write-ahead log: when the raw cause carries one of those markers the
    message names the network-drive suspicion, because ``hermes doctor --fix`` cannot repair a
    mount â€” only moving the file can.r   )Úprofile_cli_selectorz. z Run `hermes z&doctor` to check the storage location.)Údescribe_storage_failurer€   c              3   óB   •K  — | ]  }|‰j                  «       v –— Œ y ­w©N)Úlower)Ú.0ÚmÚcauses     €r^   ú	<genexpr>z0format_session_db_unavailable.<locals>.<genexpr>�  s   øè ø€ Ò
= !ˆ1�—‘“ÔÑ
=ùs   ƒÚunknownz{profile_arg}ú: Ú )Ústorage_failure_detailsz

Details: )Úhermes_constantsrê   r*   Ú_SESSION_DB_CONSEQUENCEÚhermes_state_user_copyrë   ÚglossÚactionÚanyrE   rñ   Ú_NETWORK_DRIVE_GLOSSÚ_NETWORK_DRIVE_ACTIONÚreplaceÚ_NETWORK_DRIVE_HINTrö   )rè   rç   rê   Úprofile_argrë   Úfailurerú   rû   ÚhintÚtextrö   rñ   s              @r^   Úformat_session_db_unavailabler  h  sá   ø€ õ 6á&Ó(€KÜÓ!€EÙàˆh�bÔ0Ð1°¸{¸mð L ð  ð	
õ @Ù& uÓ-€GØ!Ÿ-™-¨¯©¸�4ˆ6€EÜ
Ó
=Ô'<Ô
=Ô=Ø�=‰=˜IÒ%Ü0Ô2G×2OÑ2OÐP_ÐalÓ2m�6‰Eä&ˆDØˆX�R˜�w˜bÔ!8Ð 9¸¸6¸(À4À&ÐI€DÙÝBØ�+Ñ5°eÓ<Ð=Ð>Ñ>ˆØ€Krd   Ú_repair_attempted_pathsg      ^@Úwin32c                  ó`   — t        t        dd«      duxr t        t        j                  d«      S )z`Whether this runtime can switch off SQLite's close-time checkpoint (Python 3.12+ ``setconfig``).Ú SQLITE_DBCONFIG_NO_CKPT_ON_CLOSENÚ	setconfig)ÚgetattrÚsqlite3r³   Ú
Connectionrc   rd   r^   Ú#_close_time_checkpoint_configurabler  —  s.   € ä”GÐ?ÀÓFÈdÐRò 9Üœ×*Ñ*¨KÓ8ð:rd   r|   zOptional[Path]c           	      óœ  — t        | xs d«      j                  «       }|r|syt        «       dz  }|j                  dd¬«       ||› d�z  }|j	                  dd¬	«      5 }|D ]R  }|€Œt        |t        «      r|nd
t        |«      i}|j                  t        j                  |dt         ¬«      dz   «       ŒT 	 ddd«       |S # 1 sw Y   |S xY w)z›Append pending messages to HERMES_HOME/sessions/<id>.jsonl (state.db was replaced under a
    live process). Returns the path, or None if nothing to write.r€   NrW   T)ÚparentsÚexist_okz.jsonlÚaúutf-8)ÚencodingrË   F)Úensure_asciiÚdefaultÚ
)
rw   rß   r   Úmkdirr´   rÌ   rÍ   ÚwriteÚjsonÚdumps)r|   rÓ   ÚsidÚsessions_dirrÃ   ÚhandlerÆ   Úrecords           r^   Údivert_session_transcript_jsonlr   �  sÕ   € ô ˆjÒ˜BÓ
×
%Ñ
%Ó
'€CÙ‘hØÜ"Ó$ zÑ1€LØ×Ñ˜t¨dÐÔ3Ø˜S˜E ˜.Ñ(€DØ	�‰�3 ˆÓ	)ð Y¨VØò 	YˆCØ‰Ü *¨3´Ô 5™¸IÄsÈ3ÃxÐ;P�Ø—‘œTŸZ™Z¨¸UÌCÔPÐSWÑWÕXñ	Y÷Yð
 €K÷Yð
 €Kús   ÁCÁ'ACÃCc                 ó,   — t        j                  | «      S )z5Compatibility delegate to the state-holder authority.)Ú_state_holdersÚforeign_state_db_holders)r�   s    r^   Ú_foreign_state_db_holdersr$  °  s   € ä×2Ñ2°7Ó;Ð;rd   c                   óN  — e Zd ZU dZdZeedf   ed<   d\  ZZ	Z
dZd\  ZZdZd	\  ZZd
Zd\  ZZZd\  ZZZd\  ZZdZedee   dee   fd„«       Zedjd„«       Zedej@                  de!ee"f   fd„«       Z#edeejH                     ddfd„«       Z%deddfd„Z&dkde'de(fd„Z)djd„Z*djd„Z+d e,dejH                  fd!„Z-dld"e(ddfd#„Z.dejH                  fd$„Z/djd%„Z0djd&„Z1deejH                     fd'„Z2de(fd(„Z3djd)„Z4deejH                     fd*„Z5e6de7ejH                     fd+„«       Z8dmd,eddfd-„Z9	 dnd.e:ejH                  ge;f   d/ee,   de;fd0„Z<	 doddd1œd2ed3e"d4e(d/ee,   ddf
d5„Z=dodd6œd2ed3e"d/ee,   de>fd7„Z?dod2ed3e"deej@                     fd8„Z@dod2ed3e"deAej@                     fd9„ZBd.e:ejH                  ge;f   de;fd:„ZCdjd;„ZDdjd<„ZEde(fd=„ZFde(fd>„ZGdjd?„ZHd@ede'fdA„ZIdjdB„ZJeKdCeLde(fdD„«       ZMdpdEeddFfdG„ZNdCeLddfdH„ZOde(fdI„ZPdjdJ„ZQde(fdK„ZRdjdL„ZSdMe,d/e,de(fdN„ZTdeAee>ef      fdO„ZUdee   fdP„ZVdjdQ„ZWdqdR„ZXde(fdS„ZYdT„ ZZdjdU„Z[dVZ\dWZ]dXZ^dYZ_e`ZaebZcedZeeadZecd[eed\iZfd]Zgd^Zhd_Zid`Zjdaedee   fdb„ZkddcœdaeddedeeejØ                     ddfdf„Zmdgede>fdh„ZndEedeAeeef      fdi„Zoy)rÚ	SessionDBzVSQLite-backed session storage with FTS5 search; many reader threads, one writer (WAL).)ÚcliÚcronÚkanbanÚacpÚ
api_serverÚsubagentÚtoolÚ	recovered.Ú_AUTO_PRUNE_STALE_OPEN_SOURCES)g      4@r–   g      à?ç      @)g{®Gáz”?g333333Ã?g       @)g      Ð?ç      ð?é2   )iè  éô  é   )r3  i'  iPÃ  )i  P i  �g      >@Úsystem_promptrT   c                 ó˜   — |€y t        j                  |j                  d«      «      j                  «       }| j	                  d||f«       |S )Nr  zAINSERT OR IGNORE INTO system_prompts (hash, prompt) VALUES (?, ?))ÚhashlibÚsha256ÚencodeÚ	hexdigestÚexecute)Úconnr5  Úprompt_hashs      r^   Ú_store_system_promptzSessionDB._store_system_promptí  sL   € àÐ ØÜ—n‘n ]×%9Ñ%9¸'Ó%BÓC×MÑMÓOˆØ�‰ØOØ˜-Ð(ô	
ð Ðrd   Nc                 ó&   — | j                  d«       y )Nz|DELETE FROM system_prompts WHERE NOT EXISTS (SELECT 1 FROM sessions WHERE sessions.system_prompt_hash = system_prompts.hash))r;  ©r<  s    r^   Ú#_delete_unreferenced_system_promptsz-SessionDB._delete_unreferenced_system_promptsø  s   € à�‰ð^õ	
rd   Úrowc                 óX   — t        | «      }d|v r|j                  d«      }d|v r||d<   |S )NÚ_system_prompt_resolvedr5  )rÍ   Úpop)rB  Údatar¥   s      r^   Ú_session_row_dictzSessionDB._session_row_dictÿ  s9   € ä�C‹yˆØ$¨Ñ,Ø—x‘xÐ 9Ó:ˆHØ $Ñ&Ø(0��_Ñ%Øˆrd   r<  c                 óx   — | €y	 | j                  «        y# t        $ r t        j                  dd¬«       Y yw xY w)zCClose a partially initialized connection without masking its error.Nz&Could not close a SessionDB connectionT©Úexc_info)rµ   r[   rã   Údebugr@  s    r^   Ú_close_connection_quietlyz#SessionDB._close_connection_quietly  s=   € ð ˆ<Øð	RØ�J‰J�LøÜò 	RÜ�L‰LÐAÈDˆLÖQð	Rús   … – 9¸9Úlabelc                 ó–   — 	 |j                  «        y# t        $ r,}t        j                  d|| j                  |«       Y d}~yd}~ww xY w)zUClose *conn*; a failing close leaks a tracked fd: logged at WARNING, never swallowed.z%s close failed for %s: %sN)rµ   r[   rã   Úwarningr�   )rq   r<  rM  Úexcs       r^   Ú_close_conn_loggedzSessionDB._close_conn_logged  s=   € ð	SØ�J‰J�LøÜò 	SÜ�N‰NÐ7¸ÀÇÁÈc×RÑRûð	Sús   ‚ “	Aœ"AÁAFr�   Ú	read_onlyc                 óì  — |xs
 t        «       | _        t        | j                  «       || _        d| _        d }	 t        j                  d«      }|j                  j                  dd«      › d|j                  j                  › d|j                  › �| _        ~t        j                  «       | _        t!        j"                  t$        ¬«      | _        t)        | j                  «      | _        | j*                  j,                  | _        t        j                  «       | _        d| _        d	| _        d
\  | _        | _        d | _        d| _        i | _        dx| _         | _!        d | _"        t        j                  «       | _#        d | _$        d| _%        i | _&        d\  | _'        | _(        d| _)        dx| _*        x| _+        | _,        dx| _-        x| _.        | _/        d | _0        tc        «       | _2        t        jf                  t        j                  «       «      | _4        d | _5        dx| _6        | _7        d | _8        d| _9        d}	 |r| ju                  «        n)tw        «       sty        «       | _$        | j{                  «        | j}                  «        d}	 |s&| j`                  d c}| _0        | j…                  |«       y | j*                  j‡                  | «       t‰        | «       y # t        $ r Y �ŒNw xY w# ~w xY w# t        $ r)}t        t�        |«      j‚                  › d|› �«       ‚ d }~ww xY w# |s&| j`                  d c}| _0        | j…                  |«       w | j*                  j‡                  | «       t‰        | «       w xY w)Nró   r�   rs   ú?ú.ú:)ÚmaxsizeFg        )Fr   r   )Fr€   Trô   )Er›   r�   rª   rR  Ú_creation_siteÚsysÚ	_getframeÚ	f_globalsrY   Úf_codeÚco_nameÚf_linenor[   Ú	threadingÚLockÚ_lockÚqueueÚ	LifoQueuer+   Ú
_read_poolr-   Ú_read_budgetÚpermitsÚ_read_permitsÚ_read_conns_lockÚ_read_conns_closedÚ_read_open_failed_atÚ_wal_activeÚ_write_countÚ_db_file_identityÚ_db_file_application_idÚ_db_sidecar_identityÚ_db_replacedÚ_db_wal_generation_lostÚ_retired_generation_captureÚ_retired_capture_lockÚ_retire_connectionÚ_connection_pinnedÚ_wal_lock_guardÚ_db_corruptÚ_db_corrupt_reasonÚ_fts_usermerge_floor_appliedÚ_fts_enabledÚ
_fts_staleÚ_trigram_availableÚ_fts_cjk_loadedÚ_fts_cjk_availableÚ_fts_unavailable_warnedÚ_connr   Ú_token_queueÚ	ConditionÚ_token_queue_condÚ_token_writer_threadÚ_token_writer_stopÚ_token_writer_busyÚ_token_atexit_hookÚ_shared_registry_ownedÚ_open_read_onlyr  r8   Ú_open_writerÚ_record_db_file_identityr)   Útypers   rL  Úregisterr(   )rq   r�   rR  ÚcallerÚinitialization_completerP  r<  s          r^   rp   zSessionDB.__init__  s(  € ØÒ4Ô"2Ó"4ˆŒÜ˜tŸ|™|Ô,Ø"ˆŒà'ˆÔØˆð	Ü—]‘] 1Ó%ˆFà×#Ñ#×'Ñ'¨
°CÓ8Ð9¸¸6¿=¹=×;PÑ;PÐ:QÐQRÐSY×SbÑSbÐRcÐdð Ôð Ü—^‘^Ó%ˆŒ
ô BGÇÁÔYgÔAhˆŒô -¨T¯\©\Ó:ˆÔØ!×.Ñ.×6Ñ6ˆÔÜ )§¡Ó 0ˆÔð #(ˆÔð %(ˆÔ!Ø.6Ñ+ˆÔ˜$Ô+ð 37ˆÔØ,-ˆÔ$Ø68ˆÔ!Ø;@Ð@ˆÔ˜DÔ8à;?ˆÔ(Ü%.§^¡^Ó%5ˆÔ"ØCGˆÔØ"'ˆÔØ%'ˆÔØ4=Ñ1ˆÔ˜$Ô1Ø,1ˆÔ)ØHMÐMˆÔÐM˜DœO¨dÔ.Eð Y^Ð]ˆÔÐ]˜tÔ6¸Ô9UØˆŒ
ä#(£7ˆÔÜ!*×!4Ñ!4´Y·^±^Ó5EÓ!FˆÔØ@DˆÔ!Ø<AÐAˆÔ $Ô"9Ø@DˆÔð ',ˆÔ#Ø"'Ðð	.ÙØ×$Ñ$Õ&ô
 ;Ô<Ü.LÓ.N�DÔ+Ø×!Ñ!Ô#Ø×)Ñ)Ô+Ø&*Ñ#ñ +Ø#'§:¡:¨tÐ ��d”jØ×.Ñ.¨tÕ4ð ×!Ñ!×*Ñ*¨4Ô0ô (¨Õ-øôg ò 	Úð	ûñ ûôD ò 	ä ¤D¨£I×$6Ñ$6Ð#7°r¸#¸Ð!?Ô@Øûð	ûñ
 +Ø#'§:¡:¨tÐ ��d”jØ×.Ñ.¨tÕ4ð ×!Ñ!×*Ñ*¨4Ô0ô (¨Õ-úsJ   ºAK È:AK. Ë	K%Ë!K( Ë$K%Ë%K( Ë(K+Ë.	L Ë7$LÌL Ì L# Ì#AM3c                 óÂ  — t        | j                  j                  «       t        | j                  d¬«       	 | j                  j	                  «       rt        | j                  «      rct        | j                  «      5 }|s t        j                  d| j                  «       | j                  |¬«       | j                  «        ddd«       n"| j                  d¬«       | j                  «        | j)                  «        | j*                  r%t-        j.                  | j                  «      | _        yy# 1 sw Y   ŒKxY w# t        j                  $ r‘}t        |«      rt        | j                  «      s‚ t        j                  d|«       | j!                  | j"                  «       t%        | j                  «      j'                  d«      s‚ | j                  «        Y d}~Œíd}~ww xY w)	zŽWritable open: preflight, zero-byte quarantine, connect + schema (one in-place repair of a
        malformed sqlite_master), generation stamp.r•   ©Údb_labelzAstartup quarantine lock for %s not acquired within 5s; proceeding©Úalready_lockedNFu`   state.db schema is malformed (%s) â€” attempting automatic repair (a backup copy is made first).rå   )r   r�   ÚparentrJ   Úexistsr<   r>   rã   rO  Ú_handle_quarantine_if_invalidÚ$_connect_and_init_with_lock_patiencer  ÚDatabaseErrorr#   rI   ÚerrorrL  r€  rK   rY   Ú_ensure_db_file_generationrk  Ú
_lockguardÚholdrv  )rq   Úlock_acquiredrP  s      r^   rŠ  zSessionDB._open_writerz  s�  € ô
 	  §¡× 3Ñ 3Ô4ô 	! §¡¸
ÕCð	8ð —<‘<×&Ñ&Ô(Ô,MÈdÏlÉlÔ,[Ü2°4·<±<Ó@ð @ÀMÙ(ÜŸ™Ø_Ø ŸL™Lôð ×6Ñ6ÀmÐ6ÔTØ×=Ñ=Ô?÷@ð @ð ×2Ñ2À%Ð2ÔHØ×9Ñ9Ô;ð 	×'Ñ'Ô)Ø×Òô $.§?¡?°4·<±<Ó#@ˆDÕ ð	 ÷5@ð @ûô ×$Ñ$ò 	8ô -¨SÔ1Ô9NÈtÏ|É|Ô9\ØÜ�L‰Lð8Ø9<ôð ×*Ñ*¨4¯:©:Ô6Ü)¨$¯,©,Ó7×;Ñ;¸JÔGØØ×5Ñ5×7Ñ7ûð	8ús2   ¸AD: Á<AD.Ã+D: Ä.D7Ä3D: Ä:GÅBGÇGc                 ó4  — t        t        dz   «      D ]y  }	 | j                  d¬«      x| _        }	 t	        |d¬«       |j                  «       }| j                  |d«      du | _        | j                  r| j                  |d«      du | _         y	 y	# t        $ r d	| _        | j                  |«       ‚ w xY w# t        j                  $ rN}t        t        |«      j                  «       v }|t        k\  s|s‚ t!        j"                  t$        «       Y d	}~Œÿd	}~ww xY w)
až  Read-only attach for cross-profile aggregation: no schema init, NO write
        lock (sidebar polling never contends with that profile's backend); the DB
        must exist. FTS flags are probed with SELECTs only, and the connection is
        closed on ANY probe failure (malformed schema raises DatabaseError) so a
        leaked tracked connection cannot block the forensic backup the writable heal takes next.r�   r1  ©Útimeoutr•   r‘  Úmessages_ftsTÚmessages_fts_trigramN)ÚrangeÚ_READ_ONLY_IOERR_RETRY_ATTEMPTSÚ_connect_read_onlyr€  rG   ÚcursorÚ_fts_table_proberz  r|  ÚBaseExceptionrL  r  ÚOperationalErrorr   rw   rî   ÚtimeÚsleepÚ _READ_ONLY_IOERR_RETRY_BACKOFF_S)rq   Úattemptr<  r§  ÚioerrÚ	transients         r^   r‰  zSessionDB._open_read_only§  s  € ô Ô<¸qÑ@ÓAò 	=ˆGð=Ø$(×$;Ñ$;ÀCÐ$;Ó$HÐH�”
˜TðÜ*¨4¸*ÕEØ!Ÿ[™[›]�FØ(,×(=Ñ(=¸fÀnÓ(UÐY]Ð(]�DÔ%Ø×(Ò(à ×1Ñ1°&Ð:PÓQÐUYÐYð Ô/ñ ñ	=øô %ò Ø!%�D”JØ×2Ñ2°4Ô8Øðûô
 ×+Ñ+ò =ô
 2´S¸³Z×5EÑ5EÓ5GÐG�	ØÔ=Ò=ÁYØÜ—
‘
Ô;×<Ñ<ûð=ús*   —B6±ABÂ#B3Â3B6Â6DÃ	ADÄDr¡  c                 óŒ   — t        t        | j                  «      | j                  dd|d¬«      }t        j                  |_        |S )zÃ``mode=ro`` tracked connection with Row factory. check_same_thread=False: pooled connections
        are borrowed by whichever thread reads next; exclusive ownership is enforced by pool checkout.TFN)Útracking_pathÚuriÚcheck_same_threadr¡  Úisolation_level)r6   r   r�   r  ÚRowÚrow_factory)rq   r¡  r<  s      r^   r¦  zSessionDB._connect_read_onlyÇ  s<   € ô #Ü˜TŸ\™\Ó*¸$¿,¹,ÈDØ#¨WÀdô
ˆô #Ÿ;™;ˆÔØˆrd   r”  c                 ó  — | j                   j                  «       rt        | j                   «      sy	 | j                   j                  «       j                  }t        | j                   |¬«      }|rd|› �nd}d|› d|› d|xs | j                   › d	�}t        j                  |«       t        |«       |€F| j                   j                  «       r+t        | j                   «      rt        j                  |«      ‚yyy# t
        $ r d}Y Œ°w xY w)
z�Quarantine a zero-byte/headerless state.db so a fresh one can open; if quarantine failed,
        raise the clear message instead of opening the zeroed file.Néÿÿÿÿr“  zmoved aside to z+left in place (it could not be moved aside)zstate.db was empty or damaged (z bytes) and has been zy; Hermes started with a fresh, empty session database. To bring old sessions back, run `hermes sessions recover --source zz --inspect-only`, or restore a snapshot with `/snapshot list` then `/snapshot restore <id>` (terminal `hermes` chat only).)r�   r–  r<   r»   Úst_sizer�   r?   rã   rš  r)   r  r™  )rq   r”  ÚzsizeÚqpathÚwhererÆ   s         r^   r—  z'SessionDB._handle_quarantine_if_invalidÑ  s  € ð —‘×#Ñ#Ô%Ô*KÈDÏLÉLÔ*YØð	Ø—L‘L×%Ñ%Ó'×/Ñ/ˆEô ,¨D¯L©LÈÔXˆÙ-2�/ % Ñ)Ð8eˆà-¨e¨WÐ4IÈ%Èð Q1à16Ò1F¸$¿,¹,Ð0Gð Hjðjð 	ô 	�‰�SÔÜ˜SÔ!Øˆ=˜TŸ\™\×0Ñ0Ô2Ô7XÐY]×YeÑYeÔ7fÜ×'Ñ'¨Ó,Ð,ð 8gÐ2ˆ=øô ò 	ØŠEð	ús   ²$C9 Ã9DÄDc                 óˆ  — t        t        | j                  «      ddd¬«      }	 t        j                  |_        t        |d¬«      }|dk(  xr t        |«      dk(  | _        t        | j                  «       t        |d¬«       |j                  d«       t        |«      | _        |S # t        $ r | j                  |«       ‚ w xY w)	zíConnect + WAL/pragma/tokenizer setup for a writer connection (no schema init). Short timeout:
        jittered application-level retry handles contention, not SQLite's busy handler;
        isolation_level=None: explicit BEGIN IMMEDIATE.Fr1  N)r´  r¡  rµ  r•   r‘  ÚwalzPRAGMA foreign_keys=ON)r6   rw   r�   r  r¶  r·  rH   rF   rk  rÅ   rG   r;  r0   r}  r©  rL  )rq   r<  Úmodes      r^   Ú_open_writer_connzSessionDB._open_writer_connç  s¸   € ô #Ü�—‘Ó°ÀÐUYô
ˆð	Ü&Ÿ{™{ˆDÔÜ*¨4¸*ÔEˆDð
  $ u™}ÒUÔ1FÀtÓ1LÐPUÑ1UˆDÔô # 4§<¡<Ô0Ü" 4°*Õ=Ø�L‰LÐ1Ô2Ü#:¸4Ó#@ˆDÔ ð ˆøô ò 	Ø×*Ñ*¨4Ô0Øð	ús   ¤A?B% Â%Cc                 ó¦   — t        | j                  «       t        | j                  d¬«       | j                  «       | _        | j                  «        y )NTr«   )rB   r�   rÅ   rÁ  r€  Ú_init_schema©rq   s    r^   Ú_connect_and_initzSessionDB._connect_and_init  s<   € ô 	& d§l¡lÔ3ô 	˜tŸ|™|¸Õ>Ø×+Ñ+Ó-ˆŒ
Ø×ÑÕrd   c           
      óH  — t        j                  «       | j                  z   }	 	 | j                  «        y# t        j
                  $ r×}t        |«      j                  «       }d|vrd|vr‚ | j                  | j                  «       t        j                  «       }||k\  r!t        | j                  | j                  «       ‚ t        j                  | j                  | j                  «      }t        j                   t#        |t%        ||z
  d«      «      «       Y d}~nd}~ww xY w�Œ)a/  Open + init, waiting out a sibling's write lock with jittered patience:
        _init_schema's DDL runs on a 1s-timeout connection, so a sibling's VACUUM
        or checkpoint used to fail the ENTIRE open and callers disabled
        persistence for the whole run. Non-lock errors propagate immediately.NÚlockedÚbusyçü©ñÒMbP?)r«  Ú	monotonicÚ_WRITE_PATIENCE_SrÅ  r  rª  rw   rî   rL  r€  r5   r�   ÚrandomÚuniformÚ_WRITE_RETRY_SLOW_MIN_SÚ_WRITE_RETRY_SLOW_MAX_Sr¬  ÚminÚmax)rq   ÚdeadlinerP  ÚerrÚnowÚjitters         r^   r˜  z.SessionDB._connect_and_init_with_lock_patience  së   € ô —>‘>Ó# d×&<Ñ&<Ñ<ˆØðDØ×&Ñ&Ô(ØøÜ×+Ñ+ò 
DÜ˜#“h—n‘nÓ&�Ø 3Ñ&¨6¸Ñ+<ØØ×.Ñ.¨t¯z©zÔ:Ü—n‘nÓ&�Ø˜(’?Ü*¨4¯<©<¸×9OÑ9OÔPØÜŸ™¨×(DÑ(DÀd×FbÑFbÓc�Ü—
‘
œ3˜v¤s¨8°c©>¸5Ó'AÓB×CÑCûð
Dúñ	 s   ¤5 µDÁCDÄDc                 ót  — | j                   r| j                  ry| j                  5  | j                  }|xr t	        j
                  «       |z
  t        k  }| j                  s|r
	 ddd«       y	 ddd«       | j                  j                  | «      s&t        j                  dt        | j                  «       yd}	 | j                  d¬«      }t        |d¬«       | j                   rt#        |«       |S # 1 sw Y   Œ…xY w# t$        $ r°}|�| j'                  |d«       | j                  j)                  «        t+        |t,        j.                  «      s‚ | j                  5  t	        j
                  «       | _        ddd«       n# 1 sw Y   nxY wt        j                  d| j                  d	¬
«       Y d}~yd}~ww xY w)a“  Open a fresh read-only connection, or None when unavailable (callers
        return it to self._read_pool). WAL only: WAL readers never block on the
        writer, so reads skip self._lock; under DELETE journal mode (NFS fallback)
        readers hit SQLITE_BUSY storms, so the legacy locked path stays. Autocommit
        reads see everything committed so far (read-your-writes for flush-then-search).NzVread pool at capacity (%d) for %s; serving this read from the locked writer connectionr0  r   r•   r‘  zpartially-opened read connz'read-only connection open failed for %sTrI  )rk  rR  rh  rj  r«  rÊ  Ú_READ_OPEN_RETRY_SECONDSri  re  Úacquirerã   rK  r+   r�   r¦  rG   r}  r0   r©  rQ  ÚreleaserÌ   r  ÚError)rq   Ú	failed_atÚbacking_offr<  rP  s        r^   Ú_get_read_connzSessionDB._get_read_conn)  s�  € ð ×Ò 4§>¢>ØØ×"Ñ"ñ 	Ø×1Ñ1ˆIØ#Ò_¬¯©Ó(8¸9Ñ(DÔG_Ñ(_ˆKØ×&Ò&©+Ø÷		ð 	ð +6÷	ð × Ñ ×(Ñ(¨Ô.Ü�L‰Lð+Ü,:¸D¿L¹Lôð Øˆð	Ø×*Ñ*°3Ð*Ó7ˆDÜ" 4°*Õ=Ø×#Ò#Ü'¨Ô-ð ˆ÷?	ð 	ûô$ ò 	ð ÐØ×'Ñ'¨Ð.JÔKØ×Ñ×%Ñ%Ô'Ü˜c¤7§=¡=Ô1ØØ×&Ñ&ñ =Ü,0¯N©NÓ,<�Ô)÷=÷ =ñ =úä�L‰LÐBÀDÇLÁLÐ[_ˆLÔ`Üûð	ús<   ¦>C2Â:6C> Ã2C;Ã>	F7ÄAF2ÅE?Å6	F2Å?F	Æ)F2Æ2F7c                 óŽ   — 	 | j                   j                  «       }| j	                  |«       y# t        j                  $ r Y yw xY w)z^Close one idle pooled connection (a peer on the same file wants its permit); never a live one.FT)rd  Ú
get_nowaitrb  ÚEmptyÚ_close_read_conn©rq   r<  s     r^   Ú_evict_one_idle_read_connz#SessionDB._evict_one_idle_read_connR  sD   € ð	Ø—?‘?×-Ñ-Ó/ˆDð 	×Ñ˜dÔ#Øøô �{‰{ò 	Ùð	ús   ‚. ®AÁAc                 óœ   — 	 | j                  |d«       | j                  j                  «        y# | j                  j                  «        w xY w)zÃClose a pooled read connection and release its permit even when the close fails (withholding
        it would narrow the read path forever). Over-releasing the BoundedSemaphore raises ValueError.z	read-connN)rQ  re  rÙ  râ  s     r^   rá  zSessionDB._close_read_conn[  s<   € ð	(Ø×#Ñ# D¨+Ô6à×Ñ×%Ñ%Õ'øˆD×Ñ×%Ñ%Õ'ús	   ‚/ ¯Ac                 ó¼   — | j                   r| j                  ry	 | j                  j                  «       S # t        j
                  $ r | j                  «       cY S w xY w)z›Borrow a read connection, opening on a miss; None when the read path is unavailable.
        A pool hit costs no permit (the connection already holds one).N)rk  rR  rd  rß  rb  rà  rÝ  rÄ  s    r^   Ú_checkout_read_connzSessionDB._checkout_read_connc  sP   € ð ×Ò 4§>¢>Øð	)Ø—?‘?×-Ñ-Ó/Ð/øÜ�{‰{ò 	)Ø×&Ñ&Ó(Ò(ð	)ús   ›5 µ#AÁAc              #   óú  K  — | j                  «       }|�Z	 |–— d}| j                  5  | j                  s	 | j                  j	                  |«       d}ddd«       |s| j                  |«       y| j                  5  | j                  €| j                  d¬«       t        t        j                  | j                  «      –— ddd«       y# t
        j                  $ r Y Œ‹w xY w# 1 sw Y   Œ�xY w# d}| j                  5  | j                  s8	 | j                  j	                  |«       d}n# t
        j                  $ r Y nw xY wddd«       n# 1 sw Y   nxY w|s| j                  |«       w w xY w# 1 sw Y   yxY w­w)u&  Yield a connection for read-only statements: a pooled read-only
        connection with NO lock under WAL; otherwise (non-WAL, open failure,
        ceiling reached) the writer connection under self._lock â€” deliberate
        degradation: slower beats EMFILE, which the supervisor cannot see.NFTÚread©Úcontext)ræ  rh  ri  rd  Ú
put_nowaitrb  ÚFullrá  ra  r€  Ú_reopen_after_close_lockedr   r  r  )rq   r<  Úreturneds      r^   Ú	_read_ctxzSessionDB._read_ctxm  sk  è ø€ ð ×'Ñ'Ó)ˆØÐð0Ø’
à �Ø×*Ñ*ñ !Ø×2Ò2ð!Ø ŸO™O×6Ñ6°tÔ<Ø'+˜H÷	!ñ  ð ×)Ñ)¨$Ô/ØØ�Z‰Zñ 	7Ø�z‰zÐ!Ø×/Ñ/¸Ð/Ô?Ü”w×)Ñ)¨4¯:©:Ó6Ò6÷	7ð 	7øô  %Ÿz™zò !Ù ð!ú÷!ð !ûð !�Ø×*Ñ*ñ !Ø×2Ò2ð!Ø ŸO™O×6Ñ6°tÔ<Ø'+™HøÜ$Ÿz™zò !Ù ð!ú÷!÷ !ñ !úñ  ð ×)Ñ)¨$Õ/ð  ú÷
	7ð 	7üs¤   ‚E;–C. šE;¨C"¶C	Á(E;Á;AE/Ã 	E;Ã	CÃC"ÃCÃC"Ã"C+Ã'E;Ã.E,Ã=EÄD)Ä(EÄ)D?	Ä<EÄ>D?	Ä?EÅ	E,ÅEÅE,Å,E;Å/E8Å4E;rê  c                 óF  — | j                   r&t        j                  d| j                  › d|› d�«      ‚| j                  r>| j
                  s2| j                  «       s"| j                  d| j                  › d|› d�«      ‚| j                  «        t        j                  d| j                  |«       	 | j                  «       | _        | j                  r%t!        j"                  | j                  «      | _        y
y
# t        $ r!}t        j                  d|› d	|› �«      |‚d
}~ww xY w)aC  Reopen the writer after ``close()`` raced a live caller (a teardown owner
        set ``_conn = None`` while a worker still had a transcript flush to land).
        Loud (WARNING) and bounded (only after an explicit close()). Caller holds
        ``self._lock``. No _init_schema: no DDL races with siblings during teardown.zSessionDB for z/ was closed (read-only handle); cannot serve a z after close()zstate.db connection for zF is quarantined after structural corruption; refusing to reopen for a z after close(). uq   state.db connection for %s was closed while a %s was still in flight â€” reopening (teardown/worker race, #94736)z'state.db connection was closed while a u†    was still in flight (a session-teardown path called close() before this worker finished â€” #94736) and the automatic reopen failed: N)rR  r  ÚProgrammingErrorr�   rw  rp  Ú_db_file_was_replacedÚ_corrupt_errorÚ_halt_if_db_generation_changedrã   rO  rÁ  r€  r[   rª  rk  rœ  r�  rv  )rq   rê  rP  s      r^   rí  z$SessionDB._reopen_after_close_lockedŠ  s4  € ð
 �>Š>Ü×*Ñ*Ø  §¡ ð /"Ø") ¨.ð:óð ð ×Ò T×%6Ò%6¸$×:TÑ:TÔ:VØ×%Ñ%Ø*¨4¯<©<¨.ð 9CØCJÀ)ð L"ð"óð ð
 	×+Ñ+Ô-Ü�‰ðBØCGÇ<Á<ÐQXô	
ð	Ø×/Ñ/Ó1ˆDŒJð ×ÒÜ#-§?¡?°4·<±<Ó#@ˆDÕ ð øô ò 	Ü×*Ñ*Ø9¸'¸ð CUàUXÐTYð[óð ð	ûð	ús   Â/C6 Ã6	D Ã?DÄD ÚfnÚ
patience_sc                 óÈ  — |€| j                   }t        j                  «       |z   }d}d}	 | j                  «        d}	 | j                  5  | j                  «        | j                  €| j                  d¬«       | j                  j                  d«       	 d} || j                  «      }| j                  j                  «        	 ddd«       | xj                  dz  c_        | j                  | j                  z  dk(  r| j                  «        | j                  | j                   z  dk(  r| j#                  «        S # t        $ r, 	 | j                  j                  «        ‚ # t        $ r Y ‚ w xY ww xY w# 1 sw Y   Œ°xY w# t$        $ rN |€+t'        t        j                  «       | j(                  z   |«      }| j+                  || j(                  «      rY �Œª‚ t,        j.                  $ �ry}t1        |«      r| j+                  ||«      rY d}~�Œát3        |«      j5                  «       }	t7        |t,        j8                  «      rxd	|	v sd
|	v rI| j+                  ||«      rY d}~�Œ4t;        | j<                  |«       t-        j8                  d|d›d�«      |‚t>        |	v r|s|s| j+                  ||«      rd}Y d}~�Œ‹‚ t7        |t,        j@                  «      r‹d|	v stC        |«      s| jE                  |«      r2| j                  5  | j                  «        ddd«       n# 1 sw Y   nxY w| jG                  |«      rY d}~�Œ| jI                  |«      r| jK                  |«       ‚ d}~ww xY w)u„  Run *fn(conn)* inside BEGIN IMMEDIATE with jittered lock retry; commit
        is handled here (callers must not commit). Returns *fn*'s result.
        BEGIN IMMEDIATE takes the WAL write lock up front so contention surfaces
        immediately; on locked/busy the Python lock is released, a jitter slept,
        and the WHOLE callback retried â€” *fn* must stay idempotent under retry.NFTr  ré  zBEGIN IMMEDIATEr�   r   rÇ  rÈ  zQdatabase is locked (another Hermes process held the state.db write lock for over z.0fu‘   s â€” likely a long maintenance operation such as VACUUM, a large WAL checkpoint, or an older pre-update process; the database itself is healthy)znot a database)&rË  r«  rÊ  Ú_raise_if_db_corruptra  Ú_raise_if_db_replacedr€  rí  r;  Úcommitr©  Úrollbackr[   rl  Ú_CHECKPOINT_EVERY_N_WRITESÚ_try_wal_checkpointÚ_FTS_MERGE_EVERY_N_WRITESÚ_try_incremental_merge_ftsr   rÐ  Ú_COMPRESSION_BUSY_WAIT_SÚ_sleep_before_write_retryr  rÚ  r    rw   rî   rÌ   rª  r5   r�   r   r™  r"   Ú_is_fts_write_corruption_errorÚ_enter_fts_fail_openÚ_is_structural_corruption_errorÚ_halt_db_corrupt)
rq   rõ  rö  rÒ  Úcompression_deadlineÚioerr_begin_retriedÚ
fn_startedÚresultrP  Úerr_msgs
             r^   Ú_execute_writezSessionDB._execute_write¬  s0  € ð ÐØ×/Ñ/ˆJÜ—>‘>Ó# jÑ0ˆØ04Ðð $ÐØØ×%Ñ%Ô'ð ˆJðUØ—Z‘Zñ Ø×.Ñ.Ô0Ø—z‘zÐ)Ø×7Ñ7ÀÐ7ÔHØ—J‘J×&Ñ&Ð'8Ô9ð	Ø%)˜
Ù!# D§J¡J£˜ØŸ
™
×)Ñ)Õ+÷ð  ×!Ò! QÑ&Õ!Ø×$Ñ$ t×'FÑ'FÑFÈ!ÒKØ×,Ñ,Ô.Ø×$Ñ$ t×'EÑ'EÑEÈÒJØ×3Ñ3Ô5Ø�øô )ò ð!Ø ŸJ™J×/Ñ/Ô1ð øô  )ò !Ø Øð!úðú÷ð ûô, 5ò ð (Ð/Ü+.¬t¯~©~Ó/?À$×B_ÑB_Ñ/_ÐaiÓ+jÐ(Ø×1Ñ1Ø(¨$×*GÑ*Gôò ØÜ—=‘=ó .ô $ CÔ(¨T×-KÑ-KÈHÐV`Ô-aÝÜ˜c›(Ÿ.™.Ó*�Ü˜c¤7×#;Ñ#;Ô<Ø 7Ñ*¨f¸Ñ.?Ø×9Ñ9¸(ÀJÔOÝ$ô /¨t¯|©|¸ZÔHÜ%×6Ñ6ð<Ø<FÀsÐ;Kð LGðGóð  #ð#ô .°Ñ8ÁÑTgØ ×:Ñ:¸8ÀZÔPð /3Ð+Ý ØÜ˜c¤7×#8Ñ#8Ô9ð )¨GÑ3Ô7LÈSÔ7QØ×>Ñ>¸sÔCà!ŸZ™Zñ 9Ø ×6Ñ6Ô8÷9÷ 9ñ 9úð ×0Ñ0°Ô5Ý à×;Ñ;¸CÔ@Ø×-Ñ-¨cÔ2Øûð].ús¡   ¾E? Á
A
E3Â.D;ÃA6E? Ä;	E0ÅE ÅE0Å 	E,Å)E0Å+E,Å,E0Å0E3Å3E<Å8E? Å?AM!ÇM!Ç(MÈAMÉAMÊ5AMË<LÌ	MÌL	ÌMÌ9#MÍM!)Úmanyrö  ÚsqlÚparamsr  c                ó>   ‡‡‡— ˆˆˆfd„}| j                  ||¬«       y)z8Run one INSERT/UPDATE/DELETE through ``_execute_write``.c                 óH   •—  ‰r| j                   n| j                  ‰‰«       y rí   )Úexecutemanyr;  )r<  r  r  r  s    €€€r^   Ú_doz!SessionDB._write_sql.<locals>._do'  s   ø€ Ø7¡ˆT×Ò¨4¯<©<¸¸fÕErd   ©rö  N©r  )rq   r  r  r  rö  r  s    ```  r^   Ú
_write_sqlzSessionDB._write_sql#  s   ú€ ö	Fà×Ñ˜C¨JÐÕ7rd   r  c                ó8   ‡‡— ˆˆfd„}| j                  ||¬«      S )z�Run one UPDATE/DELETE through ``_execute_write``; return rows changed
        (``SELECT changes()`` when the driver reports None / negative).c                 ó’   •— | j                  ‰‰«      j                  }|�|dk  r"| j                  d«      j                  «       d   }|S )Nr   zSELECT changes())r;  ÚrowcountÚfetchone)r<  r  r  r  s     €€r^   r  z&SessionDB._write_rowcount.<locals>._do.  sG   ø€ Ø—|‘| C¨Ó0×9Ñ9ˆHØÐ 8¨a¢<ØŸ<™<Ð(:Ó;×DÑDÓFÀqÑI�ØˆOrd   r  r  )rq   r  r  rö  r  s    ``  r^   Ú_write_rowcountzSessionDB._write_rowcount+  s    ù€ õ	ð
 ×"Ñ" 3°:Ð"Ó>Ð>rd   c                 ó0   ‡‡— | j                  ˆˆfd„«      S )z<``fetchone()`` of one read-only statement via ``_read_ctx``.c                 óD   •— | j                  ‰‰«      j                  «       S rí   )r;  r  ©r<  r  r  s    €€r^   ú<lambda>z%SessionDB._read_one.<locals>.<lambda>7  ó   ø€ °d·l±lÀ3ÈÓ6O×6XÑ6XÓ6Z€ rd   ©Ú_read_retrying_ioerr©rq   r  r  s    ``r^   Ú	_read_onezSessionDB._read_one5  ó   ù€ à×(Ñ(Ô)ZÓ[Ð[rd   c                 ó0   ‡‡— | j                  ˆˆfd„«      S )z<``fetchall()`` of one read-only statement via ``_read_ctx``.c                 óD   •— | j                  ‰‰«      j                  «       S rí   )r;  Úfetchallr  s    €€r^   r  z%SessionDB._read_all.<locals>.<lambda>;  r  rd   r   r"  s    ``r^   Ú	_read_allzSessionDB._read_all9  r$  rd   c                 óX  — t        t        dz   «      D ]&  }	 | j                  «       5 } ||«      cddd«       c S  y# 1 sw Y   nxY wŒ7# t        j                  $ rL}|t        k\  st
        t        |«      j                  «       vr‚ t        j                  t        «       Y d}~Œ‘d}~ww xY w)a"  Run an idempotent SELECT through ``_read_ctx``, retrying a transient SQLITE_IOERR.

        A warm ``mode=ro`` pooled reader can hit the same millisecond-wide WAL transition window as a
        read-only OPEN (#100436) when its statement executes or steps: a sibling process's checkpoint /
        WAL reset / frame flush surfaces ``disk I/O error`` because a read-only connection cannot rewrite
        the -shm index (#100871, WSL2 ext4-on-vhdx, multi-process). The window closes on its own, so
        the statement is replayed on the SAME connection within the read-only IOERR budget -- never
        closed and reopened (close() cancels this process's POSIX locks for every sibling connection),
        never quarantined (busy is not broken). A persistent IOERR exhausts the budget and propagates.r�   N)r¤  r¥  rï  r  rª  r   rw   rî   r«  r¬  r­  )rq   rõ  r®  r<  rP  s        r^   r!  zSessionDB._read_retrying_ioerr=  sŸ   € ô Ô<¸qÑ@ÓAò 	=ˆGð=Ø—^‘^Ó%ð $¨Ù˜d›8÷$ó $ñ	=÷$ð $úð $øä×+Ñ+ò =ØÔ=Ò=ÔAVÔ^aÐbeÓ^f×^lÑ^lÓ^nÑAnØÜ—
‘
Ô;×<Ñ<ûð=ús,   —A
§=¯	A
½A	ÁA
Á
B)ÁAB$Â$B)c                 óÂ  — | j                   s| j                  €yt        j                  «       j                  }	 | j
                  5  | j                  j                  dt        f«      j                  «       }|r|d   sQ| j                  j                  dt        |f«       | j                  j                  dt        f«      j                  «       }|r|d   rt        |d   «      }| j                  j                  d«      j                  «       }|rt        |d   xs d«      nd}|dk(  r4t        |dd d«      dz  xs d	}| j                  j                  d
|› �«       || _        	 | j                  j                  d«       ddd«       y# t        j                  $ r Y Œw xY w# 1 sw Y   yxY w# t        j                  $ r }t        j                  d|«       Y d}~yd}~ww xY w)zäMint a once-per-file generation stamp (state_meta + application_id). First opener wins (INSERT
        OR IGNORE); application_id is written only while 0 so racers converge. PASSIVE checkpoint only.

        See #45383.
        Nú*SELECT value FROM state_meta WHERE key = ?r   z;INSERT OR IGNORE INTO state_meta (key, value) VALUES (?, ?)úPRAGMA application_idé   é   iÿÿÿr�   zPRAGMA application_id=úPRAGMA wal_checkpoint(PASSIVE)z%state.db generation stamp skipped: %s)rR  r€  ÚuuidÚuuid4Úhexra  r;  r   r  rw   rZ   rn  r  rÚ  rã   rK  )rq   ÚtokenrB  Ú
pragma_rowÚcurrentrP  s         r^   r›  z$SessionDB._ensure_db_file_generationP  s¶  € ð �>Š>˜TŸZ™ZÐ/ØÜ—
‘
“× Ñ ˆð	GØ—‘ñ ð
 —j‘j×(Ñ(Ø@ÔC[ÐB]óç‘(“*ð ñ   A¢Ø—J‘J×&Ñ&ØUÜ1°5Ð9ôð Ÿ*™*×,Ñ,ØDÜ1Ð3ó÷ ‘h“jð ñ ˜3˜qš6Ü  A¡›K�EØ!ŸZ™Z×/Ñ/Ð0GÓH×QÑQÓS�
Ù5?œ#˜j¨™mÒ0¨qÔ1ÀQ�Ø˜a’<Ü" 5¨¨! 9¨bÓ1°JÑ>ÒDÀ1�GØ—J‘J×&Ñ&Ð)?À¸yÐ'IÔJØ/6�Ô,ðØ—J‘J×&Ñ&Ð'GÔH÷5ð øô6 —}‘}ò Ùðú÷7ð ûô: �}‰}ò 	GÜ�L‰LÐ@À#×FÑFûð	GúsT   ¹F+ ÁDFÅ"FÅ=F+ ÆFÆFÆFÆFÆF(Æ$F+ Æ(F+ Æ+GÆ>GÇGc                 óp  — t        | j                  «      | _        t        | j                  «      | _        t        | j                  «      }|r|| _        y| j                  �<| j                  s/	 | j                  d«      }|r|d   rt        |d   «      | _        yyyyy# t        j                  $ r d}Y Œ6w xY w)z?Snapshot inode plus the on-disk generation header when present.Nr,  r   )Ú_stat_db_file_identityr�   rm  r:   ro  r9   rn  r€  r#  r  rÚ  rZ   )rq   Údisk_idr4  s      r^   r‹  z"SessionDB._record_db_file_identityz  s©   € ä!7¸¿¹Ó!EˆÔÜ$AÀ$Ç,Á,Ó$OˆÔ!Ü-¨d¯l©lÓ;ˆÙØ+2ˆDÕ(Ø�Z‰ZÐ#¨D×,HÒ,Hð"Ø!Ÿ^™^Ð,CÓD�
ñ ˜j¨šmÜ/2°:¸a±=Ó/A�Õ,ð ,ˆzð -IÐ#øô —=‘=ò "Ø!’
ð"ús   Á-B ÂB5Â4B5c                 óÖ   — | j                   }|�t        | j                  «      |k7  ryt        | j                  xs d«      }|syt        | j                  «      }t        |xr ||k7  «      S )zATrue when the path no longer names the file this instance opened.Tr   F)rm  r7  r�   rZ   rn  r9   rÜ   )rq   ÚrecordedÚrecorded_appÚdisk_apps       r^   rò  zSessionDB._db_file_was_replaced‰  se   € à×)Ñ)ˆØÐÔ$:¸4¿<¹<Ó$HÈHÒ$TØÜ˜4×7Ñ7Ò<¸1Ó=ˆÙØä.¨t¯|©|Ó<ˆÜ�HÒ9 ¨\Ñ!9Ó:Ð:rd   c                 ó6  ‡— | j                   xs i }t        j                  | j                  «      Š|r"t	        ˆfd„|j                  «       D «       «      S | j                  syt        j                  j                  d«      rlt        | j                  «      }	 t        t        j                  «       «      D ]5  \  }}t        j                  |«      }d|v sŒ ||v sŒ%t        |||   «      sŒ5 y 	 t#        | j                  «      }|r|| _         y# t         $ r Y yw xY w)a/  True when the WAL/SHM generation this handle opened is gone. Recorded
        generation: pure stat (no /proc walk on healthy writes). Empty identity
        (WAL appeared after open, or cleared by a clean close()): probe
        /proc/self/fd for deleted sidecars and adopt the current ones once clean.c              3   óX   •K  — | ]!  \  }}t        t        ‰|z   «      «      |k7  –— Œ# y ­wrí   )r7  r   )rï   ÚsuffixÚidentÚbases      €r^   rò   z5SessionDB._wal_generation_was_lost.<locals>.<genexpr>�  s/   øè ø€ ò ÙIVÈÐQVÔ&¤t¨D°6©MÓ':Ó;¸uÕDñùs   ƒ'*FÚlinuxz
 (deleted)T)ro  r†   Úfspathr�   rü   Úitemsrk  rY  ÚplatformrÏ   r;   r,   r‡   r"  Úcanonical_sqlite_pathr7   r�   r:   )rq   r:  ÚwatchedÚtargetÚfd_pathÚ	canonicalÚcurrent_identityrA  s          @r^   Ú_wal_generation_was_lostz"SessionDB._wal_generation_was_lost•  s  ø€ ð
 ×,Ñ,Ò2°ˆÜ�y‰y˜Ÿ™Ó&ˆÙÜó ØZb×ZhÑZhÓZjôó ð ð ×ÒØÜ�<‰<×"Ñ" 7Ô+Ü3°D·L±LÓAˆGðÜ'7¼¿	¹	»Ó'Dò $‘O�F˜GÜ .× DÑ DÀVÓ L�IØ$¨Ò.°9ÀÒ3GÜ 5°g¸wÀyÑ?QÕ RÙ#ñ	$ô 9¸¿¹ÓFÐÙØ(8ˆDÔ%Øøô ò Ùðús*   Â<D ÃD ÃD Ã)D Ã+D Ä	DÄDc                 ó  — | j                   s| j                  «       r?d| _         | j                  «        t        j	                  t
        «       t        t
        «      ‚| j                  s| j                  «       rQd| _        | j                  «        	 | j                  d«       t        j	                  t        «       t        t        «      ‚y# t        $ r+}t        j	                  d| j                  |«       Y d}~ŒXd}~ww xY w)zÓStop writes (logging once) when the file was replaced or its WAL/SHM generation
        is gone: never run in-file repair on a new generation, never keep committing on a
        split WAL. Both flags are sticky.TÚhaltz}Could not capture the retired WAL generation of %s at halt: %s. close() retries the capture and refuses to settle without it.N)rp  rò  Ú_disable_close_time_checkpointrã   rš  r   r   rq  rL  Ú_capture_retired_generationr@   r�   r   r   )rq   rP  s     r^   rô  z(SessionDB._halt_if_db_generation_changed²  sÔ   € ð ×Ò × :Ñ :Ô <Ø $ˆDÔØ×/Ñ/Ô1Ü�L‰LÔ/Ô0Ü&Ô'=Ó>Ð>Ø×'Ò'¨4×+HÑ+HÔ+JØ+/ˆDÔ(Ø×/Ñ/Ô1ðØ×0Ñ0°Ô8ô �L‰LÔ4Ô5Ü+Ô,GÓHÐHð ,Køô
 1ò Ü—‘ðDØEIÇ\Á\ÐSV÷ñ ûðús   ÂC
 Ã
	C>Ã!C9Ã9C>Útriggerc                 ó:  — | j                   5  | j                  �| j                  cddd«       S t        | j                  t	        | j
                  xs i «      |¬«      }|| _        ddd«       t        j                  d| j                  |«       |S # 1 sw Y   Œ-xY w)a6  Durably capture the lost WAL generation this handle still holds open, once per handle.

        The quarantine keeps the retired frames from being checkpointed under wrong page numbers,
        but they live only in an unlinked inode that dies with this process's last descriptor, and
        the canonical DeletedWalGenerationError remediation is to stop the writers. Capturing at the
        first halt (or at close(), whichever sees the loss first) makes "preserve" outlive the
        process. Raises RetiredGenerationCaptureError; nothing is mutated on failure.N)Úsidecar_identityrQ  z�Captured the retired WAL generation of %s at %s to %s; read its manifest.json before deciding whether those frames belong on top of the file now at the path.)rs  rr  rA   r�   rÍ   ro  rã   rO  )rq   rQ  Úartifacts      r^   rP  z%SessionDB._capture_retired_generationË  sœ   € ð ×'Ñ'ñ 	8Ø×/Ñ/Ð;Ø×7Ñ7÷	8ñ 	8ô 6Ø—‘¬t°D×4MÑ4MÒ4SÐQSÓ/TÐ^eôˆHð 08ˆDÔ,÷	8ô 	�‰ðNØOSÏ|É|Ð]dÐfnô	
ð ˆ÷	8ð 	8ús   �B¯6BÂBc                 ó�   — | j                   rt        t        «      ‚| j                  rt	        t
        «      ‚| j                  «        y)zHSticky-flag fast path (no log spam on every write), then the live probe.N)rp  r   r   rq  r   r   rô  rÄ  s    r^   rù  zSessionDB._raise_if_db_replacedà  s8   € à×ÒÜ&Ô'=Ó>Ð>Ø×'Ò'Ü+Ô,GÓHÐHØ×+Ñ+Õ-rd   rP  c                 ó¤   — t        |t        j                  «      xr5 t        |t        «       xr" | j	                  |«       xr t        |«      dk(  S )zŽBare SQLITE_CORRUPT/NOTADB with no FTS provenance: canonical B-tree/schema/freelist damage,
        never repairable from the live write path.Úcorrupt)rÌ   r  r™  r   r  r!   )ÚclsrP  s     r^   r  z)SessionDB._is_structural_corruption_errorè  sV   € ô
 �sœG×1Ñ1Ó2ò =Ü˜sÔ$7Ó8Ð8ò=à×6Ñ6°sÓ;Ð;ò=ô +¨3Ó/°9Ñ<ð		
rd   rè   r   c                 óD   — t        |› t        › d| j                  › d�«      S )zDBuild the quarantine error for this handle (message assembled once).z	 (cause: ú))r   r   rx  )rq   rè   s     r^   ró  zSessionDB._corrupt_erroró  s(   € ä" f XÔ.CÐ-DÀIÈd×NeÑNeÐMfÐfgÐ#hÓiÐird   c           	      ó&  — d| _         t        |«      | _        | j                  «        t        j                  d| j                  || j                  «       | j                  «       }dD ]'  }t        ||d«      €Œt        ||t        ||«      «       Œ) ||‚)z@Quarantine this handle and raise; never run in-file repair here.Tzþstate.db %s reported structural corruption outside the FTS indexes (%s); quarantining this handle: no further writes, no automatic reopen, no explicit WAL checkpoint at close. Stop the gateway and run `hermes sessions recover --source %s --inspect-only`.)Úsqlite_errorcodeÚsqlite_errornameN)
rw  rw   rx  rO  rã   rš  r�   ró  r  Úsetattr)rq   rP  rÓ  Úattrs       r^   r  zSessionDB._halt_db_corrupt÷  s–   € àˆÔÜ"% c£(ˆÔØ×+Ñ+Ô-Ü�‰ðTð VZ×UaÑUaÐcfØ�L‰Lô	
ð ×!Ñ!Ó#ˆØ<ò 	7ˆDÜ�s˜D $Ó'Ñ3Ü˜˜T¤7¨3°Ó#5Õ6ð	7ð �sÐrd   c                 óØ   — t        t        dd«      }| j                  }t        |dd«      }|�|€y	  ||d«       y# t        $ r% t        j                  d| j                  d¬«       Y yw xY w)aý  Best-effort SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE (Python 3.12+): sqlite3's
        close() otherwise runs the internal last-connection checkpoint that wrote
        the incident's pages under wrong page numbers (see StateDbCorruptError and
        the generation-loss halts).
        <3.12 has no setconfig, so a lost-generation handle is retired unclosed
        instead (see close()): closing its last descriptor could both run that
        checkpoint and discard committed data present only in an unlinked WAL.r	  Nr
  FTz–Could not disable SQLite's close-time checkpoint on the quarantined handle for %s; closing it may checkpoint retired frames over the newer generation.rI  )r  r  r€  r[   rã   rš  r�   )rq   Úflagr<  r
  s       r^   rO  z(SessionDB._disable_close_time_checkpoint	  s…   € ô ”wÐ BÀDÓIˆØ�z‰zˆÜ˜D +¨tÓ4ˆ	Øˆ<˜9Ð,ð
 ð
	Ù�d˜DÔ!ð øô ò 	ô �L‰LðVà—‘ tð ô ñ
 ð	ús   ±	; »+A)Á(A)c                 óN   — | j                   s| j                  |«       d| _         yy)a  Retain the exact quarantined connection past GC and interpreter teardown (once per handle).

        Takes the connection as a parameter: callers are lock-held close paths, and the
        writer-conn thread-safety audit flags self._conn in functions outside `with self._lock`.TN)ru  rt  râ  s     r^   Ú_pin_connectionzSessionDB._pin_connection'  s(   € ð
 ×&Ò&Ø×#Ñ# DÔ)Ø&*ˆDÕ#ð 'rd   c                 ó   — d| _         | j                  «        xr | j                  du}	 | j                  d«      }t        j                  d| j                  |«       |r t        j                  d| j                  «       |S # t        $ rD}|r| j                  | j                  «       t        j                  d| j                  |«       ‚ d}~ww xY w)a“  Capture the retired generation; return whether the handle must be retired unclosed.

        Where SQLite's close-time checkpoint cannot be switched off (no setconfig, Python < 3.12),
        sqlite3_close would write the retired frames over the newer generation, so the exact
        connection is retired unclosed instead. A failed capture leaves the handle open for a retry
        -- but the pin is taken FIRST on such a runtime: every production caller reaches close()
        through hermes_state_registry.release_or_close, which swallows the error, so an interpreter
        exit before the retry must not be able to checkpoint the stale frames either.TNrµ   z›Could not capture the retired WAL generation of %s at close: %s. The handle stays open and close() retries the capture; those frames are NOT yet preserved.zóSkipping the close-time WAL checkpoint for %s: this handle's WAL/SHM generation was deleted or replaced; the retired generation is captured at %s. Stop the other writers before reopening and inspect the capture before deciding its disposition.ztRetaining the quarantined connection for %s unclosed: this runtime cannot switch off SQLite's close-time checkpoint.)rq  rO  rt  rP  r@   rc  r€  rã   rš  r�   rO  )rq   Úretire_without_closerT  rP  s       r^   Ú_settle_lost_generation_lockedz(SessionDB._settle_lost_generation_locked0  sÒ   € ð (,ˆÔ$Ø#'×#FÑ#FÓ#HÐHÒpÈT×MdÑMdÐlpÐMpÐð		Ø×7Ñ7¸Ó@ˆHô 	�‰ð`ð �L‰L˜(ô		
ñ  Ü�N‰Nð=Ø>B¿l¹lôð $Ð#øô' -ò 	Ù#Ø×$Ñ$ T§Z¡ZÔ0Ü�L‰LðWØX\×XdÑXdÐfiôð ûð	ús   ªB  Â 	CÂ	?CÃCc                 ó<   — | j                   r| j                  «       ‚y rí   )rw  ró  rÄ  s    r^   rø  zSessionDB._raise_if_db_corruptR  s   € Ø×ÒØ×%Ñ%Ó'Ð'ð rd   rÒ  c           	      ó>  — t        j                  «       }||k\  ry|||z
  z
  | j                  k\  }t        j                  |r| j
                  | j                  fn| j                  | j                  fŽ }t        j                  t        |t        ||z
  d«      «      «       y)z¾Sleep one jitter interval if the budget allows; True = retry, False = deadline passed. Small
        jitter for the first _WRITE_RETRY_SLOW_AFTER_S, then slow; never overshoots the deadline.FrÉ  T)r«  rÊ  Ú_WRITE_RETRY_SLOW_AFTER_SrÌ  rÍ  rÎ  rÏ  Ú_WRITE_RETRY_MIN_SÚ_WRITE_RETRY_MAX_Sr¬  rÐ  rÑ  )rq   rÒ  rö  rÔ  ÚslowrÕ  s         r^   r  z#SessionDB._sleep_before_write_retryV  s�   € ô �n‰nÓˆØ�(Š?ØØ�h Ñ+Ñ,°×0NÑ0NÑNˆÜ—‘ÙLPˆT×)Ñ)¨4×+GÑ+GÑHØ×)Ñ)¨4×+BÑ+BÐCðˆô 	�
‰
”3�vœs 8¨c¡>°5Ó9Ó:Ô;Ørd   c                 ó,   — t        | j                  «      S )zQForeign processes holding this DB or its WAL sidecars (see hermes_state_holders).)r$  r�   rÄ  s    r^   r$  z#SessionDB._foreign_state_db_holdersd  s   € ä(¨¯©Ó6Ð6rd   c                 óp   — | j                   rd| j                  › d�S | j                  ry| j                  ryy)u  Why this handle must not checkpoint or run in-file repair, or None. A corrupted image has
        torn B-trees; a replaced file or a deleted/replaced WAL generation would checkpoint under
        wrong page numbers into the main DB -- the shutdown-time cause of #105670. Precedence note:
        close() evaluates generation loss BEFORE calling this (and skips it entirely when lost â€”
        a lost generation settles through the capture path, not the quarantine advisory), while
        the halt path checks replaced first.zstructural corruption (rZ  za replaced state.db filez&a deleted WAL generation (split-brain)N)rw  rx  rp  rq  rÄ  s    r^   Ú_quarantine_reasonzSessionDB._quarantine_reasonh  s>   € ð ×ÒØ,¨T×-DÑ-DÐ,EÀQÐGÐGØ×ÒØ-Ø×'Ò'Ø;Ørd   c                 óú  — | j                  «       �y	 | j                  5  | j                  €
	 ddd«       y| j                  r*t	        j
                  | j                  | j                  «       | j                  j                  d«      j                  «       }|r%|d   dkD  rt        j                  d|d   |d   «       ddd«       y# 1 sw Y   yxY w# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)a€  Best-effort PASSIVE WAL checkpoint; never raises. PASSIVE never blocks writers;
        TRUNCATE corrupted B-trees on 65K+ page databases under exclusive-lock I/O pressure.

        Previous TRUNCATE strategy caused B-tree corruption on large databases (65K+ pages) due to the
        exclusive-lock I/O pressure from checkpointing thousands of frames at once (issue #45383).
        Nr/  r�   r   z(WAL checkpoint: %d/%d pages checkpointedé   z#WAL checkpoint (PASSIVE) failed: %s)ro  ra  r€  rv  rœ  r�  r�   r;  r  rã   rK  r[   rO  )rq   r	  rP  s      r^   rý  zSessionDB._try_wal_checkpointw  så   € ð ×"Ñ"Ó$Ð0Øð
	GØ—‘ñ cØ—:‘:Ð%Ø÷cð cð ×'Ò'Ü—O‘O D§L¡L°$×2FÑ2FÔGØŸ™×+Ñ+Ð,LÓM×VÑVÓX�Ù˜f Q™i¨!šmÜ—L‘LÐ!KÈVÐTUÉYÐX^Ð_`ÑXaÔb÷c÷ cñ cûô ò 	GÜ�N‰NÐ@À#×FÑFûð	Gús?   “C ŸC­C ¶BCÂ<C ÃCÃ
C ÃC Ã	C:ÃC5Ã5C:c                 ó   — | S )u0  ``with SessionDB(path) as db:`` closes on exit; owners must release deterministically.

        Ownership of a SessionDB should be released explicitly. Historically an instance with a started
        token writer pinned ITSELF (bound-method writer target plus a strong ``atexit`` drain hook), so
        ``__del__`` never ran for exactly the instances that leaked descriptors (#88033). The writer now
        retires after an idle window and the atexit hook holds only a weak reference, so abandoned handles
        are eventually collectible â€” but "eventually, after the idle window and a GC cycle" is not a release
        policy. Call sites owning a handle are still expected to close it deterministically (see the
        ownership comments in ``run_agent.py`` and ``tui_gateway/methods_session.py``).
        rc   rÄ  s    r^   Ú	__enter__zSessionDB.__enter__Œ  s	   € ð ˆrd   c                 ó$   — | j                  «        y©NF)rµ   )rq   Úexc_typerP  Útbs       r^   Ú__exit__zSessionDB.__exit__™  s   € Ø�
‰
ŒØrd   c                 óš  — | j                   rddlm}  || «       y| j                  «        | j                  dc}| _        |�t        j                  |«       | j                  5  d| _        ddd«       | j                  «       r	 | j                  «       rŒ| j                  5  | j                  �r5| j                   xr5 | j                  xs' t        | j                  «      xr | j!                  «       }|xr | j#                  «       }|rdn| j%                  «       }|�-t&        j)                  d| j*                  || j*                  «       n*| j                  s|s	 | j                  j-                  d«       t3        j                  | j4                  «       |r#| j7                  | j                  «       d| _        n,| j                  dc}| _        | j9                  |«       i | _        ddd«       | j:                  j                  | «       y# 1 sw Y   �ŒŸxY w# t.        $ r }t&        j1                  d|«       Y d}~ŒÅd}~ww xY w# 1 sw Y   Œ^xY w)uƒ  Drain queued token deltas, then a PASSIVE checkpoint on writable handles
        (NOT TRUNCATE: a full WAL reset races the gateway's live writer, tearing
        B-tree pages). A registry-shared instance RELEASES one refcount instead.

        Drains queued token deltas first (the background writer needs the connection). Read-only connections
        never request a checkpoint. See #45383.
        When this instance is shared (opened via ``hermes_state_registry.acquire``), ``close()`` RELEASES one
        refcount instead of tearing down the connection: the registry owns the lifecycle and only closes on
        the final release (#90837). This prevents one caller's close from tearing down the writer connection
        that other callers in the same process are still using â€” while still letting legacy ``close()`` call
        sites return their reference instead of leaking it.
        r   )rÙ  NTzÄSkipping the close-time WAL checkpoint for %s: this handle observed %s. Take a snapshot of state.db, -wal and -shm before restarting, then run `hermes sessions recover --source %s --inspect-only`.r/  z,WAL checkpoint (PASSIVE) at close failed: %s)rˆ  Úhermes_state_registryrÙ  Ú_stop_token_writerr‡  ÚatexitÚ
unregisterrh  ri  rã  ra  r€  rR  rq  rÜ   ro  rL  rf  ro  rã   rO  r�   r;  r[   rK  rœ  rv  rc  rL  re  )rq   rÙ  ÚhookÚgeneration_lostre  Úquarantine_reasonrP  r<  s           r^   rµ   zSessionDB.close�  s  € ð ×&Ò&Ý5Ù�DŒMØØ×ÑÔ!Ø(,×(?Ñ(?ÀÐ%ˆˆdÔ%ØÐÜ×Ñ˜dÔ#à×"Ñ"ñ 	+Ø&*ˆDÔ#÷	+à×,Ñ,Ô.Øð ×,Ñ,Õ.à�Z‰Zñ $	3Ø�z‹zØ&*§n¡nÐ"4ò #Ø×0Ñ0ò ]Ü˜T×6Ñ6Ó7Ò[¸D×<YÑ<YÓ<[ð  ð (7Ò'`¸4×;^Ñ;^Ó;`Ð$Ù,;¡DÀ×AXÑAXÓAZÐ!Ø$Ð0Ü—N‘Nðlð Ÿ™Ð&7¸¿¹õ	ð Ÿš±ðZð Ÿ
™
×*Ñ*Ð+KÔLô ×"Ñ" 4×#7Ñ#7Ô8Ù'Ø×(Ñ(¨¯©Ô4Ø!%�D•Jà'+§z¡z°4Ð$�D˜$œ*Ø×2Ñ2°4Ô8ð 13�DÔ-÷I$	3ðJ 	×Ñ×$Ñ$ TÕ*÷S	+ñ 	+ûô: %ò ZÜŸ™Ð%SÐUX×YÑYûðZú÷3$	3ð $	3úsD   Á#HÂ!B7IÅHÅ4A0IÈHÈ	H>ÈH9È4IÈ9H>È>IÉI
c                 ó|   — | j                   j                  d«      �	 | j                  «        yy# t        $ r Y yw xY w)zuSafety net: close() if the caller forgot. Attribute access stays
        guarded: module teardown order is undefined.r€  N)Ú__dict__rY   rµ   r[   rÄ  s    r^   Ú__del__zSessionDB.__del__Þ  s@   € ð �=‰=×Ñ˜WÓ%Ð1ðØ—
‘
•ð 2øô ò Ùðús   �/ ¯	;º;)Úinput_tokensÚoutput_tokensÚcache_read_tokensÚcache_write_tokensÚreasoning_tokensÚapi_call_count)Úestimated_cost_usdÚactual_cost_usd)ÚmodelÚcost_statusÚcost_sourceÚpricing_versionÚbilling_providerÚbilling_base_urlÚbilling_modeÚsourceéd   r   r�   rq  zBot Chatz json:Ú	reactionsa*  id, role, content, tool_call_id, tool_calls, tool_name, effect_disposition, finish_reason, reasoning, reasoning_content, reasoning_details, codex_reasoning_items, codex_message_items, platform_message_id, observed, _compressed_summary, timestamp, active, api_content, display_kind, display_metadatarR   c                 ó¬   — | j                   5  | j                  j                  d|f«      j                  «       }ddd«       €dS |d   S # 1 sw Y   ŒxY w)z›Read state_meta[key] on self._lock (not _read_ctx): fts_rebuild_step reads progress before its
        write transaction and a WAL reader would not see it.r+  Nr   )ra  r€  r;  r  )rq   rR   rB  s      r^   Úget_metazSessionDB.get_meta  s[   € ð �Z‰Zñ 	fØ—*‘*×$Ñ$Ð%QÐTWÐSYÓZ×cÑcÓeˆC÷	fà�{ˆtÐ.¨¨A©Ð.÷	fð 	fús   �,A
Á
A©r§  r\   r§  c                ó^   — d}|�|j                  |||f«       y| j                  |||f«       y)u˜   Upsert state_meta[key]; with ``cursor`` the write is inline (the caller already holds a
        transaction â€” nesting BEGIN IMMEDIATE would deadlock).zgINSERT INTO state_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.valueN)r;  r  )rq   rR   r\   r§  r  s        r^   Úset_metazSessionDB.set_meta  s9   € ðDð 	ð ÐØ�N‰N˜3  e Õ-à�O‰O˜C # u Õ.rd   Úworkspaces_rootc                 ó¦   ‡ ‡‡— t        |«      j                  d«      Š‰syd‰› �Š‰ j                  ‰«      dk(  ryˆˆˆ fd„}‰ j                  |«      S )z§Retag legacy kanban worker rows from ``cli`` to ``kanban`` by cwd under the board's workspaces
        root; gated once per root via state_meta. Returns rows retagged.z/\r   zkanban_worker_source_retagged:Ú1c                 ó�   •— | j                  d‰t        ‰«      dz   f«      }|j                  xs d}‰j                  ‰d|¬«       |S )NzaUPDATE sessions SET source = 'kanban' WHERE source = 'cli' AND (cwd = ? OR cwd LIKE ? ESCAPE '\')z/%r   r�  r˜  )r;  Ú_escape_liker  rš  )r<  r§  ÚretaggedÚgaterè   rq   s      €€€r^   r  z3SessionDB.retag_kanban_worker_sessions.<locals>._do1  sP   ø€ Ø—\‘\ðOàœ fÓ-°Ñ4Ð5óˆFð —‘Ò+¨!ˆHØ�M‰M˜$ ¨FˆMÔ3ØˆOrd   )rw   Úrstripr—  r  )rq   r›  r  r¡  rè   s   `  @@r^   Úretag_kanban_worker_sessionsz&SessionDB.retag_kanban_worker_sessions(  sX   ú€ ô �_Ó%×,Ñ,¨UÓ3ˆÙØØ/°¨xÐ8ˆØ�=‰=˜Ó #Ò%Øö		ð ×"Ñ" 3Ó'Ð'rd   c                 ó‚   — |sg S | j                  dt        |«      dz   f«      }|D �cg c]  }|d   |d   f‘Œ c}S c c}w )u–   ``[(key, value), ...]`` for state_meta keys starting with the literal
        ``prefix`` (LIKE wildcards escaped) â€” e.g. ``loop:<session_id>`` rows.z=SELECT key, value FROM state_meta WHERE key LIKE ? ESCAPE '\'ú%r   r�   )r(  rŸ  )rq   rè   ÚrowsrB  s       r^   Úlist_meta_prefixzSessionDB.list_meta_prefix=  sQ   € ñ ØˆIØ�~‰~ØLÌ|Ð\bÓOcÐfiÑOiÐNkó
ˆð -1Ö1 S��Q‘˜˜Q™Ò Ò1Ð1ùÒ1s   ¨<)rT   Nru  )F)r  rí   )rc   )r€   )rT   r&  )prs   rt   ru   Ú__doc__r/  r   rw   Ú__annotations__rË  Ú_TRANSCRIPT_WRITE_PATIENCE_SÚ_ACTIVITY_WRITE_PATIENCE_Sr   rj  rk  ri  rÎ  rÏ  rü  rþ  Ú_FTS_MERGE_MAX_PAGES_PER_INDEXÚ_FTS_MERGE_COMMANDS_PER_PASSÚ_IMPORT_MAX_SESSIONSÚ _IMPORT_MAX_MESSAGES_PER_SESSIONÚ_IMPORT_MAX_TOTAL_MESSAGESÚ_IMPORT_MAX_SESSION_BYTESÚ_IMPORT_MAX_TOTAL_BYTESÚ_TOKEN_WRITER_IDLE_SECONDSÚstaticmethodr   r>  rA  r  r¶  r
   r   rG  r  rL  rQ  r   rÜ   rp   rŠ  r‰  Úfloatr¦  r—  rÁ  rÅ  r˜  rÝ  rã  rá  ræ  r   r   rï  rí  r	   r”   r  r  rZ   r  r#  r   r(  r!  r›  r‹  rò  rL  rô  rP  rù  Úclassmethodr©  r  ró  r  rO  rc  rf  rø  r  r$  ro  rý  rs  rx  rµ   rƒ  Ú_TOKEN_DELTA_SUM_FIELDSÚ_TOKEN_DELTA_COST_FIELDSÚ_TOKEN_DELTA_ROUTE_FIELDSÚMAX_TITLE_LENGTHÚ_TITLE_SOURCE_DERIVEDr   Ú_TITLE_SOURCE_LLMr   Ú_TITLE_SOURCE_USERr   Ú_TITLE_SOURCE_RANKÚCANONICAL_BOT_CHAT_TITLEÚ_CONTENT_JSON_PREFIXÚREACTIONS_METADATA_KEYÚ_CONVERSATION_ROW_COLUMNSr—  ÚCursorrš  r£  r§  rc   rd   r^   r&  r&  »  sQ  … ñ að7Ð" E¨#¨s¨(¡Oó ð SbÑOÐÐ3Ð5Oð  #ÐØ-9Ñ*ÐÐ*Ø #ÐØ7CÑ4ÐÐ4à!#Ðð _kÑ[ÐÐ=Ð?[àYlÑVÐÐ:Ð<VØ9ZÑ6ÐÐ6à!%Ðàð°(¸3±-ð ÀHÈSÁMò ó ðð ò
ó ð
ð ð˜wŸ{™{ð ¨t°C¸°H©~ò ó ðð ðR¨°×1CÑ1CÑ(Dð RÈò Ró ðRðS¨cð S°dó Sñ_. ð _.¸ó _.óB+AóZ=ð@¨%ð °G×4FÑ4Fó ñ-¸Dð -ÈTó -ð, 7×#5Ñ#5ó ó4óDð<' ¨×);Ñ);Ñ <ó 'ðR¨4ó ó(ð) X¨g×.@Ñ.@Ñ%Aó )ð ð7˜8 G×$6Ñ$6Ñ7ò 7ó ð7ñ8 A°#ð  AÀDó  AðF TXñuØ˜G×.Ñ.Ð/°Ð2Ñ3ðuØAIÈ%Áðuà	
óuðp ')ð8Ø:?Ð_cò8Øð8Ø #ð8Ø37ð8ØMUÐV[É_ð8à	ó8ð?Ð[_ò ? 3ð ?°ð ?ÈÐRWÉð ?Ðdgó ?ñ\˜Sð \¨#ð \°xÀÇÁÑ7Ló \ñ\˜Sð \¨#ð \°t¸G¿K¹KÑ7Hó \ð= x°×1CÑ1CÐ0DÀaÐ0GÑ'Hð =ÈQó =ó&(GóTBð
; tó 
;ð¨$ó ó:Ið2°3ð ¸4ó ó*.ð ð
°-ð 
ÀDò 
ó ð
ñj Sð jÐ2Gó jð Mð °dó ð$°ó ó<+ð $°ó  $óD(ð°%ð ÀUð Ètó ð7¨4°°c¸3°h±Ñ+@ó 7ð H¨S¡Mó óGó*ð¨Tó ò?+óBðÐð  IÐð!Ðð
 Ðð 1ÐØ(ÐØ*ÐØ.°Ð3CÀQÐHYÐ[\Ð]Ðð  *Ðð 'Ðà(Ðð	^ð ð/˜Cð / H¨S¡Mó /ð TXò 
/˜Cð 
/¨ð 
/¸ÀÇÁÑ8Pð 
/Ð\`ó 
/ð(¸Cð (ÀCó (ð*2 sð 2¨t°E¸#¸s¸(±OÑ/Dô 2rd   r&  c                   ó$   — e Zd ZdZdd„Zdefd„Zy)ÚAsyncSessionDBzžAsync door onto SessionDB: every call runs via asyncio.to_thread so a blocking SQLite call
    never freezes the event loop (no method returns a live cursor).Nc                 ó   — || _         y rí   )Ú_db)rq   Údbs     r^   rp   zAsyncSessionDB.__init__L  s	   € Øˆ�rd   rŠ   c                 óX   ‡— t        | j                  |«      Št        ‰«      s‰S ˆfd„}|S )Nc               Ÿ   óR   •K  — t        j                  ‰g| ¢­i |¤Žƒ d {  –—† S 7 Œ­wrí   )ÚasyncioÚ	to_thread)ÚargsÚkwargsr_  s     €r^   Ú
_offloadedz.AsyncSessionDB.__getattr__.<locals>._offloadedS  s(   øè ø€ Ü ×*Ñ*¨4ÐA°$ÒA¸&ÑA×AÐAÐAús   ƒ' %¡')r  rÇ  Úcallable)rq   rŠ   rÏ  r_  s      @r^   Ú__getattr__zAsyncSessionDB.__getattr__O  s+   ø€ Ü�t—x‘x Ó&ˆÜ˜Œ~ØˆKô	BàÐrd   )rÈ  r&  rT   N)rs   rt   ru   r¨  rp   rw   rÑ  rc   rd   r^   rÅ  rÅ  H  s   „ ñGóð ô rd   rÅ  )ÚSetÚAUTO_VACUUM_MIN_FREELIST_RATIO)Úhermes_state_commonrÓ  ÚActivityProvenance)zagent.session_activityrÕ  ÚCompressionSessionBusyError)Úhermes_state_errorsrÖ  ÚCompressionSessionClosedError)r×  rØ  ÚDEFERRED_INDEX_SQL)rÔ  rÙ  ÚFTS_CJK_STALE_KEY)rÔ  rÚ  ÚFTS_CJK_TABLE_SQL)Úhermes_state_ftsrÛ  ÚFTS_CJK_TRIGGER_SQL)rÜ  rÝ  ÚFTS_REBUILD_DEFERRAL_KEY)rÔ  rÞ  ÚFTS_SQL)rÔ  rß  ÚFTS_STALE_KEY)rÔ  rà  ÚFTS_STORAGE_VERSION)rÔ  rá  ÚFTS_TRIGRAM_SQL)rÔ  râ  ÚLEGACY_FTS_SQL)rÔ  rã  ÚLEGACY_FTS_TRIGRAM_SQL)rÔ  rä  ÚMAX_FTS5_QUERY_CHARS)rÔ  rå  ÚPERSISTENCE_ERROR_CAUSES)r×  ræ  Ú
SCHEMA_SQL)rÔ  rç  ÚSCHEMA_VERSION)rÔ  rè  ÚSESSION_STATUS_COMPLETE)Úhermes_state_sessionsré  ÚSESSION_STATUS_EMPTY)rê  rë  ÚSESSION_STATUS_ERROR)rê  rì  ÚSESSION_STATUS_INTERRUPTED)rê  rí  ÚSKILL_EXCERPT_JOINT)úagent.skill_commandsrî  ÚSKILL_SCAFFOLD_SQL_LIKE)rï  rð  ÚSessionTurnLeaseLostError)r×  rñ  ÚWalUnsupportedError)Úhermes_state_walrò  Úapply_durability_barriers)Úhermes_state_repairrô  Úclassify_session_status)rê  rö  Úcollect_state_db_stats)Úhermes_state_dbfiler÷  Úcount_db_holders)rø  rù  Údescribe_skill_invocation)rï  rú  Úfts5_cjk_so_path)rÜ  rû  Úis_advisory_lock_contention)rÔ  rü  )rÔ  Úis_automatic_end_reason)r×  Úis_disk_full_error)ró  Úis_sqlite_wal_reset_vulnerable)r×  Úis_transient_sqlite_error)rø  Ú#iter_deleted_sqlite_sidecar_holders)rz  Úrelease_or_close)Úhermes_startup_watchdogÚreport_startup_progress)ró  Úresolve_journal_mode)ró  Úresolve_synchronous_level)zagent.memory_managerÚsanitize_context)ró  Úsqlite_source_id)rê  Úworkspace_key)rý  rþ  rÿ  r   r  r  r  r  r  r  r  r	  c                 óÌ   — t         j                  | «      }|€t        dt        ›d| ›�«      ‚dd l}ddlm}  |t        | g|¢­Ž  t        |j                  |d   «      |d   «      S )Nzmodule z has no attribute r   )Ú	warn_oncer�   )	Ú_PLUGIN_COMPAT_LAZYrY   ÚAttributeErrorrs   Ú	importlibÚhermes_cli.plugin_compatr  r  Úimport_module)rŠ   rH  r  r  s       r^   rÑ  rÑ  ™  se   € Ü ×$Ñ$ TÓ*€FØ€~Ü˜w¤x lÐ2DÀTÀHÐMÓNÐNÛÝ2ÙŒh˜Ð&˜vÓ&Ü�9×*Ñ*¨6°!©9Ó5°v¸a±yÓAÐArd   )z/Hermes can't open its session history right now)Ár¨  rË  r|  r7  r  Úloggingr†   rb  rÌ  rƒ   r  r»   rY  r_  r«  r0  Úcollectionsr   Ú
contextlibr   Úpathlibr   r÷   r   r   Útypingr   r	   r
   r   r   r   r   r   r   rÔ  r   r»  r   r¼  r   r½  r   rŸ  r   r7  Úhermes_state_holdersr   r×  r   r   r   r   r   r   r   r   r   r    r!   r"   r#   Úhermes_state_guardr$   r%   r&   r'   r(   r)   r*   Úhermes_state_readpoolr+   r,   r-   rê  r.   rÜ  r/   r0   Úhermes_state_portabilityr1   Úhermes_state_telegramr2   Úhermes_state_profile_repairr3   Úhermes_state_schemar4   r"  Úhermes_state_lockguardrœ  Úhermes_state_lockownersr5   rø  r6   r7   r8   r9   r:   r;   r<   r=   r>   r?   r@   rA   rB   Úhermes_state_messagesrC   Úhermes_state_rewindrD   ró  rE   rF   rG   rH   rõ  rI   rJ   rK   Úhermes_state_titlesrL   Úhermes_state_usagerM   Úhermes_state_maintenancerN   Úhermes_state_gatewayrO   Úhermes_state_compressionrP   Úhermes_state_searchrQ   rˆ   ÚImportErrorÚ	getLoggerrs   rã   rv   rw   rZ   r_   re   ri   Ú
ValueErrorrk   r{   rÜ   r‘   Ú	frozensetÚ_BARE_BILLING_PROVIDERSr”   r™   rš   r×  r¥  r­  r›   rŸ   rœ   r©  rª   rÅ   rÐ   rÒ   rÙ   ÚcompilerÝ   rà   ræ   rø   r   rý   rþ   r  Úsetr  r`  Ú_repair_attempt_lockÚ_REPAIR_LOCK_TIMEOUT_SECONDSrE  Ú_IS_WINDOWSr  r   r$  r&  rÅ  rÒ  ÚerrnoÚstructÚweakrefÚMAX_SAFE_EXPORT_MESSAGESÚMAX_SAFE_RESUME_MESSAGESr  rÑ  rc   rd   r^   ú<module>r6     s   ðòó Û Û Û Û Û 	Û Û Û 	Û Û Û 
Û Û Û Ý Ý %Ý ç Eß V× VÕ V÷õ õ
 2÷÷ ÷ õ ÷÷ ñ ÷ UÑ TÝ 6ß JÝ <Ý <Ý AÝ 2Û -Û +Ý :÷÷ ÷ õ õ 7Ý 2÷ó ÷ hÑ gÝ 2Ý 0Ý <Ý 4Ý <Ý 2ðÛð 
ˆ×	Ò	˜8Ó	$€àÐ ð <Nñ 
 cð 
°Sð 
ÐRUó 
ð? có ?ð? có ?ô

 ô 

ô
 ô 
ð°Sð ¸Tó ñ< $ V¨XÐ$6Ó7Ð áˆCƒL€ñ -<Ó,=À
Ñ,JÐ J€Ð)ð  Ð ð ELÑ AÐ Ð!Aðm˜$ó mð Ð Ø57Ð   %¨¨c¨	Ñ"2Ó 7ð Dð ¨Tó ðB BGò 8 Dð 8¸$ð 8È4ó 8ðxÐ ð^¨t°C¸°H©~ð ^À$ó ^ð¨t°D¸¸c¸±NÑ/Cð ÈÈTÐRUÐWZÐRZÉ^ÑH\ó ð2 (˜RŸZšZÐ(IÓJÐ ðd¨D°°c°©Nð d¸tó dð¨T°$°s¸C°x±.Ñ-Að ÀdÈ4ÐPSÐUXÐPXÉ>ÑFZó ð LÐ Ø[Ð ØnÐ àhð ð Dð"ð ò"Øð"ð ð"ð 	ó	"ñN %(£EÐ ˜˜S™Ó )Ø%�y—~’~Ó'Ð ð  %Ð Ø�lŠl˜gÑ%€ð:¨Tó :ð°ð ÐBRó ð&< tð <°°U¸3À¸8±_Ñ0Eó <ôJ2ØÐ.Ð0BÐDVØÐ7Ð9PØÐ0Ð2CÐEWØÐ,Ð.Gô	J2÷Z$ñ õ( Û Û Û Û à!Ð à!Ð ð/Ø$Ð&_ð/àÐJð/ð "Ð#Yð/ð $Ð%]ð	/ð
 ÐGð/ð ÐEð/ð ÐBð/ð ÐFð/ð Ð Sð/ð Ð1ð/ð Ð=ð/ð ÐIð/ð ÐAð/ð Ð?ð/ð ÐOð/ð  ÐKð!/ð" Ð Sñ#/ð$ Ð7ð%/ð& Ð?ð'/ð( ÐSð)/ð* ÐMð+/ð, ÐMð-/ð. !Ð"Yð//ð0 ÐJð1/ð2 ÐRð3/ð4  Ð!Uð5/ð6 ÐFð7/ð8  Ð!Uð9/ð: ÐSð;/ð< ÐOð=/ð> ÐCð?/ð@  Ð!VðA/ðB Ð@ðC/ðD "Ð#YñE/ðF  RØGØ&\Ø!UØ+iØEØUØHØ!RØDØ@Ø?ò]/Ð ódBøðW2 ò ØƒFðús   ÅM4 Í4M?Í>M?