
    i                       U d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZmZmZmZmZ 	 ddlmZ n# e$ r ddlZdd	ZY nw xY w	 dd
lmZ n# e$ r  G d d          ZY nw xY w e            ZdZ ej                    Zdaded<   dadddddddZded<    ej         dej!                  Z" ej         dej!                  Z# ej         dej!                  Z$ ej         dej!                  Z% ej         d          Z&g dZ'ddZ(dd"Z)g d#d$d%d&d'd(d) e(g d*          d+d,d-d.d&d/d0d1 e(g d2          d+d3d4d5d&d'd6d7 e(g d8          d+d9d:d;d&d<d= e)d>d?           e)d@d           e)dAdB          gdCdDdEdFd&d/dGdH e(g dI          d+dJdKdLd&d/dMdN e(g dO          d+dPdQdRd&d/dSdT e(g d2          d+dUdVdWdXd/dYdZ e(g d[          d+d\d]d^dXd/d_d` e(g da          d+dbdcdddXd/dedf e(g d2          d+dgdhdidXd/djdkdl e(g dm          dn	dodpdqdXd/drdkds e(g dt          dn	dudvdwdXd<dx e)dydz           e)d{d|          gdCd}d~ddXd<d e)dd           e)dZd          gdCddddXd/ddkd e(g d          dn	ddddXd/ddkd e(g d2          dn	ddddXd/ddkd e(g d          dn	ddddd'dd e(g d          d+ddddd'dd@ e(g d          d+ddddd'dd e(g d          d+ddddd/dd e(g d          d+ddddd<d e)dd           e)d)d          gdCddddd/dd e(g d          d+ddddd<ddk e)dd           e)dZd          gdddddd/dd e(g d          d+ddddd/dd e(g dt          d+ddddd/dd e(g dТ          d+ddddd/dd e(g dТ          d+ddddd/dd e(g d          d+ddddd/dd e(g d          d+ddddd/dd e(g d2          d+ddddd/ddkd e(g d          dn	ddddd/dd e(g d          d+ddddd/dd e(g dТ          d+ddddd/dd e(g d          d+ddddd/dd  e(g d          d+ddddd/dd e(g d          d+dd	d
dd/dd e(g d          d+ddddd/dd e(g d          d+ddddd/dd e(g dТ          d+ddddd/dd e(g d          d+ddd d!d/d"d# e(g d          d+d$d%d&d!d/d'd( e(g d          d+d)d*d+d!d/d,d- e(g d          d+d.d/d0d!d/d1d2 e(g d3          d+d4d5d6d!d/d1d2 e(g d7          d+d8d9d:d!d/d"d; e(g d<          d+d=d>d?d!d/d_d# e(g d          d+d@dAdBd!d/ddC e(g dD          d+dEdFdGd!d/ddH e(g dD          d+dIdJdKd!d/ddL e(g dM          d+dNdOdPdd/ddQ e(g dR          d+dSdTdUdd/ddV e(g dW          d+dXdYdZdd/dGd[ e(g d          d+d\d]d^dd/dMd_ e(g d`          d+dadbdcdd/ddd e(g de          d+dfdgdhdid/djdk e(g dl          d+dmdndodid/dpdq e(g dt          d+drdsdtdid/dudv e(g dt          d+dwdxdydid/dzdkd{ e(g dТ          dn	Z*ded|<   dd}Z+dd~Z,ddZ-ddZ.ddZ/ddZ0ddZ1ddZ2ddZ3ddZ4ddZ5ddZ6dddZ7dddZ8ddZ9ddZ:d dZ;d!dZ<d"dZ=d#dZ>d$dZ?d$dZ@d$dZAi d)dd7dd>dd@ddAddddddHddNddTddZdd`ddfddlddsddydd{di dddddddddddddddddŐdÓdʐdēdϐdœdՐdƓdڐdǓdߐdȓddɓddʓdd˓i d#d̓d(d͓d-dΓd{dϓddГddѓd dғd1dӓddԓddՓdd֓ddדddؓd2dٓd;dړdCdۓdHdܓdݐdސdߐdddddddddZBd%dZCd&dZDd'dZE	 	 	 d(d)dZFd*dZGd$dZHd+dZIddd,dZJd-d.dZKdaLded<    ej                    ZMd/dZNd0d1dZOd2dZPd3d4dZQeR                    d          d             ZSeR                    d          d             ZTeR                    d	          d
             ZUeR                    d          d5d            ZVeW                    d          d             ZXeW                    d          d             ZYdS (6  zqHermes Achievements dashboard plugin backend.

Mounted at /api/plugins/hermes-achievements/ by Hermes dashboard.
    )annotationsN)Path)AnyDictListOptionalSetget_hermes_homereturnr   c                     t           j                            d          pd                                } | rt	          |           nt	          j                    dz  S )NHERMES_HOME z.hermes)_osenvirongetstripr   home)vals    O/usr/local/lib/hermes-agent/plugins/hermes-achievements/dashboard/plugin_api.pyr   r      sF    {}--3::<<<tCyyyTY[[9%<<    )	APIRouterc                      e Zd Zd Zd ZdS )r   c                    d S )Nc                    | S N fns    r   <lambda>zAPIRouter.get.<locals>.<lambda>       b r   r   self_args_kwargss      r   r   zAPIRouter.get   
     = r   c                    d S )Nc                    | S r   r   r   s    r   r    z APIRouter.post.<locals>.<lambda>   r!   r   r   r"   s      r   postzAPIRouter.post   r&   r   N)__name__
