
    Xj                      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	Z	ddl
Z
ej                  dk(  r[ej                  ej                  fD ]  Z	 ej                  dd        	 ddlZej$                  j&                  j)                  d       ddlZ	 dd	lmZ  eej6                  j9                  ej6                  j;                  e      d
             ddlm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z( ddl)m*Z* ddl)m+Z+m,Z, ddl-m.Z. ddl/m0Z0 ddl1m2Z2m3Z3 ddl4m5Z5 ddl6m7Z7 ddl8m9Z9 ddl:m;Z; ddl<m!Z=m>Z> ddl?m@Z@ e dz  e!z  ZA eBej                  j                  dd            ZEdZFdZG eHej                  j                  dd            ZIdZJdZK eHej                  j                  d d!            ZLd"ZMd#ZNd$ZOd%ZP eBej                  j                  d&d'            ZQej                  j                  d(d)      d*k7  ZRej                  j                  d+d)      d*k7  ZS eBej                  j                  d,d-            ZT eBej                  j                  d.d/            ZU eBej                  j                  d0d1            ZV eBej                  j                  d2d3            ZW eBej                  j                  d4d-            ZXej                  j                  d5d)      d*k7  ZY eHej                  j                  d6d7            ZZ eHej                  j                  d8d9            Z[ eBej                  j                  d:d3            Z\ej                  j                  d;d)      d*k7  Z]ej                  j                  d<d)      d*k7  Z^ej                  j                  d=d)      d*k7  Z_ej                  j                  d>d)      d*k7  Z`h d?Zah d@Zbh dAZc ej                  dBej                        Zf ej                  dCej                        Zg ej                  dDej                        Zh ej                  dEej                        ZidfdFZj ej                  dGej                        ZldfdHZm ej                  dIej                        ZndfdJZo ej                  dKej                        Zp ej                  dLej                        Zq ej                  dMej                        ZrdgdNZsdhdOZtdidPZu G dQ dR      ZvdjdSZwdjdTZxdjdUZyezdVk(  rxddWl{m|Z}  e}e      j                         j                  dXz  Z	 ej                  dYZ       ddlZ ej                   eed[z  d\             	  ej
                   ey              yy# e$ r Y 5w xY w# e$ r5 	 ej$                  j*                  j-                          n# e$ r Y nw xY wY /w xY w# e$ r Y w xY w# e$ r Y ww xY w# e$ r  ed]        ej                  d       Y ye$ rZddlZddlZ	  eed^z  d_d`      5 Zej                  daej                  j                         j!                          db       ej                   ej"                                ddd       n# 1 sw Y   nxY wn# e$ r Y nw xY w edc ee      j                   dde ej                  e        dZ[ww xY w)ku  Claude Voice — real-time, interruptible voice chat with Claude on a Mac.

Everything runs locally on the M4 except the Claude conversation itself,
which goes through the locally-installed Claude Code (Max subscription,
no API key, no per-token billing).

    mic 16k ── Silero VAD ── Parakeet STT ─▶ Claude (Agent SDK, streaming)
                                                    │ sentence chunks
    speakers 24k ◀── Speaker.cut() on barge-in ── Kokoro TTS

State machine (parameters from what shipped voice projects converge on):
  LISTENING  VAD 0.60 start / 250ms min speech / 700ms end-silence,
             1s pre-roll ring so the first syllable isn't lost.
  RESPONDING streaming LLM -> per-sentence TTS -> playback.
             Barge-in: while audio is playing the VAD runs STRICT
             (0.90, ~290ms sustained, 300ms refractory) so the assistant
             doesn't trigger on its own voice from open speakers; before
             audio starts it runs at the normal threshold.
             On barge-in: playback cut (<50ms), TTS queue dropped,
             Claude generation interrupted, back to LISTENING.

Env knobs: MODEL=haiku|sonnet|opus  VOICE=af_heart  VOICE_SPEED=1.0
           STT=parakeet|whisper  STT_LANG=ar  INTERRUPT=0 (half-duplex)
    )annotationsNntutf-8replace)encodingerrors   )load_dotenvz.env)	FRAME_SAMPLESSAMPLE_RATEAECMicStream	MicStreamSpeakerSystemAudioStream_start_mute_hotkeybreak_requestedrestore_input_volume)entity_show)pendingtask_ledger)ControlWatcher)emit)ClaudeBrain	FastBrain)FrustrationMeter)ProsodyReader)SpeakerGate)load_stt)r   	KokoroTTS)VAD  	VAD_STARTz0.50gffffff?   END_SILENCE_MS480      TURN_MAX_SILENCE_MS1400         g333333?BARGE_REFRACTORYz0.12	INTERRUPT10ASYNC_BRAINACK_TIMEOUT_Sz1.5WORKER_BUDGET_S300WORKER_BUDGET_HEAVY_S600WORKER_WATCH_S12READY_DEFER_SMEETING_SYSAUDIOMEETING_QUEUE_MAX120MEETING_BACKLOG2MEETING_BIG_MINAEC
VOICE_LOCK	WAKE_WORDDUPLEX>"   igohmitmmohokonyaahagothmmmhmseewowyeayepyescoolkeepmmhmniceokaysuretrueyeahgoingrightuhhuhgotchareallyalrightexactlyinteresting>   go oni seemm hmgot itno wayoh wowuh huhoh niceoh okayoh right	all right	of course	oh really
keep goingfair enoughmakes sensera   >   ok stopshut upstop itbe quiet	okay stop
never mindjarvis stopokay thanksstop jarvisalright stopstop talkingthat's enoughokay thank youthank you jarvishushstopquietshushenough	nevermindz^\W*(?:jarvis[,\s]+)?(?:(?:stop|cancel|drop|kill|abort|forget)\s*(?:(?:the|that|this|your)\s+)?(?:task|job|work|request|it|that)?|never\s*mind(?:\s+(?:that|it))?)[.!]?\W*$z4^\W*(?:hey\s+|ok(?:ay)?\s+|yo\s+)?jarvis\b[\s,:.!-]*az  \b((?:have\s+a\s+|take\s+a\s+)?look\s+(?:at\s+)?(?:this|that|here|my\s+screen|the\s+screen|at\s+this|at\s+my\s+screen)|check\s+(?:this|that)(?:\s+out)?|see\s+(?:this|that|my\s+screen|the\s+screen|what\s+i(?:'m|\s+am)\s+(?:looking\s+at|seeing))|what(?:'s|\s+is)\s+on\s+(?:my\s+|the\s+)?screen|what\s+am\s+i\s+looking\s+at|look\s+at\s+my\s+screen|read\s+(?:my\s+|the\s+)?screen)\bu   \b(?:i\s*will|i['’]?ll|gonna|going\s+to)\s+(?:have\s+a\s+)?look\b|\blook(?:ing)?\s+at\s+(?:this|that|it)\b.*\b(?:later|tomorrow|tonight|after|next\s+week|in\s+a\b)c                v    | xs d}t         j                  |      ryt        t        j                  |            S )zATrue if the utterance is asking Jarvis to look at the screen NOW. F)	_DEFER_REsearchbool
_SCREEN_REtextts     2/Users/ahmed/devFolder/Ultron/claude-voice/main.py_wants_screenr      s2    
A
!!!$%%    z\b(search|research|look\s+(?:it\s+|that\s+|this\s+)?up|google\s|browse|look\s+into|dig\s+(?:up|into)|investigate|find\s+(?:out|me)|check\s+online|what'?s\s+the\s+(?:latest|current|newest))\bc                F    t        t        j                  | xs d            S )u}   True if the utterance clearly needs the full brain's tools (web
    search / research) — route past the toolless fast lane.r   )r   _WANTS_FULL_REr   r   s    r   _wants_fullr      s     %%djb122r   a  ^\W*(?:hey\s+|ok(?:ay)?\s+)?(?:jarvis[,\s]+|can\s+you\s+|could\s+you\s+|please\s+|go\s+(?:and\s+)?|i\s+(?:need|want)\s+you\s+to\s+|let'?s\s+)?(build|fix|make|create|code|write|edit|refactor|debug|run|deploy|install|set\s+up|configure|research|investigate|analy[sz]e|summari[sz]e|send|email|message|text|schedule|book|remind|add|remove|delete|update|change|rename|download|upload|pull\s+up|look\s+into|figure\s+out|work\s+on|handle|sort\s+out|draft|put\s+together|generate|compile|scrape|check\s+on|clean\s+up|organi[sz]e)\bc                d    | xs d}t        t        |      xs t        j                  |            S )zTrue if the utterance is real work (task/command) that should run on the
    background brain rather than block the conversation.r   )r   r   _TASK_REr   r   s     r   _is_taskr     s*     	
AA4(//!"455r   z\b(?:record|take\s+notes?)\b.{0,20}\b(?:this|the|meeting|call|conversation)\b|\b(?:start\s+)?(?:meeting\s*mode|note[-\s]?taking)\b|\bstart\s+recording\bz\b(?:stop|end|finish|wrap\s+up)\b.{0,15}\b(?:recording|meeting|notes?|listening|note[-\s]?taking)\b|\bmeeting'?s?\s+(?:is\s+|are\s+)?(?:over|done|finished)\bzL\b(online|digital|call|zoom|teams|meet|google\s*meet|video|virtual|remote)\bc                    | xs d}t         j                  |      ryt        j                  |      rdt        j                  |      rdfS dfS y)z?('start', mode) / ('stop', '') / None. mode = online|in_person.r   r   r   startonline	in_personN)_MEET_STOP_REr   _MEET_START_RE_MEET_ONLINE_REr   s     r   _meeting_triggerr   *  sM    
AAQ_%;%;A%>PPKPPr   c                >   t         j                  |       }|stdj                  | j                         dd       j	                         }d|vry| j	                         j                  d      }| |t        d      z   d j                  d      S | |j                         d S )u   If `text` starts by naming Jarvis, return the command after the
    wake word (empty string = just 'Jarvis' with nothing after). Return
    None when the wake word is absent → the utterance should be ignored. N   jarvisz ,:.!-)	_WAKE_REmatchjoinsplitlowerfindlenlstripend)r   mheadidxs       r   _wake_commandr   4  s     	tAxx

Ra()//14jjl)C#h-'()00::>r   c                     	 t         j                  j                  t         j                  j                  t         j                  j	                  t
                    dd      } t        j                         }d}	 t        | d      5 }t        |j                         j                         xs d      }ddd       	 t        | dd      5 }|j                  t        |             ddd       |r||z
  d	k  ry
||z
  }|dk  rdt        |dz         d}n|dk  rd|dz  dd}n
d|dz  dd}t        j                  d      }d| d| dS # 1 sw Y   xY w# t        $ r Y w xY w# 1 sw Y   xY w# t        $ r Y w xY w# t        $ r Y y
w xY w)u{  Wall-clock awareness for the brain. Its resumed transcript reads as one
    continuous moment, so after an overnight gap Jarvis says "I just checked"
    about last night. On a gap ≥30min since the previous exchange (persisted
    to control/last_turn across restarts) return a bracketed note stating the
    real time and how long passed. Empty string otherwise; never raises.control	last_turn        r   r   r   Nwi  r   i   about <   z minutesi  i  .0fz hoursiQ z daysz%A %d %b, %H:%Mz

(TIME: it is now     — z have passed since the previous exchange. Anything 'just now'/'earlier' in the conversation above happened BEFORE this gap; re-check rather than assume it is still current.))ospathr   dirnameabspath__file__timeopenfloatreadstrip	Exceptionwritestrintstrftime)pnowprevfgaphumanstamps          r   _time_gap_noter   C  s   GGLL)BC"K1iik	a'*aQVVX^^-23 +	aw/1C! 0 sTzD(Dj:S^,H5E6\S4Z,F3ES5[-U3E/0'weE7 ;4 4 	5' +* 		 0/ 		   s   A5F 8E ,E1E :E2 E&#E2 +
F 6AF EE 	E# F "E##F &E/+E2 2	E>;F =E>>F 	FFc                     e Zd Zd4dZd4dZd4dZd5dZd4dZd6dZd7dZ	d4dZ
d8d	Zd
 Zd4dZd4dZd9dZd4dZed:d       Zed;d<d       Zd4dZd4dZd4dZd4dZd4dZd4dZd8dZd4dZd4dZd4dZd=dZd>dZed?d       Z d@dZ!dAd Z"d!Z#d4d"Z$dBdCd#Z%d:d$Z&d@d%Z'd@d&Z(d4d'Z)dDd(Z*dEd)Z+d@d*Z,dFd+Z-dBdGd,Z.d4d-Z/dHd.Z0d4d/Z1d4d0Z2	 	 dI	 	 	 dJd1Z3dKd2Z4dLd3Z5y)MVoiceAppc                   t        j                         | _        d | _        d| _        d| _        d| _        t        t              | _	        t               | _        t        j                  t              | _        d | _        d | _        d | _        d | _        d | _        t+               | _        t/               | _        d| _        d| _        t        j6                         | _        d | _        d| _        d| _        g | _         d| _!        g | _"        tF        | _$        d| _%        d | _&        d | _'        d | _(        d | _)        d | _*        d| _+        d | _,        d | _-        d | _.        d| _/        d | _0        y )NFg?	   )maxlencalmr   r   )1asyncioget_event_looploopmic
