
    i                        U d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
m
Z
mZmZ ddlmZ ddlmZmZ  ed      Zej&                  j)                  d ee             d Z e        edz  d	z  d
z  Zedz  dz  dz  Zedz  Zedz  dz  Zeeeej6                  fD ]  Zej;                  dd         ej<                  ej>                  d ej@                  edz  d       ejB                  ejD                        g        ejF                  d      Z$ejJ                  jM                  d      xs ejJ                  jM                  d      xs dZ'dZ(dZ)	 ddl*m+Z, ddl-m.Z/  e0e'      Z1e1r e,jd                  e'      Z3ndZ3ejJ                  jM                  d"d      Z6ejJ                  jM                  d#d      Z7 e8ejJ                  jM                  d$d%            Z9	 dd&l:m;Z;m<Z<m=Z=m>Z> dZ?ejJ                  jM                  d(d)      Z@	 ddlAZBdZCd*ed+eDfd,ZEd-eDd+dfd.ZFdSd/ee   d+eGeD   fd0ZHd+eDfd1ZId2eDd+dfd3ZJd4ed5eDd6eGe   d+efd7ZK G d8 d9      ZL eL       ZMd:ed;ed<eGe   d=ed>e8d+eDfd?ZNd+efd@ZOdAeDdBe8d+ePe0ef   fdCZQ	 dTd;ed6eGd5eDd<eGdBe8dDe8d+ePe0eeGf   fdEZRd+ePe=e>e<f   fdFZS G dG dH      ZTdaUeeT   eVdI<   d+eTfdJZWd*ed5eDd+eDfdKZXdSd/ee   d+eGeD   fdLZYd/edMed+eDfdNZZdUd	eGeD   dOe0d+dfdPZ[dQ Z\e]dRk(  r e\        yy# e4$ r d Z1dZ3e$jk                  d!       Y w xY w# e4$ r d Z?e$jk                  d'       Y Vw xY w# e4$ r d ZCY Aw xY w)Vu*  
Genesis Browser Skill Framework
================================
Pre-mapped browser workflows stored as skills, invoked by any agent via
Gemini Flash agentic vision. Skills are JSON workflow maps loaded from
.claude/skills/browser/, executed with Playwright, and stored to Graphiti
for RLM (Recursive Language Model) memory recall.

Architecture:
    Skill JSON  →  BrowserSkillEngine.execute_skill()
                       │
                       ├─ Playwright (local headless or Browserless)
                       │
                       ├─ Gemini 3 Flash vision (Think→Act→Observe loop)
                       │
                       └─ Graphiti episode store (success) / skill map fix (failure)

Memory Model (Ebbinghaus-inspired):
    - Skills not used in 30 days get a re-verify flag set
    - Every successful run stores a Graphiti episode with screenshot hashes
    - Next run recalls from Graphiti FIRST, uses static JSON as fallback

Usage:
    python3 core/browser_skill_engine.py --list
    python3 core/browser_skill_engine.py --list --platform gohighlevel
    python3 core/browser_skill_engine.py --execute ghl_create_subaccount_api_integration         --params '{"subaccount_name": "Test Co", "integration_name": "Genesis API"}'
    python3 core/browser_skill_engine.py --learn gohighlevel "create a new pipeline"

MCP Tool Registration (genesis-core server):
    execute_browser_skill(skill_id, params)
    list_available_skills(platform)
    learn_browser_skill(platform, goal)

Author: Genesis System (Session 59)
Date: 2026-02-24
    N)datetimetimezone	timedelta)Path)AnyOptionalz/mnt/e/genesis-systemc                     t         dz  dz  } | j                         sy| j                  d      j                         D ]  }|j	                         }|r|j                  d      sd|vr+|j                  d      \  }}}|j	                         }|j	                         j	                  d      j	                  d	      }|s|st        j                  j                  ||        y)
z5Load config/secrets.env into os.environ (idempotent).configzsecrets.envNutf-8encoding#="')
GENESIS_ROOTexists	read_text
splitlinesstrip
startswith	partitionosenviron
setdefault)secrets_pathrawlinekey_vals         2/mnt/e/genesis-system/core/browser_skill_engine.py_load_secretsr#   :   s    (*]:L %%w%7BBD ,yy{ts+s$nnS)Qiikiik$**3/3JJ!!#s+,    z.claudeskillsbrowserdatascreenshotsbrowser_skillslogszbrowser_skill_state.jsonT)parentsexist_okz+%(asctime)s [BSE] %(levelname)s %(message)szbrowser_skill_engine.logr   r   )levelformathandlersbrowser_skill_engineGEMINI_API_KEY_NEWGEMINI_API_KEY zgemini-2.0-flashzgemini-3-flash-preview)genai)types)api_keyFuQ   google-genai not installed — vision guidance disabled. pip install google-genaiBROWSERLESS_URLBROWSERLESS_TOKENPLAYWRIGHT_TIMEOUT_MS30000)async_playwrightPageBrowserBrowserContextuO   playwright not installed — browser execution disabled. pip install playwrightGRAPHITI_MCP_URLzhttp://152.53.201.221:8001/mcpskill_idreturnc                     t         |  dz  }|j                         st        d|  d|       t        |d      5 }t	        j
                  |      cddd       S # 1 sw Y   yxY w)z8Load a skill definition from the skills directory by ID..jsonzSkill 'z' not found at r   r   N)
SKILLS_DIRr   FileNotFoundErroropenjsonload)r@   
skill_filefs      r"   _load_skillrK      sc    
%00J'(?:, OPP	j7	+ qyy|  s   AA#skillc                     | d   }t         | dz  }t        |dd      5 }t        j                  | |dd       d	d	d	       t        j                  d
|       y	# 1 sw Y    xY w)z1Persist an updated skill definition back to disk.r@   rC   wr   r      Findentensure_asciiNzSkill '%s' updated on disk)rD   rF   rG   dumploginfo)rL   r@   rI   rJ   s       r"   _save_skillrV      sa    Z H
%00J	j#	0 :A		%159:HH)84: :s   AA"platformc                    g }t        t        j                  d            D ]l  }	 t        j                  |j                  d            }| 1|j                  dd      j                         | j                         k(  r|j                  |       n |S # t        j                  $ r+}t        j                  d|j                  |       Y d}~d}~ww xY w)z>Return all skill definitions, optionally filtered by platform.z*.jsonr   r   NrW   r3   zCorrupt skill file %s: %s)sortedrD   globrG   loadsr   getlowerappendJSONDecodeErrorrT   warningname)rW   r%   pses        r"   _list_skillsre      s    FJOOH-. @	@