__module____qualname__r   r)   r   r   r   r   r      s2        	! 	! 	!	! 	! 	! 	! 	!r   r   x   Optional[Dict[str, Any]]_SNAPSHOT_CACHEidle)state
started_atfinished_at
last_errorlast_duration_ms	run_countDict[str, Any]_SCAN_STATUSzv\b(error|failed|failure|traceback|exception|permission denied|not found|eaddrinuse|already in use|timed out|blocked)\bz^\b(port\s+)?(3000|5173|8000|8080|9119)\b.*\b(in use|already|taken|eaddrinuse)\b|\beaddrinuse\bz-\b(npm|pnpm|yarn|pip|uv)\b.*\b(install|add)\bzO\b(success|passed|built|compiled|done|exit_code[\"']?\s*[:=]\s*0|verified|ok)\bz[(?:/home/|~/?|\./|/mnt/)[\w./-]+\.(?:py|js|ts|tsx|jsx|css|html|md|json|yaml|yml|svg|sql|sh))CopperSilverGoldDiamondOlympianvalues	List[int]List[Dict[str, Any]]c                @    d t          t          |           D             S )Nc                    g | ]
\  }}||d S ))name	thresholdr   ).0rC   rD   s      r   
<listcomp>ztiers.<locals>.<listcomp>9   s$    bbbtYT	22bbbr   )zip
TIER_NAMES)r>   s    r   tiersrI   8   s!    bb#jZ`JaJabbbbr   metricstrgteintc                    | |dS )NrJ   rL   r   rO   s     r   reqrP   <   s    S)))r   let_him_cookzLet Him Cookz>Let Hermes run a serious autonomous tool chain in one session.zAgent Autonomybest_sessionflamemax_tool_calls_in_session)         @  )idrC   descriptioncategorykindiconthreshold_metricrI   autonomous_avalanchezAutonomous AvalanchezEAccumulate a lifetime avalanche of Hermes tool calls across sessions.lifetime	avalanchetotal_tool_calls)  rX   rY    N  P  toolchain_maxxerzToolchain Maxxerz:Use a wide spread of distinct Hermes tools in one session.nodesmax_distinct_tools_in_session)      -   F   d   	full_sendz	Full SendzBTerminal, files, and web/browser all get involved in one real run.multi_conditionrocketmax_terminal_calls_in_session   max_file_tool_calls_in_session max_web_browser_calls_in_session<   )rZ   rC   r[   r\   r]   r^   requirementssubagent_commanderzSubagent Commanderz Coordinate delegated agent work.branchtotal_delegate_calls)   (   rn   rd     background_process_enjoyerzBackground Process EnjoyerzDStart or control enough long-running processes to deserve the title.daemontotal_process_calls),       p  :  cron_necromancerzCron Necromancerz.Raise scheduled autonomous jobs from the dead.clocktotal_cron_callsred_text_connoisseurzRed Text Connoisseurz9Encounter enough errors to develop a palate for red text.zDebugging Chaoswarningtotal_errors)    '  a  i$ stack_trace_sommelierzStack Trace Sommelierz/Taste tracebacks by the flight, not by the sip.winetraceback_events)r   rd   rX   rY   re   actually_read_the_logszActually Read The Logsz,Inspect logs repeatedly instead of guessing.scrolllog_read_eventsport_3000_takenzPort 3000 Is TakenzGDiscover dev-server port conflict patterns enough times to become numb.plugTport_conflict_events)   r|   rn   r   rd   )	rZ   rC   r[   r\   r]   r^   secretr_   rI   permission_denied_any_percentzPermission Denied Any%zSpeedrun into permission walls.lockpermission_denied_events)   K   rU   X  r   dependency_hell_touristzDependency Hell Touristz3Package installs fail, then somehow life continues.package_skullinstall_error_eventsr   install_success_events
   the_fix_was_restartingzThe Fix Was Restarting Itz;Restart after enough error clusters to call it a technique.restartrestart_after_error_events2   r   forgot_the_env_varzForgot The Env VarzIAuth or configuration failed because an environment variable was missing.keyenv_var_error_events)r}   r   i@  i i yaml_colon_incidentzYAML Colon Incidentz Configuration syntax bites back.colonyaml_error_eventsdocker_name_collisionzDocker Name Collisionz3A container name already exists. Of course it does.	containerdocker_conflict_events)r   rU   r   r   r   supposed_to_be_quickzThis Was Supposed To Be Quickz(A tiny ask becomes an entire expedition.zVibe Codingmelting_clockmax_messages_in_session)r   r   rW   i	  r   one_more_small_changezOne More Small ChangezLMake enough file edits in one session to invalidate the phrase small change.pencil)     rd   rX   rY   vibe_architectzVibe Architectz2Touch a broad surface area in one project session.	blueprintmax_files_touched_in_session)r   i  r   r   r   pixel_goblinzPixel Goblinz2Do sustained frontend, CSS, SVG, or visual tuning.pixelfrontend_activity_events)re   rf   i i i 5 ship_first_ask_laterzShip First, Ask Laterz(Git activity after a serious tool chain.ship
git_eventsrV   css_exorcistzCSS Exorcistz2Cast repeated styling demons out of the interface.spark_cursorcss_activity_events)r   i0u  i8 i@ i  one_character_fixzOne Character Fixz7A tiny edit after a pile of errors. Painful. Beautiful.needletiny_patch_after_errors_eventsr{   )rZ   rC   r[   r\   r]   r^   r   rw   
skillsmith
Skillsmithz5Work with Hermes skills enough to leave fingerprints.zHermes Nativehammer_scrollskill_eventsskill_issue_skill_createdzSkill Issue? Skill Created.zACreate or patch durable procedures instead of repeating yourself.anvilskill_manage_eventsmemory_keeperzMemory Keeperz3Persist durable knowledge with memory or Mnemosyne.crystalmemory_events)rn   r   rd   rX   rY   memory_palacezMemory Palacez%Build a serious durable-memory trail.palacememory_write_eventscontext_dragonzContext DragonzFBrush against compression, huge context, or token pressure repeatedly.dragoncontext_eventsgateway_dwellerzGateway Dwellerz0Live through gateway-connected Hermes workflows.antennagateway_eventsplugin_goblinzPlugin Goblinz9Use or develop plugins enough that the dashboard notices.puzzleplugin_eventsrollback_wizardzRollback Wizardz*Invoke rollback/checkpoint recovery magic.rewindrollback_events)rV   r   r   r   r   rabbit_hole_certifiedzRabbit Hole CertifiedzESearch or extract enough web content to qualify as a research spiral.zResearch/Webspiraltotal_web_calls)r   rW   rX   rY   re   citation_goblinzCitation Goblinz4Extract enough web pages to become a tiny librarian.quotetotal_web_extract_callsdocs_archaeologistzDocs Archaeologistz0Dig through documentation sources over and over.compassdocs_activity_eventsbrowser_possessionzBrowser Possessionz0Possess a browser through automation repeatedly.browserbrowser_callsterminal_goblinzTerminal Goblinz!Spend serious time in shell-land.zTool Masteryterminaltotal_terminal_calls)  r   r   r   rf   patch_wizardzPatch Wizardz.Bend files to your will with targeted patches.wandtotal_patch_calls)   r   r   r   r   file_archaeologistzFile Archaeologistz3Dig through the filesystem with reads and searches.foldertotal_file_reads_searchesimage_whispererzImage Whispererz<Use image generation or vision tools enough for visual work.eyeimage_vision_callsvoice_of_the_machinezVoice Of The Machinez/Use text-to-speech or voice tooling repeatedly.wave	tts_calls)r      rn   r   r   model_hopperzModel Hopperz>Switch or inspect providers/models enough to count as a habit.z
Model Loreswapmodel_eventsopenrouter_enjoyerzOpenRouter Enjoyerz/Route model work through OpenRouter repeatedly.routeropenrouter_eventscodex_conjurerzCodex Conjurerz;Summon Codex-flavored assistance often enough for a ritual.codexcodex_eventsmulti_model_magezMulti-Model Magez@Use a real spread of distinct model names across Hermes history.prismdistinct_model_count)r      r|   P      five_model_flightzFive-Model FlightzQTry at least five distinct LLMs instead of marrying the first model that answers.)r{   r   r  r|   r  provider_polyglotzProvider Polyglotz9Use models from multiple providers across Hermes history.distinct_provider_count)      r{         model_sommelierzModel SommelierzATaste enough model/provider conversations to develop preferences.claude_confidantzClaude Confidantz=Bring Claude-flavored reasoning into the workflow repeatedly.claude_events)r   r   rV   r   r   gemini_cartographerzGemini Cartographerz8Map enough Gemini-related workflows to know the terrain.gemini_eventsopen_weights_pilgrimzOpen Weights PilgrimzLActually chat with local/open-weight models through Hermes session metadata.local_model_chat_sessions)   r  r   r   rn   toolset_cartographerzToolset CartographerzJNavigate Hermes toolsets deliberately instead of treating tools as a blur.toolset_events)r  rv   rU   r   r   config_surgeonzConfig Surgeonz]Operate on real config files, manifests, env files, and dashboard settings without flinching.config_events)rn   r   rd   rX   r   rebase_acrobatzRebase AcrobatzFHandle real git history surgery: rebase, conflict, merge, fetch, push.git_history_eventstest_suite_tamerzTest Suite TamerzLRun enough verification commands that green text becomes part of the ritual.test_events)rn   r   r   i`	  r   screenshot_hunterzScreenshot HunterzLCapture, inspect, and polish visual proof instead of just claiming it works.screenshot_events)r   r   rV   r   r}   marathon_operatorzMarathon Operatorz/Accumulate a serious number of Hermes sessions.	Lifestylemarathonsession_count)r   rU   rV   r   r}   weekend_warriorzWeekend Warriorz;Run Hermes on weekends enough times to make it a lifestyle.calendarweekend_sessionsnight_shift_operatorzNight Shift Operatorz-Run sessions during gremlin hours repeatedly.moonnight_sessionscache_hit_appreciatorzCache Hit Appreciatorz-Notice or benefit from prompt/cache behavior.cachecache_eventsACHIEVEMENTSc                 0    t                      dz  dz  dz  S )Npluginshermes-achievementsz
state.jsonr
   r   r   r   
state_pathr9     s    y(+@@<OOr   c                 0    t                      dz  dz  dz  S )Nr7  r8  zscan_snapshot.jsonr
   r   r   r   snapshot_pathr;     s    y(+@@CWWWr   c                 0    t                      dz  dz  dz  S )Nr7  r8  zscan_checkpoint.jsonr
   r   r   r   checkpoint_pathr=     s    y(+@@CYYYr   c                     t                      } |                                 sdi iS 	 t          j        |                                           S # t
          $ r di icY S w xY w)Nunlocks)r9  existsjsonloads	read_text	Exception)paths    r   
load_staterF     so    <<D;;== 2z$..**+++   2s   %A AAr1   Nonec                    t                      }|j                            dd           |                    t	          j        | dd                     d S NT)parentsexist_okr  )indent	sort_keys)r9  parentmkdir
write_textrA  dumps)r1   rE  s     r   
save_staterR     sO    <<DKdT222OODJuQ$???@@@@@r   valuer   c                   t          | t                    rd |                                 D             S t          | t          t          f          rd | D             S t          | t
                    rt          d | D                       S | S )Nc                4    i | ]\  }}|t          |          S r   
_json_safe)rE   kvs      r   
<dictcomp>z_json_safe.<locals>.<dictcomp>   s$    ;;;TQ:a==;;;r   c                ,    g | ]}t          |          S r   rV  rE   rY  s     r   rF   z_json_safe.<locals>.<listcomp>   s    ---!
1---r   c              3  4   K   | ]}t          |          V  d S r   rV  r\  s     r   	<genexpr>z_json_safe.<locals>.<genexpr>   s(      33jmm333333r   )
isinstancedictitemslisttuplesetsorted)rS  s    r   rW  rW     s    % <;;U[[]];;;;%$'' .--u----% 433U333333Lr   c                     t                      } |                                 sd S 	 t          j        |                                           }t          |t                    r|S n# t          $ r Y d S w xY wd S r   )r;  r@  rA  rB  rC  r_  r`  rD  rE  datas     r   load_snapshotri     s    ??D;;== tz$..**++dD!! 	K	   tt4s   <A$ $
A21A2rh  c                    t                      }|j                            dd           |                    t	          j        t          |           dd                     d S rI  )r;  rN  rO  rP  rA  rQ  rW  rh  rE  s     r   save_snapshotrl     sW    ??DKdT222OODJz$//TJJJKKKKKr   c                    t                      } |                                 sddi dS 	 t          j        |                                           }t          |t                    rl|                    dd           |                    dd           |                    di            t          |                    d          t                    r|S n# t          $ r Y nw xY wddi dS )Nr  r   schema_versiongenerated_atsessionsro  rp  rq  )
r=  r@  rA  rB  rC  r_  r`  
setdefaultr   rD  rg  s     r   load_checkpointrs     s    D;;== H"#QBGGG	z$..**++dD!! 	OO,a000OONA...OOJ+++$((:..55    CCCs   B&C 
CCc                    t                      }|j                            dd           |                    t	          j        t          |           dd                     d S rI  )r=  rN  rO  rP  rA  rQ  rW  rk  s     r   save_checkpointru     sY    DKdT222OODJz$//TJJJKKKKKr   metac                    |                      d          |                      d          |                      d          |                      d          p|                      d          pddS )Nlast_activer2   modeltitlepreviewUntitled)rx  r2   ry  rz  r   )rv  s    r   session_fingerprintr~     sc    xx..hh|,,'""'""Gdhhy&9&9GZ	  r   nowboolc                :    t           d uo| t          z
  t          k    S r   )r/   _SNAPSHOT_CACHE_ATSNAPSHOT_TTL_SECONDS)r  s    r   _cache_is_freshr     s    $&]C2D,DI]+]]r   snapshotOptional[int]c                    t          | t                    sdS t          |                     d          pd          }t          |pt	          j                              }|dk    rdS ||z
  t
          k    S )NTrp  r   )r_  r`  rM   r   timer  )r  r  tscurrents       r   _is_snapshot_staler     sm    h%% t	X\\.)).Q	/	/B#$%%G	QwwtbL000r   c                V   t          | pt          j                              }t          t          t                    rt          nd }|r&t          |pi                     d          pd          nd}t                              dd          t                              d          t                              d          t                              d          t                              d          t                              d	d          t          |pd |r||z
  nd t          ||          d

