o
    :Liue                     @   st  d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m	Z	 ddl
mZ ddlmZmZmZmZmZ ejejdd eeZe d Zed	 Zed
ZedZed Zed Zed Zed ZddddddddZ G dd dZ!G dd dZ"G dd dZ#G dd dZ$d efd!d"Z%d#efd$d%Z&d&d' Z'd(d) Z(d*d+ Z)d,d- Z*ed.kre*  dS dS )/ue  
Rook Hyperagent — Phase 1: Skill Self-Improvement

Analyzes agent session data to identify underperforming skills,
proposes improvements, tests them against historical sessions,
and promotes successful variants.

This is the entry point for self-improving behavior. The agent
doesn't just use skills — it makes them better over time.

Architecture:
    Training DBs → Identify weak skills → LLM proposes fix →
    Test against saved sessions → Promote or archive

Runs inside Citadel sandbox. Uses the same free-tier models
the agent uses for chat — no additional cost.

Usage:
    python3 skill_improver.py run           # One improvement cycle
    python3 skill_improver.py status        # Show skill performance
    python3 skill_improver.py history       # Show improvement history
    python3 skill_improver.py rollback <id> # Revert a skill change
    N)datetime)Path)AnyDictListOptionalTuplez[hyperagent] %(message)s)levelformatz.hermesskillsz2/opt/hermes-data/training/default/user-sessions.dbz//opt/hermes-data/training/default/tool-calls.dbz.hyperagentarchivezhistory.jsonlzconfig.json      zqwen/qwen3.6-plus:free
openrouterg333333?
   T)max_improvements_per_cyclemin_sessions_for_evaluationimprovement_modelimprovement_providertest_thresholddaily_budgetenabledc                   @   sl   e Zd ZdZdd Zdeeef fddZdee fddZ	dee fd	d
Z
dee fddZdd ZdS )SkillAnalyzerz3Analyzes skill performance from training databases.c                 C   s<   d | _ d | _t rtt| _ t rtt| _d S d S N)conv_dbtool_dbTRAINING_DBexistssqlite3connectTOOL_DBself r#   6/home/geodesix/Jetson/src/hyperagent/skill_improver.py__init__H   s   zSkillAnalyzer.__init__returnc                 C   s(   | j si S | j d }dd |D S )z3Get usage stats for each skill from tool call data.a  
            SELECT tool_name, COUNT(*) as uses,
                   SUM(CASE WHEN tool_response IS NOT NULL AND tool_response != '' THEN 1 ELSE 0 END) as successes
            FROM tool_calls
            GROUP BY tool_name
            ORDER BY uses DESC
        c                 S   sB   i | ]}|d  |d |d |d d kr|d |d  nd dqS )r         )uses	successessuccess_rater#   ).0rowr#   r#   r$   
<dictcomp>]   s    z1SkillAnalyzer.get_skill_usage.<locals>.<dictcomp>)r   executefetchall)r"   rowsr#   r#   r$   get_skill_usageP   s   
zSkillAnalyzer.get_skill_usagec           	      C   s   | j sg S | j d }i }|D ]\}}}||vr!g i d||< || d ||d qg }| D ]H\}}|t|d tdd |d D tdd |d D tdd |d D td	d |d D td
tdd |d D  d}|| q4|S )z/Extract quality signals from conversation data.z
            SELECT session_id, role, content
            FROM conversations
            ORDER BY session_id, turn_index
        )messagessignalsr3   rolecontentc                 s   0    | ]}|d  dkrd|d pd  v V  qdS )r6   userretryr7    Nlowerr,   mr#   r#   r$   	<genexpr>}      . z<SkillAnalyzer.get_session_quality_signals.<locals>.<genexpr>c                 s   s:    | ]}|d  dkrdD ]}||d pd  v V  qqdS )r6   r9   )thanksz	thank youperfectgreatawesomer7   r;   Nr<   )r,   r?   wr#   r#   r$   r@   ~   s   8 c                 s   r8   )r6   	assistanterrorr7   r;   Nr<   r>   r#   r#   r$   r@      rA   c                 s   s,    | ]}|d  dkrt |d pdV  qdS )r6   rG   r7   r;   N)lenr>   r#   r#   r$   r@      s   * r'   c                 s   s     | ]}|d  dkrdV  qdS )r6   rG   r'   Nr#   r>   r#   r#   r$   r@      s    )
