
    Wj                        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m	Z	 ddl
mZ  e	e      j                         j                  j                  Zedz  Zedz  ZddZ G d	 d
      ZddZddZy)u  Self-surgery control channel.

Claude (the conversation, or any background subagent) drives the running
app by writing files into control/ — natural for a model with file tools:

  control/config.json   hot-reload settings, applied within ~0.5s, no
                        restart: {"voice": "am_adam", "speed": 1.1}
  control/say.txt       speak this text aloud right now (lets background
                        workers announce results the moment they finish)
  control/restart       ask the app to restart itself. The new code is
                        py_compile-validated FIRST — a broken edit refuses
                        to restart instead of bricking the assistant. The
                        Claude session resumes after restart, so the
                        conversation continues with memory intact.

The watcher applies commands only when the app isn't mid-response, so a
restart requested during a reply lands after the sentence finishes.
    )annotationsN)Path)emitcontrol
session_idc                 @   t        t        dz        gt        dz  j                  d      D  cg c]  } t        |        c} z   }t        j                  t
        j                  ddg|ddd      }|j                  d	k(  rd
S |j                  j                         S c c} w )z?Compile-check the app's own source. None = OK, else error text.main.pyvoicez*.pyz-m
py_compileT   )capture_outputtexttimeoutr   N)
strPROJECTglob
subprocessrunsys
executable
returncodestderrstrip)pfilesprocs      ;/Users/ahmed/devFolder/Ultron/claude-voice/voice/control.py_validate_coder   $   s    9$%& 7*008*81A8* E >>	|4e4$D ??a'4@T[[->->-@@*s   Bc                      e Zd ZdZddZddZddZddZddZddZ	ddZ
dd	Zdd
ZddZddZddZddZddZddZddZy)ControlWatcherz<Polls control/ and applies commands against the running app.c                   || _         d | _        d| _        d | _        t        j                  d       t        dz  }|j                         r;	 t        j                  |j                         xs d      }|r| j                  |       dD ]  }t        |z  j                  d	        y # t        $ r}t        d| d       Y d }~>d }~ww xY w)
NFTexist_okconfig.json{}z       [startup config error: ])restartsay.txtr$   
report.txtmute	show.jsontext_input.jsonwindow.jsonscreenshot.jsonscreenshot.donegestures.json
watch.jsoncard_action.jsonemail_action.jsonloc_bind.jsonagent_action.jsonentity_action.jsonmemory_query.jsonmemory_action.jsongraph_query.jsongraph_action.jsonstorage_query.jsonstorage_action.json
missing_ok)app	_gestures_gestures_on_watcherCONTROLmkdiris_filejsonloads	read_text_apply_config	Exceptionprintunlink)selfr?   cfgdataenames         r   __init__zControlWatcher.__init__3   s    !t$ %;;==zz#--/"9T:&&t,BD t^##t#4B  =6qc;<<=s   :B( (	C
1CC
c                   K   	 	 t        j                  d       d {    	 | j                          d {    77 7 # t        $ r}t	        d| d       Y d }~"d }~ww xY w# | j                          w xY ww)Ng      ?z       [control error: r&   )asynciosleep_tickrJ   rK   _stop_gestures)rM   rP   s     r   r   zControlWatcher.runN   ss     	"mmC(((:**,&& (&  :3A3a899: !sU   A:A% <A% A  >A  A% A   	A"	AA% A""A% %A77A:c                6  K   t         dz  }|j                         rHt        j                  |j	                         xs d      }|j                          | j                  |       t         dz  }|j                         r|j	                         j                         }|j                          |r| j                  j                  rt        j                  | j                  j                  j                  |d d        d {   }| j                  j                  j                  |       t        d|        t!        d|       t         dz  }|j                         r|j	                         }|j                          	 t        j                  |      }|r|j%                  d	      s|j%                  d
      r| j                  j                  j'                          t        j(                  | j                  j+                  |j%                  d	d      |j%                  d
      xs g t-        |j%                  dd                         t         dz  }|j                         r|j	                         j                         j/                         }	|j                          |	D ]F  }
