
    )j                       d Z ddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlmZ ddlmZmZmZmZ 	 ddlmZ dZn# e$ r dZdZY nw xY wddlmZmZ dd	lmZmZmZ  ej        e           Z!d
e"fdZ#dZ$dZ%dZ&dZ'dZ(dZ)dZ*e%fdede+d
e+fdZ, e-h d          Z. e-h d          Z/dNdede0d
e0fdZ1ddddede+de+d
e"fdZ2 e-h d          Z3 e-d d!h          Z4 e-d"d#h          Z5ded
efd$Z6ded
e0fd%Z7d&e8d'e"d
d(fd)Z9d*d+d,ee"ef         d'e"d
e:eed(         f         fd-Z;d
e0fd.Z< G d/ d0          Z=d1d2d3Z>erej?        d4             Z@ndZ@dOd*e"d6e"d'e"d7e"d
ee"ef         f
d8ZAerej?        d9             ZBndZBd:d;d<d=d>d?d@dAZCerej?        dB             ZDndZD G dC dD          ZE eE            ZFd,ee"ef         dEee"         d
e"fdFZGdGee"         dHe"d
e"fdIZHdZI	 ddJlJmKZLmMZNmOZPmQZRmSZTmUZVmWZXmYZZ dZIn# e$ r dZLdZNdZPdZRdZTdZVdZXdZZY nw xY w	 ddKl[m\Z\ n# e]$ r dZ\Y nw xY w G dL dMe          Z^dS )Pu  
OpenAI-compatible API server platform adapter.

Exposes an HTTP server with endpoints:
- POST /v1/chat/completions        — OpenAI Chat Completions format (stateless; opt-in session continuity via X-Hermes-Session-Id header; opt-in long-term memory scoping via X-Hermes-Session-Key header)
- POST /v1/responses               — OpenAI Responses API format (stateful via previous_response_id; X-Hermes-Session-Key supported)
- GET  /v1/responses/{response_id} — Retrieve a stored response
- DELETE /v1/responses/{response_id} — Delete a stored response
- GET  /v1/models                  — lists hermes-agent as an available model
- GET  /v1/capabilities            — machine-readable API capabilities for external UIs
- GET  /api/sessions               — list client-visible Hermes sessions
- POST /api/sessions               — create an empty Hermes session
- GET/PATCH/DELETE /api/sessions/{session_id} — read/update/delete a session
- GET  /api/sessions/{session_id}/messages — read session message history
- POST /api/sessions/{session_id}/fork — branch a session using SessionDB lineage
- POST /api/sessions/{session_id}/chat[/stream] — chat with a persisted session
- POST /v1/runs                    — start a run, returns run_id immediately (202)
- GET  /v1/runs/{run_id}           — retrieve current run status
- GET  /v1/runs/{run_id}/events    — SSE stream of structured lifecycle events
- POST /v1/runs/{run_id}/approval — resolve a pending run approval
- POST /v1/runs/{run_id}/stop       — interrupt a running agent
- GET  /health                     — health check
- GET  /health/detailed            — rich status for cross-container dashboard probing

Any OpenAI-compatible frontend (Open WebUI, LobeChat, LibreChat,
AnythingLLM, NextChat, ChatBox, etc.) can connect to hermes-agent
through this adapter by pointing at http://localhost:8642/v1 and
authenticating with API_SERVER_KEY.

Requires:
- aiohttp (already available in the gateway)
    N)Path)AnyDictListOptional)webTF)PlatformPlatformConfig)BasePlatformAdapter
SendResultis_network_accessiblereturnc                  z    	 ddl m}   | d          S # t          $ r Y nw xY w	 ddlm} |S # t          $ r Y dS w xY w)ux  Return the hermes-agent version string, or "dev" if it can't be resolved.

    Tries the installed package metadata first (authoritative for a pip/uv
    install), then the in-tree ``hermes_cli.__version__`` (covers editable /
    source checkouts where metadata may be stale or absent). Never raises —
    a version probe must not be able to break the health endpoint.
    r   )versionhermes-agent)__version__dev)importlib.metadatar   	Exception
hermes_clir   )r   r   s     A/home/ubuntu/.hermes/hermes-agent/gateway/platforms/api_server.py_hermes_versionr   A   s    ......w~&&&   ******   uus    
  , 
::	127.0.0.1i!  d   i       >@i     valuedefaultc                 T    	 t          |           S # t          t          f$ r |cY S w xY w)zNParse a listen port without letting malformed env/config values crash startup.)int	TypeError
ValueError)r   r   s     r   _coerce_portr#   a   s<    5zzz"   s    ''>   1onyestrue>   0noofffalsec                 8   t          | t                    r| S | |S t          | t                    r>|                                                                 }|t
          v rdS |t          v rdS |S t          | t          t          f          rt          |           S |S )a  Normalize boolean-like API payload values.

    External clients should send real JSON booleans, but some OpenAI-compatible
    frontends and middleware serialize flags like ``stream`` as strings.  Using
    Python truthiness on those values misroutes requests because ``"false"`` is
    still truthy.  Treat only explicit bool-ish scalars as booleans; everything
    else falls back to the caller's default.
    NTF)	
isinstanceboolstrstriplower_TRUE_REQUEST_BOOL_STRINGS_FALSE_REQUEST_BOOL_STRINGSr    float)r   r   
normalizeds      r   _coerce_request_boolr6   m   s     % }% [[]]((**
33344445%#u&& E{{N    
   
_max_depth_depthcontentr:   r;   c                   ||k    rdS | dS t          | t                    r)t          |           t          k    r| dt                   n| S t          | t                    rg }t          |           t
          k    r| dt
                   n| }|D ]]}t          |t                    r%|r"|                    |dt                              nt          |t                    rt          |                    d          pd          	                                
                                }|dv rY|                    dd          }|rA	 |                    t          |          dt                              nR# t          $ r Y nFw xY wnAt          |t                    r,t          |||dz             }|r|                    |           t          d |D                       t          k    r n_d	                    |          }	t          |	          t          k    r|	dt                   n|	S 	 t          |           }	t          |	          t          k    r|	dt                   n|	S # t          $ r Y dS w xY w)
a  Normalize OpenAI chat message content into a plain text string.

    Some clients (Open WebUI, LobeChat, etc.) send content as an array of
    typed parts instead of a plain string::

        [{"type": "text", "text": "hello"}, {"type": "input_text", "text": "..."}]

    This function flattens those into a single string so the agent pipeline
    (which expects strings) doesn't choke.

    Defensive limits prevent abuse: recursion depth, list size, and output
    length are all bounded.
     Ntype>   text
input_textoutput_textr@      r9   c              3   4   K   | ]}t          |          V  d S N)len.0ps     r   	<genexpr>z*_normalize_chat_content.<locals>.<genexpr>   s(      ))a3q66))))))r7   
)r-   r/   rF   MAX_NORMALIZED_TEXT_LENGTHlistMAX_CONTENT_LIST_SIZEappenddictgetr0   r1   r   _normalize_chat_contentsumjoin)
r<   r:   r;   partsitemsitem	item_typer@   nestedresults
             r   rR   rR      s     
rr'3 n7:7||F`7`7`w22233fmm'4   k36w<<BW3W3W...//]d 	 	D$$$ ) DLL&A'A&A!BCCCD$'' ) 0 0 6B77==??EEGG	 EEE88FB//D !!!LLT3N4N3N)OPPPP( ! ! ! D! D$'' )0*U[^_U_``` )LL((())5)))))-GGG H5!!69&kkD^6^6^v11122djjW69&kkD^6^6^v11122djj   rrs$   /E55
FF,7I$ $
I21I2>   r@   rA   rB   	image_urlinput_imagefile
input_filec                    | dS t          | t                    r)t          |           t          k    r| dt                   n| S t          | t                    st          |           S t          |           t          k    r| dt                   n| }g }d}|D ]"}t          |t                    r<|r9|dt                   }|                    d|d           |t          |          z  }Tt          |t                    sj|	                    d          }t          |pd          
                                                                }|t          v ry|	                    d          }|t          |t                    st          |          }|r9|dt                   }|                    d|d           |t          |          z  }6|t          v r|	                    d          }	|	                    d          }
t          |
t                    r,|
	                    d	          }|
	                    d|	          }	n|
}t          |t                    r|
                                st          d
          |
                                }|                                }|                    d          r)|                    d          rd|vrt          d          n9|                    d          s$|                    d          st          d          dd	|id}|	Ut          |	t                    r|	
                                st          d          |	
                                |d         d<   |                    |           |t           v rt          d          t          d|d          |sdS t#          d |D                       rd                    d |D                       S |S )u  Validate and normalize multimodal content for the API server.

    Returns a plain string when the content is text-only, or a list of
    ``{"type": "text"|"image_url", ...}`` parts when images are present.
    The output shape is the native OpenAI Chat Completions vision format,
    which the agent pipeline accepts verbatim (OpenAI-wire providers) or
    converts (``_preprocess_anthropic_content`` for Anthropic).

    Raises ``ValueError`` with an OpenAI-style code on invalid input:
      * ``unsupported_content_type`` — file/input_file/file_id parts, or
        non-image ``data:`` URLs.
      * ``invalid_image_url`` — missing URL or unsupported scheme.
      * ``invalid_content_part`` — malformed text/image objects.

    Callers translate the ValueError into a 400 response.
    Nr>   r   r@   r?   r@   r?   detailr[   urlzAinvalid_image_url:Image parts must include a non-empty image URL.zdata:zdata:image/,zgunsupported_content_type:Only image data URLs are supported. Non-image data payloads are not supported.zhttp://zhttps://zLinvalid_image_url:Image inputs must use http(s) URLs or data:image/... URLs.)r?   r[   zKinvalid_content_part:Image detail must be a non-empty string when provided.zunsupported_content_type:Inline image inputs are supported, but uploaded files and document inputs are not supported on this endpoint.z7unsupported_content_type:Unsupported content part type z:. Only text and image_url/input_image parts are supported.c              3   H   K   | ]}|                     d           dk    V  dS )r?   r@   NrQ   rG   s     r   rJ   z0_normalize_multimodal_content.<locals>.<genexpr>9  s1      
=
=q155==F"
=
=
=
=
=
=r7   rK   c              3   P   K   | ]!}|                     d           |d          V  "dS )r@   Nre   rG   s     r   rJ   z0_normalize_multimodal_content.<locals>.<genexpr>:  s5      NNqfN6NNNNNNr7   )r-   r/   rF   rL   rM   rR   rN   rO   rP   rQ   r0   r1   _TEXT_PART_TYPES_IMAGE_PART_TYPESr"   
startswith_FILE_PART_TYPESallrT   )r<   rV   normalized_partstext_accum_lenparttrimmedraw_type	part_typer@   ra   	image_ref	url_valuelowered
image_parts                 r   _normalize_multimodal_contentrv      sa   $ r'3 n7:7||F`7`7`w22233fmmgt$$ 0 'w////27||>S/S/SG***++Y`E-/N I
 I
dC   	 /: ::; ''(I(IJJJ#g,,.$%% 	 88F##B''--//5577	(((88F##D|dC(( !4yy /: ::; ''(I(IJJJ#g,,.)))XXh''F--I )T** &%MM%00	"x88%	i-- fY__5F5F f !deee!))Ioo''G!!'** 	))-88 Cy<P<P$E   =Q
 ((33 w7I7I*7U7U  b   3>UT]L^)_)_J!!&#.. tfllnn t$%rsss4:LLNN
;'1##J///(((]   Gh G G G
 
 	

  r
 
=
=,<
=
=
=== OyyNN,<NNNNNNr7   c                    t          | t                    r!t          |                                           S t          | t                    r| D ]}t          |t
                    rt          |                    d          pd                                                                          }|t          v r9t          |                    d          pd                                          r dS |t          v r dS dS )zPTrue when content has any text or image attachment.  Used to reject empty turns.r?   r>   r@   TF)
r-   r/   r.   r0   rM   rP   rQ   r1   rg   rh   )r<   rn   ptypes      r   _content_has_visible_payloadry   ?  s    '3 %GMMOO$$$'4     	  	 D$%%  DHHV,,23399;;AACC,,,TXXf5E5E5K1L1L1R1R1T1T,44---445r7   excparamweb.Responsec                    t          |           }|                    d          \  }}}|sd|}}t          j        t	          |||          d          S )zMTranslate a ``_normalize_multimodal_content`` ValueError into a 400 response.:invalid_content_part)coder{     status)r/   	partitionr   json_response_openai_error)rz   r{   rawr   _messages         r   _multimodal_validation_errorr   N  se    
c((C}}S))D!W 4.ggD666   r7   r   r{   bodyc                :   |                      d          p|                      d          }t          |          s'dt          j        t	          dd          d          fS 	 t          |          dfS # t          $ r}dt          ||	          fcY d}~S d}~ww xY w)
zOParse and normalize session chat ``message`` / ``input`` like chat completions.r   inputNzMissing 'message' fieldmissing_messager   r   r   r   )rQ   ry   r   r   r   rv   r"   r   )r   r{   user_messagerz   s       r   _session_chat_user_messager   Z  s    88I&&;$((7*;*;L'55 
S&3:KLLL
 
 
 
 	
D,\::D@@ D D D1#UCCCCCCCCCCDs   "A3 3
B=BBBc                      t           S )z/Check if API server dependencies are available.)AIOHTTP_AVAILABLE r7   r   check_api_server_requirementsr   h  s    r7   c                       e Zd ZdZedfdedefdZddZdede	e
eef                  fd	Zded
e
eef         ddfdZdedefdZdede	e         fdZdededdfdZddZdefdZdS )ResponseStoreae  
    SQLite-backed LRU store for Responses API state.

    Each stored response includes the full internal conversation history
    (with tool calls and results) so it can be reconstructed on subsequent
    requests via previous_response_id.

    Persists across gateway restarts.  Falls back to in-memory SQLite
    if the on-disk path is unavailable.
    Nmax_sizedb_pathc                 .   || _         |4	 ddlm} t           |            dz            }n# t          $ r d}Y nw xY w|dk    r|nd | _        	 t          j        |d          | _        n2# t          $ r% t          j        dd          | _        d | _        Y nw xY wddl	m
}  || j        d           | j                            d	           | j                            d
           | j                                         |                                  d S )Nr   )get_hermes_homezresponse_store.dbz:memory:F)check_same_thread)apply_wal_with_fallback)db_labelzCREATE TABLE IF NOT EXISTS responses (
                response_id TEXT PRIMARY KEY,
                data TEXT NOT NULL,
                accessed_at REAL NOT NULL
            )zCREATE TABLE IF NOT EXISTS conversations (
                name TEXT PRIMARY KEY,
                response_id TEXT NOT NULL
            ))	_max_sizehermes_cli.configr   r/   r   _db_pathsqlite3connect_connhermes_stater   executecommit_tighten_file_permissions)selfr   r   r   r   s        r   __init__zResponseStore.__init__y  sq   !?%======oo//2EEFF % % %$%29Z2G2GwwT	! EJJJDJJ 	! 	! 	! uMMMDJ DMMM	! 	988888
