
    j*                        U d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	m
Z
mZmZmZ dZdZdZd	Zd
d
d
dddZded<   d2dZd3dZd4dZd5dZd6dZd6dZefd7d#Zd8d$Zd9d(Zd:d+Zdddd,d;d0Zd1gZdS )<u  Session recap — summarize what's happened in the current session.

Inspired by Claude Code's `/recap` command (v2.1.114, April 2026), which
shows a one-line summary of what happened while a terminal was unfocused
so users juggling multiple sessions can re-orient quickly.

Source: https://code.claude.com/docs/en/whats-new/2026-w17

Differences from Claude Code:
    - Pure local computation from the in-memory conversation history. No
      LLM call, no auxiliary model, no prompt-cache invalidation. A
      recap should be instant and free.
    - Works unchanged on CLI and every gateway platform (Telegram,
      Discord, Slack, …) because both call into the same ``build_recap``
      helper. Claude Code only shows this on the CLI.
    - Tailored to hermes-agent's tool vocabulary (``terminal``, ``patch``,
      ``write_file``, ``delegate_task``, ``browser_*``, ``web_*``) — the
      recap surfaces which classes of work were most active.
    )annotationsN)Counter)AnyIterableListMappingOptionalSequenceTuple            path	file_path)
write_filepatch	read_fileskill_manage
skill_viewzMapping[str, str]_FILE_EDIT_TOOLSvaluer   returnstrc                   | dS t          | t                    r| S t          | t                    rg }| D ]}t          |t                    r|                    |           -t          |t                    rA|                    d          }t          |t                    r|r|                    |           d                    |          S t          |           S )zFlatten assistant/user ``content`` into a plain string.

    Content can be a string or a list of content blocks (for multimodal
    or reasoning models). We concatenate every text-like block and
    ignore the rest.
    N text
)
isinstancer   listappendr   getjoin)r   partsblockr   s       7/usr/local/lib/hermes-agent/hermes_cli/session_recap.py_coerce_textr'   1   s     }r% % 
  	' 	'E%%% U###%)) 'yy((dC(( 'T 'LL&&&yyu::    	tool_callTuple[str, Mapping[str, Any]]c                   t          | t                    sdi fS |                     d          pi }t          |t                    sdi fS t          |                    d          pd          pd}|                    d          }t          |t                    r||fS t          |t                    rJ|rH	 ddl}|                    |          }t          |t                    r||fS n# t          $ r |i fcY S w xY w|i fS )zExtract ``(name, arguments_dict)`` from a tool_call entry.

    ``arguments`` may be a JSON string or a dict depending on provider.
    Return an empty dict if it cannot be parsed.
    r   functionname	argumentsr   N)r   r   r"   r   jsonloads	Exception)r)   fnr-   raw_argsr/   parseds         r&   _tool_call_name_and_argsr5   J   s(    i)) 2v	z	"	"	(bBb'"" 2vrvvf~~#$$*Dvvk""H(G$$ X~(C   X 	KKKZZ))F&'** $V|#$ 	 	 	8OOO	8Os   61C) )C:9C:messagesSequence[Mapping[str, Any]]'Iterable[Tuple[str, Mapping[str, Any]]]c              #    K   | D ]~}t          |t                    s|                    d          dk    r2|                    d          pg }t          |t                    s_|D ]}t	          |          \  }}|r||fV  d S )Nrole	assistant
tool_calls)r   r   r"   r    r5   )r6   msgr<   tcr-   argss         r&   _iter_assistant_tool_callsr@   e   s        ! !#w'' 	776??k))WW\**0b
*d++ 	 	! 	!B1"55JD$ !Dj   	!! !r(   Tuple[int, int, int]c                    dx}x}}| D ]P}t          |t                    s|                    d          }|dk    r|dz  }9|dk    r|dz  }E|dk    r|dz  }Q|||fS )zGReturn ``(user_turn_count, assistant_turn_count, tool_message_count)``.r   r:   user   r;   tool)r   r   r"   )r6   users
assistantstoolsr=   r:   s         r&   _count_visible_turnsrI   v   s     "#"E"J 	 	#w'' 	wwv6>>QJEE[  !OJJV^^QJE*e##r(   Optional[str]c                    t          |           D ]j}t          |t                    rS|                    d          dk    r:t	          |                    d                                                    }|r|c S kd S )Nr:   rC   contentreversedr   r   r"   r'   stripr6   r=   r   s      r&   _latest_user_promptrQ      s}     !!  c7## 	6(A(A	 2 23399;;D 4r(   c                    t          |           D ]l}t          |t                    s|                    d          dk    r2t	          |                    d                                                    }|r|c S md S )Nr:   r;   rL   rM   rP   s      r&   _latest_assistant_textrS      s     !!  #w'' 	776??k))CGGI..//5577 	KKK	4r(   windowintList[Mapping[str, Any]]c                $   d}d}t          t          |           dz
  dd          D ]E}| |         }t          |t                    r&|                    d          dv r|dz  }||k    r|} nFt          |           S t          | |d                   S )a  Return the tail slice of ``messages`` covering at most ``window``
    user+assistant turns (tool messages ride along inside the window).

    Iterating from the end, we count user and assistant messages and
    keep everything from the first message that falls within the window.
    r   rD   r:   >   rC   r;   N)rangelenr   r   r"   r    )r6   rT   countcutir=   s         r&   _recent_windowr^      s     E