|
j                         s| j                  j0                  j3                  |
j                                H t         dz  }|j                         r|j	                         }|j                          	 t        j                  |      }|j%                  d      dk(  rddlm} t9        |j%                  d      xs d      }|j:                  j=                  |       d {   }t        d| d       | j                  j0                  j3                  d| d| d       t         dz  }|j                         r1|j                          ddlm }m!}  |        t!        d |              t         dz  }|j                         ra|j	                         }|j                          	 t        j                  |      }t!        d |!       t        d"|j%                  d       d       t         d#z  }|j                         r|j	                         }|j                          	 t        j                  |      }t!        d$t9        |j%                  d%d            t9        |j%                  d&d            '       t        d(|j%                  d%       d       t         d)z  }|j                         rS|j	                         }|j                          	 t        j                  |xs d      }| jE                  |       d {    t         d*z  }|j                         rS|j	                         }|j                          	 t        j                  |xs d      }| jG                  |       d {    t         d+z  }|j                         rS|j	                         }|j                          	 t        j                  |xs d      }| jI                  |       d {    t         d,z  }|j                         r|j	                         }|j                          	 t        j                  |xs d      }dd-lm%} t        j                  |jL                  t9        |j%                  d.d            j                                d {    t         d/z  }|j                         rS|j	                         }|j                          	 t        j                  |xs d      }| jO                  |       d {    t         d0z  }|j                         r=|j                          dd-lm%} t        j                  |jP                         d {    t         d1z  }|j                         rS|j	                         }|j                          	 t        j                  |xs d      }| jS                  |       d {    t         d2z  }|j                         r=|j                          dd3lm*} t        j                  |jV                         d {    t         d4z  }|j                         rS|j	                         }|j                          	 t        j                  |xs d      }| jY                  |       d {    t         d5z  } | j                         r| j	                         }| j                          	 t        j                  |xs d      }dd6lm-}! t        j                  |!j\                  t9        |j%                  d7d            j                         t9        |j%                  d8d            j                         t9        |j%                  d9d            j                                d {   }"|"r;| j                  j_                          t        d:|" d;|j%                  d7       d       t         d=z  }$|$j                         r|$j	                         }|$j                          	 t        j                  |xs d      }t9        |j%                  d%      xs d>      }%|j%                  d?      }&dd@lm0}' t        j                  |'jb                  |%|&       d {   \  }(})t         dAz  je                  t        jf                  |(|(r|%nd |)dB             t!        dC|(|(r|%ndD       t        dE|( dF|)xs |% d       t         dGz  }*|*j                         rK|*j	                         }|*j                          	 t        j                  |xs d      }| ji                  |       t         dHz  }+|+j                         rd|+j	                         }|+j                          	 t        j                  |xs d      }| jk                  t-        |j%                  dI                   nQ| jl                  rE| jn                  | jn                  jq                         d| _6        t        dJ       t!        dKd       t         dLz  },|,j                         r)|,j                          | js                          d {    y y 7 # t"        $ r d }Y Qw xY w# t"        $ r i }Y 	w xY w7 	n# t"        $ r Y yw xY w# t"        $ r Y w xY w# t"        $ r i }Y w xY w7 # t"        $ r i }Y Dw xY w7 4# t"        $ r i }Y w xY w7 # t"        $ r i }Y w xY w7 J# t"        $ r i }Y w xY w7 7 # t"        $ r i }Y Yw xY w7 I7 # t"        $ r i }Y w xY w7 7 # t"        $ r}#t        d<|# d       Y d }#~#d }#~#ww xY w# t"        $ r i }Y Kw xY w7 # t"        $ r i }Y Qw xY w# t"        $ r i }Y w xY w7 Lw)MNr$   r%   r(   iX  zNOTIFY notify)r   r,   r   attachments 
want_voiceFr)   r5   actionkillr   )
agent_poolidlatestz       [agent kill: r&   z$(Ahmed killed the background agent 'u   ' from the HUD himself — zD Acknowledge it in one short line; don't restart it unless he asks.)r*   )_toggle_muteis_mutedmutedonr-   window)commandz       [window: r+   showpathtitle)rj   rk   z       [show: r2   r3   r6   r7   memory_controlqr8   r9   r:   r;   storage_controlr<   r4   )locationplacelabelssidz       [location: bound z -> z       [loc_bind failed: r.   z/tmp/jarvis_screen.pngdisplay)screenr/   )okrj   error
screenshot)rw   rj   z       [screenshot: ok= r1   r0   rf   z!       [gestures: tracker exited]gesturesr'   ):rC   rE   rF   rG   rH   rL   rI   r   r?   ttsrT   	to_threadsynthspeakerplayrK   r   rJ   getcutcreate_taskhandle_textbool
splitlinesinbox
put_nowaitr
   r_   r   poolr^   voice.audio_iorb   rc   _card_action_email_action_entity_actionrm   refresh_panel_memory_actionrefresh_graph_graph_actionrp   ri   _storage_actionrq   bind_write_statusrv   capture
write_textdumps_set_watcher_set_gesturesrA   r@   poll_restart)-rM   rN   rO   sayr   pcmtipayloadreportlineslineactr_   targetoutr*   rb   rc   wincmdri   cardmailentmqrm   magqgasqrp   salbrq   fprP   shotrj   ru   rv   rw   errwatchgestr'   s-                                                r   rV   zControlWatcher._tickY   s|    %;;=::cmmo56DJJLt$	!;;===?((*DJJL#--dhhll.@.@$t*MM  %%c*v&'XD) ((::<llnGIIKzz'* &)TXXm-D  $$&##DHH$8$8HHVR(HH]+1r,67%  <'>>$$&,,.99;EMMO::<HHNN--djjl;  ++;;=mmoGJJLzz'* xx!V+,TXXd^7x8&OO0088,SE34)):6( C''*e ,89: <<>KKM=NXZ( %;;=mmoGJJLjj)Xs+():(;1=>
 $<<>nn&GKKMzz'*V#dhhvr&:";txx457txx'7&8:; ++<<>nn&GKKMzz'/T2 ##D))) ,,<<>nn&GKKMzz'/T2 $$T*** ,,;;=mmoGJJLzz'/T2 %%d+++
 **::<llnGIIKzz'/T2 -##N$@$@$'b(9$:$@$@$BD D D ++::<llnGIIKzz'/T2 %%d+++
 ))::<IIK,##N$@$@AAA **::<llnGIIKzz'/T2 $$T*** ++::<IIK-##O$8$8999 ,,::<llnGIIKzz'/T2 &&t,,, &::<llnGIIK8zz'/T2*",,MM3txx'<#=#C#C#E"-.446,-3357 7 HH**,4RDTXXg=N<OqQR **<<>nn&GKKMzz'/T2 txx'C+CDDhhy)G$#--fnndGLLGB((44TZZ244#F6H I"242>+B4qQ?@
 ,&==?oo'GLLNzz'/T2 d# (<<>nn&GKKMzz'/T2 tDHHTN34&$..*=*=*?*K %D56&I%??NN--/!! c N  >  
 92      )  *  +  D  + B  * :  ,7  81!A6778  
 M     "s  C;x=s>A=x<s Dx0A.xs& 4Ax