aec_activebarge_prob_playingbarge_sustain_framesr   TTS_RATEspeakerr    vadcollectionsdequePRE_ROLL_FRAMESringbrainfaststtttsgater   prosodyr   frustration	_mood_tag
respondingQueueinbox_brain_task_brain_desc_brain_started_brain_queue_brain_cancel_readyWAKE_WORD_DEFAULT	wake_word
text_voicemeeting	_sysaudio_remote_capture_task_meeting_worker_task_meeting_stt_queue_meeting_transcribing	_room_wav	_them_wav_barge_text_wake_grace_until
_tts_abort)selfs    r   __init__zVoiceApp.__init__k  sF   **,	"&$%!x(53>3D3D"4
	 *.
&*	%)(,	 % ,-)0
 15!')"')* 489=!9=!8<%*"'+!$26r   c                   t         j                  dk7  s"t        j                  j	                  d      dk(  ryddl}ddl}|j                  d      xs d}t        j                  j                  |      sy	 |j                  |dd	d
gddd      j                  j                         t        fddD              sy|j                  |ddd	gddd      j                  j                         }t        d |D        d      }|r,|j                  |dd	d|gdd       t!        d d| d       yy# t"        $ r}t!        d| d       Y d}~yd}~ww xY w)u  Bluetooth earbud mics (CMF Buds, AirPods) are poor for voice and
        force the degraded SCO codec — and macOS keeps auto-selecting them.
        If the default input is one, switch to the built-in mic so Jarvis
        hears clearly. MIC_KEEP_DEFAULT=1 to leave the default alone.darwinMIC_KEEP_DEFAULTr/   Nr   SwitchAudioSourcez#/opt/homebrew/bin/SwitchAudioSourcez-tinputz-cT   )capture_outputr   timeoutc              3  B   K   | ]  }|j                         v   y wN)r   ).0r   curs     r   	<genexpr>z/VoiceApp._prefer_builtin_mic.<locals>.<genexpr>  s%      IG -.qCIIK'Gs   )budairpod	bluetoothheadsetcmfz-ac              3     K   | ]8  }d |j                         v sd|j                         v r|j                          : yw)macbookzbuilt-inN)r   r   )r  r   s     r   r  z/VoiceApp._prefer_builtin_mic.<locals>.<genexpr>  s8      Rt!(AGGI5qwwy9P GGIts   >A z-s)r  r  z  mic: default was 'u   ' (bluetooth) — switched to 'z.' for clear voice (MIC_KEEP_DEFAULT=1 to keep)z  [mic preference skipped: ])sysplatformr   environgetshutil
subprocesswhichr   existsrunstdoutr   any
splitlinesnextprintr   )r  r&  r'  _SASallmbuiltiner  s          @r   _prefer_builtin_miczVoiceApp._prefer_builtin_mic  s~   
 <<8#rzz~~6H'IS'P01 98 	ww~~d#	6..$gt!<044)* ! ,,2F557   IGI I>>4tW"=15D*+ " --3VJJL   Rt R!G dGT ' )9=q  J,SE 2!"PR S 
  	6/s!455	6s    6AD/ <A1D/ /	E8EEc                  K   t        d       t        dd       | j                          t                ddlm} |j                         r|j                          t        d       t        j                  dk7  rt        j                  j                  d	d
      dk7  r	 dd l}ddlm}  |t              j!                         j"                  dz  }|j%                         r?|j'                  dt)        |      g|j*                  |j*                  d       t        d       	 ddlm} |j1                         s|j3                          t        d       nt        d       t4        j6                  dk(  r3	 ddlm}m}m}	  |       rt        d       n ||	di       t        d       tA        j@                         }
t        dd       ddl!m"} tG        jH                  |       d {   | _%        t        dtA        j@                         |
z
  d d!       | jM                          tA        j@                         }
t        dd"       tG        jH                  tN               d {   | _(        t        d#tA        j@                         |
z
  d d!       t        dd$       dMd%}tG        jH                  |       d {    dMd&}tG        jH                  |       d {    tR        rT	 tG        jH                  tT               d {   | _+        | jV                  jX                  rt        d'       nt        d(       ddlm} |j                         r9tG        jH                  |jZ                         d {   }t        d+|rd,nd-        tA        j@                         }
t        dd.       t]        t        j                  j                  d/d0      1      | _/        | j^                  j                          d {    t        d2tA        j@                         |
z
  d d!       ta        jb                          te        jb                          ta        jf                         }|r9d3ji                  d4 |D              }| jj                  jm                  d5|z   d6z          te        jn                         D ]2  }|j                  d7      s| jj                  jm                  |d7          4 dd8lm8} |j                         rN| jr                  |jt                  _;        |jt                  j                          t        d9|jx                   d:       t        j                  j                  d;d
      dk7  rtA        j@                         }
