
    Tdj|P                    8   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	m
Z
 ddlmZ ddlmZmZmZ ddlmZ ddlmZmZmZ dd	lmZ  ej                    Zd
ZdZdZdZdZdZ ej         d          Z! ed           G d d                      Z"dXdZ#dXdZ$dYdZ%dZdZ&e
d[d            Z'd\d!Z(dd"d]d'Z)d^d)Z*d_d,Z+d`d0Z,dad1Z-dbd2Z.dcd5Z/ddd6Z0ded8Z1dfd:Z2dgd;Z3dhd>Z4dhd?Z5didAZ6djdBZ7dkdDZ8dldFZ9ddddGdHdmdNZ:dGdOdndQZ;ddRdodUZ<dpdWZ=dS )qa  Coding verification evidence ledger.

This module records what the agent actually proved while working in a code
workspace. It is deliberately passive: it never decides to run a suite, never
blocks completion, and never upgrades targeted checks into "repo green".
    )annotationsN)contextmanager)	dataclass)datetime	timedeltatimezone)Path)AnyIteratorOptionalget_hermes_homei     d   i'  )zhermes-verify-zhermes-ad-hoc-   z\s*(?:&&|\|\||;)\s*T)frozenc                  |    e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ded<   dZded<   dS )VerificationEvidencez,A classified command result worth recording.strcommandcanonical_commandkindscopestatusint	exit_codecwdroot
session_id output_summaryN)__name__
__module____qualname____doc____annotations__r!        A/home/thesage/.hermes/hermes-agent/agent/verification_evidence.pyr   r   #   s}         66LLLIIIJJJKKKNNNHHHIIIOOONr(   r   returnr   c                 b    t          j        t          j                                                  S N)r   nowr   utc	isoformatr'   r(   r)   _utc_nowr0   3   s     <%%//111r(   c                     t          j        t          j                  t	          t
                    z
                                  S )N)days)r   r-   r   r.   r   _MAX_EVIDENCE_AGE_DAYSr/   r'   r(   r)   _retention_cutoffr4   7   s1    L&&8N)O)O)OOZZ\\\r(   r	   c                 $    t                      dz  S )Nverification_evidence.dbr   r'   r(   r)   _db_pathr7   ;   s    999r(   sqlite3.Connectionc                 \   ddl m}  t                      }|j                            dd           t          j        |          }t
          j        |_        	  | |d           |	                    d           t          |           n## t          $ r |                                  w xY w|S )Nr   )apply_wal_with_fallbackT)parentsexist_okr6   )db_labelzPRAGMA busy_timeout=5000)hermes_stater:   r7   parentmkdirsqlite3connectRowrow_factoryexecute_ensure_schema	Exceptionclose)r:   pathconns      r)   _connectrK   ?   s    444444::DKdT222?4  D{D/IJJJJ/000t    	

	
 Ks   1B	 	 B)Iterator[sqlite3.Connection]c               #     K   t                      } 	 | 5  | V  ddd           n# 1 swxY w Y   |                                  dS # |                                  w xY w)u  Open a connection, commit/rollback on exit, and ALWAYS close it.

    ``sqlite3.Connection.__enter__``/``__exit__`` only commit or roll back the
    transaction; they do not close the connection. Using ``with _connect()``
    alone therefore leaks a connection — and its WAL/SHM file descriptors — on
    every call, deferring the close to the garbage collector, which over a
    long-running process can exhaust ``RLIMIT_NOFILE`` (the cron-ledger sibling
    of this bug was #69567 / PR #69594).
    N)rK   rH   rJ   s    r)   _transactionrO   R   s       ::D 	 	JJJ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	



s&   A %A )A )A ArJ   Nonec                (   |                      d           |                      d           |                      d           |                      d           |                      dt          t                    f           |                                  d S )Nz
        CREATE TABLE IF NOT EXISTS meta (
            key TEXT PRIMARY KEY,
            value TEXT NOT NULL
        )
        a  
        CREATE TABLE IF NOT EXISTS verification_events (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            created_at TEXT NOT NULL,
            session_id TEXT NOT NULL,
            cwd TEXT NOT NULL,
            root TEXT NOT NULL,
            command TEXT NOT NULL,
            canonical_command TEXT NOT NULL,
            kind TEXT NOT NULL,
            scope TEXT NOT NULL,
            status TEXT NOT NULL,
            exit_code INTEGER NOT NULL,
            output_summary TEXT NOT NULL
        )
        a9  
        CREATE TABLE IF NOT EXISTS verification_state (
            session_id TEXT NOT NULL,
            root TEXT NOT NULL,
            last_event_id INTEGER,
            last_edit_at TEXT,
            changed_paths_json TEXT NOT NULL DEFAULT '[]',
            PRIMARY KEY (session_id, root)
        )
        z
        CREATE INDEX IF NOT EXISTS idx_verification_events_session_root
        ON verification_events(session_id, root, id DESC)
        zDINSERT OR REPLACE INTO meta(key, value) VALUES ('schema_version', ?))rE   r   _VERIFY_SCHEMA_VERSIONcommitrN   s    r)   rF   rF   e   s    LL	   	LL	  $ 	LL		   	LL	   	LLN	#	$	$&   	KKMMMMMr(   posixr   rU   boollist[list[str]]c                   g }t                               |                                           D ]D}|s	 t          j        ||          }n# t          $ r Y )w xY w|r|                    |           E|S )NrT   )_SHELL_SPLIT_REsplitstripshlex
ValueErrorappend)r   rU   segmentssegmenttokenss        r)   _split_segment_tokensrb      s     "H"((99 $ $ 		[666FF 	 	 	H	 	$OOF###Os   A
AAtokenc                    |                                  } |                     d          r| dd          } |                     d          | S )Nz./   )r[   
startswith)rc   s    r)   _clean_tokenrg      sO    KKMME


4
 
  abb	 

4
 
  Lr(   	canonical	list[str]c                d    	 d t          j        |           D             S # t          $ r g cY S w xY w)Nc                0    g | ]}|t          |          S r'   rg   .0ts     r)   
<listcomp>z%_canonical_tokens.<locals>.<listcomp>   s#    EEEA1EQEEEr(   )r\   rZ   r]   )rh   s    r)   _canonical_tokensrq      sI    EEY)?)?EEEE   			s     //ra   needleOptional[int]c                   | r"|r t          |          t          |           k    rd S d | D             }t          dt          |          t          |          z
  dz             D ]$}|||t          |          z            |k    r|c S %d S )Nc                ,    g | ]}t          |          S r'   rl   rm   s     r)   rp   z%_find_subsequence.<locals>.<listcomp>   s    ///1|A///r(   r   r   )lenrange)ra   rr   cleanedidxs       r)   _find_subsequencerz      s      3v;;V#<#<t/////GQGs6{{2Q677  3sS[[(()V33JJJ 44r(   c                H   t          |           }|r|d         dk    r
|dd         }|rVd|d         v rL|d                             d          s1|dd         }|r%d|d         v r|d                             d          1|r |d         dv r|dd         }|r
|d         dv |S )zDRemove harmless command prefixes before matching canonical commands.r   envr   N=->   timenoglobr   )listrf   )ra   	remainings     r)   _strip_command_prefixr      s    VI "Yq\U**abbM	
 "y|++IaL4K4KC4P4P+abbM	  "y|++IaL4K4KC4P4P+
 "	!(EEEabbM	  "	!(EEEr(   c                ~   | g}t          |           dk    r7| d         dk    r+| d         }| d         }|dv r|                    ||g           t          |           dk    r1d| d         v r'|                    d| d         gd	| d         gg           | d
gk    r$|                    g dg dg dg dg dg           |S )zFReturn command spellings equivalent to the detected canonical command.   r   runr   re   >   bunnpmpnpmyarn/bashshpytest)python-mr   )python3r   r   )uvr   r   )poetryr   r   )pipenvr   r   )rv   r^   extend)rr   
candidatespackage_managerscript_names       r)   _equivalent_needlesr      s   J
6{{aF1I.. )Qi<<<<===
6{{aC6!9,,FF1I.vay0ABCCC(***+++'''++++++	
 	
 	
 r(   canonical_commandsOptional[tuple[str, list[str]]]c                   t          |           }|D ]t}t          |          }|s|D ]]}t          |          }t          |          D ]<}|dt	          |                   |k    r||t	          |          d         fc c c S =^udS )zEReturn ``(canonical, trailing_args)`` for the first detected command.N)rb   rq   r   r   rv   )r   r   r_   rh   rr   ra   candidate_tokens	candidates           r)   _find_canonical_matchr      s     %W--H' H H	"9-- 	 	H 	HF4V<<088 H H	#OS^^O4	AA$&6s9~~&GGGGGGGGG BH	H
 4r(   c                    |                                  t          fddD                       rdS t          fddD                       rdS dv rdS dv sd	v rd	S d
v rdvrd
S dS )Nc              3      K   | ]}|v V  	d S r,   r'   rn   wordlowereds     r)   	<genexpr>z$_kind_for_command.<locals>.<genexpr>   s'      
B
Bt47?
B
B
B
B
B
Br(   )linteslintruffr   c              3      K   | ]}|v V  	d S r,   r'   r   s     r)   r   z$_kind_for_command.<locals>.<genexpr>   s'      
U
Ut47?
U
U
U
U
U
Ur(   )	typechecktscmypypyrighttyr   buildfmtformatchecktest)lowerany)rh   r   s    @r)   _kind_for_commandr      s    ooG

B
B
B
B'A
B
B
BBB v

U
U
U
U'T
U
U
UUU {'w8w..x'fG33w6r(   argc                    | r|                      d          sd| v rdS d| v p1d| v p-d| v p)|                     d          p|                      d          S )	Nr~   r}   Fr   \z::)z.pyz.jsz.jsxz.tsz.tsxz.rsz.goz.java)test_testsspec	__tests__)rf   endswith)r   s    r)   _looks_like_targetr     s     #..%% us
 	C3;	C3;	C <<TUU	C >>ABBr(   argsc                <    t          d | D                       rdndS )Nc              3  4   K   | ]}t          |          V  d S r,   )r   )rn   r   s     r)   r   z"_scope_for_args.<locals>.<genexpr>  s+      EE/44EEEEEEr(   targetedfull)r   )r   s    r)   _scope_for_argsr     s(    EEEEEEEQ::6Qr(   c                p   | r|                      d          rdS 	 t          |                                           }|                                sdS |                                }t          t          j                                                              }||k    p||j        v S # t          $ r Y dS w xY w)Nr~   F)	rf   r	   
expanduseris_absoluteresolvetempfile
gettempdirr;   rG   )rc   rI   resolved	temp_roots       r)   _is_under_temp_dirr     s     E$$S)) uE{{%%''!! 	5<<>>,..//7799	9$E	X5E(EE   uus   5B' AB' '
B54B5r   str | Path | Nonec                   |sdS 	 t          |                                                                           }t          |                                                                          }||k    p||j        v S # t          $ r Y dS w xY wNF)r	   r   r   r;   rG   )rc   r   rI   	root_paths       r)   _is_under_rootr     s     uE{{%%''//11JJ))++3355	y =I$==   uus   A4A; ;
B	B	c                    	 t          |                                           j        }n# t          $ r Y dS w xY w|                    t
                    ot          |           ot          | |           S r   )r	   r   namerG   rf   _AD_HOC_SCRIPT_NAME_PREFIXESr   r   )rc   r   r   s      r)   _is_temp_script_pathr   *  s    E{{%%'',   uu 	455 	,u%%	,ud+++s   &) 
77Optional[list[str]]c                2   t          |           }|sd S |d         }t          ||          r
|dd          S |dv r]t          |dd          d          D ]C\  }}|dk    rt          ||          r||dz   d          c S |                    d          s d S Dd S )Nr   r   >   r   r   nodeperlrubyr   r   )startz--r~   )r   r   	enumeraterf   )ra   r   r   r   ry   rc   s         r)   _ad_hoc_script_argsr   6  s    ,V44 tq!GGT** $##MMM#$4QRR$8BBB 	 	JC}}#E400 2'a1111##C(( tt4r(   c                h    dD ].}t          | |          D ]}t          ||          }||c c S /d S )N)TFrT   )rb   r   )r   r   rU   ra   trailing_argss        r)   _find_ad_hoc_matchr   H  sg      % %+G5AAA 	% 	%F/==M($$$$$$ )	% 4r(   outputc                    | pd                                 }t          |          t          k    r|S t          dz  }t          |z
  }|d |         dt          |          t          z
   dz   || d          z   S )Nr    r   z
... [z chars omitted] ...
)r[   rv   _MAX_OUTPUT_SUMMARY_CHARS)r   textheadtails       r)   _summarize_outputr   S  s    Lb!!D
4yy---$)D$t+DUdU
PCII 99
P
P
P	Q
uvv,	r(   r   c                   t                      }|                     d||||t          f           |                     d||f           |                     d|f           |                     dt          f           dS )z?Bound ledger growth without deleting the current state pointer.a!  
        DELETE FROM verification_events
        WHERE session_id = ?
          AND root = ?
          AND id NOT IN (
              SELECT id FROM verification_events
              WHERE session_id = ? AND root = ?
              ORDER BY id DESC
              LIMIT ?
          )
        aQ  
        DELETE FROM verification_state
        WHERE (
            last_edit_at IS NOT NULL
            AND last_edit_at < ?
        )
        OR (
            last_edit_at IS NULL
            AND last_event_id IN (
                SELECT id FROM verification_events
                WHERE created_at < ?
            )
        )
        z
        DELETE FROM verification_events
        WHERE created_at < ?
          AND id NOT IN (
              SELECT last_event_id FROM verification_state
              WHERE last_event_id IS NOT NULL
          )
        aD  
        DELETE FROM verification_events
        WHERE id NOT IN (
            SELECT id FROM verification_events
            ORDER BY id DESC
            LIMIT ?
        )
          AND id NOT IN (
              SELECT last_event_id FROM verification_state
              WHERE last_event_id IS NOT NULL
          )
        N)r4   rE   _MAX_EVENTS_PER_SESSION_ROOT_MAX_TOTAL_UNREFERENCED_EVENTS)rJ   r   r   cutoffs       r)   _prune_old_eventsr   `  s      FLL
	 
