
    @=jhK                    
   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dSdZ dSdZ!dTdZ"dUdZ#dVdZ$dWd!Z%dXd#Z&dYd&Z'dZd*Z(d[d+Z)d\d,Z*d]d/Z+d^d0Z,d_d3Z-d`d5Z.dad6Z/dbd9Z0dbd:Z1dcd<Z2ddd=Z3ded?Z4dfdAZ5ddddBdCdgdIZ6dBdJdhdLZ7ddMdidPZ8djdRZ9dS )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                  |    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/rurouni/.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_nowr.   2   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_cutoffr2   6   s1    L&&8N)O)O)OOZZ\\\r&   r   c                 $    t                      dz  S )Nzverification_evidence.dbr   r%   r&   r'   _db_pathr4   :   s    999r&   sqlite3.Connectionc                    t                      } | j                            dd           t          j        |           }|                    d           |                    d           t          j        |_        t          |           |S )NT)parentsexist_okzPRAGMA journal_mode=WALzPRAGMA busy_timeout=5000)	r4   parentmkdirsqlite3connectexecuteRowrow_factory_ensure_schema)pathconns     r'   _connectrC   >   sx    ::DKdT222?4  DLL*+++LL+,,,{D4Kr&   rB   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', ?))r=   r   _VERIFY_SCHEMA_VERSIONcommit)rB   s    r'   r@   r@   I   s    LL	   	LL	  $ 	LL		   	LL	   	LLN	#	$	$&   	KKMMMMMr&   r   list[list[str]]c                    g }t                               |                                           D ]B}|s	 t          j        |          }n# t          $ r Y 'w xY w|r|                    |           C|S r*   )_SHELL_SPLIT_REsplitstripshlex
ValueErrorappend)r   segmentssegmenttokenss       r'   _split_segment_tokensrS   }   s     "H"((99 $ $ 		[))FF 	 	 	H	 	$OOF###Os   A


AAtokenc                    |                                  } |                     d          r| dd          } |                     d          | S )Nz./   )rL   
startswith)rT   s    r'   _clean_tokenrX      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%   rX   .0ts     r'   
<listcomp>z%_canonical_tokens.<locals>.<listcomp>   s#    EEEA1EQEEEr&   )rM   rK   rN   )rY   s    r'   _canonical_tokensrb      sI    EEY)?)?EEEE   			s     //rR   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%   r]   r^   s     r'   ra   z%_find_subsequence.<locals>.<listcomp>   s    ///1|A///r&   r   r   )lenrange)rR   rc   cleanedidxs       r'   _find_subsequencerk      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   )listrW   )rR   	remainings     r'   _strip_command_prefixrt      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   rV   >   bunnpmpnpmyarn/bashshpytest)python-mr   )python3r   r   )uvrw   r   )poetryrw   r   )pipenvrw   r   )rg   rO   extend)rc   
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)rS   rb   rt   r   rg   )r   r   rP   rY   rc   rR   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%   r_   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)rY   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&   argboolc                    | r|                      d          sd| v rdS d| v p1d| v p-d| v p)|                     d          p|                      d          S )	Nro   rn   Fr|   \z::)z.pyz.jsz.jsxz.tsz.tsxz.rsz.goz.java)test_testsspec	__tests__)rW   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   )r_   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)Nro   F)	rW   r   
expanduseris_absoluteresolvetempfile
gettempdirr7   	Exception)rT   rA   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   r7   r   )rT   r   rA   	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   namer   rW   _AD_HOC_SCRIPT_NAME_PREFIXESr   r   )rT   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--ro   )rt   r   	enumeraterW   )rR   r   r   r   rj   rT   s         r'   _ad_hoc_script_argsr     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                V    t          |           D ]}t          ||          }||c S d S r*   )rS   r   )r   r   rR   trailing_argss       r'   _find_ad_hoc_matchr   ,  sD    '00 ! !+FD99$     %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   rv   z
... [z chars omitted] ...
)rL   rg   _MAX_OUTPUT_SUMMARY_CHARS)r   textheadtails       r'   _summarize_outputr   4  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)r2   r=   _MAX_EVENTS_PER_SESSION_ROOT_MAX_TOTAL_UNREFERENCED_EVENTS)rB   r   r   cutoffs       r'   _prune_old_eventsr   A  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   r   rr   getr   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   factsverify_commandsmatch	is_ad_hocad_hoc_argsrY   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   r.   _DB_LOCKrC   r=   r   r   r   r   r   r   r   r   r   r   	lastrowidRuntimeErrorr   r   rG   __dict__)
r   r   r   r   r   evidencer   rB   curevent_ids
             r'   record_terminal_resultr    s    -  H tJ	 ' 'ZZ &	4,, '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   )r_   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   r   r   r   r   r   sortedr.   r   rC   r=   fetchonesetjsonloads	TypeErrorrN   dumpsrG   )r   r   r  r   r   sidr   r  	edited_atrB   rowexistingmergeds                r'   mark_workspace_editedr    s   ::::::!!#&&    t
j%I
&
&Cuyy  >D$4$4$<$<$>$>??D??U[b???@@M

I	  ZZ 	4,, 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   r   r   r   r   r   r   rC   r=   r  r  r  r  rN   dict)r   r   r   r   r  r   rB   stateeventr  r   r   s               r'   verification_statusr#  %  s   ::::::!!#&&    >*===
j%I
&
&Cuyy  >D$4$4$<$<$>$>??D	  ZZ 	4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(   r5   )rB   r5   r(   rD   )r   r   r(   rH   )rT   r   r(   r   )rY   r   r(   rZ   )rR   rZ   rc   rZ   r(   rd   )rR   rZ   r(   rZ   )rc   rZ   r(   rH   )r   r   r   rZ   r(   r   )rY   r   r(   r   )r   r   r(   r   )r   rZ   r(   r   )rT   r   r(   r   )rT   r   r   r   r(   r   )rR   rZ   r   r   r(   r   )r   r   r   r   r(   r   )r   r   r(   r   )rB   r5   r   r   r   r   r(   rD   )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  rerM   r;   r   	threadingdataclassesr   r   r   r   pathlibr   typingr	   r
   hermes_constantsr   Lockr   r   r1   r   r   r   rF   compilerJ   r   r.   r2   r4   rC   r@   rS   rX   rb   rk   rt   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r#  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&   