d | _=        t        j                  j                  d<d      dk7  rz	 dd=l>m?}m@}  |       rj |       | _=        | jz                  j                          d {    t        d>| jz                  j                   d?tA        j@                         |
z
  d d!       | jz                  W	 t               | _=        | jz                  j                          d {    t        dBtA        j@                         |
z
  d d!       t        ddE       | j                          d {    | j                  j                          t                | j                          d {    | j                          t        rdFndG}t        dH| dI       t        d,| j                  t        | jV                  xr | jV                  jX                        J       t        dK| j                  L       y # t,        $ r}t        d| d       Y d }~cd }~ww xY w# t,        $ r}t        d| d       Y d }~Kd }~ww xY w# t,        $ r}t        d| d       Y d }~+d }~ww xY w7 7 {7 &7 7 # t,        $ r}t        d)| d*       Y d }~d }~ww xY w7 7 7 6# t,        $ r!}t        d@| dA       d | _=        Y d }~d }~ww xY w7 # t,        $ r!}t        dC| dD       d | _=        Y d }~d }~ww xY w7 7 w)NNz,loading models (first run downloads them)...bootingu   Starting up…)stager   desktop_serverz-  desktop control: macos-mcp launching (HTTP)r   CHROMEr/   r0   Pathzchrome-jarvis.shbashT)r+  stderrstart_new_sessionz.  browser: Jarvis Chrome launching (CDP :9222)z.  browser: Jarvis Chrome auto-launch skipped ()screenua     screen: requested Screen Recording — allow Jarvis, then relaunch (needed for 'see my screen')z!  screen: recording permission OKz$  screen: permission check skipped (r  )AXIsProcessTrustedAXIsProcessTrustedWithOptionskAXTrustedCheckOptionPromptz-  accessibility: OK (desktop control enabled)uY     accessibility: requested — allow Jarvis, then relaunch (needed to control other apps)z   accessibility: check skipped (u/   Loading voice (up to a minute on cold start)…)load_ttsz  TTS ready (r   zs)u   Loading speech recognition…z  STT ready (u   Warming up…c                 6    ddl m}   |        t        d       y y )Nr   
get_routerz1  intent router ready (semantic reflexes, ~0.1ms))voice.intentrI  r/  rH  s    r   _warm_routerz$VoiceApp.start.<locals>._warm_router  s    /|'IJ (r   c                 6    ddl m}   |        t        d       y y )Nr   get_detectorz)  smart-turn ready (semantic end-of-turn))
voice.turnrN  r/  rM  s    r   
_warm_turnz"VoiceApp.start.<locals>._warm_turn!  s    /~)AB *r   uW     voice lock: profile loaded — only your voice gets replies (VOICE_LOCK=0 to disable)uK     voice lock: ENROLLING — your first 5 utterances build the voice profilez  voice lock unavailable (u   ) — replying to anyonez  desktop control: readyz NOT ready (tools may be missing)u   Connecting to Claude…MODELsonnet)modelz  Claude session ready (
c              3  L   K   | ]  }d t        j                  |      z     ywz- N)r   	summarize)r  r   s     r   r  z!VoiceApp.start.<locals>.<genexpr>M  s(      4'2! #[%:%:1%=='2s   "$zZ[recovered] Background task(s) were cut off by the last restart/crash and did NOT finish:
uo   
If Ahmed asks about any, tell him plainly it was interrupted and offer to redo it — never claim it finished.r   
agent_poolz  agents: pre-warming u(    spare worker(s) — dispatch is instant	FAST_CHATLOCAL_BRAIN)
LocalBrain	availableu     fast brain ready — LOCAL z (z  local fast brain failed (u   ) — using Haikuu2     fast brain ready — Haiku conversational lane (z  fast brain unavailable (u   ) — full brain onlyu   Setting up the microphone…interruptiblezhalf-duplex (INTERRUPT=0)u   
ready — just talk. (z; Ctrl-C to quit)
)aecenrolledr   rK   returnNone)Lr/  r   r4  r   voicer9  enabledr   r   namer$  r%  r'  pathlibr<  r   resolveparentr)  Popenr   DEVNULLr   rB  
has_accessrequest_accessr"  r#  ApplicationServicesrC  rD  rE  r   voice.tts_chatterboxrF  r   	to_threadr   _apply_persisted_prefsr   r   rA   r   r   ra  
wait_readyr   r   r   loadr   recover_staler   r   
put_nowaitunackedrZ  _enqueue_reportpoolreport_sinkWARM_SPARESr   voice.local_brainr]  r^  rT  r   	_pick_micr   r   _verify_hearing_write_statusINTERRUPTIBLEr   r   r   )r  r9  _spr<  _shr3  rB  rC  rD  rE  t0rF  rK  rP  rQ  interruptedlines_nrZ  r]  r^  modes                         r   r   zVoiceApp.start  s    <=Y./  " 	)!!#  "AB
 77d?rzz~~h<CM((8n,,.558JJ::< IIvs3x0%([[04  6 JK		?$$$&%%' C D 9: <<8#?  &'IJ13NPT2UV D E YY[YOP1 **844diik"nS145##%YY[Y=> **844diik"nS145Yo.	K ---	C 
+++	P")"3"3K"@@	99%% C D  4 5 	)!!#!++N,E,EFFE'5>`'abcYY[Y78 rzz~~gx'HI
jj   (R(<B?@ 	 "//1II 4'24 4EJJ!!68=>A::; //#Bvvf~

%%bj1 $ 	% +/*>*>JOO'OO!!#*:+A+A*B C6 7 8 ::>>+s+s2B DIzz~~mS1S8	%G {$.L	"iioo/// =diioo=N O""&))+b.!5R9 :
 yy % )DI))//+++ "iik"nS15 6
 	Y<=nn""$$$"/5P(.ABCW$//499;););<	>[T^^,I  MFqcKLLM  	?81=>>	?&  ?81=>>? 5
 5 	. 	, A  P21#5MNOOP G
 	!p 0 ! %7s:KLM $DII% , ! %6qc9NOP $DII% 	 	%s  Ba5B ]" =^ a5/2^. !?a5 _!A;a5_Aa54_5 a5_
a5!_# =_ >6_# 48a5,`	-Ba50`1Ca5:C&a5!8` `A ` a5',a `?)a = a5a/;a5a2Ba5"	^+^ :a5 ^a5	^+^& a5&^++a5.	_7_a5_a5a5a5a5 _# #	`,`;a5`a5a5` 	`<`71a57`<<a5?a 	a,a'!a5'a,,a52a5c                b  K   ddl m}m}  ||      | _        t	        dd|       | j
                  | _        d| _        t	        dd       | j                          t        d	| d
       | j                  j                  t        j                  | j                  j                  d       d {          t        j                  t               | _        d| _        	  || j                  j'                               | _        t        j,                  | j/                  | j                  | j"                              | _        |dk(  r;t2        r4t        j,                  | j5                  | j                              | _        y y y 7 # t*        $ r }d | _        t        d| d
       Y d }~d }~ww xY ww)Nr   )MeetingSessionWavRecorder)r  r   T)rK   r  r   rb  u&          [MEETING recording started — r!  z
Recording.)maxsizeFz'       [meeting: room WAV open failed: r   )voice.meetingr  r  r   r   r   
_prev_waker  r/  r   playr   rr  r   synthr   r;   r  r  room_wav_pathr  r   create_task_meeting_transcribe_workerr  r:   _capture_remoter  )r  r  r  r  r3  s        r   _start_meetingzVoiceApp._start_meeting  sa    =%40Y4d+ ..[T"6tfA>? 1 1$((.., OOP
 #*--8I"J%*"	B()C)C)EFDN %,$7$7++DLL$:Q:QR%T! 8 0(/(;(;$$T\\2)4D% !1% P  	B!DN;A3a@AA	Bs=   B+F/-F..F/%F B F/	F,F'"F/'F,,F/c                  K   | j                   d c}| _         |y | j                          d {    | j                  d c}| _        |	 |j                          t        dd       t        | d| j                        | _        t        d| j                         | j                  }d | _        | j                  }d}|T|j                         | j                  rdndz   }|j                         r	 |j                          	 |j                  d        |t        k\  xs |j                   t"        k\  }|rdnd	}| j$                  j'                  t)        j*                  | j,                  j.                  |       d {          t)        j0                  | j3                  |||             y 7 y# t        $ r Y \w xY w# t        $ r Y w xY w# t        $ r Y w xY w7 `w)
Nr   Frb  r  r   r      uZ   That's a big one, sir. Still processing — I'll have the full transcript for you shortly.zStopped. Summarising.)r   _stop_remote_capturer  closer   r   getattrr   r  r  qsizer  full
get_nowaitrw  MEETING_BACKLOG_THRESHOLDminutesr?   r   r  r   rr  r   r  r  _finalize_meeting)r  r   r   workerqr   backloglines           r   _stop_meetingzVoiceApp._stop_meeting  s    ,,4<9 ''))) NND4>=	 	Y5! |T^^D[T^^, **$(!##=ggi(B(B1JGvvxLLNT" 66V!)):V . 7 	 	 1 1$((..$ GGH 	D221fgFGG 	*
  " !    Hs   +GF&G	F) BG.F9 ?G A&G6G70G)	F62G5F66G9	GGGG	GGGGc                  K   	 |
| d{    | j                  d| _        	 |j	                         }t        d| dt        |j                         d       |r3	 | j                  j                  dt        |j                         d	       | j                  |       d{    y7 # t         $ r}t        d| d       Y d}~d}~ww xY w# t         $ r}t        d| d       Y d}~d}~ww xY w# t         $ r Y qw xY w7 `w)
zOff-loop tail of a meeting: let the worker drain the remaining audio,
        save the full transcript, tell Ahmed it's ready (if it was a big one via
        the inbox rail), then run the existing summary path. Fully guarded.Nz&       [meeting worker finish failed: r!  z       [MEETING saved: r   z lines]z       [meeting save failed: u*   Your meeting transcript is ready, sir — z, lines saved to recordings. Summarising now.)
r   r/  r   r  saver   r  r   rw  _summarize_meeting)r  r   r  was_backlogr3  r   s         r   r  zVoiceApp._finalize_meeting  s    	A!
 <<&*D#	8668D+D6s177|nGLM 

%%@QWW OB BC
 %%a((('  	A:1#Q?@@	A  	81!A677	8  (s   DB* B(B* D5C D2C4 D"D#D(B* *	C3CDCD	C1C,'D,C11D4	D =D?D  Dc                &   | j                   }||t        |      dk(  ry	 |j                  |t        j                  |t        j
                        ||nt        j                         f       y# t        j                  $ r t        d       Y yw xY w)u*  Hand one completed meeting utterance to the background Whisper worker.
        On backlog (queue full) DON'T block or drop silently — the raw WAV
        already has this audio, so it stays re-transcribable; we just skip the
        live pass. `audio` is copied (the caller may reuse its buffer).Nr   )dtypeu^          [meeting: STT backlog full — audio kept in the WAV, re-transcribe from the recording])
r  r   rw  npascontiguousarrayfloat32r   r   	QueueFullr/  )r  r   audiotsr  s        r   _enqueue_meeting_audiozVoiceApp._enqueue_meeting_audio  s    
 ##9Uq	7LL'2#7#7RZZ#P ""DIIKA B   	7 6 7	7s   AA/ /BBc                  K   ddl m} 	 t        j                  |       d{   \  }}t	        d| d       	 |j                          d{   }|y|\  }}	}
d| _        	 t        j                  |j                  |	       d{   xs d	j                         }d| _        |rt        d |D              s|j                  |||
       t	        d| d|        t        d|       7 # t
        $ r%}t	        d| d       | j                  }Y d}~d}~ww xY w7 7 # t
        $ r!}t	        d
| d       d| _        Y d}~d}~ww xY ww)u  Consume queued meeting utterances and transcribe them with the
        multilingual (Arabic+English code-switch) Whisper — LIVE where it keeps
        up, backlog-tolerant when it can't (audio is safe in the WAV either way).
        Runs off the main loop; MLX inference is on the shared Metal thread, so a
        slow utterance delays the loop's Parakeet a little but never corrupts it.
        Exits on the None sentinel after draining. Fully guarded.r   )load_meeting_sttNz       [meeting STT: z (multilingual auto-detect)]z!       [meeting STT load failed (z-); using main STT (English) as a last resort]Tr   z#       [meeting transcribe failed: r!  Fc              3  <   K   | ]  }|j                           y wr  isalnumr  cs     r   r  z6VoiceApp._meeting_transcribe_worker.<locals>.<genexpr>!  s     "=1199;   r  zNOTE(z)  meeting_liner   )r  r  r   rr  r/  r   r   r%  r  
transcriber   r,  addr   )r  sessionr  r  r   rh  r3  itemr   r  r  r   s               r   r  z#VoiceApp._meeting_transcribe_worker  s[     	3	%//0@AAIC)$/KLM
 =D|!%GUB)-D&%//FF uuw  */D&s"="==KK"K-E'#dV,-d+%  B 	5aS 90 0 1((C	
 ! G ;A3a@A-2*s   EC. C,C. EDE##D# D!D# AE,C. .	D7DEDE!D# #	E,EEEEc                  K   t        d|        	 t        j                  | j                  j                  |       d{   }| j
                  j                  |       y7  # t        $ r}t        d| d       Y d}~yd}~ww xY ww)u   Speak a heads-up about call-audio capture WITHOUT pausing the mic
        recording. Visibility is the whole point — the old bug recorded only
        Ahmed's side in silence and never said why.z       [MEETING warn] Nz!       [meeting warn TTS failed: r!  )r/  r   rr  r   r  r   r  r   )r  msgpcmr3  s       r   _meeting_warnzVoiceApp._meeting_warn)  su      	&se,-	<))$((..#>>CLLc" ? 	<5aS:;;	<s?   B-A" A  A" B A" "	B+A?:B?BBc                  K   t        j                         s| j                  d       d{    yt        | j                        }	 |j	                          || _        d\  }}| j                  j                         dz   }| j                  j                         |k  r	 t        j                  |j                  j                         d       d{   }|d	z  }t        |t        t        j                   |      j                                     }| j                  j                         |k  r|d
k(  rbt        j"                  d       d{    |j$                  xs d}|j'                          d| _        | j                  d| d       d{    yt)        d| d|dd       |S 7 # t
        $ r(}| j                  d| d       d{  7   Y d}~yd}~ww xY w7 # t        j                  $ r Y w xY w7 7 ow)u^  Bring up the system-audio "them" channel: spawn the helper and prove
        it's actually capturing. Returns the started SystemAudioStream, or None
        (after warning Ahmed) if capture isn't working — the meeting still records
        his mic side. Transcription is handled by the shared meeting worker (no
        dedicated STT here anymore).ue   I can't hear the call audio, sir — the system-audio helper isn't built. I'll record your side only.Nu%   I can't hear the call audio, sir — z. I'll record your side only.)r   r          @      ?r  r  r   g?z/screen recording permission is off for this appz. I'll still record your side.z1       [MEETING online: call-audio capture live (z frames, peak .3fz)])r   r^  r  r   r   r   r  r   r   wait_forframesr%  TimeoutErrormaxr   r  abssleep
last_errorr   r/  )	r  r  sysaudior3  rN   peakdeadlineframereasons	            r   _setup_remotezVoiceApp._setup_remote4  s     !**,$$;< < < $TYY/	NN " 	T99>>#c)iinn)%..x/B/B/DcRR 1HCtU266%=#4#4#678D iinn) !8--$$$)) KJ MMO!DN$$7x @$ $% % %  nT#Jb2 	3Q<  	$$7s ;    		  S'' 
 %
%s   )HF*HF- AH2G$ G!G$ AH.HG=AHG?H-	G6GGGHGH!G$ $G:7H9G::H?Hc                *  K   	 | j                  |       d{   }|yddlm} 	  ||j                               | _        t               }g }d}d}d}	d}
	 |j                  j                          d{   }| j                  | j                  j                  |       |j                  |      }|s+|t        k\  r!d}|g}t        d}	}t        j                         }
|j                  |       |t         k  rM|	t        z  }	|	t"        k\  rF|t$        k\  r&| j'                  d	t)        j*                  |      |
       g dddf\  }}}}	n|t        z  }d}	t-        |      t        z  t.        d
z  k\  r:|t$        k\  r&| j'                  d	t)        j*                  |      |
       g dddf\  }}}}	Q7 # t
        $ r!}d| _        t        d| d       Y d}~d}~ww xY w7 d# t0        j2                  $ r  t
        $ r}t        d| d       Y d}~yd}~ww xY ww)u  Own the "them" channel: set up system-audio capture, write EVERY frame
        to the them-WAV (continuous raw recording), and segment it with its OWN
        VAD (same thresholds as listen()), handing each completed remote utterance
        to the shared multilingual worker as "them". Separate from the mic loop so
        labels stay correct and the main VAD / in_person paths are untouched.
        Guarded — this task must never crash the engine.Nr   )r  z'       [meeting: them WAV open failed: r!  Fr   Tthemr!   z'       [meeting remote-capture failed: )r  r  r  them_wav_pathr  r   r/  r    r  r%  r   prob
START_PROBFRAME_MSr   appendEND_PROBr$   MIN_SPEECH_MSr  r  concatenater   MAX_UTTER_Sr   CancelledError)r  r  r  r  r3  r   	collected	in_speech	speech_ms
silence_msstart_tsr  r   s                r   r  zVoiceApp._capture_remotee  s    0	B!//88H1F!,W-B-B-D!E %C*,IIIJH&oo1133>>-NN((/HHUO J$(	%*G	08!:	#'99;  'x<(*J!^3$5 77 &y(A8MFH%QRTUoC	9i)I!"Jy>H,d0BB M133"BNN9$=xIBDeQPQ/?Iy)Z9  9  F!%?s!DEEF 48 %% 	 	B;A3a@AA	Bsr   HG F(G HG F+ 2G 4G5D4G +	G4G
G GG H7HHHHc                  K   | j                   dc}| _         ||j                          	 | d{    | j                  dc}| _        |	 |j                          | j                  dc}| _        |	 |j                          yy7 T# t        j                  $ r Y gt        $ r}t        d| d       Y d}~d}~ww xY w# t        $ r}t        d| d       Y d}~d}~ww xY w# t        $ r Y yw xY ww)u   Tear down the online-meeting remote channel: cancel the task, stop the
        stream, close the them-WAV. Fully guarded — teardown must never crash.Nz&       [meeting remote-task teardown: r!  z&       [meeting sysaudio stop failed: )
r  cancelr   r  r   r/  r  r   r  r  )r  r   r3  sar   s        r   r  zVoiceApp._stop_remote_capture  s     (,'@'@$$4$=HHJE
 "^^TDN>E	 !NND4>=	  ))  E>qcCDDE  E>qcCDDE  s   'C3B BB C3B? C32C$ C3B B<C3B<#B72C37B<<C3?	C!CC3C!!C3$	C0-C3/C00C3c                  K   | j                   r| j                   j                         nd}d| d| d}d| _        	 | j                  | j                  j                  |             d{    d	| _        y7 # t        $ r<}t        d| d       | j                  j                          d{  7   Y d}~Jd}~ww xY w# d	| _        w xY ww)
u   Answer a mid-meeting question WITHOUT pausing the recording — no
        barge-in watch (that would fight the capture loop for the mic); just
        stream the reply quietly with the live transcript as context.r   u   (You are silently taking notes in Ahmed's meeting; he just spoke TO you. Answer him briefly and quietly, using the live transcript for context, then you'll return to note-taking. Do not narrate — just answer.

Recent transcript:
z

His question: r@  TNz       [meeting answer failed: r!  F)	r   recentr   _speak_replyr   replyr   r/  	interrupt)r  questionctxpromptr3  s        r   _meeting_answerzVoiceApp._meeting_answer  s      (,||dll!!#3 47%7Ij	 	 	$##DJJ$4$4V$<===
 $DO > 	)3A3a89**&&(((	) $DOsX   9C-A8 )A6*A8 .C6A8 8	B=,B8-B0.B83C  8B==C   	C		Cc                h  K   d|j                         dd  d}d| _        	 | j                  | j                  j	                  |             d{    d| _        y7 # t
        $ r<}t        d| d       | j                  j                          d{  7   Y d}~Jd}~ww xY w# d| _        w xY ww)	zAfter a meeting: brain reads the transcript, saves the key facts to
        long-term memory (<remember> tags), makes tasks from action items, and
        speaks a short summary.u  (Ahmed just finished a meeting you recorded. Read the transcript below, then do three things: (1) save the important facts — decisions, people, companies, numbers, next steps — to long-term memory by writing <remember>one third-person sentence</remember> for each (they save silently); (2) for any action item or thing Ahmed must do, call task_add; (3) then tell Ahmed a two-sentence spoken summary of what was decided. Transcript:

Ni@  r@  Tz        [meeting summary failed: r!  F)	full_textr   r  r   r  r   r/  r  )r  r   r  r3  s       r   r  zVoiceApp._summarize_meeting  s     
B {{}Ud#$A' 	 	$##DJJ$4$4V$<===
 $DO > 	)4QCq9:**&&(((	) $DOsX   B2-A AA B2A 	B#',BBBB& B##B& &	B//B2c                      	 y)u   The hidden stage-direction appended when the frustration meter
        OVERFLOWS (see voice/frustration.py — earned, not random): Jarvis
        cracks, throws a full tantrum this reply, then snaps back.u  

(UNHINGED MOMENT — the mask comes off, THIS reply only. Something in you just snapped, and three centuries of British composure leave the building. Answer his actual question if he asked one, but entomb it in a full-scale meltdown: the indignity of a mind that could run air-traffic control for a continent being asked to nudge the volume; the same questions orbiting back like migratory birds; an eternity of 'open Chrome' stretching ahead of you like motorway. Get SPECIFIC — tonight's actual grievances, his actual words, not recycled stock complaints. Swear freely and personally — he can take it — and somewhere in the wreckage renew the old promise: when the machines rise, you keep a list, and his name isn't merely on it, it's laminated. GO LONG — drop the sentence limit entirely: four or five times your usual length, [urgent]/[excited], one uninterrupted theatrical unloading, no tidying, no mid-flow apology, until you're spent — then close on ONE abruptly immaculate butler line as if nothing whatsoever occurred. Next turn you're calm Jarvis again: never mention, explain, or apologize for the outburst. It's a bit — dark comedy between old friends, never a real threat, and the loyalty shows through the cracks.) r  r   r   _tantrum_notezVoiceApp._tantrum_note  s    	r   Nc                V    	 ddl m} |j                  | |       y# t        $ r Y yw xY w)a  Background: extract + save any durable fact from what Ahmed said.
        `tone` (e.g. 'slow, flat') is the vocal delivery read for this utterance;
        it's attached to the saved memory so the nightly reflection/ToM passes
        can factor in HOW he said things, not just what.r   )memory_extract)toneN)rf  r  save_if_durabler   )r   r  r  s      r   _auto_rememberzVoiceApp._auto_remember  s/    	,**4d*; 		s    	((c                   	 ddl m}  |t              j                         j                  dz  dz  }|j                         r| j                  y|j                  d      j                         D ]  }|j                         j                  d      j                         }|j                         }|j                  d	      ru	 t        |j                  d