5HIIII
	
 	
 	
 	
	
 	
 	
 	
 	&&(((((s    , ;;A* *,BBr   c                 N   | j         sdS t          | j                   t          | j          d          t          | j          d          fD ]Y}	 |                                r|                    d           -# t          $ r  t
                              d|d           Y Vw xY wdS )z;Force owner-only permissions on the DB and SQLite sidecars.Nz-walz-shmi  z4Failed to restrict response store permissions for %sTexc_info)r   r   existschmodOSErrorloggerdebug)r   	candidates     r   r   z'ResponseStore._tighten_file_permissions  s    } 	FDM'''((DM'''((
 	 	I
##%% +OOE***   J!      	 	s   )A88'B"!B"response_idc                    | j                             d|f                                          }|dS | j                             dt          j                    |f           | j                                          	 t          j        |d                   S # t
          j        t          f$ rT t          
                    d|           | j                             d|f           | j                                          Y dS w xY w)z?Retrieve a stored response by ID (updates access time for LRU).z0SELECT data FROM responses WHERE response_id = ?Nz:UPDATE responses SET accessed_at = ? WHERE response_id = ?r   z:Corrupted JSON in response store for id=%s, evicting entry+DELETE FROM responses WHERE response_id = ?)r   r   fetchonetimer   jsonloadsJSONDecodeErrorr!   r   warning)r   r   rows      r   rQ   zResponseStore.get  s   j  >
 

(** 	 ;4
HY[[+&	
 	
 	
 	
	:c!f%%%$i0 
	 
	 
	NNL   J=   J44
	s   ;B A&C?>C?datac                    | j                             d|t          j        |t                    t          j                    f           | j                             d                                          d         }|| j        k    rd | j                             d|| j        z
  f                                          D             }|r]d	                    d |D                       }| j                             d	| d
|           | j                             d| d
|           | j         
                                 dS )z5Store a response, evicting the oldest if at capacity.zRINSERT OR REPLACE INTO responses (response_id, data, accessed_at) VALUES (?, ?, ?)r   SELECT COUNT(*) FROM responsesr   c                     g | ]
}|d          S r   r   )rH   r   s     r   
<listcomp>z%ResponseStore.put.<locals>.<listcomp>  s,        A  r7   zBSELECT response_id FROM responses ORDER BY accessed_at ASC LIMIT ?rc   c              3      K   | ]}d V  dS )?Nr   )rH   r   s     r   rJ   z$ResponseStore.put.<locals>.<genexpr>  s"      '?'?'?'?'?'?'?'?r7   z0DELETE FROM conversations WHERE response_id IN ()z,DELETE FROM responses WHERE response_id IN (N)r   r   r   dumpsr/   r   r   r   fetchallrT   r   )r   r   r   count	evict_idsplaceholderss         r   putzResponseStore.put  s^   
`$*T3777E	
 	
 	

 
""#CDDMMOOPQR4>!! :--XT^+-  (**  I  "xx'?'?Y'?'?'???
""V|VVV  
 
""R<RRR   	
r7   c                     | j                             d|f           | j                             d|f          }| j                                          |j        dk    S )zDRemove a response from the store. Returns True if found and deleted.z/DELETE FROM conversations WHERE response_id = ?r   r   )r   r   r   rowcount)r   r   cursors      r   deletezResponseStore.delete  sh     	
=~	
 	
 	
 ##9K>
 
 	
""r7   namec                 v    | j                             d|f                                          }|r|d         ndS )z3Get the latest response_id for a conversation name.z4SELECT response_id FROM conversations WHERE name = ?r   Nr   r   r   )r   r   r   s      r   get_conversationzResponseStore.get_conversation  sA    j  BTG
 

(** 	 &s1vv$&r7   c                 r    | j                             d||f           | j                                          dS )z2Map a conversation name to its latest response_id.zFINSERT OR REPLACE INTO conversations (name, response_id) VALUES (?, ?)N)r   r   r   )r   r   r   s      r   set_conversationzResponseStore.set_conversation  sC    
T;	
 	
 	
 	
r7   c                 \    	 | j                                          dS # t          $ r Y dS w xY w)zClose the database connection.N)r   closer   r   s    r   r   zResponseStore.close	  sC    	J 	 	 	DD	s    
++c                 r    | j                             d                                          }|r|d         ndS )Nr   r   r   )r   r   s     r   __len__zResponseStore.__len__  s7    j  !ABBKKMM#s1vv!#r7   r   N)__name__
__module____qualname____doc__MAX_STORED_RESPONSESr    r/   r   r   r   r   r   rQ   r   r.   r   r   r   r   r   r   r7   r   r   r   m  sa       	 	 (<D ') ') ')c ') ') ') ')R   &s xS#X'?    4s $sCx. T    >
## 
#$ 
# 
# 
# 
#'S 'Xc] ' ' ' 'S s t       $ $ $ $ $ $ $r7   r   zGET, POST, DELETE, OPTIONSz,Authorization, Content-Type, Idempotency-Key)zAccess-Control-Allow-MethodszAccess-Control-Allow-Headersc                   K   | j                             d          }| j                            dd          }d}|?|                    |          st	          j        d          S |                    |          }| j        dk    r-|t	          j        d          S t	          j        d|	          S  ||            d{V }||j                            |           |S )
zJAdd CORS headers for explicitly allowed origins; handle OPTIONS preflight.api_server_adapterOriginr>   N  r   OPTIONS   r   headers)	apprQ   r   _origin_allowedr   Response_cors_headers_for_originmethodupdate)requesthandleradapterorigincors_headersresponses         r   cors_middlewarer      s       +//"677$$Xr22**622 0|3////";;FCCL>Y&&#|3////<sLAAAA ))))))))###L111r7   invalid_request_errorerr_typer   c                     d| |||diS )zOpenAI-style error envelope.error)r   r?   r{   r   r   )r   r   r{   r   s       r   r   r   8  s'     		
 
 r7   c                 Z  K   | j         dv r| j                            d          }|t	 t          |          t          k    r%t          j        t          dd          d          S n5# t          $ r( t          j        t          d	d
          d          cY S w xY w ||            d{V S )zAReject overly large request bodies early based on Content-Length.>   PUTPOSTPATCHzContent-LengthNzRequest body too large.body_too_larger   i  r   zInvalid Content-Length header.invalid_content_lengthr   )	r   r   rQ   r    MAX_REQUEST_BYTESr   r   r   r"   )r   r   cls      r   body_limit_middlewarer  E  s       >555$$%566B~I2ww!222"0?X_o1p1p1py|}}}} 3! I I I,];[bz-{-{-{  EH  I  I  I  I  I  IIWW%%%%%%%%%s   <A' '/BBz*default-src 'none'; frame-ancestors 'none'z(camera=(), microphone=(), geolocation=()z#max-age=31536000; includeSubDomainsnosniffDENYr(   zno-referrer)zContent-Security-PolicyzPermissions-PolicyzStrict-Transport-SecurityzX-Content-Type-OptionszX-Frame-OptionszX-XSS-ProtectionzReferrer-Policyc                    K    ||            d{V }t                                           D ] \  }}|j                            ||           !|S )z9Add security headers to all responses (including errors).N)_SECURITY_HEADERSrV   r   
setdefault)r   r   r   kvs        r   security_headers_middlewarer  `  si       !))))))))%++-- 	. 	.DAq''1----r7   c                   :    e Zd ZdZddedefdZd Zded	efd
ZdS )_IdempotencyCachez=In-memory idempotency cache with TTL and basic LRU semantics.r   ,  	max_itemsttl_secondsc                 Z    ddl m}  |            | _        i | _        || _        || _        d S )Nr   )OrderedDict)collectionsr  _store	_inflight_ttl_max)r   r  r  r  s       r   r   z_IdempotencyCache.__init__m  s:    ++++++!kmmEG				r7   c                 j    t          j                      fd j                                        D             }|D ]} j                            |d            t	           j                   j        k    r: j                            d           t	           j                   j        k    8d S d S )Nc                 B    g | ]\  }}|d          z
  j         k    |S )ts)r  )rH   r  r  nowr   s      r   r   z,_IdempotencyCache._purge.<locals>.<listcomp>v  s1    RRRAag	8Q8Q18Q8Q8Qr7   F)last)r   r  rV   poprF   r  popitem)r   expiredr  r  s   `  @r   _purgez_IdempotencyCache._purget  s    ikkRRRRR!2!2!4!4RRR 	% 	%AKOOAt$$$$$+**KU+++ $+******r7   keyfingerprintc                    K                                       j                                      }|r|d         k    r|d         S f j                                      }|J fd}t	          j         |                      }| j        <   d fd}|                    |           t	          j        |           d {V S )	Nfprespc                     K                 d {V } dd l }| |                                 dj        <                                    | S )Nr   )r'  r&  r  )r   r  r"  )r'  _tcompute_coror$  r#  r   s     r   _compute_and_storez8_IdempotencyCache.get_or_set.<locals>._compute_and_store  s`      )\^^++++++!!!!,027799#U#UC r7   	done_taskasyncio.Task[Any]r   c                 z    j                                       | u rj                             d            d S d S rE   )r  rQ   r  )r,  inflight_keyr   s    r   _clear_inflightz5_IdempotencyCache.get_or_set.<locals>._clear_inflight  sE    >%%l33y@@N&&|T::::: A@r7   )r,  r-  r   N)r"  r  rQ   r  asynciocreate_taskadd_done_callbackshield)	r   r#  r$  r*  rW   taskr+  r0  r/  s	   ````    @r   
get_or_setz_IdempotencyCache.get_or_set|  s     {s## 	 DJ+--<[)~!!,//<        &'9'9';';<<D+/DN<(; ; ; ; ; ; ; ""?333^D)))))))))r7   N)r   r  )	r   r   r   r   r    r   r"  r/   r6  r   r7   r   r  r  k  sr        GG # 3    , , ,*C *c * * * * * *r7   r  keysc                      ddl m}  fd|D             } |t          |                              d                                                    S )Nr   )sha256c                 <    i | ]}|                     |          S r   re   )rH   r  r   s     r   
<dictcomp>z-_make_request_fingerprint.<locals>.<dictcomp>  s%    +++a!+++r7   utf-8)hashlibr9  reprencode	hexdigest)r   r7  r9  subsets   `   r   _make_request_fingerprintrB    s^    ++++d+++F6$v,,%%g..//99;;;r7   system_promptfirst_user_messagec                     | pd d| }t          j        |                    d                                                    dd         }d| S )a  Derive a stable session ID from the conversation's first user message.

    OpenAI-compatible frontends (Open WebUI, LibreChat, etc.) send the full
    conversation history with every request.  The system prompt and first user
    message are constant across all turns of the same conversation, so hashing
    them produces a deterministic session ID that lets the API server reuse
    the same Hermes session (and therefore the same Docker container sandbox
    directory) across turns.
    r>   rK   r<  N   zapi-)r=  r9  r?  r@  )rC  rD  seeddigests       r   _derive_chat_session_idrI    sY     !r99%799D^DKK0011;;==crcBF&??r7   )	list_jobsget_job
create_job
update_job
remove_job	pause_job
resume_jobtrigger_job)_scan_cron_promptc                   0    e Zd ZdZdef fdZededee	df         fd            Z
ede	de	fd	            Zd
e	deee	e	f                  fdZd
e	defdZedddedede	fd            Zdddee	e	f         fdZddde	fdZdddee	e	f         fdZddded         fdZdZdddeee	         ed         f         fdZd Z	 	 	 	 	 	 	 dzdee	         dee	         dee	         defdZd{d Zd{d!Zd{d"Zd{d#Zd{d$Zd{d%Z eded&ed'edefd(            Z!ed)ee	ef         dee	ef         fd*            Z"ed+ee	ef         dee	ef         fd,            Z#dddeee	ef         ed         f         fd-Z$de	deeee	ef                  ed         f         fd.Z%de	de&ee	ef                  fd/Z'd{d0Z(d{d1Z)d{d2Z*d{d3Z+d{d4Z,d{d5Z-d{d6Z.d{d7Z/d|d9Z0d{d:Z1	 	 d}ddd;e	d<e	d=ede	de	dd8fd>Z2	 d~ddd?e	d<e	d@edAe&ee	e	f                  dBe	dCee	         dDee	         dEede	dee	         dd8fdFZ3d{dGZ4d{dHZ5d{dIZ6 e7dJ          8                    dK          Z9h dLZ:dZ;dMZ<eded         fdN            Z=dddefdOZ>d{dPZ?d{dQZ@d{dRZAd{dSZBd{dTZCd{dUZDd{dVZEd{dWZFedAe&ee	ef                  dBedXee	ef         dYede&ee	ef                  f
dZ            ZGedAe&ee	ef                  dBedXee	ef         defd[            ZHeIdAe&ee	ef                  dBedXee	ef         de&ee	ef                  fd\            ZJeddXee	ef         d^ede&ee	ef                  fd_            ZK	 	 	 	 	 	 	 	 ddBe	dAe&ee	e	f                  dee	         dee	         d`eeL         dee	         defdaZMdbZNdcZOddZPdee	dfe	dgedee	ef         fdhZQdee	didjfdkZRd{dlZSd{dmZTd|dnZUd{doZVd{dpZWddqZXdefdrZYddsZZ	 	 ddte	due	dvee	         dweee	ef                  de[f
dxZ\dte	dee	ef         fdyZ] xZ^S )APIServerAdapterz
    OpenAI-compatible HTTP API server adapter.

    Runs an aiohttp web server that accepts OpenAI-format requests
    and routes them through hermes-agent's AIAgent.
    configc           	         t                                          |t          j                   |j        pi }|                    dt          j        dt                              | _	        |                    d          }|'t          j        dt          t                              }t          |t                    | _        |                    dt          j        dd                    | _        |                     |                    dt          j        d	d                              | _        |                     |                    d
t          j        dd                              | _        d | _        d | _        d | _        t-                      | _        i | _        i | _        i | _        i | _        i | _        i | _        d | _        d S )NhostAPI_SERVER_HOSTportAPI_SERVER_PORTr#  API_SERVER_KEYr>   cors_originsAPI_SERVER_CORS_ORIGINS
model_nameAPI_SERVER_MODEL_NAME)superr   r	   
API_SERVERextrarQ   osgetenvDEFAULT_HOST_hostr/   DEFAULT_PORTr#   _port_api_key_parse_cors_origins_cors_origins_resolve_model_name_model_name_app_runner_siter   _response_store_run_streams_run_streams_created_active_run_agents_active_run_tasks_run_statuses_run_approval_sessions_session_db)r   rU  rb  raw_port	__class__s       r   r   zAPIServerAdapter.__init__  s   !4555"))FBI6G,V,VWW
99V$$y!2C4E4EFFH&x>>
"YYubi8H".M.MNN.2.F.FIInbi0I2&N&NOO/
 /
 !% 8 8IIlBI.Er$J$JKK!
 !
 26	26.2
,HJ68!24<>8: 79#*.r7   r   r   .c                     | sdS t          | t                    r|                     d          }n5t          | t          t          t
          f          r| }nt          |           g}t	          d |D                       S )z6Normalize configured CORS origins into a stable tuple.r   rc   c              3      K   | ]F}t          |                                          #t          |                                          V  Gd S rE   )r/   r0   )rH   rW   s     r   rJ   z7APIServerAdapter._parse_cors_origins.<locals>.<genexpr>  sG      NN4CIIOO<M<MNSYY__&&NNNNNNr7   )r-   r/   splitrM   tupleset)r   rV   s     r   rj  z$APIServerAdapter._parse_cors_origins  s      	2eS!! 	!KK$$EEeS122 	!EEZZLENN5NNNNNNr7   explicitc                     | r(|                                  r|                                  S 	 ddlm}  |            }|r|dvr|S n# t          $ r Y nw xY wdS )a
  Derive the advertised model name for /v1/models.

        Priority:
        1. Explicit override (config extra or API_SERVER_MODEL_NAME env var)
        2. Active profile name (so each profile advertises a distinct model)
        3. Fallback: "hermes-agent"
        r   )get_active_profile_name>   customr   r   )r0   hermes_cli.profilesr  r   )r  r  profiles      r   rl  z$APIServerAdapter._resolve_model_name  s      	$(( 	$>>###	CCCCCC--//G 7*??? 	 	 	D	~s   A 
AAr   c                     |r| j         sdS d| j         v r t          t                    }d|d<   d|d<   |S || j         vrdS t          t                    }||d<   d|d<   d|d<   |S )z2Return CORS headers for an allowed browser origin.N*zAccess-Control-Allow-Origin600zAccess-Control-Max-Ager   Vary)rk  rP   _CORS_HEADERS)r   r   r   s      r   r   z)APIServerAdapter._cors_headers_for_origin%  s     	T/ 	4$$$$=))G58G1205G,-N+++4}%%17-.",1()r7   c                 @    |sdS | j         sdS d| j         v p|| j         v S )zDAllow non-browser clients and explicitly configured browser origins.TFr  )rk  )r   r   s     r   r   z APIServerAdapter._origin_allowed9  s:     	4! 	5d((HFd6H,HHr7   r   max_lenr  c                    | dS t          |                               dd                              dd                                          }|d|         S )z:Sanitize request metadata before it reaches security logs.Nr>    rK   )r/   replacer0   )r   r  r@   s      r   _clean_log_valuez!APIServerAdapter._clean_log_valueC  sS     =25zz!!$,,44T3??EEGGHWH~r7   r   web.Requestc                    d}	 |j         r|j                             d          nd}t          |t          t          f          r|rt          |d                   }n# t          $ r d}Y nw xY w|                     t          |dd          p|          |                     |          |                     |j	        
                    dd                    |                     |j	        
                    dd                    |                     |j        d	          |                     |j        d
	          |                     |j	        
                    dd          d	          dS )z>Return non-secret source metadata for security/audit warnings.r>   peernameNr   remotezX-Forwarded-Forz	X-Real-IPrF  r    z
User-Agentr  )r  peer_ipforwarded_forreal_ipr   path
user_agent)	transportget_extra_infor-   r~  rM   r/   r   r  getattrr   rQ   r   path_qs)r   r   r  peers       r   _request_audit_contextz'APIServerAdapter._request_audit_contextK  sg   	CJCT^7$33J???Z^D$.. '4 'd1g,, 	 	 	GGG	 ++GGXr,J,J,UgVV,,W55!227?3F3FGXZ\3]3]^^,,W_-@-@b-Q-QRR++GNB+GG))'/3)GG//0C0CLRT0U0U_b/cc
 
 	
s   AA A*)A*c                     |                      |          }d |                                D             }|rd                    |          ndS )Nc                 &    g | ]\  }}|| d |S )=r   )rH   r#  r   s      r   r   z>APIServerAdapter._request_audit_log_suffix.<locals>.<listcomp>a  s/    LLLeeLS$$5$$LLLr7   r  zsource='unknown')r  rV   rT   )r   r   ctxfieldss       r   _request_audit_log_suffixz*APIServerAdapter._request_audit_log_suffix_  sM    ))'22LLsyy{{LLL#)Asxx/AAr7   c                 z   |                      |          }ddd}|                    d          r|d         |d<   |                    d          r|d         |d<   |                    d          r|d         |d<   |                    d          r|d         |d<   |                    d	          r|d	         |d	<   |S )
z@Persist safe API source metadata on cron jobs created over HTTP.
api_serverapi)platformchat_idr  	source_ipr  r  r  r  )r  rQ   )r   r   r  r   s       r   _cron_origin_from_requestz*APIServerAdapter._cron_origin_from_requestd  s    ))'22$
 
 778 	0"%h-F;779 	/ #IF977?## 	;&)/&:F?#779 	/ #IF977<   	5#&|#4F< r7   r|   c                 x   | j         sdS |j                            dd          }|                    d          r8|dd                                         }t          j        || j                   rdS t                              d| 	                    |                     t          j        ddd	d
did          S )a$  
        Validate Bearer token from Authorization header.

        Returns None if auth is OK, or a 401 web.Response on failure.
        connect() refuses to start the API server without API_SERVER_KEY, so
        the no-key branch only exists for tests or unsupported manual wiring.
        NAuthorizationr>   zBearer    z'API server rejected invalid API key: %sr   zInvalid API keyr   invalid_api_key)r   r?   r   i  r   )ri  r   rQ   ri   r0   hmaccompare_digestr   r   r  r   r   )r   r   auth_headertokens       r   _check_authzAPIServerAdapter._check_auth{  s     } 	4o))/2>>!!),, 	O))++E"5$-88 t5**733	
 	
 	
  "3=T^oppq
 
 
 	
r7      c                    |j                             dd                                          }|sdS | j        s?t                              d           dt          j        t          d          d          fS t          j
        d	|          rdt          j        d
dddid          fS t          |          | j        k    rdt          j        d
dddid          fS |dfS )a  Extract and validate the ``X-Hermes-Session-Key`` header.

        The session key is a stable per-channel identifier that scopes
        long-term memory (e.g. Honcho sessions) across transcripts.  It
        is independent of ``X-Hermes-Session-Id``: callers may send
        either, both, or neither.

        Returns ``(session_key, None)`` on success (with an empty/absent
        header yielding ``None`` for the key), or ``(None, error_response)``
        on validation failure.

        Security: like session continuation, accepting a caller-supplied
        memory scope requires API-key authentication so that an
        unauthenticated client on a local-only server can't inject itself
        into another user's long-term memory scope by guessing a key.
        X-Hermes-Session-Keyr>   NNzlX-Hermes-Session-Key rejected: no API key configured. Set API_SERVER_KEY to enable long-term memory scoping.NzfX-Hermes-Session-Key requires API key authentication. Configure API_SERVER_KEY to enable this feature.r   r   
[\r\n\x00]r   zInvalid session keyr   r   r?   r   zSession key too long)r   rQ   r0   ri  r   r   r   r   r   researchrF   _MAX_SESSION_HEADER_LEN)r   r   r   s      r   _parse_session_key_headerz*APIServerAdapter._parse_session_key_header  s:   & o!!"8"==CCEE 	:} 	NNI   *G       9]C(( 	*&;E\]]^    
 s88d222*&<F]^^_    
 Dyr7   c                     | j         I	 ddlm}  |            | _         n2# t          $ r%}t                              d|           Y d}~nd}~ww xY w| j         S )zLazily initialise and return the shared SessionDB instance.

        Sessions are persisted to ``state.db`` so that ``hermes sessions list``
        shows API-server conversations alongside CLI and gateway ones.
        Nr   )	SessionDBz(SessionDB unavailable for API server: %s)rx  r   r  r   r   r   )r   r  es      r   _ensure_session_dbz#APIServerAdapter._ensure_session_db  s     #L222222#,9;;   L L LGKKKKKKKKLs    
AA		ANephemeral_system_prompt
session_idgateway_session_keyc                    ddl m} ddlm}	m}
m}m} ddlm}  |	            }|	                                } |
            } |            }t           ||d                    }t          t          j        dd                    }|                                } |dd|i||d	d
|pd||d|||||                                 |||d}|S )u7  
        Create an AIAgent instance using the gateway's runtime config.

        Uses _resolve_runtime_agent_kwargs() to pick up model, api_key,
        base_url, etc. from config.yaml / env vars.  Toolsets are resolved
        from config.yaml platform_toolsets.api_server (same as all other
        gateway platforms), falling back to the hermes-api-server default.

        ``gateway_session_key`` is a stable per-channel identifier supplied
        by the client (via ``X-Hermes-Session-Key``).  Unlike ``session_id``
        which scopes the short-term transcript and rotates on /new, this
        key is meant to persist across transcripts so long-term memory
        providers (e.g. Honcho) can scope their per-chat state correctly
        — matching the semantics of the native gateway's ``session_key``.
        r   )AIAgent)_resolve_runtime_agent_kwargs_resolve_gateway_model_load_gateway_configGatewayRunner)_get_platform_toolsr  HERMES_MAX_ITERATIONS90modelTFN)max_iterations
quiet_modeverbose_loggingr  enabled_toolsetsr  r  stream_delta_callbacktool_progress_callbacktool_start_callbacktool_complete_callback
session_dbfallback_modelreasoning_configr  r   )	run_agentr  gateway.runr  r  r  r  hermes_cli.tools_configr  _load_reasoning_configsortedr    rc  rd  _load_fallback_modelr  )r   r  r  r  r  r  r  r  r  r  r  r  r  r  runtime_kwargsr  r  user_configr  r  r  agents                         r   _create_agentzAPIServerAdapter._create_agent  sM   2 	&%%%%%zzzzzzzzzzzz??????6688(??AA&&((**,,!"5"5k<"P"PQQRY'>EEFF ';;== 
 


 *!$;$Ct-!!"7#9 3#9..00)- 3#
 
 
 
& r7   c                 N   K   t          j        ddt                      d          S )u$   GET /health — simple health check.okr   )r   r  r   )r   r   r   )r   r   s     r   _handle_healthzAPIServerAdapter._handle_health/  s.       ODUDUVV
 
 	
r7   c                 b  K   ddl m}  |            pi }t          j        ddt	                      |                    d          |                    di           |                    dd          |                    d          |                    d	          t          j                    d
	          S )u"  GET /health/detailed — rich status for cross-container dashboard probing.

        Returns gateway state, connected platforms, PID, and uptime so the
        dashboard can display full status without needing a shared PID file or
        /proc access.  No authentication required.
        r   )read_runtime_statusr  r   gateway_state	platformsactive_agentsexit_reason
updated_at)	r   r  r   r  r  r  r  r  pid)gateway.statusr  r   r   r   rQ   rc  getpid)r   r   r  runtimes       r   _handle_health_detailedz(APIServerAdapter._handle_health_detailed5  s       	766666%%''-2 &&(($[[99 ["55$[[!<<";;}55!++l339;;
"
 
"
 
 
 
	r7   c                    K   |                      |          }|r|S t          j        d| j        dt	          t          j                              dg | j        ddgd          S )u=   GET /v1/models — return hermes-agent as an available model.rM   r  hermesN)idobjectcreatedowned_by
permissionrootparentr  r   )r  r   r   rm  r    r   r   r   auth_errs      r   _handle_modelszAPIServerAdapter._handle_modelsK  s      ##G,, 	O  *%"49;;// ("$ ," 
"
 "
   	r7   c                 >  K   |                      |          }|r|S t          j        dd| j        dt	          | j                  ddd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ddddddt	          | j                  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"d0d'd1d"dd'd2d"d3d d4d"d5d d6d"d7d d8d"d9d'd8d"d:d d;d"d<d=d;d"d>d?d;d"d d@d"d'dAd"d'dBd"d'dCd"dDdE          S )Fu  GET /v1/capabilities — advertise the stable API surface.

        External UIs and orchestrators use this endpoint to discover the API
        server's plugin-safe contract without scraping docs or assuming that
        every Hermes version exposes the same endpoints.
        zhermes.api_server.capabilitiesr   bearer)r?   requiredserver_agentserverFzThe API server creates a server-side Hermes AIAgent; tools execute on the API-server host unless a future explicit split-runtime mode is enabled.)modetool_executionsplit_runtimedescriptionchat_completionsTchat_completions_streamingresponses_apiresponses_streamingrun_submission
run_statusrun_events_sserun_stoprun_approval_responsetool_progress_eventsapproval_eventssession_resourcessession_chatsession_chat_streamingsession_forkadmin_config_rw
jobs_adminX-Hermes-Session-Idr  )memory_write_api
skills_api	audio_apirealtime_voicesession_continuity_headersession_key_headercorshealthGET/health)r   r  health_detailed/health/detailedmodels
/v1/modelsr   /v1/chat/completions	responses/v1/responsesruns/v1/runs/v1/runs/{run_id}
run_events/v1/runs/{run_id}/eventsrun_approval/v1/runs/{run_id}/approval/v1/runs/{run_id}/stopskills
/v1/skillstoolsets/v1/toolsetssessions/api/sessionssession_createsession/api/sessions/{session_id}session_updater   session_deleteDELETE#/api/sessions/{session_id}/messages/api/sessions/{session_id}/fork/api/sessions/{session_id}/chat&/api/sessions/{session_id}/chat/stream)session_messagesr  r  session_chat_stream)r  r  r  authr  features	endpoints)r  r   r   rm  r.   ri  rk  r  s      r   _handle_capabilitiesz%APIServerAdapter._handle_capabilities`  s*      ##G,, 	O 6&%  // 
 '"*!&>	 	"D,d   &t	
 !$ d !$ D ( ' "4 $T  )$   "5!" e#$ %*"""'-B&<T/001  4UI>>!e=O#P#P ULAA #v?U$V$V	
 HH 6:>> 8KLL 8RSS 6;W X X v7OPP ULAA unEE uoFF !V_"M"M e5QRR  !W>Z"["[!" !X?["\"\#$ 05>c$d$d+1;\ ] ]+1;\ ] ]28Bj'k'k+  YC"
 C"
 C C C	r7   c                 B  K   |                      |          }|r|S 	 ddlm}m}  | |d                    }nO# t          $ rB t
                              d           t          j        t          dd          d	
          cY S w xY wt          j        d|d          S )u4  GET /v1/skills — list installed skills visible to the API-server agent.

        Read-only listing intended for external clients that need to know
        which skills are available without sending a chat message and asking
        the model. Mirrors what the gateway/CLI surfaces through
        ``/skills list``, but as a deterministic JSON payload.

        Returns the same skill metadata (name, description, category) the
        skills hub uses internally. Disabled skills are excluded so the
        listing matches what the agent actually loads.
        r   )_find_all_skills_sort_skillsF)skip_disabledzGET /v1/skills failedzFailed to enumerate skillsserver_errorr   r  r   rM   r  )
r  tools.skills_toolrP  rQ  r   r   	exceptionr   r   r   )r   r   r  rP  rQ  r9  s         r   _handle_skillszAPIServerAdapter._handle_skills  s       ##G,, 	O	HHHHHHHH!\"2"2"G"G"GHHFF 	 	 	4555$:^TTT     	  "
 "
   	s   ; A	BBc                 R  K   |                      |          }|r|S 	 ddlm} ddlm}m}m} ddlm}  |            } ||dd          }	g }
 |            D ]i\  }}}	 t          t           ||                              }n# t          $ r g }Y nw xY w||	v }|
                    |||| |||          |d           jnO# t          $ rB t                              d	           t          j        t#          d
d          d          cY S w xY wt          j        dd|
d          S )u  GET /v1/toolsets — list toolsets and their resolved tools.

        Returns the toolset surface the api_server platform actually exposes
        to its agent: each toolset's enabled/configured state plus the
        concrete tool names it expands to. This is the deterministic
        equivalent of what a client would otherwise have to recover by
        asking the model what tools it can call.
        r   )load_config)$_get_effective_configurable_toolsetsr  _toolset_has_keys)resolve_toolsetr  F)include_default_mcp_servers)r   labelr  enabled
configuredtoolszGET /v1/toolsets failedzFailed to enumerate toolsetsrS  rT  r  r   rM   )r  r  r   )r  r   rY  r  rZ  r  r[  r;  r\  r  r  r   rO   r   rV  r   r   r   )r   r   r  rY  rZ  r  r[  r\  rU  r  r   r   r^  descra  
is_enableds                   r   _handle_toolsetsz!APIServerAdapter._handle_toolsets  s	      ##G,, 	O#	555555         
 100000 []]F22,1     
 *,D%I%I%K%K  !eT"3t'<'<#=#=>>EE    EEE!%55
 "#')"3"3D&"A"A"       	 	 	6777$<~VVV     	  $"
 "
   	s7   A C %BC BC B.C A	DDr   maximumc                     	 t          |           }n# t          t          f$ r |cY S w xY w|dk     r|S t          ||          S )Nr   )r    r!   r"   min)r   r   re  parseds       r   _parse_nonnegative_intz'APIServerAdapter._parse_nonnegative_int  s[    	ZZFF:& 	 	 	NNN	A::N67###s    ((r@  c                      d} fd|D             }t                               d                    |d<   t                               d                    |d<   |S )z4Return a stable, client-safe session representation.)r  sourceuser_idr  title
started_atended_at
end_reasonmessage_counttool_call_countinput_tokensoutput_tokenscache_read_tokenscache_write_tokensreasoning_tokensestimated_cost_usdactual_cost_usdapi_call_countparent_session_idlast_activepreview_lineage_root_idc                 D    i | ]}|v |                     |          S r   re   )rH   r#  r@  s     r   r;  z6APIServerAdapter._session_response.<locals>.<dictcomp>   s,    PPPS3C((r7   rC  has_system_promptmodel_confighas_model_config)r.   rQ   )r@  	safe_keyspayloads   `  r   _session_responsez"APIServerAdapter._session_response  sk    
	 QPPPIPPP (,GKK,H,H'I'I#$&*7;;~+F+F&G&G"#r7   r   c                 $     d} fd|D             S )N)r  r  roler<   tool_call_id
tool_calls	tool_name	timestamptoken_countfinish_reason	reasoningreasoning_contentc                 D    i | ]}|v |                     |          S r   re   )rH   r#  r   s     r   r;  z6APIServerAdapter._message_response.<locals>.<dictcomp>.  s,    MMM#cWnnW[[%%nnnr7   r   )r   r  s   ` r   _message_responsez"APIServerAdapter._message_response'  s'    
	
 NMMMMMMMr7   c                 $  K   	 |                                  d {V }n5# t          $ r( i t          j        t	          d          d          fcY S w xY wt          |t                    s%i t          j        t	          d          d          fS |d fS )NInvalid JSON in request bodyr   r   z"Request body must be a JSON object)r   r   r   r   r   r-   rP   )r   r   r   s      r   _read_json_bodyz APIServerAdapter._read_json_body0  s      	d ''''''DD 	d 	d 	ds(7U)V)V_bccccccc	d$%% 	js(7[)\)\ehiiiiiTzs    /AAc                    |                                  }|'d t          j        t          dd          d          fS |                    |          }|s*d t          j        t          d| d          d          fS |d fS )	NSession database unavailablesession_db_unavailabler     r   zSession not found: session_not_found  )r  r   r   r   get_session)r   r  dbr@  s       r   _get_existing_session_or_404z-APIServerAdapter._get_existing_session_or_4049  s    $$&&:*=9W^v+w+w+w  AD  E  E  E  E  E..,, 	D*=9[z9[9[bu+v+v+v  @C  D  D  D  D  D}r7   c                     |                                  }|g S 	 |                    |          S # t          $ r(}t                              d||           g cY d }~S d }~ww xY w)N)Failed to load session history for %s: %s)r  get_messages_as_conversationr   r   r   )r   r  r  rz   s       r   !_conversation_history_for_sessionz2APIServerAdapter._conversation_history_for_sessionB  s|    $$&&:I	22:>>> 	 	 	NNF
TWXXXIIIIII	s   / 
A!AA!A!c           	         K                         |          }|r|S                                  }|%t          j        t	          dd          d          S                      |j                            d          dd	
          }                     |j                            d          dd
          }|j                            d          pd}t          |j                            d          d          }|	                    ||||d          }t          j        d fd|D             ||t          |          |k    d          S )u5   GET /api/sessions — list persisted Hermes sessions.Nr  r  r   r  r   limit2   r   )r   re  offsetr   i@B rk  include_childrenFr   T)rk  r  r  r  order_by_last_activerM   c                 :    g | ]}                     |          S r   )r  )rH   sr   s     r   r   z:APIServerAdapter._handle_list_sessions.<locals>.<listcomp>c  '    AAA1T++A..AAAr7   )r  r   r  r  has_more)r  r  r   r   r   ri  queryrQ   r6   list_sessions_richrF   )	r   r   r  r  r  r  rk  r  r=  s	   `        r   _handle_list_sessionsz&APIServerAdapter._handle_list_sessionsL  sr     ##G,, 	O$$&&:$]3QXp%q%q%qz}~~~~++GM,=,=g,F,FPR\_+``,,W]->->x-H-HRS]f,gg""8,,4/0A0ABT0U0U_deee((-!% ) 
 
  AAAAAAAH."
 "
   	r7   c                 r  K   |                      |          }|r|S |                     |           d{V \  }}|r|S |                                 }|%t          j        t          dd          d          S |                    d          p|                    d          }|r!t          |                                          nCd	t          t          j
                               d
t          j                    j        dd          }|rt          j        d|          r%t          j        t          dd          d          S t!          |          | j        k    r%t          j        t          dd          d          S |                    |          r(t          j        t          d| d          d          S |                    d          p| j        }|                    d          }	|	:t)          |	t                    s%t          j        t          dd          d          S |                    |d|rt          |          nd|	           |                    d          }
|
	 |                    |t          |
                     n^# t.          $ rQ}|                    |           t          j        t          t          |          d          d          cY d}~S d}~ww xY w|                    |          p|d||
d}t          j        d|                     |          dd          S ) u:   POST /api/sessions — create an empty Hermes session row.Nr  r  r   r  r   r  r  api_r      r  Invalid session IDinvalid_session_idr   zSession ID too longSession already exists: session_exists  r  rC  zsystem_prompt must be a stringinvalid_system_promptr  )r  rC  rm  invalid_title)r  rk  r  rm  hermes.sessionr  r@     )r  r  r  r   r   r   rQ   r/   r0   r    r   uuiduuid4hexr  r  rF   r  r  rm  r-   create_sessionset_session_titler"   delete_sessionr  )r   r   r  r   errr  raw_idr  r  rC  rm  rz   r@  s                r   _handle_create_sessionz'APIServerAdapter._handle_create_sessioni  se     ##G,, 	O..w77777777	c 	J$$&&:$]3QXp%q%q%qz}~~~~$9488L#9#9,2hS[[&&(((8hs49;;?O?O8h8hRVR\R^R^RbcedeceRf8h8h
 	qRY}jAA 	q$]3GNb%c%c%cloppppz??T999$]3HOc%d%d%dmpqqqq>>*%% 	@$]3Zj3Z3Zaq%r%r%r{~!!5T%511$Zs-K-K$$]3SZq%r%r%r{~
*l:W#e***SWgtuuu!!d$$ZU<<<< d d d!!*---(s3xxo)V)V)V_bcccccccccd ..,,zz\dity0z0z ,<I_I_`gIhIh!i!iruvvvvs   /#J 
K.AK)#K.)K.c                    K   |                      |          }|r|S |                     |j        d                   \  }}|r|S t          j        d|                     |          d          S )zGET /api/sessions/{session_id}.r  r  r  )r  r  
match_infor   r   r  )r   r   r  r@  r  s        r   _handle_get_sessionz$APIServerAdapter._handle_get_session  s{      ##G,, 	O889KL9YZZ 	J ,<I_I_`gIhIh!i!ijjjr7   c                   K   |                      |          }|r|S |j        d         }|                     |          \  }}|r|S |                     |           d{V \  }}|r|S ddh}t	          t          |          |z
            }|r;t          j        t          dd	                    |           d          d	
          S | 
                                }	d|v r~	 |	                    ||d         dnt          |d                              nI# t          $ r<}
t          j        t          t          |
          d          d	
          cY d}
~
S d}
~
ww xY w|                    d          r)|	                    |t          |d                              |	                    |          p|}t          j        d|                     |          d          S )uI   PATCH /api/sessions/{session_id} — update client-safe session metadata.r  Nrm  rp  zUnsupported session fields: z, unsupported_session_fieldr   r   r   r>   r  r  r  )r  r  r  r  r  r  r   r   r   rT   r  r  r/   r"   rQ   end_sessionr  r  )r   r   r  r  r@  r  r   allowedunknownr  rz   s              r   _handle_patch_sessionz&APIServerAdapter._handle_patch_session  s:     ##G,, 	O'5
88DD 	J..w77777777	c 	JL)TW,-- 	W$]3fRVR[R[\cRdRd3f3f  nI  &J  &J  &J  SV  W  W  W  W$$&&d??d$$ZtG}7LRUVZ[bVcRdRdeeee d d d(s3xxo)V)V)V_bcccccccccd88L!! 	@NN:s4+='>'>???..,,7 ,<I_I_`gIhIh!i!ijjjs    3D 
E1EEEc                 &  K   |                      |          }|r|S |j        d         }|                     |          \  }}|r|S |                                 }|                    |          }t          j        d|t          |          d          S )z"DELETE /api/sessions/{session_id}.r  zhermes.session.deleted)r  r  deleted)r  r  r  r  r  r   r   r.   )r   r   r  r  r@  r  r  r  s           r   _handle_delete_sessionz'APIServerAdapter._handle_delete_session  s      ##G,, 	O'5
88DD 	J$$&&##J// ,DJcghocpcp!q!qrrrr7   c                 P   K                         |          }|r|S |j        d         }                     |          \  }}|r|S                                  }|                    |          }|                    |          }t          j        d| fd|D             d          S )z(GET /api/sessions/{session_id}/messages.r  rM   c                 :    g | ]}                     |          S r   )r  )rH   mr   s     r   r   z=APIServerAdapter._handle_session_messages.<locals>.<listcomp>  r  r7   )r  r  r   )r  r  r  r  resolve_resume_session_idget_messagesr   r   )	r   r   r  r  r   r  r  resolved_idmessagess	   `        r   _handle_session_messagesz)APIServerAdapter._handle_session_messages  s      ##G,, 	O'5
22:>>3 	J$$&&22:>>??;// %AAAAAAA"
 "
   	r7   c                    K   |                      |          }|r|S |j        d         }|                     |          \  }}|r|S |                     |           d{V \  }}|r|S |                                 }t          |                    d          pX|                    d          pCdt          t          j                               dt          j
                    j        dd                                                    }|rt          j        d|          r%t          j        t#          dd	
          d          S |                    |          r(t          j        t#          d| d
          d          S |                    |d           |                    |d|                    d          |                    d          |           |                    |          }	|                    ||	           |                    d          }
|
C|                    d          pd}	 |                    |          }
n# t0          $ r | d}
Y nw xY w	 |                    |t          |
                     nI# t4          $ r<}t          j        t#          t          |          d
          d          cY d}~S d}~ww xY w|                    |          p||d}t          j        d|                     |          dd          S )uQ   POST /api/sessions/{session_id}/fork — branch via current SessionDB primitives.r  Nr  r  r   r  r  r  r  r   r   r   r  r  r  branchedr  r  rC  )r  rC  r{  rm  forkz forkr  )r  r{  r  r  r  )r  r  r  r  r  r/   rQ   r    r   r  r  r  r0   r  r  r   r   r   r  r  r  r  replace_messagesget_next_title_in_lineager   r  r"   r  )r   r   r  	source_idrk  r  r   r  fork_idr  rm  baserz   r  s                 r   _handle_fork_sessionz%APIServerAdapter._handle_fork_session  sV     ##G,, 	O&|4	77	BB 	J..w77777777	c 	J$$&&dhhtnnr(>(>rBrTY[[IYIYBrBr\`\f\h\h\lmonomo\pBrBrssyy{{ 	q")M7;; 	q$]3GNb%c%c%clopppp>>'"" 	}$]3Wg3W3W^n%o%o%ox{|||| 	y*---
**W%% **_55' 	 	
 	
 	
 ??9--
GX...!!=::g&&0&D'44T:: ' ' ''	`  #e**5555 	` 	` 	`$]3s88/%R%R%R[^_________	`~~g&&Yy*Y*Y ,<I_I_`dIeIe!f!forsssss0   1I II#J 
K1K<KKc                 z  K   |                      |          }|r|S |                     |          \  }}||S |j        d         }|                     |          \  }}|r|S |                     |           d{V \  }}|r|S t          |          \  }	}||S |                    d          p|                    d          }
|
:t          |
t                    s%t          j
        t          dd          d	          S |                     |          }|                     |	||
||
           d{V \  }}t          |t                    r|                    d          n|}t          |t                    r|                    dd          nd}d|p|i}|r||d<   t          j
        d|p|d|d|d|          S )uD   POST /api/sessions/{session_id}/chat — one synchronous agent turn.Nr  system_messageinstructionssystem_message must be a stringinvalid_system_messager   r   r   r   conversation_historyr  r  r  final_responser>   r  r  zhermes.session.chat.completion	assistantr  r<   )r  r  r   usager   )r  r  r  r  r  r   rQ   r-   r/   r   r   r   r  
_run_agentrP   )r   r   r  r  key_errr  r   r  r   r   rC  historyrZ   r  effective_session_idr  r   s                    r   _handle_session_chatz%APIServerAdapter._handle_session_chat  sQ     ##G,, 	O'+'E'Eg'N'N$WN'5
22:>>3 	J..w77777777	c 	J6t<<c?J!122Ndhh~6N6N$Zs-K-K$$]3T[s%t%t%t  ~A  B  B  B  B88DD"oo%!($1! 3 . 
 
 
 
 
 
 
 
 <Ffd;S;Scvzz,777Yc=GPT=U=U]$4b999[](*>*L*M 	B.AG*+ :2@j$/NKK	  
 
 
 	
r7   web.StreamResponsec                 ^   K                         |          }|r|S                      |          \  }||S |j        d                                        \  }}|r|S                      |           d{V \  }}|r|S t          |          \  }||S |                    d          p|                    d          :t          t                    s%t          j
        t          dd          d	          S t          j                    t          j                    d
t          j                    j         dt          j                    j         ddt          dt$          t          t&          f         dt(          t          t$          t          t&          f         f         ffddt          dt$          t          t&          f         ddffddt          ddffdd+dt          dt          dt          ddffdd, f
d}t          j         |                      }	  j                            |           n# t0          $ r Y nw xY wt3          |d          r|                     j        j                   dddd}	r|	d<   t          j        d|	           }
|
                    |           d{V  t=          j                    }	 	 	 t          j                                         tB          "           d{V }nC# t          j"        $ r1 |
#                    d#           d{V  t=          j                    }Y uw xY w|nd|\  }}tI          j%        |d$%          }|
#                    d&| d'| d(&                    d)                     d{V  t=          j                    }n\# t          j'        tP          f$ r |)                                  tT          $ r%}tV          ,                    d*|           Y d}~nd}~ww xY w|
S )-uL   POST /api/sessions/{session_id}/chat/stream — SSE wrapper over _run_agent.Nr  r  r  r  r  r   r   r   msg_run_r   r   r  r   c                     dz  |                     d           |                     d           |                     d           |                     dt          j                               | |fS )NrC   r  run_idseqr  )r
  r   )r   r  r  r  r  s     r   _event_payloadzDAPIServerAdapter._handle_session_chat_stream.<locals>._event_payloadJ  st    1HC|Z888x000uc***tTY[[111= r7   c                      | |          }	 t          j                    }n# t          $ r d }Y nw xY w	 |u r                    |           d S                     j        |           d S # t          $ r Y d S w xY wrE   )r1  get_running_loopRuntimeError
put_nowaitcall_soon_threadsafe)r   r  eventrunning_loopr  loopqueues       r   _enqueuez>APIServerAdapter._handle_session_chat_stream.<locals>._enqueueS  s    "N411E$&799 $ $ $#$4''$$U+++++--e.>FFFFF   s#   # 22A. A. .
A<;A<deltac                 .    | r d| d           d S d S )Nzassistant.delta)
message_idr  r   )r  r  r  s    r   _deltaz<APIServerAdapter._handle_session_chat_stream.<locals>._deltaa  s:     X*:PU,V,VWWWWWX Xr7   
event_typer  r}  c                     | dk    r d|pd|pdd           d S | dv r)|                      dd          } ||||d           d S d S )	Nreasoning.availableztool.progress	_thinkingr>   )r  r  r  >   tool.failedtool.startedtool.completedztool.)r  r  r}  args)r  )r	  r  r}  r  kwargs
event_namer  r  s         r   _tool_progresszDAPIServerAdapter._handle_session_chat_stream.<locals>._tool_progresse  s    222R[Rj_ju|  vC  AC  +D  +D  E  E  E  E  EPPP'//AA
JYcjtx%y%yzzzzz QPr7   c                    
K   	                       ddddi                     d {V                        ddddi                     d {V                                }                     | 	
	           d {V \  }}t          |t                    r|                    d
d          nd}t          |t                    r|                    d          n}t          |t                    r                    | |          ng }                      d||dddd                     d {V                        d|d||d                     d {V  ne# t          $ rX}t          	                    d                                 ddt          |          i                     d {V  Y d }~nd }~ww xY w                      di                      d {V                       d            d {V  d S #                       di                      d {V                       d            d {V  w xY w)Nzrun.startedr   userr  zmessage.startedr   r  )r  r  )r   r  r  r  r  r  r  r  r>   r  zassistant.completedTF)r  r  r<   	completedpartialinterruptedrun.completed)r  r  r  r  r  z'[api_server] session chat stream failedr   done)r   r  r  r-   rP   rQ   _turn_transcript_messagesr   r   rV  r/   )r  rZ   r  r  r  turn_messagesrz   r  r  r  r  r  r  r   r  rC  r   s          r   _run_and_signalzEAPIServerAdapter._handle_session_chat_stream.<locals>._run_and_signall  s     $&ii}~X^kwOxOx>y z z{{{{{{{{{ii/@9U_itNuNuBv w wxxxxxxxxx@@LL&*oo!-)0,9)*0+9(; '6 ' ' ! ! ! ! ! ! FPPVX\E]E]!e,<b!A!A!AceOYZ`bfOgOg'wvzz,
'K'K'Kmw$aklrtxayay  !B > >wV\ ] ] ]  @Bii/D"6",-!%$#(G G ! !          ii"6",!% -"A A ! !            P P P  !JKKKiiwCHH8M N NOOOOOOOOOOOOOOP iivr : :;;;;;;;;;iioo%%%%%%%%% iivr : :;;;;;;;;;iioo%%%%%%%%s2   E-E3 2H 3
G=AGH GH AIr3  text/event-streamno-cacher)   )Content-TypeCache-ControlX-Accel-Bufferingr  r  r   r   Ttimeout   : keepalive

F)ensure_asciievent: 
data: 

r<  z)[api_server] session SSE stream error: %sNNNr   )-r  r  r  r  r  r   rQ   r-   r/   r   r   r   r1  r  Queuer  r  r  r   r   r~  r2  _background_tasksaddr!   hasattrr3  discardStreamResponsepreparer   	monotonicwait_for&CHAT_COMPLETIONS_SSE_KEEPALIVE_SECONDSTimeoutErrorwriter   r   r?  CancelledErrorConnectionResetErrorcancelr   r   r   )r   r   r  r  r   r  r   r  r5  r   r   
last_writerW   r   r  r   rz   r  r  r  r  r  r  r  r  r  r  r  rC  r   s   `                @@@@@@@@@@@@@r   _handle_session_chat_streamz,APIServerAdapter._handle_session_chat_stream.  s     ##G,, 	O'+'E'Eg'N'N$WN'5
22:>>3 	J..w77777777	c 	J6t<<c?J!122Ndhh~6N6N$Zs-K-K$$]3T[s%t%t%t  ~A  B  B  B  B'))GN}.DJLL,..
*
(**	! 	!tCH~ 	!%TRUWZRZ^H[B\ 	! 	! 	! 	! 	! 	! 	! 	!	3 	c3h 	D 	 	 	 	 	 	 	 		X# 	X$ 	X 	X 	X 	X 	X 	X 	X	{ 	{s 	{s 	{C 	{pt 	{ 	{ 	{ 	{ 	{ 	{ 	{%	& %	& %	& %	& %	& %	& %	& %	& %	& %	& %	& %	& %	& %	& %	&N "??#4#455	"&&t,,,, 	 	 	D	4,-- 	C""4#9#ABBB 0'!%#-	
 
  	B.AG*+%S'BBBw'''''''''^%%
	K.!(!1%))++Gm!n!n!nnnnnnnDD+   "..);<<<<<<<<<!%!1!1JH < $gz'>>>nn%Gt%G%GT%G%G%G%N%Nw%W%WXXXXXXXXX!^--
. 
 &(<= 	 	 	KKMMM 	K 	K 	KLLDcJJJJJJJJ	KsO   I 
I+*I+0O 23L& %O &=M&#O %M&&A*O 4P*P%%P*c                    *+,-./0K                         |          }|r|S 	 |                                 d{V }n?# t          j        t          f$ r& t	          j        t          d          d          cY S w xY w|                    d          }|rt          |t                    st	          j        dddd	id          S t          |                    d
          d          }d/g }t          |          D ]\  }}|                    dd          }	|                    dd          }
