Ë
    âß²j´•  ã                  ó¬  — U d Z ddlm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mZmZ ddlmZ  ej$                  d«      ZdZdZ e«       Zd	ed
<    e	j2                  «       Z e«       Zd	ed<    e	j2                  «       Z e«       Zd	ed<    e	j2                  «       Z e«       Zd	ed<    e	j2                  «       Z  e«       Z!d	ed<    e	j2                  «       Z" e«       Z#ded<    e	j2                  «       Z$dZ%dRd„Z&dSd„Z'dTd„Z(dUd„Z)dTd„Z*dTd„Z+dTd„Z,dVdWd„ZdRd„Z-dXd„Z.dRd„Z/ G d„ dej`                  «      Z1 e2h d£«      Z3i Z4d ed!<    e	j2                  «       Z5 e«       Z6d	ed"<    e	j2                  «       Z7 e«       Z8d	ed#<    e	j2                  «       Z9dYdZd$„Z:dYd[d%„Z;d\d&„Z<d]d'„Z=d^d(„Z>d)d*d+œd_d,„Z?d`d-„Z@dad.„ZAdTd/„ZBd*d0œdbd1„ZCdRd2„ZDd3d4d5d6œZEe dejŒ                  d7fe$dejŒ                  d8fed
ejŽ                  d9fedejŒ                  d:fe"dejŽ                  d;fedejŒ                  d<fe7d"ejŒ                  d=fe9d#ejŽ                  d>fd?œZHdcd@„ZId*dAœdddB„ZJdedC„ZK ej˜                  eIdD«      ZM ej˜                  eIdE«      ZNddFdGdHdIœZOdJedK<   eOj¡                  «       D � �ci c]  \  } }|| “Œ
 c}} ZQdLedM<   dGZRdfdN„ZSdgdO„ZTd)dPœdhdQ„ZUyc c}} w )iz»SQLite journal-mode and PRAGMA policy for state.db (split from hermes_state).

Patchable helpers are looked up as module globals at call time, so tests patch ``hermes_state_wal.<name>``.
é    )ÚannotationsN)ÚAnyÚDictÚOptional)Úis_sqlite_wal_reset_vulnerableÚhermes_state)zlocking protocolznot authorizedúdisk i/o errori   zset[str]Ú_wal_fallback_warned_pathsÚ!_wal_delete_fallback_failed_pathsÚ_wal_probe_unknown_pathsÚ_wal_reset_bug_warned_pathsÚ_delete_overridden_warned_pathsÚsetÚ_journal_upgrade_warned_pathsuÉ   could not verify journal mode before applying configured journal_mode=delete (database is locked â€” possible concurrent openers); refusing to downgrade a database this process does not exclusively ownc                óh   — | r/| d   �*t        | d   «      j                  «       j                  «       S dS )zFLower-cased mode from a ``PRAGMA journal_mode`` row, ``""`` if no row.r   Ú )ÚstrÚstripÚlower)Úrows    ú5/home/ubuntu/.hermes/hermes-agent/hermes_state_wal.pyÚ_mode_from_rowr   8   s2   € á*-°#°a±&Ð2DŒ3ˆs�1‰v‹;×ÑÓ×$Ñ$Ó&ÐLÈ"ÐLó    c                ó  — t        d«      D ]{  }	 | j                  d«      j                  «       }|r|d   nd}t        |t        «      r	 |j                  d«      }|�)t        |«      j                  «       j                  «       c S dc S  t        j                  d«       y# t        j                  $ rB}dt        |«      j                  «       vrY d}~ y|}t        j                  d«       Y d}~Œåd}~ww xY w# t        $ r Y  yw xY w)	a"  Read the journal mode from the DB header; ``None`` if undeterminable (new DB, or PRAGMA failed) ->
    callers take their fail-closed "refuse to downgrade" branch. ``disk i/o error`` can be transient on
    virtualized block devices (XFS on cloud hosts), so it is retried a few times first.é   úPRAGMA journal_moder	   Nçš™™™™™©?r   Úasciiz:_on_disk_journal_mode: retries exhausted on disk read (%s))ÚrangeÚexecuteÚfetchoneÚsqlite3ÚOperationalErrorr   r   ÚtimeÚsleepÚ
isinstanceÚbytesÚdecodeÚUnicodeDecodeErrorr   ÚloggerÚdebug)ÚconnÚ_r   ÚexcÚlast_excÚmodes         r   Ú_on_disk_journal_moder1   =   sñ   € ô �1‹Xò Gˆð	Ø—,‘,Ð4Ó5×>Ñ>Ó@ˆCñ ˆs�1Šv $ˆÜ�dœEÔ"ðØ—{‘{ 7Ó+�ð -1Ð,<Œs�4‹y�‰Ó ×&Ñ&Ó(ÒFÀ$ÒFðGô  ‡L�LÐMÈxÔXØøô ×'Ñ'ò 	Ø¤s¨3£x§~¡~Ó'7Ñ7ÝØˆHÜ�J‰J�tÔÜûð	ûô &ò Úðús/   �B!Á	C9Â!C6Â4C1ÃC1Ã1C6Ã9	DÄDc                ó¢   — 	 | j                  dt        › �«       y# t        j                  $ r }t        j                  d|«       Y d}~yd}~ww xY w)a,  Bound the WAL so it returns space after big transactions. With the default (-1) a checkpointed WAL is
    reused in place, never truncated, so ``state.db-wal`` keeps the high-water mark of the largest
    transaction ever (a 3 GB optimize left a 3 GB WAL). Best-effort: failure only costs disk slack.zPRAGMA journal_size_limit=z"journal_size_limit not applied: %sN)r    Ú_WAL_SIZE_LIMIT_BYTESr"   r#   r*   r+   )r,   r.   s     r   Ú_apply_wal_size_limitr4   U   sH   € ð@Ø�‰Ð1Ô2GÐ1HÐIÕJøÜ×#Ñ#ò @Ü�‰Ð9¸3×?Ñ?ûð@ús   ‚ ›A®A	Á	Ac                ó¾   — t         j                  dk(  r>t        j                  t        j
                  «      5  | j                  |«       ddd«       yy# 1 sw Y   yxY w)zABest-effort PRAGMA on macOS only (no-op elsewhere, never raises).ÚdarwinN)ÚsysÚplatformÚ