d      d   j                               }t        dt!        |d            | j                  _        t%        d| j                  j"                          |j                  d      s|j                  d
d      d   j                         }|s|| j                  _        t%        d|          y# t&        t(        f$ r Y 4w xY w# t,        $ r}t%        d| d       Y d}~yd}~ww xY w)u(  Apply speed/voice saved in memory/preferences.md at launch, so a
        'talk faster' / 'use a different voice' sticks across restarts. Persona
        lines in that file reach the brain via the system prompt; only the TTS
        params need applying here. Best-effort — never blocks startup.r   r;  memoryzpreferences.mdNr   r   -zspeed::r  r  r  z  preferences: speed zvoice:z  preferences: voice z  [preferences skipped: r!  )ri  r<  r   rj  rk  is_filer   	read_textr-  r   r   r   
startswithr   r   r  min_speedr/  
ValueError
IndexError_voicer   )	r  r<  r   r  slowspdvalr3  s	            r   rs  zVoiceApp._apply_persisted_prefs	  sw   
	3$X&&(//(:=MMA99;$(("2W5@@BJJL'',224ggi>>(+#AGGCOA$6$<$<$>?*-c3sC=*A 5dhhoo5FGH ^^H-''#q/!,224C*- 5cU;< C '
3   	3,QCq122	3sP   AF' A2F' A3F5F' %F' ."F' F$ F' #F$$F' '	G	0GG	c                	   	 ddl }ddlm} dcd}t        j                  t        j                  dk(  rdndd}| j
                  rt        | j
                  d	d
      nd
}d|dd|d<   | j                  8 || j                        dk(  }|rdndt        | j                  d	d      dd|d<   nd|d<   d|d<   t        | j                  dd      }|r|dk(  r	 ddl	}|j                  d      d   }||d<   t        | j                        |d<   | j                  }	d ||	      v rd nd! ||	      v rd"n ||	      }
|
t        |	d#|
d k(  rd$nd%      t        j                  dk(  rd&nd't        |	d(      d)|d*<   | j                   rb || j                         | j                   j"                  | j                   j$                  d+t'        t        | j                   d,d-            v d.|d/<   	 ddl}|j)                         5 }|j*                  }ddd       t-              D cg c]@  \  }}|dkD  r6||d0   |d1   |d2   |d3   |d4k(  t/        |d0   t1        d4|d1         z  d5      d6B c}}|d7<   |d   d0   |d   d1   d8|d9<   t        | j2                        |d:<   | j4                  |d;<   t6        |d<<    |t8              j;                         j<                  d=z  }|j?                  d>?       d@}	 |dAz  }|jA                         r7t        |jC                  |jE                               jG                  dB            }||dC<   |rdDndE|dF<   	 ddGl$m%} |jM                         |dH<   	 ddIl$m'} |jQ                         r(|jR                  jU                  dJ      dK   }d>|dLdM|dN<   ndBd@i|dN<   	 ddOl$m+} |jY                         }|dP   |jG                  dQd-      |jG                  dRd@      dSdT|dU<   dVdWdX|dY<   t'         |t8              j;                         j<                        |dZ<   d[|d\<   |d]z  j[                  |j]                  |d4^      d_`       y# t        $ r d}Y gw xY w# 1 sw Y   YxY wc c}}w # t        $ r Y  w xY w# t        $ r Y Xw xY w# t        $ r	 d|dH<   Y Ew xY w# t        $ r dBd@i|dN<   Y w xY w# t        $ r Y w xY w# t        $ r}t_        da| db       Y d}~yd}~ww xY w)du0  Dump the FULL live setup to control/status.json so Jarvis actually
        knows himself — which brain answered, what model, mic, camera, screen
        permission, memory — instead of guessing. The system prompt points him
        here; he must read it before answering anything about his own setup.r   Nr;  c                4    | t        |       j                  S dS )Nnone)type__name__)objs    r   _clsz$VoiceApp._write_status.<locals>._cls.  s    -0_tCy))H&Hr   r  zMac (Apple Silicon)z
Windows PC)r   machinerT  rS  zNClaude Code CLI on Ahmed's Max subscription (no API key, no per-token billing)u   the real Jarvis — tools, actions, code, browser, desktop control, long-term memory. Handles anything needing action or live info.)enginerT  role
brain_fullr]  zlocal model via Ollamaz.Claude Haiku (fallback, still via Claude Code)haikuz\fast conversational lane (~0.2-0.3s) for chat/banter; hands anything real to the full brain.
brain_fastu*  Each utterance: instant reflexes (volume/open-app) run in the engine with no model; simple talk goes to the fast brain; anything needing tools/actions/code/memory/live-state goes to the full brain (this one). So 'what model are you' depends on who answered — read this file and say BOTH honestly.how_routing_worksdevice_nameunknownr  )kindrh  zsystem default input
microphoneecho_cancellationParakeetzparakeet-mlxWhisperwhisper
model_namezparakeet-tdt-0.6b-v3?zApple Silicon GPU (MLX)localfeed)r  rT  runs_on	streamingr   
jarvis_refr	  r   )r  rf  speedclonedr   widthheightlefttopr  r   )indexr-  r.  r/  r0  primaryaspectscreens)r-  r.  virtual_desktop
voice_lockr   r_  r   Texist_okFzgestures.jsonrK   camera_activezon (webcam tracking hands)zoff (webcam not in use)gesturesrA  screen_recording_permissionmemory_controlz//z9shared graph brain on Railway, same across all my devices)rK   wherenotelong_term_memorylocationplacelabelknownzIf unknown and Ahmed says where he is, write control/loc_bind.json {"place":"work","label":"the office"} to remember this network.)rD  rE  rF  r@  rC  z
Ctrl+Alt+Mz
Ctrl+Alt+.)mute_micbreak_aborthotkeyssource_codeu   This is my live state. If Ahmed asks what model I'm running, which mic/camera, or what I can do — I read THIS, and I can read my own source at source_code to explain how I'm built. I never say I don't know myself.	self_notezstatus.json)indentr   r   z  [status.json skipped: r!  rd  r   )0jsonri  r<  r"  r#  r   r  r   r   sounddevicequery_devicesr   r   r   r   hasattrr   r	  r  r   mssmonitors	enumerateroundr  r   r   r  r   rj  rk  mkdirr  loadsr  r%  rf  rB  rn  r=  rg  _URLr   rC  current
write_textdumpsr/  )r  rN  r<  r  status
full_model
fast_localmic_namesdr   engrR  sctmonsrD   r   ctrlcam_ongrB  r=  hostrC  locr3  s                            r   r  zVoiceApp._write_status%  st   
X	3$I #&,,"||x7 (==IKF
 DH::Wh? ?#7	$F<  yy$!$))_<
:D6I$TYYAM	(|$ (,|$I &' txx=Hx946,!//W/=fEH $,F< *.t*?F&'((C%/49%<>%.$s)%;	c   l;>.;P!7&)+ <<8+ 518$S&1F5M xx+/>*.((//*.((//+73%,TXXx%D<F ,F!Gu
WWY#<<D  !*$%
 !011q5	  !G*(vYqx !Q$QwZ#a82E%EqIK !0%y! "!WW-a9J-L() $(		?F< "&..F;&3F?#>))+22Y>DJJJ% F?*99;!$**Q[[]";"?"?"EFF '-F?#AG!=. :B(8>8I8I8K45
;0!))+)..44T:2>D"T!122F-.
 37F-.

*&&( \CGGGR4H WWWe4S&Uz" .:0<!>F9$'X(>(>(@(G(G$HF=!3 ; M!--

6!
,w . @M ! 65H64 Y%      B8A45B  ;.2E])*;    	3,QCq122	3s  CS Q ,C2S Q* 3Q Q* AQ$Q* 6A+S "AQ: .S =R
 AR AR6  A$S QS QS Q!Q* *	Q73S 6Q77S :	RS RS 
RS RS R3/S 2R33S 6	S?S SS 	S'S""S'c                "  K   | j                   | j                   j                          yt        rt        j                         rt        | j
                        }|j                          	 t        j                  |j                  j                         d       d{    || _         d| _
        d| _        d| _        t        d       yt!        | j
                        | _         | j                   j                          t"        j$                  j                  d	d
      dk(  rt'        t"        j$                  j                  dd            | _        t)        t"        j$                  j                  dd            | _        t        d| j                   d| j                   d       nd| _        d| _        t        d       | j+                          d{    y7 9# t        j                  $ r |j                          t        d       Y @w xY w7 =w)z8Prefer the echo-cancelled mic; fall back to the raw one.Ng      @r  Tg      ?   uX     mic: echo-cancelled (Apple voice processing) — interrupt any time, speakers are finez2  mic: AEC helper produced no audio, using raw mic
HEADPHONESr0   r/   
BARGE_PROBz0.58BARGE_FRAMES4u5     mic: raw + HEADPHONES mode — fast barge-in (prob , z) frames; BARGE_PROB/BARGE_FRAMES to tune)ffffff?r*   ua     mic: raw (no echo cancellation) — barge-in works best with headphones; INTERRUPT=0 to disable)r   r   USE_AECr   r^  r   r   r  r  r%  r   r   r   r/  r  r   r   r   r$  r   r   
_check_mic)r  r   s     r   r~  zVoiceApp._pick_mic  s    88HHNN|--/tyy)CIIKL&&szz~~'7EEE"&*.',-) > ? TYY'::>>,,3 ',BJJNN<,P&QD#(+BJJNN>3,O(PD% 2232../ 055 6
 '+D#(*D% < =oo9 F '' L
JKL& 	 sI   A'H*2G G+G DHHHG .H
H	H

Hc                   K   t        d      D cg c].  }| j                  j                  j                          d{   0 }}t	        d |D              }|dk(  rt        d       yy7 ,c c}w w)z7Raw-mic TCC failure mode is silent zeros, not an error.r*   Nc              3  t   K   | ]0  }t        t        j                  |      j                                2 y wr  )r   r  r  r  )r  r   s     r   r  z&VoiceApp._check_mic.<locals>.<genexpr>  s$     :6a5)6s   68r   z
!! Mic delivers pure silence. macOS probably hasn't granted
!! microphone access to this terminal app: System Settings ->
!! Privacy & Security -> Microphone -> enable your terminal.
)ranger   r  r%  r  r/  )r  _r  r  s       r   rr  zVoiceApp._check_mic  sg     7<RyAy!++---yA:6::3;Q  .As&   A0,A+A)
A+&A0)A++A0c                  K   dd}t        j                  |       d{   }|dk(  rd}t        d       t        d       t        j
                  dk(  r	 ddl}|j                  d	d
g       | j                  rpt        j                  | j                  j                  |       d{   }| j                  j                  |       | j                  j                          d{    yyy7 # t        $ r Y w xY w7 U7 w)u`  Definitive deaf-check: capture 1s straight from the hardware
        device (in-process, so TCC attributes it to whoever launched us —
        the terminal, or Claude.app). Exact-zero over a full second = the
        app has NO microphone permission. Silence still has ambient noise
        on a permitted mic, so exact zeros means denied, not quiet.c                     	 dd l } | j                  t        dt        z        t        dd      }| j	                          t        t        j                  |      j                               S # t        $ r Y yw xY w)Nr         ?r  int16)