|	dk    rt          |
          }/|/K/dz   |z   /T|	dv rW	 t          |
          }n.# t          $ r!}t          |d| d          cY d}~c S d}~ww xY w|                    |	|d           d0g -|r&|d                             dd          0|dd         -t#          0          st	          j        dddd	id          S                      |          \  ,}||S |j                            dd                                          }|r؉ j        s=t,                              d           t	          j        t          d          d          S t1          j        d|          rt	          j        dddd	id          S |.	                                  }||                    .          -n}# t          $ r(}t,                              d.|           g -Y d}~nPd}~ww xY wd}|D ]3}|                    d          d k    r|                    dd          } n4t9          /|          .d!t;          j                    j        dd"          }|                    d# j                   }tC          tE          j"                              }|rd$dl#}|$                                ++fd%}tK                      **+fd&}*+fd'}dg}tM          j'         (                    0-/.||||,(	  	                  }|)                    +fd)            *                    ||||+||.,*	  	         d{V S ,- ./0fd+}|j                            d,          }|rtW          |g d-.          }	 tX          -                    |||           d{V \  }} n# t          $ rO}t,          .                    d/|d01           t	          j        t          d2| d34          d5          cY d}~S d}~ww xY w	  |             d{V \  }} n\# t          $ rO}t,          .                    d/|d01           t	          j        t          d2| d34          d5          cY d}~S d}~ww xY w|                    d6          pd}!t_          |                    d7                    }"t_          |                    d8                    }#t_          |                    d9d0                    }$|                    d          }%|"r|%rd:|%0                                v rd;}&n|#s|$s|%rd}&nd<}&d|                    d=.          i}',r,|'d><   |!sL|#s|"rHt          |%pd?d3d@A          }(|$|"|#dB|(d         dC<   dD|'dE<   |"rdFndD|'dG<   t	          j        |(dH|'I          S |dJ||d$dK|!d|&dLg|                     dMd$          |                     dNd$          |                     dOd$          dPdQ})|"s|#s|$s0|$|"|#|%|&d;k    rdRndSdT|)dC<   dD|'dE<   |"rdFndD|'dG<   |%r|%ddU         |'dV<   t	          j        |)|'W          S )Xu=   POST /v1/chat/completions — OpenAI Chat Completions format.Nr  r   r   r  r   z#Missing or invalid 'messages' fieldr   r  streamFr   r  r>   r<   systemrK   >   r  r  z	messages[	].contentr   r  z!No user message found in messagesr  zSession continuation via X-Hermes-Session-Id rejected: no API key configured.  Set API_SERVER_KEY to enable session continuity.zfSession continuation requires API key authentication. Configure API_SERVER_KEY to enable this feature.r   r  r  r  r  z	chatcmpl-   r  r   c                 :    |                      |            d S d S rE   r   r  	_stream_qs    r   	_on_deltaz<APIServerAdapter._handle_chat_completions.<locals>._on_delta5  s+     $MM%((((( %$r7   c           	          | r|                     d          rdS                     |            ddlm}m}  |||          p|}                    d| ||          || ddf           dS )u  Emit ``hermes.tool.progress`` with ``status: running``.

                Replaces the old ``tool_progress_callback("tool.started",
                ...)`` emit so SSE consumers receive a single event per
                tool start, carrying both the legacy ``tool``/``emoji``/
                ``label`` payload (for #6972 frontends) and the new
                ``toolCallId``/``status`` correlation fields (#16588).

                Skips tools whose names start with ``_`` so internal
                events (``_thinking``, …) stay off the wire — matching
                the prior ``_on_tool_progress`` filter exactly.
                r   Nr   )build_tool_previewget_tool_emoji__tool_progress__running)toolemojir^  