S )Nrp  r   r1   r0   r2   r3   r4   r5   r6   )
r1   r2   r3   r4   r5   r6   ttl_secondssnapshot_generated_atsnapshot_age_secondssnapshot_stale)	rM   r  r_  r/   r`  r   r8   r  r  )r  r  snaprp  s       r   _scan_status_payloadr     s   #$%%G($??I??TDAEL3
''77<1===1L!!'622"&&|44#''66"&&|44(,,-?@@!%%k155+!-!5<H R<!7!7d,T7;;  r   callOptional[str]c                    t          | t                    sd S |                     d          pi }|                     d          p|                    d          S )NfunctionrC   )r_  r`  r   )r  r   s     r   _tool_name_from_callr  
  sP    dD!! t	*			#B88F-rvvf~~-r   msgc                    |                      d          }|dS t          |t                    r|S 	 t          j        |          S # t
          $ r t          |          cY S w xY w)Ncontentr   )r   r_  rK   rA  rQ  rD  )r  r  s     r   _contentr    sv    ggi  Gr'3 z'"""   7||s   A A"!A"
tool_names	List[str]needlesc                R    d | D             }t          fd|D                       S )Nc                6    g | ]}|                                 S r   )lower)rE   rC   s     r   rF   z_count_tool.<locals>.<listcomp>  s     333tzz||333r   c              3  T   K   | ]!t          fd D                       dV  "dS )c              3      K   | ]}|v V  	d S r   r   )rE   r   rC   s     r   r^  z(_count_tool.<locals>.<genexpr>.<genexpr>  s'      +Q+QvFdN+Q+Q+Q+Q+Q+Qr   r  N)any)rE   rC   r  s    @r   r^  z_count_tool.<locals>.<genexpr>  sF      RRT+Q+Q+Q+Q+Q+Q+Q(Q(QRqRRRRRRr   )sum)r  r  lowereds    ` r   _count_toolr    s9    33
333GRRRRWRRRRRRr   
model_namec                <   | pd                                                                 }|r|dk    rd S d|v r|                    dd          d         S dD ]}||v r|dk    rdn|c S |                    d	d          d                             d
d          d         S )Nr   none/r  r   )openai	anthropicgooglegeminimistralrv  qwendeepseekxainousollamagroq
openrouterr  r  r  :-)r   r  split)r  rC   providers      r   model_providerr  "  s    "##%%++--D 46>>t
d{{zz#q!!!$$ _ B Bt'83388AAA ::c1a &&sA..q11r   c                    | pd                                                                 rdk    rdS g d}t          fd|D                       S )Nr   r  F)r  z	llama.cpp	localhostz	127.0.0.1zlocal/zlocal:ggufz
vllm-localc              3      K   | ]}|v V  	d S r   r   )rE   markerrC   s     r   r^  z&is_local_model_name.<locals>.<genexpr>3  s'      ::&v~::::::r   )r   r  r  )r  local_markersrC   s     @r   is_local_model_namer  .  sh    "##%%++--D 46>>uoooM::::M::::::r   
session_idrz  messagesc                r   t                      }g }t                      }g }d}|D ]}t          |          }	|                    |	           |                    d          rXt	          |d                   }
|                    |
           |                    d          dk    r|                    |
           |                    d          pg D ]=}t          |          }
|
r*|                    |
           |                    |
           >t                              |	          r|dz  }|	}|                    d          r4|dt          j
        |                    d          t                    z   z  }|                    t                              |                     d	                    |          }|                                }t!          |d
          }t!          |dd          }t!          |d          }t!          |d          }||z   }t!          |d          }t!          |dd          }t!          |dddd          }t!          |d          }t!          |d          t#          t%          j        d|t$          j                            z   }t!          |d          }t!          |dd          }t!          |dd          }t!          |d          t#          t%          j        d|                    z   }t!          |d          }t!          |dd          }t!          |dd          }i d | d!|pd"d#t#          |          d$t#          |          d%|d&t#          |          d'|d(|d)|d*|d+|d,|d-|d.|d/|d0t#          |          d1|i d2|d3|d4|d5|d6|d7|d8|d9|d:t)          t*                              |                    d;t*                              |          rdndd<t#          t%          j        d=|t$          j                            d>t#          t%          j        d?|t$          j                            d@t#          t%          j        dA|t$          j                            dBt,                              |          rt                              |          rdnddCt,                              |          rt.                              |          rdnddD|r"t%          j        dE|t$          j                  rdnddFt#          t%          j        dG|t$          j                            i dHt                              |          r-t#          t%          j        dI|t$          j                            nddJt#          t%          j        dK|t$          j                            dLt#          t%          j        dM|t$          j                            dNt#          t%          j        dO|t$          j                            dPt#          t%          j        dQ|t$          j                            dR|dSk    r"t%          j        dT|t$          j                  rdnddUt#          t%          j        dV|t$          j                            dWt#          t%          j        dX|t$          j                            dYt#          t%          j        dZ|t$          j                            d[t#          t%          j        d\|t$          j                            d]t#          t%          j        d^|t$          j                            d_t#          t%          j        d`|t$          j                            dat#          t%          j        db|t$          j                            dct#          t%          j        dd|t$          j                            det#          t%          j        df|t$          j                            dgt#          t%          j        dh|t$          j                            dit#          t%          j        dj|t$          j                            t#          t%          j        dk|t$          j                            t#          t%          j        dl|t$          j                            t#          t%          j        dm|t$          j                            t#          t%          j        dn|t$          j                            t#          t%          j        do|t$          j                            t#          t%          j        dp|t$          j                            t#          t%          j        dq|t$          j                            t                      drS )sNr   	tool_nameroletool
tool_callsr   )default
r   
web_searchweb_extractr   patch	read_filesearch_files
write_filedelegate_taskprocesszbackground\s*=\s*truecronjobimagevisionttstext_to_speechskillz\bskillskill_managememory	mnemosynemnemosyne_rememberr  rz  zUntitled sessionmessage_counttool_call_countr  distinct_tool_counterror_countterminal_calls	web_callsweb_extract_callsr   web_browser_callspatch_callsfile_reads_searchesfile_tool_callsfiles_touched_countdelegate_callsprocess_calls
cron_callsr   r   r   r   r   r   port_conflictr   r   ztraceback|exceptionr   z6gateway\.log|errors\.log|agent\.log|/api/logs|\blogs\br   z0permission denied|eacces|operation not permittedr   r   r   z\brestart|reload|kill|start\br   zKmissing .*env|api key|environment variable|not configured|unauthorized|authr   zyaml|yml|colon|parse errorr   zRdocker.*(name|container).*already|container name conflict|Conflict\. The containerr   z+\.(css|svg|tsx|jsx)|frontend|tailwind|reactr   z%\.css|tailwind|style|className|visualr   z.\bgit\s+(commit|push|merge|rebase|status|diff)r   r{   z#one character|single character|typor   z#compress|context window|token|cacher   z)gateway|discord|telegram|slack|api_serverr   z7plugin|dashboard-plugins|__HERMES_PLUGIN|manifest\.jsonr   zrollback|checkpointr   z$docs|documentation|docusaurus|READMEr  zkmodel|provider|openrouter|codex|gemini|claude|anthropic|openai|mistral|qwen|deepseek|llama|ollama|vllm|ggufr  r  r  r  r  zclaude|anthropicr  zgemini|google ai|google modellocal_model_eventszCollama|llama\.cpp|gguf|vllm|local model|open[- ]weight|open weightszFtoolset|enabled_toolsets|browser tool|terminal tool|file tool|web toolzconfig\.ya?ml|\b[a-z0-9_-]+config\.(?:js|ts|json|ya?ml)|\.env(?:\b|\.)|manifest\.json|settings\.json|pyproject\.toml|package\.jsonzb\bgit\s+(rebase|merge|fetch|pull|push|tag|checkout)|merge conflict|conflict\s*\(|rebase --continuezapytest|unittest|vitest|playwright|npm test|pnpm test|node --check|py_compile|tests? passed|\bOK\bzDscreenshot|playwright|vision_analyze|browser_vision|\.png|image dataz>\bgit\s+tag|release|version bump|changelog|publish|pushed? tagz#cache hit|prompt caching|cache_read)r  r!  r#  r%  r'  release_eventsr4  model_names)rd  r  appendr   rK   addr  ERROR_REsearchrA  rQ  updateFILE_REfindalljoinr  r  lenreIr  PORT_RE
INSTALL_RE
SUCCESS_RE) r  rz  r  r  tool_sequencefiles_touchedfull_text_partsr  r  textrC   r  blob	full_textr  r  r  r  r   r  r  r  r  r  r  r  r   r   r   r   r   r   s                                    r   analyze_messagesr  6  s
   55J!M!eeM!#OK 4 4}}t$$$77; 	+s;'((DNN4    wwv&(($$T***GGL))/R 	+ 	+D'--D +t$$$$$T***??4   	1K77<   	IC$*SWW\%:%:CHHHHHDW__T223333		/**IOOE 
;;NM<GGI#M=AAy99M!M1mW55K%m[.QQ!-lGUcddO @@Ny99C
Kcenprpt@u@u<v<vvM]I66J$]GXFFM52BCCI}g66RZ
TY=Z=Z9[9[[L%m^DDxEEM%m5I8TT<j<,,< 	X< 	3}--	<
 	j< 	s:< 	{< 	.< 	Y< 	.< 	< 	.< 	{< 	2< 	?<  	s=11!<" 	.#< <$ 	%<& 	j'<( 	0)<* 	Y+<, 	-<. 	2/<0 	1<2 	23<4 	gnnY77885<6 	W^^I%>%> EA7<8 	C
+A9bd S STT9<: 	3rz*cenprptuuvv;<< 	#C
3fhqsusw(x(x$y$y=<> 	Z%6%6y%A%A ghooV_F`F` gfg?<@ 	!z'8'8'C'C"k
HYHYZcHdHd"k!!jkA<B 	%;&~29Eegprtrv;w;w&~aa}~C<D 	BJ/}  @I  KM  KO  %P  %P  !Q  !QE< < <F 	`h`o`opy`z`z  BS,I9VXVZ![![\\\  ABG<H 	!#bj  2G  IR  TV  TX  'Y  'Y  #Z  #ZI<J 	#C
3aclnpnr(s(s$t$tK<L 	s2:.VXacecg#h#hiiM<N 	c"*%VXacecghhiiO<P 	){a/?/?BINtv  BD  BF  EG  EG/?!!  MNQ<R 	#bj)OQZ\^\`aabbS<T 	#bj)UW`bdbfgghhU<V 	RZ(bdmoqosttuuW<X 	3rz*@)RTRRSSY<Z 	BJ/VXacecg$h$h i i[<\ 	BJ  (V  Xa  ce  cg  h  h  i  i]<^ 	SM9bd!K!KLL_<` 	BJxBDAABBa<b 	RZ(;YMMNNc<d 	RZ(H)UWUYZZ[[e<f 	c"*-su~  AC  AE  #F  #F  G  Gg< <h bj)rt}  @B  @D  E  E  F  FRZ  )n  py  {}  {  @  @  A  A!"*  .S  U^  `b  `d  #e  #e  f  f2:  'K  MV  XZ  X\  ]  ]  ^  ^ ,su~  AC  AE  "F  "F  G  Gbj)jluwyw{||}}BJ'MyZ\Z^__``uuw< < < <r   
definition	aggregatec           
        | d         }t          |                    |d          pd          t          |                     dg           d           }fd|D             }fd|D             }|r|d         d	         nd }|r|d         d	         nd }|r|d         d
         n|r|d         d
         nd}|r|d         d
         nd}	t          d||	z
            }
|s|rdn8t          dt	          dt          j        |	z
  |
z  dz                                }t          |          }t          dk              }|rdn|                     d          r|sdnd}||p|                     d           |||||dS )Nr_   r   rI   c                    | d         S NrD   r   ts    r   r    z!evaluate_tiered.<locals>.<lambda>  s
    1[> r   r   c                ,    g | ]}|d          k    |S rD   r   rE   r  progresss     r   rF   z#evaluate_tiered.<locals>.<listcomp>  s'    DDDaQ{^)C)C)C)C)Cr   c                ,    g | ]}|d          k     |S r  r   r  s     r   rF   z#evaluate_tiered.<locals>.<listcomp>  s'    EEE8an+D+D!+D+D+Dr   rC   rD   r  rn   c   unlockedr   
discoveredr  r  r1   tierr  	next_tiernext_thresholdprogress_pct)rM   r   re  maxminmathfloorr  )r  r	  rJ   
tiers_listachieved
next_tiersr  r  r  current_thresholddenompctr  r  r1   r  s                  @r   evaluate_tieredr(    s   *+F9==++0q11H
w339Q9QRRRJDDDD:DDDHEEEEZEEEJ#+58B<D)3=
1f%%I3=uZ];//cmDtJrNS^D_D_stN5=D[111>$5566E
}H
}###aRhYjNjnsMswzLzA{A{9|9|2}2}CH~~Hhl##J"sJJZ^^H5M5M)rV`)rfrE 
0Z*..QYBZBZ>Zejtx  GO  ^g  {I  [^  _  _  _r   c                   |                      dg           }|s6d|                      d           |                      d          rdndd dd dddS g }d}d}|D ]}t          |                     |d	         d          pd          }t          |                     d
d                    }|p|dk    }|o||k    }|                    t          d|t	          d|          z                       t          j        t          |          t          |          z  dz            }	|rdn|                      d          r|sdnd}
||p|                      d           |
d |	d d|rdnt          d|	          dS )Nrw   Fr   r  r   r  r  TrJ   rL   g      ?rn   r  r  )	r   rM   r  r  r  r   r!  r  r  )r  r	  rw   partsany_progresscompleterequirementrS  rD   r'  r1   s              r   evaluate_requirementsr.    s   >>."55L h!Z^^H5M5M1Mdndrdrs{d|d|  YOX`X`  CO  Y]  kl  {  ST  fg  h  h  	hELH# : :IMM+h"7;;@qAAq1122	#0uqy2 2Sec!Y&7&77889999
*c%jj3u::-4
5
5C"uJJZ^^H5M5M)tVb)thtE 0\JNNS[D\D\@\glvz  IL  [_  sv  OW  Hi  HK  HK  ]`  ac  eh  ]i  ]i  j  j  jr   c           	         t          |                    | d                             }|d|rdndd |rdndd d|rdnddS )	NrJ   Tr  r  r  r   rn   r  )r  r   )r  r	  r  s      r   evaluate_booleanr0    s    IMM*X"67788H X?gzz[gqu  IQ  DX  DE  DE  WX  gk  @  Ya  Rh  RU  RU  gh  i  i  ir   ztool calls in one sessionz)distinct Hermes tools used in one sessionzterminal calls in one sessionz&file/search/patch calls in one sessionz2web search/extract or browser calls in one sessionzmessages in one sessionzfiles touched in one sessionzlifetime delegate_task callsz&lifetime background process operationsz!lifetime scheduled-job operationsz(error/failed/traceback messages observedztraceback or exception mentionszlog inspectionsz#dev-server port conflict detectionszpermission-denied errorszpackage-install failuresz.successful package installs after package workz+restart/reload actions after error clustersz/missing auth/config/environment-variable eventszYAML/config parse incidentszDocker/container-name conflictsz(frontend/CSS/SVG/React activity mentionsz-CSS, styling, Tailwind, or className activityzgit workflow commandsz*tiny typo-style fixes after error clustersz!Hermes skill mentions or tool usez+skill_manage create/patch/delete operationszmemory or Mnemosyne tool eventszdurable memory writesz7context, compression, token, or cache-pressure mentionsz"gateway/API/chat-platform activityz-dashboard plugin development or usage signalsz%rollback/checkpoint recovery mentionsz"documentation/README/docs activityzmodel/provider-related activityzOpenRouter mentionszCodex mentionszprompt-cache/cache-hit mentionsz%lifetime web_search/web_extract callszlifetime web_extract callsz!lifetime browser automation callszlifetime Hermes tool callszlifetime terminal callszlifetime targeted patch editsz%lifetime read_file/search_files callsz%image generation or vision tool callsz"text-to-speech or voice tool callsz-distinct model names seen in session metadataz7distinct model providers inferred from session metadatazClaude/Anthropic model mentionszGemini/Google model mentionsz local/open-weight model mentionsz9Hermes sessions whose model metadata is local/open-weightztoolset or tool-family mentionsz+configuration/environment/manifest activityzAgit history operations such as rebase, merge, fetch, push, or tagz(test/check/verification command mentionsz:screenshot, Playwright, PNG, or vision-inspection activityz,release, version, publish, or git tag eventszHermes sessionszsessions started on weekendsz*sessions started late night or before dawn)r  r  r  r!  r#  r%  r'  r  r+  r.  r1  c                `    t                               | |                     dd                    S )N_r  )METRIC_LABELSr   replace)rJ   s    r   metric_labelr5    s&    VV^^C%=%=>>>r   c                   |                      d          r|                      d          dk    rdS d}d| v rmt          |                      dg           d           }|s|d	z   S t          | d                   }d
                    d |D                       }|d| d| dz   S |                      d          pg }|r*d |D             }|dz   d                    |          z   dz   S |dz   S )Nr   r1   zSecret: exact requirement hidden until Hermes sees the first matching signal. Keep using Hermes across debugging, tools, memory, skills, plugins, and model workflows to reveal it.r   r_   rI   c                    | d         S r  r   r  s    r   r    zcriteria_for.<locals>.<lambda>  s
    q~ r   r  z1Requirement: use Hermes in the matching workflow.z, c              3  <   K   | ]}|d           d|d          V  dS )rC   r  rD   Nr   )rE   r  s     r   r^  zcriteria_for.<locals>.<genexpr>  s8      OOqai::!K.::OOOOOOr   zRequirement: z. Tier ladder: .rw   c           
         g | ]=}t          |d                     dt          |                    dd                     >S )rJ   u    ≥ rL   r  )r5  rM   r   )rE   rs     r   rF   z criteria_for.<locals>.<listcomp>#  sF    aaaqL8--JJCeQ4H4HJJaaar   z; z3Requirement: complete the matching Hermes behavior.)r   re  r5  r  )r  secret_prefixr"  rJ   ladderrw   r*  s          r   criteria_forr>    s4   ~~h EJNN7$;$;x$G$G E  EMZ''JNN7B77=U=UVVV
 	W #VVVj);<==OOJOOOOOOvOOfOOOOO>>.117RL HaaT`aaa.51A1AACGGPPPr   itemc                    t          |           }|                    d          dk    ri |ddt          |          ddS t          |          |d<   |S )Nr1   r   z???zdSecret achievement: hidden until Hermes detects the first relevant behavior in your session history.)rC   r[   criteriar^   rA  )r`  r   r>  )r?  cleans     r   display_achievementrC  (  s    JJEyyX%% R%  R  8^  lx  y~  l  l  IQ  R  R  R  	R$U++E*Lr   r   limitprogress_callbackOptional[Any]progress_everyc           	     	   	 ddl m} n&# t          $ r}g i d| ddddddcY d}~S d}~ww xY wt                      }t	          |                    d          t                    r|                    d          ni }d}d}| | dk    rd	nt          |           }	 |            }
	 |
                    |	d
d          }t          |          }g }i }t          |d          D ]l\  }}|                    d          }|st          |          }t	          |t                    r|                    |          nd}t	          |t                    r|                    d          nd}t	          |t                    r|                    d          nd}t	          |t                    r||k    rt          |          }|dz  }nU|
                    |          }t          ||                    d          p|                    d          pd|          }|dz  }||d<   |                    d          p+|                    d          p|                    d          pd|d<   |                    d          |d<   |                    d          |d<   |                    d          |d<   |                    d          r |                    dt                                 t	          |d         t                    r<|d                             t#          |                    d                               nt	          |d         t$                    rft#          |                    d                    |d         vr;|d                             t#          |                    d                               n&t#          |                    d                    h|d<   |                    |           |t)          |          d||<   |C|dk    r=||z  dk    r4||k     r.	  |t%          |          ||           [# t          $ r Y hw xY wnt+          dt          t-          j                              |d           t/          |
dd          }|r
 |             n## t/          |
dd          }|r |             w w xY w|t1          |          |dk    rdndt          |          ||t          |          |d d!S )"u  Scan Hermes sessions and build per-session achievement stats.

    ``limit=None`` (the default) scans the ENTIRE session history. Prior
    versions capped this at 200, which silently reduced achievement totals
    to ~2% of history on long-running installs and made lifetime badges
    unreachable. SQLite's ``LIMIT -1`` means "unlimited"; we map ``None``
    and non-positive values to ``-1`` so callers get the full catalog.

    Warm scans stay cheap: the checkpoint cache stores per-session stats
    keyed by ``(started_at, last_active)`` and only re-analyzes sessions
    whose fingerprint changed. Cold scans on large histories (thousands
    of sessions) take tens of seconds to several minutes; ``evaluate_all``
    runs them on a background thread so the dashboard UI never blocks on
    the first request.

    ``progress_callback(partial_sessions, scanned_so_far, total)`` — when
    provided, fires every ``progress_every`` sessions with the sessions
    analyzed so far and progress counters. Background scans use this to
    publish intermediate snapshots so a long cold scan surfaces badges
    incrementally on each dashboard refresh instead of going all-at-once
    at the end.
    r   )	SessionDBzCould not import SessionDB: failedmodesessions_totalsessions_rescannedsessions_reused)rq  r	  error	scan_metaNrq  r  TF)rD  include_childrenproject_compression_tipsr  )startrZ   statsfingerprintrz  r{  r|  r  r2   rx  sourcery  r  )rV  rU  rn  closeincrementalfullrL  rM  rN  rO  sessions_scanned_so_farsessions_expected_totalrq  r	  rQ  )hermes_staterI  rD  rs  r_  r   r`  rM   list_sessions_richr  	enumerater~  get_messagesr  rr  rd  r  rK   rb  r  rW  ru  r  getattraggregate_stats)rD  rE  rG  rI  exc
checkpointprevious_sessionsreused	rescanneddb_limitdbsessions_metatotal_sessionsrq  checkpoint_sessionsidxrv  sidfpcachedcached_stats	cached_fprU  r  rX  s                            r   scan_sessionsru  0  s   6E******* E E ER:^Y\:^:^v~  ST  lm  BC  nD  nD  E  E  	E  	E  	E  	E  	E  	EE !""J6@PZA[A[]a6b6bj
z222hjFI
 muzzrrE

H	B8--Htns-tt]++)+.0"=::: )	 )	IC((4..C $T**B3=>OQU3V3V`&**3///\`F2<VT2J2JT6::g...PTL5?5M5MW

=111SWI,-- )r//\**!??3//(dhhw.?.?.d488ICVCV.dZdfnooQ	"%E,!XXg..i$((92E2EiSZI[I[i_iE'N"&((<"8"8E,#'88M#:#:E- "hhx00E(Oxx   D  666eM2C88 D-(,,S'1B1B-C-CDDDDm 4d;; D488G,,--U=5IIIm,33C8I8I4J4JKKK,/0A0A,B,B+CE-(OOE"""79JuDUDU'V'V$ ,!1C1C~I]abIbIbhkn|h|h|%%d8nnc>JJJJ     D
 		,,+
 
 	 	 	 GT** 	EGGG GT** 	EGGGG	 $X..%+aZZMMV!(mm"+%'*8}}'5
 
  sE   	 
,',,$M:R P;9R ;
Q	R Q		6R  R=rq  c                
   i dt          |           ddddddddddddd	dd
dddddddddddddddddddddddddddd
}g d}|D ]}d||<   t                      }t                      }| D ]}t          |d         |                    dd                    |d<   t          |d         |                    dd                    |d<   t          |d         |                    dd                    |d<   t          |d         |                    dd                    |d<   t          |d         |                    dd                    |d<   t          |d         |                    dd                    |d<   t          |d	         |                    dd                    |d	<   t          |d
         |                    dd                    |d