session_id
turn_count	has_retry
has_thanks	has_erroravg_response_len)	r   r/   r0   appenditemsrI   anysummax)	r"   r1   sessionsrJ   r6   r7   resultsdatar4   r#   r#   r$   get_session_quality_signalsf   s*   

0z)SkillAnalyzer.get_session_quality_signalsc                 C   s   g }t  s|S t  D ]R}| sq|jt||d  |d  dd |dD tdd |dD d}|d }| rYz| }|d	d
 |d< W n	 t	yX   Y nw |
| q|S )z*List installed skills with their metadata.SKILL.mdzDESCRIPTION.mdc                 S   s   g | ]	}|  r|jqS r#   )is_filenamer,   fr#   r#   r$   
<listcomp>   s    z6SkillAnalyzer.get_installed_skills.<locals>.<listcomp>*c                 s   "    | ]}|  r| jV  qd S r   rZ   statst_sizer\   r#   r#   r$   r@           z5SkillAnalyzer.get_installed_skills.<locals>.<genexpr>)r[   pathhas_skill_mdhas_descriptionfiles
size_bytesNi  content_preview)
SKILLS_DIRr   iterdiris_dirr[   strrglobrS   	read_text	ExceptionrP   )r"   r   	skill_dir
skill_infoskill_mdr7   r#   r#   r$   get_installed_skills   s.   


z"SkillAnalyzer.get_installed_skillsc           	      C   s@  |   }|  }|  }g }| D ]3\}}|d dkrE|d dk rE|d|d| d|d dd	|d
  d|d  d	d|d  |d qdd |D }|ro|ddt| dt|tdt| t|t|dd dd |D }|r|ddt| dt|tdt| dt|id |jdd dd |S )z4Identify skills that could benefit from improvement.r)   r   r+   gffffff?low_success_ratezTool 'z' has .0%z success rate (r*   / calls)r'   )typetargetreasonpriorityrW   c                 S      g | ]}|d  r|qS )rL   r#   r,   sr#   r#   r$   r^          z>SkillAnalyzer.identify_improvement_targets.<locals>.<listcomp>
user_retryresponse_qualityz3 sessions had user retries (dissatisfaction signal))retry_counttotal_sessionsc                 S   r~   )rN   r#   r   r#   r#   r$   r^      r   error_responseserror_handlingz# sessions contained error responseserror_countc                 S   s   | d S )Nr}   r#   )tr#   r#   r$   <lambda>   s    z<SkillAnalyzer.identify_improvement_targets.<locals>.<lambda>T)keyreverse)r2   rX   ru   rQ   rP   rI   rT   sort)	r"   
tool_usagesession_signalsinstalled_skillstargets	tool_namestatsretry_sessionserror_sessionsr#   r#   r$   identify_improvement_targets   sD   *
		
	z*SkillAnalyzer.identify_improvement_targetsc                 C   s(   | j r| j   | jr| j  d S d S r   )r   closer   r!   r#   r#   r$   r      s
   
