
    6jR                       U 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mZ ddl	m
Z
mZmZ  ej        e          Z	 ddlmZmZmZmZmZ n# e$ r dZdZdZdZdZY nw xY weZi Zd	ed
<   dAdZdBdZdCdZ G d d          ZdddDdZdEdZdddddddddFd,Z dd-dGd/Z!dHd1Z"dId2Z#dddJd3Z$dddKd6Z%	 dLd7dd8dMd;Z&dddNd<Z'ddd=dOd@Z(dS )Pz1WebUI bridge for Hermes persistent session goals.    )annotationsN)Path)AnyDictOptional)CONTINUATION_PROMPT_TEMPLATEDEFAULT_MAX_TURNSGoalManager	GoalState
judge_goal    zdict[str, Any]	_DB_CACHEreturnintc            	        	 ddl m}  t          | di           pi }t          |t                    r|                    di           ni }t          |t                    st          t          pd          S t          dt          |                    dt          pd          pd                    S # t          $ r t          t          pd          cY S w xY w)zHReturn the configured /goal turn budget, defaulting to Hermes' 20 turns.r   )configcfggoalsr      	max_turns)
apir   getattr
isinstancedictgetr   r	   max	Exception)_configr   	goals_cfgs      /root/hermes-webui/api/goals.py_default_max_turnsr"   "   s    	,))))))gub))/R,6sD,A,AICGGGR(((r	)T** 	0(.B///1c)--5F5L"MMSQSTTUUU , , ,$*+++++,s   A0B- 39B- - CC
session_idstrc                    d|  S )Nzgoal: )r#   s    r!   	_meta_keyr'   0   s    :    profile_home
str | Pathc                p   t          |                                                                           }t          |          }t                              |          }||S 	 ddlm}  ||dz            }n4# t          $ r'}t          
                    d||           Y d}~dS d}~ww xY w|t          |<   |S )a  Return a SessionDB pinned to *profile_home*, without reading HERMES_HOME.

    The upstream Hermes GoalManager persists through hermes_cli.goals.load_goal(),
    which resolves SessionDB from process-global HERMES_HOME. WebUI sessions are
    profile-scoped and can run concurrently, so the WebUI bridge uses an explicit
    state.db path whenever the caller provides the session's profile home.
    Nr   )	SessionDBzstate.db)db_pathz-GoalManager profile DB unavailable for %s: %s)r   
expanduserresolver$   r   r   hermes_stater,   r   loggerdebug)r)   homekeycachedr,   dbexcs          r!   _profile_dbr8   4   s     ((**2244D
d))C]]3F******Ytj0111   DdCPPPttttt IcNIs   "A8 8
B)B$$B)c                      e Zd ZdZddd)d
Zed             Zd Zd*dZd+dZ	d+dZ
d,dZddd-dZd.d/dZddd0d Zd*d!Zdd"d1d&Zd2d(ZdS )3_ProfileGoalManagerzHSmall WebUI-local GoalManager adapter with explicit profile persistence.r   )default_max_turnsr#   r$   r)   r*   r;   r   c                  t           t          d          || _        t          |                                                                          | _        t          |pt          pd          | _	        | 
                                | _        d S )NzHermes goal state unavailabler   )r   RuntimeErrorr#   r   r.   r/   r)   r   r	   r;   _load_state)selfr#   r)   r;   s       r!   __init__z_ProfileGoalManager.__init__O   su    >???$ ..99;;CCEE!$%6%Q:K%Qr!R!Rjjllr(   c                    | j         S N)r?   r@   s    r!   statez_ProfileGoalManager.stateW   s
    {r(   c                   t          | j                  }|| j        sd S 	 |                    t	          | j                            }n3# t
          $ r&}t                              d|           Y d }~d S d }~ww xY w|sd S 	 t          j	        |          S # t
          $ r,}t          
                    d| j        |           Y d }~d S d }~ww xY w)Nz'GoalManager profile get_meta failed: %sz1GoalManager profile state parse failed for %s: %s)r8   r)   r#   get_metar'   r   r1   r2   r   	from_jsonwarning)r@   r6   rawr7   s       r!   r>   z_ProfileGoalManager._load[   s    *++:T_:4	++i8899CC 	 	 	LLBCHHH44444	  	4	&s+++ 	 	 	NNNPTP_adeee44444	s/   'A	 	
A9A44A9B 
C!CCr   Nonec                $   t          | j                  }|	| j        r|d S 	 |                    t	          | j                  |                                           d S # t          $ r&}t                              d|           Y d }~d S d }~ww xY w)Nz'GoalManager profile set_meta failed: %s)	r8   r)   r#   set_metar'   to_jsonr   r1   r2   )r@   rE   r6   r7   s       r!   _savez_ProfileGoalManager._savel   s    *++:T_:F	IKK	$/22EMMOODDDDD 	I 	I 	ILLBCHHHHHHHHH	Is   :A 
B)B