contextlibÚsuppressr"   r#   r    )r,   Úpragmas     r   Ú_darwin_pragmar<   _   sM   € ä
‡|�|�xÒÜ× Ñ ¤×!9Ñ!9Ó:ñ 	!Ø�L‰L˜Ô ÷	!ð 	!ð  ÷	!ð 	!ús   ·AÁAc                ó   — t        | d«       y)u  Enable ``PRAGMA checkpoint_fullfsync`` on macOS. Apple's ``fsync(2)`` guarantees neither data-on-platter nor
    ordering, so without ``F_FULLFSYNC`` a launchd shutdown can turn a "durable" checkpoint into a malformed
    ``state.db``. Checkpoint boundaries only (~+0.1 ms/commit vs ~+4 ms for ``fullfsync=1``).

    During a launchd *system* shutdown/reboot the OS page cache is dropped (effectively a power-loss event
    for in-flight pages), so a WAL checkpoint whose ``fsync()`` "reported" durable may never have hit the
    platter â€” corrupting ``state.db`` with a malformed image. This is the trigger in issue #30636 ("SIGTERM
    during launchd shutdown under high load"), distinct from a plain in-session kill (which the page cache
    survives and SQLite recovers from).
    zPRAGMA checkpoint_fullfsync=1N©r<   ©r,   s    r   Ú_apply_macos_checkpoint_barrierr@   f   s   € ô �4Ð8Õ9r   c                ó   — t        | d«       y)zíEnforce ``PRAGMA synchronous=FULL`` on macOS: with NORMAL a WAL checkpoint
    racing process termination leaves half-written btree pages (``btreeInitPage
    error 11``). Called after every WAL activation so a prior NORMAL never sticks.zPRAGMA synchronous=FULLNr>   r?   s    r   Ú_enforce_macos_synchronous_fullrB   t   s   € ô �4Ð2Õ3r   c                óF   — t        | «       t        | «       t        | «       y)zGThe settings every WAL activation carries: size limit + macOS barriers.N)r4   r@   rB   r?   s    r   Ú_apply_wal_companionsrD   {   s   € ä˜$ÔÜ# DÔ)Ü# DÕ)r   c                óD   — t        | €t        j                  «      S | «      S )uµ   True when the linked SQLite has the WAL-reset bug (3.7.0â€“3.51.2; fixed 3.51.3+, backports 3.50.7 /
    3.44.6). Pre-WAL libraries are safe. https://sqlite.org/wal.html#walresetbug)Ú_is_sqlite_wal_reset_vulnerabler"   Úsqlite_version_info)Úversion_infos    r   r   r   ‚   s#   € ô +È,ÐJ^¬7×+FÑ+FÓqÐqÐdpÓqÐqr   c                 ó  — 	 t        j                  t        j                  d«      «      5 } | j	                  d«      j                  «       }ddd«       r|d   �t        |d   «      S dS # 1 sw Y   Œ xY w# t        j                  $ r Y yw xY w)zCReturn ``sqlite_source_id()``, or an empty string when unavailable.z:memory:zSELECT sqlite_source_id()Nr   r   )r9   Úclosingr"   Úconnectr    r!   ÚErrorr   ©r,   r   s     r   Úsqlite_source_idrN   ˆ   s†   € ðÜ×Ñ¤§¡°
Ó ;Ó<ð 	GÀØ—,‘,Ð:Ó;×DÑDÓFˆC÷	Gñ  # a¡&Ð"4Œ3ˆs�1‰v‹;Ð<¸"Ð<÷		Gð 	Gûä�=‰=ò Ùðús(   ‚(A5 ª A)Á
A5 Á)A2Á.A5 Á5BÂ
Bc                óØ   — 	 | j                  d«      j                  «       }t        |«      xr |d   duxr t        |d   «      dkD  S # t        j
                  t        t        f$ r Y yw xY w)z×Whether the file already holds pages (existing vs brand-new DB); lock-free header read. Fail-quiet False: the
    only caller gates a warning on this and an unknown-answer warning would fire on every fresh database.zPRAGMA page_countr   NF)r    r!   ÚboolÚintr"   rL   Ú	TypeErrorÚ