1;;;89A155R#8#>#>#@HNNDT#Ta 	@ M ## 	@KK3QVVQ??	@s   A)BC%!CCc                      t         j                         r*	 t        j                  t         j	                  d            S i S # t
        $ r Y i S w xY w)zDLoad mutable run-state overlay (execution counts, timestamps, etc.).r   r   )SKILL_STATE_DBr   rG   r[   r   	Exception r$   r"   _load_skill_staterj      sP    	::n666HII I  	I	s   (A 	AAstatec                     t        t        dd      5 }t        j                  | |dd       ddd       y# 1 sw Y   yxY w)zPersist run-state overlay.rN   r   r   rO   FrP   N)rF   rg   rG   rS   )rk   rJ   s     r"   _save_skill_staterm      s7    	ncG	4 :		%159: : :s   6?textparamsenv_varsc                     | }|j                         D ]$  \  }}|j                  d| dt        |            }& |D ]8  }t        j                  j                  |d      }|j                  d| d|      }: |S )zBSubstitute {param} and {ENV_VAR} placeholders in instruction text.{}r3   )itemsreplacestrr   r   r\   )rn   ro   rp   resultr   r!   varenv_vals           r"   _resolve_paramsrz      s~    FLLN 8S"SEc#h78 7**..b)"SEg67 Mr$   c            	       b    e Zd ZdZefdefdZdefdZddededede	fd	Z
dd
ededee	   fdZy)GraphitiClientz?Lightweight Graphiti MCP client for episode storage and recall.urlc                      || _         d | _        y N)r}   
_available)selfr}   s     r"   __init__zGraphitiClient.__init__   s    *.r$   rA   c                 &   t         sy| j                  R	 t        j                  | j                  j                  dd      d      }|j                  dk  | _        | j                  S | j                  S # t        $ r d| _        Y | j                  S w xY w)NFz/mcpz/health   timeouti  )REQUESTS_AVAILABLEr   _req_libr\   r}   ru   status_coderh   )r   rs     r"   _check_availablezGraphitiClient._check_available   s    !??"(LL!1!1&)!DaP"#--#"5 t  ("'(s   AA2 2BBra   bodysourcec           	         | j                         st        j                  d|       dddS dddd|||d	| d
dd}	 t        j                  | j
                  |d      }|j                         }|j                  di       j                  dd      }t        j                  d||       d|dS # t        $ r.}t        j                  d|       dt        |      dcY d}~S d}~ww xY w)z4Store a browser skill execution episode in Graphiti.u0   Graphiti unavailable — episode '%s' not storedFgraphiti_unavailable)storedreason2.0   
tools/call
add_memoryzBrowser skill execution: )ra   episode_bodyr   source_descriptionra   	argumentsjsonrpcidmethodro      rG   r   rw   episode_uuidunknownz%Graphiti episode stored: %s (uuid=%s)T)r   r   zGraphiti store failed: %sN)r   rT   r`   r   postr}   rG   r\   rU   rh   rv   )	r   ra   r   r   payloadr   rw   r   rd   s	            r"   store_episodezGraphitiClient.store_episode   s    $$&KKJDQ#/EFF "$ $($,EdV*L		
	7dhhWbAAVVXF!::h377	RLHH<dLQ"LAA 	7KK3Q7#s1v66	7s   A/B/ /	C&8#C!C&!C&querylimitc                 F   | j                         sg S dddd||ddd}	 t        j                  | j                  |d	      }|j	                         j                  d
i       j                  dg       }|S # t        $ r"}t        j                  d|       g cY d}~S d}~ww xY w)z4Search Graphiti for relevant browser skill episodes.r   r   r   search_memory)r   num_resultsr   r   
   r   rw   factszGraphiti search failed: %sN)	r   r   r   r}   rG   r\   rh   rT   r`   )r   r   r   r   r   r   rd   s          r"   search_episodeszGraphitiClient.search_episodes   s    $$&I"'',UC	
	dhhWbAAFFHLL2.227B?EL 	KK4a8I	s   AA5 5	B >BB B N)browser_skill)   )__name__
__module____qualname____doc__r?   rv   r   boolr   dictr   intlistr   ri   r$   r"   r|   r|      s`    I"2 /C /	$ 	7# 7S 7# 7TX 7<S  T$Z r$   r|   screenshot_b64instructionhistorycurrent_urlstepc           	        K   t         rt        dddS |rdj                  |dd       nd}d| d	| d
| d| d	}t        j                  j                  t        j                  |       d      }t        t        dfD ]  }	 t        j                  j                  |||g      }	|	j                  j                         }
|
j                  d      r*|
j                  d      d   }
|
j                  d      r|
dd }
t!        j"                  |
j                               c S  dddS # t         j$                  $ r t&        j)                  d|       Y t*        $ rm}t-        |      }d|j/                         v sd|j/                         v rt&        j)                  d|       Y d}~%t&        j1                  d||       Y d}~ d}~ww xY ww)a  
    Ask Gemini Flash vision to decide the next browser action.

    Returns a dict with keys:
        action_type: click | type | scroll | navigate | extract | done | fail
        selector: CSS selector or None
        coordinates: [x, y] or None
        text: text to type or None
        url: URL to navigate to or None
        value: extracted value or None
        reasoning: brief explanation
    Nfailu+   Gemini not available — set GEMINI_API_KEY)action_type	reasoning
iNonezQYou are controlling a web browser to complete a task step.

CURRENT INSTRUCTION: z
CURRENT URL: z
STEP NUMBER: z
RECENT ACTIONS (last 8):
au  