T:t-IJ   	LL	 
  " 	LL	 
	
 
 
 	LL	 
()    r(   r    r   r   r   r   r   
str | Noner   r   Optional[VerificationEvidence]c               6   | rt          | t                    sdS 	 ddlm}  ||          }n# t          $ r d}Y nw xY w|sdS t          |                    d          pg           }t          | |          }d}	|-|s+t          | |                    d                    }
|
d|
f}d}	|dS |\  }}t          | ||	rd	nt          |          |	rd
nt          |          t          |          dk    rdndt          |          t          t          |pd                                                    t          |                    d          p"t          |pd                                                    t          |pd          t          |          
  
        S )zDClassify a terminal command as verification evidence, if applicable.Nr   project_facts_forverifyCommandsFr   zad-hoc verification scriptTad_hocr   passedfailed.default)
r   r   r   r   r   r   r   r   r   r!   )
isinstancer   agent.coding_contextr   rG   r   getr   r   r   r   r   r   r	   r   r   )r   r   r   r   r   r   factsverify_commandsmatch	is_ad_hocad_hoc_argsrh   r   s                r)   classify_verification_commandr    s     *Wc22 t::::::!!#&&    t599%566<"==O!'?;;EI}_}(%))F2C2CDD"1;?EI}t$I}#"DXX(9)(D(D%Ijj?=+I+Iy>>Q..xxHi..SZC  ((**++6""@d3:#&6&6&>&>&@&@AAz.Y//(00   s   - <<)r   Optional[dict[str, Any]]c                   t          | ||||          }|dS t                      }t          5  t                      5 }|                    d||j        |j        |j        |j        |j	        |j
        |j        |j        |j        |j        f          }|j        t!          d          t#          |j                  }	|                    d|j        |j        |	f           t%          ||j        |j                   |                                 ddd           n# 1 swxY w Y   ddd           n# 1 swxY w Y   d|	i|j        d|iS )	zERecord a foreground terminal result when it is verification evidence.r   Na  
                INSERT INTO verification_events(
                    created_at, session_id, cwd, root, command, canonical_command,
                    kind, scope, status, exit_code, output_summary
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                z.verification event insert did not return an ida  
                INSERT INTO verification_state(
                    session_id, root, last_event_id, last_edit_at, changed_paths_json
                ) VALUES (?, ?, ?, NULL, '[]')
                ON CONFLICT(session_id, root) DO UPDATE SET
                    last_event_id = excluded.last_event_id,
                    last_edit_at = NULL,
                    changed_paths_json = '[]'
                )r   r   id