ValueErrorrM   s     r   Ú_database_has_contentrT   ’   se   € ðØ�l‰lÐ.Ó/×8Ñ8Ó:ˆÜ�C‹yÒC˜S ™V¨4Ð/ÒC´C¸¸A¹³KÀ!±OÐCøÜ�M‰Mœ9¤jÐ1ò Ùðús   ‚AA ÁA)Á(A)c                 ó  — 	 ddl m}   | «       xs i j                  di «      }t        |t        «      r|j                  dd«      nd}t        |t        «      r|j                  «       j                  «       nd}|dv r|S dS # t
        $ r Y yw xY w)z¶The configured ``database.journal_mode`` (``wal`` default; ``delete`` for filesystems without WAL-safe
    durability: macOS virtiofs, NFS, SMB). Invalid values fail safe to ``wal``.r   )Úload_config_readonlyÚdatabaseÚjournal_modeÚwalr   )rY   Údelete)	Úhermes_cli.configrV   Úgetr&   ÚdictÚ	Exceptionr   r   r   )rV   rW   Úrawr0   s       r   Úresolve_journal_moder`   œ   sˆ   € ðÝ:Ù(Ó*Ò0¨b×5Ñ5°jÀ"ÓEˆÜ5?ÀÌ$Ô5Oˆh�l‰l˜>¨5Ô1ÐUZˆô #-¨S´#Ô"6ˆ3�9‰9‹;×ÑÔ¸B€DØÐ,Ñ,ˆ4Ð7°%Ð7øô ò Ùðús   ‚AA? Á?	BÂ
Bc                  ó   — e Zd ZdZy)ÚWalUnsupportedErrora@  Raised by :func:`apply_wal_with_fallback` under ``require_wal=True`` when
    the filesystem cannot provide WAL (SQLITE_PROTOCOL raised, or macOS-NFS silent
    refusal) or the on-disk mode cannot be verified (probe blocked by a concurrent
    opener). Subclasses ``OperationalError`` so DB-init handlers still catch it.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© r   r   rb   rb   ©   s   „ òTr   rb   >   ú9p2000.lú9p2000.uúfuse.virtiofsÚ9pÚ9p2000ÚvirtiofszDict[str, bool]Ú_cross_vm_fs_cacheÚ_cross_vm_warned_pathsÚ#_cross_vm_existing_wal_warned_pathsc                óJ  — 	 t        |ddd¬«      5 }|j                  «       j                  «       }ddd«       d\  }}D ]¸  }|j	                  d«      \  }}}	|j                  «       }
t        |
«      d	k  s|	sŒ9|
d
   }d|v r!|j                  dd«      j                  d«      }| |k(  s$| j                  |j                  d«      dz   «      sŒŒt        |«      |kD  sŒ›t        |«      |	j                  «       d   }}Œº |j                  «       S # 1 sw Y   ŒÛxY w# t        $ r Y yw xY w)zffstype of the longest mount point that is a prefix of ``directory`` (``""`` if unreadable / no match).Úrzutf-8Úreplace)ÚencodingÚerrorsNr   )éÿÿÿÿr   z - é   r   ú\zlatin-1ÚignoreÚunicode_escapeú/r   )ÚopenÚreadÚ
splitlinesÚOSErrorÚ	partitionÚsplitÚlenÚencoder(   Ú
startswithÚrstripr   )Ú	directoryÚmountinfo_pathÚfhÚlinesÚbest_lenÚbest_fstypeÚlineÚfieldsr-   ÚtailÚpartsÚmount_points               r   Ú_mountinfo_fstyper‘   À   s(  € ðÜ�. #°À	ÔJð 	+ÈbØ—G‘G“I×(Ñ(Ó*ˆE÷	+ð #Ñ€HˆkØò JˆàŸ.™.¨Ó/‰ˆ��4Ø—‘“ˆÜˆu‹:˜Š>¡ØØ˜A‘hˆØ�;ÑØ%×,Ñ,¨Y¸ÓA×HÑHÐIYÓZˆKØ˜Ò# y×';Ñ';¸K×<NÑ<NÈsÓ<SÐVYÑ<YÕ'ZÜ�;Ó (Ó*Ü(+¨KÓ(8¸$¿*¹*»,Àq¹/˜+‘ðJð ×ÑÓÐ÷#	+ð 	+ûäò Ùðús'   ‚D ‘D
°D Ä
DÄD Ä	D"Ä!D"c                óN   — t         j                  dk7  ryt        | |«      t        v S )zPTrue only when ``directory`` sits on a virtiofs/9p mount per ``mountinfo_path``.ÚlinuxF)r7   r8   r‘   Ú_CROSS_VM_FSTYPES)r†   r‡   s     r   Ú_detect_cross_vm_fsr•   ×   s$   € ä
‡|�|�wÒØÜ˜Y¨Ó7Ô;LÐLÐLr   c                óz  — 	 t         j                  j                  t         j                  j                  | «      «      xs d}t        5  t        j                  |«      }ddd«       €%t        |«      }t        5  |t        |<   ddd«       |S |S # t        t
        f$ r Y yw xY w# 1 sw Y   ŒGxY w# 1 sw Y   |S xY w)zaTrue when ``path`` resides on a virtiofs/9p (cross-VM) filesystem; cached per resolved directory.r{   FN)
ÚosÚpathÚdirnameÚrealpathr   rS   Ú_cross_vm_fs_cache_lockrn   r\   r•   )r˜   r†   Úcacheds      r   Ú_path_on_cross_vm_fsr�   Þ   s«   € ðÜ—G‘G—O‘O¤B§G¡G×$4Ñ$4°TÓ$:Ó;ÒB¸sˆ	ô 
!ñ 3Ü#×'Ñ'¨	Ó2ˆ÷3à€~Ü$ YÓ/ˆÜ$ñ 	3Ø,2Ô˜yÑ)÷	3à€Mˆ6€Møô ”ZÐ ò Ùðú÷3ð 3ú÷	3à€Mús*   ‚A B ÁB$Á9
B0ÂB!Â B!Â$B-Â0B:c                ó¸   — 	 | j                  d«      j                  «       }|r|d   rt        |d   «      S dS # t        j                  t
        t        f$ r Y yw xY w)zIFilesystem path of the ``main`` database, ``""`` for in-memory / unknown.zPRAGMA database_listé   r   )r    r!   r   r"   r#   Ú
IndexErrorrR   rM   s     r   Ú_connection_db_filer¡   í   sY   € ðØ�l‰lÐ1Ó2×;Ñ;Ó=ˆÙ! c¨!¢fŒs�3�q‘6‹{Ð4°"Ð4øÜ×$Ñ$¤j´)Ð<ò Ùðús   ‚38 ¶8 ¸AÁAc                óJ   — | dk7  rt        j                  d| xs d› d�«      ‚| S )zIRaise unless SQLite reported ``delete`` for an explicit operator request.rZ   z2could not set configured journal_mode=delete (got z	no resultú))r"   r#   )Úactuals    r   Ú_verify_configured_deleter¥   ö   s3   € à�ÒÜ×&Ñ&Ð)[Ð\bÒ\qÐfqÐ[rÐrsÐ'tÓuÐuØ€Mr   zstate.dbF)Údb_labelÚrequire_walc               óú  — t        «       }t        «       rt        | ||dk(  ¬«      S t        | «      }|dk(  r'|dk(  rt	        |«       t        | «       t        | «       y|dk(  r0|€t        j                  t        «      ‚t        t        | d«      «      S |€|rt        d«      ‚t        d|«       yt        | «      }|r4t        |«      r)|rt        d«      ‚t        d|«       t        | d«      xs dS t!        | |||«      S )	uD  Set ``journal_mode=WAL`` on ``conn``, falling back to DELETE on failure.

    Returns the mode actually set â€” or, when the read-only mode probe is blocked by a concurrent opener, ``"wal"``
    as the assumed mode with nothing touched (``require_wal=True`` raises instead). Shared by :class:`SessionDB`
    and ``hermes_cli.kanban_db_connect.connect``. WAL-incompatible filesystems either raise ``OperationalError`` ("locking protocol" / "disk I/O error") or â€”
    macOS NFS / SMB / AgentFS â€” silently refuse and stay in DELETE; either way log ERROR once per process per
    ``db_label`` and fall back. ``require_wal=True`` raises :class:`WalUnsupportedError` instead. WAL-reset-bug
    builds (https://sqlite.org/wal.html#walresetbug) never enable WAL on non-WAL files; an already-WAL DB keeps WAL
    with a warning. Gate deliberately RETAINED: re-measured on the bundled 3.50.4 there is no evidence WAL is safer.

    Invariant on every path: never downgrade to DELETE if the on-disk header reports WAL or cannot be read â€” other
    gateway/cron/worker connections may hold the DB open, and a live downgrade destroys their uncheckpointed
    commits.

    An earlier revision of the lock-cancellation fix (#71724) reverted it on the theory that DELETE was "the
    mode that corrupts", but that comparison was confounded: the clean WAL result came from SQLite 3.53.1,
    which carries BOTH the WAL-reset fix AND 3.51.0's defenses against close()-broken POSIX locks, so it
    says nothing about 3.50.4. Re-measured on the actually-bundled 3.50.4 with the lock fix in place, WAL
    and DELETE are both clean (0/3 each) â€” i.e. there is no evidence that WAL is safer here, and upstream
    still documents the WAL-reset bug as real through 3.51.2 with serious consequences. Until a fixed
    runtime is delivered, keep new databases out of WAL.
    rZ   )r¦   Úrequire_deleterY   ÚDELETEut   could not verify the on-disk journal mode (database is locked â€” possible concurrent openers); cannot guarantee WALÚwal_probe_unknownzvjournal_mode=WAL refused: database is on a cross-VM filesystem (virtiofs/9p) where WAL shared-memory silently corruptsÚcross_vm_fs)r`   r   Ú_apply_delete_for_wal_reset_bugr1   Ú&_log_configured_delete_overridden_onceÚ!_warn_existing_wal_on_cross_vm_fsrD   r"   r#   Ú_CANNOT_VERIFY_DELETE_MSGr¥   Ú_set_journal_mode_no_waitrb   Ú	_log_oncer¡   r�   Ú_enable_wal)r,   r¦   r§   Ú
