
    `gjiL                    J   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mZmZ ddlmZ ddl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d3dZ d3dZ!d4dZ"d5dZ#d6dZ$ddd7dZ%d8dZ&d9dZ'd:dZ(d;dZ)d<dZ*d=d Z+d>d!Z,d?d"Z-d@d#Z.dAd$Z/dBd%Z0dBd&Z1dCd'Z2dDd(Z3dEd)Z4dFd*Z5dddd+d,	 	 	 	 	 	 	 	 	 	 	 dGd-Z6d+d.	 	 	 	 	 	 	 	 	 	 	 dHd/Z7dd0	 	 	 	 	 	 	 dId1Z8	 	 	 	 	 	 dJd2Z9y)Ka  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)	dataclass)datetime	timedeltatimezone)Path)AnyOptionalget_hermes_homei     d   i'  )zhermes-verify-zhermes-ad-hoc-   z\s*(?:&&|\|\||;)\s*T)frozenc                  z    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<   y)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        N/root/.hermes/venv/lib/python3.12/site-packages/agent/verification_evidence.pyr   r   "   s=    6L
IJKN	H
IONCr&   r   c                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformatr%   r&   r'   _utc_nowr-   2   s    <<%//11r&   c                     t        j                  t        j                        t	        t
              z
  j                         S )N)days)r   r*   r   r+   r   _MAX_EVIDENCE_AGE_DAYSr,   r%   r&   r'   _retention_cutoffr1   6   s)    LL&8N)OOZZ\\r&   c                     t               dz  S )Nzverification_evidence.dbr   r%   r&   r'   _db_pathr3   :   s    999r&   c                    t               } | j                  j                  dd       t        j                  |       }|j                  d       |j                  d       t        j                  |_        t        |       |S )NT)parentsexist_okzPRAGMA journal_mode=WALzPRAGMA busy_timeout=5000)	r3   parentmkdirsqlite3connectexecuteRowrow_factory_ensure_schema)pathconns     r'   _connectrA   >   sa    :DKKdT2??4 DLL*+LL+,{{D4Kr&   c                    | j                  d       | j                  d       | j                  d       | j                  d       | j                  dt        t              f       | j                          y )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', ?))r;   r   _VERIFY_SCHEMA_VERSIONcommit)r@   s    r'   r>   r>   I   st    LL	 	LL	$ 	LL		 	LL	 	LLN	#	$& 	KKMr&   posixc                   g }t         j                  | j                               D ]1  }|s	 t        j                  ||      }|s!|j                  |       3 |S # t        $ r Y Aw xY w)NrE   )_SHELL_SPLIT_REsplitstripshlex
ValueErrorappend)r   rF   segmentssegmenttokenss        r'   _split_segment_tokensrQ   }   sn     "H"((9 $	[[6F OOF#$ O	  		s   A	A('A(c                v    | j                         } | j                  d      r| dd  } | j                  d      r| S )Nz./   )rJ   
startswith)tokens    r'   _clean_tokenrV      s;    KKME


4
 ab	 

4
 Lr&   c                    	 t        j                  |       D cg c]  }|st        |       c}S c c}w # t        $ r g cY S w xY wr)   )rK   rI   rV   rL   )	canonicalts     r'   _canonical_tokensrZ      s>    ).Y)?EA1QEEE 	s   6 116 6 AAc                    | r|rt        |      t        |       kD  ry | D cg c]  }t        |       }}t        dt        |      t        |      z
  dz         D ]  }|||t        |      z    |k(  s|c S  y c c}w )Nr   r   )lenrV   range)rP   needlerY   cleanedidxs        r'   _find_subsequencera      s    3v;V#<(./1|A/G/QGs6{2Q67 3sS[()V3J 	 0s   A7c                    t        |       }|r|d   dk(  r|dd }|r>d|d   v r7|d   j                  d      s#|dd }|rd|d   v r|d   j                  d      s#|r|d   dv r|dd }|r|d   dv r|S )zDRemove harmless command prefixes before matching canonical commands.r   envr   N=->   timenoglobr   )listrT   )rP   	remainings     r'   _strip_command_prefixrj      s    VIYq\U*abM	
y|+IaL4K4KC4PabM	 y|+IaL4K4KC4P
	!(EEabM	 	!(EEr&   c                *   | g}t        |       dk\  r)| d   dk(  r!| d   }| d   }|dv r|j                  ||g       t        |       dk(  r$d| d   v r|j                  d| d   gd	| d   gg       | d
gk(  r |j                  g dg dg dg dg dg       |S )zFReturn command spellings equivalent to the detected canonical command.   r   runr   rS   >   bunnpmpnpmyarn/bashshpytest)python-mru   )python3rw   ru   )uvrm   ru   )poetryrm   ru   )pipenvrm   ru   )r\   rM   extend)r^   
candidatespackage_managerscript_names       r'   _equivalent_needlesr      s    J
6{aF1I. )Qi<<<=
6{aC6!9,FF1I.vay0ABC(*+'++	
 r&   c                    t        |       }|D ]Y  }t        |      }|s|D ]D  }t        |      }t        |      D ])  }|dt	        |       |k(  s||t	        |      d fc c c S  F [ y)zEReturn ``(canonical, trailing_args)`` for the first detected command.N)rQ   rZ   rj   r   r\   )r   canonical_commandsrN   rX   r^   rP   candidate_tokens	candidates           r'   _find_canonical_matchr      s     %W-H' H	"9- 	HF4V<08 H	#OS^4	A$&6s9~&GGGH	H	H r&   c                    | j                         t        fddD              ryt        fddD              rydv rydv sd	v ry	d
v rdvry
y)Nc              3  &   K   | ]  }|v  
 y wr)   r%   .0wordlowereds     r'   	<genexpr>z$_kind_for_command.<locals>.<genexpr>   s     
Bt47?
B   )linteslintruffr   c              3  &   K   | ]  }|v  
 y wr)   r%   r   s     r'   r   z$_kind_for_command.<locals>.<genexpr>   s     
Ut47?
Ur   )	typechecktscmypypyrighttyr   buildfmtformatchecktest)lowerany)rX   r   s    @r'   _kind_for_commandr      se    ooG

B'A
BB

U'T
UU'8w.'fG3r&   c                    | r| j                  d      sd| v ryd| v xs0 d| v xs* d| v xs$ | j                  d      xs | j                  d      S )	Nre   rd   Frr   \z::)z.pyz.jsz.jsxz.tsz.tsxz.rsz.goz.java)test_testsspec	__tests__)rT   endswith)args    r'   _looks_like_targetr      si    #..%s
 	C3;	C3;	C <<TU	C >>ABr&   c                .    t        d | D              rdS dS )Nc              3  2   K   | ]  }t        |        y wr)   )r   )r   r   s     r'   r   z"_scope_for_args.<locals>.<genexpr>   s     E/4Es   targetedfull)r   )argss    r'   _scope_for_argsr      s    EEE:Q6Qr&   c                >   | r| j                  d      ry	 t        |       j                         }|j                         sy|j	                         }t        t        j                               j	                         }||k(  xs ||j                  v S # t        $ r Y yw xY w)Nre   F)	rT   r   
expanduseris_absoluteresolvetempfile
gettempdirr5   	Exception)rU   r?   resolved	temp_roots       r'   _is_under_temp_dirr      s    E$$S)E{%%'!<<>,,./779	9$E	X5E5E(EE s   )B  AB 	BBc                    |sy	 t        |       j                         j                         }t        |      j                         j                         }||k(  xs ||j                  v S # t        $ r Y yw xY wNF)r   r   r   r5   r   )rU   r   r?   	root_paths       r'   _is_under_rootr     sn    E{%%'//1J))+335	y =I$== s   A"A( (	A43A4c                    	 t        |       j                         j                  }|j	                  t
              xr t        |       xr t        | |       S # t        $ r Y yw xY wr   )r   r   namer   rT   _AD_HOC_SCRIPT_NAME_PREFIXESr   r   )rU   r   r   s      r'   _is_temp_script_pathr     sf    E{%%',, 	45 	,u%	,ud++  s   #A 	A"!A"c                    t        |       }|sy |d   }t        ||      r|dd  S |dv rGt        |dd  d      D ]4  \  }}|dk(  rt        ||      r
||dz   d  c S |j                  d      r4 y  y )Nr   r   >   rt   rs   nodeperlrubyrv   rx   )startz--re   )rj   r   	enumeraterT   )rP   r   r   r   r`   rU   s         r'   _ad_hoc_script_argsr     s    ,V4q!GGT*##MM#$4QR$8B 	JC}#E40'a11##C(	 r&   c                ^    dD ](  }t        | |      D ]  }t        ||      }||c c S  * y )N)TFrE   )rQ   r   )r   r   rF   rP   trailing_argss        r'   _find_ad_hoc_matchr   ,  sF      %+G5A 	%F/=M($$	%%
 r&   c                    | xs dj                         }t        |      t        k  r|S t        dz  }t        |z
  }|d | dt        |      t        z
   dz   || d  z   S )Nr   rl   z