s8CxA s; 9xA3t ;9x5t x"t-#=x!t0 :xu=xu &x:u;=x9u Ax"u,#=x!u/ :xvAx$v%=x#v <xvAx&v'=x%v >xv1>xB9v7 v4Av7 9xw  Ax9w/:Bxw2 /A
x:x B7x
xxs#x"s##x&s51x4s55x;	txtx	txtxt*&x)t**x0t?;x>t??xuxuxu)%x(u))x/u>:x=u>>xxvxvxxv.*x-v..x4v7 7	w wxwxw,(x+w,,x2x=x xxxxxxc                  K   t        |j                  dd            j                         }t        |j                  dd            j                         xs d}|dk(  rt        |j                  dd            j                         }t        |j                  dd            j                         }|dv re|rc	 d	d
l}d	dlm} |j                  |j                  |       d
{    | j                  j                  j                  d|rd| dndz   dz          y
y
d	dlm}	 t        |j                  dd            j                         }
|j                  d      xs i }|
r|	j                  |
      sy
t        |t               r2d|v r.t!        |      }|	j#                  |j                  d            |d<   t        |t               r|r|	j%                  |
|d       |dk(  ry
|dk(  rT|	j'                  |
       d
{   \  }}|rd| d| nd| d| }| j                  j                  j                  |       y
|dk(  r;|	j)                  |
       | j                  j                  j                  d| d       y