Bboolc                4    | j         d uo| j         j        dk    S )Nactiver?   statusrD   s    r!   	is_activez_ProfileGoalManager.is_activeu   s    {$&I4;+=+IIr(   c                0    | j         d uo| j         j        dv S )N)rR   pausedrS   rD   s    r!   has_goalz_ProfileGoalManager.has_goalx   s    {$&U4;+=AU+UUr(   c                2   | j         }|	|j        dv rdS |j         d|j         d}|j        dk    rd| d|j         S |j        dk    r"|j        r
d	|j         nd
}d| | d|j         S |j        dk    rd| d|j         S d|j         d| d|j         S )Ncleared*No active goal. Set one with /goal <text>./z turnsrR      ⊙ Goal (active, ): rW       — r      ⏸ Goal (paused, done   ✓ Goal done (Goal (, )r?   rT   
turns_usedr   goalpaused_reason)r@   sturnsextras       r!   status_linez_ProfileGoalManager.status_line{   s    K9L00??<55!+5558x:::!&:::8x12H-AO---bEAAuAAAAA8v7U77qv777666E66af666r(   N)r   rg   r   Optional[int]c                  |pd                                 }|st          d          t          |dd|rt          |          n| j        t          j                    d          }|| _        |                     |           |S )Nr   zgoal text is emptyrR   r   g        )rg   rT   rf   r   
created_atlast_turn_at)strip
ValueErrorr   r   r;   timer?   rO   )r@   rg   r   rE   s       r!   setz_ProfileGoalManager.set   s    
!!## 	31222(1Mc)nnnt7My{{
 
 
 