configuredÚcurrent_modeÚdb_files         r   Úapply_wal_with_fallbackr·   ý   s  € ô. &Ó'€Jô &Ô'Ü.¨t¸hÐWaÐemÑWmÔnÐnä(¨Ó.€LØ�uÒØ˜Ò!ä2°8Ô<Ü)¨$Ô/Ü˜dÔ#Øà�XÒð Ðä×*Ñ*Ô+DÓEÐEÜ(Ô)BÀ4ÈÓ)RÓSÐSØÐñ Ü%ð 'Ró Sð SäÐ% xÔ0Øô " $Ó'€GÙÔ'¨Ô0ÙÜ%ð 'Ró Sð Sä�- Ô*Ü(¨¨xÓ8ÒD¸HÐDÜ�t˜X {°LÓAÐAr   c                ó~  ‡ ‡‡‡‡— ‰duxr ‰dk7  xr t        ‰ «      Šdˆ ˆˆˆfd„}	 t        ‰ j                  d«      j                  «       «      }|dk(  r |«       S t	        d|›d�«      }|r|‚t        ‰|«       |xs dS # t        $ r ‚ t        j                  $ �rž}t        |«      j                  «       Št        ˆfd„t        D «       «      s‚ d	‰v r¡t        d
«      D ]“  }t        j                  d«       	 ‰ j                  d«      j                  «       }	n># t        j                  $ r(}
d	t        |
«      j                  «       vr‚ |
}Y d}
~
Œod}
~
ww xY wt        |	«      dk(  r |«       c cY d}~S  n t        ‰ «      dv r‚ |rt	        t        |«      «      |‚t        ‰|«       	 t!        ‰ d«       n~# t        j                  $ rh}t#        d‰||«       	 t        ‰ j                  d«      j                  «       «      }n# t        j                  $ r d}Y nw xY w|xs dcY d}~cY d}~S d}~ww xY wY d}~yd}~ww xY w)zIFlip a non-WAL, non-vulnerable connection to WAL, or fall back to DELETE.NrY   c                 ó8   •— ‰rt        ‰‰«       t        ‰ «       y)NrY   )Ú_log_journal_mode_upgrade_oncerD   )r,   rµ   r¦   Úupgrading_existing_dbs   €€€€r   Ú_wal_activatedz#_enable_wal.<locals>._wal_activatedF  s   ø€ Ù Ü*¨8°\ÔBÜ˜dÔ#Ør   zPRAGMA journal_mode=WALz0journal_mode=WAL refused without raising (still r£   rZ   c              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­w©Nrg   )Ú.0ÚmarkerÚmsgs     €r   ú	<genexpr>z_enable_wal.<locals>.<genexpr>[  s   øè ø€ ÒE V�6˜S”=ÑEùs   ƒr	   rŸ   r   )rY   Nrª   Úwal_delete_fallback_failedr   r   ©Úreturnr   )rT   r   r    r!   rb   Ú_log_wal_fallback_oncer"   r#   r   r   ÚanyÚ_WAL_INCOMPAT_MARKERSr   r$   r%   r1   r±   r²   )r,   r¦   r§   rµ   r¼   r0   Ú
silent_excr.   r-   r   Ú	retry_excÚ
delete_excÚ	read_backrÁ   r»   s   `` `         @@r   r³   r³   @  s+  ü€ ð )°Ð4Òn¸ÈÑ9NÒnÔShÐimÓSnÐ÷ð ð=ô ˜dŸl™lÐ+DÓE×NÑNÓPÓQˆØ�5Š=Ù!Ó#Ð#Ü(Ð+[Ð\`Ð[cÐcdÐ)eÓfˆ
ÙØÐÜ˜x¨Ô4ØÒ�xÐøÜò ØÜ×#Ñ#ó 0Ü�#‹h�n‰nÓˆÜÓEÔ/DÔEÔEØð ˜sÑ"ô ˜1“Xò �Ü—
‘
˜4Ô ðØŸ,™,Ð'@ÓA×JÑJÓL‘CøÜ×/Ñ/ò Ø'¬s°9«~×/CÑ/CÓ/EÑEØØ#�CÜûð	úô
 " #Ó&¨%Ò/Ù)Ó+×+Ùðô ! Ó&¨-Ñ7ØÙÜ%¤c¨#£hÓ/°SÐ8Ü˜x¨Ô-ð	)Ü% d¨HÕ5øÜ×'Ñ'ò 	)ô Ð2°H¸cÀ:ÔNðÜ*¨4¯<©<Ð8MÓ+N×+WÑ+WÓ+YÓZ‘	øÜ×+Ñ+ò Ø’	ðúàÒ( ×(Ò(ûð	)úô ûða0ús®   ¨3B Á$B ÂH<ÂAH7Ã8DÄH7ÄEÄ+EÅ	H7ÅEÅH7Å,H<Å24H7Æ'F4Æ3H7Æ4H/ÇH*Ç(G?Ç>H*Ç?HÈH*ÈHÈH*ÈH/È H7È$H<È*H/È/H7È7H<c                óp  — 	 | j                  d«      j                  «       }|r|d   �t        |d   «      nd}| j                  d«       	 t        | j                  d|› �«      j                  «       «      t        j                  t        j                  «      5  | j                  d|› �«       ddd«       S # t        j                  t
        t        f$ r d}Y ŒŸw xY w# 1 sw Y   S xY w# t        j                  t        j                  «      5  | j                  d|› �«       ddd«       w # 1 sw Y   w xY wxY w)a‚  Execute ``PRAGMA journal_mode=<mode>`` without waiting on other openers.

    The ONLY place a non-WAL journal-mode switch may be issued. ``busy_timeout=0`` turns SQLite's exclusivity
    requirement into a concurrent-opener detector: leaving WAL needs exclusive access, so if ANY other connection
    holds the DB the pragma fails immediately with ``database is locked`` instead of sneaking the flip between a
    writer's transactions (how uncheckpointed WAL commits die). Callers must treat a raised ``OperationalError`` as
    "not exclusively owned: leave the mode alone", never as retryable. Returns the reported mode, ``""`` if no row.zPRAGMA busy_timeoutr   NzPRAGMA busy_timeout=0zPRAGMA journal_mode=zPRAGMA busy_timeout=)
r    r!   rQ   r"   r#   rR   rS   r   r9   r:   )r,   r0   r   Úprevious_timeouts       r   r±   r±   Œ  s   € ðØ�l‰lÐ0Ó1×:Ñ:Ó<ˆÙ*-°#°a±&Ð2Dœ3˜s 1™vœ;È!Ðð 	‡L�LÐ(Ô)ðDÜ˜dŸl™lÐ-AÀ$ÀÐ+HÓI×RÑRÓTÓUä× Ñ ¤×!9Ñ!9Ó:ñ 	DØ�L‰LÐ/Ð0@Ð/AÐBÔC÷	Dð 	Døô ×$Ñ$¤i´Ð<ò ØÒðú÷	Dð 	DûŒZ× Ñ ¤×!9Ñ!9Ó:ñ 	DØ�L‰LÐ/Ð0@Ð/AÐBÔC÷	D÷ 	Dñ 	DýsA   ‚6B5 Á
*C' ÂCÂ5 CÃCÃC$	Ã'$D5ÄD)Ä 	D5Ä)D2Ä.D5c                óP   — t        | «      }|rt        |«      rt        d|«       yyy)aG  #110848: the fresh-DB cross-VM refusal never sees a DB that was already WAL before the check existed (or was
    created on a native volume and then moved). Keeping WAL is right (never live-downgrade); staying silent is not.
    Both the vulnerable-SQLite and the regular already-WAL paths return early, so both must call this.Úcross_vm_fs_existing_walN)r¡   r�   r²   )r,   r¶   s     r   r¯   r¯   ¡  s+   € ô " $Ó'€GÙÔ'¨Ô0äÐ,¨gÕ6ð 1€wr   )r©   c               ó  — t        | «      }|dk(  r1t        |d¬«       |rt        |«       t        | «       t	        | «       y|€*|rt        j                  t        «      ‚t        |dd¬«       yd}	 t        | d«      }|rt        |«       t        |d¬«       y
# t
        j                  $ r\}|r‚ dt        |«      j                  «       v sd	t        |«      j                  «       v rt        |dd¬«       |xs d
cY d}~S Y d}~Œ…d}~ww xY w)u  Avoid enabling WAL when the linked SQLite has the WAL-reset bug.

    Already-WAL on disk: keep WAL (no live downgrade) and warn. Mode unreadable (probe blocked by a concurrent
    opener): not provably exclusive â€” leave it and warn; treating "could not read" as "not WAL" once flipped a live
    WAL state.db to DELETE under a writer, destroying its uncheckpointed commits. Otherwise set DELETE without
    waiting out openers and warn; an explicit operator request additionally verifies SQLite accepted DELETE.rY   T)Úkept_walN)rÒ   Úindeterminater   rª   ÚlockedÚbusyrZ   F)r1   Ú_log_wal_reset_bug_oncer®   r¯   rD   r"   r#   r°   r±   r   r   r¥   )r,   r¦   r©   Úcurrentr¤   r.   s         r   r­   r­   «  s÷   € ô $ DÓ)€GØ�%ÒÜ °4Õ8Ùä2°8Ô<Ü)¨$Ô/Ü˜dÔ#ØØ€ÙÜ×*Ñ*Ô+DÓEÐEÜ °4ÀtÕLØØ€Fð'Ü*¨4°Ó:ˆñ Ü! &Ô)Ü˜H¨uÕ5Øøô ×#Ñ#ò 'ÙØØ”s˜3“x—~‘~Ó'Ñ'¨6´S¸³X·^±^Ó5EÑ+Eä# H°tÈ4ÕPØÒ&˜hÕ&ô ,Fûð'ús   Á1B ÂDÂ+ADÃ7DÄDc                 óŽ   — 	 ddl m} m}m}  |  |«       «      } ||«      }|dv rd|› d�S |dk(  rd|› d�S |S # t        $ r Y yw xY w)	zhRepair hint matching what ``hermes update`` can actually do for this install type.

    See #75153.
    r   )Údetect_install_methodÚget_project_rootÚ%recommended_update_command_for_method>   ÚgitÚunknownz>Hermes-managed installs can repair the embedded runtime with `ú`Údockerz!update the container image with `zdinstall a Python build bundled with SQLite 3.51.3+ (or backports 3.50.7 / 3.44.6) and restart Hermes)r[   rÙ   rÚ   rÛ   r^   )rÙ   rÚ   rÛ   ÚmethodÚcmds        r   Ú_wal_reset_repair_hintrâ   Ñ  sr   € ð