<   |dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |dxx         |                    dd          z  cc<   |dxx         |                    d d          z  cc<   |dxx         |                    d!d          z  cc<   |d"xx         |                    d#d          z  cc<   |d$xx         |                    d%d          z  cc<   |d&xx         |                    d&d          z  cc<   |d'xx         |                    d'd          z  cc<   |d(xx         |                    d(d          z  cc<   |D ]&}||xx         |                    |d          z  cc<   '|                    |                    d)          pt                                 |                    d)          pt                      }|D ]5}t          t          |                    }	|	r|                    |	           6t          d* |D                       r|d+xx         d,z  cc<   |                    d-          r	 t          j
        t          |                    d-                              }
|
j        d.k    r|d/xx         d,z  cc<   |
j        d0k     s|
j        d1k    r|d2xx         d,z  cc<   # t          $ r Y w xY wt          d3 |D                       |d4<   t          |          |d5<   |S )6Nr+  rT   r   ri   r   rr   rt   max_web_calls_in_sessionru   r   r   rc   r   r   r   r   r   rz   )
r   r   r   r   r   r  r  r  r.  r1  )$r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r  r  r!  r#  r%  r'  r  r4  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r   r  r   r   r   r  c              3  N   K   | ] }t          t          |                    V  !d S r   )r  rK   )rE   r  s     r   r^  z"aggregate_stats.<locals>.<genexpr>  s1      UU
"3z??33UUUUUUr   r  r  r2   r{   r.        r1  c                "    h | ]}||d k    
|S )rG  r   )rE   ms     r   	<setcomp>z"aggregate_stats.<locals>.<setcomp>  s"    &S&S&SQ&SqF{{q{{{r   r  r  )r  rd  r  r   r  r  rK   r  r  r  	localtimefloattm_wdaytm_hourrD  )rq  aggsum_keysr   r  provider_namesssession_modelsr  r  lts              r   rd  rd    s   X#Q 	( 	"1	
 	( 	)! 	#A 	+A 	' 	 	A 	 	1 	"1 	Q  	$Q!" 	#$  ! !#$%&7  C:  H   CEEK"uuN ( (+.s3N/OQRQVQVWhjkQlQl+m+m'(/237V3WYZY^Y^_tvwYxYx/y/y+,),S1J-KQUUSbdeMfMf)g)g%&/237V3WYZY^Y^_oqrYsYs/t/t+,03C8X4Y[\[`[`artu[v[v0w0w,-*-c2L.MquuU`bcOdOd*e*e&'25c:\6]_`_d_dexz{_|_|2}2}./.1#6T2UWXW\W\]rtuWvWv.w.w*+Nquu]A666155):A#>#>>"###quu-=q'A'AA###!%%Q"7"77%&&&!%%0CQ*G*GG&&&   AEE-$;$;;   '(((AEE2G,K,KK((("###quu-=q'A'AA###!"""aeeOQ&?&??"""155q#9#99Ooq 9 99 !!!QUU+?%C%CC!!!KAEE+q111 	& 	&CHHHc1%HHHH155//8355999}--6( 	- 	-J%c*oo66H -""8,,,UUnUUUUU 	2+,,,1,,,55 	^E!%%*=*=$>$>??:??*+++q0+++:>>RZ2%5%5()))Q.)))   	 #&&S&S+&S&S&S"T"TC%(%8%8C!"Js   4A5T++
T98T9c                r    d| v rt          | |          S d| v rt          | |          S t          | |          S )Nr_   rw   )r(  r.  r0  )r  r	  s     r   evaluate_definitionr    sH    Z''z9555##$Z;;;J	222r   c           	        |sd S |                      d          }dddddddd	d
}||v r\||         t          |fd          }|                     d          |                     d          |                     d          dS d S )Nr_   r  r  r  r  r  r  r  r  )rT   ri   r   rr   rt   rw  ru   r   c                0    |                      d          S )Nr   r}  )xr   s    r   r    zevidence_for.<locals>.<lambda>  s    c1 r   r  r  rz  r   )r  rz  rS  )r   r  )r  rq  rJ   metric_to_session_keyr  r   s        @r   evidence_forr    s     t^^.//F%6)>#2)9*;$/,?(=	 	 &&&#F+5555666eeL11AEE'NNUVUZUZ[^`aUbUbccc4r   F
is_partialscanr  c               `   |                      di           }|st                      ndi i}|                    di           }t          t	          j                              }g }t
          D ]}t          ||          }|d         }	|sJ|d         rB|	|vr>||                     d          t          ||                      dg                     d||	<   i ||}
|d         r||                     |	i                                d          |
d<   |                     |	i                                d	          p#t          ||                      dg                     |
d	<   |                    t          |
                     |st          |           d
 |D             }d |D             }d |D             }||                      dg           ||                      di           |                      d          t          |          t          |          t          |          t          |          |d
S )ug  Evaluate every achievement definition against a scan result.

    Used by ``compute_all`` for finished scans AND by the background
    progress callback for partial, in-flight snapshots. ``is_partial=True``
    skips persisting ``state.json`` unlocks — we don't want to record an
    "unlock time" based on half a scan that a later session might shift.
    r	  r?  rZ   r  r  rq  )unlocked_at
first_tierevidencer  r  c                "    g | ]}|d          
|S r  r   rE   as     r   rF   z&_compute_from_scan.<locals>.<listcomp>,  s!    666a*6666r   c                D    g | ]}|                     d           dk    |S )r1   r  r}  r  s     r   rF   z&_compute_from_scan.<locals>.<listcomp>-  s,    III!%%..L*H*H!*H*H*Hr   c                D    g | ]}|                     d           dk    |S )r1   r   r}  r  s     r   rF   z&_compute_from_scan.<locals>.<listcomp>.  s,    AAAAaeeGnn&@&@a&@&@&@r   rQ  rP  
achievementsrq  r	  rQ  rP  unlocked_countdiscovered_countsecret_counttotal_countrp  )r   rF  rr  rM   r  r5  r  r  r  rC  rR  r  )r  r  r	  r1   r?  r  	evaluatedr  result	unlock_idr?  r  r  r   s                 r   _compute_from_scanr    ss    b))I *?JLLLBEy"--G
dikk

CI" 	4 	4
$Z;;t$	 	XfZ0 	XYg5M5M14FJJvDVDVdpq{  ~B  ~F  ~F  GQ  SU  ~V  ~V  eW  eW  "X  "XGI'*''* 	@")++i"<"<"@"@"O"OD&{{9b99==jII\Zdfjfnfnoy{}f~f~MMD,T223333 5669666HIIYIIIJAAAAAF!HHZ,,XXk2..'""h--
OOF9~~  r   c                F    t          | |          }t          |d          S )N)rE  rG  Fr  )ru  r  )rE  rG  r  s      r   compute_allr  =  s(    +<^\\\Ddu5555r   zOptional[threading.Thread]_BACKGROUND_SCAN_THREADc                    d t           D             }|g i dddddddt          d |D                       t          d |D                       t          |          | d
S )	zPlaceholder payload used while the first-ever scan is still running.

    Returns a structurally-complete response so the dashboard UI can render
    an empty achievement list + spinner without special-casing "no data yet".
    c                2   g | ]}t          i |d d |                    d          rdnddd|                    d          pi gd                             d          |                    d          pi gd                             dd          dd	          S )
Fr   r  r   rI   rC   rD   r  N)r  r  r1   r  r  r  r  r  )rC  r   )rE   ds     r   rF   z+_build_pending_snapshot.<locals>.<listcomp>L  s     P  P  P  ~$  &x  &x%uklkpkpqykzkz  `M_g_g  AM  [\  no  @  D  D  EL  M  M  U  RT  QU  WX  ~Y  ~]  ~]  ^d  ~e  ~e  z{  z  z  @G  zH  zH  zP  MO  LP  RS  yT  yX  yX  Yd  fg  yh  yh  rv  .w  .w  &x  y  y  P  P  Pr   pendingr   rK  Nc              3  L   K   | ]}|                     d           dk    dV   dS )r1   r  r  Nr}  r  s     r   r^  z*_build_pending_snapshot.<locals>.<genexpr>T  s6      WWag,8V8V8V8V8V8VWWr   c              3  L   K   | ]}|                     d           dk    dV   dS )r1   r   r  Nr}  r  s     r   r^  z*_build_pending_snapshot.<locals>.<genexpr>U  s6      OO!AEE'NNh4N4NA4N4N4N4NOOr   r  )r5  r  r  )r  r  s     r   _build_pending_snapshotr  F  s     P  P  CO  P  P  PI!'1TUjkllWW9WWWWWOOyOOOOO9~~  r   publish_partial_snapshotsc           	     j   t           5  t          t          j                              }dt          d<   |t          d<   dt          d<   d }| r|nd}	 t	          |          }t          |          at          t                              d          pt          t          j                                        at          t                     d	t          d<   n8# t          $ r+}d
t          d<   t          |          t          d<   Y d}~nd}~ww xY wt          t          j                              t          d<   t          t          d         |z
  dz            t          d<   t          t                              dd                    dz   t          d<   n# t          t          j                              t          d<   t          t          d         |z
  dz            t          d<   t          t                              dd                    dz   t          d<   w xY w	 ddd           dS # 1 swxY w Y   dS )u*  Execute a scan + snapshot update. Called synchronously or from a thread.

    When ``publish_partial_snapshots=True`` (the default for background
    scans), the scanner periodically publishes an in-progress snapshot to
    ``_SNAPSHOT_CACHE`` so each dashboard refresh during a long cold scan
    shows more progress — badges unlock incrementally as sessions stream
    in, instead of staying at zero for minutes and then jumping to the
    final state. Synchronous /rescan callers pass ``False`` because they
    block on the full result anyway.
    runningr1   r2   Nr4   c           	         	 | t          |           d|dd||dd}t          |d          }t          |          adad S # t
          $ r Y d S w xY w)Nin_progressr   r[  r^  Tr  )rd  r  rW  r/   r  rD  )partial_sessionsscanned_so_fartotalpartial_scanpartials        r   _publish_partialz4_run_scan_and_update_cache.<locals>._publish_partialm  s     0!01A!B!B -*8./+,3A38" "    -\dKKK #-W"5"5%&"""   s   <A   