5r(   user-pausedreasonc                    | j         sd S d| j         _        || j         _        |                     | j                    | j         S )NrW   )r?   rT   rh   rO   )r@   rv   s     r!   pausez_ProfileGoalManager.pause   sA    { 	4%$*!

4;{r(   T)reset_budgetry   c                   | j         sd S d| j         _        d | j         _        |rd| j         _        |                     | j                    | j         S )NrR   r   )r?   rT   rh   rf   rO   )r@   ry   s     r!   resumez_ProfileGoalManager.resume   sS    { 	4%$(! 	'%&DK"

4;{r(   c                r    | j         d S d| j         _        |                     | j                    d | _         d S )Nr[   )r?   rT   rO   rD   s    r!   clearz_ProfileGoalManager.clear   s8    ;F&

4;r(   user_initiatedlast_responser   Dict[str, Any]c                  | j         }||j        dk    r|r|j        nd dd ddddS |xj        dz  c_        t          j                    |_        t
          d\  }}n't          |j        t          |pd                    \  }}||_        ||_	        |d	k    r(d	|_        | 
                    |           d	dd d	|d
| dS |j        |j        k    rNd|_        d|j         d|j         d|_        | 
                    |           ddd d|d|j         d|j         ddS | 
                    |           dd|                                 d|d|j         d|j         d| dS )NrR   Finactiveno active goalr   rT   should_continuecontinuation_promptverdictrv   messager   )continuezgoal judge unavailablerb   u   ✓ Goal achieved: rW   zturn budget exhausted (r]   )r   u   ⏸ Goal paused — zD turns used. Use /goal resume to keep going, or /goal clear to stop.Tu   ↻ Continuing toward goal (r_   )r?   rT   rf   rs   rp   r   rg   r$   last_verdictlast_reasonrO   r   rh   next_continuation_prompt)r@   r   r   rE   r   rv   s         r!   evaluate_after_turnz'_ProfileGoalManager.evaluate_after_turn   s   =ELH44*/9%,,T#('+%*   	A!Y[[BOGVV(S9L"5M5MNNOGV$"f!ELJJu #('+! 999   u..#EL"aE<L"a"au"a"a"aEJJu"#('+% N5+; N Neo N N N
 
 
 	

5##'#@#@#B#B!ee6Feeee]cee
 
 	
r(   Optional[str]c                r    | j         r| j         j        dk    rd S t          j        | j         j                  S )NrR   )rg   )r?   rT   r   formatrg   rD   s    r!   r   z,_ProfileGoalManager.next_continuation_prompt   s9    { 	dk0H<<4+28HIIIIr(   )r#   r$   r)   r*   r;   r   )r   rK   )r   rP   )r   r$   )rg   r$   r   rm   )ru   )rv   r$   )ry   rP   )r   r$   r   rP   r   r   )r   r   )__name__
__module____qualname____doc__rA   propertyrE   r>   rO   rU   rX   rl   rt   rx   r{   r}   r   r   r&   r(   r!   r:   r:   L   su       RR^` # # # # # #   X  "I I I IJ J J JV V V V7 7 7 7 <@            .2          QU :
 :
 :
 :
 :
 :
xJ J J J J Jr(   r:   r)   str | Path | Nonec                   t           d S |rgt           t          u rYt          R	 t          | |t	                                S # t
          $ r&}t                              d|           Y d }~d S d }~ww xY wt          | t	                                S )N)r#   r)   r;   z*Profile-scoped GoalManager unavailable: %s)r#   r;   )r
   _NativeGoalManagerr   r:   r"   r   r1   r2   )r#   r)   r7   s      r!   _managerr      s    t 	'999i>S	&%)"4"6"6   
  	 	 	LLEsKKK44444	 *@R@T@TUUUUs   A   
A0
A++A0rE   r   Optional[Dict[str, Any]]c                4   | d S t          | dd          pdt          | dd          pdt          t          | dd          pd          t          t          | dd          pd          t          | dd           t          | dd           t          | d	d           d
S )Nrg   r   rT   rf   r   r   r   r   rh   )rg   rT   rf   r   r   r   rh   )r   r   )rE   s    r!   _state_payloadr     s    }tvr**0b%2..4"'%q99>Q??Q77<1==~t<<umT:: >>  r(   T)okrE   errorkickoff_promptdecisionmessage_keymessage_argsr   rP   actionr   r   
str | Noner   r   Dict[str, Any] | Noner   r   list[Any] | Noner   c        	            t          |           ||t          |          d}	|r||	d<   |r||	d<   |||	d<   |r||	d<   |d |D             |	d<   |	S )N)r   r   r   rg   r   r   r   r   c                    g | ]}||S rC   r&   ).0as     r!   
<listcomp>z_payload.<locals>.<listcomp>-  s    IIIa1====r(   r   )rP   r   )
r   r   r   rE   r   r   r   r   r   bodys
             r!   _payloadr     s     2hhu%%	 D  W 0!/#Z *)]II<III^Kr(   )default_messager   c               \   |d}| |ddS t          t          | dd          pd                                          }|dv r|ddS t          t          | dd	          pd	          }t          t          | d
d	          pd	          }t          t          | dd          pd          }|dk    rd| d| d| d|||gdS |dk    r<t          t          | dd          pd          }d| d| |rd|z   nd d| d||||gdS |dk    rd| d| d| d|||gdS d| d| d| d| ||||gdS )z>Build localized-status style payload fields from a goal state.Nr\   goal_status_none)r   r   rT   r   rZ   rf   r   r   rg   rR   r^   r]   z	 turns): goal_status_active)r   r   r   rW   rh   ra   r`   r_   goal_status_pausedrb   rc   goal_status_donerd   re   )r   r   )r$   r   rq   r   )rE   r   rT   rf   r   rg   rv   s          r!   _goal_status_payloadr   1  s   F}*;MNNN"--344::<<F*;MNNNWUL!449::JGE;227a88Iwufb))/R00DSJSSSSTSS/'D9
 
 	

 WUOR88>B??qJqqqX^DfGfDTDTdfqqkoqq/'FDA
 
 	

 JJJiJJDJJ-'D9
 
 	
 HFGGjGG9GGGGY=  r(   tuple[int, int]c                    | sdS t          j        d|           }|sdS 	 t          |                    d                    t          |                    d                    fS # t          $ r Y dS w xY w)z?Best-effort extraction for continuation messages like '(1/20)'.r   r   z\((\d+)\s*/\s*(\d+)\)r      )researchr   groupr   )r   matchs     r!    _extract_goal_turns_from_messager   V  s     tI.88E t5;;q>>""CA$7$777   tts   AA# #