|d k(  r;|	j)                  |
       | j                  j                  j                  d!| d"       y
y
7 # t        $ r}t        d| d       Y d
}~y
d
}~ww xY w7 w)#zEApply a card action from the HUD (email/event draft, or a task tick).r]   r[   kinddrafttaskr`   r   )donecompleter   N)tasks_controlz)[task] Ahmed ticked off a task on the HUD: ""u    — it's done.z       [task done failed: r&   )composedraft_idfieldsrZ   F)reemiteditsendz$[compose] Ahmed clicked Send on the u    — z but it FAILED: cancelz[compose] Ahmed cancelled the u    draft — do not send it.closez[compose] Ahmed dismissed the z card without sending.)r   r   r   rT   r
   r   r}   	mark_doner?   r   r   rJ   rK   r   	get_draft
isinstancedict_parse_attachmentsupdate_draft
send_draftcancel_draft)rM   rO   r]   r   tidr   _ar   rP   r   didr   rw   r   notes                  r   r   zControlWatcher._card_actionz  s    TXXh+,224488FG,-335@ 6>dhhtR()//1Ctxx+,224D--#=(3,,}'>'>DDDHHNN--C,0Sa=b:<MNO
 !$((:r*+113(#)r'++C0
 fd#(?&\F$+$>$>

=)%+F=!fd#  fU ;VV#..s33GBOQ:4&cUK?v F$$'5*  HHNN%%d+x  %HHNN%%06PQSw  %HHNN%%06LMO I E ! =6qc;<<=. 4sP   B4K$7)J=  J:!7J= C!K$9K":C K$:J= =	KKK$KK$c                  K   ddl m} t        |j                  dd            j	                         }t        |j                  dd            j	                         }t        |j                  dd            j	                         }t        |j                  dd            j	                         }	 |dk(  r{t        j                  |j                  ||t        |j                  d	d            j	                         t        |j                  d
d            j	                         |       d{    y|dk(  rTt        |j                  dd            j	                         }|rt        j                  |j                  |       d{    y|dv r{t        j                  |j                  ||t        |j                  dd            |dk(  |       d{   \  }}	| j                  j                  j                  |rd|	 nd|	        y|dk(  r|j                  ||       y|dk(  r|j                  |       yyy7 7 7 m# t        $ r}
t!        d|
 d       Y d}
~
yd}
~
ww xY ww)u  Apply an email-card action from the HUD (control/email_action.json):
          attachment      → download it, then emit email_attachment (chip lights up)
          open_attachment → open the downloaded file in Preview/Finder
          reply/reply_all → send it on the thread, then tell Jarvis to say so
          images          → re-emit the thread with that message's remote images
          close           → drop the engine-side thread state (the HUD already
                            dismissed the card itself, so no event goes back)
        Blocking backends (Gmail API / IMAP+SMTP) run off-thread so the voice
        loop keeps listening while a big attachment downloads.r   )	mail_viewr]   r[   	thread_idaccountmsg_id
attachmentatt_idfilenameNopen_attachmentrj   )reply	reply_allbodyr   u2   [mail] Ahmed sent a reply from the email card — z1[mail] Ahmed's reply from the email card FAILED: imagesr   z       [email action failed: r&   )r
   r   r   r   r   rT   r}   download	open_pathr   r?   r   r   load_imagesdroprJ   rK   )rM   rO   r   r]   r   acctmidrj   rw   r   rP   s              r   r   zControlWatcher._email_action  s'     	$TXXh+,224$((;+,224488Ir*+113$((8R()//1	8%''&&S2./557R017794A A A ,,488FB/0668!++I,?,?FFF11 ' 1 1OOS#s488FB3G/Hk)4!1 1C )) INGuMO 8#%%c3/7"s# # A G1  	81!A677	8s   B+I#.A:H> (H7)H> -I#.AH> H:H> I#AH> H<6H> I#H> I#H> 4I#7H> :H> <H> >	I II#I  I#c           	       K   ddl m}m} t        |j	                  dd            j                         }	 |dk(  rUt        |j	                  dd            j                         }|rt        j                  |j                  |       d{    y|dk(  r~t        |j	                  d	d            j                         }t        |j	                  d