vßtÑtÙ&Ñ'7Ó'9Ó:ˆÙ3°FÓ;ˆØÐ'Ñ'ØSÐTWÐSXÐXYÐZÐZØ=CÀxÒ=OÐ2°3°%°qÐ9ÐXÐUXÐXøÜò vÙuðvús   ‚(8 «
8 ¶8 ¸	AÁAuÀ   journal mode could not be verified or exclusively switched (database is locked â€” possible concurrent openers); leaving the journal mode untouched (no live downgrade under concurrent openers)u\   is already in WAL mode â€” leaving WAL in place (no live downgrade under concurrent openers)z1using journal_mode=DELETE instead of enabling WAL)rÓ   rÒ   rZ   u
  %s: linked SQLite %s (interpreter %s) is vulnerable to the WAL-reset corruption bug (https://sqlite.org/wal.html#walresetbug) â€” %s. Upgrade to SQLite 3.51.3+ (or backports 3.50.7 / 3.44.6); %s. See `hermes doctor`. This warning fires once per process per database.aÔ  %s: on-disk journal_mode was %s and has been switched to WAL. This rewrites the database header and persists after this process exits. If %s was a deliberate choice (for example the mitigation for the SQLite WAL-reset bug, or a WAL-unsafe filesystem), setting it with PRAGMA on the file will not survive -- every open re-applies the configured mode. Set `database.journal_mode: delete` in config.yaml to make it stick. This message fires once per process per database.u  %s: WAL journal_mode unsupported on this filesystem (%s) â€” falling back to journal_mode=DELETE (slower rollback-journal mode; reduces concurrency but works on NFS/SMB/FUSE/ZFS). See https://www.sqlite.org/wal.html for details. This message fires once per process per database.u  %s: both WAL and DELETE journal_mode failed (WAL: %s; DELETE: %s) â€” continuing with the connection's current journal mode (reads/writes still work). Typically seen on APFS external SSDs under heavy contention. This message fires once per process per database.a„  %s: database.journal_mode=delete is configured but the on-disk database is already WAL; keeping WAL (a live downgrade under open connections can corrupt the DB). To apply journal_mode=DELETE, stop every Hermes process using this database and run `hermes sessions set-journal-mode delete` (add `--db PATH` for a store other than state.db). This message fires once per process per database.aY  %s: could not verify the on-disk journal mode (database is locked / busy); not issuing a journal-mode set-pragma while another connection may hold the file (it could unlink the -wal/-shm sidecars that connection still uses). Leaving the file untouched; this connection inherits the on-disk mode. This message fires once per process per database.uÄ  %s: database directory is on a cross-VM filesystem (virtiofs/9p â€” typical for Docker Desktop / OrbStack / Podman host bind mounts). SQLite WAL shared-memory is not coherent across the VM boundary and can silently corrupt the database, so journal_mode=DELETE is used instead. To restore WAL concurrency, move the database onto a native volume (e.g. a named Docker volume) instead of a host bind mount. This message fires once per process per database.uo  %s: existing WAL-mode database is on a cross-VM filesystem (virtiofs/9p â€” typical for Docker Desktop / OrbStack / Podman host bind mounts). SQLite WAL shared-memory is not coherent across the VM boundary and concurrent writers can silently corrupt the database. Hermes does not live-downgrade an on-disk WAL database. Fix one of two ways: stop every Hermes process using this database and run `hermes sessions set-journal-mode delete` (set `database.journal_mode: delete` in config.yaml to keep it), or move the database onto a native volume (e.g. a named Docker volume). This message fires once per process per database.)Úwal_reset_bugÚjournal_upgradeÚwal_fallbackrÃ   Údelete_overriddenr«   r¬   rÐ   c                óä   — t         |    \  }}}}t        «       |   }|5  ||v r
	 ddd«       y|j                  |«       ddd«       t        j                  |||gd„ |D «       ¢­Ž  y# 1 sw Y   Œ)xY w)z˜Emit ``_ONCE_LOGS[kind]`` once per (process, db_label). Callable *args* are
    resolved only after the dedupe check, so install-method probes run once.Nc              3  óD   K  — | ]  }t        |«      r |«       n|–— Œ y ­wr¾   )Úcallable)r¿   Úas     r   rÂ   z_log_once.<locals>.<genexpr>/  s   è ø€ Ò*SÈ´(¸1´+©1¬3À1Ó+DÑ*Sùs   ‚ )Ú