zSkillAnalyzer.closeN)__name__
__module____qualname____doc__r%   r   rn   r2   r   rX   ru   r   r   r#   r#   r#   r$   r   E   s      -r   c                	   @   sX   e Zd ZdZddededefddZdefd	d
Zdededee de	e fddZ
dS )SkillModifierz*Uses an LLM to propose skill improvements.Nmodelproviderapi_keyc                 C   s   || _ || _|p|  | _d S r   )r   r   _load_api_keyr   )r"   r   r   r   r#   r#   r$   r%      s   zSkillModifier.__init__r&   c                 C   sx   t d | j d }| r|  S t d d }| r:|  D ]}|dr9|	ddd    S q&dS )	zLoad API key from secrets.secretsz.keyzhermes-agentz.envzOPENROUTER_API_KEY==r'   r;   )
HERMES_HOMEr   r   rp   stripr   home
splitlines
startswithsplit)r"   key_fileenv_fileliner#   r#   r$   r      s   
zSkillModifier._load_api_keyskill_contentr{   session_examplesc              
   C   sJ  | j s
td dS d|dd }d|dd  d|d  d	|dd  d
}z`ddl}ddl}d}dd| j  d}	t| jd|dgdd	 }
|j
j||
|	d}| }|j
j|d|d}t| }|d d d d  W  d   W S 1 sw   Y  W dS  ty } ztd|  W Y d}~dS d}~ww )z6Ask the LLM to propose an improved version of a skill.z*No API key available for improvement modelNz

r   zWYou are a meta-agent improving an AI assistant's skills.

## Current Skill Content
```
i  z
```

## Problem Identified
r|   z

## Relevant Session Examples
u  

## Your Task
Propose an improved version of this skill that addresses the identified problem.
Return ONLY the improved skill content — no explanation, no markdown wrapping.
The output should be a drop-in replacement for the current skill file.

Focus on:
- Clearer instructions that reduce errors
- Better handling of edge cases
- More specific guidance for the agent
- Removing ambiguity that might cause retries

Keep the same format and structure. Make targeted improvements, not a complete rewrite.r   -https://openrouter.ai/api/v1/chat/completionsapplication/jsonBearer zContent-TypeAuthorizationr9   r5   r   r3   
max_tokensrW   headers   timeoutcontextchoicesmessager7   zLLM call failed: )r   loggerrH   joinurllib.requestssljsondumpsr   encoderequestRequestcreate_default_contexturlopenloadsreadr   rq   )r"   r   r{   r   examples_textprompturllibr   urlr   bodyreqctxresprW   er#   r#   r$   propose_improvement   sH   




(z!SkillModifier.propose_improvementr   )r   r   r   r   rn   r%   r   r   r   r   r   r#   r#   r#   r$   r      s
    &r   c                   @   s   e Zd ZdZdd ZdededefddZd	edefd
dZde	e
 fddZde
fddZddede	e
 fddZdefddZdS )SkillArchivez/Manages skill versions and rollback capability.c                 C   s    t jddd tjddd d S )NTparentsexist_ok)ARCHIVE_DIRmkdirHYPERAGENT_DIRr!   r#   r#   r$   r%   7  s   zSkillArchive.__init__
skill_name
skill_pathr&   c                 C   sR   | dt  d }t| }t| r't|| t	d| d|  |S )z<Backup current skill before modification. Returns backup ID._z%Y%m%d_%H%M%Sz
Backed up u    → )
r   nowstrftimer   r   r   shutilcopytreer   info)r"   r   r   	backup_id
backup_dirr#   r#   r$   backup_current;  s   zSkillArchive.backup_currentr   c                 C   st   t | }| std|  dS |ddd }t| }| r't| t|| t	d| d|  dS )	zRestore a skill from backup.zBackup not found: Fr   r(   r   zRolled back z from T)
r   r   r   rH   rsplitrk   r   rmtreer   r   )r"   r   r   r   rr   r#   r#   r$   rollbackF  s   
zSkillArchive.rollbackc                 C   sl   g }t  r4tt  ddD ]%}| r3||j|jddd | j	t
dd |dD d	 q|S )
z!List all archived skill versions.T)r   r   r(   r   c                 s   r`   r   ra   r\   r#   r#   r$   r@   `  rd   z,SkillArchive.list_backups.<locals>.<genexpr>r_   )idskill	timestampsize)r   r   sortedrl   rm   rP   r[   r   rb   st_mtimerS   ro   )r"   backupsdr#   r#   r$   list_backupsV  s   zSkillArchive.list_backupsentryc                 C   sT   t   |d< ttd}|t|d  W d   dS 1 s#w   Y  dS )zAppend to improvement history.r   a
N)r   r   	isoformatopenHISTORY_FILEwriter   r   )r"   r   r]   r#   r#   r$   record_improvementd  s   "zSkillArchive.record_improvement   limitc              	   C   sb   t  sg S t   d}g }|| d D ]}z
|t| W q tjy.   Y qw |S )zRead improvement history.r   N)	r   r   rp   r   r   rP   r   r   JSONDecodeError)r"   r   linesentriesr   r#   r#   r$   get_historyj  s   zSkillArchive.get_historyc                    s*   t  d t fdd| dD S )zCount improvements made today.z%Y-%m-%dc                 3   s&    | ]}| d d rdV  qdS )r   r;   r'   N)getr   )r,   r   todayr#   r$   r@   z  s    
z/SkillArchive.get_daily_count.<locals>.<genexpr>d   )r   r   r   rS   r   r!   r#   r  r$   get_daily_countw  s   zSkillArchive.get_daily_countN)r   )r   r   r   r   r%   rn   r   boolr   r   r   r   r   intr   r  r#   r#   r#   r$   r   4  s    r   c                
   @   s@   e Zd ZdZdefddZdedededee d	ef
