
    FJjh                         d Z ddlZddlZddlmZmZ ddlmZmZ ddl	m
Z
mZ ddlmZ ddlmZ e
rdd	lmZ dd
lmZmZmZmZmZmZmZmZ  G d d      Zy)z3Claude SDK Client for interacting with Claude Code.    N)AsyncIterableAsyncIterator)asdictreplace)TYPE_CHECKINGAny   )	Transport)CLIConnectionError)MaterializedResume)ClaudeAgentOptionsContextUsageResponse	HookEventHookMatcherMcpStatusResponseMessagePermissionModeResultMessagec            	          e Zd ZdZ	 	 d'dedz  dedz  fdZdeee	e
   f   deee	eeef      f   fdZ	 d(d	eeeeef      z  dz  ddfd
Zd	eeeeef      z  dz  deeeef      ddfdZdee   fdZ	 d)d	eeeeef      z  deddfdZd*dZdeddfdZd(dedz  ddfdZdeddfdZdeddfdZdededdfdZdeddfdZdefdZde fdZ!deeef   dz  fdZ"dee   fd Z#d*d!Z$d+d"Z%d#ed$ed%edefd&Z&y),ClaudeSDKClienta  
    Client for bidirectional, interactive conversations with Claude Code.

    This client provides full control over the conversation flow with support
    for streaming, interrupts, and dynamic message sending. For simple one-shot
    queries, consider using the query() function instead.

    Key features:
    - **Bidirectional**: Send and receive messages at any time
    - **Stateful**: Maintains conversation context across messages
    - **Interactive**: Send follow-ups based on responses
    - **Control flow**: Support for interrupts and session management

    When to use ClaudeSDKClient:
    - Building chat interfaces or conversational UIs
    - Interactive debugging or exploration sessions
    - Multi-turn conversations with context
    - When you need to react to Claude's responses
    - Real-time applications with user input
    - When you need interrupt capabilities

    When to use query() instead:
    - Simple one-off questions
    - Batch processing of prompts
    - Fire-and-forget automation scripts
    - When all inputs are known upfront
    - Stateless operations

    See examples/streaming_mode.py for full examples of ClaudeSDKClient in
    different scenarios.

    Caveat: As of v0.0.20, you cannot use a ClaudeSDKClient instance across
    different async runtime contexts (e.g., different trio nurseries or asyncio
    task groups). The client internally maintains a persistent anyio task group
    for reading messages that remains active from connect() until disconnect().
    This means you must complete all operations with the client within the same
    async context where it was connected. Ideally, this limitation should not
    exist.
    Noptions	transportc                 b    |
t               }|| _        || _        d| _        d| _        d| _        y)zInitialize Claude SDK client.N)r   r   _custom_transport
_transport_query_materialized)selfr   r   s      a/Users/ahmed/devFolder/claude-voice/.venv/lib/python3.12/site-packages/claude_agent_sdk/client.py__init__zClaudeSDKClient.__init__C   s6     ?(*G!*,0"&8<    hooksreturnc                 2   i }|j                         D ]  \  }}g ||<   |D ]r  }t        |d      r|j                  ndt        |d      r|j                  ng d}t        |d      r|j                  |j                  |d<   ||   j                  |       t  |S )z4Convert HookMatcher format to internal Query format.matcherNr"   )r%   r"   timeout)itemshasattrr%   r"   r&   append)r   r"   internal_hookseventmatchersr%   internal_matchers          r   !_convert_hooks_to_internal_formatz1ClaudeSDKClient._convert_hooks_to_internal_formatQ   s     ;=${{}OE8$&N5!# 3:'92MwSW.5gw.GW]]R4  7I.7??3N29//$Y/u%,,-=> $  - r!   promptc                   K   ddl m} ddlm} dt        t
        t        t        f      fd}t        |t              r|n |       } || j                         | j                   || j                         d{   nd| _        	 | j                  ||       d{    y7 '7 # t        $ r | j                          d{  7    w xY ww)z2Connect to Claude with a prompt or message stream.r	   )materialize_resume_session)validate_session_store_optionsr#   c                    K   y wN r5   r!   r   _empty_streamz.ClaudeSDKClient.connect.<locals>._empty_streaml   s      s   N)_internal.session_resumer1   "_internal.session_store_validationr2   r   dictstrr   
isinstancer   r   r   r   _connect_innerBaseException
disconnect)r   r/   r1   r2   r6   actual_prompts         r   connectzClaudeSDKClient.connectc   s     
 	IV	]4S>%B 	 #-V]"C 	't||4 %%- -T\\::: 	
		%%fm<<< ;
 = 	 //###	sH   A7C9B!:CB% B#B%  C#B% %C	CC		Cr?   c                 	  K   ddl m} ddlm}m} ddlm} | j                  j                  rTt        |t              rt        d      | j                  j                  rt        d      t        | j                  d      }n| j                  }| j                   ||| j                        }| j                  r| j                  | _        n |||	      | _        | j                   j#                          d {    i }| j                  j$                  r~t        | j                  j$                  t&              rZ| j                  j$                  j)                         D ]3  \  }	}
