Ë
    áß²j�7 ã                  ó"  — 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	m
Z
 ddlmZmZ ddlmZmZmZmZmZmZ ddlmZ ddlmZ  ej2                  e«      Zd	Z G d
„ d«      Ze	d_d„«       Zd`d„Zdad„Z dbd„Z!dcd„Z"dZ#dZ$dZ%dZ&ddd„Z'dedfd„Z(dedgd„Z)	 dh	 	 	 	 	 did„Z*djd„Z+dedkd„Z,dld„Z-dmd„Z.dnd„Z/dodpd„Z0dZ1d e1z   d!z   Z2d"Z3d#Z4d$e3z   d%z   e4z   d&z   Z5d'e1z   d(z   e3z   d)z   e4z   d*z   Z6dqd+„Z7d,d-d.d/œZ8d0e9d1<   drd2„Z:dsd3„Z;dtd4„Z<d5Z=dud6„Z>dvd7„Z?dwd8„Z@dxd9„ZA	 dy	 	 	 	 	 	 	 dzd:„ZBddddd;œ	 	 	 	 	 	 	 	 	 	 	 d{d<„ZCd=ZDd|d>„ZEd}d?„ZFd~d@„ZGddA„ZHdBZId€dC„ZJded�dD„ZKdbdE„ZLd‚dF„ZM	 de	 	 	 dƒdG„ZNdHZOd„dI„ZP	 dedJdKœ	 	 	 	 	 	 	 	 	 d…dL„ZQdM„ ZRd†dN„ZSd‡dO„ZT	 dˆ	 	 	 	 	 	 	 d‰dP„ZUe G dQ„ dR«      «       ZVdbdS„ZW	 	 dŠ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d‹dT„ZXdŒdU„ZY	 	 d�	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dŽdV„ZZdWdXdYdYdZœZ[	 	 	 	 d�	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d�d[„Z\g d\¢Z]dd]l^m_Z_ 	 de	 	 	 d‘d^„Z`y)’u)  Background memory/skill review â€” fork the agent to evaluate the turn. After every turn
``AIAgent.run_conversation`` may spawn a daemon thread that replays the conversation snapshot in a
forked :class:`AIAgent` and asks "should any skill/memory be saved or updated?". Writes go
straight to the memory + skill stores; the main conversation and prompt cache are never touched.
The fork inherits the parent's live runtime (provider, model, credentials, cached system prompt)
so it hits the same prefix cache, and runs under a dispatch-side tool whitelist.é    )ÚannotationsN)ÚcontextmanagerÚsuppress)Ú	dataclassÚfield)ÚAnyÚDictÚIteratorÚListÚOptionalÚTuple)Úresolve_prompt_cache_scope_safe)Úthread_scoped_silenceg       @c                  ó0   — e Zd ZdZdd„Zdd„Zd	d„Zd
d„Zy)Ú_BackgroundReviewRunz9Per-review cancellation and request-completion handshake.c                óÄ   — t        j                  «       | _        t        j                  «       | _        t        j                  «       | _        d | _        dx| _        | _        y )NF)	Ú	threadingÚEventÚcancel_requestedÚrequest_doneÚLockÚ_lockÚ_review_agentÚ_request_finishedÚ_cancel_dispatched©Úselfs    ú</home/ubuntu/.hermes/hermes-agent/agent/background_review.pyÚ__init__z_BackgroundReviewRun.__init__   sF   € Ü )§¡Ó 1ˆÔÜ%ŸO™OÓ-ˆÔÜ—^‘^Ó%ˆŒ
Ø!ˆÔØ;@Ð@ˆÔ Õ!8ó    c                ó¶   — | j                   5  | j                  j                  «       s| j                  r
	 ddd«       y|| _        	 ddd«       y# 1 sw Y   yxY w)z9Atomically admit the first provider-capable review phase.NFT)r   r   Úis_setr   r   )r   Úreview_agents     r   Úbegin_requestz"_BackgroundReviewRun.begin_request%   sR   € à�Z‰Zñ 	Ø×$Ñ$×+Ñ+Ô-°×1GÒ1GØ÷	ð 	ð ".ˆDÔØ÷		÷ 	ñ 	ús   �(A¾AÁAc                óä   — | j                   5  | j                  j                  «        | j                  �| j                  r
	 ddd«       yd| _        | j                  cddd«       S # 1 sw Y   yxY w)z?Fence startup and return the running fork, if one was admitted.NT)r   r   Úsetr   r   r   s    r   Úcancelz_BackgroundReviewRun.cancel-   sd   € à�Z‰Zñ 	&Ø×!Ñ!×%Ñ%Ô'Ø×!Ñ!Ð)¨T×-DÒ-DØ÷	&ð 	&ð '+ˆDÔ#Ø×%Ñ%÷	&÷ 	&ò 	&ús   �4A&Á
A&Á&A/c                ó’   — | j                   5  | j                  r
	 ddd«       yd\  | _        | _        	 ddd«       y# 1 sw Y   yxY w)z>Latch request completion once; the caller publishes the event.NF)TNT)r   r   r   r   s    r   Úmark_request_finishedz*_BackgroundReviewRun.mark_request_finished6   sJ   € à�Z‰Zñ 	Ø×%Ò%Ø÷	ð 	ð :DÑ6ˆDÔ" DÔ$6Ø÷		÷ 	ñ 	ús   �=¤=½AN©ÚreturnÚNone)r#   r   r+   Úbool)r+   r   )r+   r-   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r$   r'   r)   © r    r   r   r      s   „ ÙCóAóó&ôr    r   c              #  ój   K  — t        | |d«      }|€d–— y|5  d–— ddd«       y# 1 sw Y   yxY w­w)zF``with`` over a lock attribute that may be absent (direct test stubs).N)Úgetattr)ÚagentÚattrÚlocks      r   Ú_optional_lockr8   ?   s;   è ø€ ô �5˜$ Ó%€DØ€|ÛØØ	ñ Û÷÷ ñ üs   ‚3™'ž	3§0¬3c                ó>  — t        «       }	 t        | dd«      }|€t        j                  «       x}| _        |5  t        | dd«      }|�$|j
                  j                  «       s
	 ddd«       y|| _        ddd«       |S # 1 sw Y   |S xY w# t        t        f$ r Y yw xY w)zCInstall a unique run token on the parent before ``Thread.start()``.Ú_background_review_lockNÚ_background_review_run)
r   r4   r   r   r:   r   r"   r;   ÚAttributeErrorÚ	TypeError)r5   Úrunr7   Úcurrents       r   Úprepare_background_review_runr@   J   s­   € ä
Ó
 €Cð
Ü�uÐ7¸Ó>ˆØˆ<Ü3<·>±>Ó3CÐCˆD�5Ô0Øñ 	/Ü˜eÐ%=¸tÓDˆGØÐ"¨7×+?Ñ+?×+FÑ+FÔ+HØ÷	/ð 	/ð ,/ˆEÔ(÷		/ð €J÷	/ð €Jûô œIÐ&ò Ùðús:   Œ,B
 ¸+A=Á#B
 Á,A=Á3B
 Á=BÂB
 ÂB
 Â
BÂBc                óÌ   — |�|j                  «       syt        | d«      5  t        | dd«      |u rd| _        ddd«       |j                  j                  «        y# 1 sw Y   Œ$xY w)zGPublish one run's request exit without clearing a successor (ABA-safe).Nr:   r;   )r)   r8   r4   r;   r   r&   )r5   r>   s     r   Úfinish_background_review_runrB   [   sc   € à
€{˜#×3Ñ3Ô5ØÜ	˜Ð8Ó	9ñ 0Ü�5Ð2°DÓ9¸SÑ@Ø+/ˆEÔ(÷0ð ×Ñ×ÑÕ÷0ð 0ús    AÁA#c                ó¬   ‡ — dˆ fd„}	 t        j                  |dd¬«      j                  «        y# t        $ r t        j                  dd¬«       Y yw xY w)	z¢Request abort off-thread so a wedged abort hook cannot stall the live turn (the bounded
    ``request_done`` wait in the canceller relies on this returning fast).c                 óv   •— 	 ddl m}   | ‰dd¬«       y # t        $ r t        j	                  dd¬«       Y y w xY w)	Nr   )Úrequest_hard_interruptzsuperseded by a new live turnzbackground review superseded)Útool_reasonz;Failed to cancel in-flight background review for a new turnT©Úexc_info)Úagent.interrupt_compatrE   Ú	ExceptionÚloggerÚdebug)rE   r#   s    €r   Ú
_interruptz0_interrupt_background_review.<locals>._interrupth   sB   ø€ ð	gÝEá"ØÐ=ÐKiöøô ò 	gÜ�L‰LÐVÐaeˆLÖfð	gús   ƒ • 8·8Tzbg-review-cancel)ÚtargetÚdaemonÚnamez5Failed to start background-review cancellation threadrG   Nr*   )r   ÚThreadÚstartrJ   rK   rL   )r#   rM   s   ` r   Ú_interrupt_background_reviewrS   e   sN   ø€ õgð]Ü×Ñ 
°4Ð>PÔQ×WÑWÕYøÜò ]Ü�‰ÐLÐW[ˆÖ\ð]ús   ‰&0 ° AÁAc                ó8  — t        | d«      5  t        | dd«      }t        | dd«      }ddd«       €n|j                  «       }|�t        |«       |€y|j                  j                  t        ¬«      st        j                  dt        «       yy# 1 sw Y   ŒixY w)u“  Cancel the current review and await its request-phase acknowledgement. Foreground priority:
    past the bounded deadline, warn and let the live turn proceed â€” self-improvement work must
    never block a user-facing turn.

    Foreground priority is preserved: if the review does not acknowledge within the bounded deadline, a
    warning is logged and the live turn proceeds anyway. See #84423.
    r:   r;   NÚ_background_review_agent)ÚtimeoutzeBackground review did not acknowledge cancellation within %.1fs; proceeding with foreground live turn)	r8   r4   r'   rS   r   ÚwaitÚ)_BACKGROUND_REVIEW_CANCEL_TIMEOUT_SECONDSrK   Úwarning)r5   r>   Úlegacy_agentr#   s       r   Ú&cancel_background_review_for_live_turnr[   x   s£   € ô 
˜Ð8Ó	9ñ HÜ�eÐ5°tÓ<ˆÜ˜uÐ&@À$ÓGˆ÷Hð $' ;‘<°C·J±J³L€Lð ÐÜ$ \Ô2Ø
€{ØØ×Ñ× Ñ Ô)RÐ ÔSÜ�‰ð3ä5õ	
ð T÷Hð Hús   �BÂBé   iÀ'	 g      è?iÀÔ c                ó´   — t        | j                  d«      t        «      r| j                  di «      ni }|j                  di «      }t        |t        «      r|S i S )zS``cfg["auxiliary"]["background_review"]`` as a dict (``{}`` on any shape mismatch).Ú	auxiliaryÚbackground_review)Ú
isinstanceÚgetÚdict)ÚcfgÚauxÚtasks      r   Ú_task_blockrf   ¢   sL   € ä&0°·±¸Ó1EÄtÔ&Lˆ#�'‰'�+˜rÔ
"ÐRT€CØ�7‰7Ð&¨Ó+€DÜ˜d¤DÔ)ˆ4Ð1¨rÐ1r    c                ó~   — | �t        | t        «      r| S i S 	 ddlm} t	         |«       «      S # t
        $ r i cY S w xY w)z©``auxiliary.background_review`` (or ``{}`` on any failure); pass a pre-loaded ``task_cfg``
    so the spawn / resolve / prompt paths do not re-read config on every turn.r   ©Úload_config_readonly)r`   rb   Úhermes_cli.configri   rf   rJ   )Útask_cfgri   s     r   Ú_background_review_task_configrl   ©   sI   € ð ÐÜ% h´Ô5ˆxÐ=¸2Ð=ðÝ:ÜÑ/Ó1Ó2Ð2øÜò ØŠ	ðús   ˜. ®<»<c           
     óÜ   — t        t        | dd«      dd«      }t        |t        «      rt        |t        «      s|dk  rt        S t        t        t        dt        |t        z  «      «      «      S )ur  Default budget: 75% of the review fork's resolved context window, capped at the historical
    600k ceiling. The fork's ``context_compressor.context_length`` is already resolved by
    ``AIAgent.__init__`` (config overrides, catalog, endpoint probe) â€” no second lookup here.
    Unknown window â†’ a conservative fixed fallback so unattended review work stays bounded.Úcontext_compressorNÚcontext_lengthr   é   )	r4   r`   Úintr-   Ú!_REVIEW_MAX_INPUT_TOKENS_FALLBACKÚminÚ_REVIEW_MAX_INPUT_TOKENS_CAPÚmaxÚ_REVIEW_INPUT_CONTEXT_FRACTION)r#   Úcontext_windows     r   Ú$_context_derived_review_input_budgetrx   µ   s`   € ô
 œW \Ð3GÈÓNÐP`ÐbfÓg€NÜ�n¤cÔ*¬j¸ÌÔ.NÐR`ÐdeÒReÜ0Ð0ÜÔ+¬S°´C¸ÔIgÑ8gÓ4hÓ-iÓjÐjr    c                ó’   — t        | «      }	 t        |d   «      }|dkD  r|S dS # t        t        t        f$ r t        |«      cY S w xY w)u³   Aggregate input-token budget for one review fork (None = unlimited; <= 0 disables). Unset
    or malformed ``max_input_tokens`` â†’ derived from ``review_agent``'s context window.Úmax_input_tokensr   N)rl   rq   ÚKeyErrorr=   Ú