_ONCE_LOGSÚglobalsÚaddr*   Úlog)Úkindr¦   ÚargsÚlockÚset_nameÚlevelÚmessageÚseens           r   r²   r²   &  s{   € ô &0°Ñ%5Ñ"€Dˆ(�E˜7Ü‹9�XÑ€DØ	ñ Ø�tÑØ÷ð ð 	�‰�Ô÷ô ‡J�Jˆu�g˜xÐTÑ*SÈdÔ*SÔT÷	ð ús   žA&­A&Á&A/)rÓ   c               ó†   — t         |rdn|rdnd   }t        d| t        j                  t        j
                  |t        «       y)zHLog once per (process, db_label) about the WAL-reset vulnerability path.rÓ   rÒ   rZ   rã   N)Ú_WAL_RESET_BUG_ACTIONSr²   r"   Úsqlite_versionr7   Ú
executablerâ   )r¦   rÒ   rÓ   Úactions       r   rÖ   rÖ   2  s4   € ä#±}¡OÑX`É*ÐfnÑo€FÜˆo˜x¬×)?Ñ)?ÄÇÁÐQWÔYoÕpr   c                ó    — t        d| ||«       y)zCSingle WARNING per (process, db_label) about a non-WAL -> WAL flip.rä   N)r²   )r¦   Úprevious_modes     r   rº   rº   8  s   € äÐ ¨=¸-ÕHr   rå   ræ   é   rŸ   é   )ÚOFFÚNORMALÚFULLÚEXTRAzDict[str, int]Ú_SYNCHRONOUS_LEVELSzDict[int, str]Ú_SYNCHRONOUS_NAMESc                óš  — t        | t        «      r| du rdS dS t        | t        «      r| t        v r| S dS t	        | «      j                  «       }|j                  «       t        v rt        |j                  «          S t        j                  t        t        «      5  t        |«      t        v rt        |«      ndcddd«       S # 1 sw Y   yxY w)zéMap ``database.synchronous`` (``OFF``/``NORMAL``/``FULL``/``EXTRA`` any
    case, or ``0``-``3``) to its PRAGMA integer; None for anything else so the
    caller warns and leaves the level untouched (guessing at durability is worse).Fr   N)r&   rP   rQ   r  r   r   Úupperr  r9   r:   rR   rS   )Ú	raw_valueÚtexts     r   Úresolve_synchronous_levelr	  H  s°   € ô �)œTÔ"à Ñ&ˆqÐ0¨DÐ0Ü�)œSÔ!Ø%Ô);Ñ;ˆyÐEÀÐEÜˆy‹>×ÑÓ!€DØ‡z�zƒ|Ô*Ñ*Ü" 4§:¡:£<Ñ0Ð0Ü	×	Ñ	œY¬