created_at)r  r0   _DB_LOCKrO   rE   r   r   r   r   r   r   r   r   r   r!   	lastrowidRuntimeErrorr   r   rS   __dict__)
r   r   r   r   r   evidencer  rJ   curevent_ids
             r)   record_terminal_resultr    s    -  H tJ	 ' '^^ &	t,, 'LM$.MNO&+ C* }$"#STTT3=))HLL $hmX>   dx/BWWWWKKMMMM&	 &	 &	 &	 &	 &	 &	 &	 &	 &	 &	 &	 &	 &	 &	' ' ' ' ' ' ' ' ' ' ' ' ' ' 'R (Jh/JzJJJs5   D1CDD1D	D1!D	"D11D58D5)pathsr  "list[str] | tuple[str, ...] | Nonec                   	 ddl m}  ||          }n# t          $ r d}Y nw xY w|sdS t          | pd          }t          |                    d          p"t          |pd                                                    }t          d |pg D                       }t                      }t          5  t                      5 }	|	                    d||f                                          }
t                      }|
P	 t          t          j        |
d	         pd
                    }n%# t           t"          f$ r t                      }Y nw xY wt          |t          |          z            dd         }|	                    d|||t          j        |          f           |	                                 ddd           n# 1 swxY w Y   ddd           n# 1 swxY w Y   ||||dS )z>Mark verification evidence stale after a successful file edit.r   r   Nr   r   r   c                0    h | ]}|t          |          S r'   )r   )rn   ps     r)   	<setcomp>z(mark_workspace_edited.<locals>.<setcomp>!  s#    ???qQ?CFF???r(   z
                SELECT changed_paths_json FROM verification_state
                WHERE session_id = ? AND root = ?
                changed_paths_json[]i8a~  
                INSERT INTO verification_state(
                    session_id, root, last_event_id, last_edit_at, changed_paths_json
                ) VALUES (?, ?, NULL, ?, ?)
                ON CONFLICT(session_id, root) DO UPDATE SET
                    last_edit_at = excluded.last_edit_at,
                    changed_paths_json = excluded.changed_paths_json
                )r   r   last_edit_atchanged_paths)r   r   rG   r   r   r	   r   sortedr0   r  rO   rE   fetchonesetjsonloads	TypeErrorr]   dumpsrS   )r   r   r  r   r   sidr   r  	edited_atrJ   rowexistingmergeds                r)   mark_workspace_editedr&    s   ::::::!!#&&    t