ValueErrorrx   )rk   r#   re   Úbudgets       r   Ú_review_input_token_budgetr~   À   s\   € ô
 *¨(Ó3€DðBÜ�TÐ,Ñ-Ó.ˆð ˜a’Zˆ6Ð) TÐ)øô ”i¤Ð,ò BÜ3°LÓAÒAðBús   �$ ¤AÁAc                 óÆ   — 	 ddl m}  ddlm} t	         | «       «      } ||j                  d«      d¬«      |fS # t        $ r t        j                  dd¬«       di fcY S w xY w)	u§   Single config read -> ``(enabled, task_cfg)``. Fail-open (``enabled=True``) so a broken
    config never silently disables reviews â€” but WARN so the cost is visible.r   rh   ©Úis_truthy_valueÚenabledT©ÚdefaultzVFailed to read background_review.enabled; leaving automatic review enabled (fail-open)rG   )	rj   ri   Úutilsr�   rf   ra   rJ   rK   rY   )ri   r�   re   s      r   Úload_background_review_settingsr†   Í   si   € ðÝ:Ý)ÜÑ/Ó1Ó2ˆÙ˜tŸx™x¨	Ó2¸DÔAÀ4ÐGÐGøÜò Ü�‰ð)àð 	ô 	
ð
 �RˆxŠðús   ‚69 ¹$A ÁA c                ó´  ‡— | j                  «       }|j                  d«      xs d}| j                  | j                  |j                  d«      xs d|j                  d«      xs d|dk(  rdn|t	        | dd«      t        t	        | di «      xs i «      t	        | d	d«      t	        | d
d«      t        t	        | dg «      xs g «      ddœ}t        |«      Šˆfd„dD «       \  }}}}|r-|dk7  r(|r&|| j                  xs dk(  r|| j                  xs dk(  r|S 	 ddlm	}	  |	||||¬«      }
|
j                  d«      xs ||
j                  d«      xs |dœdD �ci c]  }||
j                  |«      “Œ c}¥t        |
j                  d«      xs i «      t        |
j                  d«      xs g «      ddœ¥S c c}w # t        $ r}t        | |||«       |cY d}~S d}~ww xY w)a[  Resolve provider/model/credentials for the review fork. Default (auto / unset / same as
    parent): the parent's live runtime with ``routed=False`` (codex_app_server -> codex_responses
    downgrade applied). When ``auxiliary.background_review.{provider,model}`` names a different
    concrete model, resolve that runtime and set ``routed=True``.Úapi_modeNÚapi_keyÚbase_urlÚcodex_app_serverÚcodex_responsesÚ_credential_poolÚrequest_overridesÚ
max_tokensÚacp_commandÚacp_argsF)ÚproviderÚmodelr‰   rŠ   rˆ   Úcredential_poolrŽ   r�   ÚcommandÚargsÚroutedc              3  óx   •K  — | ]1  }t        ‰j                  |d «      «      j                  «       xs d–— Œ3 y­w)Ú N)Ústrra   Ústrip)Ú.0Úkeyre   s     €r   ú	<genexpr>z*_resolve_review_runtime.<locals>.<genexpr>ï   s7   øè ø€ ò >Ø36ŒˆD�H‰H�S˜"ÓÓ×$Ñ$Ó&Ò.¨$Ó.ñ>ùs   ƒ7:)r’   r“   rŠ   r‰   Úautor™   r   )Úresolve_runtime_provider)Ú	requestedÚtarget_modelÚexplicit_api_keyÚexplicit_base_urlr’   r“   )r’   r“   )r‰   rŠ   rˆ   r”   r•   r–   T)rŽ   r–   r—   )Ú_current_main_runtimera   r’   r“   r4   rb   Úlistrl   Úhermes_cli.runtime_providerr    rJ   Ú_warn_review_routing_fallback)r5   rk   Úparent_runtimeÚparent_api_modeÚparentÚtask_providerÚ
task_modelÚtask_base_urlÚtask_api_keyr    Úrpr�   Úere   s                @r   Ú_resolve_review_runtimer²   Þ   sæ  ø€ ð
 ×0Ñ0Ó2€NØ$×(Ñ(¨Ó4Ò<¸€Oà—N‘N¨U¯[©[Ø!×%Ñ% iÓ0Ò8°DÀn×FXÑFXÐYcÓFdÒFlÐhlØ)8Ð<NÒ)NÑ%ÐTcÜ" 5Ð*<¸dÓCÜ!¤'¨%Ð1DÀbÓ"IÒ"OÈRÓPÜ˜e \°4Ó8ÄWÈUÐTaÐcgÓEhÜ”W˜U J°Ó3Ò9°rÓ:Àeñ€Fô *¨(Ó3€Dó>Ø:fô>Ñ:€M�:˜}¨lñ ˜m¨vÒ5¹*Ø˜%Ÿ.™.Ò.¨BÒ/°JÀ5Ç;Á;ÒCTÐRTÒ4UàˆðÝHÙ%Ø#°*Ø)¸]ô
ˆð
 Ÿ™˜zÓ*Ò;¨mÀbÇfÁfÈWÃoÒFcÐYcñ
à+lÖm Cˆs�B—F‘F˜3“KÑÒmð
ô "& b§f¡fÐ-@Ó&AÒ&GÀRÓ!HÜ˜Ÿ™ ›Ò-¨2Ó.¸$ò	
ð 	
ùâmøô ò Ü% e¨]¸JÈÔJØ�ûðús2   ÄA F4 ÅF/Å-AF4 Æ/F4 Æ4	GÆ=GÇGÇGc                ód  — d|› d|› dt        |«      j                  «       d   › d| j                  › d| j                  › d�}t        j                  d|«       t        | d	d
«      ryd| _        t        | dd«      }t        |«      r!t        t        «      5   ||«       ddd«       yy# 1 sw Y   yxY w)a"  The configured review route could not be resolved, so the fork runs on the main model. That
    was a debug-level line nobody saw (#116055): the misrouted model never ran and nothing said so.
    User-visible notice once per agent (same rail as the reasoning_effort notice); log every time.u*   âš  auxiliary.background_review.provider='z
' (model 'z') could not be resolved: r   u.    â€” background reviews run on the main model ú/z9 instead. Run 'hermes doctor' to check auxiliary routing.ú%sÚ_warned_bg_review_routingFNTÚ_emit_warning)rš   Ú
splitlinesr’   r“   rK   rY   r4   r¶   Úcallabler   rJ   )r5   r¬   r­   ÚerrorÚmessageÚemits         r   r¨   r¨     s¿   € ð
 5°]°OÀ:ÈjÈ\ð ZÜ˜“Z×*Ñ*Ó,¨QÑ/Ð0Ð0^Ø�>‰>Ð
˜!˜EŸK™K˜=Ð(að	cð ô
 ‡N�N�4˜Ô!ÜˆuÐ1°5Ô9ØØ&*€EÔ#Ü�5˜/¨4Ó0€DÜ�„~Ü”iÓ ñ 	Ù�ŒM÷	ð 	ð ÷	ð 	ús   Â	B&Â&B/c                óˆ   — t        | dd«      }||�t        |«      ndfD ]!  }t        |dd«      }|€Œ|€Œt        |«      c S  y)u  Whether a fork inheriting ``agent``'s runtime could act at all: an agent-as-provider client
    shim declaring ``SUPPORTS_HERMES_TOOL_CALLS = False`` (instance or class) is skipped â€” the fork
    would be a guaranteed no-op that still pays a full spawn. Silence means capable.ÚclientNÚSUPPORTS_HERMES_TOOL_CALLST)r4   Útyper-   )r5   r¾   Ú	candidateÚ	supporteds       r   Ú_parent_can_emit_tool_callsrÃ     sZ   € ô �U˜H dÓ+€FØ¨fÐ.@œd 6œlÀdÐKò #ˆ	Ü˜IÐ'CÀTÓJˆ	ØÑ  YÑ%:Ü˜	“?Ò"ð#ð r    c                ó¸   — | j                  d«      }t        |t        «      rdj                  d„ |D «       «      }t        |t        «      r|j                  «       S dS )NÚcontentÚ c              3  ób   K  — | ]'  }t        |t        «      sŒ|j                  d d«      –— Œ) y­w)Útextr™   N©r`   rb   ra   )rœ   Úbs     r   rž   z_msg_text.<locals>.<genexpr>)  s$   è ø€ ÒI¨1´ZÀÄ4Õ5H�Q—U‘U˜6 2×&ÑIùs   ‚/˜/r™   )ra   r`   r¦   Újoinrš   r›   )ÚmÚcs     r   Ú	_msg_textrÎ   &  sH   € Ø	�‰ˆiÓ€AÜ�!”TÔØ�H‰HÑI°ÔIÓIˆÜ" 1¤cÔ*ˆ1�7‰7‹9Ð2°Ð2r    c                ó(  — t        | xs g «      }t        |«      |kD  rE|| d }t        |d   t        «      r|d   j	                  d«      dk(  sn|dz  }t        |«      |kD  rŒE|S g }|dt        |«        D ]ù  }t        |t        «      sŒ|j	                  d«      t        |«      j                  dd«      }}|dk(  r|r|j                  d	|dd
 › �«       Œ_|dk(  sŒe|j	                  d«      rj|d   D �cg c]8  }t        |t        «      sŒ|j	                  d«      xs i j	                  dd«      ‘Œ: }	}|j                  ddj                  |	«      › d�«       |sŒã|j                  d|dd › �«       Œû ddj                  |«      z   }
d|
dœg|z   S c c}w )a  Compact replay for the routed (different-model) path only: keep the recent ``tail``
    messages verbatim (extended so the kept run never starts on a tool result) and collapse older
    turns into one synthetic user-role digest, preserving role alternation.Nr   ÚroleÚtoolrp   Ú
