
    i                         U d Z ddlmZ ddlmZ ddlmZmZ ddlmZm	Z	m
Z
mZ ddlmZmZ d	d
gZddddddZded<   ddZddZddZ	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZy)a  Incremental session-summary derivation for :class:`SessionStore` adapters.

:func:`fold_session_summary` lets a store maintain a per-session
:class:`SessionSummaryEntry` sidecar incrementally inside ``append()`` so
``list_sessions_from_store()`` can fetch all metadata in a single
``list_session_summaries()`` call instead of N per-session ``load()`` calls.

Every derived field is append-incremental (set-once or last-wins) so adapters
never need to re-read previously appended entries.
    )annotations)datetime)Anycast   )SDKSessionInfo
SessionKeySessionStoreEntrySessionSummaryEntry   )_COMMAND_NAME_RE_SKIP_FIRST_PROMPT_PATTERNfold_session_summarysummary_entry_to_sdk_infocustom_titleai_titlelast_promptsummary_hint
git_branch)customTitleaiTitle
lastPromptsummary	gitBranchzdict[str, str]_LAST_WINS_FIELDSc                    t        | t              sy	 | j                  d      r| j                  dd      n| }t	        t        j                  |      j                         dz        S # t        $ r Y yw xY w)z>Parse an ISO-8601 timestamp string to Unix epoch milliseconds.NZz+00:00i  )	
isinstancestrendswithreplaceintr   fromisoformat	timestamp
ValueError)tsnorms     q/volume1/homes/robertsu/coba/app/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_summary.py_iso_to_epoch_msr)   '   sg    b#,.KK,<rzz#x("8))$/99;dBCC s   AA' '	A32A3c                   | j                  d      }t        |t              sg S |j                  d      }g }t        |t              r|j	                  |       |S t        |t
              ra|D ]\  }t        |t              s|j                  d      dk(  s)t        |j                  d      t              sI|j	                  |d          ^ |S )zEExtract text strings from a ``type=="user"`` entry's message content.messagecontenttypetext)getr   dictr   appendlist)entryr+   r,   textsblocks        r(   _entry_text_blocksr6   3   s    ii	"Ggt$	kk)$GE'3W L 