... [z chars omitted] ...
)rJ   r\   _MAX_OUTPUT_SUMMARY_CHARS)outputtextheadtails       r'   _summarize_outputr   7  sv    Lb!D
4y--$)D$t+DUdCI 99::O
P	Q
uv,	r&   c                   t               }| j                  d||||t        f       | j                  d||f       | j                  d|f       | j                  dt        f       y)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)r1   r;   _MAX_EVENTS_PER_SESSION_ROOT_MAX_TOTAL_UNREFERENCED_EVENTS)r@   r   r   cutoffs       r'   _prune_old_eventsr   D  s~     FLL
	 
T:t-IJ 	LL	 
" 	LL	 
	
 	LL	 
()r&   r   r   r   r   r   c                  | rt        | t              sy	 ddlm}  ||      }|syt        |j                  d      xs g       }t        | |      }d}	|%|s#t        | |j                  d            }
|
d|
f}d}	|y|\  }}t        | ||	rd	n
t        |      |	rd
n
t        |      t        |      dk(  rdndt        |      t        t        |xs d      j                               t        |j                  d      xs t        |xs d      j                               t        |xs d      t        |      
      S # t        $ r d}Y %w xY w)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   r   rh   getr   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   factsverify_commandsmatch	is_ad_hocad_hoc_argsrX   r   s                r'   classify_verification_commandr     sJ    *Wc2:!#& 599%56<"=O!'?;EI}_(%))F2CD"1;?EI}$I}#"X(9)(D%j?=+Iy>Q.xHi.SZC ((*+6"@d3:#&6&>&>&@Az.Y/(0 #  s   D: :E	E	)r   c                   t        | ||||      }|yt               }t        5  t               5 }|j	                  d||j
                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  |j                  f      }|j                  t!        d      t#        |j                        }	|j	                  d|j
                  |j                  |	f       t%        ||j
                  |j                         |j'                          ddd       ddd       d	i|j(                  d|iS # 1 sw Y   %xY w# 1 sw Y   )xY w)	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   r-   _DB_LOCKrA   r;   r   r   r   r   r   r   r   r   r   r   	lastrowidRuntimeErrorr   r   rD   __dict__)