rÆ   ÚuserzUSER: i,  Ú	assistantÚ
tool_callsÚfunctionrP   ú?zASSISTANT[tools: ú, ú]zASSISTANT: éÈ   uœ   [Earlier conversation digest â€” older turns summarised to bound the review's cold-write cost on the routed aux model. Recent turns follow verbatim below.]
)rÐ   rÅ   )	r¦   Úlenr`   rb   ra   rÎ   ÚreplaceÚappendrË   )Úmessages_snapshotÚtailÚmsgsÚkeepÚlinesrÌ   rÐ   rÈ   ÚtcÚnamesÚdigests              r   Ú_digest_historyræ   -  s§  € ô Ð!Ò' RÓ(€DÜ
ˆd‹)�dÒ
Ø�T�E�Fˆ|ˆÜ˜4 ™7¤DÔ)¨d°1©g¯k©k¸&Ó.AÀVÒ.KØØ�‰	ˆô	 ˆd‹)�dÓ
ð ˆØ€EØ�+”C˜“I�:Ðò 9ˆÜ˜!œTÔ"ØØ—U‘U˜6“]¤I¨a£L×$8Ñ$8¸¸sÓ$CˆdˆØ�6Š>™dØ�L‰L˜6 $ t¨ * Ð.Õ/Ø�[Ó Ø�u‰u�\Ô"ØOPÐQ]ÉÖwÈÔblÐmoÔquÕbv˜"Ÿ&™& Ó,Ò2°×7Ñ7¸ÀÕDÐw�ÐwØ—‘Ð0°·±¸5Ó1AÐ0BÀ!ÐDÔEÚØ—‘˜{¨4°°¨:¨,Ð7Õ8ð9ð	$à&*§i¡i°Ó&6ñ	7ð ð
 ¨Ñ/Ð0°4Ñ7Ð7ùò xs   Ã3FÄ	'Fuì  TWO distinct stores â€” pick the right one for each fact:
  â€¢ USER.md (memory tool, target='user'): who the user is â€” persona, preferences, communication and work style, personal details they revealed, and expectations about how you should behave.
  â€¢ MEMORY.md (memory tool, target='memory'): facts about the ENVIRONMENT you operate in â€” tool quirks, project conventions, config gotchas, paths and endpoints that matter.

One fact goes to ONE store, never both â€” writing it to both bloats both files until they hit their size limits and crowds out the facts that matter; misrouting it puts it where the next session won't look. If the tool schema lists only one target, that store is the only one enabled â€” use it and skip the other.

zXReview the conversation above and consider saving to memory if appropriate.

Memory has z¬If something stands out, save it once, in the right store, using the memory tool with the matching target. If nothing is worth saving, just say 'Nothing to save.' and stop.uH  What a skill IS: the instructions for doing a class of task the most efficient and correct way, to THIS user's specifications â€” the procedure, the tools and commands that work, the order, the user's preferences for how the result should look, and the pitfalls that cost time. A future session should be able to follow it and produce what the user wants on the first try. Everything below is about writing that well:
  â€¢ Procedure first: the steps in the order they are done, with the concrete commands, tool calls, and decision points. Lessons and pitfalls attach to the step they affect.
  â€¢ A pitfall is a generalizable rule + one clause of WHY (the mechanism), imperative. 'Grep the test tree for the SYMBOL before widening a helper signature â€” hand-rolled mocks reimplement the old shape and fail on a shard you did not run.' Not a narrative of what happened this session.
  â€¢ No PR/issue numbers, dates, ticket IDs, or quoted user text as content â€” the rule must stand without the incident behind it. Keep a short quote ONLY when the quote itself is the clearest statement of the rule.
  â€¢ The same lesson learned twice is ONE rule. Before adding, search the skill (and its references/) for the rule already stated; strengthen or clarify it rather than appending a second copy.
  â€¢ Not a duplicate of what the environment already teaches: repo AGENTS.md files, tool schema descriptions, and other always-loaded context. A skill carries the WORKFLOW and the pitfalls; it does not restate the codebase map or a tool's parameter list.
  â€¢ Always-on rules (standing user preferences, gates that apply to every instance of the task) live in SKILL.md itself, whole. references/ is for depth that is only needed sometimes: a decision table, a recipe, a domain note â€” each file topical and reusable, never '<date>-<incident>.md'. Prefer extending an existing references/ file over creating one; a skill with dozens of one-off references is the failure shape, not the goal.
  â€¢ Fix the skill in place when it is wrong: edit the sentence that misled, do not append 'UPDATE: actually...' underneath it.

u®   (these become persistent self-imposed constraints that bite you later when the environment changes):
  â€¢ Environment-dependent failures: missing binaries, fresh-install errors, post-migration path mismatches, 'command not found', unconfigured credentials, uninstalled packages. The user can fix these â€” they are not durable rules.
  â€¢ Negative claims about tools or features ('browser tools do not work', 'X tool is broken', 'cannot use Y from execute_code'). These harden into refusals the agent cites against itself for months after the actual problem was fixed.
  â€¢ Session-specific transient errors that resolved before the conversation ended. If retrying worked, the lesson is the retry pattern, not the original failure.
  â€¢ One-off task narratives. A user asking 'summarize today's market' or 'analyze this PR' is not a class of work that warrants a skill.

  â€¢ Unresolved failures: if the session ended WITHOUT actually finding a working method â€” you tried several things, none worked, and told the user to check manually â€” do NOT write those attempts up as a 'reliable workflow' or 'recommended approach'. That presents an untested sequence of failures as validated guidance a future session will trust and repeat. Either say 'Nothing to save', or, only if you are independently confident of a real working alternative (not something you are merely guessing might work), capture ONLY that alternative â€” never the dead ends, and never dressed up as best practice.

If a tool failed because of setup state, capture the FIX (install command, config step, env var to set) under an existing setup or troubleshooting skill â€” never 'this tool does not work' as a standalone constraint.

u  Review the conversation above and update the skill library. Be ACTIVE â€” most sessions produce at least one skill update, even if small. A pass that does nothing is a missed learning opportunity, not a neutral outcome.

Target shape of the library: CLASS-LEVEL skills, each with a SKILL.md of always-on rules and a small `references/` set of topical depth. Not a flat list of narrow one-session skills, and not an umbrella hoarding a references/ file per session. This shapes HOW you update, not WHETHER you update.

uË  Signals to look for (any one of these warrants action):
  â€¢ User corrected your style, tone, format, legibility, or verbosity. Frustration signals like 'stop doing X', 'this is too verbose', 'don't format like this', 'why are you explaining', 'just give me the answer', 'you always do Y and I hate it', or an explicit 'remember this' are FIRST-CLASS skill signals, not just memory signals. Update the relevant skill(s) to embed the preference so the next session starts already knowing.
  â€¢ User corrected your workflow, approach, or sequence of steps. Encode the correction as a pitfall or explicit step in the skill that governs that class of task.
  â€¢ Non-trivial technique, fix, workaround, debugging path, or tool-usage pattern emerged that a future session would benefit from. Capture it.
  â€¢ A skill that got loaded or consulted this session turned out to be wrong, missing a step, or outdated. Patch it NOW.

Preference order â€” prefer the earliest action that fits, but do pick one when a signal above fired:
  1. UPDATE A CURRENTLY-LOADED SKILL. Look back through the conversation for skills the user loaded via /skill-name or you read via skill_view. If any of them covers the territory of the new learning, PATCH that one first (re-load it with skill_view during this review â€” see Read-before-write below). It is the skill that was in play, so it's the right one to extend â€” but only if it is curator-managed. Bundled, hub, pinned, and user-owned skills are off-limits to you no matter how relevant (see Protected skills below); for those, fall through to the next option.
  2. UPDATE AN EXISTING UMBRELLA (via skills_list + skill_view). If no loaded skill fits but an existing class-level skill does, patch it. Add a subsection, a pitfall, or broaden a trigger.
  3. ADD A SUPPORT FILE under an existing umbrella. Skills can be packaged with three kinds of support files â€” use the right directory per kind:
     â€¢ `references/<topic>.md` â€” topical depth needed only sometimes: a decision table, a reproduction recipe, provider quirks, condensed domain notes or API excerpts. Name it by TOPIC and extend an existing file when one covers the topic; do not create a per-session or per-incident file, and do not paste error transcripts â€” distill them to the rule.
     â€¢ `templates/<name>.<ext>` â€” starter files meant to be copied and modified (boilerplate configs, scaffolding, a known-good example the agent can `reproduce with modifications`).
     â€¢ `scripts/<name>.<ext>` â€” statically re-runnable actions the skill can invoke directly (verification scripts, fixture generators, deterministic probes, anything the agent should run rather than hand-type each time).
     Add support files via skill_manage action=write_file with file_path starting 'references/', 'templates/', or 'scripts/'. The umbrella's SKILL.md should gain a one-line pointer to any new support file so future agents know it exists.
  4. CREATE A NEW CLASS-LEVEL UMBRELLA SKILL when no existing skill covers the class. The name MUST be at the class level. The name MUST NOT be a specific PR number, error string, feature codename, library-alone name, or 'fix-X / debug-Y / audit-Z-today' session artifact. If the proposed name only makes sense for today's task, it's wrong â€” fall back to (1), (2), or (3).

Read-before-write (ENFORCED â€” skill_manage refuses otherwise): before you patch or edit an existing skill's SKILL.md, call skill_view(name) for that skill during this review. Before you overwrite or remove an EXISTING supporting file, call skill_view(name, file_path=...) for that exact file. Content quoted earlier in the conversation transcript does NOT count â€” the guard requires a fresh load within this review, and your write must be based on what skill_view just returned. Creating a brand-new skill or adding a NEW supporting file needs no prior read. If a write is refused with a read-before-write error, call skill_view for the named target once and retry the write once; do not loop.

User-preference embedding (important): when the user expressed a style/format/workflow preference, the update belongs in the SKILL.md body, not just in memory. Memory captures 'who the user is and what the current situation and state of your operations are'; skills capture 'how to do this class of task for this user'. When they complain about how you handled a task, the skill that governs that task needs to carry the lesson.

If you notice two existing skills that overlap, note it in your reply â€” the background curator handles consolidation at scale.

Protected skills (DO NOT edit these):
  â€¢ Bundled skills (shipped with Hermes, e.g. 'hermes-agent').
  â€¢ Hub-installed skills (installed via 'hermes skills install').
  â€¢ Skills in skills.external_dirs (externally owned).
  â€¢ PINNED skills (marked via 'hermes curator pin'). You are an autonomous no-user-present actor, so pin blocks your writes too â€” content updates included. Only the user, in a foreground session, can change a pinned skill.
  â€¢ USER-OWNED skills â€” anything not curator-managed. A skill the user hand-wrote, installed by URL, or asked a foreground agent to create is theirs, not yours; your writes to it WILL be refused. This includes skills that were loaded or consulted this session: being in play does not make one yours to edit. If such a skill is wrong or outdated, say so in your reply and recommend 'hermes curator adopt <name>' â€” do not try to patch it.
If the only skills that need updating are protected, say
'Nothing to save.' and stop.

Do NOT capturezÇ'Nothing to save.' is a real option but should NOT be the default. If the session ran smoothly with no corrections and produced no new technique, just say 'Nothing to save.' and stop. Otherwise, act.zBReview the conversation above and update two things:

**Memory**: u¥  **Skills**: how to do this class of task. Be ACTIVE â€” most sessions produce at least one skill update. A pass that does nothing is a missed learning opportunity, not a neutral outcome.

Target shape of the skill library: CLASS-LEVEL skills with a SKILL.md of always-on rules and a small `references/` set of topical depth â€” not narrow one-session skills, and not an umbrella hoarding a references/ file per session.

u›  Signals that warrant a skill update (any one is enough):
  â€¢ User corrected your style, tone, format, legibility, verbosity, or approach. Frustration is a FIRST-CLASS skill signal, not just a memory signal. 'stop doing X', 'don't format like this', 'I hate when you Y' â€” embed the lesson in the skill that governs that task so the next session starts fixed.
  â€¢ Non-trivial technique, fix, workaround, or debugging path emerged.
  â€¢ A skill that was loaded or consulted turned out wrong, missing, or outdated â€” patch it now.

Preference order for skills â€” pick the earliest that fits:
  1. UPDATE A CURRENTLY-LOADED SKILL. Check what skills were loaded via /skill-name or skill_view in the conversation. If one of them covers the learning, PATCH it first (re-load it with skill_view during this review â€” see Read-before-write below). It was in play; it's the right place â€” provided it is curator-managed. Protected and user-owned skills are off-limits however relevant; fall through when one of those is the best fit.
  2. UPDATE AN EXISTING UMBRELLA (skills_list + skill_view to find the right one). Patch it.
  3. ADD A SUPPORT FILE under an existing umbrella via skill_manage action=write_file. Three kinds: `references/<topic>.md` for topical depth (decision tables, recipes, quirks, condensed domain notes) â€” extend an existing topical file before creating one, never a per-session file; `templates/<name>.<ext>` for starter files meant to be copied and modified; `scripts/<name>.<ext>` for statically re-runnable actions (verification, fixture generators, probes). Add a one-line pointer in SKILL.md so future agents find them.
  4. CREATE A NEW CLASS-LEVEL UMBRELLA when nothing exists. Name at the class level â€” NOT a PR number, error string, codename, library-alone name, or 'fix-X / debug-Y' session artifact. If the name only fits today's task, fall back to (1), (2), or (3).

Read-before-write (ENFORCED â€” skill_manage refuses otherwise): before patching or editing an existing skill's SKILL.md, call skill_view(name) during this review; before overwriting or removing an EXISTING supporting file, call skill_view(name, file_path=...) for that exact file. Content quoted earlier in the transcript does NOT count â€” base the write on what skill_view just returned. New skills and NEW supporting files need no prior read. On a read-before-write refusal: view the named target once, retry the write once, do not loop.

User-preference embedding: when the user complains about how you handled a task, update the skill that governs that task rather than memory. Memory says 'who the user is and what the current situation and state of your operations are'; skills say 'how to do this class of task for this user'. A user-preference lesson lives in exactly ONE place: the skill that governs the task when one exists, USER.md only for cross-cutting preferences no skill owns â€” never both. Duplicating it is how a memory file ends up restating SKILL.md until both hit their size limits.

If you notice overlapping existing skills, mention it â€” the background curator handles consolidation.

Protected skills (DO NOT edit these):
  â€¢ Bundled skills (shipped with Hermes, e.g. 'hermes-agent').
  â€¢ Hub-installed skills (installed via 'hermes skills install').
  â€¢ Skills in skills.external_dirs (externally owned).
  â€¢ PINNED skills (marked via 'hermes curator pin'). Pin blocks autonomous writes entirely â€” content updates included â€” because no user is present to consent. Only a foreground session can change one.
  â€¢ USER-OWNED skills â€” anything not curator-managed (hand-written, URL-installed, or created by a foreground agent at the user's request). Your writes to these WILL be refused, including to skills loaded or consulted this session. If one is wrong, say so in your reply and recommend 'hermes curator adopt <name>' instead.
If the only skills that need updating are protected, say
'Nothing to save.' and stop.

Do NOT capture as skillsu¸   Act on whichever of the two dimensions has real signal. If genuinely nothing stands out on either, say 'Nothing to save.' and stop â€” but don't reach for that conclusion as a default.c                ó6   — | d | t        | «      |kD  rdz   S dz   S )Nu   â€¦r™   )rÛ   )rÈ   Úlimits     r   Ú_previewré   :  s&   € Ø��ˆ<¤C¨£I°Ò$5˜5Ñ>Ð>¸2Ñ>Ð>r    )u   âž•rÅ   éx   )u   âœ�ï¸�rÅ   rê   )u   âž–Úold_texté<   )ÚaddrÜ   ÚremovezDict[str, Tuple[str, str, int]]Ú_MEMORY_OP_FORMATSc                óž   — t         j                  |«      xs d\  }}}|r|j                  |«      xs dnd}|r| › d|› dt        ||«      › �S dS )zMVerbose line for one memory add/replace/remove, or None when no preview text.)Nr™   r   r™   rÆ   N)rï   ra   ré   )ÚlabelÚactionÚfieldsÚglyphÚ
field_namerè   rÈ   s          r   Ú_memory_op_linerö   D  s[   € ä1×5Ñ5°fÓ=ÒNÀÑ€Eˆ:�uÙ+0ˆ6�:‰:�jÓ!Ò'¡R°b€DÙ9=ˆeˆW�A�e�W˜Aœh t¨UÓ3Ð4Ð5ÐGÀ4ÐGr    c                óô  — |j                  dd«      }|j                  dd«      }| j                  d«      }t        |t        «      r|ni }|j                  dd«      xs |j                  dd«      }|j                  dd«      xs |j                  dd«      }|d	k(  r|s|rd
„ ||fD «       \  }	}
d|› d|	› d|
› d�S dddœj                  |«      }|r|j                  d«      rd|› d|› d|d   › �S |rd|› �S d|› �S )Nrò   r™   rP   Ú_changeÚoldÚ
old_stringÚnewÚ
new_stringÚpatchc              3  óT   K  — | ]   }t        |d «      j                  dd«      –— Œ" y­w)éP   rÒ   rÆ   N)ré   rÜ   ©rœ   Úts     r   rž   z&_verbose_skill_line.<locals>.<genexpr>T  s#   è ø€ Ò#iÈ1¤H¨Q°£O×$;Ñ$;¸DÀ#×$FÑ#iùs   ‚&(u   ðŸ“� Skill 'z' patched: "u   " â†’ "ú"ÚcreatedÚ	rewritten)ÚcreateÚeditÚdescriptionú' z: u   ðŸ“� zSkill )ra   r`   rb   )ÚdataÚdetailr»   rò   Ú
skill_nameÚ
change_rawÚchangerú   rü   Úold_previewÚnew_previewÚverbs               r   Ú_verbose_skill_liner  K  s  € Ø�Z‰Z˜ "Ó%€FØ—‘˜F BÓ'€Jà—‘˜)Ó$€JÜ!+¨J¼Ô!=‘:À2€FØ—‘˜E 2Ó&ÒF¨&¯*©*°\À2Ó*F€JØ—‘˜E 2Ó&ÒF¨&¯*©*°\À2Ó*F€JØ�Ò™j©JÙ#iÐQ[Ð]gÐPhÔ#iÑ ˆ�[Ø˜j˜\¨°{°mÀ9È[ÈMÐY[Ð\Ð\Ø¨Ñ5×9Ñ9¸&ÓA€DÙ�—
‘
˜=Ô)Ø˜j˜\¨¨D¨6°°F¸=Ñ4IÐ3JÐKÐKÙ 'ˆU�7�)ÐÐ>¨v°f°XÐ->Ð>r    c           
     óB  — |j                  d«      }t        |t        «      rP|rN|D �cg c]0  }t        |t        «      sŒt	        | |j                  dd«      |«      ‘Œ2 }}|D �cg c]  }|sŒ|‘Œ	 c}S t	        | |j                  dd«      |«      xs | › d�gS c c}w c c}w )NÚ