AA)rE  rp  r0   rJ  r3   rd   r5   r6   r   r  )
_SCAN_LOCKrM   r  r8   r  rW  r/   r   r  rl  rD  rK   )r  startedr  callbackcomputedre  s         r   _run_scan_and_update_cacher  [  sn    
 .R .Rdikk"" )W%,\"%)\"	 	 	6 (AJ##d	R"X>>>H(22O!$_%8%8%H%H%\CPTPYP[P[L\L\!]!]/***$*L!! 	2 	2 	2$,L!),SL&&&&&&	2 +.dikk*:*:L'/2L4ORY4Y]a3a/b/bL+,(+L,<,<[!,L,L(M(MPQ(QL%% +.dikk*:*:L'/2L4ORY4Y]a3a/b/bL+,(+L,<,<[!,L,L(M(MPQ(QL%QQQQ%].R .R .R .R .R .R .R .R .R .R .R .R .R .R .R .R .R .RsQ   AH(BCF
D !DFDFBH(BHH((H,/H,c                    t           5  t          } | "|                                 r	 ddd           dS t          j        t
          ddidd          }|a|                                 ddd           dS # 1 swxY w Y   dS )a  Kick off a scan in a daemon thread if one isn't already running.

    Idempotent: concurrent callers see the in-flight thread and return
    immediately. The thread updates ``_SNAPSHOT_CACHE`` on completion so
    subsequent ``/achievements`` requests see fresh data. While running,
    it also publishes partial snapshots every ~250 sessions so the UI
    reflects incremental progress on long cold scans.
    Nr  Tzhermes-achievements-scan)targetkwargsrC   r   )_BACKGROUND_SCAN_LOCKr  is_alive	threadingThreadr  rT  )existingthreads     r   _start_background_scanr    s     
  *H$5$5$7$7        !-/6+	
 
 
 #)                 s   A65A66A:=A:forcec                   t          t          j                              }| st          |          r	t          pi S t          Mt	                      }t          |t                    r*t          |                    d          pd          }|a|p|a| r-t          d           t          t          S t          |          S t          $t          |          st                       t          S t                       t          |          S )u  Return the current achievements payload.

    Behavior matrix:

    * Fresh in-memory cache → return it instantly.
    * Stale on-disk snapshot → load it, kick a background rescan, return
      the stale data (UI decorates it with ``is_stale=True``).
    * No snapshot yet (first-ever run) → kick a background scan, return
      an empty-but-valid "pending" payload so the UI can render a spinner
      without blocking.
    * ``force=True`` (manual /rescan) → run synchronously, block the
      caller, replace the cache.

    Warm scans stay cheap (the checkpoint cache reuses per-session stats).
    Cold scans on 8000+ session databases take minutes; the background
    thread prevents that from ever blocking the dashboard request path.
    Nrp  r   F)r  )rM   r  r  r/   ri  r_  r`  r   r  r  r  r  )r  r  	persistedrp  s       r   evaluate_allr    s   & dikk

C %_S)) %$"$ !OO	i&& 	5y}}^<<ABBL'O!-!4 , 	#UCCCC&""&s+++ "s## 	%"$$$
 "3'''r   z/achievementsc                    K   t                      fddD             } t                    | d<   i                     d          pi dt                      i| d<   | S )Nc                *    i | ]}|v ||         S r   r   )rE   rX  rh  s     r   rZ  z achievements.<locals>.<dictcomp>  sP      `  `  `a  VW  [_  V_  V_q$q'  V_  V_  V_r   )r  r  r  r  r  rP  rp  is_stalerQ  status)r  r  r   r  )payloadrh  s    @r   r  r    s      >>D `  `  `  `  $R  `  `  `G,T22GJ88K  &B&(( GK Nr   z/scan-statusc                 "   K   t                      S r   )r  r   r   r   scan_statusr    s      !!!r   z/recent-unlocksc                 x   K   t                      } t          d | d         D             d d          d d         S )Nc                "    g | ]}|d          
|S r  r   r  s     r   rF   z"recent_unlocks.<locals>.<listcomp>  s!    DDDa
mD1DDDr   r  c                0    |                      d          pdS )Nr  r   r}  )r  s    r   r    z recent_unlocks.<locals>.<lambda>  s    TUTYTYZgThThTmlm r   T)r   reverser  )r  re  )rh  s    r   recent_unlocksr    s[      >>DDDd>2DDDJmJmw{|||  ~A  A  ~A  B  Br   z/sessions/{session_id}/badgesc                4   K   t                      }t           fd|d         D             d           }|s g dS t          |g          }g }t          D ]@}t	          ||          }|d         r&|                    t          i ||                     A |dS )Nc              3  4   K   | ]}|d          k    |V  dS )r  Nr   )rE   r  r  s     r   r^  z!session_badges.<locals>.<genexpr>  s1      QQ!1\?j3P3PA3P3P3P3PQQr   rq  )r  badgesr  )r  nextrd  r5  r  r  rC  )r  rh  sessionr	  r  r  r  s   `      r   session_badgesr    s      >>DQQQQtJ/QQQSWXXG 8(B777	**IF" I I
$Z;;* 	IMM-.F.Fv.FGGHHH$777r   z/rescanc                 .   K   ddit          d          S )NokT)r  )r  r   r   r   rescanr    s!      $3,T22233r   z/reset-statec                 l  K   t          di i           d adadt          d<   d t          d<   d t          d<   d t          d<   d t          d<   	 t	                                          d	
           n# t          $ r Y nw xY w	 t                                          d	
           n# t          $ r Y nw xY wdd	iS )Nr?  r   r0   r1   r2   r3   r4   r5   T)
missing_okr  )rR  r/   r  r8   r;  unlinkrD  r=  r   r   r   reset_stater    s       	2O"L!%L"&L!%L'+L#$$////     D 1111   $<s$   "A. .
A;:A;?"B" "
B/.B/)r   r   )r>   r?   r   r@   )rJ   rK   rL   rM   r   r7   )r   r7   )r1   r7   r   rG  )rS  r   r   r   )r   r.   )rh  r7   r   rG  )rv  r7   r   r7   )r  rM   r   r  r   )r  r.   r  r  r   r  )r  r  r   r7   )r  r   r   r  )r  r7   r   rK   )r  r  r  rK   r   rM   )r  rK   r   r  )r  rK   r   r  )r  rK   rz  rK   r  r@   r   r7   )r  r7   r	  r7   r   r7   )rJ   rK   r   rK   )r  r7   r   rK   )r?  r7   r   r7   )NNr   )rD  r  rE  rF  rG  rM   r   r7   )rq  r@   r   r7   )r  r7   rq  r@   r   r.   )r  r7   r  r  r   r7   )Nr   )rE  rF  rG  rM   r   r7   )r  rM   r   r7   )T)r  r  r   rG  )r   rG  )F)r  r  r   r7   )r  rK   )Z__doc__
__future__r   rA  r   r  r  r  pathlibr   typingr   r   r   r   r	   hermes_constantsr   ImportErrorosr   fastapir   rD  r  r  Lockr  r/   __annotations__r  r8   compiler  r  r  r  r   r  rH   rI   rP   r5  r9  r;  r=  rF  rR  rW  ri  rl  rs  ru  r~  r  r  r  r  r  r  r  r  r  r(  r.  r0  r3  r5  r>  rC  ru  rd  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r)   r  r  r   r   r   <module>r     s$     # " " " " "   				            1 1 1 1 1 1 1 1 1 1 1 1 1 1=0000000 = = == = = = = ==!!!!!!!! ! ! !! ! ! ! ! ! ! ! ! !! 
 Y^
,0 0 0 0 0         2:  P  RT  RV  W  W
"*vxzx|
}
}RZH"$OO
RZjlnlpqq

"*s
t
t@@@
c c c c* * * *N&>  CC  Qa  ky  CJ  `{  FK  FK  Lh  Lh  Lh  Fi  Fi  j  jN& "+A  SZ  hx  BL  Va  wI  TY  TY  Zz  Zz  Zz  T{  T{  |  |N& '9  KG  Ue  o}  GN  dC  NS  NS  Ti  Ti  Ti  Nj  Nj  k  k	N&
   =A  O_  iz  DL  _b  _b  cB  DG  _H  _H  JM  JM  Nn  ps  Jt  Jt  vy  vy  z\  ^`  va  va  ^b  c  cN&  )=Np  O  Yc  mu  Ka  lq  lq  rJ  rJ  rJ  lK  lK  L  LN& (1M  _e  sC  MW  ai  T  _d  _d  eB  eB  eB  _C  _C  D  DN& '9Jz  IY  cm  w~  Tf  qv  qv  wW  wW  wW  qX  qX  Y  YN& "+A  SN  \m  wA  KT  jx  CH  CH  Ij  Ij  Ij  Ck  Ck  l  lN& #,C  UF  Te  oy  CI  _q  |A  |A  B`  B`  B`  |a  |a  b  bN& $-E  WE  Sd  nx  BJ  `q  |A  |A  Bb  Bb  Bb  |c  |c  d  dN& &:  LU  ct  ~H  RX  dh  ~T  _d  _d  e}  e}  e}  _~  _~    N& +4L]~  M^  hr  |B  NR  hB  MR  MR  Sk  Sk  Sk  Ml  Ml  m  mN&  %.G  YN  \m  wH  Ra  tw  tw  xN  PR  tS  tS  UX  UX  Yq  su  Uv  Uv  sw  x  x!N&" $-H  ZW  ev  @Q  [d  wz  wz  {W  Y[  w\  w\  ^a  ^a  bp  rv  ^w  ^w  vx  y  y#N&$  )=  OZ  hy  CM  W\  hl  BX  ch  ch  iM  iM  iM  cN  cN  O  O%N&& !*?Pr  AR  \f  pw  CG  ]p  {@  {@  Aa  Aa  Aa  {b  {b  c  c'N&( #,C  UJ  Xi  s}  GR  ^b  xP  [`  [`  a{  a{  a{  [|  [|  }  })N&. "+J  \F  Ta  ky  CR  hA  LQ  LQ  Rn  Rn  Rn  Lo  Lo  p  p/N&0 #,C  Uc  q~  HV  `h  ~^  in  in  oK  oK  oK  iL  iL  M  M1N&2 %5Fz  IV  `n  xC  Yw  BG  BG  He  He  He  Bf  Bf  g  g3N&4 >Bv  ER  \f  pw  Mg  rw  rw  x^  x^  x^  r_  r_  `  `5N&6 "+BS}  LY  ct  ~D  WZ  WZ  [g  ik  Wl  Wl  nq  nq  rM  OR  nS  nS  VT  U  U7N&8 >Bv  ER  \f  p~  Ti  ty  ty  z_  z_  z_  t`  t`  a  a9N&: (;  MF  Ta  k|  FN  Z^  qt  qt  uU  WX  qY  qY  [^  [^  _m  os  [t  [t  pu  v  v;N&@ >u  DS  ]g  q@  Vd  ot  ot  uY  uY  uY  oZ  oZ  [  [AN&B '0M  _b  p  IS  ]d  zO  Z_  Z_  `x  `x  `x  Zy  Zy  z  zCN&D ODy  HW  ak  u~  Tc  ns  ns  tP  tP  tP  nQ  nQ  R  REN&F ODk  zI  S]  go  EZ  ej  ej  kG  kG  kG  eH  eH  I  IGN&H %5  GO  ]l  v@  JR  hx  CH  CH  Im  Im  Im  Cn  Cn  o  oIN&J &7Hz  IX  bl  v  Ue  pu  pu  vZ  vZ  vZ  p[  p[  \  \KN&L OD  N]  gq  {C  Yh  sx  sx  yY  yY  yY  sZ  sZ  [  [MN&N &7Ht  CR  \f  px  DH  ^o  z  z  @_  @_  @_  z`  z`  a  aON&T #,C  U\  jx  BL  V^  tE  PU  PU  Vt  Vt  Vt  Pu  Pu  v  vUN&V &7H~  M[  eo  y@  Vo  z  z  @\  @\  @\  z]  z]  ^  ^WN&X  )=  OA  O]  gq  {D  Zp  {@  {@  Ae  Ae  Ae  {f  {f  g  gYN&Z  )=  OA  O]  gq  {D  Zi  ty  ty  zT  zT  zT  tU  tU  V  V[N&` &7Hk  zH  R\  fp  F\  gl  gl  mL  mL  mL  gM  gM  N  NaN&b >Br  AO  Yc  ms  I\  gl  gl  mJ  mJ  mJ  gK  gK  L  LcN&d  )=  OD  R`  jt  ~F  \w  BG  BG  Hg  Hg  Hg  Bh  Bh  i  ieN&f &7  IG  Uc  mw  AF  \p  {@  {@  A]  A]  A]  {^  {^  _  _gN&h "+A  SD  R`  jt  ~D  Ze  pu  pu  vM  vM  vM  pN  pN  O  OiN&n >  CC  Q]  gq  {A  We  pu  pu  v[  v[  v[  p\  p\  ]  ]oN&p  )=N  NZ  dn  x@  Vi  ty  ty  zW  zW  zW  tX  tX  Y  YqN&r %5  GD  R^  hr  |C  Yg  rw  rw  xW  xW  xW  rX  rX  Y  YsN&t '9  KM  [g  q{  EL  bx  CH  CH  I^  I^  I^  C_  C_  `  `uN&v (;  M`  nz  DN  X_  uK  V[  V[  \o  \o  \o  Vp  Vp  q  qwN&x (;  MH  Vb  lv  @F  \u  @E  @E  FV  FV  FV  @W  @W  X  XyN&z &7  IL  Zf  pz  DJ  `n  y~  y~  \  \  \  y]  y]  ^  ^{N&| '9  KJ  Xd  nx  BI  _n  y~  y~  Y  Y  Y  yZ  yZ  [  [}N&~ !*?  QK  Ye  oy  CL  bq  |A  |A  B\  B\  B\  |]  |]  ^  ^N&@ "+A  Sa  o{  EO  Yc  yT  _d  _d  ex  ex  ex  _y  _y  z  zAN&F "+A  S_  m|  FP  Zc  yI  TY  TY  Zr  Zr  Zr  Ts  Ts  t  tGN&H %5  Gf  tC  MW  af  |K  V[  V[  \y  \y  \y  Vz  Vz  {  {IN&J %5  GO  ]j  t~  HP  fz  EJ  EJ  Kb  Kb  Kb  Ec  Ec  d  dKN&L '9  KY  gu  I  S[  q~  IN  IN  Oj  Oj  Oj  Ik  Ik  l  lMN&N (;  M[  iw  AK  UZ  pC  NS  NS  Tn  Tn  Tn  No  No  p  pON&T (;L}  LW  ak  u  Ud  ot  ot  uO  uO  uO  oP  oP  Q  QUN&V &7  IF  T_  is  }G  ]o  z  z  @X  @X  @X  zY  zY  Z  ZWN&X "+A  SB  P[  eo  y  Ue  pu  pu  vN  vN  vN  pO  pO  P  PYN&Z #,C  UD  R]  gq  {B  NR  hv  AF  AF  Gc  Gc  Gc  Ad  Ad  e  e[N& N N N NbP P P PX X X XZ Z Z Z   A A A A   
 
 
 
L L L LD D D D"L L L L   ^ ^ ^ ^1 1 1 1 1    $. . . .	 	 	 	S S S S
	2 	2 	2 	2; ; ; ;m m m m`_ _ _ _$j j j j$i i i i?!<?#%P? $%D? %&N	?
 '(\? 8? #$B? :? C? ;? >? 9? (? A?  :?  6!?" N#? ?$ !"O%?& M'?( 6)?* ?+?,  J-?. J/?0 )1?2 %&R3?4 75?6 H7?8 69?: 2;?< O=?> :??@ DA?B >C?D @E? ? ?F 5G?H .I?J $K?L 5M?N >O?P ;Q?R 8S?T 4U?V 5W?X 8Y?Z  !H[?\ A]?^ 5_?` Ka?b Xc?d 6e?f 3g? ?h =!\7B]=UD&6B}? ? ?D? ? ? ?Q Q Q Q$     '+o o o o odQ Q Q Qh3 3 3 3   * DI ' ' ' ' ' 'T6 6 6 6 6
 7;  : : : :&	((    *:R :R :R :R :Rz   04( 4( 4( 4( 4(n O   N" " " B B B
 +,,8 8 8 -,8 Y4 4 4 ^    s!   : A
AA A+*A+