
    xjW                        U d Z ddlmZ ddlZddlZddlZ ej        e          Z ej	                    Z
 ej        e
          Zi Zded<   ddZdd
Zd dZd dZddd!dZd"dZd#dZd$dddd%dZddZdS )&u+  
Hermes WebUI memory-provider session lifecycle.

Batch-extraction memory providers (OpenViking, Holographic) only extract memories
when AIAgent.commit_memory_session() invokes provider on_session_end(). WebUI
sessions can be reopened and continued many times, so the lifecycle must guarantee:

1. Only completed, non-ephemeral turns are committable.
2. A commit finishing late must not erase work completed while it was in flight.
3. A failed commit preserves the uncommitted generation and owning agent handle.
4. Replacement/reopened agents cannot steal older dirty generations.
5. Overlapping commits are serialised via a per-session in-flight guard.

CLI-parity semantics — post-turn marking, boundary extraction/commit:

- Completed turn: Hermes core still mirrors the exchange through
  run_agent.py::_sync_external_memory_for_turn(), MemoryManager sync_all(), and
  provider sync_turn() WITHOUT triggering extraction.  WebUI then calls
  mark_turn_completed() after the saved/completed-turn boundary so later drains
  know the synced session has uncommitted work and which agent owns it.

- Session boundary: commit_session_memory() triggers
  AIAgent.commit_memory_session(), which calls provider on_session_end(),
  posting /api/v1/sessions/<sid>/commit and triggering extraction. This is
  called only at boundaries — /api/session/new with prev_session_id, explicit
  agent eviction, LRU cache eviction, and shutdown drain — matching the CLI's
  AIAgent.commit_memory_session()/shutdown_memory_provider() boundary.

The design uses a monotonic generation counter per session plus per-generation
agent ownership segments. mark_turn_completed() records which agent owns the new
generation. commit_session_memory() commits the earliest uncommitted segment and
compare-and-clears only that captured segment after success.
    )annotationsNzdict[str, dict]	_sessionsreturndictc                     ddd dg dS )Nr   F)
generationcommitted_generationagent	in_flightsegments r       8/Users/bertmahoney/hermes-webui/api/session_lifecycle.py
_new_entryr   1   s      !  r   Nonec                     t           5  t                                           t                                            d d d            d S # 1 swxY w Y   d S N)
_conditionr   clear
notify_allr   r   r   _reset_for_testsr   ;   s    	                                       s   3AAA
session_idstrc                    | sdS t           5  t                              | t                                }||d<   t                                            ddd           dS # 1 swxY w Y   dS )a	  Register the current agent handle for future completed generations.

    Existing dirty generations keep their original segment owner. This prevents
    a rebuilt/reopened agent from overwriting the handle needed to retry older
    failed memory-provider work.
    Nr
   )r   r   
setdefaultr   r   )r   r
   entrys      r   register_agentr   A   s      	    $$Z>>g                                   s   AAA#&A#c                    | sdS t           5  t                              |           }|d|d<   t                                            ddd           dS # 1 swxY w Y   dS )zClear the current future-generation agent handle.

    Dirty segment owners are intentionally preserved so failed work remains
    retryable even if the cache drops the current agent reference.
    Nr
   )r   r   getr   r   r   s     r   unregister_agentr!   P   s      	    j))!E'N	                                   s   ;AAA)r
   intc                  | sdS t           5  t                              | t                                }|||d<   ||n|                    d          }|dxx         dz  cc<   |d         }|d         }|r1|d         s)|d                             d          |u r||d         d<   n|                    |||d	           t                                            |cd d d            S # 1 swxY w Y   d S )