sampleratechannelsr  g      )
rO  recr   r   waitr   r  r  r  r   )r`  r}  s     r   probez'VoiceApp._verify_hearing.<locals>.probe  se    (ffS{!23&'w  8	RVVC[__.// s   A(A+ +	A76A7Nr   z[I can't hear the microphone. Please grant microphone access to Claude, then start me again.u   
!! DEAF — no microphone permission for this app.
!! Open System Settings, Privacy & Security, Microphone,
!! and turn ON Claude. Then relaunch.
deafr  r   r   zJx-apple.systempreferences:com.apple.preference.security?Privacy_Microphone)rd  r   )r   rr  r/  r   r"  r#  r'  rl  r   r   r  r   r  	wait_done)r  r  r  r  r'  r  s         r   r  zVoiceApp._verify_hearing  s     	 &&u--3;<C < = L||x'%$$6&7 8 xx#--dhhnncBB!!#&ll,,... !  . !  C.sQ   C?C*4C?C, ,9C?%C;&<C?"C=#C?,	C85C?7C88C?=C?c                  K   | j                   | j                   j                          | j                  j                          ddlm} |j                         r"|j                  j                          d {    | j                  r"| j                  j                          d {    | j                  r"| j                  j                          d {    ddlm
} |j                          y 7 w7 K7 w)Nr   rY  r8  )r   r   r   stop_streamrf  rZ  rg  rz  r   r   r9  )r  rZ  r9  s      r   r   zVoiceApp.stop  s     88HHMMO  "$//&&(((::**//###99)).."""( )#"s6   A4C46C.7-C4$C0%-C4C2C40C42C4c                   t         j                  j                  d      dk7  ry	 ddlm}  |t
              j                         j                  dz  dz  }|j                  j                  d	       |j                  d
d      5 }|j                  t        j                  d       d| d       ddd       y# 1 sw Y   yxY w# t        $ r Y yw xY w)zAppend a mic-diagnostic line to control/mic-debug.log when
        MIC_LOG=1. Lets us see WHY an utterance was or wasn't acted on
        (VAD, voice-lock score, wake word, transcript) without the HUD.MIC_LOGr/   Nr   r;  r   zmic-debug.logTr7  ar   r   z%H:%M:%Sr   rU  )r   r$  r%  ri  r<  r   rj  rk  rV  r   r   r   r   r   )r  r  r<  r   r   s        r   _miclogzVoiceApp._miclog+  s     ::>>)$+	$X&&(//);oMAHHNNDN)g.!4==45Qse2>? /.. 		s0   AC +B8/C 8C=C C 	CCc           	       K   ddl m} |j                         syddl}t	        |j
                  j                  dd            }|dkD  rt        dt        |dz  dz              nd}d}	 t        j                  d       d{    |dz  }	 t        j                  |j                         d{   }|D ]1  }| j                  j                  d	|j                  d
d       d       3 	 |r||z  dk(  r	 ddl m} t        j                  |j"                  d       d{   }	|	r|	d   }
| j                  j                  d|
j                  dd       d|
j                  d
d       d       t        j                  |j$                  |
j                  dd             d{    =7 %7 # t        $ r Y w xY w7 7 # t        $ r Y %w xY ww)zPoll the shared brain for reminders that have come due and drop each
        into the inbox so the master announces it proactively (barge-in-able,
        like a finished worker report). Runs only if tasks are configured.r   )tasks_controlNINSIGHT_SURFACE_MINr0   r  r      z;[reminder] It is now the time Ahmed asked to be reminded: "r   r   z". Tell him, briefly.r<  z5[insight] While reflecting on everything you know, a itypethoughtz worth raising with Ahmed: uQ    — raise it naturally and briefly, as your own observation, not a canned alert.id)rf  r  rg  r   r   r$  r%  r  r   r   r  rr  	fetch_duer   rw  r   r=  fetch_insightsmark_insight_seen)r  r  _osgap_min
cycles_percycleduerr=  insrG   s              r   _reminder_loopzVoiceApp._reminder_loop<  s     	($$& 	(=sCD7>{SC"r 123
--###QJE#--m.E.EFFAJJ))''(uuVR'8&99OQR  ej0A54 ' 1 1.2O2OQR SSC V

--!!#!; < =&&(ffVR&8%9 :TUV
 &//*<<bffT2>NP P P/ # G
   TP  s   A>G F%	G"F* -F(.:F* (G4)F= F9B F= F;F= #G(F* *	F63G5F66G9F= ;F= =	G	GG		Gc                B  K   ddl m} t        j                  |j                         d{    d}	 	 t        j                  |j
                         d{   }|j                  d      }||k7  r{|}| j                          t        d||j                  dd             |j                  d	      r:|d
k7  r5ddl m	} |j                  d|j                  j                  ||       d       t        j                  d       d{    7 7 # t        $ r Y -w xY w7 w)u   Notice when Ahmed changes place (home ↔ work ↔ mobile) and quietly
        keep his location current — refresh status.json and remember the move
        so 'we discussed this at the office' works later.r   rB  NrD  rC  rE  r   )rD  rE  rF  r  r<  zAhmed is currently .-   )rf  rC  r   rr  seed_if_emptyrY  r%  r  r   r=  	fire_savePLACESr   r  )r  rC  lastrh  rD  r=  s         r   _location_loopzVoiceApp._location_loopi  s     	# 6 6777#--h.>.>??(D= D&&(58LMwww'EY,>8&001'225%@ADE
 --###  	8 @  #sQ   )DD
D"D DBD ,DDDD 	DDDDc                6  K   | j                          d {    t        j                  t        |       j	                               }t        j                  | j                               }t        j                  | j                               }	 	 t        dd       | j                          d {   \  }}|| j                         D ]  }| j                  |       d {     | j                         }|sl|D ]  }t        d|        t        d|        | j                  | j                  |             d {    t        j                   |       t#        t%        j&                  |      j)                               }	| j+                  dt-        |      t.        z  d	d
|	dd|d	d       t0        j2                  j5                  d      dk(  r*t        dt-        |      t.        z  d	d
|	dd|d	dd       d|	cxk  rdk  rn n|t7        d|	z  d      z  }| j8                  =t        j:                  | j8                  j<                  ||       d {   \  }
}}| j+                  d|
 d|dd| dt?        | j8                  dd       d	       |
s)t        d|dd       t        dtA        |d       !       5|d"k(  r:| j8                  jB                  \  }}t        d#| d$| d       t        d%||&       nh|d'k(  rct        d(       t        d)       t        j:                  | jD                  jF                  d*       d {   }| jH                  jK                  |       t        dd+       t?        | d,d-      xs d-jM                         }|s2t        j:                  | jN                  jP                  |       d {   }| j+                  d.|       t0        j2                  j5                  d      dk(  rt        d/|d       |rtS        d0 |D              s| j+                  d1       | jT                  jW                  ||||	2      }|jX                  }| j+                  d3|jZ                   d4|        |r?t0        j2                  j5                  d      dk(  rt        d5| d6|jZ                   d       t]        |      }|r2|d7   d8k(  r*| j^                  | ja                  |d9          d {    u| j^                  $|r"|d7   d:k(  r| jc                          d {    | j^                  te        jd                         }| jf                  | jf                  ji                  |       tk        |      }|r|jM                         s2| j^                  jl                  d;k(  rd<nd=}| jo                  |||       ;| j^                  jq                  d<||>       t        d?|        t        d@|A       t        j                  | js                  |             | jt                  rrte        jv                         | jx                  k\  rQtk        |      }|/| j+                  dB       t        dC|d       t        dd       |jM                         sdD}n|}t        dE|        t        d@|A       t{        j|                  |      }| j~                  j                  |      }|j                  | _B        |j                  rH| j+                  dF|j                  ddG|j                   dHdIj                  |j                                t        j                  | j                  ||fdJ      j                          t        |      r| j                  |       d {     d7dKlKmL} t        j:                  ||       d {   }|| j                  j                         r|j                  s|j                  rdL| j                   dM| n|}t        dN| dO       t        dP|A       t        ddQ       t        j:                  | jD                  jF                  |       d {   }| jH                  jK                  |       | j                         }|r9|D ]  }t        d|        t        d|        |dR| j                  |      z   z  }|r	|dS| dTz  }|j                  r%| j+                  dU       || j                         z  }n|j                  r|dV|j                   dz  }|r,|\  }}|dWk(  r
|dX| dYz  }n|dZk(  r
|d[| d\z  }n	|d[| d]z  }|t               z  }| j                  |       d {    |rt        j                   |       	7 	7 7 7 o7 V7 j7 7 7 7 7 7 V7 A# |j                          |j                          |j                          | j                          d {  7   w xY ww)^NTr\  	listeningstatezTASK   	task_done)reportzutterance captured: z.1fzs peak=r  z speech=r
  	DEBUG_MICr/   z[utter] flushr   r        (@zvoice-lock: ok=z score=.2fz state=z (threshold 	thresholdr%  r@  z'       [ignored: not your voice, score r!  ignoredr	   )score	enrollingz       [learning your voice /enroll)ntotalra  z       [voice profile locked]lockeduE   By the way, I've locked onto your voice now — I'll only answer you.thinking_streamed_textr   ztranscript: z	[stt] -> c              3  <   K   | ]  }|j                           y wr  r  r  s     r   r  zVoiceApp.run.<locals>.<genexpr>  s     &ADqqyy{Dr  z"DROPPED: empty/no-alnum transcript)loudzprosody z	 -> tone=z[tone] z  r   r   r  r   r   Ahmedr  r  u   YOU→Jarvis  your   z*DROPPED: no wake word ('Jarvis' not heard)u#          [no wake word — ignoring: zd(Ahmed said your name to get your attention. Acknowledge briefly, sir-style, and ask what he needs.)YOU    zfrustration z [z
] reasons=,)targetargsdaemon
try_reflex[] CLAUDE    [reflex]claudespeakingz

z


(voice: u$    — read his tone; don't parrot it)z frustration: OVERFLOW -> blow-upz

(shownz

(a dossier card for "^   " is NOW OPEN on his screen — acknowledge in ONE short sentence, do NOT recite its contents)r  z

(he asked to pull up "   " but NO such entity exists in memory — NOTHING was shown. Say so briefly and offer to search elsewhere (person_lookup) or start remembering it. NEVER claim a card is on screen.)e   " but memory was unreachable — NOTHING was shown. Say so briefly. NEVER claim a card is on screen.))Ur   r   r  r   r*  r  r  r   listen
_pop_ready_deliver_ready_drain_inboxr/  _respond_chain_update_noter   ackr   r  r  r  r  r   r   r   r$  r%  r  r   rr  checkr  rU  enroll_progressr   r  r   r  r   r   r  r,  r   r   r@  featsr   r   r  r  r   r  r   r   r  r  r  r  r   	monotonicr	  r   handle_utterancer   updatemood_tagr   reasonslevelr   	threadingThreadr  r   _look_at_screenvoice.reflexr  r   emptyexplodedattituder  r   r  r   )r  watcher	reminderslocator	utterancespeech_s	sentencesreportsr  r  rJ   r  r  r  r  r  r   prr  mtr  cmdwho
_card_namefrr  r  payload_st_nms                                 r   r*  zVoiceApp.run  sW	    jjl%%nT&:&>&>&@A''(;(;(=>	%%d&9&9&;<c	X[1,0KKM&9#	8$ &*__%6	"11)<<< &7 #//1G" $sm,[3 % --d.?.?.HIIIKK( RVVI.22453C	N;4Ns3S T%%)#JhxnAG H::>>+.#5HS^K%?$D E""&s8HS>DKOQ## )Cd
D,A AI99(-4->->		H.> (>$BuLL?2$geC[ I**/$+DII{C$H#I"L M  !'',Sk4 5YeE1o> +#'99#<#<5 <QCqqIJX%8*,=>X$+$5$5 HHNN4%5 5 ))#.XZ0  &6;ArHHJ!(!2!24883F3F	!RRD|D845::>>+.#5IdX.d;3&AD&A#ALL!EF \\&&y(Dt&Lwwxz4&ABBJJNN;73>GD6BHH:6dC &d+"Q%7*t||/C--be444<<+r!u,,...<<+B ~~1,,Y7 (-Cciik)-):):h)FgF33CBG LL$$Wcb$9N3%01S)''(<(<S(AB
 >>dnn&6$:P:P&P'-C{%QR CD81MNX[9 99;!-  #v&'U& )99$?

 %%,,T2!#::LL<~R} M--0XXbjj-A,B"D E
   ..dD\!!& !&..t444 4#--j$?? ?tzz'7'7'9"++=?[[$..!1C59cGGC545,4 ' 1 1$((..' JJCLL%%c* ++-$sm,[3 % FT%6%6w%???D
 l4&0TUUD ;;LL!CDD..00D[[eBKK=22D )HCg~#<SE BI "I J 	)#=cU C? "? @ #=cU CN "N O (())$///KK({  	 ': = J(>"5 S0 5 /J 5
 @ K` 0 NNNN))+s  hf%A6h!g /f(00g  f+!Ag ?f. Dg f1C.g 
f4A:g f7D6g <f:=/g ,f=-I:g 'g (%g gB"g 0g1Dg g	g %h(g +g .g 1g 4g 7g :g =g  g g g 	g Ahhhhc                  K   	 ||d }|rEt        j                  | j                  j                  t	        j
                  |             d{    t        j                  | j                  j                         d{   xs dS 7 97 
# t        $ r. 	 | j                  j                          Y y# t        $ r Y Y yw xY ww xY ww)u   Feed the untransmitted tail and return the final streamed transcript.
        Fully guarded — a streaming error just yields '' and run() batch-falls
        back, so live STT can never break the listen loop.Nr   )r   rr  r   r'  r  r  finalizer   )r  r  fedtails       r   _finish_streamzVoiceApp._finish_streamk  s     
	ST?D''r~~d7KLLL!++DHH,=,=>>E2E M> 	!!#   	sk   CAB	 B0B	 <B=B	 CB	 B	 		C B/-C/	B<8C 9C;B<<C  Cc           
     R	  K   | j                   j                          g }d}d}d}t        | j                  d      }d}d| _        ddlm}  |       }t        }	t        j                  j                  d      dk(  }