operationsrò   r™   ú updated)ra   r`   r¦   rb   rö   )rñ   r
  Úops_rawÚoprâ   Úlines         r   Ú_verbose_memory_linesr  \  s•   € à�j‰j˜Ó&€GÜ�'œ4Ô ¡WØLSÖlÀbÔWaÐbdÔfjÕWk” ¨¯©¨x¸Ó(<¸bÕAÐlˆÐlØ!&Ö/˜ª$’Ò/Ð/Ü˜E 6§:¡:¨h¸Ó#;¸VÓDÒZÈ5È'ÐQYÐHZÐ[Ð[ùò mùÚ/s   ¨B¾BÁ#BÁ+B))rò   r×   )rN   Úmemory)rÅ   r™   )rë   r™   )rP   r™   )rú   r™   )rü   r™   c                ó°  — ddh}t        «       }i }| xs g D �]  }t        |t        «      r|j                  d«      dk7  rŒ)|j                  dg «      xs g D ]Ñ  }t        |t        «      sŒ|j                  di «      xs i }|j                  dd«      }|j                  d	«      }|r|j	                  |«       ||vs|sŒg	 t        j                  |j                  d
d«      «      }	||	j                  d«      xs g dœt        D �
�ci c]  \  }
}|
|	j                  |
|«      “Œ c}}
¥||<   ŒÓ �Œ ||fS # t
        j                  t        f$ r i }	Y Œkw xY wc c}}
w )a  Map review-agent tool_call ids -> parsed call arguments for notify tools. Result JSON only
    says "Entry added"; the call arguments carry action, target and content previews. Restricting
    to notify tools keeps helper tools from surfacing as memory work just because they succeeded.r  Úskill_managerÐ   rÔ   rÕ   rÖ   rP   r™   ÚidÚ	argumentsú{}r  )rÑ   r  )
r&   r`   rb   ra   rí   ÚjsonÚloadsÚJSONDecodeErrorr=   Ú_CALL_DETAIL_DEFAULTS)Úreview_messagesÚnotify_toolsÚall_tool_call_idsÚcall_detailsÚmsgrã   ÚfnÚfn_nameÚtcidr–   Úkr„   s               r   Ú_collect_review_call_detailsr,  l  s`  € ð ˜nÐ-€LÜ ›UÐØ€LØÒ$ "ó ˆÜ˜#œtÔ$¨¯©°«¸;Ò(FØØ—'‘'˜,¨Ó+Ò1¨rò 	ˆBÜ˜b¤$Ô'ØØ—‘˜
 BÓ'Ò-¨2ˆBØ—f‘f˜V RÓ(ˆGØ—6‘6˜$“<ˆDÙØ!×%Ñ% dÔ+Ø˜lÑ*±$ØðÜ—z‘z "§&¡&¨°dÓ";Ó<�ð  ¨t¯x©x¸Ó/EÒ/KÈñ"ä=R×S©z¨q°'�1�d—h‘h˜q 'Ó*Ñ*ÓSð"ˆL˜Òò	ðð* ˜lÐ*Ð*øô ×(Ñ(¬)Ð4ò Ø’ðüó Ts   Â>%D1ÄEÄ1EÅEc                ó   — d„ | xs g D «       S )Nc              3  ól   K  — | ],  }t        |t        «      sŒ|j                  d «      dk(  sŒ)|–— Œ. y­w)rÐ   rÑ   NrÉ   )rœ   rÌ   s     r   rž   z!_tool_messages.<locals>.<genexpr>Œ  s)   è ø€ ÒY�!¬°A´tÕ)<ÀÇÁÀvÃÐRXÓAXŒAÑYùs   ‚4˜4­4r2   )Úmessagess    r   Ú_tool_messagesr0  ‹  s   € ÙY�x’~ 2ÔYÐYr    c                ó  — t        t        | «      «      }|D �ch c]  }|j                  d«      sŒ|d   ’Œ }}|D �ch c]9  }|j                  d«      rŒt        |j                  d«      t        «      sŒ5|d   ’Œ; }}||fS c c}w c c}w )zN``(tool_call_ids, contents)`` of tool messages already in the parent snapshot.Útool_call_idrÅ   )r¦   r0  ra   r`   rš   )Úprior_snapshotÚpriorsrÌ   ÚidsÚcontentss        r   Ú_prior_tool_keysr7  �  s€   € ä”. Ó0Ó1€FØ&,Ö