toolCallIdr   )ri   r-  agent.displayrH  rI  r   )r  function_namefunction_argsrH  rI  r^  _started_tool_call_idsrE  s         r   _on_tool_startzAAPIServerAdapter._handle_chat_completions.<locals>._on_tool_startF  s     $ }'?'?'D'D F&**<888LLLLLLLL**=-HHYM2)+^M::"".'5 5      r7   c                 x    | r| vrdS                      |                                d|| ddf           dS )a  Emit the matching ``status: completed`` event.

                Dropped if the start was filtered (internal tool, missing
                id, or never seen) so clients never get an orphaned
                ``completed`` they can't correlate to a prior ``running``.
                NrJ  r  )rL  rN  r   )r/  r   )r  rP  rQ  function_resultrR  rE  s       r   _on_tool_completezDAPIServerAdapter._handle_chat_completions.<locals>._on_tool_complete`  sl     $ |;Q'Q'QF&..|<<<2)".)5 5      r7   )	r   r  r  r  r  r  r  	agent_refr  c                 .                         d           S rE   rC  _futrE  s    r   <lambda>z;APIServerAdapter._handle_chat_completions.<locals>.<lambda>      immD6I6I r7   )r  r  c                  H   K                                    d {V S Nr  r  )r  r  r   r  rC  r   s   r   _compute_completionzFAPIServerAdapter._handle_chat_completions.<locals>._compute_completion  sO      )%,(5%$7 )         r7   Idempotency-Key)r  r  ra  tool_choicer=  r7  z,Error running agent for chat completions: %sTr   Internal server error: rS  rT  r  r  r  failedr  truncatlengthstopr  r  z%Agent run did not produce a response.agent_incomplete)r   r   )r  r  re  r  r+   zX-Hermes-Completedr'   zX-Hermes-Partiali  r   zchat.completionr  )indexr   r  rs  rt  total_tokensprompt_tokenscompletion_tokensrk  r  r  r  r  choicesr  output_truncatedagent_error)r  r  re  r   
error_coder   zX-Hermes-Errorr  )1r  r   r   r   r   r   r   rQ   r-   rM   r6   	enumeraterR   rv   r"   r   rO   ry   r  r   r0   ri  r   r   r  r  r  r  rI  r  r  r  rm  r    r   r  r+  r  r1  ensure_futurer  r3  _write_sse_chat_completionrB  _idem_cacher6  r   r.   r1   )1r   r   r  r   r  r=  conversation_messagesidxmsgr  raw_contentr<   rz   r  provided_session_idr  r  
first_usercmcompletion_idr^  r  _qrF  rS  rV  rW  
agent_taskr`  idempotency_keyr&  rZ   r  r  
is_partial	is_failedr  err_msgr  response_headerserr_bodyresponse_datarR  rE  r  r  r  rC  r   s1   `                                         @@@@@@@r   _handle_chat_completionsz)APIServerAdapter._handle_chat_completions  s
     ##G,, 	O	` ''''''DD$i0 	` 	` 	`$]3Q%R%R[^______	` 88J'' 	z(D99 	$&KUlmmn   
 &dhhx&8&8%HHH 68!(++ 	Q 	QHC7762&&D'')R00Kx 2+>> ($+MM$1D$87$BMM..._;KHHGG! _ _ _7C]sC]C]C]^^^^^^^^^^^_%,,dw-O-OPPP   	10488BGGL+CRC0G+L99 	$&ISjkkl    (,'E'Eg'N'N$WN &o112GLLRRTT &	L= *  
 (!K      y(;<< (*>H_``a    -J,,..> ==jIIG   JJXYZZZ J+  66&>>V++!#	2!6!6JE , 1
KKJ <DJLL$4SbS$9;;XXgt'788
dikk"" [	"$((**I	) 	) 	) 	) 	) 03uu"     4     0 I .t)%,(5%&/$2'8#$7 0? 
0 
0 
 
J (()I)I)I)IJJJ88
GYI*$7 9         	 	 	 	 	 	 	 	 	 	 "/--.?@@ 	*46m6m6mnnnB&1&<&<_bRe&f&f f f f f f f   KQY]^^^(!"?A"?"?.YYY        &9&9&;&; ; ; ; ; ; ;   KQY]^^^(!"?A"?"?.YYY          $455;&**Y//00
H--..	K6677	**W%%
  	#' 	#i7==??&B&B$MM 	#y 	#W 	##MM"M "6::lJ#G#G
  	K7J34
  	U9 	U
 	U$BB''  H '%#+ +HWh'
 6=12=G3T66W/0$XcCSTTTT  '  +#1    &3 	 "'>1!=!=%*YY%B%B %		.! < < 
 
*  	C 	C) 	C&%# 4AX4M4M00S`' 'M(# 6=12=G3T66W/0 C5<TcT] !12 8HIIIIs   A   9A<;A<E&&
F0FFF+K1 1
L#;LL#.%S 
T-AT("T-(T-1U 
VAVVVr  r  r  c
                 4	  K   ddl }
dddd}|j                            dd          }|r|                     |          nd}|r|                    |           |r||d	<   |	r|	|d
<   t          j        d|                              |           d{V  	 t          j	                    }ddddiddgd}
                    dt          j        |           d                                           d{V  t          j	                    }fd}t          j                    }	 	 |                    dfd           d{V }n# |