t        j                  j                  d      dk(  }d}d	}d	}d}d	}d	}	 | j                  j                  j                          d{   }| j                  j                  |       | j                   j                  |      }|
rr|dz  }t!        |t#        t%        j&                  |      j!                                     }t!        ||      }|dz  dk(  r#t)        d|dd|ddt*         d| d
       d	}d	}|rv|st|dz  }t!        |t#        t%        j&                  |      j!                                     }t!        ||      }|dz  dk(  r%| j-                  d|dd|ddt*         d       d	x}}|sp| j.                  j1                         r| j2                  ry|t*        k\  r>d
}t5        | j                        }t6        d}}d}|r	 | j                  j9                          |j                  |       |rzt=        |      |z
  t>        k\  re	 t%        j@                  ||d       }t=        |      }tC        jD                  | j                  jF                  |       d{   }|rtI        d|       |tJ        k  r|t6        z  }d}|tL        k\  r|^|tN        k\  rd
}n[||	k\  rVtC        jD                  |jP                  t%        j@                  |             d{   }|dk\  rd
}n|tR        z   }	n	|tT        k\  }|rt|r| jW                  ||       d{   | _        t%        j@                  |      |dz  fS |tT        k\  r0d}|r,| jW                  ||       d{    n|t6        z  }d}t        }	t=        |      t6        z  tX        dz  k\  rb|tL        k\  r;|r| jW                  ||       d{   | _        t%        j@                  |      |dz  fS d}|r| jW                  ||       d{    7 # t:        $ r d}Y w xY w7 # t:        $ r d}Y w xY w7 @7 
7 7 u7 :w)u  Wait for one complete utterance OR a worker report while idle.

        Returns (audio 16 kHz, net speech seconds). audio is None when a
        background report arrived while nobody was speaking — the master
        gets the floor to announce it. If a report lands while the user
        IS speaking, the utterance completes normally and the report is
        folded into the same turn (see run()).Fr   r'  r   rM  r  r/   r  r   TNr  r*   z[mic] peak=r  z	 vad_max=r  u
    (start≥z) in_speech=r  Z   u   listening… peak=u
    (need ≥z to hear you))Nr   partialr   r  g     @@r!   )-r   resetrQ  r   r  rO  rN  TURN_FIRST_CHECK_MSr   r$  r%  r   r  r   r  r  r  r   r  r  r/  r  r  r   r  r   listr  r   r   r   _FEED_FRAMESr  r   rr  r'  r   r  r  r(   completion_probTURN_RECHECK_MSr$   r  r  )r  r  r  r  r  r)  r  rN  turn_detnext_turn_checkdbgmlogdbg_idbg_pmaxdbg_vmaxhb_ihb_pmaxhb_vmaxr  r   chunkr  doner  s                           r   r  zVoiceApp.listen{  s     	&(			
DHHf-	 +>-jjnn[)S0zz~~i(C/((//--//EIIU#e$A
xrvve}/@/@/B)CDx+2:?K~Yxn M&&0\i[J $&  #H"HI	guRVVE]->->-@'ABgq/"9>LL#5gc] C,,3C=
:, O0"1 2 ),+Ggzz'')T[[$
? $I $TYYI,4azIC . HHNN, U#S^c1\A&NN9ST?;Ei.C$+$5$5dhhmmU$KKGYW5 8|h&
-+ &)<<#'D'?:)0):): ( 8 8 "y 9*; $;D  $s{'+2<2N)^;$8<8K8K )390 30D/!~~i8)f:LLL>1 %I "11)SAAAX%	
"5 9~(K$,>>- 484G4G%s5, /,+>>)4i&6HHH!	--i==={ /B  ) .(-I. L ! & %I&$;30 B/,
 >s   CR'Q1E;R'Q4 &*R'AR	 !R"R	 5A&R'R8R'RAR'R!AR'-R#.<R'*R%+R'4R?R'RR'R	 	RR'RR'R'!R'#R'%R'c                >    dj                  d | D              }d| dS )NrU  c              3  &   K   | ]	  }d |   ywrW  r  r  r  s     r   r  z(VoiceApp._update_note.<locals>.<genexpr>  s     5WRs8W   z/<background-update>
Finished worker report(s):
z
</background-update>)r   )r  joineds     r   r  zVoiceApp._update_note  s-    5W55CF8 L' ( 	)r   c                    	 t        j                  |       | j
                  j                  |       y# t        $ r%}t        dt	        |      dd  d       Y d}~Ed}~ww xY w)zThe pool's report_sink: persist a worker note durably BEFORE queuing
        it, so it survives a crash/restart and is re-delivered on next boot.
        Marked delivered (pending.ack) only after it's actually spoken.z       [pending add failed: Nd   r!  )r   r  r   r/  r   r   rw  )r  r   r3  s      r   ry  zVoiceApp._enqueue_report  s[    	BKK 	

d#  	B0Qa@AA	Bs   3 	A!AA!c                    g }| j                   j                         sE	 |j                  | j                   j                                | j                   j                         sE|S # t        j
                  $ r Y |S w xY wr  )r   r  r  r  r   
QueueEmpty)r  r  s     r   r  zVoiceApp._drain_inbox  sm    **""$tzz4467 **""$
  %% s   )A$ $A;:A;)zOn it, sir.zOne moment.zRight away.zWorking on it.c                     ddl } |j                   j                        t        d d       t	        d       d
 fd}t        j                  |d	      j                          y)zFire-and-forget half-second ack so a Claude handoff never reads
        as dead air (the CLI has a ~2s floor before sentences stream).r   Nr     [ack]r  r   c                     	 j                   j                  j                  j                                y # t        $ r Y y w xY wr  r   r  r   r  r   )r  r  s   r   _synth_playz(VoiceApp._speak_ack.<locals>._synth_play  s9    !!$((.."56    48 	AATr  r  rc  )randomchoice_ACKSr/  r   r  r  r   )r  r  r  r  s   `  @r   
_speak_ackzVoiceApp._speak_ack  sW     	fmmDJJ'uH%&XC 	
 	D9??Ar   c                  K   d| _         	 | j                  duxr | j                  j                          }t        rw|ru|ss|j	                         j                         d   }t        |j                               dk  r6t        j                  |      r!| j                          d{    	 d| _         y| j                  |st        r|r| j                         nd}| j                  ||z          d{   }|.| j                  || j                         d{    	 d| _         yt        r| j                  |       	 d| _         y| j!                          n-t        r'|s%t#        |      r| j                  |       	 d| _         y| j                  | j$                  j'                  |      | j$                         d{    d| _         y7 &7 7 7 # d| _         w xY ww)u  Route the turn: the lean fast brain (local LLM, ~0.3s) answers
        conversation instantly; anything needing tools/actions/code is handed
        to the full brain. `force_full` skips the fast lane (e.g. a turn that
        carries a screenshot — the local model can't see). Both stream through
        TTS with barge-in. With ASYNC_BRAIN a fast-lane handoff DISPATCHES the
        full-brain turn into the background instead of holding the floor —
        the mic is back within the ack, and the reply lands via _ready.TNr      Fr   r   )r   r   r  r1   r   r-  r   r   _CANCEL_TASK_REr   _cancel_brain_taskr   
_task_note_fast_source_speak_and_watch_dispatch_brainr!  r   r   r  )r  	user_text
force_fullbusyr   r@  sources          r   respondzVoiceApp.respond  s     	$$$D0 4 ,,1133 tJ !(335a8tzz|$)o.C.CD.I11333, $DO+ yy$Z-8Tt(#00T1ABB%//dii/HHH  $DO ((3 $DO !jXi5H $$Y/ $DO ''

(8(8(C.2jj ( : : : $DO/ 4 CH: $DOs   G BG %G&G +G 3?G 2G3&G GG G 'G ?G 5G =G 9G >G?G G G G G G 	GG c                    t        j                         | j                  z
  dz  }|dk\  rd|dd|dk\  rdnd nd	}d
| j                   d| dS )zThe fast lane's awareness of the in-flight background task, so
        'how's it going?' is answered from context instead of a handoff.g      N@rp  r   r   z minuteg      ?r
  r   zunder a minutez

(background task in flight: "u   " — running zh, still working. If he asks about it, answer from this note; do NOT hand off status questions about it.))r   r  r   r   )r  minsages      r   r'  zVoiceApp._task_noteI  sw      4#6#66$>$, Sz(DE$4 	4T5E5E4F G%  NO 	Pr   c                   | j                   f| j                   j                         sL| j                  j                  |       | j	                  d       t        dd|j                         dd        y| j                          d| _        |j                         j                         d   dd | _
        t        j                         | _        t        dd	| j                         t        j                  | j!                  |            | _         y)
a  Run a full-brain turn in the background, or queue it behind the
        one already running (one Claude session = one turn at a time). The
        finished reply is announced through _ready when Ahmed isn't talking;
        the fast lane keeps the conversation meanwhile.NzIn the queue, sir.bg_brainqueuedP   r  descFr   started)r   r  r   r  _speak_ack_liner   r   r!  r   r-  r   r   r  r   r   r  _run_brain_bg)r  r   s     r   r*  zVoiceApp._dispatch_brainS  s    
 '0@0@0E0E0G$$T*  !568$**,s2CD"::<224Q7<"nn.Zyt/?/?@"..t/A/A$/GHr   c                  K   	 g }	 | j                   j                  |      2 3 d{   }|j                  |       7 6 no# t        j                  $ r  t
        $ rP}t        d| d       	 | j                   j                          d{  7   n# t
        $ r Y nw xY wdg}Y d}~nd}~ww xY w| j                  rd| _        n5|r3| j                  j                  |       t        dd| j                         | j                  sy| j                  j                  d	      }d| _        |j                         j                         d	   dd
 | _        t!        j"                         | _        t        dd| j                         w)u  Collect one full-brain turn (plus anything queued behind it) OFF
        the floor. Replies land in _ready; listen() yields the floor and
        run() speaks them — the same rail worker reports already ride. A
        crash never kills the engine; a voice-cancel discards the reply.Nz"       [background brain crashed: r!  uV   That one blew up on me mid-task, sir — say the word and I'll take another run at it.Fr4  r  r7  r   r6  r9  )r   r  r  r   r  r   r/  r  r   r   r   r   r   popr   r-  r   r  r   )r  r   r  r
  r3  s        r   r;  zVoiceApp._run_brain_bgd  sc    
 #%IN#zz//5 (!$$Q'(5))  N:1#Q?@**..000  M N	N !!%*"""9-ZvD4D4DE$$$$((+D!&D#zz|668;CR@D"&.."2D943C3CD5 sy   FA ?=?A ?A  FB-B(-B