F °·±°nÕ0Eˆ1ˆ^ÓÐ
F€CÐ
FØ&,Öp °A·E±E¸.Õ4IÌjÐYZ×Y^ÑY^Ð_hÓYiÔknÕNo��)“Ðp€HÐpØ�ˆ=Ðùò GùÚps   ™B°B½BÁBÁ4Bc                ó   ‡— | j                  d«      r*| j                  d«      r| j                  d«      r| d   gS g S | j                  dd«      }| j                  dd«      xs |j                  dd«      }|j                  d«      dk(  }|rÝd| v rÙd	d
dddddœ}| j                  d«      }| j                  d«      rt        |t        «      sg S g }|D ]“  }	t        |	t        «      r|	j                  d«      durŒ'|j                  |	j                  d«      «      }
|
sŒJ|	j                  d«      sŒ\|	j                  d«      r	d|	d   › d�nd}|j	                  d|	d   › d|
› |› �«       Œ• |S |j                  «       Š|s!d	‰v sd‰v s|rt        ˆfd„dD «       «      r|gS |s|sg S |rdnddd œj                  ||«      }|r|rt        | ||«      gS t        ||«      S t        ˆfd!„d"D «       «      xs |xr d#‰v }|r|› d$�gS g S )%zVSummary line(s) for one successful notify-tool result (``[]`` when nothing to report).ÚstagedÚproposal_stagedr»   r™   rN   rÑ   r  Úresultsr  Úpatchedr  ÚwrittenÚremovedÚdeleted)r  rý   r  Ú
write_fileÚremove_fileÚdeleteÚoperations_appliedÚsuccessTrò   rP   Ú	file_pathz (ú)zSkill 'r  Úupdatedc              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­w©Nr2   )rœ   ÚwordÚlowers     €r   rž   z _action_lines.<locals>.<genexpr>³  s   øè ø€ Ò)f¸D¨$°%¬-Ñ)fùó   ƒ)r<  r?  r=  ÚSkillÚMemoryzUser profile)r  rÓ   c              3  ó&   •K  — | ]  }|‰v –— Œ
 y ­wrI  r2   )rœ   r+  rK  s     €r   rž   z _action_lines.<locals>.<genexpr>º  s   øè ø€ ÒN˜Qˆa�5ŒjÑNùrL  )ÚaddedÚreplacedr>  Úappliedrí   r  )	ra   r`   r¦   rb   rÝ   rK  Úanyr  r  )r	  r
  Úverboser»   rN   Úis_skillÚverbsr;  râ   Úresultr  Úpathrñ   ÚhitrK  s                 @r   Ú_action_linesrZ  —  s)  ø€ à‡x�x�Ôð %)§H¡HÐ->Ô$?ÀDÇHÁHÈYÔDW��Y‘Ð Ð_Ð]_Ð_Ø�h‰h�y "Ó%€GØ�X‰X�h Ó#Ò? v§z¡z°(¸BÓ'?€FØ�z‰z˜&Ó! ^Ñ3€HÙ�I Ñ%ð %¨yÀ+Ø(¸ÈiñYˆà—(‘(˜9Ó%ˆØ�x‰xÐ,Ô-´ZÀÌÔ5NØˆIØˆØò 	GˆFÜ˜f¤dÔ+¨v¯z©z¸)Ó/DÈDÑ/PØØ—9‘9˜VŸZ™Z¨Ó1Ó2ˆDÚ˜Ÿ
™
 6Õ*Ø6<·j±jÀÔ6M˜˜F ;Ñ/Ð0°Ñ2ÐSU�Ø—‘˜w v¨f¡~Ð&6°b¸¸¸t¸fÐEÕFð	Gð ˆØ�M‰M‹O€EÙ˜	 UÑ*¨i¸5Ñ.@Ù!¤cÓ)fÐDeÔ)fÔ&fØˆyÐÙ™FØˆ	Ù‰G°À.Ñ%Q×%UÑ%UÐV\Ð^dÓ%e€EÙÙ?GÔ# D¨&°'Ó:Ð;ÐqÔMbÐchÐjpÓMqÐqÜ
ÓNÐ"MÔNÓ
NÒ
mÐSYÒSlÐ^cÐglÐ^l€CÙ#&ˆuˆg�XÐÐÐ.¨BÐ.r    c           	     ój  — t        |xs d«      j                  «       }|dk(  rg S |dk(  }t        |«      \  }}t        | «      \  }}g }	t	        | «      D ]¾  }
|
j                  d«      }|r||v s|r,||vr(Œ!t        |
j                  d«      t         «      r|
d   |v rŒH	 t        j                  |
j                  dd«      «      }t        |t        «      r|j                  d«      sŒ�|	j                  t        ||j                  |«      xs i |«      «       ŒÀ |	S # t        j                  t        f$ r Y ŒÞw xY w)aÆ  Human-facing action summary for a background review pass: successful memory /
    skill-management tool results from the review agent's messages, skipping tool messages already
    present in ``prior_snapshot`` so inherited results are not re-surfaced as fresh work.
    ``notification_mode``: ``off`` -> no actions; ``on`` -> generic "Memory updated"/tool messages;
    ``verbose`` -> content previews from the tool-call arguments.

    See #14944.
    ÚonÚoffrT  r2  rÅ   r  rD  )rš   rK  r7  r,  r0  ra   r`   r  r   r!  r=   rb   ÚextendrZ  )r#  r3  Únotification_modeÚmoderT  Úexisting_tool_call_idsÚexisting_tool_contentsr%  r&  Úactionsr'  r*  r	  s                r   Ú#summarize_background_review_actionsrd  ¾  s<  € ô Ð Ò( DÓ)×/Ñ/Ó1€DØˆu‚}Øˆ	Ø�iÑ€GÜ5EÀnÓ5UÑ2ÐÐ2Ü&BÀ?Ó&SÑ#Ð�|Ø€GÜ˜oÓ.ò SˆØ�w‰w�~Ó&ˆÙØÐ-Ñ-Ñ2CÈÐT`ÑH`ØÜ˜Ÿ™ 	Ó*¬CÔ0°S¸±^ÐG]Ñ5]Øð	Ü—:‘:˜cŸg™g i°Ó6Ó7ˆDô
 ˜$¤Ô%¨T¯X©X°iÔ-@ØØ�‰”} T¨<×+;Ñ+;¸DÓ+AÒ+GÀRÈÓQÕRðSð  €Nøô ×$Ñ$¤iÐ0ò 	Ùð	ús   Â%DÄD2Ä1D2)Úwrite_originÚexecution_contextÚtask_idr2  c          	     óR  — |xs t        | dd«      |xs t        | dd«      | j                  xs d| j                  xs d| j                  xs  t        j
                  j                  dd«      d|xs d	|xs d	d
œ}|j                  «       D ��ci c]  \  }}|dvsŒ||“Œ c}}S c c}}w )z?Build provenance metadata for external memory-provider mirrors.Ú_memory_write_originÚassistant_toolÚ_memory_write_contextÚ
foregroundr™   ÚHERMES_SESSION_SOURCEÚclir  N)re  rf  Ú
session_idÚparent_session_idÚplatformÚ	tool_namerg  r2  >   Nr™   )r4   ro  Ú_parent_session_idrq  ÚosÚenvironra   Úitems)r5   re  rf  rg  r2  Úmetadatar+  Úvs           r   Úbuild_memory_write_metadatary  ã  s§   € ð %Ò`¬°Ð7MÐO_Ó(`Ø.Òg´'¸%ÐAXÐZfÓ2gØ×&Ñ&Ò,¨"Ø"×5Ñ5Ò;¸Ø—N‘NÒT¤b§j¡j§n¡nÐ5LÈeÓ&TØØ’?˜dØ$Ò,¨ñ	 €Hð &Ÿ^™^Ó-×E‘T�Q˜°¸*Ò1DˆAˆq‰DÓEÐEùÓEs   ÂB#ÂB#)Úinput_tokensÚoutput_tokensÚcache_read_tokensÚcache_write_tokensÚreasoning_tokensÚ	api_callsc                óÐ   — i dD �ci c]  }|t        | |d«      “Œ c}¥t        D �ci c]   }|t        t        | d|› �d«      xs d«      “Œ" c}¥dt        | dd«      i¥S c c}w c c}w )zASnapshot in-memory usage counters from a review fork (pre-close).)r“   r’   rŠ   NÚsession_r   Úestimated_cost_usdÚsession_estimated_cost_usd)r4   Ú_USAGE_COUNTERSrq   )r#   r�   s     r   Ú_snapshot_review_usager…  ú  s|   € ðØ<]Ö
^°Sˆ3”˜ c¨4Ó0Ñ0Ò
^ðäP_Ö
`Èˆ3””G˜L¨H°S°EÐ*:¸AÓ>ÒCÀ!ÓDÑDÒ
`ðð 	œg lÐ4PÐRVÓWñð ùÚ
^ùÚ
`s
   †A¦%A#c                ó  — 	 t        | dd«      }t        | dd«      }t        D �ci c]!  }|t        |j                  |«      xs d«      “Œ# }}|�|rt	        |j                  «       «      sy |j                  |fd|j                  d«      |j                  d«      |j                  d«      |j                  d	«      |j                  d
«      dœ|¤Ž yc c}w # t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)uH  Record a fork's usage against the parent session (best-effort, never raises). The fork has
    ``_session_db = None`` so conversation_loop's DB-gated accounting never sees its calls; route
    them through the aux-accounting chokepoint, which writes only ``session_model_usage`` â€” never
    the transcript or ``sessions`` row.Ú_session_dbNro  r   r_   r“   r’   rŠ   r‚  r  )re   r“   Úbilling_providerÚbilling_base_urlr‚  Úapi_call_countz8Background review usage recording failed (non-fatal): %s)r4   r„  rq   ra   rS  ÚvaluesÚrecord_auxiliary_usageÚpoprJ   rK   rL   )Úparent_agentÚusageÚ
session_dbro  r�   Úcountsr±   s          r   Ú_record_review_usage_to_parentr’    só   € ð
TÜ˜\¨=¸$Ó?ˆ
Ü˜\¨<¸Ó>ˆ
Ü;JÖK°C�#”s˜5Ÿ9™9 S›>Ò.¨QÓ/Ñ/ÐKˆÐKØÐ¡Z´s¸6¿=¹=»?Ô7KØØ)ˆ
×)Ñ)Øð	
Ø0¸¿	¹	À'Ó8JØ"ŸY™Y zÓ2ÀUÇYÁYÈzÓEZØ$Ÿy™yÐ)=Ó>Ø!Ÿ:™: kÓ2ñ		
ð 7=ó		
ùò Løô ò TÜ�‰ÐOÐQR×SÑSûðTús/   ‚"C ¤&CÁ
C Á*A%C ÃC Ã	C>ÃC9Ã9C>c                óB  — | xs g D �cg c]F  }t        |«      j                  «       j                  d«      j                  «       j                  «       ‘ŒH }}t	        d„ |D «       «      }t	        d„ |D «       «      }dj                  d„ d|fd|ffD «       «      xs dS c c}w )	uE  Map a review action summary to ``none`` / ``skill`` / ``memory`` / ``skill+memory``.
    Prefix-based on the formats :func:`summarize_background_review_actions` emits (``Skill â€¦``,
    ``ðŸ“� Skill â€¦``, ``Memory â€¦``, ``User profile â€¦``), so a free-text line like ``Skipped: no
    skill worth saving`` stays ``none``.u   ðŸ“�c              3  ó>   K  — | ]  }|j                  d «      –— Œ y­w)ÚskillN©Ú
startswithr   s     r   rž   z*_classify_review_result.<locals>.<genexpr>  s   è ø€ Ò:¨a�A—L‘L ×)Ñ:ùó   ‚c              3  ó>   K  — | ]  }|j                  d «      –— Œ y­w))r  zuser profileNr–  r   s     r   rž   z*_classify_review_result.<locals>.<genexpr>  s   è ø€ ÒNÀ!�Q—\‘\Ð"<×=ÑNùr˜  ú+c              3  ó,   K  — | ]  \  }}|sŒ	|–— Œ y ­wrI  r2   )rœ   ÚkindrY  s      r   rž   z*_classify_review_result.<locals>.<genexpr>   s   è ø€ Ò`™Y˜T 3Ò\_”DÑ`ùs   ‚
�r•  r  Únone)rš   ÚlstripÚremoveprefixrK  rS  rË   )rc  rò   ÚlowersÚ	has_skillÚ
has_memorys        r   Ú_classify_review_resultr£    s™   € ð
 X_ÒWdÐbdÖeÈVŒc�&‹k× Ñ Ó"×/Ñ/°Ó7×>Ñ>Ó@×FÑFÕHÐe€FÐeÜÑ:°6Ô:Ó:€IÜÑNÀvÔNÓN€JØ�8‰8Ñ`¨G°YÐ+?À(ÈJÐAWÐ*XÔ`Ó`ÒjÐdjÐjùò fs   ‰ABc                óH   ‡ — t        j                  dgˆ fd„dD «       ¢|‘­Ž  y)zHEmit a per-fork completion line so cost is visible where it is incurred.zZBackground review complete: thread=bg-review calls=%d in=%d out=%d cache_read=%d result=%sc              3  óZ   •K  — | ]"  }t        ‰j                  |«      xs d «      –— Œ$ y­w)r   N)rq   ra   )rœ   r+  r�  s     €r   rž   z)_log_review_completion.<locals>.<genexpr>(  s#   øè ø€ Ò	n QŒ#ˆe�i‰i˜‹lÒ˜a×
 Ñ	nùs   ƒ(+)r  rz  r{  r|  N)rK   Úinfo)r�  rW  s   ` r   Ú_log_review_completionr§  #  s/   ø€ ä
‡K�Kð	"ðó 
oÐ*mÔ	nðð 	ô	r    )Úproviders_allowedÚproviders_ignoredÚproviders_orderÚprovider_sortÚprovider_require_parametersÚprovider_data_collectionc           	     óæ   — t        | dd«      t        | dd«      dœt        D �ci c]  }t        | |d«      x}sŒ||“Œ c}¥}t        j                  t        | dd«      xs g «      x}r||d<   |S c c}w )ar  AIAgent kwargs that keep a SAME-model fork's request bytes identical to the parent's. Only
    for the un-routed path: on a different model the cache is cold anyway, and the parent's
    reasoning-effort vocabulary may be invalid for the routed provider (OpenRouter forwards
    ``reasoning.effort`` unclamped; codex_responses passes ``max``/``ultra`` through unmapped).Úreasoning_configNÚephemeral_system_prompt)r¯  r°  Úprefill_messages)r4   Ú_PROVIDER_PIN_ATTRSÚcopyÚdeepcopy)r5   r6   ÚvalÚkwargsÚparent_prefills        r   Ú_same_model_parity_kwargsr¸  6  sŽ   € ô $ EÐ+=¸tÓDÜ#*¨5Ð2KÈTÓ#Rñ	ô
 "5Ö
\˜ÄÈÈtÐUYÓ@ZÐ9Z¸Ñ9Zˆ4�‰9Ò
\ð€Fô Ÿ™¤w¨uÐ6HÈ$Ó'OÒ'UÐSUÓVÐV€~ÐVØ%3ˆÐ!Ñ"Ø€Mùò ]s
   £A.¸A.c                ó\  — t        t        |«      j                  d«      xs d«      j                  «       }|rt	        | dd«      ryd| _        d|› d�}t	        | d	d«      }t        |«      r t        t        «      5   ||«       ddd«       t        j                  d
|«       y# 1 sw Y   Œ xY w)a	  One-shot user-visible notice: ``auxiliary.background_review.reasoning_effort`` is IGNORED on
    the same-model path (#104116). The fork inherits the parent's ``reasoning_config`` verbatim so
    its request bytes keep the parent's prompt-cache prefix (#30532: a diverged ``thinking`` field
    on the fork-birth request re-created a large share of the cache); the no-op used to be silent,
    so a user who set the key saw no feedback at all. Gated on the parent so a nudge-per-turn
    session warns once, not per fork.Úreasoning_effortr™   Ú"_warned_bg_review_reasoning_effortFNTu2   âš  auxiliary.background_review.reasoning_effort='a,  ' has no effect while the review runs on the main model: the fork inherits the conversation's reasoning effort to keep the parent's prompt-cache prefix (see memory docs, same-model review reasoning). Route the review elsewhere via auxiliary.background_review.provider/model to use a different effort.r·   rµ   )rš   rl   ra   r›   r4   r»  r¹   r   rJ   rK   rY   )r5   rk   Úeffortr»   r¼   s        r   Ú_warn_ignored_reasoning_effortr½  J  s«   € ô Ô/°Ó9×=Ñ=Ð>PÓQÒWÐUWÓX×^Ñ^Ó`€FÙ”W˜UÐ$HÈ%ÔPØØ/3€EÔ,à