C3x==1$b"--  qkc7## 	;P(P(PQJEH~~r(   c                   | s| S 	 t           j                            t           j                            |                     }t          j                    }||k    rdS |                    |t           j        z             r|t          |          dz   d         S t           j                            d          }|                    |t           j        z             rd|t          |          dz   d         z   S |S # t          $ r | cY S w xY w)zFShow a path relative to cwd when possible, otherwise with ~ expansion..rD   N~z~/)	osr   abspath
expandusergetcwd
startswithseprZ   r1   )r   abs_pathcwdhomes       r&   _shortened_pathrk      s     7??27#5#5d#;#;<<ikks??3sRV|,, 	,CHHqLNN++w!!#&&tbf}-- 	4(3t99q=??333   s%   AC9 ;C9 AC9 7C9 9DDr<   'Sequence[Tuple[str, Mapping[str, Any]]]'Tuple[List[Tuple[str, int]], List[str]]c                   t                      }g }t                      }t          t          |                     D ]\  }}||xx         dz  cc<   t                              |          }|rg|                    |          }t          |t                    r=|r;||vr7|                    |           |	                    t          |                     t          |                                d           }||fS )a  Return ``(tool_counts_sorted, recently_edited_files)``.

    ``tool_counts_sorted`` is descending by count, keeping the full list
    so callers can truncate for display. ``recently_edited_files`` lists
    distinct paths (most recent first) from file-editing tools.
    rD   c                $    | d          | d         fS )NrD   r    )kvs    r&   <lambda>z*_summarise_tool_activity.<locals>.<lambda>   s    2a5&"Q% r(   )key)r   setrN   r    r   r"   r   r   addr!   rk   sorteditems)	r<   counter
files_seen	files_setr-   r?   arg_keyr   tool_countss	            r&   _summarise_tool_activityr}      s     $IIGJ%%ItJ//00 9 9
d"&&t,, 	988G$$D$$$ 9 9$i2G2Gd###!!/$"7"7888 .H.HIIIK
""r(   r   limitc                    d                     |                                           } t          |           |k    r| S | d |dz
                                           dz   S )N rD   u   …)r#   splitrZ   rstrip)r   r~   s     r&   	_truncater      sU    88DJJLL!!D
4yyE%!)##%%--r(   )session_title
session_idplatformr   r   r   c          	        |}g }dg}|r|                     d|            n"|r |                     d|dd                     |                     d                    |                     | s*|                     d           d                    |          S t          |           \  }}}	t          |           }
t          |
          \  }}}| d|d	k    rd
nd d| d|d	k    rdnd }||f||fk    r|d| d| dz  }|                     d| d|	 d|	d	k    rd
nd            t	          t          |
                    }t          |          \  }}|r`d                    d |dd         D                       }t          |          dz
  }|dk    r	|d| dz  }|                     d|            |rj|dt                   }t          |          t          |          z
  }d                    |          }|dk    r	|d| dz  }|                     d|            t          |
          }|r+|                     dt          |t                                t          |
          }|r+|                     dt          |t                                t          |          dk    r|                     d            d                    |          S )!uW  Build a multi-line recap of recent activity.

    Inputs:
        messages: the full conversation history as a list of
            chat-completion-style dicts (``role``, ``content``,
            ``tool_calls``, …).
        session_title: optional human title (from SessionDB).
        session_id: optional session id.
        platform: optional hint (``"cli"``, ``"telegram"``, …). Does not
            change behavior today but is accepted for forward compat.

    The output is plain text designed to render well in both a terminal
    (with 80-col wrapping) and a gateway message bubble.
    zSession recapu   — N   r   u(     (nothing to recap — no messages yet)r   z
 user turnrD   sr   z / z assistant repliesyz (of /z total)z
  Recent: z, z tool resultc              3  *   K   | ]\  }}| d | V  dS )   ×Nrp   ).0r-   r[   s      r&   	<genexpr>zbuild_recap.<locals>.<genexpr>   s4      NN{tU4**5**NNNNNNr(   r   r   z (+z more)z  Tools used: z  Files touched: z  Last ask: z  Last reply:    z,  (no assistant activity yet in this window))r!   r#   rI   r^   r    r@   r}   rZ   _MAX_FILES_LISTEDrQ   r   _PROMPT_PREVIEW_CHARSrS   _ASSISTANT_PREVIEW_CHARS)r6   r   r   r   _linesheader_bitsrF   rG   	tool_msgsrT   	win_userswin_assistantsscoper<   r|   filestopextrashownentrylatest_userlatest_replys                          r&   build_recapr      s   * 	AE-.K 41-112222	 42*RaR.22333	LL+&&'''  ?@@@yy#7#A#A E:yH%%F#7#?#? I~q  	R 	RyA~~2 	R 	R	R 	R3AQ3F3F%%C	R 	R 
 	zy.9994444444	LL]e]]y]]YRS^^ccY[]]^^^08899J1*==K -iiNNk"1"oNNNNNK  1$199&&&&&C+c++,,, 2((()E

SZZ'		%  199(5((((E000111%f--K USIk;P$Q$QSSTTT)&11L [Yi>V&W&WYYZZZ
5zzQCDDD99Ur(   r   )r   r   r   r   )r)   r   r   r*   )r6   r7   r   r8   )r6   r7   r   rA   )r6   r7   r   rJ   )r6   r7   rT   rU   r   rV   )r   r   r   r   )r<   rl   r   rm   )r   r   r~   rU   r   r   )
r6   r7   r   rJ   r   rJ   r   rJ   r   r   ) __doc__
__future__r   rb   collectionsr   typingr   r   r   r   r	   r
   r   _RECENT_TURN_WINDOWr   r   r   r   __annotations__r'   r5   r@   rI   rQ   rS   r^   rk   r}   r   r   __all__rp   r(   r&   <module>r      s    & # " " " " " 				       J J J J J J J J J J J J J J J J J J        
 ' '        2   6! ! ! !"$ $ $ $$       :M         .   &# # # #8. . . . $( $"K K K K K K\ /r(   