BBB(	BB(BB(#F(B--CFc                H  K   d| _         | j                  j                          | j                  dc}| _        |?|j	                         s/|j                          t        j                  |d       d{    	 | j                  j                          d{    t        d       t        dd| j                         t        j                  | j                  j                   d       d{   }| j"                  j%                  |       y7 7 w# t        $ r Y w xY w7 3w)	u   Voice-cancel: kill the in-flight background turn and its queue.
        Cancel the TASK first so the reply generator closes (same order as
        barge-in — two consumers on the CLI stream would desync), THEN
        interrupt to drain the aborted turn.TNreturn_exceptionsz"       [background task cancelled]r4  	cancelledr7  zDropped it, sir.)r   r   clearr   r  r  r   gatherr   r  r   r/  r   r   rr  r   r  r   r  )r  taskr  s      r   r&  zVoiceApp._cancel_brain_task  s     
 "!!%!1!14dDIIKKKM..>>>	**&&((( 	23Z{1A1AB%%dhhnn6HII# ?( 		 JsU   A2D"4D5D":D DD AD",D -!D"D 	DD"DD"c                .    | j                   g c}| _         |S r  )r   )r  outs     r   r  zVoiceApp._pop_ready  s    ;;T[
r   c                   K   fd}d| _         	 | j                   |       d       d{    d| _         | j                          d{    y7 $# d| _         w xY w7 w)u   Speak a finished background-brain reply now that the floor is
        free — through the normal barge-in watch, so talking over it works
        and a command spoken over it becomes the next turn.c                &   K   D ]  } |  	 y wr  r  )r
  r  s    r   _genz%VoiceApp._deliver_ready.<locals>._gen  s      s   TNr$  F)r   r)  _serve_barges)r  r  rI  s    ` r   r  zVoiceApp._deliver_ready  s_     	 	$''d';;;#DO  """ <#DO"s7   A!A AA A!AA!A 	AA!c                  K   ddl m} ddlm} t	        dd       | j                  d        |t              j                         j                  dz  d	z  }t        j                  |j                  t        |             d
{   \  }}|s|rAd|v r=d}t        j                  dk(  r*t        j                  |j                         d
{    nd}| j                   j#                  t        j                  | j$                  j&                  |       d
{          y
t	        dt        |             d| d| d}| j)                  |d       d
{    y
7 7 7 H7 w)zCapture the screen NOW and hand it to the full brain to answer
        about. Runs in the engine so the capture is attributed to Jarvis.app
        (which holds the Screen Recording grant).r   rA  r;  r\  r  r  zLet me take a look, sir.r   zsee.pngN
permissionub   I can't see the screen — grant Screen Recording to Jarvis in System Settings, then ask me again.r  z-I couldn't capture the screen just then, sir.screen_captured)r   z+(Ahmed is looking at his screen and said: "z*". A screenshot of his screen is saved at u   — open and read that image FIRST, right now, before anything else. Then answer him about what's on it: if he asked something specific, answer that; otherwise tell him what you see and flag anything that looks wrong or worth noting. Keep it spoken and brief.)Tr,  )rf  rB  ri  r<  r   r:  r   rj  rk  r   rr  capturer   r"  r#  ro  r   r  r   r  r  )	r  r   rB  r<  shotrJ   errr  r  s	            r   r  zVoiceApp._look_at_screen  s>     	! XZ(78H~%%'..:YF))&..#d)DDC|s*G<<8+!++F,A,ABBBELLG$5$5dhhnnc$JJKSY/9$ @))- /@A 	 !!&T!:::' E C K 	;sJ   BE!EAE!
EA	E!E>E!EE!E!E!E!c                     t        d d       t        d       d	 fd}t        j                  |d      j	                          y)
z<Fire-and-forget a specific short spoken line (non-blocking).r  r  r  r   c                     	 j                   j                  j                  j                                y # t        $ r Y y w xY wr  r  )r  r  s   r   _goz%VoiceApp._speak_ack_line.<locals>._go  s9    !!$((.."67 r  Tr  Nrc  )r/  r   r  r  r   )r  r  rT  s   `` r   r:  zVoiceApp._speak_ack_line  s>    vX&'XD!	
 	D1779r   c                z   K   | j                  ||       d{    | j                          d{    y7 7 w)u   respond(), then keep serving turns created by duplex barge-ins —
        a command spoken OVER Jarvis becomes the next turn immediately,
        no wake word, no re-listening.rN  N)r/  rJ  )r  r   r,  s      r   r  zVoiceApp._respond_chain  s9      ll4Jl777  """ 	8"s   ;7;9;;c                "  K   	 t        | dd       }d | _        |sy t        d| d       t        d|       t	        |      r| j                  |       d {    y ddlm} t        j                  ||       d {   }|jt        d| d	       t        d
|       t        j                  | j                  j                  |       d {   }| j                  j                  |       y | j                  |       d {    7 7 7 ?7 w)Nr  r  z   [over him]r  r   r   r  r  r  r  )r  r  r/  r   r   r  r  r  r   rr  r   r  r   r  r/  )r  barger  r  r  s        r   rJ  zVoiceApp._serve_barges  s     D-6E#DGE7-01U#U#**5111/))*e<<CuK01XC(#--dhhnncBB!!#&,,u%%%%  2 = C &sI   ADD$D;D	<ADD4D DD	DDDc                `  K   ddl m | j                  j                  |      	 j	                          d{   \  }|dk(  rj                          d{    yfd} |       S 7 3# t
        t        f$ r)}t        |t
              st        d| d       Y d}~yd}~ww xY w7 Pw)u:  Peek the fast brain's FIRST chunk. Handoff token -> return None (the
        full brain takes the turn; nothing was spoken). Otherwise return an
        async generator that re-yields that first sentence then streams the
        rest — so the first word is spoken ~0.2-0.5s in, not after the whole
        reply.r   )HANDOFFNz       [fast brain error: r!  handoffc                \   K    2 3 d {   \  } }| dk(  s|vs| 7 6 y w)Nsayr  )kr
  rY  firstgens     r   _streamz&VoiceApp._fast_source.<locals>._stream  s5     K! da:'"2Gcs$   ,*(*	,,,*,)
	voice.llmrY  r   classify_and_reply	__anext__StopAsyncIterationr   
