
    epj{x                         d Z ddlZddlZddlZddlmZmZmZmZ ddl	m
Z
 ddlmZmZmZmZ  ej        d          Z G d d          ZdS )	u  Session listing/rich rows, export, and import (portability) for SessionDB.

Mixin contract: this is a plain mixin class consumed by
``hermes_state.SessionDB``. It defines no ``__init__`` and no state of its
own; methods access the host's attributes (``self._conn``, ``self.db_path``,
``self._execute_write`` and other SessionDB methods) established by
``SessionDB.__init__``. It must never import hermes_state (cycle) — shared
module-level constants live in hermes_state_common.
    N)AnyDictListOptional)SKILL_SCAFFOLD_SQL_LIKE)
SCHEMA_SQL_PREVIEW_RAW_SELECT_shape_preview_sql_session_last_activehermes_statec                   h   e Zd ZdZedefd            Zd(dedee	ee
f                  fdZ	 	 d)d	ed
ededee	ee
f                  fdZd(dededee	ee
f                  fdZ	 d(dede	ee	ee
f         f         fdZd(dededee	ee
f                  fdZd*d
edee	ee
f                  fdZdedefdZdedee	ee
f                  fdZdedee	ee
f                  fdZd+dedee	ee
f                  fdZede
dedee         fd            Zede
dedee         fd            Zede
dee         fd            Zede
dedee         fd            Zed,de
d edefd!            Zede
de
fd"            Zed#eded$ede	ee
f         fd%            Zd&ee	ee
f                  de	ee
f         fd'Z dS )-SessionPortabilityMixinu<   See module docstring — mixin for SessionDB (Port cluster).returnc                       j         F                     t                    d         }d                     fd|D                        _          j         S )zSELECT list for compact_rows: every ``sessions`` column declared in
        SCHEMA_SQL except prompt storage internals, aliased with the ``s``
        prefix used by list_sessions_rich/_get_session_rich_row queries.Nsessionsz, c              3   4   K   | ]}|j         vd | V  dS )zs.N)_SESSION_COMPACT_EXCLUDED).0nameclss     >/home/thesage/.hermes/hermes-agent/hermes_state_portability.py	<genexpr>z@SessionPortabilityMixin._compact_session_cols.<locals>.<genexpr>'   sC       6 6 $s<<< T<<<<6 6    )_session_compact_cols_sql_parse_schema_columnsr   join)r   declareds   ` r   _compact_session_colsz-SessionPortabilityMixin._compact_session_cols    sm    
 (000<<ZHH,0II 6 6 6 6(06 6 6 - -C) ,,r   Finclude_archivedc                     d}|s|dz  }| j         5  | j                            d| d                                          }ddd           n# 1 swxY w Y   d |D             S )u  Distinct non-empty session cwds with usage stats, for repo discovery.

        Aggregates across ALL session history (not a single page), so the desktop
        can surface every git repo the user has worked in — not just the repos
        that happen to be in the currently-loaded recents. Children/branches
        count: a worktree session is still a real workspace signal.
        z#cwd IS NOT NULL AND TRIM(cwd) != ''z AND archived = 0zsSELECT cwd AS cwd, COUNT(*) AS sessions, MAX(COALESCE(ended_at, started_at, 0)) AS last_active FROM sessions WHERE z GROUP BY cwdNc                 z    g | ]8}|d          t          |d         pd          t          |d         pd          d9S )cwdr   r   last_active)r"   r   r#   )intfloat)r   rs     r   
<listcomp>zASessionPortabilityMixin.distinct_session_cwds.<locals>.<listcomp>>   sa     
 
 
 	 x* 233$Q}%5%:;; 
 
 
r   )_lock_connexecutefetchall)selfr   whererowss       r   distinct_session_cwdsz-SessionPortabilityMixin.distinct_session_cwds-   s     6 	)((EZ 	 	:%%<',< < <  hjj	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	
 
 
 
 
 	
s   1AAA   r   job_idlimitoffsetc                    d| d}|dd         t          t          |d                   dz             z   }dt           dt          d           d	}| j        5  | j                            |||||f          }|                                }ddd           n# 1 swxY w Y   g }	|D ]R}
|                     |
          }t          |
                    d