j%I
&
&Cuyy  >D$4$4$<$<$>$>??D??U[b???@@M

I	  ^^ 	t,, d  hjj  "%H%"4:c2F.G.O4#P#PQQHH!:. % % %"uuHHH%XM(:(::<<TUUCFLL dItz&'9'9:
 
 
 KKMMM5	 	 	 	 	 	 	 	 	 	 	 	 	 	 	              : tYYfgggsg    ##0G?;F=;)D%$F=%EF=EA*F=1G=G	GG	GGGdict[str, Any]c                   	 ddl m}  ||          }n# t          $ r d}Y nw xY w|sdddS t          | pd          }t          |                    d          p"t          |pd                                                    }t          5  t                      5 }|	                    d	||f          
                                }| d
d||g dcddd           cddd           S d}|d         /|	                    d|d         f          
                                }ddd           n# 1 swxY w Y   ddd           n# 1 swxY w Y   g }		 t          j        |d         pd          }	n# t          t          f$ r g }	Y nw xY w|d
d|||	dS t          |          }
|d         r|d         |
d         k    rd}n|
d         }||
|||	dS )zAReturn the best known verification state for a session/workspace.r   r   Nnot_applicable)r   r  r   r   r   z
                SELECT last_event_id, last_edit_at, changed_paths_json
                FROM verification_state
                WHERE session_id = ? AND root = ?
                