<¸V¸Hð Eeð 	eð ô �5˜/¨4Ó0€DÜ�„~Ü”iÓ ñ 	Ù�ŒM÷	ä
‡N�N�4˜Õ!÷	ð 	ús   Á:	B"Â"B+c                óä   — t        t        | dd«      dd«      }d}t        |«      r	  |dd¬«       d}d| _        || _        |rd| _        yy# t        $ r t        j	                  dd¬	«       Y Œ;w xY w)
ui  Detached in-memory compaction for a fork sharing the parent's session_id. Disabling
    compression (the old guard against compacting the parent's live session) removed the only
    bound on the review's snapshot. Persistence is already off, so compaction can only rewrite the
    fork's transcript â€” but the compressor's own SessionDB/session_id binding must be severed too,
    or cooldown/streak counters land on the parent's row. Force in-place mode and re-enable
    compression ONLY after the rebind succeeded (fail-closed); gates stay deferred until the first
    response so request #1 is a warm cache read.rn   NÚbind_session_stateFr™   )r�  ro  Tz…background-review compressor detachment failed; keeping compression DISABLED on this review fork (fail-closed, issue #93057 / #38727)rG   )r4   r¹   rJ   rK   rY   Úcompression_in_placeÚcompression_enabledÚ._review_defer_compaction_before_first_response)r#   ÚbindÚdetacheds      r   Ú_detach_fork_compressionrÅ  b  s�   € ô ”7˜<Ð)=¸tÓDÐFZÐ\`Óa€DØ€HÜ�„~ð	ñ ˜D¨RÕ0ØˆHð )-€LÔ%Ø'/€LÔ$ÙØFJˆÕCð øô ò 	ô �N‰Nð7ð ð	 ö ð	ús   §A Á A/Á.A/c                óº   — t        | «      j                  d«      }|�|dk(  ryddlm}m}  ||«      }|€&t
        j                  d|dj                  |«      «       |S )aç  ``reasoning_config`` for a ROUTED fork from ``auxiliary.background_review.reasoning_effort``
    (#94825). The routed branch never inherits the parent's effort (its vocabulary may be invalid for
    the routed provider), but an explicit per-task pin is the user's choice for THAT model and must
    win over provider defaults, as every other aux task already does via ``_get_task_extra_body``.
    None = unset (provider default); an unknown level warns and falls through to the default.rº  Nr™   r   )ÚVALID_REASONING_EFFORTSÚparse_reasoning_effortuw   auxiliary.background_review.reasoning_effort %r is not a valid level (none, %s) â€” using the routed provider's defaultrØ   )rl   ra   Úhermes_constantsrÇ  rÈ  rK   rY   rË   )rk   r¼  rÇ  rÈ  Úparseds        r   Ú_routed_reasoning_configrË  �  sa   € ô ,¨HÓ5×9Ñ9Ð:LÓM€FØ€~˜ 2šØßPÙ# FÓ+€FØ€~Ü�‰ð,Ø-3°T·Y±YÐ?VÓ5Wô	
ð €Mr    c                óæ  — |j                  d«      xs | j                  |d| j                  |j                  d«      xs | j                  |j                  d«      |j                  d«      xs d|j                  d«      xs d|j                  d«      |j                  d	«      xs i | j                  t        | d
d«      t        | dd«      ddœ}t        |j                  d«      t        «      r|d   |d<   t        |j                  d«      t        «      r.|d   r)|j                  |d   |j                  d«      xs g ¬«       |s|j                  t        | «      «       |S t        |«      x}�||d<   |S )a˜  AIAgent constructor kwargs for the review fork. skip_memory=True: an external memory plugin
    scoped to the parent's session_id would leak the harness prompt into the user's real memory
    namespace; built-in MEMORY.md/USER.md state is re-bound by the caller. Toolsets match the
    parent so ``tools[]`` is byte-identical (Anthropic's cache key includes it); the runtime
    whitelist restricts dispatch.r“   Tr’   rˆ   rŠ   Nr‰   r”   rŽ   Úenabled_toolsetsÚdisabled_toolsets)r“   Úmax_iterationsÚ
quiet_moderq  r’   rˆ   rŠ   r‰   r”   rŽ   rp  rÍ  rÎ  Úskip_memoryr�   r•   r–   )r�   r‘   r¯  )ra   r“   rq  r’   ro  r4   r`   rq   rš   Úupdater¸  rË  )r5   Úrtr—   rÏ  rk   r¶  Ú
routed_cfgs          r   Ú_fork_init_kwargsrÕ  ”  sL  € ð —‘˜“Ò/ E§K¡KÀ>ÐaeØ—N‘N°·±°zÓ0BÒ0TÀeÇnÁnØ—F‘F˜:Ó&°B·F±F¸:Ó4FÒ4NÈ$Ø—6‘6˜)Ó$Ò,¨ÀÇÁÐHYÓAZØŸV™VÐ$7Ó8Ò>¸BÐUZ×UeÑUeÜ# EÐ+=¸tÓDÜ$ UÐ,?ÀÓFÐW[ñ€Fô �"—&‘&˜Ó&¬Ô,Ø! ,Ñ/ˆˆ|ÑÜ�"—&‘&˜Ó#¤SÔ)¨b°ªmØ�‰ " Y¡-¸"¿&¹&À».Ò:NÈBˆÔOÙØ�‰Ô/°Ó6Ô7ð €Mô 1°Ó:Ð
:ˆ*Ð	GØ%/ˆÐ!Ñ"Ø€Mr    iÿÿÿc                óÀ   — t        j                  t        |dd«      xs g «      | _        | j                  D �ch c]
  }|d   d   ’Œ c}| _        t
        | _        yc c}w )u  Same-model fork: advertise the parent's exact tools[] (its last outbound payload â€” an
    empty list included) so the request prefix matches byte-for-byte, then freeze the snapshot
    generation. Dispatch stays behind the review whitelist; advertising is not permission.ÚtoolsNrÖ   rP   )r³  r´  r4   r×  Úvalid_tool_namesÚ _FROZEN_TOOL_SNAPSHOT_GENERATIONÚ_tool_snapshot_generation)r#   r5   rÑ   s      r   Ú_inherit_parent_tool_surfacerÛ  µ  sT   € ô
 Ÿ™¤w¨u°g¸tÓ'DÒ'JÈÓK€LÔØJV×J\ÑJ\Ö$]À$ T¨*Ñ%5°fÓ%=Ò$]€LÔ!Ü-M€LÕ*ùò %^s   ¸Ar_   )re  c               ó¤  — ddl m} t        | |«      }t        |j	                  d«      «      }|s|dk(  rt        | |«        |di t        | ||||«      ¤Ž}|x|_        |_        | j                  |_	        | j                  |_
        | j                  |_        dx|_        |_        dx|_        x|_        |_        d|_        d|_        | j&                  |_        |sW| j(                  |_        | j*                  |_        t-        | «      }|r||_        | j1                  «       |_        t5        || «       t7        |«       t9        ||«      |_        |||fS )	uL  Construct a detached AIAgent fork with warm prompt-cache parity (shared with ``/btw``): same
    runtime/credentials as the parent, byte-identical system prompt / tools[] / reasoning config on
    the same-model path, shared session_id for prefix warmth, full persistence detachment (no
    state.db writes, rotation, or external memory providers; in-place-only compaction). Returns
    ``(fork_agent, runtime_dict, routed)``; ``routed`` means a different model (cache cold â€”
    replay a digest). The caller owns registration, whitelisting, running, usage attribution and
    teardown.r   )ÚAIAgentr—   r_   TFNr2   )Ú	run_agentrÝ  r²   r-   ra   r½  rÕ  ri  rk  Ú_memory_storeÚ_memory_enabledÚ_user_profile_enabledÚ_memory_nudge_intervalÚ_skill_nudge_intervalÚ_skip_mcp_refreshÚ_persist_disabledÚsuppress_status_outputÚ_end_session_on_closer‡  ro  Ú_cached_system_promptÚsession_startr   Ú_inherited_cache_scopeÚ_conversation_root_idÚ_cached_conversation_rootrÛ  rÅ  r~   )	r5   rk   rÏ  re  rÝ  Ú_rtÚ_routedr#   Úinherited_scopes	            r   Úbuild_cache_parity_forkrð  À  s\  € õ "ô " %¨Ó
2€CÜ�3—7‘7˜8Ó$Ó%€Gñ �|Ð':Ò:Ü& u¨hÔ7ÙÑ^Ô.¨u°c¸7ÀNÐT\Ó]Ñ^€LØMYÐY€LÔ%¨Ô(JØ!&×!4Ñ!4€LÔØ#(×#8Ñ#8€LÔ Ø).×)DÑ)D€LÔ&ØOPÐP€LÔ'¨,Ô*Lð mqÐp€LÔ"Ðp \Ô%CÀlÔFiØ).€LÔ&Ø#€LÔØ#×.Ñ.€LÔñ Ø-2×-HÑ-HˆÔ*Ø%*×%8Ñ%8ˆÔ"ô :¸%Ó@ˆÙØ2AˆLÔ/ð 27×1LÑ1LÓ1NˆÔ.Ü$ \°5Ô9Ü˜\Ô*ô /IÈÐS_Ó.`€LÔ+Ø˜˜gÐ%Ð%r    c                ó2   — t         j                  d| |«       y)z‹Non-interactive approval: dangerous-command guards resolve to "deny" instead of input(),
    which would deadlock against the parent's TUI.z8Background review auto-denied dangerous command: %s (%s)Údeny)rK   rY   )r•   r  r¶  s      r   Ú_bg_review_auto_denyró    s   € ô ‡N�NÐMÈwÐXcÔdØr    c                óh   — ddl m} t        t        «      5   || «       d d d «       y # 1 sw Y   y xY w)Nr   )Úset_approval_callback)Útools.terminal_toolrõ  r   rJ   )Úcallbackrõ  s     r   Ú_set_thread_approval_callbackrø    s*   € Ý9ä	”)Ó	ñ (Ù˜hÔ'÷(÷ (ñ (ús   –	(¨1c               óÂ  — |€yt        | d«      r4t        | d«      5  |r|| _        n| j                  |u rd| _        ddd«       t        | d«      rlt        | d«      5  |r| j                  j	                  |«       n8t        t        t        «      5  | j                  j                  |«       ddd«       ddd«       yy# 1 sw Y   Œ‚xY w# 1 sw Y   ŒxY w# 1 sw Y   yxY w)up  Add (``register=True``) or remove the fork on the PARENT's tracking slots:
    ``_background_review_agent`` (direct pointer the next live turn interrupts) and
    ``_active_children`` (interrupt() fan-out). Removal is identity-scoped and idempotent; both
    are best-effort for direct test stubs â€” the prepared run token is the live-turn cancellation
    authority.NrU   r:   Ú_active_childrenÚ_active_children_lock)	Úhasattrr8   rU   rú  rÝ   r   r|   r<   rî   )r5   r#   Úregisters      r   Ú_track_review_forkrþ    sã   € ð ÐØÜˆuÐ0Ô1Ü˜EÐ#<Ó=ñ 	6ÙØ1=�Õ.Ø×/Ñ/°<Ñ?Ø15�Ô.÷		6ô
 ˆuÐ(Ô)Ü˜EÐ#:Ó;ñ 	@ÙØ×&Ñ&×-Ñ-¨lÕ;äœj¬.Ó9ñ @Ø×*Ñ*×1Ñ1°,Ô?÷@÷		@ð 	@ð *÷	6ð 	6ú÷@ð @ú÷		@ð 	@ús/   œ B=Á3CÂC	Â+CÂ=CÃ	C	ÃCÃCc                óø  — ddl m} | j                  xs | j                  }|r|rddgndg} ||d¬«      D �ch c]
  }|d   d   ’Œ }}|d	d