Ó	3ñ FÜ ›IÔ);Ñ;Œs�4ŒyÀ÷F÷ Fñ Fàús   ÂCÃC
c               ó\  — t        |«      x}€t        j                  d||«       yt        j                  dk(  r(|t
        k  rt        j                  d|t        |   «       yt        j                  t        j                  «      5  | j                  d|› �«       ddd«       y# 1 sw Y   yxY w)uñ  Set ``PRAGMA synchronous`` from config, never below FULL on macOS.

    Kept out of the integer loop in :func:`apply_database_pragmas`: this PRAGMA decides whether a commit is on
    the platter, so an unrecognised value must not fall through to "SQLite default" the way a bad
    ``cache_size`` can. Darwin floor: this runs after :func:`_enforce_macos_synchronous_full`, so a configured
    ``NORMAL`` would silently undo the btree protection â€” raising is allowed, lowering is refused out loud.Nz]%s: ignoring unrecognized database.synchronous=%r (expected OFF, NORMAL, FULL, EXTRA, or 0-3)r6   z¿%s: refusing database.synchronous=%s on macOS; keeping FULL. Darwin's fsync() does not guarantee write ordering, so a lower level readmits the half-written btree pages FULL exists to prevent.zPRAGMA synchronous=)r	  r*   Úwarningr7   r8   Ú_SYNCHRONOUS_FULLr  r9   r:   r"   r#   r    )r,   r  r¦   ró   s       r   Ú_apply_synchronous_pragmar  Y  sž   € ô +¨9Ó5Ð5ˆÐ>Ü�‰ÐvØ ô	,àÜ
‡|�|�xÒ EÔ,=Ò$=Ü�‰ð %à&.Ô0BÀ5Ñ0Iô	Kð 	Ü	×	Ñ	œW×5Ñ5Ó	6ñ 4Ø�‰Ð*¨5¨'Ð2Ô3÷4÷ 4ñ 4ús   ÂB"Â"B+©r¦   c               óæ  — 	 ddl m}m}  |«       }dD ]w  } ||d|d¬«      x}€Œ	 t	        t        |«      j                  «       «      }t        j                  t        j                  «      5  | j                  d|› d	|› �«       ddd«       Œy  ||dd
d¬«      x}�t!        | ||¬«       yy# t        $ r Y yw xY w# t        t        f$ r t        j                  d|||«       Y ŒÐw xY w# 1 sw Y   ŒÝxY w)ah  Apply optional performance and WAL-sizing PRAGMAs from ``config.yaml``.

    Journal mode is NOT handled here (owned by :func:`apply_wal_with_fallback`). ``database:`` keys: ``cache_size``
    (negative = KiB, positive = pages), ``mmap_size`` (bytes, 0 = off), ``temp_store`` (0-3), ``wal_autocheckpoint``
    (pages), ``journal_size_limit`` (bytes), ``synchronous`` (unset leaves the compile-time default, which differs
    between bundled/distro/Homebrew builds). Best-effort: failures are ignored so DB init never breaks on a
    malformed section. Applied to ALL connection types: writer, read_only, WAL readers.r   )Úcfg_getrV   N)Ú