Look at the screenshot carefully and decide the SINGLE best next action.

Respond ONLY with valid JSON matching this schema:
{
  "action_type": "click|type|scroll|navigate|extract|done|fail",
  "selector": "CSS selector string or null",
  "coordinates": [x_int, y_int] or null,
  "text": "text to type if action_type is type, else null",
  "url": "URL if action_type is navigate, else null",
  "value": "extracted text/value if action_type is extract, else null",
  "reasoning": "brief 1-sentence explanation of why this action"
}

Rules:
- Use "done" when the instruction has been completed successfully
- Use "fail" only if the page state makes it impossible to complete
- Prefer CSS selectors over coordinates when identifiable
- For type actions, always click the field first then type
- If you see a login form and credentials are needed, use action_type "type" for email first
z	image/png)r'   	mime_typezgemini-2.0-flash-exp)modelcontentsz```r   rG      u1   Gemini returned non-JSON for step %d — retryingz	not foundinvalidz&Model %s not available, trying next...z!Gemini vision error (step %d): %sz=Gemini failed to produce valid action JSON after all attempts)GEMINI_AVAILABLE_gemini_clientjoingenai_typesPart
from_bytesbase64	b64decodePREFERRED_VISION_MODELVISION_MODELmodelsgenerate_contentrn   r   r   splitrG   r[   r_   rT   r`   rh   rv   r]   error)r   r   r   r   r   history_textprompt
image_part
model_nameresponser   rd   err_strs                r"   _gemini_decide_actionr     s    & ~5!F
 	

 /6499WRS\*6L!] #] V  F: !!,,n- - J .|=ST 
	%,,== $f- > H --%%'C~~e$ii&q)>>&)ab'C::ciik**2 T  ## 	KKKTR 	!fGgmmo-gmmo1MDjQII94C	sJ   A=G BD#	G#)GGGAF>G!F>8G>GGc                 ^  K   | j                  dd       d{   }t        j                  |      j                         dd }t        t        j                         j                  d       d| dz  }|j                  |       t        j                  |      j                  d	      |fS 7 w)
z;Capture a full-page screenshot and return as base64 string.Fpng)	full_pagetypeN   z%Y%m%d_%H%M%Sr    z.pngr   )
screenshothashlibsha256	hexdigestSCREENSHOT_DIRr   nowstrftimewrite_bytesr   	b64encodedecode)pager   hfnames       r"   _take_screenshot_b64r   y  s     %e<
<Cs%%',A 7 7 HI1#TRRE	cC ''0!33	 =s   B-B+BB-actionstep_numc                 0  K   |j                  dd      }	 |dk(  ry|dk(  rdd|j                  dd       fS |d	k(  r;|j                  d
      }|sy| j                  |dt               d{    dd| fS |dk(  rz|j                  d      }|j                  d      }|r"| j                  |d       d{    dd| fS |r1| j                  j                  |d   |d          d{    dd| fS y|dk(  rv|j                  dd      }|j                  d      }|r| j                  ||d       d{    n#| j                  j                  |       d{    ddt        |       dfS |dk(  rN|j                  ddd g      }| j                  j                  dt        |t              r|d   nd        d{    y!|d"k(  r|j                  d      }|j                  d#d      }|rS	 | j                  |       d{   }	|	r7|	j                          d{   xs |	j                  d#       d{   xs |}dd$|dd%  fS dd&| fS 7 7 7 o7 $7 7 7 _7 G7 .# t        $ r Y 3w xY w# t        $ r}
dd'|
 fcY d}
~
S d}
~
ww xY ww)(zl
    Execute a single Gemini-decided action on the Playwright page.
    Returns (success, description).
    r   r   done)TzStep completedFzGemini declared fail: r   r3   navigater}   )Fznavigate action missing urldomcontentloaded
wait_untilr   NTzNavigated to clickselectorcoordinatesi'  r   zClicked selector: r   r   zClicked coordinates: )Fz,click action missing selector or coordinatesr   rn   zTyped text (length=)scrolli,  )TzScrolled pageextractvaluezExtracted:    zUnknown action_type: zAction execution error: )r\   gotoPLAYWRIGHT_TIMEOUTr   mousefillkeyboardr   lenwheel
isinstancer   query_selector
inner_textget_attributerh   )r   r   r   atyper}   selcoordsrn   r!   elrd   s              r"   _execute_step_actionr    s    
 JJ}f-E85F?)f_26::k23N2OPPPj **U#C;))C,>HZ)[[[=...g**Z(CZZ.Fjjej4441#777jj&&vay&)<<<4VH===Lf_::fb)D**Z(CiiT5i999mm((....s4yk;;;hZZ3x8F**""1:fd3KfQiQTUUU(i**Z(C**Wb)C#22377B$&MMO3]R=M=Mg=V7V]Z] ;s4Cyk222 1%999U \ 5 = :.
 V 837V    504445sd  JI9 JI9 JI9 JI9 ,I-
I9 7J8?I9 7I8
I9 J'I9 *I+
I9 5J7AI9 8I9#I9 II9 1J2AI9 ?I" I9 J*I9 0I* I$I* I&I* 9I(:I* 	I9 JI9 JI9 I9 I9 I9 I9 "I9 $I* &I* (I* *	I63I9 5I66I9 9	JJJ	JJJmax_retriesc                   K   t        |||      }| j                  }t        |      D ]  }	t        |        d{   \  }
}t	        |
||||       d{   }t
        j                  d||	dz   |j                  d      |j                  dd      dd        |j                  d      dk(  r|j                  d	| d
|        dd|fc S t        | ||       d{   \  }}|j                  d	| d|	dz    d|j                  d       d|        |rqt        j                  d       d{    t        |        d{   \  }}t	        |||| j                  |       d{   }|j                  d      dk(  rd||fc S [|	|dz
  k(  rd||fc S t        j                  d       d{     dd|fS 7 }7 f7 7 7 7 a7 w)zp
    Execute a single vision_guided step with retries.
    Returns (success, description, updated_history).
    Nu   Step %d (attempt %d): %s — %sr   r   r   r3   P   r   Step u   : DONE — TzInstruction completedz	 attempt :     — 皙?Fg      ?zMax retries exceeded)rz   r}   ranger   r   rT   rU   r\   r^   r  asynciosleep)r   r   rp   ro   r   r   r  resolvedr   attemptr   screenshot_hashr   successdescscreenshot_b64_2r    verify_actions                     r"   _run_vision_stepr     s     {FH=H((K% %!0DT0J*J',Hg{H
 
 	-gkJJ}%JJ{B',		
 ::m$.NNU8*KzBC0'9924JJH:YwqykFJJ}4M3NeTXSYZ	
 --$$$(<T(B"Ba"7 (GTXXx# M   /69T7**kAo%$''mmC   K%!N ('11M +K
 K %"B 	!s~   7GF6GF9BGF<AG)F>*G<G =#G G!AG)G*G9G<G>G GGGc                   K   t         rQt        rKt        j                  dt                | j                  j                  t          dt                d{   }n<t        j                  d       | j                  j                  dg d       d{   }|j                  dd	d
ddd       d{   }|j                  d       d{    |j                          d{   }|||fS 7 7 \7 >7 '7 w)z@Launch Playwright browser, preferring Browserless if configured.zConnecting to Browserless at %sz?token=Nz!Launching local headless ChromiumT)z--no-sandboxz---disable-blink-features=AutomationControlledz--disable-web-security)headlessargsi  i  )widthheightzoMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36zen-AUzAustralia/Brisbane)viewport
user_agentlocaletimezone_idzEObject.defineProperty(navigator, 'webdriver', {get: () => undefined}))
r7   r8   rT   rU   chromiumconnect_over_cdplaunchnew_contextadd_init_scriptnew_page)
playwrightr&   contextr   s       r"   _setup_browserr2    s	    ,2OD"++<<w'8&9:
 
 	45"++22 3 
 
 ''3/- ( ( 	 	G 
!
!O   !!##DGT!!?


	 $sZ   AC6C,<C6C.C61C02C6
C2C6"C4#
C6.C60C62C64C6c                       e Zd ZdZd ZdededefdZdededefd	Zded
eddfdZ	dededefdZ
ddee   dee   fdZdedefdZdededefdZdededdfdZdedededdfdZdededefdZdededefdZy)BrowserSkillEngineu  
    Genesis Browser Skill Framework — core execution engine.

    Loads pre-mapped skill JSON workflows, executes them with Playwright
    and Gemini Flash vision guidance, stores successful runs to Graphiti,
    and self-updates skill maps on failure.
    c                 N    t         | _        t        | _        t	               | _        y r   )rD   
skills_dir	_graphitigraphitirj   _state)r   s    r"   r   zBrowserSkillEngine.__init__1  s    $!')r$   r@   ro   rA   c                    t         sdd|dS 	 t        |      }| j	                  ||      r't
        j                  d||j                  dd             | j                  |j                  dd	      |      }|j                  d
      r/t
        j                  d|       |j                  d      r|d   |d<   t
        j                  d|t        |j                                      t        j                  | j                  ||            }| j                  ||d          |d   r| j!                  |||       |d   s&|j                  d      r| j#                  ||d          |S # t        $ r}dt        |      |dcY d}~S d}~ww xY w)u   
        Load skill, execute with Gemini vision, store to Graphiti.
        Synchronous entry point — runs the async executor internally.
        FzTPlaywright not installed. Run: pip install playwright && playwright install chromium)r  r   r@   Nu>   Skill '%s' not used in %d days — flagged for re-verificationebbinghaus_verify_days   rW   r3   foundz)Using Graphiti-recalled workflow for '%s'refined_stepsstepsz$Executing skill '%s' with params: %sr  failure_info)PLAYWRIGHT_AVAILABLErK   rE   rv   _needs_reverificationrT   r`   r\   recall_skill_from_graphitirU   r   keysr  run_async_execute_skill_update_skill_stats_store_to_graphitiupdate_skill_from_failure)r   r@   ro   rL   rd   recalledrw   s          r"   execute_skillz BrowserSkillEngine.execute_skill:  so   
 $ o$ 	M)E
 %%h6KKP%))$<bA 22599Z3LhW<< HH@(K||O,!)/!:g74CVWT66ufEF 	  6)+<= )##E66: i VZZ%?**8VN5KLA ! 	M$s1v8LL	Ms   E! !	F*E>8F>FrW   goalc                     t         rt        sdddS t        j                  d||       t	        j
                  | j                  ||            }|S )z
        Have Gemini explore a platform UI and map a new workflow skill.
        Generates a new skill JSON file from an exploratory session.
        Fz:Both Playwright and Gemini are required for skill learningr  r   z/Learning new skill for platform='%s', goal='%s')rA  r   rT   rU   r  rE  _async_learn_skill)r   rW   rL  rw   s       r"   learn_new_skillz"BrowserSkillEngine.learn_new_skillj  sM    
 $+; U 
 	BHdST44XtDEr$   r@  Nc                    	 t        |      }|j	                  dd      }|j	                  dd      }|j	                  dd      }d	|vrg |d	<   |d	   j                  t        j                  t        j                        j                         |||d
       |d	   dd |d	<   |j	                  dg       D ]j  }|j	                  d      |k(  sd|vrg |d<   |d   j                  dt        j                  t        j                        j                  d       d|        l t        |       t        j                  d||       y# t        $ r t        j                  d|       Y yw xY w)z
        Surprise gate: update workflow map when a step fails.
        Increments failure count, logs failure context, optionally updates steps.
        z%Cannot update non-existent skill '%s'Nr   r   r   r   r}   r3   failure_log)	timestampr   r   r}   ir?  failure_notes[%Y-%m-%d] z*Skill '%s' failure map updated for step %d)rK   rE   rT   r`   r\   r^   r   r   r   utc	isoformatr   rV   rU   )r   r@   r@  rL   failed_stepfailure_reasonurl_at_failurer   s           r"   rI  z,BrowserSkillEngine.update_skill_from_failurey  sh   
	)E #&&vq1%))(I>%))%4%#%E- m##!hll3==?$!	%
 	  %]3CD9m IIgr* 	Dxx;."$.,.D)_%,,X\\2;;JGH>JZ[		 	E=xUA ! 	KK?J	s   E E#"E#c                     d| d| d}| j                   j                  |d      }|sddiS t        j                  dt	        |      ||       d	|d
dS )z{
        Search Graphiti for previously learned browser workflows.
        Returns recalled episode data if found.
        zbrowser skill  z automation steps workflowr   r   r=  Fz)Recalled %d Graphiti episodes for '%s/%s'TN)r=  episodesr>  )r8  r   rT   rU   r  )r   rW   rL  r   r`  s        r"   rC  z-BrowserSkillEngine.recall_skill_from_graphiti  sk    
 !