d                    |d<   |	                    |           S|	S )u[  List the run sessions produced by a single cron job, newest first.

        Cron runs are flat, independent sessions whose id is
        ``cron_{job_id}_{timestamp}`` (see ``cron/scheduler.run_job``). They are
        never compression roots and never branch, so this deliberately skips the
        ``list_sessions_rich`` recursive compression-chain CTE / leading-wildcard
        ``id_query`` path — that path seeds from *every* ``source='cron'`` row in
        the DB and only filters to one job's runs after the scan, so it scales
        with the whole cron pile (a heavy history makes the desktop run-history
        endpoint time out before it eventually populates).

        Instead this binds to one job with a ``[prefix, prefix_hi)`` range over
        the id (an index range scan, not a ``%...%`` substring), filters
        ``source='cron'``, and orders by ``started_at DESC``. Work scales with
        the requested window, not the total cron history.

        Returns the same enriched row shape as ``list_sessions_rich`` (adds
        ``preview`` + ``last_active``) so callers can reuse it.
        cron__N   z
            SELECT s.*,
                COALESCE(sp.prompt, s.system_prompt) AS _system_prompt_resolved,
                COALESCE(
                    (SELECT   
                     FROM messages m
                     WHERE m.session_id = s.id AND m.role = 'user' AND m.content IS NOT NULL
                     ORDER BY m.timestamp, m.id LIMIT 1),
                    ''
                ) AS _preview_raw,
                sa   AS last_active
            FROM sessions s
            LEFT JOIN system_prompts sp ON sp.hash = s.system_prompt_hash
            WHERE s.source = 'cron' AND s.id >= ? AND s.id < ?
            ORDER BY s.started_at DESC, s.id DESC
            LIMIT ? OFFSET ?
        _preview_raw preview)chrordr	   r   r(   r)   r*   r+   _session_row_dictr
   popappend)r,   r1   r2   r3   prefix	prefix_hiquerycursorr.   runsrowr:   s               r   list_cron_job_runsz*SessionPortabilityMixin.list_cron_job_runsG   s]   2 #"""
 3B3K#c&*oo&9":"::	 1	  *#..  " Z 	% 	%Z''	5&/QRRF??$$D	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% &( 	 	C&&s++A)!%%*C*CDDAiLKKNNNNs   4BB B
session_idcompact_rowsc                 X    |                      |g|                              |          S )a  Fetch a single session with the same enriched columns as
        ``list_sessions_rich`` (preview + last_active). Returns None if the
        session doesn't exist.

        Pass ``compact_rows=True`` to omit the ``system_prompt`` blob (see
        ``list_sessions_rich`` for details).

        Thin wrapper over ``_get_session_rich_rows_batch`` so the enriched
        SELECT lives in exactly one place.
        rK   )_get_session_rich_rows_batchgetr,   rJ   rK   s      r   _get_session_rich_rowz-SessionPortabilityMixin._get_session_rich_row   s3     00L| 1 
 

