
    .cjL                       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dUdZ dUdZ!dVdZ"dWdZ#dXdZ$dddYd$Z%dZd&Z&d[d)Z'd\d-Z(d]d.Z)d^d/Z*d_d2Z+d`d3Z,dad5Z-dbd7Z.dcd8Z/ddd;Z0ddd<Z1ded>Z2dfd?Z3dgdAZ4dhdCZ5ddddDdEdidKZ6dDdLdjdNZ7ddOdkdRZ8dldTZ9dS )ma  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        =/home/ice/.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 )Nverification_evidence.dbr   r%   r&   r'   _db_pathr5   :   s    999r&   sqlite3.Connectionc                    ddl m}  t                      }|j                            dd           t          j        |          } | |d           |                    d           t
          j        |_	        t          |           |S )Nr   )apply_wal_with_fallbackT)parentsexist_okr4   )db_labelzPRAGMA busy_timeout=5000)hermes_stater8   r5   parentmkdirsqlite3connectexecuteRowrow_factory_ensure_schema)r8   pathconns      r'   _connectrG   >   s    444444::DKdT222?4  DD+EFFFFLL+,,,{D4Kr&   rF   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', ?))rA   r   _VERIFY_SCHEMA_VERSIONcommit)rF   s    r'   rD   rD   K   s    LL	   	LL	  $ 	LL		   	LL	   	LLN	#	$	$&   	KKMMMMMr&   posixr   rM   boollist[list[str]]c                   g }t                               |                                           D ]D}|s	 t          j        ||          }n# t          $ r Y )w xY w|r|                    |           E|S )NrL   )_SHELL_SPLIT_REsplitstripshlex
ValueErrorappend)r   rM   segmentssegmenttokenss        r'   _split_segment_tokensrZ      s     "H"((99 $ $ 		[666FF 	 	 	H	 	$OOF###Os   A
AAtokenc                    |                                  } |                     d          r| dd          } |                     d          | S )Nz./   )rS   
startswith)r[   s    r'   _clean_tokenr_      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%   r_   .0ts     r'   
<listcomp>z%_canonical_tokens.<locals>.<listcomp>   s#    EEEA1EQEEEr&   )rT   rR   rU   )r`   s    r'   _canonical_tokensri      sI    EEY)?)?EEEE   			s     //rY   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%   rd   re   s     r'   rh   z%_find_subsequence.<locals>.<listcomp>   s    ///1|A///r&   r   r   )lenrange)rY   rj   cleanedidxs       r'   _find_subsequencerr      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   )listr^   )rY   	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   r]   >   bunnpmpnpmyarn/bashshpytest)python-mr   )python3r   r   )uvr~   r   )poetryr~   r   )pipenvr~   r   )rn   rV   extend)rj   
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)rZ   ri   r{   r   rn   )r   r   rW   r`   rj   rY   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%   rf   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)r`   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 )	Nrv   ru   Fr   \z::)z.pyz.jsz.jsxz.tsz.tsxz.rsz.goz.java)test_testsspec	__tests__)r^   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   )rf   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)Nrv   F)	r^   r   
expanduseris_absoluteresolvetempfile
gettempdirr9   	Exception)r[   rE   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   r9   r   )r[   r   rE   	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   r^   _AD_HOC_SCRIPT_NAME_PREFIXESr   r   )r[   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--rv   )r{   r   	enumerater^   )rY   r   r   r   rq   r[   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                h    dD ].}t          | |          D ]}t          ||          }||c c S /d S )N)TFrL   )rZ   r   )r   r   rM   rY   trailing_argss        r'   _find_ad_hoc_matchr   .  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] ...
)rS   rn   _MAX_OUTPUT_SUMMARY_CHARS)r   textheadtails       r'   _summarize_outputr   9  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   rA   _MAX_EVENTS_PER_SESSION_ROOT_MAX_TOTAL_UNREFERENCED_EVENTS)rF   r   r   cutoffs       r'   _prune_old_eventsr   F  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   ry   getr   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   factsverify_commandsmatch	is_ad_hocad_hoc_argsr`   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_LOCKrG   rA   r   r   r   r   r   r   r   r   r   r   	lastrowidRuntimeErrorr   r   rK   __dict__)
r   r   r   r   r   evidencer   rF   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   )rf   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  rG   rA   fetchonesetjsonloads	TypeErrorrU   dumpsrK   )r   r   r	  r   r   sidr   r  	edited_atrF   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  rG   rA   r  r  r  r  rU   dict)r   r   r   r   r  r   rF   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(   r6   )rF   r6   r(   rH   )r   r   rM   rN   r(   rO   )r[   r   r(   r   )r`   r   r(   ra   )rY   ra   rj   ra   r(   rk   )rY   ra   r(   ra   )rj   ra   r(   rO   )r   r   r   ra   r(   r   )r`   r   r(   r   )r   r   r(   rN   )r   ra   r(   r   )r[   r   r(   rN   )r[   r   r   r   r(   rN   )rY   ra   r   r   r(   r   )r   r   r   r   r(   r   )r   r   r(   r   )rF   r6   r   r   r   r   r(   rH   )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  rerT   r?   r   	threadingdataclassesr   r   r   r   pathlibr   typingr	   r
   hermes_constantsr   Lockr  r   r1   r   r   r   rJ   compilerQ   r   r.   r2   r5   rG   rD   rZ   r_   ri   rr   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r)  r%   r&   r'   <module>r3     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&   