r   r   r   r   r   evidencer   r@   curevent_ids
             r'   record_terminal_resultr     s`    -H J	 'Z &	4,, ''LLMM$$..MMNNOO&&++C* }}$"#STT3==)HLL $$hmmX> dx/B/BWKKMM&	'R (Jh//JzJJQ&	 &	' 's#   E(D	E8E(E%	!E((E1)pathsc                X   	 ddl m}  ||      }|syt        | xs d      }t        |j	                  d      xs t        |xs d      j                               }t        |xs g D ch c]  }|st        |       c}      }t               }	t        5  t               5 }
|
j                  d||f      j                         }t               }|&	 t        t        j                  |d   xs d	            }t        |t        |      z        d
d }|
j                  d|||	t        j$                  |      f       |
j'                          ddd       ddd       |||	|dS # t        $ r d}Y \w xY wc c}w # t         t"        f$ r t               }Y w xY w# 1 sw Y   NxY w# 1 sw Y   RxY w)z>Mark verification evidence stale after a successful file edit.r   r   Nr   r   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   r   r   r   r   r   sortedr-   r   rA   r;   fetchonesetjsonloads	TypeErrorrL   dumpsrD   )r   r   r   r   r   sidr   pr   	edited_atr@   rowexistingmergeds                 r'   mark_workspace_editedr    s   :!#& 
j%I
&Cuyy >D$4$<$<$>?DU[b?qQCF?@M
I	 Z 	4,, d hj  "%H%"4::c2F.G.O4#PQH XM(::<TUCFLL dItzz&'9:
 KKM5	: tYYfggO   @  ":. %"uH%	 	 sf   E (E00E0F /F%E54AFF E-,E-5FFFFF	F  F)c                   	 ddl m}  ||      }|sdddS t        | xs d      }t        |j	                  d      xs t        |xs d      j                               }t        5  t               5 }|j                  d	||f      j                         }|d