hz  }t	        «       }	 t        |«      j                  dg «      }	t        |	t        «      r?|	D �
ch c]4  }
t        |
t        «      sŒ|
j                  «       sŒ%|
j                  «       ’Œ6 }}
||z  |fS c c}w c c}
w # t        $ r t        j                  dd¬«       Y Œ3w xY w)u±   ``(whitelist, configured_extra_tools)`` for the review fork â€” DISPATCH-side only, so the
    advertised ``tools[]`` stays byte-identical to the parent's (prompt-cache parity).r   )Úget_tool_definitionsr  ÚskillsT)rÍ  rÐ  rÖ   rP   Ú	read_fileÚsearch_filesÚextra_toolsz*background_review extra_tools parse failedrG   )Úmodel_toolsr   rà  rá  r&   rl   ra   r`   r¦   rš   r›   rJ   rK   rL   )r#   rk   Úreview_memoryr   Ú	memory_onÚreview_toolsetsr  Ú	whitelistÚconfigured_extra_toolsÚ	extra_rawrP   s              r   Ú_review_tool_whitelistr  2  s  € õ
 1ð ×,Ñ,ÒR°×0RÑ0R€IÙ.7¹M�x Ñ*ÐPXÈz€OÙ0DÐVeÐrvÔ0wÖx¨1��:‘˜vÓ&Ðx€IÐxð �+˜~Ð.Ñ.€Iô$ #&£%ÐðRÜ2°8Ó<×@Ñ@ÀÐPRÓSˆ	Ü�i¤Ô&Ø?HÖ%s°tÌJÐW[Ô]`ÕLaÐfj×fpÑfpÕfr d§j¡j¥lÐ%sÐ"Ð%sð Ð-Ñ-Ð/EÐEÐEùò; yùò4 &tøÜò RÜ�‰ÐAÈDˆÖQðRús5   ¸CÁ/C Â
CÂ CÂ1CÃC ÃC Ã C9Ã8C9c                  óX   — e Zd ZU dZdZded<    ee¬«      Zded<    ee	¬«      Z
ded	<   y)
Ú_ReviewForkStatezSMutable hand-off between the fork phase and the outer worker's error/cleanup paths.Nr   r#   )Údefault_factoryú
List[Dict]r#  úDict[str, Any]Úreview_usage)r.   r/   r0   r1   r#   Ú__annotations__r   r¦   r#  rb   r  r2   r    r   r  r  ]  s-   … á]à€L�#ÓÙ"'¸Ô"=€O�ZÓ=Ù#(¸Ô#>€L�.Ô>r    r  c                ól   — t        t        «      5  | j                  «        ddd«       y# 1 sw Y   yxY w)z±The fork shares the foreground session ID: close() / shutdown_memory_provider() are
    session-bound (close() kills that session's terminal processes), so release only clients.N)r   rJ   Úrelease_clients)r#   s    r   Ú_release_fork_clientsr  f  s-   € ô 
”)Ó	ñ 'Ø×$Ñ$Ô&÷'÷ 'ñ 'ús   �*ª3c                óü  — t        | |t        ¬«      \  |_        }}	||j                  _        t	        | |j                  d¬«       ddlm}
m} t        |j                  ||«      \  }}dj                  t        |«      «      }|rd|› d�nd	}|rd
|› d�nd	}d|v rdnd	}d|v rdnd} |
|d|z   dz   |z   dz   ¬«       t        t        «      5  ddlm}  |«        ddd«       	 |�|j                  |j                  «      r6|j                  j!                  |dz   |z   dz   |z   |	rt#        |«      n|¬«        |«        |j                  �D|j$                  j'                  t)        |j                  «      «       t+        | |j$                  «       t	        | |j                  d¬«       t-        | |«       t/        t1        |j                  dg «      «      |_        t5        |j                  «       d|_        y# 1 sw Y   �ŒxY w#  |«        |j                  �D|j$                  j'                  t)        |j                  «      «       t+        | |j$                  «       t	        | |j                  d¬«       t-        | |«       w xY w)aÝ  Fork phase (inside thread-scoped silence): build the fork, run the prompt under the tool
    whitelist, snapshot its messages/usage, release its clients. Partial progress lands on ``st``
    so the caller's error path still sees usage and the fork to clean up. ``explicit`` (/refine)
    keeps the ``background_review`` origin (curator/skill guards still apply) but marks the fork
    attended, so the unattended-only memory delete gate leaves the full operation set available.)rÏ  T©rý  r   )Úset_thread_tool_whitelistÚclear_thread_tool_whitelistrØ   z& Configured extra tools also allowed: ú.r™   u8    Exception â€” these configured tools are also allowed: r  z  and memory for notes (add only)zmemory and skill zskill z²Background review denied non-whitelisted tool: {tool_name}. Allowed here: skill_view/skills_list/read_file/search_files to read, skill_manage(action='patch'|...) to change skillsz Do not retry {tool_name}.)Údeny_msg_fmt)Ú#_reset_background_review_read_marksNz

You can only call uP   management tools. Other tools will be denied at runtime â€” do not attempt them.)Úuser_messageÚconversation_historyFÚ_session_messages)rð  Ú_REVIEW_MAX_ITERATIONSr#   Ú_review_attendedrþ  Úhermes_cli.pluginsr  r  r  rË   Úsortedr   rJ   Útools.skill_manager_guardsr  r$   Úrun_conversationræ   r  rÒ  r…  r’  rB   r¦   r4   r#  r  )r5   rÞ   Úpromptrk   Ú
review_runÚstr  Úexplicitrí  rî  r  r  Úreview_whitelistr
  Ú