d            j                         }|rW|rSt        j                  | j                  ||             y|dk(  rt        |j	                  d	d            j                         }t        |j	                  d
d            j                         }|r|rt        j                  |j                  dd|||j                  d       d{    y|dk(  rt        |j	                  d	d            j                         }t        |j	                  dd            j                         }|r<|r9t        j                  |j                  dd|||j                  d       d{    yyyyyyyyy7 7 7 # t        $ r%}	t        dt        |	      dd  d       Y d}	~	yd}	~	ww xY ww)u  Apply an Entity Dossier card action from the HUD
        (control/entity_action.json). Mirrors _email_action — same atomic-file
        mechanism, blocking backends run off-thread so the voice loop keeps
        listening:
          show_entity    {name}       → drill-down: pop that entity's card
          entity_note    {key, text}  → save a note attached to the entity (memory)
          entity_summary {key, text}  → upsert the entity's current-state summary
        Best-effort; a bad payload never crashes the watcher.r   )entity_showrm   r   r[   show_entityrQ   Nentity_notekeyr   entity_summaryPOSTz/summary)rQ   r   groupentity_unrelateotherz/entity/unrelate)abr   z       [entity action failed: x   r&   )r
   r   rm   r   r   r   rT   r}   r   ensure_future_entity_note_agent_req_GROUPrJ   rK   )
rM   rO   r   rm   r   rQ   r   r   r   rP   s
             r   r   zControlWatcher._entity_action  sC     	6$((5"%&,,.!	Dm#488FB/0668!++K,C,CTJJJ%$((5"-.446488FB/06684
 ))$*A*A#t*LM(($((5"-.446488FB/06684!++&++VZ!$d"0"7"79: : : )) $((5"-.446DHHWb1288:5!++&++V5G!"0"7"79: : : !3 *  3  3 J::  	D23q6$3<.BCC	Ds   2I:AI	 	I
I	 I:BI	 I:BI	  I!I	 %I:&BI	 4I5I	 9	I:I	 I	 I	 		I7I2-I:2I77I:c           	     ~  K   ddl m}m} d| d| d| d| d	}	 |j                  j	                  |d| 	       d
{   }t        |t              r|j                  d      rt        |      y
y
7 2# t        $ r>}t        dt        |      d
d  d       |j                  d| d| d       Y d
}~y
d
}~ww xY ww)u  Run a dossier-card note through a pooled Claude worker as a memory
        CURATION task: interpret Ahmed's note as a correction or an addition,
        apply it to the graph (memory_update supersede / memory_remember), and
        re-show the entity so the corrected card replaces the stale one. Falls
        back to a plain saved note if the pool is unavailable — the note is
        never lost either way.r   )r_   rm   z)Ahmed typed this on the dossier card of "z" in his memory HUD: "u  "

This is a memory-CURATION order about that entity. Do exactly this:
1. mcp__memory__memory_search for that entity's facts — search the entity name AND the topic of his note; read every result (ids included).
2. If the note CORRECTS something: for EVERY fact that states the wrong version, call mcp__memory__memory_update (old_id = that fact's id, text = the corrected fact, written cleanly in third person). Fix all of them, not just the first.
3. If the note ADDS something new: save it with mcp__memory__memory_remember (entities="z?").
4. When the graph is fixed, call mcp__entity__show_entity("z") so his card refreshes with the corrected dossier.
Be surgical: change only what the note actually says. Reply with one line summarizing what you fixed/added.zfix memory: )rk   NREFUSEDz5       [entity note agent failed, saving plain note: d   r&   zNote about : r   )r   )r
   r_   rm   r   dispatchr   r   