#j//	r   c                 4   d |D             }|si S d}t          |          |k    rZi }t          dt          |          |          D ]7}|                    |                     ||||z            |                     8|S |                                  |r|                                 nd}d                    d |D                       }|rdnd	}	|rdnd
}
d| |	 dt           dt          d           d|
 d| d}| j	        5  | j
                            ||          }|                                }ddd           n# 1 swxY w Y   i }|D ]H}|                     |          }t          |                    dd                    |d<   |||d         <   I|S )a  Fetch multiple sessions with the same enriched columns as
        ``_get_session_rich_row``, in a single query.

        Used by ``list_sessions_rich``'s compression-tip projection to resolve
        every tip row for a page in one round trip instead of one query per
        compression-root row. Returns a dict keyed by session id; ids that
        don't exist are simply absent from the result (same as
        ``_get_session_rich_row`` returning ``None`` for them).
        c                     g | ]}||S  rT   )r   sids     r   r'   zHSessionPortabilityMixin._get_session_rich_rows_batch.<locals>.<listcomp>   s    111sS1s111r   i  r   rM   zs.*,c              3      K   | ]}d V  dS )?NrT   )r   r6   s     r   r   zGSessionPortabilityMixin._get_session_rich_rows_batch.<locals>.<genexpr>   s"      11111111r   r<   zA, COALESCE(sp.prompt, s.system_prompt) AS _system_prompt_resolvedz=LEFT JOIN system_prompts sp ON sp.hash = s.system_prompt_hashz
            SELECT z8,
                COALESCE(
                    (SELECT r9   r:   z8 AS last_active
            FROM sessions s
            z
            WHERE s.id IN (z
)
        Nr;   r=   id)lenrangeupdaterN   flush_token_countsr   r   r	   r   r(   r)   r*   r+   r@   r
   rA   )r,   session_idsrK   ids_CHUNKresultstart_selplaceholdersprompt_selectprompt_joinrE   rF   r.   rH   r:   s                   r   rN   z4SessionPortabilityMixin._get_session_rich_rows_batch   sn    21k111 	I s88f02Fq#c((F33  55E%&.01 6     
 M!!!/;Ft))+++xx11S11111 UBBT 	
  QBBP 	'  1  *#..    )   Z 	% 	%Z''s33F??$$D	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% -/ 	  	 C&&s++A)!%%*C*CDDAiLF1T7OOs   0E  EEc                 0    |                      ||          S )a  Public wrapper for :meth:`_get_session_rich_row`.

        Exposes the single-session enriched row (same columns as
        ``list_sessions_rich``: preview + last_active) for callers outside
        this module, e.g. the web server's session-search hydration.
        rM   )rQ   rP   s      r   get_session_rich_rowz,SessionPortabilityMixin.get_session_rich_row   s     ))*<)PPPr      c                     | j         5  | j                            dt          t	          |          f                                          }ddd           n# 1 swxY w Y   d |D             S )uy  Titled sessions whose first user turn was a ``/skill`` invocation.

        Those titles were generated from the expanded message, which embeds the
        whole skill body — so they describe the skill rather than the request.
        Returns ``id``, ``title``, and the full first-turn ``content`` so a
        caller can re-derive what the user typed. Newest first.
        a  
                SELECT s.id, s.title, m.content
                FROM sessions s
                JOIN messages m ON m.id = (
                    SELECT m2.id FROM messages m2
                    WHERE m2.session_id = s.id AND m2.role = 'user'
                      AND m2.content IS NOT NULL
                    ORDER BY m2.timestamp, m2.id LIMIT 1
                )
                WHERE s.title IS NOT NULL AND m.content LIKE ?
                ORDER BY s.started_at DESC
                LIMIT ?
                Nc                 ,    g | ]}t          |          S rT   )dict)r   rH   s     r   r'   zJSessionPortabilityMixin.list_skill_scaffolded_sessions.<locals>.<listcomp>   s    ***cS		***r   )r(   r)   r*   r   r$   r+   )r,   r2   r.   s      r   list_skill_scaffolded_sessionsz6SessionPortabilityMixin.list_skill_scaffolded_sessions   s     Z 	 	:%% )#e**5  hjj 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	" +*T****s   AAAAc                    | j         5  | j                            d|f                                          }ddd           n# 1 swxY w Y   |sdS |                     |d                   }t          |t                    r|ndS )zThe session's first assistant reply as plain text ('' when none).

        Pairs with :meth:`list_skill_scaffolded_sessions` so a re-title can feed
        the titler the same (request, reply) shape the live path uses.
        zSELECT content FROM messages WHERE session_id = ? AND role = 'assistant' AND content IS NOT NULL ORDER BY timestamp, id LIMIT 1Nr<   content)r(   r)   r*   fetchone_decode_content
isinstancestr)r,   rJ   rH   decodeds       r   get_first_assistant_textz0SessionPortabilityMixin.get_first_assistant_text   s     Z 	 	*$$1 	 
 hjj 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	  	2&&s9~66$Wc22:ww:s   /AA