unverified)r   r  r   r   r  last_event_idz.SELECT * FROM verification_events WHERE id = ?r  r  r  r  staler   )r   r   rG   r   r   r	   r   r  rO   rE   r  r  r  r  r]   dict)r   r   r   r   r!  r   rJ   stateeventr  r  r   s               r)   verification_statusr0  D  s   ::::::!!#&&    >*===
j%I
&
&Cuyy  >D$4$4$<$<$>$>??D	  ^^ 	tLL
 d  hjj  }* $ "%%' 	 	 	 	 	 	 	       $ E_%1D?+-  (** '	 	 	 	 	 	 	 	 	 	 	 	 	 	 	              2  "M
5)=#>#F$GGz"    }"*
 
 	
 E{{H^ $~!6,9O!O!O(#&  s`    ##
E4D+E&9D+E+D/	/E2D/	3EE	EE- -FF)r*   r   )r*   r	   )r*   r8   )r*   rL   )rJ   r8   r*   rP   )r   r   rU   rV   r*   rW   )rc   r   r*   r   )rh   r   r*   ri   )ra   ri   rr   ri   r*   rs   )ra   ri   r*   ri   )rr   ri   r*   rW   )r   r   r   ri   r*   r   )rh   r   r*   r   )r   r   r*   rV   )r   ri   r*   r   )rc   r   r*   rV   )rc   r   r   r   r*   rV   )ra   ri   r   r   r*   r   )r   r   r   r   r*   r   )r   r   r*   r   )rJ   r8   r   r   r   r   r*   rP   )r   r   r   r   r   r   r   r   r   r   r*   r   )r   r   r   r   r   r   r   r   r   r   r*   r  )r   r   r   r   r  r  r*   r  )r   r   r   r   r*   r'  )>r%   
__future__r   r  rer\   rA   r   	threading
contextlibr   dataclassesr   r   r   r   pathlibr	   typingr
   r   r   hermes_constantsr   Lockr  r   r3   r   r   r   rR   compilerY   r   r0   r4   r7   rK   rO   rF   rb   rg   rq   rz   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r&  r0  r'   r(   r)   <module>r;     s    # " " " " "  				        % % % % % % ! ! ! ! ! ! 2 2 2 2 2 2 2 2 2 2       * * * * * * * * * * , , , , , , 9>   " !' C  "*344 $       2 2 2 2] ] ] ]: : : :   &    $1 1 1 1h :>               	 	 	 	   .       	 	 	 	R R R R      	 	 	 	   $   
 
 
 
; ; ; ;B "!, , , , , ,j >K >K >K >K >K >KJ 15	3h 3h 3h 3h 3h 3hlE E E E E Er(   