d
dZ	dS )SkillEvaluatorzBEvaluates whether a proposed skill improvement is actually better.modifierc                 C   s
   || _ d S r   )r  )r"   r  r#   r#   r$   r%     s   
zSkillEvaluator.__init__original_contentimproved_contentr{   r   r&   c              
   C   s  d|dd  d|dd  d|d  d}zddl }ddl}d	}d
d| jj d}	t| jjd|dgdd }
|jj	||
|	d}|
 }|jj|d|dJ}t| }|d d d d  }d|v r|dd  }|dr|dd  }t|}t|dd|dddW  d   W S 1 sw   Y  W dS  ty } ztd|  dd | dW  Y d}~S d}~ww )!z5Ask the LLM to evaluate if the improvement is better.zQYou are evaluating a proposed improvement to an AI skill.

## Original Skill
```
Ni  z"
```

## Proposed Improvement
```
z!
```

## Problem Being Addressed
r|   a  

## Evaluate
Rate the improvement on a scale of 0.0 to 1.0:
- 0.0 = worse than original
- 0.5 = about the same
- 1.0 = significantly better

Consider:
1. Does it address the identified problem?
2. Could it introduce new problems?
3. Is it clear and unambiguous?
4. Is it a targeted fix, not an unnecessary rewrite?