startswithRuntimeErrorrJ   rK   	fire_save)rM   r   r   r_   rm   r   r   rP   s           r   r   z!ControlWatcher._entity_note_agent  s      	57u =F 7 8;e <)), .99 	(	O"00l3%0 1 2 2C#s#y(A"3'' )B#2  	OIVDS\N!% &$${3%r$%@v$NN	Os>   B=#A3 A10A3 /B=1A3 3	B:<4B50B=5B::B=c                  K   ddl m} t        |j                  dd            j	                         }t        |j                  dd            j	                         }|syt        |j                  dd            j	                         }t        |j                  dd            j	                         }	 |d	k(  rlt        j                  |j                  |       d{    t        d
|       | j                  j                  j                  d|r	d|dd  dndz   dz          n^|dk(  rX|rVt        j                  |j                  ||       d{    | j                  j                  j                  d|dd  d       nyt        j                  |j                  |       d{    y7 7 ]7 	# t        $ r}t        d| d       Y d}~yd}~ww xY ww)zApply a memory action from the HUD viewer: forget (permanent delete)
        or edit (supersede with corrected text). Then refresh the panel and drop
        a note so Jarvis knows the brain changed.r   rl   r]   r[   r`   Nrn   r   forgetmemory_forgotten)r`   z-[memory] Ahmed deleted a memory in the viewerr   P   r   u6    — forget it, it was wrong; don't bring it up again.r   z5[memory] Ahmed corrected a memory in the viewer to: "r   z".z       [memory action failed: r&   )r
   rm   r   r   r   rT   r}   r  r   r?   r   r   r   r   rJ   rK   )rM   rO   rm   r]   r   rn   r   rP   s           r   r   zControlWatcher._memory_action-  s     	)TXXh+,224$((4$%++-b!"((*488FB'(..0	9!''(=(=sCCC'C0))C-1T#2YKq)r;NOP 6!d''(;(;S$GGG))Tc
|2'( ##N$@$@!DDD D H E 	921#Q788	9ss   B.G1(F/ F)A3F/ F+1F/ ?G #F/ #F-$F/ (G)F/ +F/ -F/ /	G8GGGGc                l  K   ddl m} t        |j                  dd            j	                         }t        |j                  dd            j	                         }t        |j                  dd            j	                         }|r|sy	 |dk(  r}t        |j                  d	d
            j	                         xs d
}t        j                  |j                  |||       d{    | j                  j                  j                  d       nU|dk(  rOt        j                  |j                  ||       d{    | j                  j                  j                  d       nyt        j                  |j                         d{    y7 7 U7 	# t        $ r}t        d| d       Y d}~yd}~ww xY ww)zApply a graph edit from the HUD viewer: link (connect two memories)
        or unlink (cut the edge). Then refresh the graph and drop a note so
        Jarvis knows the brain's connections changed.r   rl   r]   r[   r   r   Nlinktyperelatedz8[memory] Ahmed connected two memories in the graph view.rL   z;[memory] Ahmed disconnected two memories in the graph view.z       [graph action failed: r&   )r
   rm   r   r   r   rT   r}   r  r?   r   r   rL   r   rJ   rK   )rM   rO   rm   r]   r   r   typrP   s           r   r   zControlWatcher._graph_actionK  sn     	)TXXh+,224b!"((*b!"((*	8$((6956<<>K)''(;(;Q3GGG))NP8#''(=(=q!DDD))QS ##N$@$@AAA H E
 B 	81!A677	8st   BF4
AF !F	"AF 5F6*F  F4!"F FF F4	F F F 	F1F,'F4,F11F4c           	     L  K   ddl m} t        |j                  dd            j	                         }	 |dk(  rUt        |j                  dd            j	                         }|syt        j                  |j                  |       d{    y|dk(  rt        |j                  dd            j	                         }|syt        j                  |j                  |       d{    t        j                  |j                         d{    | j                  j                  j                  d	| d
       y|dk(  r|j                  d      }|s0t        |j                  dd            j	                         }|r|gng }g }|D ]V  }t        |      j	                         }|s	 |j                  t        j                  |j                  |       d{          X |rjt        j                  |j                         d{    | j                  j                  j                  ddj#                  d |D              z   dz          yyy7 7 l7 H7 # t        $ r}	t!        d| d|	 d       Y d}	~	d}	~	ww xY w7 {# t        $ r}	t!        d|	 d       Y d}	~	yd}	~	ww xY ww)uX  Apply a storage action from the HUD file panel:
          open   → download the file (cached) then open it in its default app
          delete → remove it, then re-emit the list so the panel refreshes
          upload → upload the dropped file(s), then refresh the panel
        Only `open` pulls bytes, and only for the one file clicked.r   ro   r]   r[   openrQ   Ndeletez[storage] Ahmed deleted "z" from his files on the HUD.uploadpathsrj   z"       [storage upload failed for r   r&   z[storage] Ahmed uploaded , c              3  (   K   | ]
  }d | d   yw)r   N ).0ns     r   	<genexpr>z1ControlWatcher._storage_action.<locals>.<genexpr>  s     #;das!Hds   z to his files on the HUD.z       [storage action failed: )r
   rp   r   r   r   rT   r}   	open_filer  ri   r?   r   r   appendr  rJ   rK   join)