j        $ r |                                r>	 	                                 }|n# ||           d{V }n# |
j        $ r Y nw xY w;Y nht          j	                    |z
  t(          k    r.
                    d           d{V  t          j	                    }Y w xY w|n ||           d{V }dddd}	 | d{V \  }}|p|}n3# t*          $ r&}t,                              d|           Y d}~nd}~ww xY wddi ddg|                    dd          |                    dd          |                    dd          dd}
                    dt          j        |           d                                           d{V  
                    d            d{V  n# t0          t2          t4          t6          f$ r |r|d         nd}|'	 |                    d!           n# t*          $ r Y nw xY w|                                s:|                                 	 | d{V  n# t          j        t*          f$ r Y nw xY wt,                              d"           Y nt*          $ r}ddl }t,          !                    d#|"                                dd$                    	 ddi d%dgd}
                    dt          j        |           d                                           d{V  
                    d            d{V  n# t*          $ r Y nw xY wY d}~nd}~ww xY wS )&a,  Write real streaming SSE from agent's stream_delta_callback queue.

        If the client disconnects mid-stream (network drop, browser tab close),
        the agent is interrupted via ``agent.interrupt()`` so it stops making
        LLM API calls, and the asyncio task wrapper is cancelled.
        r   Nr  r  r)   r   r!  r"  r   r>   r  r  r   r   chat.completion.chunkr  r  rj  r  r  r  r  r  r  rp  data: r)  c                   K   t          | t                    rkt          |           dk    rX| d         dk    rLt          j        | d                   }                    d| d                                           d{V  nRddd	| idd
gd}                    dt          j        |           d                                           d{V  t          j                    S )a  Write a single queue item to the SSE stream.

                Plain strings are sent as normal ``delta.content`` chunks.
                Tagged tuples ``("__tool_progress__", payload)`` are sent
                as a custom ``event: hermes.tool.progress`` SSE event so
                frontends can display them without storing the markers in
                conversation history.  See #6972 for the original event,
                #16588 for the ``toolCallId``/``status`` lifecycle fields.
                   r   rJ  rC   z"event: hermes.tool.progress
data: r)  Nr  r<   r  r  r  )	r-   r~  rF   r   r   r6  r?  r   r2  )rW   
event_datacontent_chunkr  r  r  r   s      r   _emitz:APIServerAdapter._write_sse_chat_completion.<locals>._emit(  s$      dE** \s4yyA~~$q'M`B`B`!%DG!4!4J"..NjNNNUUWW         
 ,7N#*U./9d:K^b$c$c#d% %M
 #..)Q$*]2K2K)Q)Q)Q)X)X)Z)Z[[[[[[[[[~'''r7   Tc                  0                          d          S Ng      ?r#  re   stream_qs   r   r[  z=APIServerAdapter._write_sse_chat_completion.<locals>.<lambda>D  s    X\\Z]\E^E^ r7   r%  rs  rt  rk  z)Agent task %s failed, usage data lost: %srh  rs  rt  rk  rl  ro  s   data: [DONE]

SSE client disconnected2SSE client disconnected; interrupted agent task %s#Agent crashed mid-stream for %s: %sr  r   )#r  r   rQ   r   r   r   r0  r1  r   r2  r6  r   r   r?  r1  r  run_in_executorEmptyr  
get_nowaitr4  r   r   r   r8  ConnectionAbortedErrorBrokenPipeErrorr   	interruptr9  r7  info	tracebackr   
format_exc)r   r   r  r  r  r  r  rW  r  r  r  sse_headersr   r&  last_activity
role_chunkr  r  r  r  rZ   agent_usagerz   finish_chunkr  _exc_tberror_chunkr   s     ````                      @r   rv  z+APIServerAdapter._write_sse_chat_completion  s      	 0'!%
 
 $$Xr228>Ht,,V444D 	%t$$$ 	<1;K-. 	F2EK./%S+FFFw'''''''''u	 N,,M $/F"U&'6;2GZ^__` J
 ..!F$*Z*@*@!F!F!F!M!M!O!OPPPPPPPPP N,,M( ( ( ( ( ( ( (2 +--D3"&"6"6t=^=^=^=^"_"_______EEx   !(( 
&&(0(;(;(=(=#(=$)6;eEll0B0B0B0B0B0B#%8 & & & %&& ~''-7;aaa&nn-?@@@@@@@@@(,(8(8H" =&+eEll 2 2 2 2 2 2/34 &'ANNE`,6&6&6&6&6&6&6##,u ` ` `JM[^________`
 $/F"U&'"vNNO%*YY~q%A%A).?A)F)F$)IIna$@$@ 		 	L ..!H$*\*B*B!H!H!H!O!O!Q!QRRRRRRRRR..!45555555555$&<owW 	] 	] 	] %.7IaLL4E OO$=>>>>    D??$$ !!###$$$$$$$$$.	:   DKKLm\\\\\ 	 	 	 $###LL>s~~O_O_`dad`dOefff	'3J&*+b7 S ST 
 nn%Kdj.E.E%K%K%K%R%R%T%TUUUUUUUUUnn%89999999999   	" s  BL 2E L G>2FG>	FG>
F(%G>'F((G>,L .AG>;L =G>>L H- ,L -
I7IL IB/L +R:MR
MRM+R	NRN+(R*N++R	R:RA+Q;:R;
RRRRRr   
created_atr  r   r  conversationstorec                    	
-./0123456789:;<=>?@ABCDEK   ddl }dddd}|j                            dd          }|r                     |          nd}|r|                    |           r|d	<   |r||d
<   t          j        d|          BB                    |           d{V  g <g Ag :d@d9dCdt          j	                    j
        dd          =d?d>dt          dt          t          t          f         ddfBCfd8dt          dt          t          t          f         ffd4d;d}ddddEdDdddt          t          t          f         dt          t          t          t          t          f                           ddf
 	fd7d[47:;<DE	f
d}	  4d          }g |d<    8dd|d           d{V   7|           t!          j                    }d[8=>?@fd 6d!t          ddf68<=?fd"1d#t          t          t          f         dt          f89:@Afd$3d#t          t          t          f         ddf8:@Afd%2d[-.0235fd&}g -d0t%          j                    /d't(          ddf-05fd(.d[-/1fd)5t%          j                    }	 	 |                    dfd+           d{V }n# |j        $ r |                                rQ	 	                                 }|n6 ||           d{V  t!          j                    }n# |j        $ r Y nw xY wNY nt!          j                    |z
  t4          k    r.B                    d,           d{V  t!          j                    }Y w xY w|?0r*0                                s0                                 d0-r 5             d{V  n& ||           d{V  t!          j                    }J-r 5             d{V  	 | d{V \  }}|pEEt;          |t<                    r|                    d-d          nd}|r<s 1|           d{V  |r;s|;t;          |t<                    r|                    d.          r
;s|d.         }nC# t>          $ r6}t@          !                    d/|d*0           t          |          }Y d}~nd}~ww xY wd"                    <          p;;>r; 8d1d1=?d;g d2           d{V  =d3d4d5d6;d7gd8} 8d9d9?|d:           d{V  tG          :          }|D ]} |                     d;          d<k    r	 t;          |                     d=          t                    r(tI          j%        |                     d=d>                    n|                     d=i           }!t;          |!t<                    rd?D ]n}"t;          |!                    |"          t                    rDtM          |!|"                   d@k    r+dAt          tM          |!|"                             z   dBz   |!|"<   otI          j'        |!          | d=<   ## t>          $ r Y 0w xY w|                     d;          dCk    r|                     dg           }#t;          |#tF                    r|#r|#d         }$t;          |$t<                    r{|$                    d;          dDk    rb|$                    dEd          }%tM          |%          dFk    r9|%dd@         dGz   t          tM          |%          d@z
            z   dHz   |$dE<   |$g| d<   |(                    d3d5d6;p|pdd7gdI           |r 4dJ          }&||&d<   |dKdL|&d.<   E                    dMd          E                    dNd          E                    dOd          d|&dP<   tG                    }'|'(                    dQ	dR           ;s|r|'(                    d5;p|dR            7|&|'           d*D 8dSdS|&d           d{V  n 4d4          }(||(d<   E                    dMd          E                    dNd          E                    dOd          d|(dP<    )                    	|;          }) 7|(|)           d*D 8dTdT|(d           d{V  ni# tT          tV          tX          tZ          f$ r  |             |r|d         nd}*|*'	 |*.                    dU           n# t>          $ r Y nw xY w|                                s:|                                 	 | d{V  n# t$          j/        t>          f$ r Y nw xY wt@          0                    dV           Y nt$          j/        $ r  |             |r|d         nd}*|*'	 |*.                    dW           n# t>          $ r Y nw xY w|                                s|                                 t@          0                    dX            t>          $ r}+ddl1}, |             |,2                                }	  4dJ          }&tG          :          |&d<   t          |+          dd@         dKdL|&d.<   E                    dMd          E                    dNd          E                    dOd          d|&dP<    8dSdS|&d           d{V  n# t>          $ r Y nw xY wt@          !                    dYt          |          ddZ                    Y d}+~+nd}+~+ww xY wBS )\u  Write an SSE stream for POST /v1/responses (OpenAI Responses API).

        Emits spec-compliant event types as the agent runs:

        - ``response.created`` — initial envelope (status=in_progress)
        - ``response.output_text.delta`` / ``response.output_text.done`` —
          streamed assistant text
        - ``response.output_item.added`` / ``response.output_item.done``
          with ``item.type == "function_call"`` — when the agent invokes a
          tool (both events fire; the ``done`` event carries the finalized
          ``arguments`` string)
        - ``response.output_item.added`` with
          ``item.type == "function_call_output"`` — tool result with
          ``{call_id, output, status}``
        - ``response.completed`` — terminal event carrying the full
          response object with all output items + usage (same payload
          shape as the non-streaming path for parity)
        - ``response.failed`` — terminal event on agent error

        If the client disconnects mid-stream, ``agent.interrupt()`` is
        called so the agent stops issuing upstream LLM calls, then the
        asyncio task is cancelled.  When ``store=True`` an initial
        ``in_progress`` snapshot is persisted immediately after
        ``response.created`` and disconnects update it to an
        ``incomplete`` snapshot so GET /v1/responses/{id} and
        ``previous_response_id`` chaining still have something to
        recover from.
        r   Nr  r  r)   r  r   r>   r  r  r   r   r     Fr	  r   r   c                    K   d|vr|d<   dz  d|  dt          j        |           d}                    |                                           d {V  d S )Nsequence_numberrC   r'  r(  r)  )r   r   r6  r?  )r	  r   r  r   r  s      r   _write_eventz;APIServerAdapter._write_sse_responses.<locals>._write_event  s}       ,,*9&'q OJ
JJDJt4D4DJJJG..!1!122222222222r7   r   c                     d| d}|S )Nr   )r  r  r   r  r  r   )r   envr  r  r   s     r   	_envelopez8APIServerAdapter._write_sse_responses.<locals>._envelope  s%    !$ (# #C Jr7   r  conversation_history_snapshotresponse_envr  c                    sd S |'t                    }|                    d	d           j                            | |d           rj                                       d S d S )Nr  r  r   r  r  r  )rM   rO   rq  r   r   )
r  r  r  r  r  r   r   r  r  r   s
     r   _persist_response_snapshotzIAPIServerAdapter._write_sse_responses.<locals>._persist_response_snapshot	  s    
  ,4045I0J0J--44fQ]5^5^___ $$[((E ,(	3 3     Q$55lKPPPPPQ Qr7   c                    
 
rrdS d                     	          p} t                    }| r|                    ddd| dgd            d          }||d	<                       d
d                              dd                              dd          d|d<   t                    }|                    dd           | r|                    d| d            ||           dS )ak  Persist an ``incomplete`` snapshot if no terminal one was written.

            Called from both the client-disconnect (``ConnectionResetError``)
            and server-cancellation (``asyncio.CancelledError``) paths so
            GET /v1/responses/{id} and ``previous_response_id`` chaining keep
            working after abrupt stream termination.
            Nr>   r   r  rB   r`   r?   r  r<   
incompleteoutputrs  r   rt  rk  r  r  r  r  r  )rT   rM   rO   rQ   )incomplete_textincomplete_itemsincomplete_envincomplete_historyr  r  r  emitted_itemsfinal_response_textfinal_text_partsr  terminal_snapshot_persistedr  r   s       r   _persist_incomplete_if_neededzLAPIServerAdapter._write_sse_responses.<locals>._persist_incomplete_if_needed	  sY     7  gg&677N;NO59-5H5H  ''%')6 P PQ) )   
 'Y|44N'7N8$ %		.! < <!&?A!>!> %		.! < <' 'N7#
 "&&:!;!;%%v,&O&OPPP ]"));?*[*[\\\&&.@     r7   in_progressr  zresponse.created)r?   r   c                  b   K   rdS ddz  dddg d}  dd| d	           d{V  dS )
zpEmit response.output_item.added for the assistant message
                the first time any text delta arrives.NTrC   r   r  r  r  r?   r   r  r<   response.output_item.addedr?   output_indexrW   r   )rW   r  message_item_idmessage_openedmessage_output_indexr  s    r   _open_message_itemzAAPIServerAdapter._write_sse_responses.<locals>._open_message_itemA	  s       " F!%'3$!)%+'!  #l#?8$8 B B           r7   
delta_textc           
         K                 d {V                       |             ddd| g d           d {V  d S )Nzresponse.output_text.deltar   )r?   item_idr  content_indexr  logprobs)rO   )r  r  r  r  r  r  s    r   _emit_text_deltaz?APIServerAdapter._write_sse_responses.<locals>._emit_text_deltaW	  s      ((********* ''
333"l#?8.$8%&' "B B           r7   r  c                 z  K   dz  |                      d          pddd          d }|                      di           }t          |t                    rt          j        |          }nt          |          }dt          j                    j        dd	          d
d|                      dd          ||d}	}	dz  	
	                    ||                      dd          ||d         |d           	                    d
|                      dd          ||d            dd||d           d{V  |S )aX  Emit response.output_item.added for a function_call.

                Returns the call_id so the matching completion event can
                reference it.  Prefer the real ``tool_call_id`` from the
                agent when available; fall back to a generated call id for
                safety in tests or older code paths.
                rC   r  call_   Nr   	argumentsfc_r  function_callr  r   r>   r  r?   r   r   call_idr  r  )r  r   r  r  r  r?   r   r  r  r  r  )
rQ   r-   rP   r   r   r/   r  r  r  rO   )r  r  r  arguments_strrW   ry  r  call_counterr  r  pending_tool_callsr   s         r   _emit_tool_startedzAAPIServerAdapter._write_sse_responses.<locals>._emit_tool_startedc	  s      !!++n55a9aQRR9a9aS_9a9a{{;33dD)) .$(Jt$4$4MM$'IIM7
 0" 577++#KK33&!.  #!"))&#KK33!.#Dz$'+ +    $$+#KK33!.&	& &    #l#?8$' B B         
 r7   c                   K   |                      d          }|                      dd          }d}|r8t                    D ](\  }}|d         |k    r                    |          } n)|dS |d         dd|d	         |d         |d
         d} dd|d         |d           d{V  t          |t                    r|nt          j        |          }d|dg}dt          j                    j	        dd          d|d         |dd}	}
dz  
                    d|d         |d            dd|
|	d           d{V   dd|
|	d           d{V  dS )z}Emit response.output_item.done (function_call) followed
                by response.output_item.added (function_call_output).r  rZ   r>   Nr  r  r  r  r   r  r  response.output_item.doner  r  rA   r`   fco_r  function_call_output)r  r?   r  r  r   rC   r?   r  r  r  )rQ   rt  r  r-   r/   r   r   r  r  r  rO   )r  r  rZ   pendingirI   	done_item
result_stroutput_partsoutput_itemry  r  r  r  r  s              r   _emit_tool_completedzCAPIServerAdapter._write_sse_responses.<locals>._emit_tool_completed	  se      "++n55 Xr22 " )*< = = " "1Y<722&8&<&<Q&?&?G!E 3 ? F "),+)#FO&y1!(!5 	 #l#>7$+N$;%A A          (2&#'>'>VVVDJvDVDV
)5z J JK8!1#2#!6882&y1*)  #!$$2&y1*& &   
 #l#?8$''B B         
 #l#>7$''A A           r7   c                   K   t          | t                    ryt          |           dk    rft          | d         t                    rK| \  }}r              d{V  |dk    r |           d{V  dS |dk    r |           d{V  dS dS t          | t                    r6                    |            !t          j         d                    dS dS dS )u|  Route a queue item to the correct SSE emitter.

                Plain strings are text deltas — they are batched (50ms)
                to reduce Open WebUI re-render storms.  Tagged tuples
                with ``__tool_started__`` / ``__tool_completed__``
                prefixes are tool lifecycle events and flush the buffer
                before emitting.
                r  r   N__tool_started____tool_completed__皙?)r-   r~  rF   r/   rO   r1  r2  )	ittagr  
_batch_buf_batch_flush_after_batch_timerr  r  _flush_batchs	      r   	_dispatchz8APIServerAdapter._write_sse_responses.<locals>._dispatch	  s?      b%(( USWW\\jAPS>T>T\#%LC! -*lnn,,,,,,,0000099999999999 444227;;;;;;;;;;; 54C(( U%%b)))#+'.':;M;Md;S;S'T'T	U U ,+r7   delayc                    K   	 t          j        |            d{V  n# t           j        $ r Y dS w xY wd              d{V  dS )z7Wait delay seconds, then flush accumulated text deltas.N)r1  sleepr7  )r   r  r  r  s    r   r  zAAPIServerAdapter._write_sse_responses.<locals>._batch_flush_after	  s      !-..........-   FF
  $"lnn$$$$$$$$$s     33c                     K   4 d{V  r(d                               } g  |            d{V  ddd          d{V  dS # 1 d{V swxY w Y   dS )z1Emit a single SSE delta for all accumulated text.Nr>   )rT   )combinedr  _batch_lockr  s    r   r  z;APIServerAdapter._write_sse_responses.<locals>._flush_batch	  s      ' 9 9 9 9 9 9 9 9! 9#%77:#6#6%'
..x888888888	9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9s   +A


AATc                  0                          d          S r  re   r  s   r   r[  z7APIServerAdapter._write_sse_responses.<locals>.<lambda>
  s    HLLY\LD]D] r7   r%  r  r   z/Error running agent for streaming responses: %sr   zresponse.output_text.done)r?   r  r  r  r@   r  r   r  r  rB   r`   r  r  r  r?   r  r  z{})r<   r  pattern
old_string
new_stringr  [u,    chars — truncated for response.completed]r  rA   r@   r   z...[z more chars]r  re  rS  r  rs  rt  rk  r  r  r  zresponse.failedzresponse.completedr  r  zSSE task cancelledz8SSE task cancelled; persisted incomplete snapshot for %sr  r  r   )3r  r   rQ   r   r   r   r0  r1  r  r  r  r/   r   r   r   r   r   r2  r1  Lockr4   r  r  r  r  r  r4  r6  r9  r-   rP   r   r   r   rT   rM   r   r   rF   r   rO   $_build_response_conversation_historyr8  r  r  r   r  r7  r  r  r  )Fr   r   r   r  r  r  r  rW  r  r   r  r  r  r  r  r  r  r   r&  rr  r  created_envr  r  r  rW   rZ   r  agent_finalr  msg_done_itemfinal_items_item_args_k_output_first_text
failed_env_failed_historycompleted_envfull_historyr  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  sF   ` ````  ``````                               @@@@@@@@@@@@@@@@@@@@@@@@@r   _write_sse_responsesz%APIServerAdapter._write_sse_responses  sO     Z 	 0'!%
 

 $$Xr228>Ht,,V444D 	%t$$$ 	<1;K-. 	F2EK./%S+FFFw''''''''' ') 46 /1   9!1#2#!688.2	33 	3d38n 	3 	3 	3 	3 	3 	3 	3 	3	c 	d38n 	 	 	 	 	 	 	 	 !%)12QXY Z Z&+#
 MQ	Q 	Q 	QsCx.	Q ,4Dc3h4H+I	Q 		Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q 	Q& 	  	  	  	  	  	  	  	  	  	  	  	  	  	  	Dn	e#)M22K$&K!,1*'4 4          '&{333 N,,M         ,