Respond with ONLY a JSON object: {"score": 0.X, "reason": "brief explanation"}r   r   r   r   r   r9   r5      r   r   r   r   r   r   r7   z```r'   r      scorer;   )r  r|   zEvaluation failed: g      ?zEvaluation error: )r   r   r  r   r   r   r   r   r   r   r   r   r   r   r   r   r   floatr   rq   r   rH   )r"   r	  r
  r{   r   r   r   r   r   r   r   r   r   r   rW   responseresultr   r#   r#   r$   evaluate_improvement  sP   

	



(z#SkillEvaluator.evaluate_improvementN)
r   r   r   r   r   r%   rn   r   r   r  r#   r#   r#   r$   r    s    r  r&   c                  C   sp   t  r4z#tt } i tt| W  d   W S 1 sw   Y  W t S  ty3   Y t S w t S )zLoad hyperagent configuration.N)CONFIG_FILEr   r   DEFAULT_CONFIGr   loadrq   copy)r]   r#   r#   r$   load_config  s   
$r  configc                 C   sN   t jddd ttd}tj| |dd W d   dS 1 s w   Y  dS )zSave hyperagent configuration.Tr   rF   r(   )indentN)r   r   r   r  r   dump)r  r]   r#   r#   r$   save_config  s   "r  c                  C   s  t  } | ddstd dS t }| }|| d kr-td| d| d  d dS td	 t }| }|sFtd
 |  dS tdt	| d |dd D ]}td|d dd|d   qWt
| d | d }t|}g }|jr|jd }	dd |	D }d}
t| d | d | }|d| D ]}td|d   | }|std  ntdd |D |d }t|d }|d }| sq| }td |d!   ||||}|r||krtd" ||d! |d d#d$ qtd% |||||}td&|d' dd(|d   |d' | d) kr||d! t|}|| td*|d!  d+| d, z$dd-lm} |d.|d!  d/|d dd0  d1|d' dd2| d3	 W n
 tys   Y nw ||d! |d d4|d' |d |d5 |
d67 }
qtd7|d' dd8| d)   ||d! |d d9|d' |d d: q|  td;|
 d< dS )=z(Run one cycle of skill self-improvement.r   Tz)Hyperagent is disabled. Enable in config.Nr   zDaily budget reached (rx   z). Skipping.zAnalyzing skill performance...z9No improvement targets identified. Everything looks good.zFound z improvement targets:r     [r}   .2f] r|   r   r   zISELECT role, content FROM conversations ORDER BY turn_index DESC LIMIT 20c                 S   s&   g | ]\}}| d |dd  qS )z: Nr  r#   )r,   r6   r7   r#   r#   r$   r^     s   & z)run_improvement_cycle.<locals>.<listcomp>r   r   z
Processing: zNo installed skills to improve.c                 s   s    | ]	}|d  r|V  qdS )rf   Nr#   r   r#   r#   r$   r@   %  s    z(run_improvement_cycle.<locals>.<genexpr>re   rY   z#  Proposing improvement for skill: r[   z$  No improvement proposed. Skipping.	no_change)r   r{   statusz$  Evaluating proposed improvement...z	  Score: r      — r   u     ✓ PROMOTED — skill 'z' improved (backup: ))notify_ownerzImproved skill <b>z</b>
Reason: r  z
Score: z*
Rollback: <code>rook hyperagent rollback z</code>promoted)r   r{   r  r  r|   r   r'   u     ✗ REJECTED — score z below threshold rejected)r   r{   r  r  r|   z
Cycle complete: z improvements promoted.)r  r   r   r   r   r  r   r   r   rI   r   r  r   r/   r0   minru   nextr   r   rp   r   r   r  r   rn   
write_textnotifyr"  rq   )r  r   daily_countanalyzerr   r   r  	evaluatorr   r1   improvements_mademax_improvementsr{   r   r   r   
skill_filer	  r
  
evaluationr   r"  r#   r#   r$   run_improvement_cycle  s   


"


 

 
r0  c                  C   s  t  } t }t }td td| dd  td| d  td|  d| d	 d
 td| d  | }tdt|  |dd D ]}td|d ddt|d dd|d dd qO| }|rtd t	|
 dd ddd D ]1\}}dt|d d  d dt|d d    }td|d!d| d|d d"d#|d$  d%	 q| }	|	rtd&t|	  |	dd' D ]}
td(|
d) d*d+|
d,   q| }|rtd-t|  |dd' D ]}td|d.   q|  dS )/z5Show current skill performance and hyperagent status.u    
⚔ Rook Hyperagent — Status
zEnabled:          r   TzModel:            r   zDaily budget:     rx   r   z iterations used todayzThreshold:        r   z
Installed skills: Nr     r[   30 rh   3z files  ri   z>8z bytesz
Tool performance:c                 S   s   | d d  S )Nr'   r)   r#   )xr#   r#   r$   r     s    zshow_status.<locals>.<lambda>)r   u   █r+   u   ░25rw   z  (r)   ry   z
Improvement targets: r   r  r}   r  r  r|   z
Archived versions: r   )r  r   r   printr   r  ru   rI   r2   r   rQ   r  r   r   r   )r  r   r*  r   r   r   r[   r   barr   r   r   br#   r#   r$   show_statusm  s:    4$,0 r:  c                  C   s   t  } | d}|std dS td t|D ]R}|ddkr#dn
|ddkr,d	nd
}|dd}t|ttfrBd|ddnd}td| d|dddd  d|dd d|dddd  | 	 qdS )zShow improvement history.r   zNo improvement history yet.Nu-   
⚔ Rook Hyperagent — Improvement History
r  r#  u   ✓r$  u   ✗u   —r  r;   z (r  r!  r1  z [r   ?   r  r   r   r{   <   )r   r   r7  reversedr   
isinstancer  r  )r   historyr   status_iconr  	score_strr#   r#   r$   show_history  s   
( NrC  c                  C   sJ  dd l } | jdd}|jdd}|jddd |jd	d
d |jddd |jddd}|jddd |jddd |jddd | }|jdkrOt  d S |jd	krYt  d S |jdkrct	  d S |jdkrst
 }||j d S |jdkrt }d|d< t| td d S |jdkrt }d|d< t| td d S |  d S )Nr   u*   Rook Hyperagent — Skill Self-Improvement)descriptioncommand)destrunzRun one improvement cycle)helpr  z,Show skill performance and hyperagent statusr@  zShow improvement historyr   zRevert a skill changer   zBackup ID to restoreenablezEnable hyperagentdisablezDisable hyperagentTr   zHyperagent enabled.FzHyperagent disabled.)argparseArgumentParseradd_subparsers
add_parseradd_argument
parse_argsrE  r0  r:  rC  r   r   r   r  r  r7  
print_help)rK  parsersubrollback_cmdargsr   r  r#   r#   r$   main  s>   








rV  __main__)+r   hashlibr   loggingosr   r   systimer   pathlibr   typingr   r   r   r   r   basicConfigINFO	getLoggerr   r   r   r   rk   r   r    r   r   r   r  r  r   r   r   r  r  r  r0  r:  rC  rV  r#   r#   r#   r$   <module>   sZ   
 VPW ')