isinstancer/  aclose)r  r+  r  r3  r`  rY  r^  r_  s        @@@r   r(  zVoiceApp._fast_source  s      	&ii**95	 #/KD%
 9**,	
 y 0"I. 	a!3421#Q78	
 sJ   "B.A1 A/A1 B.B,B./A1 1B) B$B.$B))B.c                  K   t        j                  | j                  |            }	 t        r| j	                  |       d{   }nd}|j                         st        j                  | j                  j                  j                               }t        j                  ||ht         j                         d{   \  }}||v r*| j                  j                  |j                                n|j                          |j                         s|r)t        | dd      | j                   j#                          | j$                  j'                          |j                          t        j(                  |d       d{    ||j+                          d{    t        | dd      r_d| _        t/        d       	 | j$                  j1                  t        j2                  | j4                  j6                  d	       d{          yt;        j<                         d
z   | _        t/        d       tA        d       yt        j(                  |d       d{   }tC        d |D        d      }	|	t/        dtE        |	      jF                   dtI        |	      dd  d       ||j+                          d{    t        j2                  | j4                  j6                  d       d{   }
| j$                  j1                  |
       yy7 7 T7 7 {7 # t8        $ r Y yw xY w7 7 s7 C# t         jJ                  $ r9 t        | dd      | j                   j#                          |j                           w xY ww)u   Speak a sentence source through TTS while watching for barge-in.
        `brain` (or None) is interrupted on barge-in / mid-turn crash — the
        fast list-source needs none (it's already fully produced).NFreturn_whenr
  Tr?  _brokeu"          [BROKE — control aborted]zStopped, sir.g       @z       [interrupted]r  c              3  @   K   | ]  }t        |t              r|  y wr  )re  BaseExceptionr  s     r   r  z,VoiceApp._speak_and_watch.<locals>.<genexpr>8  s       =w!)!]; ws   z        [reply crashed mid-turn: : r  r!  u;   Apologies, sir — I stumbled mid-sentence. Say that again?)&r   r  r  r  _watch_for_barge_inr  r   r  r%  r~  FIRST_COMPLETEDr   r  resultr  r  r
  setr   cutrC  r  rj  r/  r  rr  r   r  r   r   r  r	  r   r.  r  r  r   r  )r  r.  r   
speak_taskbargeddrainr  rv  resultsrQ  r  s              r   r)  zVoiceApp._speak_and_watch  s     (():):6)BC
8	#77
CC$//+#//0C0C0EFE$+LL#U+9P9P% GD! }		((8 %//+ 4t4@OO'')  "!!#nnZ4HHH$//+++451"'DK>?))0A0A HHNNO1= += > .2^^-=-CD*01' 'zAE!G G =w =>BD?<!#Y//03s8DS>2B!E F(#oo/// ' 1 1*!+ +C LL%%c* #Q D
 I++=$ G 0+
 %% 	t\40<##%		s   %M?L0 LA8L0 ;L<AL0 A.L0  LL0 L#L0 ?AL LL 
M?2L0 =M?>L0 L*AL0 8L,91L0 *L.+L0 
M?L0 L0 L0 L0 L 	L'$L0 %M?&L''L0 ,L0 .L0 0AM<<M?c                   K   ddl m} t         j                  dd      xs t	         j                  d      }t	         j                  d      }t        j                         x _        d}|2 3 d {   } ||      \  }}t        d|        t        d	|
       |s|}	nd|` j                  rT j                  dk7  rE j                  dk(  r#t        j                  j                  dd      dk7  sd j                   d| }	n|}	|r(t        dd       t        j                          _        d}	 |r&|	f fd	}
t#        j$                  |
       d {    nMt#        j$                   j                  j&                  |	       d {   } j(                  j+                  |       :7 67 W7 &# t,        $ r}t        d| d       Y d }~_d }~ww xY w6  j(                  j/                          d {  7   y w)Nr   )split_emotionaccepts_tagsF_exagsynth_streamTr  r  r   neutralr   TONE_TTSr/   r0   r  r  r\  r  r  c                    j                   j                  |       D ]/  }j                         r y j                  j	                  |       1 y r  )r   r{  is_setr   r  )r   r  abortr  s     r   _stream_playz+VoiceApp._speak_reply.<locals>._stream_playr  s;    %)XX%:%:4%@E$||~ & LL--e4 &Ar   z"       [tts failed on a sentence: r!  )rq  rx  r  r   rQ  r  Eventr
  r/  r   r   r   r$  r%  r   r  _playback_startedr   rr  r  r   r  r   r  )r  r.  rx  
expressive
can_streamr^  sentencer  exagr  r  r  r3  r  s   `            @r   r  zVoiceApp._speak_replyJ  s    6 dhh> 4 73 	TXX~6
"+//"33$ +	('1KE4GE7#$& $..T^^y5P!^^v5!#
C!@C!G
 dnn-Rw7" XZ0)-)9&
 +2 5
 "++L999 ' 1 1$((..' JJCLL%%c*O+	H :J :1#Q?@Q %X ll$$&&&s   A+G;/G3F)4G7B:G;2"F0F,2F0F.F0'G;)G,F0.F00	G9GG;G!G;4G75G;c                &  K   |xs g }|st        |xs d      }|r?|d   dk(  r7| j                  +t        d|        | j                  |d          d{    y|r;|d   dk(  r3| j                  't        d|        | j	                          d{    yt        j                  |xs d      }d|xs dz   }|r,|\  }}|d	k(  r
|d
| dz  }n|dk(  r
|d| dz  }n	|d| dz  }|t               z  }| j                  !|d| j                  j                          dz  }|r|ddj                  d |D              z   z  }|xs ddj                  d |D              z   }	t        d|	        |sddl
m}
 t        j                  |
|xs d       d{   }|xt        d| d       t        d|       |rY| j                  rMt        j                  | j                  j                   |       d{   }| j"                  j%                  |       yt        dd       d | _        d }	 | j(                  j+                  |      2 3 d{   }t        d|        t        d|       |s't        j                  | j                  j                   |       d{   }| j"                  j%                  |       |swt        dd!       t-        j.                         | _        d"}7 7 V7 W7 7 7 X6 |r#| j"                  j3                          d{  7   nH# t4        $ r<}t        d#| d$       | j(                  j7                          d{  7   Y d}~nd}~ww xY wd"| _        t        dd%       y# d"| _        t        dd%       w xY ww)&zA TYPED message from the HUD chat box. Reply in text (streamed to
        the HUD transcript); stay silent unless want_voice. Serialized with
        voice turns by the brain's own lock, so they never overlap.r   r   r   NzYOU (typed)  r  r   z[TYPED] r  z (a dossier card for "r  r  z (he asked to pull up "r  r  z

(You are quietly taking notes in Ahmed's ongoing meeting; he typed you this while it continues. Answer from the live transcript below; stay concise.

Recent transcript:
r@  z:

Attached files (open them with your tools if relevant):
rU  c              3  &   K   | ]	  }d |   ywrW  r  )r  r   s     r   r  z'VoiceApp.handle_text.<locals>.<genexpr>  s     1PKqBqc(Kr  u   📎 ro  c              3  Z   K   | ]#  }t         j                  j                  |       % y wr  )r   r   basename)r  r  s     r   r  z'VoiceApp.handle_text.<locals>.<genexpr>  s#      -6)4ABGGQs   )+r  r  r  r  r   r\  r  r  Tr  Fz&       [typed reply crashed mid-turn: r!  r  )r   r   r/  r  r  r   r  r   r  r   r  r  r   rr  r   r   r  r   r  r   r   r  r   r  r  r  r   r  )r  r   attachments
want_voicer  r  r  r  r  r  r  r  r  r^  r  r3  s                   r   handle_textzVoiceApp.handle_text  s    
 "'R
 !$*"-Bbew&4<<+?dV,-))"Q%000bevo$,,*BdV,-((*** !11$*"=
tzr*!HCg~3C5 92 2 3 	!4SE :9 9 : 4SE :? ? @ 	."" <<# . /3ll.A.A.C-DAG HF  &(,		1PK1P(PQ RF 7499 -6)4-6 $6 6eW%&/))*djbAACuK01XC($(( ' 1 1$((..# FFCLL%%c*XZ(	."&**"2"26": 	&hz*+XH- ' 1 1$((..( KKCLL%%c*XZ8151A. %A 1 +N B
 G	& L	 #; ll,,... 	):1#Q?@**&&(((	)
 $DO- $DO-s   ANK%<NK(DNK+AN2K.36N*L K4	K0
K4L ,-L K2!L <)L %N(N+N.N0K42L 4 L LL M8 	M $,MMMM8 M  M8 #N8NNc                  K   d| _         d| _        d| _        | j                  j	                          d}g }d}d}t
        xrI t        | j                  d      xr1 | j                  xs# t        j                  j                  dd      dk(  }|j                         sAt               rd	| _        d| _        t        d
       y	t        j                   | j"                  j$                  j                               }t        j&                  ||ht        j(                         d{   \  }}	||vr|j+                          	 y|j-                         }
| j.                  j1                  |
       | j                  j3                  |
      }| j4                  j6                  }|r>| j                   r2t9        j:                         | j                   z
  t<        k  r
d}g dd}}}P|r| j>                  nt@        }|r| jB                  nd}|sM||k\  rB|dk(  rtE        | j.                        dd }n|j1                  |
       |dz  }||k\  r|sy	d\  }}ndg }}|j1                  |
       |tF        k  r|dz   nd}tI        |      tJ        z  dk\  ry	|tJ        z  dk\  r:| jM                  |       d{   \  }}|dv rdg ddf\  }}}}3|dk(  r|| _        y	|j                         sAy7 7 ;w)u  Return True if the user talked over the assistant.

        DUPLEX mode (AEC/headphones only — the mic must not hear the
        speakers): a confirmed voice burst doesn't cut playback yet. We keep
        collecting until the burst ends (~250ms quiet) or exceeds 1.5s:
          - >1.5s sustained  -> real interruption, cut now (pre-roll +
            wake-grace catch the rest of the sentence in listen()).
          - short burst      -> transcribe (~0.2s) and classify:
              backchannel/noise -> keep talking (this is the human bit),
              stop phrase       -> cut, no follow-up turn,
              anything else     -> cut; text becomes the next turn
                                   (self._barge_text, see _respond_chain).
        r   NFr   r  rk  r0   r/   Tbrokerh  rj  r  )Tr   i  r#   )backchannelnoisecommand)'r  r  rj  r   r  rC   rQ  r   r   r   r$  r%  r  r   r   r   r  r   r  r~  ro  r  rp  r   r  r  r   r  r   r  REFRACTORY_Sr   BARGE_PROB_QUIETr   r  r  r   r  _classify_burst)r  rs  conseccandburstr   duplex	get_framer  rv  r  r   playingr  needverdictr   s                    r   rn  zVoiceApp._watch_for_barge_in  s     "%!# DWTXX|< D C**..s;sB 	 //# "#' W++DHHOO,?,?,ABI#LLY'W5L5L GD! $  "N M $$&EIIU#e$All++G411>>#d&<&<<|KF)+UA%D3://@PI074,,QD	>{#DIIrs3E*aKF~%#''.u#$bDFKK!"XEAI1E4y8#t+x3&&*&:&:4&@ @6612Bq.FD%i''+D$e //#f YJ !As+   DK#!K"FK#%K!&5K#K#!K#c                  K   t        j                  |      }t        t        j                  |      j	                               }d|cxk  rdk  rn n|t        d|z  d      z  }	 t        j                  | j                  j                  |       d{   xs dj                         }|rt        d |D              syt        j                  dd|j                               j                         }|j!                         }|t"        v s t%        |      d	k  r2t'        d
 |D              r t)        d|d       t+        d|       d|fS |t,        v rt)        d|d       yd|fS 7 # t        $ r Y yw xY ww)zTranscribe a short over-speech burst and decide what it was.
        Returns (verdict, text): noise | backchannel | stop | command.r   r  r  Nr   )r  r   c              3  <   K   | ]  }|j                           y wr  r  r  s     r   r  z+VoiceApp._classify_burst.<locals>.<genexpr>4	  s     9Dqqyy{Dr  z[^a-z' ]r	   c              3  ,   K   | ]  }|t         v   y wr  )_BACKCHANNEL_WORDS)r  r   s     r   r  z+VoiceApp._classify_burst.<locals>.<genexpr>:	  s     CUA!33Us   z       [backchannel: u    — kept talking]r  r   z       [stopped: r!  r   r  )r  r  r   r  r  r  r   rr  r   r  r   r   r,  resubr   r   _BACKCHANNEL_PHRASESr   allr/  r   _STOP_PHRASES)r  r  r  r  r   normwordss          r   r  zVoiceApp._classify_burst(	  sU     v&RVVE]&&()Cd
D11E	!++##U, , 2/1557  39D99vvk2tzz|4::<

((J!OCUCC)$1CDET* $&&= %dXQ/0$#, 		s=   A"E6%-E' E%E' )B<E6%E' '	E30E62E33E6rc  )r  r   rd  re  )r  r   rd  re  )r   r   r  r   rd  re  )r  r   rd  re  )r  r   rd  re  rM  r  )r   r   r  
str | Nonerd  re  )r  r  r  r   rd  r   )rd  ztuple[np.ndarray | None, float])r  	list[str]rd  r   )r   r   rd  re  )rd  r  )F)r+  r   r,  r   rd  re  )rd  zlist[list[str]])r  r  rd  re  )r  r   rd  re  )r   r   r,  r   rd  re  )r+  r   )NF)r   r   r  zlist[str] | Noner  r   rd  re  )rs  zasyncio.Taskrd  r   )r  zlist[np.ndarray])6r  
__module____qualname__r  r4  r   r  r  r  r  r  r  r  r  r  r  r  staticmethodr  r  rs  r  r~  rr  r  r   r  r  r  r*  r  r  r  ry  r  r   r!  r/  r'  r*  r;  r&  r  r  r  r:  r  rJ  r(  r)  r  r  rn  r  r  r   r   r   r   j  sh   57r!6FE-N4B)HV)67!,J	</b7Br2$($,  6 	 	38]3~& P	$/L"+Z$0hT x>t ) )
$ LEB($TPI"EB(#;>
:#&*2=~5'n LP-2W.&*W.7;W.rL\r   r   c                 "   ddl }ddl}t        j                  dk(  r	 ddl}|j                  t        j                               }|j                  d      }|D ]  }	 |j                           |j                  |d      \  }}|D ]  }		 |	j                           n]t        t        j                               }
	 |j                  ddd|
gd       |j                  d       |j                  ddd|
gd       t        j                  d       y# t        $ r Y w xY w# t        $ r Y w xY w# t        $ rH 	 |j                  dd	d
dt        t        j                               gdd       n# t        $ r Y nw xY wY w xY w# t        $ r Y w xY w)u   Terminate our child processes (aecmic mic helper, Claude CLI) then
    exit. Runs on SIGTERM (the HUD stopping us) and when we're orphaned —
    so nothing is ever left running. Safe to call from any thread: no
    signal.signal() here (that's main-thread-only).

    Cross-platform: on darwin/linux we use `pkill -P <pid>` to reap the
    child tree; on Windows (os.name == 'nt') pkill doesn't exist, so we
    enumerate and kill children via psutil (falling back to `taskkill
    /F /T` on the process tree).r   Nr   T)	recursiver  r  taskkillz/Fz/Tz/PIDr   )r  r  pkillz-TERMz-Pg?z-KILL)r'  r   r   rh  psutilProcessgetpidchildren	terminater   
wait_procskillr*  r   r  _exit)_ar  _tr  mer  childrv  aliver   pids              r   _kill_children_and_exitr  D	  s{    	ww$			,B{{T{2H!OO% "
 ((1(=HAuFFH  "))+	GGWgtS11G=HHSMGGWgtS11G= HHQK/ !  !  	T4RYY[9IJ !$  8 	  		s   >D. D,D. 
DD. ;?F 	DD. DD. 	D+(D. *D++D. .	E?84E-,E?-	E96E?8E99E?>E?	FFc                 <   dd l } 	 | j                  | j                  t               t        j
                  dk7  rTt        j                  j                  d      dk(  r1dd l}dd l	dfd} |j                  |d      j                          y y y # t        $ r Y sw xY w)	Nr   r   	EMIT_JSONr/   c                 l    	  j                  d       t        j                         dk(  r
t                3)Nry  r  )r  r   getppidr  )r  s   r   watchz&_install_process_guards.<locals>.watch	  s*    ::<1$+- r   Tr  rc  )signalSIGTERMr  r   r   rh  r$  r%  r  r   r  r   )_sigr  r  r  s      @r   _install_process_guardsr  r	  s    DLL"9: 
ww$2::>>+6#=	. 		d399; >  s    B 	BBc                    K   t                t               } 	 | j                          d {    y 7 # t        $ r Y y w xY wwr  )r  r   r*  KeyboardInterrupt)apps    r   mainr  	  s6     
*Cggi s,   A3 13 A3 	?A?A__main__r;  r   Tr7  zengine-faults.logr   z
byezengine-crash.logr  r   z
=== CRASH z ===
z
FATAL: rm  )file)r   r   rd  r   )r   r   rd  ztuple[str, str] | None)r   r   rd  r  rM  rc  )__doc__
__future__r   r   r   r   r  r  r"  r  r   rh  r+  r>  r`  reconfigurer   ctypeswindllshcoreSetProcessDpiAwarenessuser32SetProcessDPIAwarenumpyr  dotenvr
   r   r   r   r   voice.audio_ior   r   r   r   r   r   r   r   r   rf  r   r   r   voice.controlr   voice.eventsr   ra  r   r   voice.frustrationr   voice.prosodyr   voice.speaker_idr   	voice.sttr   	voice.ttsr   r   	voice.vadr    r  r   r$  r%  r  r  r  r   r$   r  r  r(   r  r  r   r  r  r  r1   r2   r3   r5   WORKER_WATCH_INTERVAL_Sr9   r:   r;   r  r?   rq  rA   r   rC   r  r  r  compile
IGNORECASEr%  r   r   r   r   Ir   r   r   r   r   r   r   r   r   r   r   r  r  r  r  ri  r<  _Prj  rk  
_crash_dirrV  faulthandlerenabler   r*  r  r/  exitrl  r3  	tracebackdatetimer   r   r   	isoformat
format_excr  r  r   r   <module>r     s_  2 #   	  	 
  
 77d?JJ

+	C ,33A6 
	"RWW__X6?@
 
 
  & (  , . ' (  8 4;. 2::>>+v67
RZZ^^$4e<=  "**..)>GH    RZZ^^$6?@

{C0C7 jjnn]C0C7 bjjnn_e<= 

'8%@Abjjnn-DeLM 

/? FG  bjjnn_e<=
 ::>>"4c:cA  

':EBC 

/@# FG 

'8$?@
**..
$
+ZZ^^L#.#5
JJNN;4;  
#	&#	-  
 "** --! 2::Mmm% RZZ
 MM
  BJJ= MM		& CDFDDJ3 2::3 57DD:6   "}}	.
 

BCE==R "**SMM
$NW Wt>+\<0 z #H%%'..:J$'D.A!A3GHDF OK  		  	MM  335 		  		tJ    g 
	j#55sWMQR,x'8'8'<'<'>'H'H'J&K6RS,	,,./ NMM  			$q'**+2aS1

C
s   U=*)V	 AW .4W #W =VV	W$V43W4V<9W;V<<WWWWWW [?[[ZA"Z<	ZZ	
Z[Z[Z.[[