3 
4 
 
 
 
 
 
 
 
 
 
,$sCx. ,S , , , , , , , , , , ,\:DcN :t : : : : : : : : :zU U U U U U U U U U U6 %'J37L!,..K
% 
%$ 
% 
% 
% 
% 
% 
% 
% 
%9 9 9 9 9 9 9 9 +--D1!%!5!5d<]<]<]<]!^!^^^^^^^DDx   !(( &&'/':':'<'<#'<$)&/ioo 5 5 5 5 5 5 5040@0@#%8 & & & %&& ~''-7;aaa&nn-?@@@@@@@@@(,(8(8H!$ <# ,L,=,=,?,? ,$++---'+! -*lnn,,,,,,,ioo%%%%%%% $ 0 0?1D  %"lnn$$$$$$$%,6&6&6&6&6&6&6##,u
 CMVUYBZBZbfjj)92>>>`b 8'7 8**;777777777 6': 6*5'fd++ 2

70C0C 2L_ 2"(/K % % %NPQ\`aaa!!ff%
 #%''*:";";"R?R "l#>7.$8%&/ "A A          *%)'!.8KLL ! ! #l#>7$8)A A          15]0C0CK
 % ; ;99V$$77LVW\W`W`alWmWmorLsLs  !T
599[$+G+G H H Hy~  zC  zC  DO  QS  zT  zT%eT22 C&a { {#-eiimmS#A#A !{c%PR)nnWZFZFZ03c#eBi..6I6I0ILz0zE"I15E1B1BE+.$   YYv&&*@@@#ii"55G!'400 ;W ;!(%fd33 ;

68J8Jl8Z8Z$*JJvr$:$:E"5zzD0016ttv1ECPUJJY\L\H]H]1]`n1nv39(h!#*4G4^KL][]__        /&Yx00
'2
8$2=~&V&V
7#$)IIna$@$@%*YY%B%B$)IIna$@$@' '
7#
 #'';"<"<&&<'P'PQQQ& + #** +#6#E+, ,    +*2A    /3+"l#4- *7 7          
 !*	+ 6 6*5h'$)IIna$@$@%*YY%B%B$)IIna$@$@* *g&
  $HH( '	    +*!2>    /3+"l#70 -: :         
 %&<owW 	[ 	[ 	[))+++ %.7IaLL4E OO$=>>>>    D??$$ !!###$$$$$$$$$.	:   DKKLkZZZZZ% 	 	 	
 *)+++$-7IaLL4E OO$89999    D??$$ $!!###KKRT_``` 	e 	e 	e
 $###))+++..**K&Yx00
'+M':':
8$25d))DSD/>&Z&Z
7#$)IIna$@$@%*YY%B%B$)IIna$@$@' '
7#
 #l#4- *7 7              LL>SQ\M]M]^b_b^bMcdddddddd/	e2 se  5C>c 4K c N4L0
N$L0/N0
L=:N<L==Nc ANc NA<c BR c 
S&,Sc SBc "DY'%c '
Y51c 4Y55Ic 5l;d! l;!
d.+l;-d..+l;e#"l;#e<9l;;e<<l;&l;gl;
g'$l;&g''Al;8"l6Bk0/l60
k=:l6<k==4l66l;c                    -./012K                         |          }|r|S                      |          \  /}||S 	 |                                 d{V }n7# t          j        t          f$ r t          j        ddddid          cY S w xY w|                    d          }|#t          j        t          d	          d          S |                    d
          0|                    d          }|                    d          }t          |                    d          d          }|r%|r#t          j        t          d          d          S |r j
                            |          }g }	t          |t                    rd|dg}	nt          |t                    rt          |          D ]\  }
}t          |t                    r|	                    d|d           3t          |t"                    r|                    dd          }	 t%          |                    dd                    }n.# t&          $ r!}t)          |d|
 d          cY d}~c S d}~ww xY w|	                    ||d           n#t          j        t          d          d          S g .|                    d          }|rt          |t                    s#t          j        t          d          d          S t          |          D ]\  }}t          |t"                    rd|vsd|vr)t          j        t          d| d          d          c S 	 t%          |d                   }n.# t&          $ r!}t)          |d| d          cY d}~c S d}~ww xY w.                    t          |d                   |d           |rt*                              d           d}.s|r j
                            |          }|&t          j        t          d|           d           S t          |                    dg                     .|                    d!          }0|                    d
          0|	dd"         D ]}.                    |           |	r|	d"                             dd          nd2t/          2          s#t          j        t          d#          d          S |                    d$          d%k    rt1          .          d&k    r
.d'd         .|pt          t3          j                              1t          |                    d(          d)          }|rd*dl}|                                --fd+}d, }-fd-}-fd.}dg}t;          j                             2.01|||||//
  
                  }|                     -fd0           d1t3          j                    j!        dd2          }|                    d3 j"                  }tG          tI          j$                              }  %                    |||| -||.20||1/4           d{V S ./0 12fd5}!|j&                            d6          }"|"rtO          |g d78          }#	 tP          )                    |"|#|!           d{V \  }$}%n# t          $ rO}&t*          *                    d9|&d:           t          j        t          d;|& d<=          d>          cY d}&~&S d}&~&ww xY w	  |!             d{V \  }$}%n\# t          $ rO}&t*          *                    d9|&d:           t          j        t          d;|& d<=          d>          cY d}&~&S d}&~&ww xY w|$                    d?d          }'|'s|$                    dd@          }'d1t3          j                    j!        dd2          }tG          tI          j$                              }  +                    .2|$|'          }( ,                    .2|$          }) -                    |$|)A          }*|dBdC| |                    d3 j"                  |*|%                    dDd*          |%                    dEd*          |%                    dFd*          dGdH}+|r= j
        .                    ||+|(01dI           |r j
        /                    ||           dJ1i},/r/|,dK<   t          j        |+|,L          S )Mu3   POST /v1/responses — OpenAI Responses API format.Nr   r  r   r  r   r   r   Missing 'input' fieldr  previous_response_idr  r  Tr   z9Cannot use both 'conversation' and 'previous_response_id'r  r  r  r<   r>   zinput[r?  r   z!'input' must be a string or arrayr  :'conversation_history' must be an array of message objectsconversation_history['] must have 'role' and 'content' fieldsWBoth conversation_history and previous_response_id provided; using conversation_historyzPrevious response not found: r  r  r@  No user message found in input
truncationautor   ir=  Fr   c                 :    |                      |            d S d S rE   rC  rD  s    r   rF  z5APIServerAdapter._handle_responses.<locals>._on_delta^  s+     $MM%((((( %$r7   c                     dS )a%  Queue non-start tool progress events if needed in future.

                The structured Responses stream uses ``tool_start_callback``
                and ``tool_complete_callback`` for exact call-id correlation,
                so progress events are currently ignored here.
                Nr   )r	  r   r}  r  r  s        r   _on_tool_progressz=APIServerAdapter._handle_responses.<locals>._on_tool_progresse  s	     r7   c                 B                         d| ||pi df           dS )z6Queue a started tool for live function_call streaming.r  )r  r   r  NrC  )r  rP  rQ  rE  s      r   rS  z:APIServerAdapter._handle_responses.<locals>._on_tool_startn  sC    1$0)!.!4"4 4      r7   c                 D                         d| ||pi |df           dS )zFQueue a completed tool result for live function_call_output streaming.r  )r  r   r  rZ   NrC  )r  rP  rQ  rU  rE  s       r   rV  z=APIServerAdapter._handle_responses.<locals>._on_tool_completev  sF    3$0)!.!4"-	6 6      r7   )
r   r  r  r  r  r  r  r  rW  r  c                 .                         d           S rE   rC  rY  s    r   r[  z4APIServerAdapter._handle_responses.<locals>.<lambda>  r\  r7   resp_   r  )r   r   r  r  r  r  rW  r  r   r  r  r  r  r  c                  H   K                                    d {V S r^  r_  )r  r  r  r   r  r   s   r   _compute_responsez=APIServerAdapter._handle_responses.<locals>._compute_response  sO      )%9(4%$7 )         r7   ra  )r   r  r  r  r  ra  rc  z%Error running agent for responses: %sr   rd  rS  rT  r  r  (No response generated))start_indexr   r  rs  rt  rk  r  )r  r  r   r  r  r  r  r  r  r  r  )0r  r  r   r   r   r   r   rQ   r   r6   rq  r   r-   r/   rM   rt  rO   rP   rv   r"   r   r   r   ry   rF   r  r  r  r+  r1  ru  r  r3  r  rm  r    r   r  r   rB  rw  r6  r   r  #_response_messages_turn_start_index_extract_output_itemsr   r   )3r   r   r  r  r   	raw_inputr  r  r  input_messagesry  rW   r  r<   rz   raw_historyr  entryentry_contentstored_session_idstoredrz  r=  r  rF  r(  rS  rV  rW  r  r   r^  r  r/  r  r&  rZ   r  r  r  r  output_start_indexoutput_itemsr  r  rE  r  r  r  r  r   s3   `                                            @@@@@@r   _handle_responsesz"APIServerAdapter._handle_responses
  s     ##G,, 	O (,'E'Eg'N'N$WN	 ''''''DD$i0 	 	 	$&DNeffg     	 HHW%%	$]3J%K%KTWXXXXxx//#xx(>??xx//$TXXg%6%6EEE  	}0 	}$]3n%o%ox{||||  	W#'#7#H#H#V#V  02i%% 	e'-)DDENN	4(( 	e&y11 	N 	N	TdC(( N"))6d*K*KLLLLd++ N88FF33D`"?TV@W@W"X"X% ` ` `;CG^PSG^G^G^___________`"))4G*L*LMMM	N $]3V%W%W`cdddd 68hh566 	xk400 (!"^__    &k22 
d 
d5!%.. &2E2EZ_I_I_,%&ha&h&h&hii"     i$A%	BR$S$SMM! i i i7Cg[\CgCgCghhhhhhhhhhhi$++Sv5G5GTa,b,bcccc# xvwww # 	:(< 	:)--.BCCF~(7mWk7m7m)n)nwz{{{{#'

3I2(N(N#O#O  &

< 8 8#%zz.99 "#2#& 	- 	-C '',,,, FT[N2.229bAAAY[+L99 	b$]3S%T%T]`aaaa 88L!!V++4H0I0IC0O0O#7#>  ';#djll*;*;
%dhhx&8&8%HHH L	 "$((**I) ) ) ) )           I .t)%9(4%&/'8$2'8#$7 0? 0 0  J (()I)I)I)IJJJ9$*,,"23B3"799K'4+;<<JTY[[))J22' %"%#%9)))%$7 3         "	 	 	 	 	 	 	 	 	 	 "/--.?@@ 	*hhh  B&1&<&<_bRc&d&d d d d d d d   DaRVWWW(!"?A"?"?.YYY        &7&7&9&9 9 9 9 9 9 9   DaRVWWW(!"?A"?"?.YYY          $4b99 	L#ZZ1JKKN5djll.ss355%%
 @@ 	
 
 "EE 
 

 11&FX1YY  !$XXgt'788" %		.! < <!&?A!>!> %		.! < < 
 
  
	Q $$[)(4 ,(	3 3     Q$55lKPPP1:> 	K7J34 8HIIIIs    A 1BB#H88
I#II#I#M&&
N0NNN%[ 
\A\\\\3 3
^=A^^^c                 
  K   |                      |          }|r|S |j        d         }| j                            |          }|&t	          j        t          d|           d          S t	          j        |d                   S )u?   GET /v1/responses/{response_id} — retrieve a stored response.r   NResponse not found: r  r   r   )r  r  rq  rQ   r   r   r   )r   r   r  r   r:  s        r   _handle_get_responsez%APIServerAdapter._handle_get_response  s      ##G,, 	O(7%))+66>$]3W+3W3W%X%Xadeeee 
!3444r7   c                   K   |                      |          }|r|S |j        d         }| j                            |          }|s&t	          j        t          d|           d          S t	          j        |ddd          S )u@   DELETE /v1/responses/{response_id} — delete a stored response.r   r?  r  r   r   T)r  r  r  )r  r  rq  r   r   r   r   )r   r   r  r   r  s        r   _handle_delete_responsez(APIServerAdapter._handle_delete_response  s      ##G,, 	O(7&--k:: 	f$]3W+3W3W%X%Xadeeee  "
 "
   	r7   r  z[a-f0-9]{12}>   r   skillpromptrepeatr9  deliverr_  schedulei  c                  D    t           st          j        ddid          S dS )z5Return error response if cron module isn't available.r   zCron module not availablei  r   N)_CRON_AVAILABLEr   r   r   r7   r   _check_jobs_availablez&APIServerAdapter._check_jobs_available,  s8      	$56s    tr7   c                     |j         d         }| j                            |          sIt                              d||                     |                     |t          j        ddid          fS |dfS )z>Validate and extract job_id. Returns (job_id, error_response).job_idz,Cron jobs API rejected invalid job_id %r: %sr   zInvalid job ID formatr   r   N)r  
_JOB_ID_RE	fullmatchr   r   r  r   r   )r   r   rL  s      r   _check_job_idzAPIServerAdapter._check_job_id5  s    #H-((00 	NN>..w77  
 3,123     t|r7   c                   K   |                      |          }|r|S |                                 }|r|S 	 |j                            dd                                          dv }t          |          }t          j        d|i          S # t          $ r/}t          j        dt          |          id          cY d	}~S d	}~ww xY w)
u%   GET /api/jobs — list all cron jobs.include_disabledr>   >   r$   r'   )rQ  jobsr   r  r   N)
r  rJ  r  rQ   r1   
_cron_listr   r   r   r/   )r   r   r  cron_errrQ  rR  r  s          r   _handle_list_jobsz"APIServerAdapter._handle_list_jobsC  s      ##G,, 	O--// 	O	D&}001CRHHNNPPTaa/?@@@D$fd^444 	D 	D 	D$gs1vv%6sCCCCCCCCC	Ds   AB
 

C$B>8C>Cc                   K   |                      |          }|r|S |                                 }|r|S 	 |                                 d{V }|                    d          pd                                }|                    d          pd                                }|                    dd          }|                    dd          }|                    d          }	|                    d	          }
|st          j        d
did          S t          |          | j        k    r!t          j        d
d| j         did          S |st          j        d
did          S t          |          | j	        k    r!t          j        d
d| j	         did          S |r0t          )t          |          }|rt          j        d
|id          S |
3t          |
t                    r|
dk     rt          j        d
did          S |||||                     |          d}|	r|	|d<   |
|
|d	<   t          di |}t          j        d|i          S # t          $ r/}t          j        d
t!          |          id          cY d}~S d}~ww xY w)u)   POST /api/jobs — create a new cron job.Nr   r>   rG  rD  rF  localr9  rE  r   zName is requiredr   r      Name must be ≤  characterszSchedule is required   Prompt must be ≤ rC   z!Repeat must be a positive integer)rD  rG  r   rF  r   jobr  r   )r  rJ  r   rQ   r0   r   r   rF   _MAX_NAME_LENGTH_MAX_PROMPT_LENGTHrR  r-   r    r  _cron_creater   r/   )r   r   r  rT  r   r   rG  rD  rF  r9  rE  
scan_errorr  r[  r  s                  r   _handle_create_jobz#APIServerAdapter._handle_create_jobR  s     ##G,, 	O--// 	O+	D ''''''DHHV$$*1133D,,299;;HXXh++Fhhy'22GXXh''FXXh''F T('3E)FsSSSS4yy4000(T$2GTTTU^a     X('3I)JSVWWWW6{{T444(XD4KXXXYbe     P+7.v66
 P,gz-B3OOOO!:fc+B+B!fqjj('3V)W`cdddd !$"88AA F  *#)x !#)x ((((C$eS\222 	D 	D 	D$gs1vv%6sCCCCCCCCC	DsC   CI 8I 
I $8I 1I 4I A
I 
J$J=JJc                   K   |                      |          }|r|S |                                 }|r|S |                     |          \  }}|r|S 	 t          |          }|st	          j        ddid          S t	          j        d|i          S # t          $ r/}t	          j        dt          |          id          cY d}~S d}~ww xY w)u1   GET /api/jobs/{job_id} — get a single cron job.r   Job not foundr  r   r[  r  N)r  rJ  rO  	_cron_getr   r   r   r/   r   r   r  rT  rL  id_errr[  r  s           r   _handle_get_jobz APIServerAdapter._handle_get_job  s     ##G,, 	O--// 	O++G44 	M	DF##C Q('?)CCPPPP$eS\222 	D 	D 	D$gs1vv%6sCCCCCCCCC	D$   (B :B 
C	$C>C	C	c                    K                         |          }|r|S                                  }|r|S                      |          \  }}|r|S 	 |                                 d{V } fd|                                D             }|st          j        ddid          S d|v r?t          |d                    j        k    r!t          j        dd j         d	id          S d
|v r?t          |d
                    j	        k    r!t          j        dd j	         d	id          S |
                    d
          r6t          /t          |d
                   }|rt          j        d|id          S t          ||          }	|	st          j        ddid          S t          j        d|	i          S # t          $ r/}
t          j        dt          |
          id          cY d}