t        |
t&              s|
j+                  d
      dk(  s,|
d   ||	<   5 t-        t.        j0                  j+                  dd            }t3        |dz  d      }d }| j                  j4                  }t        |t&              r7|j+                  d
      dk(  r#|j+                  d      }t        |t6              r|}d }| j                  j8                  ri| j                  j8                  j)                         D 	ci c]6  \  }	}|	t;        |      j)                         D ci c]  \  }}|	|| c}}8 }}}}	} || j                   d| j                  j                  | j                  j<                  r%| j?                  | j                  j<                        nd ||||| j                  j@                  	      | _!        | j                  jD                  | jB                  dtF        dt        dd ffd}| jB                  jI                   || j                  jD                  | j                  | j                  jJ                  || j                  jL                               | jB                  jO                          d {    | jB                  jQ                          d {    t        |t              rDdd|dd dd}| j                   jS                  tU        jV                  |      dz          d {    y |Ft        |tX              r5| jB                  j[                  | jB                  j]                  |             y y y 7 c c}}w c c}}}}	w 7 7 7 cw)Nr	   )Query)apply_materialized_optionsbuild_mirror_batcher)SubprocessCLITransportzmcan_use_tool callback requires streaming mode. Please provide prompt as an AsyncIterable instead of a string.zccan_use_tool callback cannot be used with permission_prompt_tool_name. Please use one or the other.stdio)permission_prompt_tool_name)r/   r   typesdkinstance CLAUDE_CODE_STREAM_CLOSE_TIMEOUT60000g     @@g      N@presetexclude_dynamic_sectionsT)	r   is_streaming_modecan_use_toolr"   sdk_mcp_serversinitialize_timeoutagentsrN   skillskeyerrorr#   c                 2   K   j                  | |       y wr4   )report_mirror_error)rU   rV   qs     r   _on_mirror_errorz8ClaudeSDKClient._connect_inner.<locals>._on_mirror_error   s     %%c51s   )storematerializedenvon_error
flush_modeuserrolecontentdefaultrH   messageparent_tool_use_id
session_id
)/_internal.queryrB   r7   rC   rD   "_internal.transport.subprocess_clirE   r   rP   r;   r:   
ValueErrorrG   r   r   r   r   r@   mcp_serversr9   r'   getintosenvironmaxsystem_promptboolrS   r   r"   r.   rT   r   session_storer   set_transcript_mirror_batcherr]   session_store_flushstart
initializewritejsondumpsr   
spawn_taskstream_input)r   r/   r?   rB   rC   rD   rE   r   rQ   nameconfiginitialize_timeout_msrR   rN   spedsagents_dict	agent_defkvrZ   rf   rY   s                         @r   r<   zClaudeSDKClient._connect_inner   s    
 	+	
 	O <<$$&#& U  ||77 3  dllPGllG)0$:L:LMG !!"44DO4$DO oo%%''' <<##