GT	"E5$'IIf%/uyy0#6U6]+  L    c                   | j                  d      ry|j                  d      dk7  ry|j                  d      du s|j                  d      du ry|j                  d      }t        |t              r4|j                  d	      }t        |t              rt	        d
 |D              ryt        |      D ]  }|j                  dd      j                         }|s&t        j                  |      }|r&| j                  d      s|j                  d      | d<   ct        j                  |      ryt        |      dkD  r|dd j                         dz   }|| d<   d| d<    y y)aB  Replicate ``_extract_first_prompt_from_head`` for a single parsed entry.

    Mutates ``data`` in place: sets ``first_prompt`` + ``first_prompt_locked``
    on a real match, or stashes a ``command_fallback`` for slash-command
    messages. Skips tool_result, isMeta, isCompactSummary, and auto-generated
    patterns.
    first_prompt_lockedNr-   userisMetaTisCompactSummaryr+   r,   c              3  h   K   | ]*  }t        |t              xr |j                  d       dk(   , yw)r-   tool_resultN)r   r0   r/   ).0bs     r(   	<genexpr>z%_fold_first_prompt.<locals>.<genexpr>Y   s0      -
LSqJq$BAEE&M]$BBGs   02
 command_fallbackr      u   …first_prompt)r/   r   r0   r2   anyr6   r!   stripr   searchgroupr   matchlenrstrip)datar3   r+   r,   rawresult	cmd_matchs          r(   _fold_first_promptrR   G   sI    xx%&yyF"yyd"eii0B&Ct&Kii	"G'4 ++i(gt$ -
LS-
 *
 !%(T3'--/$++F3	88./+4??1+='(%++F3v;DS\((*X5F%^&*"# )r7   c                   | | d   | d   t        | d         d}n	|d   di d}|d   }|D ]  }t        d|      }t        |j                  d            }d	|vr|j                  d
      du |d	<   d|vr|||d<   d|vr(|j                  d      }t	        |t
              r|r||d<   t        ||       t        j                         D ],  \  }	}
|j                  |	      }t	        |t
              s(|||
<   . |j                  d      dk(  s|j                  d      }t	        |t
              r	|r||d<   	|j                  dd        |S )u  Fold a batch of appended entries into the running summary for ``key``.

    Stores call this from inside ``append()`` to keep a
    :class:`SessionSummaryEntry` sidecar up to date without re-reading the
    transcript. ``prev`` is the previous summary for the same key (or ``None``
    for the first append).

    Do not call this for keys with a ``subpath`` — subagent transcripts must
    not contribute to the main session's summary. Guard with
    ``if key.get("subpath") is None:`` before calling.

    All derived state lives in the opaque ``data`` dict; stores persist it
    verbatim and do not interpret it.

    ``mtime`` is NOT touched by the fold — it is the sidecar's storage
    write time and must be stamped by the adapter after persisting. It has
    to share a clock with the ``mtime`` returned by
    :meth:`SessionStore.list_sessions` for the same session (typically file
    mtime, S3 ``LastModified``, Postgres ``updated_at``, or whatever native
    timestamp the adapter surfaces); deriving it from entry ISO timestamps
    would make every batched-write sidecar appear strictly older than the
    session's current mtime, defeating the fast-path staleness check. For a
    new session (``prev is None``) the fold returns ``mtime=0`` as a
    placeholder; the adapter is expected to overwrite it.

    ``created_at`` latches the first parseable entry timestamp. The disk
    lite-parse scans the head buffer for the first timestamp occurrence,
    so the two paths agree for any timestamp appearing within the head
    window.
    N
session_idmtimerN   )rT   rU   rN   r   dict[str, Any]r$   is_sidechainisSidechainT
created_atcwdr-   tag)
r0   r   r)   r/   r   r   rR   r   itemspop)prevkeyentriesr   rN   rO   r3   msrZ   srcdstvaltag_vals                r(   r   r   p   se   F |,']f&(
 "%\!2QK6?D %s+eii45%#(99]#;t#CD t#!#D))E"C#s#!U4')//1HC))C.C#s#S	 2
 99V%ii&G'3'G%U %= @ Nr7   c                   | d   }|j                  d      ry|j                  d      r|j                  d      n|j                  d      xs d}|j                  d      xs |j                  d      xs d}|xs( |j                  d	      xs |j                  d
      xs |}|syt        | d   || d   d|||j                  d      xs d|j                  d      xs |xs d|j                  d      xs d|j                  d      
      S )zConvert a :class:`SessionSummaryEntry` to :class:`SDKSessionInfo`.

    Returns ``None`` for sidechain sessions or sessions with no extractable
    summary, matching ``_parse_session_info_from_lite``'s filtering.
    rN   rW   Nr9   rF   rD   r   r   r   r   rT   rU   r   rZ   r[   rY   )
rT   r   last_modified	file_sizer   rF   r   rZ   r[   rY   )r/   r   )r3   project_pathrN   rF   r   r   s         r(   r   r      s     =Dxx 88)* 	 XX() 
	 
 88N+Ktxx
/CKtL 	88M"	88N#	 	  &Gn !!88L)1THHUO3|3tHHUO#t88L) r7   N)r&   r   returnz
int | None)r3   rV   rj   z	list[str])rN   rV   r3   rV   rj   None)r^   zSessionSummaryEntry | Noner_   r	   r`   zlist[SessionStoreEntry]rj   r   )r3   r   ri   z
str | Nonerj   zSDKSessionInfo | None)__doc__
__future__r   r   typingr   r   typesr   r	   r
   r   sessionsr   r   __all__r   __annotations__r)   r6   rR   r   r    r7   r(   <module>rt      s   	 #    C!#>
? "% > 	(&RM
$M	M %M 	M`((.8((r7   