d||g dcddd       cddd       S d}|d   $|j                  d|d   f      j                         }ddd       ddd       g }		 t        j                  d   xs d      }	d
d|||	dS t        |      }
d   r|d   |
d   kD  rd}n|
d   }||
|||	dS # t        $ r d}Y <w xY w# 1 sw Y   wxY w# 1 sw Y   {xY w# t        t        f$ r g }	Y rw xY w)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   r   r   r   r   r   r   rA   r;   r   r   r   r   rL   dict)r   r   r   r   r  r   r@   stateeventr   r   r   s               r'   verification_statusr  (  s   :!#& *==
j%I
&Cuyy >D$4$<$<$>?D	 Z 	4LL
 d hj  }* $ "%%'	 	 $ E_%1D?+- (* '	2  "M

5)=#>#F$G }"*
 	
 E{H^~!6,9O!O(#& i  	 	 8 z" sS   D> %E0,E	E/+EE-E( >EEE	EE%(E<;E<)returnr   )r  r   )r  sqlite3.Connection)r@   r  r  None)r   r   rF   boolr  list[list[str]])rU   r   r  r   )rX   r   r  	list[str])rP   r  r^   r  r  zOptional[int])rP   r  r  r  )r^   r  r  r  )r   r   r   r  r  zOptional[tuple[str, list[str]]])rX   r   r  r   )r   r   r  r  )r   r  r  r   )rU   r   r  r  )rU   r   r   str | Path | Noner  r  )rP   r  r   r  r  Optional[list[str]])r   r   r   r  r  r  )r   r   r  r   )r@   r  r   r   r   r   r  r  )r   r   r   r  r   
str | Noner   r   r   r   r  zOptional[VerificationEvidence])r   r   r   r  r   r  r   r   r   r   r  Optional[dict[str, Any]])r   r  r   r  r   z"list[str] | tuple[str, ...] | Noner  r  )r   r  r   r  r  zdict[str, Any]):r#   
__future__r   r   rerK   r9   r   	threadingdataclassesr   r   r   r   pathlibr   typingr	   r
   hermes_constantsr   Lockr   r   r0   r   r   r   rC   compilerH   r   r-   r1   r3   rA   r>   rQ   rV   rZ   ra   rj   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r%   r&   r'   <module>r$     s   #  	     ! 2 2    , 9>>   " !' C  "**34 $  2]:1h :> 	. 	R	$
;B "!,, 
, 	,
 , , $,j >K>K 
>K 	>K
 >K >K >KJ 15	3h3h 
3h .	3h
 3hlEE 
E 	Er&   