Ac                 n    |                      |          }|sdS |                     |          }i |d|iS )z8Export a single session with all its messages as a dict.Nmessages)get_sessionget_messages)r,   rJ   sessionrw   s       r   export_sessionz&SessionPortabilityMixin.export_session
  sJ    "":.. 	4$$Z000'0:x000r   c                 R   |                      |          }|sdS g }|D ].}|                     |          }|r|                    |           /|sdS t          |d                   }t	          d |D                       }||d<   d |D             |d<   ||d<   d |D             |d	<   |S )
z9Export a compression lineage as one logical session dict.Nr7   c              3   ^   K   | ](}t          |                    d           pg           V  )dS )rw   N)rZ   rO   r   segs     r   r   zASessionPortabilityMixin.export_session_lineage.<locals>.<genexpr>  s;      PPS!4!4!:;;PPPPPPr   segmentsc                     g | ]
}|d          S )rY   rT   r~   s     r   r'   zBSessionPortabilityMixin.export_session_lineage.<locals>.<listcomp>!  s    &E&E&ESs4y&E&E&Er   lineage_session_idsmessage_countc                 F    g | ]}|                     d           pg D ]}|S )rw   )rO   )r   r   msgs      r   r'   zBSessionPortabilityMixin.export_session_lineage.<locals>.<listcomp>#  s7    [[[C
@S@S@YWY[[C[[[[r   rw   )get_compression_lineager{   rB   rl   sum)r,   rJ   lineage_idsr   rU   segmentbasetotal_messagess           r   export_session_lineagez.SessionPortabilityMixin.export_session_lineage  s    22:>> 	4 	) 	)C))#..G )((( 	4HRL!!PPxPPPPP#Z&E&EH&E&E&E"# ._[[8[[[Zr   Nsourcec                     |                      |d          }g }|D ]8}|                     |d                   }|                    i |d|i           9|S )z
        Export all sessions (with messages) as a list of dicts.
        Suitable for writing to a JSONL file for backup/analysis.
        i )r   r2   rY   rw   )search_sessionsry   rB   )r,   r   r   resultsrz   rw   s         r   
export_allz"SessionPortabilityMixin.export_all&  sq    
 ''vV'DD 	> 	>G((77HNN<g<z8<<====r   valuefieldc                 \    | d S t          | t                    r| S t          | d          )Nz must be a string)rr   rs   
ValueError)r   r   s     r   _import_text_or_nonez,SessionPortabilityMixin._import_text_or_none2  s:    =4eS!! 	LE444555r   c                    | d S t          | t                    ri	 t          j        |           }n*# t          j        $ r}t          | d          |d }~ww xY wt          |t                    st          | d          | S t          | t                    st          | d          	 t          j        |           S # t          t
          f$ r}t          | d          |d }~ww xY w)Nz must be valid JSONz must be a JSON objectz must be JSON serializable)	rr   rs   jsonloadsJSONDecodeErrorr   rl   dumps	TypeError)r   r   parsedexcs       r   _import_json_object_or_nonez3SessionPortabilityMixin._import_json_object_or_none:  s#   =4eS!! 	IE**' I I I E!>!>!>??SHIfd++ C E!A!A!ABBBL%&& 	?===>>>	L:e$$$:& 	L 	L 	LAAABBK	Ls,   0 AAA+B? ?C(C##C(c                 Z    | d S 	 t          |           S # t          t          f$ r Y d S w xY wN)r%   r   r   r   s    r   _float_or_nonez&SessionPortabilityMixin._float_or_noneM  sD    =4	<<:& 	 	 	44	s    **c                     | d S 	 t          |           S # t          t          f$ r}t          | d          |d }~ww xY w)Nz must be an integerr$   r   r   )r   r   r   s      r   _import_int_or_nonez+SessionPortabilityMixin._import_int_or_noneV  s_    =4	Eu:::& 	E 	E 	E:::;;D	Es    >9>defaultc                 \    | |S 	 t          |           S # t          t          f$ r |cY S w xY wr   r   )r   r   s     r   _int_or_defaultz'SessionPortabilityMixin._int_or_default_  sF    =N	u:::& 	 	 	NNN	s    ++c                     t          | t                    s| S 	 t          j        |           S # t          j        t
          f$ r | cY S w xY wr   )rr   rs   r   r   r   r   r   s    r   _reasoning_json_valuez-SessionPortabilityMixin._reasoning_json_valueh  sZ    %%% 	L	:e$$$$i0 	 	 	LLL	s   - AAindexerrorc                     | |d}|r||d<   |S )N)r   r   rJ   rT   )r   rJ   r   items       r   _import_errorz%SessionPortabilityMixin._import_errorq  s&    ).?? 	,!+Dr   r   c                 "    t          |t                    st          d          t          |           j        k    rt          d j         d          g g }t                      }d}d}d}d}t          |          D ]\  }}	t          |	t                    s+|                     	                    |dd                     Ft          |	                    d	          pd                                          }