~
S d}
~
ww xY w)u/   PATCH /api/jobs/{job_id} — update a cron job.Nc                 .    i | ]\  }}|j         v ||S r   )_UPDATE_ALLOWED_FIELDS)rH   r  r  r   s      r   r;  z7APIServerAdapter._handle_update_job.<locals>.<dictcomp>  s,    [[[$!Q!t?Z:Z:ZA:Z:Z:Zr7   r   zNo valid fields to updater   r   r   rX  rY  rD  rZ  rb  r  r[  r  )r  rJ  rO  r   rV   r   r   rF   r\  r]  rQ   rR  _cron_updater   r/   )r   r   r  rT  rL  re  r   	sanitizedr_  r[  r  s   `          r   _handle_update_jobz#APIServerAdapter._handle_update_job  sw     ##G,, 	O--// 	O++G44 	M	D ''''''D[[[[$**,,[[[I ]('3N)OX[\\\\""s9V+<'='=@U'U'U(T$2GTTTU^a    9$$Yx-@)A)ADD[)[)[(XD4KXXXYbe    }}X&& P+<+H.y/BCC
 P,gz-B3OOOOvy11C Q('?)CCPPPP$eS\222 	D 	D 	D$gs1vv%6sCCCCCCCCC	Ds@   AF7 &AF7 )AF7 ,A
F7 7)F7 !F7 7
G0$G+%G0+G0c                   K   |                      |          }|r|S |                                 }|r|S |                     |          \  }}|r|S 	 t          |          }|st	          j        ddid          S t	          j        ddi          S # t          $ r/}t	          j        dt          |          id          cY d}~S d}~ww xY w)	u0   DELETE /api/jobs/{job_id} — delete a cron job.r   rb  r  r   r  Tr  N)r  rJ  rO  _cron_remover   r   r   r/   )r   r   r  rT  rL  re  successr  s           r   _handle_delete_jobz#APIServerAdapter._handle_delete_job  s     ##G,, 	O--// 	O++G44 	M	D"6**G Q('?)CCPPPP$dD\222 	D 	D 	D$gs1vv%6sCCCCCCCCC	Drg  c                   K   |                      |          }|r|S |                                 }|r|S |                     |          \  }}|r|S 	 t          |          }|st	          j        ddid          S t	          j        d|i          S # t          $ r/}t	          j        dt          |          id          cY d}~S d}~ww xY w)u3   POST /api/jobs/{job_id}/pause — pause a cron job.r   rb  r  r   r[  r  N)r  rJ  rO  _cron_pauser   r   r   r/   rd  s           r   _handle_pause_jobz"APIServerAdapter._handle_pause_job  s     ##G,, 	O--// 	O++G44 	M	Df%%C Q('?)CCPPPP$eS\222 	D 	D 	D$gs1vv%6sCCCCCCCCC	Drg  c                   K   |                      |          }|r|S |                                 }|r|S |                     |          \  }}|r|S 	 t          |          }|st	          j        ddid          S t	          j        d|i          S # t          $ r/}t	          j        dt          |          id          cY d}~S d}~ww xY w)u<   POST /api/jobs/{job_id}/resume — resume a paused cron job.r   rb  r  r   r[  r  N)r  rJ  rO  _cron_resumer   r   r   r/   rd  s           r   _handle_resume_jobz#APIServerAdapter._handle_resume_job  s     ##G,, 	O--// 	O++G44 	M	Dv&&C Q('?)CCPPPP$eS\222 	D 	D 	D$gs1vv%6sCCCCCCCCC	Drg  c                   K   |                      |          }|r|S |                                 }|r|S |                     |          \  }}|r|S 	 t          |          }|st	          j        ddid          S t	          j        d|i          S # t          $ r/}t	          j        dt          |          id          cY d}~S d}~ww xY w)u<   POST /api/jobs/{job_id}/run — trigger immediate execution.r   rb  r  r   r[  r  N)r  rJ  rO  _cron_triggerr   r   r   r/   rd  s           r   _handle_run_jobz APIServerAdapter._handle_run_job  s     ##G,, 	O--// 	O++G44 	M	D''C Q('?)CCPPPP$eS\222 	D 	D 	D$gs1vv%6sCCCCCCCCC	Drg  rZ   r  c                    t          |           }d|d}t          |t                    r|                    d          nd}t          |t                     r]|r[t                              | ||          }|rt          |          S |}|                    |           |                    |           |S |}|                    |           |                    d|d           |S )zBBuild the stored Responses transcript without duplicating history.r  r  r  Nr  )rM   r-   rP   rQ   rT  r2  rO   extend)	r  r   rZ   r  priorcurrent_useragent_messages
turn_startr  s	            r   r  z5APIServerAdapter._build_response_conversation_history  s    )** &<@@3=fd3K3KUJ///QUnd++ 	  	 )MM$ J
  ,N+++ L---///L)))[^LLMMMr7   c                 p   t          |t                    r|                    d          nd}t          |t                    r|sdS t          |           }d|d}||gz   }|dt	          |                   |k    rt	          |          S |r*|dt	          |                   |k    rt	          |          S dS )zBDetect transcript-shaped result["messages"] and return turn start.r  Nr   r  r  )r-   rP   rQ   rM   rF   )r  r   rZ   r  r}  r~  expected_prefixs          r   r2  z4APIServerAdapter._response_messages_turn_start_index.  s     4>fd3K3KUJ///QU.$// 	~ 	1)** &<@@<.0/3///0OCC''' 	^KSZZK0E99u::qr7   c                    t          |t                    r|                    d          nd}t          |t                    r|sg S |                     |||          }||d         }g }|D ]X}t          |t                    s|                    d          dvr0|                    |                     |                     Y|S )u  Return this turn's assistant/tool messages in client-safe shape.

        The streaming SSE contract delivers all assistant text as
        ``assistant.delta`` events under one ``message_id`` interleaved with
        ``tool.*`` events, and a single ``assistant.completed`` carrying only
        the final reply.  A client that accumulates deltas into one buffer
        cannot reconstruct *intermediate* assistant text segments that preceded
        tool calls — so when the page is re-opened mid/post-stream those
        segments appear lost, even though state.db persisted them correctly.

        Emitting the authoritative per-turn transcript on ``run.completed`` lets
        any SSE consumer reconcile its live view against ground truth without a
        separate ``GET /messages`` round-trip.  Purely additive: clients that
        ignore the field are unaffected.  Refs #34703.
        r  Nr  >   rL  r  )r-   rP   rQ   rM   r2  rO   r  )	clsr  r   rZ   r  startturnoutrz  s	            r   r  z*APIServerAdapter._turn_transcript_messagesB  s    , 4>fd3K3KUJ///QU.$// 	~ 	I77 ,
 
 eff%$& 	3 	3Cc4(( wwv&;;;JJs,,S112222
r7   r   r1  c                    g }|                      dg           }|dk    r
||d         }|D ]}|                     d          }|dk    r|                     d          rx|d         D ]n}|                     di           }|                    d|                     d	d
          |                     dd
          |                     dd
          d           o|dk    rA|                    d|                     dd
          |                     dd
          d           |                      dd
          }|s|                      dd          }|                    ddd|dgd           |S )ac  
        Build the output item array from the agent's messages.

        Walks *result["messages"]* starting at *start_index* and emits:
        - ``function_call`` items for each tool_call on assistant messages
        - ``function_call_output`` items for each tool-role message
        - a final ``message`` item with the assistant's text reply
        r  r   Nr  r  r  functionr  r   r>   r  r  r  rL  r  r  r<   r  r  r   r0  r   rB   r`   r  )rQ   rO   )	rZ   r1  rV   r  rz  r  tcfuncfinals	            r   r3  z&APIServerAdapter._extract_output_itemsh  s    ')::j"--??-H 	 	C776??D{""sww|'<'<"l+  B66*b11DLL / $ 4 4%)XXk2%>%>#%66$#3#3	" "     2"ww~r::!ggi44     

+R00 	CJJw(ABBE *! 	
 	
 		 		 		 r7   rW  c                     	
K   t          j                    }	
 fd}|                    d|           d{V S )a  
        Create an agent and run a conversation in a thread executor.

        Returns ``(result_dict, usage_dict)`` where *usage_dict* contains
        ``input_tokens``, ``output_tokens`` and ``total_tokens``.

        If *agent_ref* is a one-element list, the AIAgent instance is stored
        at ``agent_ref[0]`` before ``run_conversation`` begins.  This allows
        callers (e.g. the SSE writer) to call ``agent.interrupt()`` from
        another thread to stop in-progress LLM calls.
        c            	         ddl m} m}  |dpdppdpd          }	                     
          }|d<   pt	          t          j                              }|                    	|          }t          |dd          pdt          |d	d          pdt          |d
d          pdd}t          |d          }t          |t                    r|r||d<   ||f | |           S #  | |           w xY w)Nr   clear_session_varsset_session_varsr  r>   )r  r  session_keyr  )r  r  r  r  r  r  r  r   r  task_idsession_prompt_tokenssession_completion_tokenssession_total_tokensr  r  )
gateway.session_contextr  r  r  r/   r  r  run_conversationr  r-   )r  r  tokensr  effective_task_idrZ   r  _eff_sidrW  r  r  r  r   r  r  r  r  r  r   s           r   _runz)APIServerAdapter._run_agent.<locals>._run  s   TTTTTTTT%%%"(b/C:C%+	  F+**,C)*?+A(;+A(; +   (#(IaL$.$C#djll2C2C!//!-)=- 0   %,E3JA$N$N$SRS%,U4OQR%S%S%XWX$+E3I1$M$M$RQR  #5,
CCh,, 4 4+3F<(u}""6****""6****s   CC4 4DN)r1  r  r  )r   r   r  r  r  r  r  r  r  rW  r  r  r  s   ```````````  r   r  zAPIServerAdapter._run_agent  s      0 '))(	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+ (	+T ))$555555555r7   r8   r  i  r  r   r  c                 (   t          j                     }| j                            |i           }|                    d|||d           |                    d|                    d|                     |                    |           || j        |<   |S )zBUpdate pollable run status without exposing private agent objects.z
hermes.run)r  r  r   r  r  )r   rv  rQ   r   r
  r  )r   r  r   r  r  currents         r   _set_run_statusz APIServerAdapter._set_run_status  s    ikk$((44"	
 
 	 	 	 	<L#)F)FGGGv%,6"r7   r  zasyncio.AbstractEventLoopc                      dt           t          t          f         ddf fdd	dt          dt          dt          ffd}|S )
zUReturn a tool_progress_callback that pushes structured events to the run's SSE queue.r   r   Nc                 N                        j                            i                               dd          |                     d                     j                                      }|d S 	                     |j        |            d S # t          $ r Y d S w xY w)Nr   rK  r   
last_event)r  rv  rQ   rr  r  r  r   )r   qr  r  r   s     r   _pushz8APIServerAdapter._make_run_event_callback.<locals>._push  s      "&&vr2266xKK 99W-- !   
 !%%f--Ay))!,>>>>>   s   9B 
B$#B$r	  r  r}  c                 4   t          j                     }| dk    r d|||d           d S | dk    rJ d||t          |                    dd          d          |                    dd          d	           d S | d
k    r d
||pdd           d S d S )Nr  )r   r  r  rL  r}  r  durationr      is_errorF)r   r  r  rL  r  r   r  r>   )r   r  r  r@   )r   roundrQ   )r	  r  r}  r  r  r  r  r  s         r   	_callbackz<APIServerAdapter._make_run_event_callback.<locals>._callback  s   B^+++$!#%&       ///-$!#% %fjjQ&?&? C C#ZZ
E::       4442$!##Mr	       54r7   r*  )r   r/   r   )r   r  r  r  r  s   ``` @r   _make_run_event_callbackz)APIServerAdapter._make_run_event_callback  s    	c3h 	D 	 	 	 	 	 	 	 		 	# 	# 	s 	 	 	 	 	 	 	8 r7   c                 `   K                         |          }|r|S                      |          \  }||S t           j                   j        k    r.t          j        t          d j         dd          d          S 	 |                                 d{V }n3# t          $ r& t          j        t          d          d	          cY S w xY w|
                    d
          }|s#t          j        t          d          d	          S t          |t                    r|n2t          |t                    r|d         
                    dd          nds#t          j        t          d          d	          S |
                    d          }|
                    d          }g |
                    d          }|rt          |t                    s#t          j        t          d          d	          S t          |          D ]\  }	}
t          |
t                    rd|
vsd|
vr)t          j        t          d|	 d          d	          c S                     t          |
d                   t          |
d                   d           |rt"                              d           d}sm|rk j        
                    |          }|rOt          |
                    dg                     |
                    d          }||
                    d          }st          |t                    rt          |          dk    r|dd         D ]}t          |t                    r|
                    d          r||
                    d          rg|d         }t          |t                    rd                    d |D                       }                    |d         t          |          d           dt+          j                    j         |
                    d          p|ppp|t1          j                    t1          j                    t7          j                    } j        <   | j        <    j        <                                  dt>          t                   ddffd                       d!||
                    d" j!                  #            fd$}t1          j"         |                      }| j#        <   	  j$        %                    |           n# tL          $ r Y nw xY wtO          |d%          r|(                     j$        j)                   rd&ini }t          j        d'd(d)|*          S )+u@   POST /v1/runs — start an agent run, return run_id immediately.NzToo many concurrent runs (max r   rate_limit_exceededr   i  r   Invalid JSONr   r   r  r@  r<   r>   r#  r  r  r  r  r  r   r!  r  r"  r  rC   r  c              3      K   | ]H}t          |t                    r1|                    d           dk    0|                    dd          V  IdS )r?   r@   r>   N)r-   rP   rQ   )rH   rn   s     r   rJ   z0APIServerAdapter._handle_runs.<locals>.<genexpr>q  se       + +59)$55+:>((6:J:Jf:T:T !HHVR00:T:T:T:T+ +r7   r  r  r   c                     | d S 	                      j        dt          j                    | d           d S # t          $ r Y d S w xY w)Nzmessage.delta)r   r  r  r  )r  r  r   r   )r  r  r  r  s    r   _text_cbz/APIServerAdapter._handle_runs.<locals>._text_cb  sx    }))!,,$!%"	9 9         s   1: 
AAqueuedr  )r  r  r  c            	        K   	                      d                               	          j        <   dt          t          t
          f         dd ffd
fd} t          j                                        d |            d {V \  }}t          |t                    rq|                    d          r\|                    d          pd	}                    d
t          j                    |d                                d|d
           nst          |t                    r|                    dd          nd}                    dt          j                    ||d                                d||d           n# t          j        $ rV                      dd           	                     dt          j                    d           n# t          $ r Y nw xY w t          $ r}t                               d                                dt	          |          d
           	                     d
t          j                    t	          |          d           n# t          $ r Y nw xY wY d }~nd }~ww xY w	 ddlm}  |
           n# t          $ r Y nw xY w	                     d            n# t          $ r Y nw xY wj                            d            j                            d            j                            d            d S # 	 ddlm}  |
           n# t          $ r Y nw xY w	                     d            n# t          $ r Y nw xY wj                            d            j                            d            j                            d            w xY w)NrK  )r  r  r  r  r  approval_datar   c                    t          | pi           }|                    dt          j                    g dd                               dd           	                     j        |           d S # t          $ r Y d S w xY w)Nzapproval.request)oncer@  alwaysdeny)r   r  r  rp  waiting_for_approvalr  )rP   r   r   r  r  r  r   )r  r   r  r  r  r   s     r   _approval_notifyzOAPIServerAdapter._handle_runs.<locals>._run_and_close.<locals>._approval_notify  s     !4"55ELL!3"(%)Y[[#H#H#H	" "    ((.#5 )   
11!,FFFFF$   s   A6 6
BBc                  p   ddl m} m} ddlm}m}m}m} p}d }g }	  |          } |d          } |                               |          }		  |           |	  ||           n# t          $ r Y nw xY w|r	  | |           n# t          $ r Y nw xY wn# |	  ||           n# t          $ r Y nw xY w|r	  | |           w # t          $ r Y w w xY ww xY w# 	  |           |	  ||           n# t          $ r Y nw xY w|r	  | |           w # t          $ r Y w w xY ww # |	  ||           n# t          $ r Y nw xY w|r	  | |           w # t          $ r Y w w xY ww xY wxY wt          dd          pdt          dd          pdt          d	d          pdd
}
|	|
fS )Nr   r  )register_gateway_notifyreset_current_session_keyset_current_session_keyunregister_gateway_notifyr  )r  r  r  r  r  r  r  )r  r  r  tools.approvalr  r  r  r  r  r   r  )r  r  r  r  r  r  r  approval_tokensession_tokensrur  r  approval_session_keyr  r  r  r   s              r   	_run_synczHAPIServerAdapter._handle_runs.<locals>._run_and_close.<locals>._run_sync  s   \\\\\\\\            )3(<f%%)N%'N) *A)@AU)V)V)9)9%1(<* * * 0/0DFVWWW!22)51E$5 3  )556JKKK-9!)$=$=n$M$M$M$M'0 !) !) !)$(D!)- )!)$6$6~$F$F$F$F'0 !) !) !)$(D!))  .9!)$=$=n$M$M$M$M'0 !) !) !)$(D!)- )!)$6$6~$F$F$F$F'0 !) !) !)$(D!)))556JKKK-9!)$=$=n$M$M$M$M'0 !) !) !)$(D!)- )!)$6$6~$F$F$F$F'0 !) !) !)$(D!))  .9!)$=$=n$M$M$M$M'0 !) !) !)$(D!)- )!)$6$6~$F$F$F$F'0 !) !) !)$(D!)) )07NPQ(R(R(WVW)08SUV)W)W)\[\(/7Mq(Q(Q(VUV A
 a4Ks+  <C) B& *A6 6