Nr   r
   r      r   r   end)startr&   r
   )r   r   r   r   r   appendr   )r   r
   r   ownerr   r   s         r   mark_turn_completedr*   _   s`    q	  $$Z>>"E'N*		'0B0Blq <(
$ 	VE+. 	V8B<3C3CG3L3LPU3U3U",HRLOOjeTTUUU                 s   CC''C+.C+boolc                    | sdS t           5  t                              |           }|	 d d d            dS |d         |d         k    cd d d            S # 1 swxY w Y   d S )NFr   r	   )_lockr   r   r    s     r   has_uncommitted_workr.   r   s     u	 C Cj))=C C C C C C C C \"U+A%BB	C C C C C C C C C C C C C C C C C Cs   AAAAr   dict | Nonec                L    | d         }| d         D ]}|d         |k    r|c S d S )Nr	   r   r&   r   )r   	committedsegments      r   _first_uncommitted_segmentr3   |   sC    ,-I$  5>I%%NNN &4r   F)waittimeoutr4   r5   float | Nonec               6   | sdS |t          j                    |z   nd }t          5  t                              |           }|	 d d d            dS |d         r|s	 d d d            dS |t                                           nD|t          j                    z
  }|dk    r	 d d d            dS t                              |           t                              |           }|	 d d d            dS |d         |d         |d         k    r	 d d d            dS t          |          }|	 d d d            dS |                    d          }| ||n|                    d          }|||d<   |	 d d d            dS |d         }	d|d<   d d d            n# 1 swxY w Y   	 |                                 n# t          $ rx t          
                    d	|            t          5  t                              |           }
|
d|
d<   t                                           d d d            n# 1 swxY w Y   Y dS w xY wt          5  t                              |           }
|
d|
d<   |	|
d         k    r|	|
d<   |
d         }|
d
         }|r;|d         d         |k    r)|                    d           |r|d         d         |k    )|r |d         d         |k    r|dz   |d         d<   t                                           d d d            n# 1 swxY w Y   dS )NFr   r   r   r	   r
   r&   Tz-commit_memory_session() failed for session %sr   r'   r$   )time	monotonicr   r   r   r4   r3   commit_memory_session	Exceptionlogger	exceptionr   pop)r   r
   r4   r5   deadliner   	remainingr2   effective_agentcaptured_generationre_entryr1   r   s                r   commit_session_memoryrD      s    u-4-@t~'))dH	 " "j))=" " " " " " " " K  	 " " " " " " " " !!!!$t~'7'77	>> " " " " " " " " 	***MM*--E}!" " " " " " " " K  	 %(>"???%" " " " " " " "& -U33?+" " " " " " " ", "++g.."','8eeeii>P>PO*#2 "9" " " " " " " ": &en!k=" " " " " " " " " " " " " " "@	--////   H*UUU 	$ 	$ }}Z00H#(-%!!###		$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$
 uu 
    ==,,$)H[!"X.D%EEE3F/0 !78I
+H  x{51Y>>Q   x{51Y>> 5HQK0I=='01}G$                              4s   FF)9F/7F3FF::FFF!F&F; ;,H=';H."H=.H2	2H=5H2	6H=<H=B;LLLc                    	 t           5  d t                                          D             } d d d            n# 1 swxY w Y   | sd S d}| D ]}t          |d          rd}|s*t                              dt          |                      d S )NTc                <    g | ]\  }}|d          |d         k    |S )r   r	   r   ).0sidr   s      r   
<listcomp>z)drain_all_on_shutdown.<locals>.<listcomp>   s2    www
U5CVY^_uYvCvCvCvCvCvr   F)r4   z<drain_all_on_shutdown: stopped with uncommitted sessions: %s)r-   r   itemsrD   r<   debugsorted)snapshotmade_progressrH   s      r   drain_all_on_shutdownrO      s     	x 	xwwioo.?.?wwwH	x 	x 	x 	x 	x 	x 	x 	x 	x 	x 	x 	x 	x 	x 	x 	F 	% 	%C$St444 % $ 	LLWY_`hYiYijjjFs   $9= =)r   r   )r   r   )r   r   r   r   )r   r   r   r"   )r   r   r   r+   )r   r   r   r/   r   )r   r   r4   r+   r5   r6   r   r+   )__doc__
__future__r   logging	threadingr8   	getLogger__name__r<   Lockr-   	Conditionr   r   __annotations__r   r   r   r!   r*   r.   r3   rD   rO   r   r   r   <module>rY      sq       D # " " " " "      		8	$	$	 Y ''
	                             37      &C C C C   <ufj < < < < < <~     r   