cache_sizeÚ	mmap_sizeÚ
temp_storeÚwal_autocheckpointÚjournal_size_limitrW   )Údefaultz'%s: ignoring non-integer database.%s=%rzPRAGMA Ú=Úsynchronousr  )r[   r  rV   r^   rQ   r   r   rR   rS   r*   r  r9   r:   r"   r#   r    r  )	r,   r¦   r  rV   ÚcfgÚpragma_namer  ÚvalueÚraw_synchronouss	            r   Úapply_database_pragmasr  m  s  € ðßCÙ"Ó$ˆð mò 	:ˆÙ   j°+ÀtÔLÐLˆIÐUØð	Üœ˜I›×,Ñ,Ó.Ó/ˆEô × Ñ ¤×!9Ñ!9Ó:ñ 	:Ø�L‰L˜7 ; -¨q°°Ð8Ô9÷	:ð 	:ð	:ñ # 3¨
°MÈ4ÔPÐPˆÐ]Ü! $¨À(ÖKð ^øô ò Ùðûô œ:Ð&ò 	Ü�N‰NÐDÀhÐP[Ð]fÔgÙð	ú÷	:ð 	:ús.   ‚B+ ¦"B:Á+C'Â+	B7Â6B7Â:'C$Ã#C$Ã'C0	rÄ   )r,   úsqlite3.ConnectionrÅ   úOptional[str])r,   r  rÅ   ÚNone)r,   r  r;   r   rÅ   r   r¾   )rH   zOptional[tuple]rÅ   rP   )r,   r  rÅ   rP   )z/proc/self/mountinfo)r†   r   r‡   r   rÅ   r   )r†   r   r‡   r   rÅ   rP   )r˜   r   rÅ   rP   )r,   r  rÅ   r   )r¤   r   rÅ   r   )r,   r  r¦   r   r§   rP   rÅ   r   )
r,   r  r¦   r   r§   rP   rµ   r  rÅ   r   )r,   r  r0   r   rÅ   r   )r,   r  r¦   r   r©   rP   rÅ   r   )rï   r   r¦   r   rð   r   rÅ   r   )r¦   r   rÒ   rP   rÓ   rP   rÅ   r   )r¦   r   rü   r   rÅ   r   )r  r   rÅ   zOptional[int])r,   r  r  r   r¦   r   rÅ   r   )r,   r  r¦   r   rÅ   r   )Vrf   Ú
__future__r   r9   Ú	functoolsÚloggingr—   r"   r7   Ú	threadingr$   Útypingr   r   r   Úhermes_cli.sqlite_runtimer   rF   Ú	getLoggerr*   rÈ   r3   r   r
   Ú__annotations__ÚLockÚ_wal_fallback_warned_lockr   Ú _wal_delete_fallback_failed_lockr   Ú_wal_probe_unknown_lockr   Ú_wal_reset_bug_warned_lockr   Ú_delete_overridden_warned_lockr   Ú_journal_upgrade_warned_lockr°   r   r1   r4   r<   r@   rB   rD   rN   rT   r`   r#   rb   Ú	frozensetr”   rn   r›   ro   Ú_cross_vm_warned_lockrp   Ú"_cross_vm_existing_wal_warned_lockr‘   r•   r�   r¡   r¥   r·   r³   r±   r¯   r­   râ   r÷   ÚWARNINGÚERRORrë   r²   rÖ   rº   ÚpartialrÆ   r®   r  Úitemsr  r  r	  r  r  )ÚkÚvs   00r   ú<module>r9     sª  ðòõ
 #ã Û Û Û 	Û Û 
Û Û ß &Ñ &å gð 
ˆ×	Ñ	˜>Ó	*€ð QÐ à(Ð ñ (+£uÐ ˜HÓ ,Ø*˜IŸN™NÓ,Ð ñ /2«eÐ ! 8Ó 3Ø#1 9§>¡>Ó#3Ð  ñ &)£UÐ ˜(Ó *Ø(˜)Ÿ.™.Ó*Ð Ù(+«Ð ˜XÓ -Ø+˜YŸ^™^Ó-Ð Ù,/«EÐ  Ó 1Ø!/ §¡Ó!1Ð Ù%(£UÐ ˜sÓ *Ø-˜yŸ~™~Ó/Ð ð8Ð ó
Mó
ó0@ó!ó:ó4ó*ôró=óó
8ôT˜'×2Ñ2ô Tñ ÒcÓdÐ Ø&(Ð �OÓ (Ø(˜)Ÿ.™.Ó*Ð Ù#&£5Ð ˜Ó (Ø&˜	Ÿ™Ó(Ð Ù03³Ð # XÓ 5Ø%3 Y§^¡^Ó%5Ð "ôô.Móóóð JTÐinõ @BóFIóXDó*7ð hmõ #óLvð&!ð oØAñÐ ð 1Ð2OÐQX×Q`ÑQ`ð	UðVð
 5Ð6UÐW^×WfÑWfð	<ð	=ð /Ð0LÈgÏmÉmð	iðjð
 $DÐEhÐjq×jyÑjyð
	Hð#Ið 9Ð:[Ð]d×]jÑ]jð	XðYð 2Ð3MÈwÏÉð	[ð\ð *Ð+CÀWÇ_Á_ð	 ð!ð "DÐEjÐls×lyÑlyð	ð	!ñ_9€
óx	Uð UZõ qóIð +˜×*Ñ*¨9°nÓEÐ Ø):¨×):Ñ):¸9ÐFYÓ)ZÐ &ð /0¸1ÀaÐRSÑ&TÐ �^Ó TØ7J×7PÑ7PÓ7R×%S©t¨q°! a¨¡dÓ%SÐ �NÓ SØÐ óó"4ð( ISö LùóS &Ts   Ê(K