!D61KL==00a0@U##<c(mXW[\ !
 	
r$   c                 J   t        |      }t               }g }|D ]  }|d   }|j                  |i       }| j                  ||      }|j	                  ||j                  d      |j                  dd      dd |j                  dd      |j                  d	d      t        |j                  d
g             |j                  d|j                  dd            |j                  dd      |j                  dd      ||j                  dd      |j                  dg       d        |S )z:Return all available skills with their metadata summaries.r@   rW   descriptionr3   Nd   requires_authFrequires_human_oversightr?  success_rate        execution_countr   last_runneverlast_verifiedr   tags)r@   rW   rb  rd  re  
step_countrf  rh  ri  needs_reverificationrk  rl  )re   rj   r\   rB  r^   r  )	r   rW   r%   rk   	summariesrc   sidrun_dataneeds_reverifys	            r"   list_skillszBrowserSkillEngine.list_skills  s   h'!#	 	AJ-Cyyb)H!77Q?NEE*- uu]B7=!"!>,-EE2Le,T!!%%"45 (^QUU>SV=W X#+<<0A1#E$LLW=(6!"	!Bfb) 		$ r$   c                     t        |      S )zReturn full skill definition.)rK   )r   r@   s     r"   	get_skillzBrowserSkillEngine.get_skill  s    8$$r$   rL   c                 @   | j                   j                  |i       }|j                  d      }|r|dk(  ry	 t        j                  |      }|j                  dd      }t        j                  t
        j                        |z
  j                  |kD  S # t        $ r Y yw xY w)u;   Check Ebbinghaus decay — has skill been dormant too long?ri  rj  Fr;  r<  )	r9  r\   r   fromisoformatr   r   rX  daysrh   )r   r@   rL   rk   ri  last_dt