rM   rO   rp   r]   rQ   r  oner   r   rP   s
             r   r   zControlWatcher._storage_actione  sb     	*TXXh+,224'	:488FB/0668''(A(A4HHH8#488FB/0668''(>(>EEE''(<(<===))/v5QRT8#)dhhvr2399;C%(SEbEAAA N'*;*;+22A+7 %7 8  !++O,@,@AAAHHNN--3))#;d#;;<567
  3 I
 F=%7$ N B1#Rs!LMMN B  	:3A3a899	:s   0J$0I? #J$$#I? I
I? J$0I? =J$>#I? !I"&I? I	-I? 6J$7A,I? $.II
I&I? I=AI? J$
I? I? I? I	I:I50I? 5I::I? ?	J!JJ$J!!J$c                   g }d|v rT| j                   j                  r>t        |d         | j                   j                  _        |j	                  d|d           d|v rT| j                   j                  r>t        |d         | j                   j                  _        |j	                  d|d           d|v rft        |d         | j                   _        |j	                  d| j                   j                          t        d| j                   j                         d|v rft        |d         | j                   _
        |j	                  d	| j                   j                          t        d| j                   j                         d
|v r9t        d
t        |d
                |j	                  dt        |d
                 d|v rYdt        |d         i}d|v rt        |d         |d<   d|v rt        |d         |d<   t        di | |j	                  d|        |rt        ddj                  |       d       y y )Nr
   zvoice=speedzspeed=	wake_wordz
wake_word=re   
text_voiceztext_voice=	collapsedz
collapsed=dockrf   
dock_widthwidth	dock_sidesidezdock=z       [hot-reload: r  r&   )r  )r?   r|   r   _voicer  float_speedr   r  r   r  rK   r  )rM   rO   appliedr   s       r   rI   zControlWatcher._apply_config  s   d?txx||"%d7m"4DHHLLNNVDM?34d?txx||"'W"6DHHLLNNVDM?34$!%d;&7!8DHHNNZ(:(:';<=!3!344"&tL'9":DHHNN[)<)<(=>?$(("5"56$d;&7!89NNZT+->(?'@ABT>T$v,/0Gt##(l);#< d""%d;&7"8#7#NNU7),-(7);(<A>?     c                ^   | j                   }|;|j                         +|j                          	 |j                  d       d| _         yd| _         y# t        j
                  $ rG |j                          	 |j                  d       n# t        j
                  $ r Y nw xY wY d| _         yw xY w)u4  SIGTERM the tracker and WAIT for it to actually exit (so the camera
        is released) — poll up to ~2s, then hard-kill. Keeps the camera-busy
        window near zero across an off→on restart, so the fresh tracker doesn't
        sit in its 20x1s open-retry loop while an old one still holds camera 0.N   r      )r@   r   	terminatewaitr   TimeoutExpiredr^   )rM   r   s     r   _terminate_gesturesz"ControlWatcher._terminate_gestures  s    
 ~~		 3NN		!	$  ,, 		IIaI(!00 s5   A #B,6B	B,	BB,BB,+B,c                6   | j                   d uxr | j                   j                         d u }|r|r| j                          t        j                  dk(  rt
        dz  dz  dz  }nt
        dz  dz  dz  }|j                         rt        |      nt        j                  }t        j                  |t        t
        dz  dz        g      | _         t        d	       n|r| j                          t        d
       || j                  k7  r|| _        t        d|       y y )Nntz.venv-handsScriptsz