4<<3K3KT(R $ 8 8 > > @ffd+

60Be0K,2:,>OD) !A !$JJNN=wG!
 !!6!?F 15 \\''b$BFF6Nh$>&&34C#t$+.( 9=<< (,||':':'@'@'B'BOD) y(9(?(?(AS(A1Q]q!t(ASS'B   oo"22||!! 889K9KL+1%=<<&&
 <<%%1A2C 2 2 2 KK55$,,44!%!3!3((-#||?? kk!!!kk$$&&& fc"$*v>&*'	G //''

7(;d(BCCCJv}$EKK""4;;#;#;F#CD %F[ 	(: TJ 	"& Ds}   C4S7R08A:S3SC"S*%R9

R3R3R9
%ES,S-!SSAS!S"AS3R9
9	SSSc                   K   | j                   st        d      ddlm} | j                   j	                         2 3 d{   } ||      }|| 7 6 yw)z!Receive all messages from Claude.$Not connected. Call connect() first.r	   )parse_messageN)r   r   _internal.message_parserr   receive_messages)r   r   datarf   s       r   r   z ClaudeSDKClient.receive_messages  sQ     {{$%KLL;++668 	$#D)G"	8s,   8AAAAAAAArh   c                   K   | j                   r| j                  st        d      t        |t              rDdd|dd|d}| j                  j                  t        j                  |      dz          d{    y|2 3 d{   }d|vr||d<   | j                  j                  t        j                  |      dz          d{    L7 S7 I7 	6 yw)z
        Send a new request in streaming mode.

        Args:
            prompt: Either a string message or an async iterable of message dictionaries
            session_id: Session identifier for the conversation
        r   r`   ra   Nre   ri   rh   )r   r   r   r;   r:   rz   r{   r|   )r   r/   rh   rf   msgs        r   queryzClaudeSDKClient.query  s      {{$//$%KLL fc"$*v>&*(	G //''

7(;d(BCCC $ Dcs*(2C%oo++DJJsOd,BCCC DD D	 $sH   A2C4C5C<C C
C>CCC
CCCc                    K   | j                   st        d      | j                   j                          d{    y7 w)z7Send interrupt signal (only works with streaming mode).r   N)r   r   	interruptr   s    r   r   zClaudeSDKClient.interrupt9  s/     {{$%KLLkk##%%%s   5?=?modec                    K   | j                   st        d      | j                   j                  |       d{    y7 w)a  Change permission mode during conversation (only works with streaming mode).

        Args:
            mode: The permission mode to set. Valid options:
                - 'default': CLI prompts for dangerous tools
                - 'acceptEdits': Auto-accept file edits
                - 'plan': Plan-only mode (no tool execution)
                - 'bypassPermissions': Allow all tools (use with caution)
                - 'dontAsk': Deny anything not pre-approved by allow rules
                - 'auto': A model classifier approves or denies each tool call

        Example:
            ```python
            async with ClaudeSDKClient() as client:
                # Start with default permissions
                await client.query("Help me analyze this codebase")

                # Review mode done, switch to auto-accept edits
                await client.set_permission_mode('acceptEdits')
                await client.query("Now implement the fix we discussed")
            ```
        r   N)r   r   set_permission_mode)r   r   s     r   r   z#ClaudeSDKClient.set_permission_mode?  s3     . {{$%KLLkk--d333   6A >A modelc                    K   | j                   st        d      | j                   j                  |       d{    y7 w)a  Change the AI model during conversation (only works with streaming mode).

        Args:
            model: The model to use, or None to use default. Examples:
                - 'claude-sonnet-4-5'
                - 'claude-opus-4-1-20250805'
                - 'claude-opus-4-20250514'

        Example:
            ```python
            async with ClaudeSDKClient() as client:
                # Start with default model
                await client.query("Help me understand this problem")

                # Switch to a different model for implementation
                await client.set_model('claude-sonnet-4-5')
                await client.query("Now implement the solution")
            ```
        r   N)r   r   	set_model)r   r   s     r   r   zClaudeSDKClient.set_modelZ  s3     ( {{$%KLLkk##E***r   user_message_idc                    K   | j                   st        d      | j                   j                  |       d{    y7 w)al  Rewind tracked files to their state at a specific user message.

        Requires:
            - `enable_file_checkpointing=True` to track file changes
            - `extra_args={"replay-user-messages": None}` to receive UserMessage
              objects with `uuid` in the response stream

        Args:
            user_message_id: UUID of the user message to rewind to. This should be
                the `uuid` field from a `UserMessage` received during the conversation.

        Example:
            ```python
            options = ClaudeAgentOptions(
                enable_file_checkpointing=True,
                extra_args={"replay-user-messages": None},
            )
            async with ClaudeSDKClient(options) as client:
                await client.query("Make some changes to my files")
                async for msg in client.receive_response():
                    if isinstance(msg, UserMessage) and msg.uuid:
                        checkpoint_id = msg.uuid  # Save this for later

                # Later, rewind to that point
                await client.rewind_files(checkpoint_id)
            ```
        r   N)r   r   rewind_files)r   r   s     r   r   zClaudeSDKClient.rewind_filesr  s3     8 {{$%KLLkk&&777r   server_namec                    K   | j                   st        d      | j                   j                  |       d{    y7 w)a  Reconnect a disconnected or failed MCP server (only works with streaming mode).

        Use this to retry connecting to an MCP server that failed to connect
        or was disconnected. Raises an exception if the reconnection fails.

        Args:
            server_name: The name of the MCP server to reconnect

        Example:
            ```python
            async with ClaudeSDKClient(options) as client:
                status = await client.get_mcp_status()
                for server in status.get("mcpServers", []):
                    if server["status"] == "failed":
                        await client.reconnect_mcp_server(server["name"])
            ```
        r   N)r   r   reconnect_mcp_server)r   r   s     r   r   z$ClaudeSDKClient.reconnect_mcp_server  s3     $ {{$%KLLkk..{;;;r   enabledc                    K   | j                   st        d      | j                   j                  ||       d{    y7 w)aV  Enable or disable an MCP server (only works with streaming mode).

        Disabling a server disconnects it and removes its tools from the
        available tool set. Enabling a server reconnects it and makes its
        tools available again. Raises an exception on failure.

        Args:
            server_name: The name of the MCP server to toggle
            enabled: True to enable the server, False to disable it

        Example:
            ```python
            async with ClaudeSDKClient(options) as client:
                # Temporarily disable a server
                await client.toggle_mcp_server("my-server", enabled=False)
                await client.query("Do something without my-server tools")

                # Re-enable it later
                await client.toggle_mcp_server("my-server", enabled=True)
            ```
        r   N)r   r   toggle_mcp_server)r   r   r   s      r   r   z!ClaudeSDKClient.toggle_mcp_server  s5     , {{$%KLLkk++KAAAs   7A?Atask_idc                    K   | j                   st        d      | j                   j                  |       d{    y7 w)a  Stop a running task (only works with streaming mode).

        After this resolves, a `task_notification` system message with
        status `'stopped'` will be emitted by the CLI in the message stream.

        Args:
            task_id: The task ID from `task_notification` events.

        Example:
            ```python
            async with ClaudeSDKClient() as client:
                await client.query("Start a long-running task")

                # Listen for task_notification to get task_id, then:
                await client.stop_task("task-abc123")
                # A task_notification with status 'stopped' will follow
            ```
        r   N)r   r   	stop_task)r   r   s     r   r   zClaudeSDKClient.stop_task  s3     & {{$%KLLkk##G,,,r   c                    K   | j                   st        d      | j                   j                          d{   }|S 7 w)a  Get current MCP server connection status (only works with streaming mode).

        Queries the Claude Code CLI for the live connection status of all
        configured MCP servers.

        Returns:
            McpStatusResponse dictionary with an 'mcpServers' key containing
            a list of McpServerStatus entries. Each entry includes:
            - 'name': Server name (str)
            - 'status': Connection status ('connected', 'pending', 'failed',
              'needs-auth', 'disabled')
            - 'serverInfo': MCP server name/version (when connected)
            - 'error': Error message (when status is 'failed')
            - 'config': Server configuration (stdio/sse/http/sdk/claudeai-proxy)
            - 'scope': Configuration scope (e.g., project, user, local)
            - 'tools': List of tools provided by the server (when connected)

        Example:
            ```python
            async with ClaudeSDKClient(options) as client:
                status = await client.get_mcp_status()
                for server in status["mcpServers"]:
                    print(f"{server['name']}: {server['status']}")
                    if server["status"] == "failed":
                        print(f"  Error: {server.get('error')}")
            ```
        r   N)r   r   get_mcp_statusr   results     r   r   zClaudeSDKClient.get_mcp_status  s;     8 {{$%KLL*.++*D*D*F$F %G   5A >A c                    K   | j                   st        d      | j                   j                          d{   }|S 7 w)a3  Get a breakdown of current context window usage by category.

        Returns the same data shown by the `/context` command in the CLI,
        including token counts per category, total usage, and detailed
        breakdowns of MCP tools, memory files, and agents.

        Returns:
            ContextUsageResponse dictionary with keys including:
            - 'categories': List of categories with name, tokens, color
            - 'totalTokens': Total tokens in context
            - 'maxTokens': Effective context limit
            - 'percentage': Percent of context used (0-100)
            - 'model': Model the usage is calculated for
            - 'mcpTools': Per-tool token breakdown for MCP servers
            - 'memoryFiles': Per-file token breakdown for CLAUDE.md files
            - 'agents': Per-agent token breakdown

        Example:
            ```python
            async with ClaudeSDKClient() as client:
                await client.query("Read this file")
                async for _ in client.receive_response():
                    pass

                usage = await client.get_context_usage()
                print(f"Using {usage['percentage']:.1f}% of context")
                for cat in usage['categories']:
                    print(f"  {cat['name']}: {cat['tokens']} tokens")
            ```
        r   N)r   r   get_context_usager   s     r   r   z!ClaudeSDKClient.get_context_usage  s;     > {{$%KLL-1[[-J-J-L'L (Mr   c                 f   K   | j                   st        d      t        | j                   dd      S w)a  Get server initialization info including available commands and output styles.

        Returns initialization information from the Claude Code server including:
        - Available commands (slash commands, system commands, etc.)
        - Current and available output styles
        - Server capabilities

        Returns:
            Dictionary with server info, or None if not in streaming mode

        Example:
            ```python
            async with ClaudeSDKClient() as client:
                info = await client.get_server_info()
                if info:
                    print(f"Commands available: {len(info.get('commands', []))}")
                    print(f"Output style: {info.get('output_style', 'default')}")
            ```
        r   _initialization_resultN)r   r   getattrr   s    r   get_server_infozClaudeSDKClient.get_server_info  s/     ( {{$%KLLt{{$<dCCs   /1c                t   K   | j                         2 3 d{   }| t        |t              s y7 6 yw)aB  
        Receive messages from Claude until and including a ResultMessage.

        This async iterator yields all messages in sequence and automatically terminates
        after yielding a ResultMessage (which indicates the response is complete).
        It's a convenience method over receive_messages() for single-response workflows.

        **Stopping Behavior:**
        - Yields each message as it's received
        - Terminates immediately after yielding a ResultMessage
        - The ResultMessage IS included in the yielded messages
        - If no ResultMessage is received, the iterator continues indefinitely

        Yields:
            Message: Each message received (UserMessage, AssistantMessage, SystemMessage, ResultMessage)

        Example:
            ```python
            async with ClaudeSDKClient() as client:
                await client.query("What's the capital of France?")

                async for msg in client.receive_response():
                    if isinstance(msg, AssistantMessage):
                        for block in msg.content:
                            if isinstance(block, TextBlock):
                                print(f"Claude: {block.text}")
                    elif isinstance(msg, ResultMessage):
                        print(f"Cost: ${msg.total_cost_usd:.4f}")
                        # Iterator will terminate after this message
            ```

        Note:
            To collect all messages: `messages = [msg async for msg in client.receive_response()]`
            The final message in the list will always be a ResultMessage.
        N)r   r;   r   )r   rf   s     r   receive_responsez ClaudeSDKClient.receive_response7  s7     H "224 	'M'=1	4s    86468868c                 ,  K   | j                   rC| j                   j                          d{    | j                   j                          d| _         d| _        | j                  *| j                  j                          d{    d| _        yy7 c7 w)zDisconnect from Claude.N)r   closeclose_receive_streamr   r   cleanupr   s    r   r>   zClaudeSDKClient.disconnect`  s}     ;;++##%%%KK,,.DK)$$,,...!%D *	 &
 /s"   *BBABBBBc                 B   K   | j                          d{    | S 7 w)zSEnter async context - automatically connects with empty stream for interactive use.N)r@   r   s    r   
__aenter__zClaudeSDKClient.__aenter__k  s     lln 	s   exc_typeexc_valexc_tbc                 @   K   | j                          d{    y7 w)z(Exit async context - always disconnects.NF)r>   )r   r   r   r   s       r   	__aexit__zClaudeSDKClient.__aexit__p  s     oo 	 s   )NNr4   )rd   )r#   N)r#   r   )'__name__
__module____qualname____doc__r   r
   r    r9   r   listr   r:   r   r.   r   r@   r<   r   r   r   r   r   r   r   r   r   r   rt   r   r   r   r   r   r   r   r   r>   r   r   r5   r!   r   r   r      s4   &T .2&*=#d*= t#=)T+%667	c4S#X''	(& DH.M$sCx.99D@.	.`zEmDcN33d:zE %T#s(^4zE 
	zEx
g(> 
 NWDM$sCx.99DGJD	D<&4n 4 46+S4Z +4 +08# 8$ 8@<c <d <,B3 B B$ B4-s -t -.&7 B")= "HDtCH~'< D2'g(> 'R	&
 c 3 4 r!   r   )r   r{   rp   collections.abcr   r   dataclassesr   r   typingr   r    r
   _errorsr   r7   r   typesr   r   r   r   r   r   r   r   r   r5   r!   r   <module>r      s>    9  	 8 ' %  '<	 	 	Y	 Y	r!   