decay_dayss          r"   rB  z(BrowserSkillEngine._needs_reverification  s    "-99Z(8w.	,,X6G#;R@JLL.8>>KK 		s   AB 	BBr  c                 f   t               }||vr
ddddd||<   ||   }|dxx   dz  cc<   |r|j                  dd      dz   |d<   |d   }t        |j                  dd      |z  d      |d	<   t        j                  t
        j                        j                         |d
<   || _        t        |       y)z0Update mutable execution statistics in state DB.r   rg  N)rh  success_countrf  ri  rh  r   r|  r   rf  ri  )
rj   r\   roundr   r   r   rX  rY  r9  rm   )r   r@   r  rk   rectotals         r"   rG  z&BrowserSkillEngine._update_skill_stats  s    !#5 #$!" # 	E(O Ho!##&77?A#>#BC %&#CGGOQ$?%$GKN",,x||4>>@J% r$   rw   c                    |d   }|j                  dd      }|j                  dg       }d| d| d|j                  dd	       d
|j                  dd       dt        |j                  dg              dt        j                  |j	                         D ci c]  \  }}|d|j                         v rdn| c}}       ddj                  |       dt        j                  |j                  di              dt        j                  t        j                        j                          dt        j                  |j                  dg              }	| j                  j                  d| dt        j                         j                  d       |	d      }
|
j                  d      r	 t        |      }d|vrg |d<   |d   j!                  |
j                  d       t        j                  t        j                        j                         d!       |d   d"d# |d<   t#        |       y#y#c c}}w # t$        $ r }t&        j)                  d$|       Y d#}~y#d#}~ww xY w)%z3Store a successful execution as a Graphiti episode.r@   rW   r   screenshot_hasheszBrowser skill 'z%' executed successfully on platform 'z	'.
Goal: rb  r3   z
Steps completed: steps_completedr    of r?  z
Params used: passwordz***z
Screenshot hashes: , z
Output fields: outputsz
Timestamp: 
Action history: action_historybrowser_skill_r    z%Y%m%dr0   )ra   r   r   r   graphiti_episodesr   )r   rS  iNz4Could not update graphiti_episodes in skill file: %s)r\   r  rG   dumpsrt   r]   r   r   r   r   rX  rY  r8  r   r   rK   r^   rV   rh   rT   r`   )r   rL   ro   rw   r@   rW   r  kvr   episode_result
skill_datard   s                r"   rH  z%BrowserSkillEngine._store_to_graphiti  s[   $99Z3"JJ':B? hZ'LXJ WYY}b12 3  &

+<a @Ac%))T[]_J`FaEb c JJ_e_k_k_m'nW[WXZ[J!'')4K5QR(R'nop q""&)),=">!? @"jjIr)BCD E",,x||4>>@A B#zz&**5Er*JKLN 	 44!(1X\\^-D-DX-N,OP) 5 
 h'W(2