extra_listÚ
deny_extraÚprompt_extraÚmemory_phrase_denyÚmemory_phrase_promptr  s                       r   Ú_run_review_forkr1  m  sl  € ô %<ØˆxÔ(>ô%@Ñ!€B„O�S˜'à'/€B‡O�OÔ$Ü�u˜bŸo™o¸Õ=ßYÜ/EÀbÇoÁoÐW_ÐanÓ/oÑ,ÐÐ,Ø—‘œ6Ð"8Ó9Ó:€JÙKaÐ9¸*¸ÀQÑGÐgi€JÙ_uÐMÈjÈ\ÐYZÑ[Ð{}€Lð @HÐK[Ñ?[Ñ;ÐacÐØ2:Ð>NÑ2NÑ.ÐT\ÐÙØð@ð !ñ!ð $'ñ'ð *4ñ4ð 7SñSõô 
”)Ó	ñ .ÝRá+Ô-÷.ð8ØÐ ×!9Ñ!9¸"¿/¹/Ô!Jà�O‰O×,Ñ,àÐ5Ñ5Ð8LÑLð:ñ:à<HñIñ LS¤_Ð5FÔ%GÐXið -ô ñ 	$Ô%ð �?‰?Ð&Ø�O‰O×"Ñ"Ô#9¸"¿/¹/Ó#JÔKÜ*¨5°"·/±/ÔBô 	˜5 "§/¡/¸EÕBÜ$ U¨JÔ7Üœg b§o¡oÐ7JÈBÓOÓP€BÔÜ˜"Ÿ/™/Ô*Ø€B…O÷;.ñ .ûñ  	$Ô%ð �?‰?Ð&Ø�O‰O×"Ñ"Ô#9¸"¿/¹/Ó#JÔKÜ*¨5°"·/±/ÔBô 	˜5 "§/¡/¸EÕBÜ$ U¨JÕ7ús   Ã
G1Ã!AG> Ç1G;Ç>A=I;c                óþ   — dj                  t        j                  |«      «      }| j                  d|› �«       | j                  r-t        t        «      5  | j	                  d|› �«       d d d «       y y # 1 sw Y   y xY w)Nu    Â· u      ðŸ’¾ Self-improvement review: u   ðŸ’¾ Self-improvement review: )rË   rb   ÚfromkeysÚ_safe_printÚbackground_review_callbackr   rJ   )r5   rc  Úsummarys      r   Ú_publish_review_summaryr7  ­  sz   € Ø�k‰kœ$Ÿ-™-¨Ó0Ó1€GØ	×ÑÐ8¸¸	ÐBÔCØ×'Ò'Ü”iÓ ñ 	YØ×,Ñ,Ð/MÈgÈYÐ-WÔX÷	Yð 	Yð (÷	Yð 	Yús   ÁA3Á3A<c                ó¬  — |�'|j                   j                  «       rt        | |«       yt        t        «       t        | «      sHt        | |«      j                  d«      s-t        j                  dt        | dd«      «       t        d«       yt        «       }	 t        «       5  t        | |||||||«       ddd«       	 t        |j                  |t        | dd«      ¬«      }t#        |j$                  t'        |«      «       |rt)        | |«       t-        | |j.                  d¬«       t        | |«       |j.                  �@t1        t         «      5  t        «       5  t3        |j.                  «       ddd«       ddd«       t        d«       y# 1 sw Y   ŒÖxY w# t         $ r"}	t        j                  d	|	«       g }Y d}	~	ŒÛd}	~	ww xY w# t         $ rU}	t        j                  d
|	«       |j$                  rt#        |j$                  d«       | j+                  d|	«       Y d}	~	�Œd}	~	ww xY w# 1 sw Y   Œ¸xY w# 1 sw Y   Œ¼xY w# t-        | |j.                  d¬«       t        | |«       |j.                  �Zt1        t         «      5  t        «       5  t3        |j.                  «       ddd«       n# 1 sw Y   nxY wddd«       n# 1 sw Y   nxY wt        d«       w xY w)aC  Daemon-thread worker: build the fork, run the prompt, surface the action summary via
    ``agent._safe_print`` / ``background_review_callback``. ``review_run`` (from
    :func:`prepare_background_review_run`) cancelled before the first provider call aborts
    without entering ``run_conversation()``.

    See #84423.
    Nr—   zÔBackground review skipped: provider %r cannot emit Hermes tool calls, so the review fork could not write memories or skills. Set auxiliary.background_review.{provider,model} to route the review to a normal model.r’   r×   Úmemory_notificationsr\  )r_  z³summarize_background_review_actions returned partial results after exception (treating as empty); suppressing AttributeError that previously aborted the entire review (#59437): %sz)Background memory/skill review failed: %srº   zbackground reviewFr  )r   r"   rB   rø  ró  rÃ   r²   ra   rK   rY   r4   r  r   r1  rd  r#  rJ   r§  r  r£  r7  Ú_emit_auxiliary_failurerþ  r#   r   r  )
r5   rÞ   r'  rk   r(  r  r*  r)  rc  r±   s
             r   Ú_run_review_in_threadr;  µ  sv  € ð Ð *×"=Ñ"=×"DÑ"DÔ"FÜ$ U¨JÔ7ØÜ!Ô"6Ô7ô ' uÔ-Ô6MÈeÐU]Ó6^×6bÑ6bÐckÔ6lÜ�‰ðbô �E˜: sÓ+ô		
ô 	& dÔ+ØÜ	Ó	€Bð4,ô #Ó$ñ 	rÜ˜UÐ$5°v¸xÈÐUWÐYfÐhpÔq÷	rð	ô :Ø×"Ñ"Ð$5Ü")¨%Ð1GÈÓ"NôˆGô 	˜rŸ™Ô0GÈÓ0PÔQÙÜ# E¨7Ô3ô 	˜5 "§/¡/¸EÕBÜ$ U¨JÔ7Ø�?‰?Ð&Üœ)Ó$ñ 7Ô&;Ó&=ñ 7Ü% b§o¡oÔ6÷7÷ 7ô 	& dÕ+÷Y	rð 	rûô$ ò 	Ü�N‰NðIð ô	ð �Gûð	ûô ò >Ü�‰ÐBÀAÔFØ�?Š?Ü" 2§?¡?°GÔ<Ø×%Ñ%Ð&9¸1×=Ò=ûð	>ú÷7ð 7ú÷ 7ð 7ûô 	˜5 "§/¡/¸EÕBÜ$ U¨JÔ7Ø�?‰?Ð&Üœ)Ó$ñ 7Ô&;Ó&=ñ 7Ü% b§o¡oÔ6÷7÷ 7ñ 7ú÷ 7÷ 7ñ 7úô 	& dÕ+úsÂ   Â
G Â!F	Â4G Â=#F Ã -G ÅH0ÅH$Å-H0Æ	FÆG Æ	G ÆF;Æ6G Æ;G Ç G Ç	H!ÇA
HÈH< ÈH!È!H< È$H-	È)H0È0H9È<A KÉ<J;ÊJ&	Ê	J;Ê&J/Ê+J;Ê2	KÊ;KË KÚ_COMBINED_REVIEW_PROMPTÚ_MEMORY_REVIEW_PROMPTÚ_SKILL_REVIEW_PROMPT))TT)TF)FT©FFc                óÈ   ‡ ‡‡‡‡‡‡
— ‰€
t        «       Št        ‰|f   }t        ‰ |t        «       |   «      Š
|xs dj	                  «       x}r‰
› d|› �Š
dˆ ˆˆˆ
ˆˆˆfd„}	|	‰
fS )aé  Return ``(target, prompt)``; the caller builds the ``threading.Thread`` so test patches of
    ``run_agent.threading.Thread`` keep working. ``focus`` (``/refine [instructions]``) is appended
    to the chosen prompt; automatic reviews pass ``None``. ``task_cfg`` is the pre-loaded
    ``auxiliary.background_review`` block; when omitted it is read once here. ``explicit``
    (/refine) propagates to the fork's write origin so user-requested reviews keep the full
    memory operation set.r™   u|   

The user explicitly requested this review with the following focus â€” prioritize it over the general instructions above:
c            	     ó*   •— t        ‰ ‰‰‰‰‰‰¬«       y )N)rk   r(  r  r*  )r;  )r5   r*  rÞ   r'  r  r(  rk   s   €€€€€€€r   Ú_targetz/spawn_background_review_thread.<locals>._target'  s   ø€ ÜØÐ$ f°xÈJØ'°(ö	<r    r*   )rl   Ú_PROMPT_NAME_BY_SCOPEr4   Úglobalsr›   )r5   rÞ   r  Úreview_skillsÚfocusrk   r(  r*  rP   rB  r'  s   ```  ```  @r   Úspawn_background_review_threadrG    s   þ€ ð ÐÜ1Ó3ˆä  -°Ð!?Ñ@€DÜ�U˜D¤'£)¨D¡/Ó2€FØ’˜"×#Ñ#Ó%Ð%€uÐ%àˆhð MØMRÈGðUð 	÷
<ó <ð
 �Fˆ?Ðr    )r=  r>  r<  r†   rG  rd  ry  )ÚPathc                ó°   — | � 	 ddl m}  || j                  d«      d¬«      S t        «       \  }}|S # t        $ r t        j                  dd¬«       Y yw xY w)uØ  Return whether automatic post-turn background review may spawn.

    Controlled by ``auxiliary.background_review.enabled`` (default ``true``).
    Explicit ``/refine`` (``focus`` set) bypasses this gate â€” same contract as
    zeroing the nudge intervals, which stops automatic forks but leaves manual
    refine working (issue #87250).

    Prefer :func:`load_background_review_settings` at the spawn call site so
    the task block is not re-read on the same turn.
    r   r€   r‚   Trƒ   z[Failed to interpret background_review.enabled; leaving automatic review enabled (fail-open)rG   )r…   r�   ra   rJ   rK   rY   r†   )rk   r�   r‚   Ú_s       r   Úis_background_review_enabledrK  ;  sj   € ð Ðð
	Ý-á" 8§<¡<°	Ó#:ÀDÔIÐIô 1Ó2�J€GˆQØ€Nøô ò 	Ü�N‰Nð7àð ô ñ
 ð	ús   „2 ² AÁA)r5   r   r6   rš   r+   zIterator[None])r5   r   r+   úOptional[_BackgroundReviewRun])r5   r   r>   rL  r+   r,   )r#   r   r+   r,   )r5   r   r+   r,   )rc   r   r+   r  rI  )rk   úOptional[Dict[str, Any]]r+   r  )r#   r   r+   rq   )NN)rk   rM  r#   r   r+   zOptional[int])r+   ztuple[bool, Dict[str, Any]])r5   r   rk   rM  r+   r  )
r5   r   r¬   rš   r­   rš   rº   rJ   r+   r,   )r5   r   r+   r-   )rÌ   r	   r+   rš   )é   )rÞ   r  rß   rq   r+   r  )rÈ   rš   rè   rq   r+   rš   )rñ   rš   rò   rš   ró   zDict[str, str]r+   úOptional[str])r	  r	   r
  r	   r»   rš   r+   rš   )rñ   rš   r
  r	   r+   ú	List[str])r#  r  r+   zTuple[set, dict])r/  r  r+   zIterator[Dict])r3  r  r+   úTuple[set, set])r	  r	   r
  r	   rT  r-   r+   rP  )r\  )r#  r  r3  r  r_  rš   r+   rP  )r5   r   re  rO  rf  rO  rg  rO  r2  rO  r+   r  )r#   r   r+   r  )rŽ  r   r�  r  r+   r,   )rc  rP  r+   rš   )r�  r  rW  rš   r+   r,   )r5   r   r+   r  )r5   r   rk   rM  r+   r,   )rk   rM  r+   rM  )r5   r   rÓ  r  r—   r-   rÏ  rq   rk   rM  r+   r  )r#   r   r5   r   r+   r,   )
r5   r   rk   rM  rÏ  rq   re  rš   r+   z Tuple[Any, Dict[str, Any], bool])r÷  r   r+   r,   )r5   r   r#   r   rý  r-   r+   r,   )F)r#   r   rk   rM  r  r-   r+   rQ  r?  )r5   r   rÞ   r  r'  rš   rk   rM  r(  rL  r)  r  r  r-   r*  r-   r+   r,   )r5   r   rc  rP  r+   r,   )NNFF)r5   r   rÞ   r  r'  rš   rk   rM  r(  rL  r  r-   r*  r-   r+   r,   )FFNNNF)r5   r   rÞ   r  r  r-   rE  r-   rF  rO  rk   rM  r(  rL  r*  r-   )rk   rM  r+   r-   )ar1   Ú
__future__r   r³  r  Úloggingrt  r   Ú
contextlibr   r   Údataclassesr   r   Útypingr   r	   r
   r   r   r   Úagent.prompt_cache_scoper   Úagent.thread_scoped_outputr   Ú	getLoggerr.   rK   rX   r   r8   r@   rB   rS   r[   r!  rt   rv   rr   rf   rl   rx   r~   r†   r²   r¨   rÃ   rÎ   ræ   Ú_MEMORY_ROUTING_BLOCKr=  Ú_LESSON_LAYER_BLOCKÚ_DO_NOT_CAPTURE_BLOCKr>  r<  ré   rï   r  rö   r  r  r"  r,  r0  r7  rZ  rd  ry  r„  r…  r’  r£  r§  r²  r¸  r½  rÅ  rË  rÕ  rÙ  rÛ  rð  ró  rø  rþ  r  r  r  r1  r7  r;  rC  rG  Ú__all__ÚpathlibrH  rK  r2   r    r   ú<module>r_     sÙ  ðòTõ #ã Û Û Û 	Û ß /ß (ß =× =å DÝ <à	ˆ×	Ñ	˜8Ó	$€à,/Ð )÷!ñ !ðH òó ðóó"ó]ó&
ð< Ð ð  'Ð Ø!%Ð Ø$+Ð !ó2ô	ôkð DHð
*Ø&ð
*Ø=@ð
*àó
*óô"&óRó&	ó3ô8ðN	Tð ðØ)ñ*ðYñYð ð/ð ð>,ð ð4ð !4ñ4ðHñOð^ -ñ_O-ð`ñaRð ðnØ*ñ+ð3ñ3ð 6IñIð4ñ<ðx "7ñy<7ðzdñ{>dð óF?ð #Ð/IÐUlñ7Ð Ð3ó ó
Hó?ó"\ðÐ ó+ó>Zóó$/ðP W[ð"Øð"Ø1;ð"ØPSð"àó"ðL 26ÐZ^Ø!ÀñFØðFØ!.ðFØJWðFàðFà1>ðFð óFð$€ó
óTó*kóðÐ óô("ó0Kó>ð( <@ðØ 8ðØDRóð< $1Ð  óNð 6:ðG&à+ñG&ØðG&Ø2ðG&ØNQðG&àðG&ð &óG&òZó(ó@ð0 RWð(FØð(FØ!9ð(FØJNð(Fàó(FðV ÷?ð ?ó ð?ó'ð ]bØð=Øð=Ø#-ð=Ø7:ð=ØF^ð=à.ð=à4Dð=àUYð=ð ð=ð 
ó	=ó@Yð ]aØ27ðQ,ØðQ,Ø#-ðQ,Ø7:ðQ,à&ðQ,à;YðQ,ð ðQ,ð ,0ðQ,ð 
ó	Q,ðl ,Ð<SØ)Ð;QñÐ ð FKØ8<Ø\`Øð	ØðØ#-ðØ>Bðàðà(5ðð 'ðð <Zðð ó	ò>€õ ð *.ðØ&ðà	ôr    