python.exebinpythonr
   zhands.pyz"       [gestures: tracker started]z"       [gestures: tracker stopped]r{   re   )r@   r   r.  osrQ   r   rE   r   r   r   r   PopenrK   rA   r   )rM   rf   runningvenvpys        r   r   zControlWatcher._set_gestures  s    ..,N1D1D1F$1N ((* ww$.:\I.6A"llnT#..B'--S7*Z789;DN67((*:;""" "D# #r&  c                f    | j                          | j                  rd| _        t        dd       y y )NFr{   re   )r.  rA   r   )rM   s    r   rW   zControlWatcher._stop_gestures  s.      " %D& r&  c                j   | j                   duxr | j                   j                         du }|j                  d      s/|r%| j                   j                          t	        d       d| _         y|r7| j                   j                          	 | j                   j                  d       t        t        j                  t        |j                  dd            t        |j                  dd	            t        |j                  d
d            |j                  d      rdnd      }t        j                  t        j                  t        t         dz  dz        g|      | _         t	        d|j                  dd       d       y# t        j                  $ r | j                   j                          Y w xY w)z;Start/stop the local-VLM screen watcher (voice/watcher.py).Nrf   z       [watcher: stopped]r(  r)  r   z
the screenminutes   interval   live10)
WATCH_TASKWATCH_MINUTESWATCH_INTERVAL
WATCH_LIVEr
   z
watcher.py)envu          [watcher: started — r&   )rB   r   r   r+  rK   r,  r   r-  r^   r   r4  environr   r5  r   r   r   )rM   rO   r6  rF  s       r   r   zControlWatcher._set_watcher  sX   --t+L0B0B0D0Lxx~'')12 DMMM##%%""1"- 2::!$((6<"@A!$TXXi%;!<"%dhhz1&=">%)XXf%5c3	@
 #((^^S7!2\!ABCN-dhhv|.L-MQOP ,, %""$%s   
F -F21F2c                  K   t               }|rt        d|        | j                  j                  rat	        j
                  | j                  j                  j                  d       d {   }| j                  j                  j                  |       y | j                  j                  s | j                  j                  j                  rUt	        j                  d       d {    | j                  j                  r4| j                  j                  j                  rUt        d       t        d       | j                  j                  rt	        j
                  | j                  j                  j                  d       d {   }| j                  j                  j                  |       | j                  j                  j                          d {    | j                  j                          d {    t        dz  }|j!                          t#        d      D ]1  }t	        j                  d	       d {    |j%                         r1 y  |j'                  d
       | j)                          t+        j,                  t.               t+        j0                  t2        j4                  t2        j4                  t7        t.        dz        g       y 7 i7 7 V7 7 7 w)Nu0          [restart REFUSED — new code is broken]
uB   I can't restart — the new code has an error. Check the terminal.g333333?z9       [self-restart: code validated, handing off to HUD]
restartingz$Restarting myself. Back in a moment.hud_restart   g?Tr=   r	   )r   rK   r?   r|   rT   r}   r~   r   r   
respondingspeakingrU   r   	wait_donestoprC   touchrangeexistsrL   rW   r4  chdirr   execvr   r   r   )rM   r   r   rJ  _s        r   r   zControlWatcher._restart  s    EcUKLxx||#--HHLL&&*+ +   %%c*hh!!TXX%5%5%>%>--$$$ hh!!TXX%5%5%>%>IJ\88<<))""$JL LCHH!!#&(("",,...hhmmo
 - rA--$$$%%'	  	d+

#..#g	6I2J!KLE+ %L / %s   A(K#*K+A8K##K$K#? K# A#K#KAK#K!K#6K7AK#:K!;K#BK#K#K#K#K#!K#N)returnNone)rO   r   rV  rW  )r   r   r   r   rV  rW  )rf   r   rV  rW  )__name__
__module____qualname____doc__rR   r   rV   r   r   r   r   r   r   r   rI   r.  r   rW   r   r   r  r&  r   r    r    0   sb    F56	"_"B	5On&8P,D\$OL9<84/:b@<$$4'Q0'Mr&  r    c                 |    t         j                         r(t         j                         j                         } | xs d S y )N)SESSION_FILErE   rH   r   )sids    r   load_saved_sessionr_  (  s2    $$&,,.{dr&  c                Z    t         j                  d       t        j                  |        y )NTr"   )rC   rD   r]  r   )r   s    r   save_sessionra  /  s    MM4M J'r&  )rV  z
str | None)r   r   rV  rW  )r[  
__future__r   rT   rF   r4  r   r   pathlibr   voice.eventsr   __file__resolveparentr   rC   r]  r   r    r_  ra  r  r&  r   <module>rh     su   & #   	  
  
x.
 
 
"
)
)
0
0
I
%	AuM uMp(r&  