&j868J23./66$2$6$6~$F!)hll!;!E!E!G8  3==P2QRURV2W
./J' ( (o2  WRTUVVWs   !H( 'A?H. .	I7IIc                   K   |d   }|j                  dg       }|j                  dg       }t        j                         }g }g }i }	d}
d}t               4 d{   }	 t        |       d{   \  }}}	 |D ]  }|j                  d	|
d
z         }|j                  dd      }t
        j                  d||       |dk(  r[t        |j                  dd      ||      }	 |j                  |dt               d{    |j                  d| d|        |
d
z  }
|dv r|j                  dd      }t        ||||||       d{   \  }}}t        |       d{   \  }}|j                  |       |rc|
d
z  }
|dk(  s	d|j                         v s|j                  dd
      d   j                         }|j                  dg       }|sV||	|d   <   `|||j                   d}t
        j#                  d|||        n t
        j#                  d|||       |
d
z  }
 t        |       d{   \  }}|j                  |       |j)                          d{    |j)                          d{    ddd      d{    |du xr |
t+        |      k(  }t-        t        j                         |z
  d#      }|||
t+        |      |	||||t/        j0                  t2        j4                        j7                         d$
S 7 7 # t        $ r&}d|d| ddcY d}~cddd      d{  7   S d}~ww xY w7 C# t        $ r}|d| |d}Y d}~ 5d}~ww xY w7 7 7 5# t        $ r3}t
        j%                  d ||d!"       |
t'        |      dd}Y d}~Vd}~ww xY w7 K7 6# |j)                          d{  7   |j)                          d{  7   w xY w7 `# 1 d{  7  sw Y   qxY ww)%uE   Core async skill executor — runs all steps via Playwright + Gemini.r@   r?  auth_env_varsr   NFBrowser launch failed: )r  r@   r   r  r   r   r   vision_guidedzExecuting step %d (%s)...r   targetr3   r   r   r  z: navigated to zNavigation failed: )r   r   r}   )r  extract_textr   r  z
extracted:z
Extracted:output_fieldsz Skill '%s' failed at step %d: %sz3Unknown step action_type '%s' in skill '%s' step %dz"Unexpected error in skill '%s': %sT)exc_inforO   )
r  r@   r  total_stepsr  r  r  elapsed_secondsr@  rS  )r\   timer;   r2  rh   rT   rU   rz   r   r   r^   r   r   r]   r   r   r}   r`   r   rv   closer  r}  r   r   r   rX  rY  )r   rL   ro   r@   r?  rp   
start_timer  r  r  r  r@  r0  r&   r1  r   rd   step_defr   r   r  r   r  r  r    r   extracted_valr  
final_hashelapseds                                 r"   rF  z'BrowserSkillEngine._async_execute_skill$  s6    $		'2&99_b1YY[
$&')#% R	& R	&/=j/I)I&$G& % ;-H'||FOa4GHH"*,,x"IKHH8(KP"j0!0$LL26"""&)) &3E(: #, #   +11E(?SYRZ2[\+q0O %(II&.ll="&E>N +x*H? 95~
 &:$%??1)003"+q0O*n<QUQ[Q[Q]A]04

<0KB0O0U0U0W05		/20N#0@MGM!,<$= )1*.'+xx,L
  KK B ((D " Q'8 (1,w;-| ';4&@ @:!((4 mmo%%mmo%%eR	& R	&h $&H?c%j+H		j0!4  .u:,!2&(!hll3==?
 	
oR	&)I $ (6qc:'(	 	R	& R	& R	&&  ) "(0,?s*C'-,L
 ""9
  @> !A  V		>!VZ	[(73q6RTUV &% mmo%%mmo%%eR	& R	& R	& R	&s  AO=LO=O'L	,L-L	6A&M(L>8L;9 L>*M(MM(M"$M(M(7M(A"M(0M%1M(	O'N'O'4N*5O'9O=O$A?O=L			L8	L3L8O' O=,L/-O=3L88O';L>>	M	MM(MM("M(%M((	N$1(NN-N$$N-'O'*O'-O!O
O!O
O!!O'$O='O:-O0.O:5O=c                 B
  K   |j                         j                  dd       d|j                         j                  dd      dd j                  dd       }t        j                  d||       g }g }t	               4 d{   }	 t        |       d{   \  }}}		 d	d
ddddt        j                  j                  dd      d}|j                  |j                         d| d      }|	j                  |dt               d{    |j                  dd|d       d| d| d}t        dd      D ]h  }t        |	       d{   \  }}t        ||||	j                   |       d{   }|j                  d      dk(  rt        j                  d |        n|j                  d      d!k(  r(t        j#                  d"||j                  d#              n|j                  |d$|j                  d#d%| d&|       |d'       t%        |	||       d{   \  }}|j                  d%| d(|j                  d       d)|        |st        j#                  d*||       t'        j(                  d+       d{    d,| d| d-t+        j,                  |d.d        d/}k |j/                          d{    |j/                          d{    ddd      d{    i d0|d1|j                         d2d3| d4j                  |j                         d| d      d5d6d7g d8i d9|d:| d;d<t1        j2                  t4        j6                        j9                  d=      d>d?d@dAdBdAdCg dDd6dEt1        j2                  t4        j6                        j;                         dFdGdH|j                         dIgi}t<        | dJz  }t?        |dKdLM      5 }t+        j@                  ||dN       ddd       t        j                  dO||       | jB                  jE                  dP| dQ| dR| dS| dTtG        |       dUt+        j,                  |       
V       d6|tI        |      tG        |      |dWS 7 7 # t        $ r$}
dd|
 dcY d}
~
cddd      d{  7   S d}
~
ww xY w7 7 h7 G7 7 A7 7 # |j/                          d{  7   |j/                          d{  7   w xY w7 # 1 d{  7  sw Y   -xY w# 1 sw Y   !xY ww)Xzr
        Exploratory skill learning session.
        Gemini explores the platform UI and maps a workflow.
        r^  r    Nr<  z(Starting skill learning session: %s / %sFr  rN  zhttps://app.gohighlevel.com/zhttps://portal.telnyx.com/zhttps://elest.io/dashboardzhttps://sso.godaddy.com/zhttps://dashboard.stripe.com/zhttps://app.netlify.com/N8N_BASE_URLz)https://n8n-genesis-u50607.vm.elestio.app)gohighleveltelnyxelestiogodaddystripenetlifyn8nzhttps://z.com/r   r   r   r   )r   r   r  z3You are mapping a browser workflow to accomplish: 'z' on u   . Explore the UI and identify the steps needed. Take note of navigation paths, button locations, and form fields required. This is step 2 — what do you see and what should be the first action?rO   r   r   r   z!Skill mapping complete at step %dr   zLearning failed at step %d: %sr   r  r  r  )r   r   r   gemini_actionr  r  zExploration step %d failed: %sr  zGoal: 'z. Previous actions: z.. What is the next step to complete this goal?r@   rW   rb  zAuto-learned: r}   rd  Tr  ro   r?  
memory_key	_workflowrk  rV  rf  rg  rh  r   avg_steps_takenr  auto_learnedlearning_timestampr;     rl  zauto-learnedrC   rN   r   r   rQ   zNew skill '%s' saved to %sskill_learning_zLearned new browser skill 'z' for goal: z
Platform: z
Steps discovered: r  )ra   r   )r  r@   rI   steps_discoveredrL   )%r]   ru   rT   rU   r;   r2  rh   r   r   r\   r   r   r^   r  r   r   r}   r`   r  r  r  rG   r  r  r   r   r   rX  r   rY  rD   rF   rS   r8  r   r  rv   )r   rW   rL  r@   r  discovered_stepsr0  r&   r1  r   rd   platform_urls	start_urlexplore_promptr   r   r    r   r  r  	new_skillrI   rJ   s                          r"   rO  z%BrowserSkillEngine._async_learn_skill  s    
 nn&..sC894::<;O;OPSUX;YZ][];^;f;fgjlo;p:qr;XtL$&')#% E	& E	&R/=j/I)I&$?& $B:;9=9::>>.:ef! *--hnn.>(8*TY@Z[	ii	6HRdieee ''jT](^_ J$uU]T^ _^ _  !&a $H.B4.H(H%NA#8&RZ$ F zz-0F:!DhOzz-0F:$DhPVPZPZ[fPgh %++ ("1'-zz+xjPTUYTZ?['\)/	-  +?tVX*V$VMGT"))E(2fjj>W=XX]^b]c*de"$DhPTU!--,,, "$uXJ 7--1ZZrs8K-L,M NGH #A$N mmo%%mmo%%KE	& E	&P

(
 ^D62
 =$$X^^%5(57QR	

 T
 R
 b
 %
 XJi0
 X\\(,,7@@L
 C
 q
 q
  
 D
  !(,,x||"<"F"F"H!
" %b#
$ X^^%~6%
	*  XJe"44
*cG4 	.IIi1-	. 	-xD 	##"8*--hZ|D6 J%J '%%()9%:$; <##'::n#=">@ 	$ 	
  j/ #$4 5
 	
]E	&)I R#(5LQC3PQQ	E	& E	& E	&R  f )I* %W - &% mmo%%mmo%%KE	& E	& E	& E	&|	. 	.sb  B TQ7TS<Q=Q:Q= A'SR-A SR0	#S,R3-B)SR6A!S8R99)S"S<5R<6S<R?S<TS9C<TT3BT:Q==	R*R%R*S<TR!T%R**S<-S0S3S6S9S<S<?S<S6S
S6/S2
0S66S<9T<TTT
TTTr   )r   r   r   r   r   rv   r   rK  rP  rI  rC  r   r   rs  ru  r   rB  rG  rH  rF  rO  ri   r$   r"   r4  r4  (  s@   *.c .4 .D .` 3 4 'V# 'VT 'Vd 'VR
3 
c 
d 
,HSM T$Z 2%# %$ %c $ 4 !C !$ !4 !($W $Wd $WD $WT $WTo
 o
d o
t o
b
 
C 
D 
r$   r4  _engine_singletonc                  .    t         
t               a t         S )z-Return singleton BrowserSkillEngine instance.)r  r4  ri   r$   r"   
get_enginer    s      .0r$   c                 6    t               j                  | |      S )z>MCP tool: Execute a browser skill by ID with given parameters.)r  rK  )r@   ro   s     r"   mcp_execute_browser_skillr  &  s    <%%h77r$   c                 4    t               j                  |       S )zMMCP tool: List all available browser skills, optionally filtered by platform.)r  rs  )rW   s    r"   mcp_list_available_skillsr  +  s    <##H--r$   rL  c                 6    t               j                  | |      S )zGMCP tool: Explore a platform UI and learn a new browser workflow skill.)r  rP  )rW   rL  s     r"   mcp_learn_browser_skillr  0  s    <''$77r$   verbosec                 l   i }| D ]1  }|j                  dd      }||vrg ||<   ||   j                  |       3 t        |       }t        d| d       t        ddddd	dd
dddddddddd       t        d       t	        |j                               D ]  \  }}|D ]  }|j                  dd      }|j                  dd      dkD  r	|dz  ddnd}	|j                  d      rdnd}
|j                  d      rdnd}t        d|d    d!d|d	d|j                  d"d      dd|j                  dd      dd|	dd|
d|        |st        d#|j                  d$d      d%d&         t        d'd(j                  |j                  d)g                     t                  t                y%)*z"Print skills in a formatted table.rW   r   u%   
Genesis Browser Skills Registry — z
 skill(s)