BB	B 
B"!B"&C&*B65C&6
C C&CC&	CC&
C"C&!C""C&)E8+D56E89DE8
DE8DE8D$#E8$
D1.E80D11E85E59EE5
EE5EE5E$#E5$
E1.E50E11E55E8re  r   zagent run failedz
run.failed)r   r  r  r   )r   r  r  r>   r  )r   r  r  r  r  r  )r  r  r  	cancelledzrun.cancelledr  )r   r  r  z[api_server] run %s failedr   r  )r  r  rt  r   r/   r   r1  r  r  r-   rP   rQ   r  r   r7  r   r   rV  r  r  r  ru  rw  )r  rZ   r  	error_msgr  rz   r  r  r  r  r  r  r  event_cbr  r  r  r  r   r  r   s          @@r   _run_and_closez5APIServerAdapter._handle_runs.<locals>._run_and_close  s[     b>$$VY777**,C)*2+3(; +   38'/DcN t         $.  .  .  .  .  .  .  .  .  .  . ` '.&>&@&@&P&PQUW`&a&a a a a a a a fd++ 

80D0D  &

7 3 3 I7IILL!-"(%)Y[[!*	" "    (( '#/	 )     JTTZ\`IaIa%iVZZ0@"%E%E%EgiNLL!0"(%)Y[["0!&" "    ((#-##2 )    )   $$. %   
LL!0"(%)Y[[" "    
 !   D     !=vFFF$$c((+	 %   LL!-"(%)Y[[!$S	" "     !   D.HHHHHH--.BCCCC    DLL&&&&    D'++FD999&**64888+//=====HHHHHH--.BCCCC    DLL&&&&    D'++FD999&**64888+//====s   FF L= 'J*G.-J.
G;8J:G;;JAJ
8JJ
JJJJL= JL= !J3 3
K ?K K 
K'&K'=O?MO
MOMO"M87O8
NONAOr3  r  startedr  r      r   )*r  r  rF   rr  _MAX_CONCURRENT_RUNSr   r   r   r   r   rQ   r-   r/   rM   rt  rP   rO   r   r   rq  rT   r  r  r  r1  r  r+  r   rs  rw  r  r   r  rm  r2  ru  r,  r-  r!   r.  r3  r/  )r   r   r  r  r   r4  r  r  r6  r  r7  r9  r:  rz  r<   r  r  r5  r  r  r  r  r  r  r  r  r  r  r  r   s   `                  @@@@@@@@@@@r   _handle_runszAPIServerAdapter._handle_runs)  s     ##G,, 	O (,'E'Eg'N'N$WN t !!T%>>>$[t?X[[[bwxxx   
	P ''''''DD 	P 	P 	P$]>%B%B3OOOOOO	P HHW%%	 	Y$]3J%K%KTWXXXX$.y#$>$>  Nyyist}  @D  jE  jE  FMYr]EVEVW`bdEeEeEe  KM 	b$]3S%T%T]`aaaaxx//#xx(>?? 68hh566 	xk400 (!"^__    &k22 l l5!%.. &2E2EZ_I_I_,%&ha&h&h&hii"      %++Sv5G5GTWX]^gXhTiTi,j,jkkkk# xvwww # 	>(< 	>)--.BCCF >'+FJJ7Mr,R,R'S'S$$*JJ|$<$<!'#)::n#=#=L
 $ 
	`
9d(C(C 
	`IYZHZHZ "~ 	` 	`c4(( `SWWV__ `ASAS `!)nG!'400 "%(( + +=D+ + + # # )//VQTU\Q]Q]0^0^___*
(**XXl++J/@JF
2JjJF".'))-4]__Y[[
$%&!,6!&).B#F+00>>	HSM 	d 	 	 	 	 	 	 	 	 	!!((7D$455 	 	
 	
 	
c	> c	> c	> c	> c	> c	> c	> c	> c	> c	> c	> c	> c	> c	> c	> c	>J ">>#3#344)-v&	"&&t,,,, 	 	 	D	4,-- 	C""4#9#ABBB >QX#%899VX 	  33$
 
 
 	
s$   B+ +-CC4U 
UUc                   K   |                      |          }|r|S |j        d         }| j                            |          }|(t	          j        t          d| d          d          S t	          j        |          S )uF   GET /v1/runs/{run_id} — return pollable run status for external UIs.r  NRun not found: run_not_foundr   r  r   )r  r  rv  rQ   r   r   r   )r   r   r  r  r   s        r   _handle_get_runz APIServerAdapter._handle_get_runQ  s      ##G,, 	O#H-#''//>$888OOO     (((r7   c                 p  K   |                      |          }|r|S |j        d         }t          d          D ]'}|| j        v r nCt	          j        d           d{V  (t          j        t          d| d          d	          S | j        |         }t          j	        d
dddd          }|
                    |           d{V  	 	 	 t	          j        |                                d           d{V }n0# t          j        $ r |                    d           d{V  Y ]w xY w||                    d           d{V  nFdt          j        |           d}|                    |                                           d{V  n3# t$          $ r&}	t&                              d||	           Y d}	~	nd}	~	ww xY w| j                            |d           | j                            |d           n;# | j                            |d           | j                            |d           w xY w|S )uQ   GET /v1/runs/{run_id}/events — SSE stream of structured agent lifecycle events.r     r  Nr  r  r   r  r   r   r  r  r)   r  r   Tr   r#  r%  s   : stream closed

r  r)  z,[api_server] SSE stream error for run %s: %s)r  r  rangerr  r1  r  r   r   r   r0  r1  r3  rQ   r5  r6  r   r   r?  r   r   r   r  rs  )
r   r   r  r  r   r  r   r   r  rz   s
             r   _handle_run_eventsz#APIServerAdapter._handle_run_events`  s     ##G,, 	O#H- r 	r 	rA***-%%%%%%%%%%$]3MV3M3MTc%d%d%dmpqqqqf%% 3!+%) 
 
 
 w'''''''''	87")"215577D"I"I"IIIIIIIEE+   "..);<<<<<<<<<H ="..)?@@@@@@@@@:4:e#4#4:::nnW^^%5%56666666667   	V 	V 	VLLGQTUUUUUUUU	V !!&$///%))&$7777 !!&$///%))&$7777sU   F .C< ;F <*D)&F (D))A'F G; 
GF<7G; <GG; ;8H3c                   K   |                      |          }|r|S |j        d         }| j                            |          }|(t	          j        t          d| d          d          S 	 |                                 d{V }n3# t          $ r& t	          j        t          d          d	          cY S w xY wt          |                    d
d                    
                                                                }dddd}|                    ||          }h d}	||	vr%t	          j        t          dd          d	          S | j                            |          }
|
s(t	          j        t          d| d          d          S t          |                    d          d          p#t          |                    d          d          }	 ddlm}  ||
||          }nb# t          $ rU}t                               d|           t	          j        t          t          |                    d          cY d}~S d}~ww xY w|dk    r(t	          j        t          d| d          d          S |                     |dd !           | j                            |          }|>	 |                    d |t+          j                    ||d"           n# t          $ r Y nw xY wt	          j        d#|||d$          S )%uC   POST /v1/runs/{run_id}/approval — resolve a pending run approval.r  Nr  r  r   r  r   r  r   choicer>   r  )approveapprovedallow>   r  r  r  r@  zEInvalid approval choice; expected one of: once, session, always, denyinvalid_approval_choicez$Run has no active approval session: approval_not_activer  rk   Fr   resolve_allr   )resolve_gateway_approval)r  z2[api_server] approval resolution failed for run %sr  zRun has no pending approval: approval_not_pendingrK  zapproval.respondedr  )r   r  r  r  resolvedzhermes.run.approval_response)r  r  r  r  )r  r  rv  rQ   r   r   r   r   r   r/   r0   r1   rw  r6   r  r  r   rV  r  rr  r  r   )r   r   r  r  r   r   
raw_choicealiasesr  r  r  r  r  r  rz   r  s                   r   _handle_run_approvalz%APIServerAdapter._handle_run_approval  s     ##G,, 	O#H-#''//>$888OOO   
	P ''''''DD 	P 	P 	P$]>%B%B3OOOOOO	P (B//006688>>@@
$&6JJZ44777  $[2        $:>>vFF# 	$C6CC.       !%%@@@ L#DHH]$;$;UKKK 	
	J??????//$'  HH
  	J 	J 	JQSYZZZ$]3s88%<%<SIIIIIIIII	J q==$<F<</       	VY;OPPP!!&))=	1$!%$ (           4 	"
 "
   	sC   .B	 	-B98B9G. .
I8A
III3,K   
K-,K-c                   K   |                      |          }|r|S |j        d         }| j                            |          }| j                            |          }|*|(t          j        t          d| d          d          S |                     |dd	
           |'	 |	                    d           n# t          $ r Y nw xY w||                                s|                                 	 t          j        t          j        |          d           d{V  nG# t          j        $ r t"                              d|           Y nt          j        t          f$ r Y nw xY wt          j        |dd          S )u:   POST /v1/runs/{run_id}/stop — interrupt a running agent.r  Nr  r  r   r  r   stoppingzrun.stoppingr  zStop requested via APIg      @r#  z^[api_server] stop for run %s timed out after 5s; agent may still be finishing the current stepr  )r  r  rt  rQ   ru  r   r   r   r  r  r   r  r9  r1  r3  r4  r5  r   r   r7  )r   r   r  r  r  r5  s         r   _handle_stop_runz!APIServerAdapter._handle_stop_run  s     ##G,, 	O#H-'++F33%))&11=T\$]3MV3M3MTc%d%d%dmpqqqqVZNKKK 89999    DIIKKKKMMM
	&w~d';';SIIIIIIIIIII'   D    
 *I6     Fj!I!IJJJs*   $B: :
CC5.D$ $*E(E('E(c                 B   K   	 t          j        d           d{V  t          j                     fdt           j                                                  D             }|D ]}t                              d|           	 ddlm	}  j
                            |          }|r ||           n# t          $ r Y nw xY w j                            |d            j                            |d            j                            |d            j                            |d            j
                            |d            fdt           j                                                  D             }|D ]} j                            |d           )	z;Periodically clean up run streams that were never consumed.T<   Nc                 6    g | ]\  }}|z
  j         k    |S r   )_RUN_STREAM_TTL)rH   r  r  r  r   s      r   r   z9APIServerAdapter._sweep_orphaned_runs.<locals>.<listcomp>  s:       &FJ#d&::: :::r7   z%[api_server] sweeping orphaned run %sr   r  c           	          g | ]O\  }}|                     d           dv r3t          |                     dd          pd          z
  j        k    M|PS )r   >   re  r  r  r  r   )rQ   r4   _RUN_STATUS_TTL)rH   r  r   r  r   s      r   r   z9APIServerAdapter._sweep_orphaned_runs.<locals>.<listcomp>,  sp       "FF::h''+OOO%

< ; ; @qAAADDXXX  YXXr7   )r1  r  r   rM   rs  rV   r   r   r  r  rw  rQ   r   rr  r  rt  ru  rv  )r   staler  r  r  stale_statusesr  s   `     @r   _sweep_orphaned_runsz%APIServerAdapter._sweep_orphaned_runs  s     	5-#########)++C    *.t/H/N/N/P/P*Q*Q  E
   > >DfMMMHHHHHH+/+F+J+J6+R+R(+ H112FGGG    D!%%fd333)--fd;;;'++FD999&**64888+//====    &*4+=+C+C+E+E&F&F  N ) 5 5"&&vt4444?	5s   -B44
C Cc                 2  K   t           s"t                              d| j                   dS 	 d t          t
          t          fD             }t          j        |t                    | _
        | j
        J | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d	| j                   | j
        j                            d
| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                   | j
        j                            d| j                    | j
        j                            d| j!                   | j
        j                            d| j"                   | j
        j                            d| j#                   | j
        j                            d| j$                   | j
        j                            d| j%                   | j
        j                            d| j&                   | j
        j                            d| j'                   | j
        j                            d| j(                   | j
        j                            d| j)                   | j
        j                            d| j*                   | j
        j                            d| j+                   | j
        j                            d| j,                   | j
        j                            d| j-                   | j
        j                            d| j.                   | j
        j                            d| j/                   | | j
        d <   ta          j1        | 2                                          }	 | j3        4                    |           n# tj          $ r Y nw xY wtm          |d!          r|7                    | j3        j8                   | j9        s(t          :                    d"| j        | j;                   dS ty          | j;                  rY| j9        rR	 d#d$l=m>}  || j9        d%&          s(t          :                    d'| j        | j;                   dS n# t~          $ r Y nw xY w	 t          jA        t          jB        t          jC                  5 }|D                    d(           |E                    d)| jF        f           ddd           n# 1 swxY w Y   t          :                    d*| j        | jF                   dS # t          t          f$ r Y nw xY wt          jI        | j
                  | _J        | jJ        K                                 d{V  t          jL        | jJ        | j;        | jF                  | _M        | jM        N                                 d{V  | O                                 t          P                    d+| j        | j;        | jF        | jQ                   d,S # t          $ r,}t          :                    d-| j        |           Y d}~dS d}~ww xY w).zStart the aiohttp web server.z[%s] aiohttp not installedFc                     g | ]}||S rE   r   )rH   mws     r   r   z,APIServerAdapter.connect.<locals>.<listcomp>@  s    xxx"ikiw2iwiwiwr7   )middlewaresclient_max_sizeNr)  r+  z
/v1/healthr-  z/v1/capabilitiesr:  r<  r>  rA  rE  rF  rG  rH  r.  r0  z/v1/responses/{response_id}z	/api/jobsz/api/jobs/{job_id}z/api/jobs/{job_id}/pausez/api/jobs/{job_id}/resumez/api/jobs/{job_id}/runr2  r3  r5  r7  r8  r   r3  zk[%s] Refusing to start: API_SERVER_KEY is required for the API server, including loopback-only binds on %s.r   )has_usable_secretr  )
min_lengthz[%s] Refusing to start: API_SERVER_KEY is set to a placeholder value. Generate a real secret (e.g. `openssl rand -hex 32`) and set API_SERVER_KEY before exposing the API server on %s.rC   r   z[[%s] Port %d already in use. Set a different port in config.yaml: platforms.api_server.portz5[%s] API server listening on http://%s:%d (model: %s)Tz#[%s] Failed to start API server: %s)Sr   r   r   r   r   r  r  r   Applicationr  rn  routeradd_getr  r  r  rN  rW  rd  r  add_postr  r  	add_patchr  
add_deleter  r  r  r  r;  r  r=  r@  rB  rU  r`  rf  rm  rq  rt  rw  rz  r  r  r  r  r  r1  r2  r  r,  r-  r!   r.  r3  r/  ri  r   rf  r   hermes_cli.authr  ImportError_socketsocketAF_INETSOCK_STREAM
settimeoutr   rh  ConnectionRefusedErrorr   	AppRunnerro  setupTCPSiterp  r  _mark_connectedr  rm  r   )r   mws
sweep_taskr  _sr  s         r   r   zAPIServerAdapter.connect9  s       	NN7CCC5k	xx2GId exxxCCIZ[[[DI9(((I$$Y0CDDDI$$%79UVVVI$$\43FGGGI$$\43FGGGI$$%79RSSSI$$\43FGGGI$$^T5JKKKI$$_d6PQQQI%%ot7RSSSI$$%A4C[\\\I&&'CTE_```I''(DdFabbbI$$%JDLijjjI%%&GIbcccI%%&GIbcccI%%&NPTPpqqqI%%&<d>[\\\I%%ot7MNNNI$$%BDD]^^^I''(EtGcdddI$$[$2HIIII%%k43JKKKI$$%94;OPPPI&&';T=TUUUI''(<d>UVVVI%%&@$BXYYYI%%&A4CZ[[[I%%&>@TUUUI%%j$2CDDDI$$%8$:NOOOI$$%?AXYYYI%%&BDD]^^^I%%&>@UVVV
 /3DI*+ !,T-F-F-H-HIIJ&**:6666   z#677 M,,T-C-KLLL
 = ;Itz  
 u %TZ00 T] AAAAAA,,T]qIII %D !Itz    %u% #   D^GOW5HII :RMM!$$$JJTZ8999: : : : : : : : : : : : : : : z  }A  }F  HL  HR  S  S  Su*G4    =33DL,$$&&&&&&&&&T\4:tzJJDJ*""$$$$$$$$$  """KKG	4:tz43C   4 	 	 	LL>	1MMM55555	s   T<_  *V _  
V_  VA_  3_  >Y _  
Y_  Y_  !)[; 
2[<[; [[; [)[; ;\_  \C_   
`*!``c                   K   |                                   | j        M	 | j                                         n2# t          $ r% t                              d| j        d           Y nw xY w| j        r&| j                                         d{V  d| _        | j	        r&| j	        
                                 d{V  d| _	        d| _        t                              d| j                   dS )uv  Stop the aiohttp web server and release all owned resources.

        Closes the ResponseStore SQLite connection in addition to stopping
        the aiohttp web server. Without this, every adapter instance leaks
        2 file descriptors (the database file and its WAL sidecar) — the
        reconnect loop in ``gateway.run`` constructs a fresh adapter on
        every retry, so 2 fds/retry × 300s backoff cap ≈ 12 fds/hour, which
        exhausts the default 2560 fd limit after ~12h of failed reconnects
        and turns the whole gateway into a zombie
        (OSError: [Errno 24] Too many open files, #37011).
        Nz%Failed to close response store for %sTr   z[%s] API server stopped)_mark_disconnectedrq  r   r   r   r   r   rp  rh  ro  cleanuprn  r  r   s    r   
disconnectzAPIServerAdapter.disconnect  s      	!!!+$**,,,,   ;TYQU       : 	*//#########DJ< 	 ,&&(((((((((DL	-ty99999s   9 ,A('A(r  r<   reply_tometadatac                 (   K   t          dd          S )uU   
        Not used — HTTP request/response cycle handles delivery directly.
        Fz1API server uses HTTP request/response, not send())rp  r   )r   )r   r  r<   r  r  s        r   sendzAPIServerAdapter.send  s       %/bccccr7   c                 (   K   dd| j         | j        dS )z'Return basic info about the API server.z
API Serverr  )r   r?   rW  rY  )rf  rh  )r   r  s     r   get_chat_infozAPIServerAdapter.get_chat_info  s'       !JJ	
 
 	
r7   )NNNNNNN)r   r  r   r|   )r   r  r   r  r*  rE   r   )NNNNNNNNr   r  )_r   r   r   r   r
   r   staticmethodr   r~  r/   rj  rl  r   r   r   r.   r   r    r  r  r  r  r  r  r  r  r  r  r  r  rN  rW  rd  ri  r  r  r  r  r   r  r  r  r  r  r  r  r  r  r;  r  rv  r  r=  r@  rB  
__import__compilerM  rj  r\  r]  rJ  rO  rU  r`  rf  rm  rq  rt  rw  rz  r  r2  classmethodr  r3  rM   r  r  r  r  r  r  r  r  r  r  r  r  r   r  r   r  r  __classcell__)rz  s   @r   rT  rT    s          /~  /  /  /  /  /  /D O3 O5c? O O O \O c c    \&s xS#X7O    (Ic Id I I I I 7:     s    \
m 
S#X 
 
 
 
(B B3 B B B B
 4S>    .
= 
Xn5M 
 
 
 
J "2$2	x}h~66	72 2 2 2p     ( 26$("# #-1= =!)#= SM= &c]= 
= = = =F
 
 
 
   ,   *N N N N`   >6 6 6 6x $c $C $# $# $ $ $ \$ 4S> d38n    \" N4S> Nd38n N N N \N] uT#s(^U]^lUmEm?n    s uXdSVX[S[nE]_ghv_wEw?x    C DcSVhDX       :#w #w #w #wJk k k kk k k k8s s s s   $-t -t -t -t^*
 *
 *
 *
XK K K KZJ J J JF
 OS#'V V$V58VADVVHKV !V 
	V V V VN .2R	 R	R	 R	 	R	
 R	 #4S>2R	 R	 smR	 smR	 R	 R	 &c]R	  
!R	 R	 R	 R	hVJ VJ VJ VJx5 5 5 5   * D!!))/::Jnnn8N#;    \] u    D D D D3D 3D 3D 3DjD D D D&#D #D #D #DJD D D D&D D D D&D D D D&D D D D. "4S>2 S#X 	
 
d38n	   \< "4S>2 S#X 
	   \& #"4S>2# # S#X	#
 
d38n	# # # [#J / /d38n /3 /tTXY\^aYaTbOc / / / \/r 26$("# #$(-1D6 D6D6 #4S>2D6 "*#	D6
 SMD6 D>D6 &c]D6 
D6 D6 D6 D6T OOc 3 # $sTWx.    ,s ,:U , , , ,\f
 f
 f
 f
P	) ) ) )/ / / /dV V V Vp&K &K &K &KP!5 !5 !5 !5Nqt q q q qf: : : :B #'-1
d 
d
d 
d 3-	
d
 4S>*
d 

d 
d 
d 
d
3 
4S> 
 
 
 
 
 
 
 
r7   rT  )F)r   NN)_r   r1  r=  r  r   loggingrc  r  r  r  r   r   r  pathlibr   typingr   r   r   r   aiohttpr   r   r  gateway.configr	   r
   gateway.platforms.baser   r   r   	getLoggerr   r   r/   r   re  rg  r   r  r4  rL   rN   r    r#   	frozensetr2   r3   r.   r6   rR   rg   rh   rj   rv   ry   r"   r   r~  r   r   r   r  
middlewarer   r   r  r	  r  r  rw  rB  rI  rI  	cron.jobsrJ  rS  rK  rc  rL  r^  rM  rk  rN  ro  rO  rs  rP  rv  rQ  ry  tools.cronjob_toolsrR  r   rT  r   r7   r   <module>r+     s   B      				     				          , , , , , , , , , , , ,   
CCC 4 3 3 3 3 3 3 3          
	8	$	$    .   )- &#   -9   c S     'Y'A'A'ABB 'i(C(C(CDD   d t    4 (*7 7 77!$73677 7 7 7| 9BBBCC I{M:;; 9fl344 s3 s3 s s s sl# $    	j 	C 	N 	 	 	 	 FO D D DT#s(^ Ds DSXY\^fgu^vYvSw D D D Dt    
e$ e$ e$ e$ e$ e$ e$ e$Z %A$R   ^  ^ ( O	 	3 	# 	PS 	be 	quvy{~v~q 	 	 	 	  !^
& 
& ^
& 
& !  LD!F'$    	'^  ^  #'** ** ** ** ** ** ** **Z  !!<DcN <$s) < < < < <C= 	   $ 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 OO   JILLLKLMMM$JJJJJJJ   D8
 D8
 D8
 D8
 D8
* D8
 D8
 D8
 D8
 D8
s6   A 	AA/H HH"H) )H32H3