|
s+|                     	                    |dd
                     |
|v r+|                     	                    ||
d                     |	                    d          pg }t          |t                    s,|                     	                    ||
d                     0t          |           j        k    r,|                     	                    ||
d                     tt          d |D                       r,|                     	                    ||
d                     	 t          t          j        |	dd                              d                    }nB# t$          t          f$ r. |                     	                    ||
d                     Y 0w xY w| j        k    r,|                     	                    ||
d                     k||z  }| j        k    r,|                     	                    ||
d                     	 t          |	          }|
|d	<                        |                    d          d          |d<                        |                    d          d          |d<   |D ].}                     |                    |          |          ||<   /g }t          |          D ]\  }}t          |          }|                    d          }t          |t                    r|st          d| d          |D ]5}|dk    r	                     |                    |          |          ||<   6                     |                    d          d          |d<   |                    |           nO# t          $ rB}|                     	                    ||
t          |                               Y d}~kd}~ww xY w|t          |          z  }| j        k    r,|                     	                    ||
d                     |                    |
                               |||d            |rdddd|d!S  fd"}                     |          S )#a  Import sessions exported by :meth:`export_session` or ``export_all``.

        Existing session IDs are skipped. Imported child sessions keep their
        parent only when that parent already exists or is included in the same
        import payload; otherwise the child is detached so partial imports don't
        fail foreign-key validation. Gateway routing, handoff, rewind, and other
        live runtime state are intentionally reset: this restores conversation
        history, not ownership of a live channel or process.

        Activity contract (#76354 review S4): export INCLUDES the live
        activity fields (``last_activity_at`` / ``last_activity_description``
        / ``last_activity_provenance``) because they are part of the durable
        row, but import deliberately RESETS them to NULL. Resurrecting a
        stale "working ..." label on a machine where no agent is running
        would fabricate activity the watchdog and session listings act on.
        This asymmetry is intentional and covered by regression
        (tests/gateway/test_watchdog_review_76354.py::test_s4_export_includes_activity_import_resets_it).
        zsessions must be a listzsessions must contain at most z entriesr   )r   user_idmodelsystem_prompt
end_reasonr"   
git_branchgit_repo_rootbilling_providerbilling_base_urlbilling_modecost_statuscost_sourcepricing_versiontitle)	roletool_call_id	tool_nameeffect_dispositionfinish_reason	reasoningreasoning_contentplatform_message_id
message_idr<   zsession must be an objectrY   zsession id is requiredzduplicate session idrw   zmessages must be a listz-messages exceeds the per-session import limitc              3   B   K   | ]}t          |t                     V  d S r   )rr   rl   )r   r   s     r   r   z:SessionPortabilityMixin.import_sessions.<locals>.<genexpr>  s/      AAz#t,,,AAAAAAr   z"messages must contain only objectsF)rV   :)ensure_ascii
separatorszutf-8z!session must be JSON serializablez%session exceeds the import size limitz#import exceeds the total size limitmodel_configparent_session_idr   z	messages[z!].role must be a non-empty stringtoken_countNz'messages exceeds the total import limit)r   rz   rw   )okimportedskippeddetachederrorsc                 L    g }g }g }d}D ]}|d         }|d         }t          |                    d          pd                                          }                     d|f                                          }	|	r|                    |                               |                    d                    }
|
t          j                    }
|                    d          rd	nd}                     |                    d
                    }                     di d|dt          |                    d          pd          d|                    d          d|                    d          d|                    d          d|d|