zSKILL IDz<45r^  PLATFORMz<15STEPSz>5RUNSzSUCCESS%z>9zVERIFY?z<8z_-----------------------------------------------------------------------------------------------rf  rg  rh  r   rc  z.0f%zn/arn  YESr3   re  z [HUMAN]  r@   z<43rm  z    rb  NZ   z
    Tags: r  rl  )r\   r^   r  printrY   rt   r   )r%   r  	platformsrc   rb   r  rW   pskillssrsr_strreverify	oversights               r"   _print_skills_tabler  9  s   I EE*i(IIaL!A	 KE	25'
EF	ZAj-Qwrl!F2;a
SUVWXabdWe
fg	(O#IOO$56 ' 	A~s+B)*/@!)Dq)H3s|1%eF !&< =u2H&'ee,F&G
RIQz]3'q#alA8Nr7RRS55*A.r2!F2;a}YKY QUU="5cr:;<=
499QUU62->#?"@AB	 
Gr$   c                  
   t        j                  dt         j                  d      } | j                  dddd       | j                  d	d
d       | j                  dddd       | j                  dddd       | j                  dddd       | j                  ddddd       | j                  d d!dd"       | j                  d#dd$       | j                  d%dd&       | j	                         }t               }|j                  s7t        |j                  |j                  |j                  |j                  g      sl|j                  |j                        }t        ||j                  '       t         st#        d(       t#        d)       t$        st#        d*       t#        d+       y y |j                  r=	 |j'                  |j                        }t#        t)        j*                  |d,             y |j                  r|j4                  r	 |j'                  |j                        }t#        d0|j                   d1       t#        d2|j7                  d3              t#        d4|j7                  d5              t#        d6t9        |j7                  d7g              d8       |j7                  d7g       D ]=  }t#        d9|d:   d;d<|d=    d>|j7                  d?|j7                  d@dA                    ? y 	 t)        j:                  |j<                        }t#        dC|j                   dD       |jA                  |j                        }t#        t)        j*                  |d,             t/        j2                  |j7                  dE      rdFnd/       y |j                  rS|j                  \  }	}
t#        dG|	 dH|
        |jC                  |	|
      }t#        t)        j*                  |d,             y |j                  rtt#        dItD         dJ       |jF                  jI                         }t#        dK|        |r6|jF                  jK                  dLdM      }t#        dNt9        |       dO       y y y # t,        $ r=}t#        d-| t.        j0                  .       t/        j2                  d/       Y d }~y d }~ww xY w# t,        $ r=}t#        d-| t.        j0                  .       t/        j2                  d/       Y d }~y d }~ww xY w# t(        j>                  $ r>}t#        dB| t.        j0                  .       t/        j2                  d/       Y d }~<d }~ww xY w)PNzGenesis Browser Skill Enginea  