A10A1c                   t          | t                    s| S t          |                     d          pd                                          }t          |                     d          pd                                          }t          t          |dd          pd          }t          t          |dd          pd          }||fdk    r4t          t          |                     d          pd                    \  }}|d	k    r
i | d
|gdS |dk    ri | d||gdS |                     d          ri | d|||gdS | S )z<Attach goal message i18n key/args to an evaluation decision.rT   r   rv   rf   r   r   r   r   rb   goal_achieved)r   r   rW   goal_paused_budget_exhaustedr   goal_continuing)r   r   r$   r   rq   r   r   r   )r   rE   rT   rv   rf   r   s         r!   _goal_decision_payloadr   c  s   
 h%% h''-2..4466Fh''-2..4466FWUL!449::JGE;227a88II&(( @X\\R[E\E\Eb`bAcAc d d
I

*#H
 
 
 	

 

9'3
 
 
 	

 ||%&& 


,'F;
 
 
 	

 Or(   c                   t          t          | pd          |          }|dS t          j        t	          |dd                    S )zEReturn a deep copy of current goal state for rollback before kickoff.r   r   NrE   )r   r$   copydeepcopyr   )r#   r)   mgrs      r!   goal_state_snapshotr     sG    
3z'R((|
D
D
DC
{t=gt44555r(   snapshotrK   c                  t          t          | pd          |          }|dS |(	 |                                 n# t          $ r Y nw xY wdS t	          |t
                    r||_        |                    |           dS 	 ddlm	}  |t          | pd          |           dS # t          $ r'}t                              d| |           Y d}~dS d}~ww xY w)z?Restore a prior goal state after kickoff stream creation fails.r   r   Nr   )	save_goalz$Goal state restore failed for %s: %s)r   r$   r}   r   r   r:   r?   rO   hermes_cli.goalsr   r1   r2   )r#   r   r)   r   r   r7   s         r!   restore_goal_stater     s$   
3z'R((|
D
D
DC
{	IIKKKK 	 	 	D	#*++ 
		(N......	#j&B''22222 N N N;ZMMMMMMMMMNs'   = 
A
	A
!B& &
C0CCF)stream_runningr)   argsr   c          
        t          | pd                                          }|st          dddd          S t          ||          }|t          ddd	d
          S t          |pd                                          }|                                }|r|dk    r/t          |dd          }t          |          }	t          d-d|d|	S |dk    r[|                    d          }|t          ddddd          S t          dd|j         dt          |j                  g|          S |dk    rZ|	                                }|t          ddddd          S t          dd|j         ddt          |j                  g|          S |dv r_t          |                                          }
|                                 t          d|
rdnd |
rd!ndt          |dd          "          S |rt          dd#d$d%          S 	 |                    |          }n-# t          $ r }t          dd#d&d'|           cY d}~S d}~ww xY wt          d#d(|j         d)|j         d*d+|j        |j        g||j        ,          S ).a  Return the WebUI response payload for a /goal command.

    Mirrors the gateway command semantics:
    - /goal or /goal status shows status
    - /goal pause pauses
    - /goal resume resumes without auto-starting a turn
    - /goal clear|stop|done clears
    - /goal <text> sets a new active goal and returns kickoff_prompt so the
      caller can start the first normal user-role turn immediately.
    r   Fr   missing_sessionzsession_id required)r   r   r   r   r   Nunavailablez"Goals unavailable on this session.rT   rE   )r   rE   rx   ru   )rv   no_goalzNo goal set.goal_no_goal)r   r   r   r   r   u   ⏸ Goal paused: goal_paused)r   r   r   r   rE   r{   zNo goal to resume.u   ▶ Goal resumed: z6
Send a new message, or type continue, to kick it off.goal_resumed)r}   stoprb   r}   zGoal cleared.zNo active goal.goal_cleared)r   r   r   rE   rt   agent_runningub   Agent is running — use /goal status / pause / clear mid-run, or /stop before setting a new goal.invalid_goalzInvalid goal: u   ⊙ Goal set (z-turn budget): u   
I'll keep working until the goal is done, you pause/clear it, or the budget is exhausted.
Controls: /goal status · /goal pause · /goal resume · /goal cleargoal_set)r   r   r   r   rE   r   r&   )r$   rq   r   r   lowerr   r   rx   rg   r{   rP   rX   r}   rt   rr   r   )r#   r   r   r)   sidr   textr   rE   status_payloadhadr7   s               r!   goal_command_payloadr     sl   " jB


%
%
'
'C j58IShiiii
3\
2
2
2C
{5Ostttttzr??  ""DJJLLE H5H$$Wd++-e44GxuGGGGG			//=&*    4
44%ej//*
 
 
 	
 

=,*    HUZ H H H 'ej//*	
 	
 	
 		
 )))3<<>>""		'*AOO0A*-A>#w--	
 
 
 	
  	
!6
 
 
 	
f f f f5nNd_bNdNdeeeeeeeeef SU_ S SUZ S S S ouz2z   s   H 
I'I<IIc               &   t          | pd                                          }|sdS t          ||          }|dS 	 t          |                                          S # t
          $ r'}t                              d||           Y d}~dS d}~ww xY w)zEReturn True when the session has an active standing goal to evaluate.r   Fr   Nz1goal active-state check failed for session=%s: %s)r$   rq   r   rP   rU   r   r1   r2   )r#   r)   r   r   r7   s        r!   has_active_goalr     s     jB


%
%
'
'C u
3\
2
2
2C
{uCMMOO$$$   H#sSSSuuuuus    A 
B)BB)r   r)   r   r   c          	        t          | pd                                          }|s	dddddddS t          ||          }|	dddddddS 	 |                                s't	          t	          |d	d          d
d          ddddddS |                    t          |pd          |          }nQ# t          $ rD}t                              d||           dddddt          |          j
         ddcY d}~S d}~ww xY wt          |t                    si }|                    dd           |                    dd           |                    dd           t          |          }t          |t	          |d	d                    }|S )z<Evaluate a completed turn against the standing goal, if any.r   NFr   zmissing session_idr   r   zgoals unavailablerE   rT   r   r~   z)goal evaluation failed for session=%s: %sr   zgoal evaluation failed: r   r   r   )r$   rq   r   rU   r   r   r   r1   r2   typer   r   r   
setdefaultr   )r#   r   r   r)   r   r   r   r7   s           r!   evaluate_goal_after_turnr   (  s    jB


%
%
'
'C 
$#'!*
 
 	
 3\
2
2
2C
{$#'!)
 
 	

}} 	!'#w"="=xNN#('+%*   **3}/B+C+CTb*cc 	
 	
 	
@#sKKK$#'Ec1CEE
 
 	
 	
 	
 	
 	
 	
	
 h%% )5111-t444	2&&&H~~H%hWd0K0KLLHOs$   :B. &B. .
C<89C71C<7C<)r   r   )r#   r$   r   r$   )r)   r*   )r#   r$   r)   r   )rE   r   r   r   )r   rP   r   r$   r   r$   rE   r   r   r   r   r   r   r   r   r   r   r   r   r   )rE   r   r   r   r   r   )r   r$   r   r   )r   r   rE   r   r   r   )r#   r$   r)   r   r   r   )r#   r$   r   r   r)   r   r   rK   )r   )
r#   r$   r   r$   r   rP   r)   r   r   r   )r#   r$   r)   r   r   rP   )
r#   r$   r   r$   r   rP   r)   r   r   r   ))r   
__future__r   r   loggingr   rs   pathlibr   typingr   r   r   	getLoggerr   r1   r   r   r	   r
   r   r   r   r   r   __annotations__r"   r'   r8   r:   r   r   r   r   r   r   r   r   r   r   r   r&   r(   r!   <module>r      s@   7 7 7 " " " " " "   				        & & & & & & & & & &		8	$	$                  #% IJJJ !	    , , , ,          0eJ eJ eJ eJ eJ eJ eJ eJP DH V V V V V V       !%&*"%)     > GK " " " " " "J
 
 
 
       F OS 6 6 6 6 6 6 ]a N N N N N N2 m !&*m m m m m mf '+     .  &*8 8 8 8 8 8 8 8s   A
 
AA