d                    |                    d                    d|                    d          d	                    |                    d                    d	                    |                    d                    d	                    |                    d                    d	                    |                    d                    d	                    |                    d                    d|                    d          d|                    d          d|                    d          |                    d          |                    d          |                    d                              |                    d                                        |                    d                     |                    d!          |                    d"          |                    d#          |                    d$          	                    |                    d%                    |d&           g }|D ]V}t          |          }d'D ]-}                    |                    |                    ||<   .|                    |           W                     ||          \  }}                     d(|||f           t          |                    d)          pd                                          }|r|                    ||f           |                    |           t          |          d*t           d+t           d,t          f fd-}|D ]p\  }}                     d|f                                          }|r% |||          s                     d.||f           U                    |d            |d	z  }qd/t          |          t          |          |||g d0S )1Nr   rz   rw   rY   r<   z+SELECT 1 FROM sessions WHERE id = ? LIMIT 1
started_atarchivedr8   r   a  INSERT INTO sessions (
                           id, source, user_id, model, model_config, system_prompt,
                           system_prompt_hash,
                           parent_session_id, started_at, ended_at, end_reason,
                           message_count, tool_call_count, input_tokens, output_tokens,
                           cache_read_tokens, cache_write_tokens, reasoning_tokens,
                           cwd, git_branch, git_repo_root,
                           billing_provider, billing_base_url, billing_mode,
                           estimated_cost_usd, actual_cost_usd, cost_status, cost_source,
                           pricing_version, title, api_call_count, archived
                       )
                       VALUES (
                           :id, :source, :user_id, :model, :model_config,
                           NULL, :system_prompt_hash, NULL, :started_at, :ended_at,
                           :end_reason, 0, 0, :input_tokens, :output_tokens,
                           :cache_read_tokens, :cache_write_tokens,
                           :reasoning_tokens, :cwd, :git_branch, :git_repo_root,
                           :billing_provider, :billing_base_url, :billing_mode,
                           :estimated_cost_usd, :actual_cost_usd, :cost_status,
                           :cost_source, :pricing_version, :title,
                           :api_call_count, :archived
                       )r   importr   r   r   system_prompt_hashended_atr   input_tokensoutput_tokenscache_read_tokenscache_write_tokensreasoning_tokensr"   r   r   r   r   r   estimated_cost_usdactual_cost_usdr   r   r   r   api_call_count)r   r   r   r   r   r   r   r   r   r   r   )reasoning_detailscodex_reasoning_itemscodex_message_itemszGUPDATE sessions SET message_count = ?, tool_call_count = ? WHERE id = ?r   rJ   	parent_idr   c                     | h}|}|r_||v rdS |                     |           |v r	|         }*                    d|f                                          }|dS |d         }|_dS )NTz;SELECT parent_session_id FROM sessions WHERE id = ? LIMIT 1Fr   )addr*   rp   )rJ   r   seencurrentrH   connparent_by_childs        r   _would_create_cyclezQSessionPortabilityMixin.import_sessions.<locals>._do.<locals>._would_create_cycle  s    "|# 7$#tHHW%%%/11"1'": ,,U 
  hjj  {$u!"56G  7 ur   z6UPDATE sessions SET parent_session_id = ? WHERE id = ?T)r   r   r   r   imported_idsskipped_idsr   )rs   rO   stripr*   rp   rB   r   time_store_system_promptr   rl   r   _insert_message_rowsboolrA   rZ   )r   r   r   parent_updatesr   r   rawrw   rJ   existsr   r   r   sanitized_messagesr   cleankeyr   total_tool_callsr   r   parent_existsr   
normalizedr,   s   `                     @r   _doz4SessionPortabilityMixin.import_sessions.<locals>._do%  sQ   &(L%'K46NH" m0 m09o
+ !4"55;;==
AM  (**   &&z222!001F1FGG
%!%J #
 3 3:11%)%>%>#''/22& &" ,'j' #cggh&7&7&C8"D"D' "3779#5#5'  !1!1	'
 '(?(?' -.@' %j' #D$7$7
8K8K$L$L' %cggl&;&;' '(<(<SWW^=T=T(U(U' ()=)=cggo>V>V)W)W' ,T-A-AGG$788. .' -d.B.BGG$899/ /'$ +D,@,@GG$677- -%'* swwu~~+', %cggl&;&;-'. ()A)A/'0 -0GG4F,G,G,/GG4F,G,G(+(?(?.2.A.AGG$899/ / ,0+>+>GG$566, , (+ww}'='='*ww}'='=+.773D+E+E!$!1!1*.*>*>swwGW?X?X*Y*Y$,M' ' '/? ? ?B <>"# 5 5C IIE  P P
 &*%?%?		#%O%Oc

&--e4444373L3L&4 40 0
 ]#%5zB  
  (; < < BCCIIKK	 C")):y*ABBB##J////">22O         & *8 " "%
I $AL! ! (**  ! 
")<)<Z)S)S 
"LLP"J/    $''
D999MHH --{++$ ,*  r   )rr   listr   rZ   _IMPORT_MAX_SESSIONSset	enumeraterl   rB   r   rs   rO   r    _IMPORT_MAX_MESSAGES_PER_SESSIONanyr   r   encoder   _IMPORT_MAX_SESSION_BYTES_IMPORT_MAX_TOTAL_BYTESr   r   r   _IMPORT_MAX_TOTAL_MESSAGESr   _execute_write)r,   r   r   seen_idsr   total_bytessession_text_fieldsmessage_text_fieldsr   r   rJ   rw   session_bytesclean_sessionr   clean_messagesmessage_indexmessageclean_messager   r   r   r   s   `                     @r   import_sessionsz'SessionPortabilityMixin.import_sessionsx  s   & (D)) 	86777x==4444T1JTTT   ,.
') UU
"

 $H-- f	 f	JE3c4(( d00<WXXYYYSWWT]]0b117799J d00<TUUVVVX%%d00
DZ[[\\\wwz**0bHh-- d00
D]^^___8}}tDDD&&"G    AAAAAAA &&"<     #Js:NNNUUV]^^! ! z*   &&uj:]^^   	
 t===&&uj:abb   =(KT999&&uj:_``     $S		&0d#040P0P!%%n55~1 1n- 6:5N5N!%%&9::<O6 612 1  E+/+D+D%))%00%, ,M%(( 8:.7.A.A 9 9*M7$(MMM(,,V44D%dC00 g g()e])e)e)efff!4   F??$/3/H/H)--e44e0 0e,, 483K3K%))-88-4 4M-0 #))-88889    d00
CHHMMNNN c.111N ???&&"A    LL$$$M~VV     	   c	 c	 c	 c	 c	 c	J ""3'''s+   :7I22;J10J1(E;R$$
S0.7S++S0)F)r0   r   )ri   r   )r   )!__name__
__module____qualname____doc__classmethodrs   r   r   r   r   r   r/   r$   rI   r   rQ   rN   rh   rm   ru   r{   r   r   staticmethodr   r   r%   r   r   r   r   r   r  rT   r   r   r   r      s\       FF
-c 
- 
- 
- [
-
 
d 
tDQTVYQYNG[ 
 
 
 
: 	: :: : 	:
 
d38n	: : : :x  4 T\]abegjbj]kTl      16@ @)-@	c4S>!	"@ @ @ @DQ Qs Q$ QS[\`adfiai\jSk Q Q Q Q+ +C +$tCQTH~BV + + + +6;3 ;3 ; ; ; ;$1 1$sCx.1I 1 1 1 1 $sCx.9Q    (
 
 
T#s(^0D 
 
 
 
 6C 6 6 6 6 6 \6 L3 Ls Lx} L L L \L$ c huo    \ E3 Es Ex} E E E \E  s S     \ S S    \ S c # $sCx.    \R(T#s(^(< R(c3h R( R( R( R( R( R(r   r   )r  loggingr   r   typingr   r   r   r   agent.skill_commandsr   hermes_state_commonr   r	   r
   r   	getLoggerloggerr   rT   r   r   <module>r      s        , , , , , , , , , , , , 8 8 8 8 8 8            
	>	*	*m
( m
( m
( m
( m
( m
( m
( m
( m
( m
(r   