Examples:
  python3 core/browser_skill_engine.py --list
  python3 core/browser_skill_engine.py --list --platform gohighlevel
  python3 core/browser_skill_engine.py --list --verbose
  python3 core/browser_skill_engine.py --execute ghl_create_subaccount_api_integration \
      --params '{"subaccount_name": "Test Co", "integration_name": "Genesis"}'
  python3 core/browser_skill_engine.py --learn gohighlevel "set up email campaign"
  python3 core/browser_skill_engine.py --show ghl_deploy_snapshot
        )rb  formatter_classepilogz--listz-l
store_truezList all available skills)r   helpz
--platformz-pz-Filter by platform (e.g. gohighlevel, telnyx))r  z	--verbosez-vzShow descriptions and tagsz	--executez-eSKILL_IDzExecute a skill)metavarr  z--paramsJSONz{}zJSON params for --execute)r  defaultr  z--learnz-LrO   )r  GOALzLearn new skill)nargsr  r  z--showz-szShow full skill definitionz--check-graphitizTest Graphiti connectionz	--dry-runz+Load skill and show steps without executing)r  u9   WARNING: Playwright not installed — execution disabled.zD         Run: pip install playwright && playwright install chromium
u<   WARNING: Gemini not configured — vision guidance disabled.z2         Set GEMINI_API_KEY in config/secrets.env
r  zError: )filer   z
DRY RUN: Skill 'r   z
Platform: rW   zDescription: rb  z
Steps (r?  z):r  r   2z. [r   rW  r   r  r3   zInvalid JSON params: z
Executing skill 'z'...r  r   z
Learning new skill for: z / z!
Checking Graphiti connection at z...zGraphiti available: zbrowser skill testr_  zTest search returned z results)&argparseArgumentParserRawDescriptionHelpFormatteradd_argument
parse_argsr4  r   anyexecutelearnshowcheck_graphitirs  rW   r  r  rA  r  r   ru  rG   r  rE   sysstderrexitdry_runr\   r  r[   ro   r_   rK  rP  r?   r8  r   r   )parserr#  enginer%   rL   rd   rc   ro   rw   rW   rL  	availabler`  s                r"   mainr  Y  s   $$2 <<	F $|B]^
d1`a
T,Eab
T:DUV

FDGbc
	4q:NUfg
$
A]^
*<F`a
L?lmD!FyydllDJJ		4CVCVWX##DMM2FDLL9#MNYZPQGH   
	$$TYY/E$**U1-.
 
<<
((6*4<<.:;
599Z#8"9:;eii&>%?@A	#eii&<"=!>bAB7B/ kABqym3q{m2aeeMSTSXSXYaceSf>g=hijkDKK0 '~T:;))$,,?F$**VA./HH&**Y/QQ7	$*8*Cv>?''$7djj*+			23C2DCHIOO446	$YK01667KST6UH)#h-AB 	 
E ! 	GA3-cjj1HHQKK	 % sm#**5 '' -aS1

CsI   8;Q5 C(R> 8T 5	R;>3R66R;>	T3S??TU3UU__main__r   )r   )F)^r   r  r  r   r   rG   loggingr   r  r  r   r   r   pathlibr   typingr   r   r   pathinsertrv   r#   rD   r   LOG_DIRrg   parent_dmkdirbasicConfigINFOFileHandlerStreamHandlerstdout	getLoggerrT   r   r\   r2   r   r   googler4   google_genaigoogle.genair5   r   r   r   Clientr   ImportErrorr`   r7   r8   r   r   playwright.async_apir;   r<   r=   r>   rA  r?   requestsr   r   r   rK   rV   r   re   rj   rm   rz   r|   r7  r   r   tupler  r   r2  r4  r  __annotations__r  r  r  r  r  r  r   ri   r$   r"   <module>r     s  $L       	 
  2 2    +, 3|$ %,  *X5	A
'-7:JJ''*DD~w0E0E
F *BHHTDH)*   
,,8G&@@7Scjj) g./ JJNN'( 
	zz~~&'
	 
 "1 e,1N+,,,^D **..!2B7JJNN#6; (?IJ cTT ::>>"46VW # $ 5t 5 5
8C= 
DJ 
4 :T :d :# t tCy S C CL 	XXX #YX 	X
 X 
X~4 4?5T ?5S ?5U4QT9EU ?5R 7272 72 	72
 72 72 72 4d?72t#"g~t.K(L #"Tl
 l
f 37 8./ 6& 8 8T 8d 8
. .d .
8c 8 8 8T
 T d @SCl zF !  eNKKcde  c KKabc  s6   ",L )L2 M L/.L/2MMMM