§
    ÛŽ’jðˆ  ã                   ó0  — d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZ ddlmZ ddlmZmZmZ ddlmZ dd	lmZ dd
lmZmZ ddlmZ ddlmZmZmZm Z m!Z!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Z-dZ.dZ/ G d„ d¦  «        Z0dS )uÇ  WorkspaceBase â€” abstract interface and shared backend-driven impl.

A workspace provides:

- **Resources** â€” skills available to the agent.
- **Tools** â€” MCPs and built-in tools for operating on resources.
- **Offload** â€” persistence of compressed context and tool results
  for agentic retrieval.

Three concrete implementations:

- :class:`agentscope.workspace.LocalWorkspace` â€” local filesystem.
- :class:`agentscope.workspace.DockerWorkspace` â€” Docker container.
- :class:`agentscope.workspace.E2BWorkspace` â€” E2B cloud sandbox.
- :class:`agentscope.workspace.OpenSandboxWorkspace` â€” OpenSandbox
  remote sandbox.

Consumers:

- **Agent** â€” calls ``list_mcps``, ``list_skills``, ``list_tools``,
  ``offload_context``, ``offload_tool_result``.
- **User** â€” dynamically adds/removes MCPs and skills via
  ``add_mcp`` / ``remove_mcp`` / ``add_skill`` / ``remove_skill``.
- **Developer** â€” manages lifecycle via ``initialize`` / ``close``.
- **Backend consumers** access the active backend via :meth:`get_backend`.

Shared implementation
---------------------

The base class implements every operation that can be expressed against
the workspace's :class:`BackendBase` plus a fixed layout derived from
``workdir``:

.. code-block:: text

    {workdir}/
    â”œâ”€â”€ .mcp          # persisted MCP client configs (JSON array)
    â”œâ”€â”€ data/         # offloaded multimodal payloads
    â”œâ”€â”€ skills/       # skill subdirectories
    â””â”€â”€ sessions/     # per-session context and tool-result files

Subclasses only set ``self.workdir`` (the agent-visible root); all
other directory paths are derived via :meth:`BackendBase.join_path`,
keeping path semantics consistent with whichever backend is bound.
é    N)Úabstractmethod)Údeepcopy)ÚPath)ÚAsyncIteratorÚLiteralÚSelf)ÚAnyUrlé   )Úlogger)Ú_generate_idÚ_normalize_local_path)Ú	MCPClient)ÚBase64SourceÚ	DataBlockÚMsgÚ	TextBlockÚToolResultBlockÚ	URLSource)ÚSkill)ÚBackendBaseÚToolBaseé   )ÚDEFAULT_DATA_DIRÚDEFAULT_MCP_FILEÚDEFAULT_SESSIONS_DIRÚDEFAULT_SKILLS_DIRaâ  import tarfile, sys, os
src, dst = sys.argv[1], sys.argv[2]
os.makedirs(dst, exist_ok=True)
dst_real = os.path.realpath(dst)
tf = tarfile.open(src)
try:
    members = tf.getmembers()
    for m in members:
        target = os.path.realpath(os.path.join(dst, m.name))
        if not (target == dst_real or target.startswith(dst_real + os.sep)):
            raise Exception('unsafe tar member: ' + m.name)
    tf.extractall(dst, members=members)
finally:
    tf.close()
os.unlink(src)
a  import os, sys, tarfile, zipfile
src, dst, fmt, limit = sys.argv[1:5]
limit = int(limit)
os.makedirs(dst, exist_ok=True)
dst_real = os.path.realpath(dst)
def check(name):
    target = os.path.realpath(os.path.join(dst, name))
    if not (target == dst_real or target.startswith(dst_real + os.sep)):
        raise Exception('unsafe archive member: ' + name)
if fmt == 'zip':
    ar = zipfile.ZipFile(src)
    members = ar.infolist()
    total = sum(m.file_size for m in members)
    names = [m.filename for m in members]
else:
    ar = tarfile.open(src)
    members = ar.getmembers()
    total = sum(m.size for m in members)
    names = [m.name for m in members]
    for m in members:
        if m.issym() or m.islnk():
            check(os.path.join(os.path.dirname(m.name), m.linkname))
try:
    if total > limit:
        raise Exception('archive expands to %d bytes, limit is %d'
                        % (total, limit))
    for name in names:
        check(name)
    ar.extractall(dst, members)
finally:
    ar.close()
os.unlink(src)
i  @c                   ó¨  — e Zd ZU dZeed<   	 eed<   	 eed<   	 edz  ed<   	 ee	         ed<   	 ee         ed<   	 ee	         ed	<   	 e
j        ed
<   	 e
j        ed<   	 ededz  fd„¦   «         Zddddœdedz  dee	         dz  dee         dz  ddfd„Zedefd„¦   «         Zedefd„¦   «         Zedefd„¦   «         Zedefd„¦   «         Zedefd„¦   «         Zededefd„¦   «         Zed;d„¦   «         Zed;d„¦   «         Zd;d„Zdefd„Zdefd„Zdeddfd„Zedefd„¦   «         Zdee          fd„Z!dee	         fd „Z"ed!e	ddfd"„¦   «         Z#ed#eddfd$„¦   «         Z$d;d%„Z%d&ed'ee&         defd(„Z'd&ed)e(defd*„Z)d+e*de*fd,„Z+dee,         fd-„Z-d.eddfd/„Z.e/fd0e0e1         d1e2d2         d3ed4e3ddf
d5„Z4d6edefd7„Z5d3edefd8„Z6d#eddfd9„Z7d;d:„Z8dS )<ÚWorkspaceBasea<  Abstract base class for all workspace implementations.

    Subclasses provide concrete behaviour for one execution backend
    (local filesystem, Docker container, E2B sandbox). The base class
    owns:

    - lifecycle scaffolding (``async with`` protocol, ``is_alive``);
    - the canonical workspace layout derived from ``workdir`` (data/,
      skills/, sessions/, .mcp);
    - shared backend-driven implementations of offload, MCP
      persistence and a basic skill manager that subclasses can
      override (LocalWorkspace does, with a hash-indexed variant).
    Úworkspace_idÚworkdirÚis_aliveNÚ_backendÚdefault_mcpsÚskill_pathsÚ_mcpsÚ	_mcp_lockÚ_skill_lockÚreturnc                 ó   — dS )aa  Optional path (backend-side) to the ``Glob`` helper script.

        ``None`` means the :class:`Glob` builtin tool falls back to its
        default behaviour (suitable for :class:`LocalBackend`). Remote
        backends override this with a sandbox-/container-side script path
        so :class:`Glob` can run efficiently inside the workspace.
        N© ©Úselfs    úHC:\Projects\DSCons\.venv\Lib\site-packages\agentscope\workspace\_base.pyÚ_glob_helper_pathzWorkspaceBase._glob_helper_pathÂ   ó	   € ð ˆtó    )r   r#   r$   c                ó  — |pt          ¦   «         | _        d| _        d| _        t	          |pg ¦  «        | _        d„ |pg D ¦   «         | _        g | _        t          j	        ¦   «         | _
        t          j	        ¦   «         | _        dS )a>  Initialise the shared workspace state.

        Subclasses must call ``super().__init__`` and then set
        :attr:`workdir` themselves before any base-class method is
        invoked. Backend binding (``self._backend``) is left to the
        subclass (Local sets it eagerly; Docker/E2B set it during
        :meth:`initialize`).

        Args:
            workspace_id (`str | None`, optional):
                Existing identifier to adopt; ``None`` mints a fresh
                UUID.
            default_mcps (`list[MCPClient] | None`, optional):
                MCP clients to register when the workspace boots
                without a persisted ``.mcp`` file.
            skill_paths (`list[str] | None`, optional):
                Local skill directories to copy into ``skills/`` on
                first start.
        FNc                 ó,   — g | ]}t          |¦  «        ‘ŒS r*   )r   )Ú.0Úpaths     r-   ú
<listcomp>z*WorkspaceBase.__init__.<locals>.<listcomp>ì   s.   € ð 
ð 
ð 
Ø,0Õ! $Ñ'Ô'ð
ð 
ð 
r0   )r   r   r!   r"   Úlistr#   r$   r%   ÚasyncioÚLockr&   r'   )r,   r   r#   r$   s       r-   Ú__init__zWorkspaceBase.__init__Í   sˆ   € ð4 )Ð:­L©N¬NˆÔØˆŒØˆŒå  Ð!3°Ñ4Ô4ˆÔð
ð 
Ø4?Ð4EÀ2ð
ñ 
ô 
ˆÔð ˆŒ
Ý œ™œˆŒÝ"œ<™>œ>ˆÔÐÐr0   c                 óf   — |                       ¦   «                              | j        t          ¦  «        S )u6   ``${workdir}/data`` â€” offloaded multimodal payloads.)Úget_backendÚ	join_pathr    r   r+   s    r-   Ú	_data_dirzWorkspaceBase._data_dirö   ó)   € ð ×ÒÑ!Ô!×+Ò+¨D¬LÕ:JÑKÔKÐKr0   c                 óf   — |                       ¦   «                              | j        t          ¦  «        S )u/   ``${workdir}/skills`` â€” skill subdirectories.)r;   r<   r    r   r+   s    r-   Ú_skills_dirzWorkspaceBase._skills_dirû   s0   € ð ×ÒÑ!Ô!×+Ò+ØŒLÝñ
ô 
ð 	
r0   c                 óf   — |                       ¦   «                              | j        t          ¦  «        S )u6   ``${workdir}/sessions`` â€” per-session offload files.)r;   r<   r    r   r+   s    r-   Ú_sessions_dirzWorkspaceBase._sessions_dir  s0   € ð ×ÒÑ!Ô!×+Ò+ØŒLÝ ñ
ô 
ð 	
r0   c                 óf   — |                       ¦   «                              | j        t          ¦  «        S )u4   ``${workdir}/.mcp`` â€” persisted MCP registrations.)r;   r<   r    r   r+   s    r-   Ú	_mcp_filezWorkspaceBase._mcp_file  r>   r0   c                 ó   — dS )aC  Whether the workspace storage survives :meth:`close`.

        Defaults to ``True``. Subclasses with conditional persistence
        (e.g. :class:`DockerWorkspace` without a host bind-mount)
        override this to gate the cost of writing ``.mcp`` and other
        files that would not survive the next session.
        Tr*   r+   s    r-   Úis_persistentzWorkspaceBase.is_persistent  r/   r0   r4   c                 óx   — |                       d¦  «        rd| › �S t          | ¦  «                             ¦   «         S )a�  Convert an absolute backend-side path to a ``file://`` URI.

        Absolute POSIX paths (every remote backend, plus
        :class:`LocalBackend` on Linux/macOS) start with ``/`` and use
        the plain ``file://{path}`` form. Windows absolute paths
        (e.g. ``C:\Users\...``) round-trip through
        :meth:`pathlib.Path.as_uri` to produce ``file:///C:/...`` form.
        ú/zfile://)Ú
startswithr   Úas_uri)r4   s    r-   Ú_path_to_file_urizWorkspaceBase._path_to_file_uri  s>   € ð �?Š?˜3ÑÔð 	$Ø#˜TÐ#Ð#Ð#Ý�D‰zŒz× Ò Ñ"Ô"Ð"r0   c              ƒ   ó
   K  — dS )z6Provision resources, connect MCP servers, copy skills.Nr*   r+   s    r-   Ú
initializezWorkspaceBase.initialize+  ó
   è è € € € r0   c              ƒ   ó
   K  — dS )z&Release all resources and connections.Nr*   r+   s    r-   ÚclosezWorkspaceBase.close/  rN   r0   c              ƒ   ó
   K  — dS )uã  Reset the workspace to a clean state.

        Closes and removes all registered MCPs, deletes all skills,
        and wipes per-session state (offloaded context / tool results
        and any data files). Constructor-time ``default_mcps`` and
        ``skill_paths`` are **not** re-seeded â€” reset returns the
        workspace to an empty state, not its initial state.

        The default implementation is a no-op. Subclasses with user
        state must override this.
        Nr*   r+   s    r-   ÚresetzWorkspaceBase.reset3  rN   r0   c                 óf   — | j         €$t          t          | ¦  «        j        › d�¦  «        ‚| j         S )a¹  Return the workspace's active filesystem/execution backend.

        Docker and E2B workspaces may replace their backend when reconnecting,
        so callers should resolve it from the workspace when beginning an
        operation rather than retaining a stale private ``_backend`` value.

        Raises:
            RuntimeError:
                If the workspace has not been initialized or has no active
                backend.
        NzO has no active backend. Initialize the workspace before requesting its backend.)r"   ÚRuntimeErrorÚtypeÚ__name__r+   s    r-   r;   zWorkspaceBase.get_backend@  sF   € ð Œ=Ð ÝÝ˜‘:”:Ô&ð Jð Jð Jñô ð ð Œ}Ðr0   c              ƒ   óL   K  — |                       ¦   «         ƒ d{V —† d| _        | S )zwContext manager support for ``async with``. Calls ``initialize()``
        and returns the workspace instance.
        NT)rM   r!   r+   s    r-   Ú
__aenter__zWorkspaceBase.__aenter__S  s6   è è € ð �oŠoÑÔÐÐÐÐÐÐÐØˆŒØˆr0   Úexcc              ‡   óL   K  — |                       ¦   «         ƒ d{V —† d| _        dS )zrContext manager support for ``async with``. Calls ``close()``
        and returns the workspace instance.
        NF)rP   r!   )r,   rY   s     r-   Ú	__aexit__zWorkspaceBase.__aexit__[  s3   è è € ð �jŠj‰lŒlÐÐÐÐÐÐÐØˆŒˆˆr0   c              ƒ   ó
   K  — dS )z*Workspace-specific system prompt fragment.Nr*   r+   s    r-   Úget_instructionszWorkspaceBase.get_instructionsd  rN   r0   c              ƒ   ó  K  — ddl m}m}m}m}m}m} |                      ¦   «         }d|i}| j        �
| j        |d<    || j	        |¬¦  «         ||¬¦  «         |di |¤Ž ||¬¦  «         ||¬¦  «         ||¬¦  «        gS )	a_  Built-in tools scoped to this workspace.

        Returns the six builtin tools (:class:`Bash`, :class:`Edit`,
        :class:`Glob`, :class:`Grep`, :class:`Read`, :class:`Write`),
        each bound to the workspace's active backend so that all
        filesystem and process I/O happens inside the workspace's
        execution environment. :class:`Bash` is rooted at
        :attr:`workdir`; :class:`Glob` receives the optional
        :attr:`_glob_helper_path` when the backend ships one.

        Raises:
            RuntimeError:
                If the workspace has not been initialised yet.
        r
   )ÚBashÚEditÚGlobÚGrepÚReadÚWriteÚbackendNÚglob_helper_path)Úcwdre   )re   r*   )
Útoolr_   r`   ra   rb   rc   rd   r;   r.   r    )	r,   r_   r`   ra   rb   rc   rd   re   Úglob_kwargss	            r-   Ú
list_toolszWorkspaceBase.list_toolsj  sÛ   è è € ð 	?Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ð>à×"Ò"Ñ$Ô$ˆØ&¨Ð0ˆØÔ!Ð-Ø.2Ô.DˆKÐ*Ñ+àˆD�T”\¨7Ð3Ñ3Ô3ØˆD˜Ð!Ñ!Ô!ØˆDÐÐ�;ÐÐØˆD˜Ð!Ñ!Ô!ØˆD˜Ð!Ñ!Ô!ØˆE˜'Ð"Ñ"Ô"ð
ð 	
r0   c              ƒ   ó.   K  — t          | j        ¦  «        S )z,Return the currently registered MCP clients.)r6   r%   r+   s    r-   Ú	list_mcpszWorkspaceBase.list_mcpsˆ  s   è è € å�D”JÑÔÐr0   Ú
mcp_clientc              ƒ   ó
   K  — dS )zåRegister a new MCP server.

        Args:
            mcp_client (`MCPClient`):
                The MCP to register.

        Raises:
            `ValueError`:
                If an MCP with the same name already exists.
        Nr*   )r,   rm   s     r-   Úadd_mcpzWorkspaceBase.add_mcpŽ  rN   r0   Únamec              ƒ   ó
   K  — dS )z¸Deregister an MCP server by name.

        Args:
            name (`str`):
                MCP name to remove. Unknown names log a warning and
                return silently.
        Nr*   )r,   rp   s     r-   Ú
remove_mcpzWorkspaceBase.remove_mcp›  rN   r0   c              ƒ   óN  K  — | j         sdS | j        }|€dS t          j        d„ | j        D ¦   «         dd¬¦  «                             d¦  «        }	 |                     | j        |¦  «        ƒ d{V —† dS # t          $ r&}t          j
        d| j        |¦  «         Y d}~dS d}~ww xY w)u§  Persist ``self._mcps`` to ``${workdir}/.mcp`` via backend.

        No-op when :attr:`is_persistent` is ``False`` (e.g. ephemeral
        Docker container without a host bind-mount). Failures are
        logged but not raised â€” the in-memory MCP list remains the
        authoritative copy regardless of whether disk persistence
        succeeded.

        Callers are expected to hold :attr:`_mcp_lock` already.
        Nc                 ó:   — g | ]}|                      d ¬¦  «        ‘ŒS )Újson)Úmode)Ú
model_dump)r3   Úms     r-   r5   z0WorkspaceBase._save_mcp_file.<locals>.<listcomp>¸  s&   € Ð;Ð;Ð;¨1ˆQ�\Š\˜vˆ\Ñ&Ô&Ð;Ð;Ð;r0   r
   F)ÚindentÚensure_asciiúutf-8z!Failed to save MCP file at %s: %s)rF   r"   ru   Údumpsr%   ÚencodeÚ
write_filerD   Ú	Exceptionr   Úwarning)r,   re   ÚpayloadÚes       r-   Ú_save_mcp_filezWorkspaceBase._save_mcp_file§  sù   è è € ð Ô!ð 	ØˆFØ”-ˆØˆ?ØˆFÝ”*Ø;Ð;°´
Ð;Ñ;Ô;ØØð
ñ 
ô 
÷ Š&�‰/Œ/ð	 	ð
	Ø×$Ò$ T¤^°WÑ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ð=øÝð 	ð 	ð 	ÝŒNØ3Ø”Øñô ð ð ð ð ð ð ð øøøøð	øøøs   Á!A4 Á4
B$Á>BÂB$Ú
session_idÚmsgsc              ƒ   ó&  K  — |                       ¦   «         }|                     | j        |¦  «        }|                     |d¦  «        }t          |¦  «        }g }|D ]µ}t	          |j        t          ¦  «        srg }	|j        D ]a}
t	          |
t          ¦  «        r5t	          |
j        t          ¦  «        r|  
                    |
¦  «        ƒ d{V —†}
|	                     |
¦  «         Œb|	|_        |                     |                     ¦   «         ¦  «         Œ¶d                     |¦  «        dz   }d}	 |                     |¦  «        ƒ d{V —†}n# t          t           f$ r Y nw xY w|                     |||                     d¦  «        z   ¦  «        ƒ d{V —† |S )uE  Persist compressed context for agentic retrieval.

        Appends every message in ``msgs`` to
        ``${workdir}/sessions/<session_id>/context.jsonl`` (one
        message per JSONL line). Inline base64
        :class:`DataBlock` payloads are extracted into ``data/`` and
        rewritten as ``file://`` URL blocks before serialisation so
        the JSONL line size stays bounded.

        Args:
            session_id (`str`):
                Session-scope key used to partition offloaded data
                (one subdirectory per session).
            msgs (`list[Msg]`):
                Conversation messages to offload. Not mutated â€” a
                deep copy is used internally.

        Returns:
            `str`:
                Backend-side path of the JSONL file that received
                the new lines.
        zcontext.jsonlNú
r0   r{   )r;   r<   rB   r   Ú
isinstanceÚcontentÚstrr   Úsourcer   Úoffload_data_blockÚappendÚmodel_dump_jsonÚjoinÚ	read_fileÚFileNotFoundErrorÚOSErrorr~   r}   )r,   r„   r…   re   Úbaser4   ÚcopiedÚlinesÚmsgr‰   Úblockr�   Úexistings                r-   Úoffload_contextzWorkspaceBase.offload_contextÇ  sÊ  è è € ð6 ×"Ò"Ñ$Ô$ˆØ× Ò  Ô!3°ZÑ@Ô@ˆØ× Ò   Ñ7Ô7ˆå˜$‘”ˆØˆØð 	0ð 	0ˆCÝ˜cœk­3Ñ/Ô/ð 	&Ø "�Ø œ[ð *ð *�EÝ! %­Ñ3Ô3ð E½
ØœÝ$ñ9ô 9ð Eð '+×&=Ò&=¸eÑ&DÔ&DÐ DÐ DÐ DÐ DÐ DÐ D˜Ø—N’N 5Ñ)Ô)Ð)Ð)Ø%�”Ø�LŠL˜×,Ò,Ñ.Ô.Ñ/Ô/Ð/Ð/à—)’)˜EÑ"Ô" TÑ)ˆàˆð	Ø$×.Ò.¨tÑ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4ˆHˆHøÝ!¥7Ð+ð 	ð 	ð 	ØˆDð	øøøà× Ò   x°'·.².ÀÑ2IÔ2IÑ'IÑJÔJÐJÐJÐJÐJÐJÐJÐJØˆs   Ä,E ÅEÅEÚtool_resultc           
   ƒ   ó   K  — |                       ¦   «         }|                     | j        |¦  «        }|                     |d|j        › d�¦  «        }d}|                     |¦  «        ƒ d{V —†rB|                     |d|j        › d|› d�¦  «        }|dz  }|                     |¦  «        ƒ d{V —†°Bg }t          |j        t          ¦  «        r|                     |j        ¦  «         nå|j        D ]Ý}t          |t          ¦  «        r|                     |j
        ¦  «         Œ2t          |t          ¦  «        r–t          |j        t          ¦  «        r5|                      |¦  «        ƒ d{V —†}	t          |	j        j        ¦  «        }
nt          |j        j        ¦  «        }
|                     d|
› d|j        › d	|j        j        › d
�¦  «         ŒÞ|                     |d                     |¦  «                             d¦  «        ¦  «        ƒ d{V —† |S )uP  Persist a single tool result as a flat text file.

        Writes ``${workdir}/sessions/<session_id>/tool_result-<id>.txt``.
        Text blocks are concatenated verbatim; :class:`DataBlock` items
        emit ``<data url='â€¦' name='â€¦' media_type='â€¦'/>`` placeholders,
        with inline base64 payloads first offloaded to ``data/``.

        On a filename clash (same tool-result ``id`` written twice in
        one session) the new file is suffixed with ``(1)``, ``(2)``,
        â€¦ to avoid clobbering the prior content.

        Args:
            session_id (`str`):
                Session-scope key used to partition offloaded data.
            tool_result (`ToolResultBlock`):
                The tool result block to persist.

        Returns:
            `str`:
                Backend-side path of the offloaded text file.
        ztool_result-z.txtr   Nú(z).txtz<data url='z' name='z' media_type='z'/>Ú r{   )r;   r<   rB   ÚidÚfile_existsrˆ   ÚoutputrŠ   r�   r   Útextr   r‹   r   rŒ   Úurlrp   Ú
media_typer~   r�   r}   )r,   r„   rš   re   r“   r4   ÚindexÚpartsr—   Údr¢   s              r-   Úoffload_tool_resultz!WorkspaceBase.offload_tool_resultÿ  sa  è è € ð4 ×"Ò"Ñ$Ô$ˆØ× Ò  Ô!3°ZÑ@Ô@ˆØ× Ò  Ð'J°k´nÐ'JÐ'JÐ'JÑKÔKˆàˆØ×'Ò'¨Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-ð 	Ø×$Ò$ØØ<˜{œ~Ð<Ð<°Ð<Ð<Ð<ñô ˆDð �Q‰JˆEð ×'Ò'¨Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-ð 	ð ˆÝ�kÔ(­#Ñ.Ô.ð 	Ø�LŠL˜Ô+Ñ,Ô,Ð,Ð,à$Ô+ð ð �Ý˜e¥YÑ/Ô/ð Ø—L’L ¤Ñ,Ô,Ð,Ð,Ý ¥yÑ1Ô1ð 	Ý! %¤,µÑ=Ô=ð 4Ø"&×"9Ò"9¸%Ñ"@Ô"@Ð@Ð@Ð@Ð@Ð@Ð@˜Ý! !¤(¤,Ñ/Ô/˜˜å! %¤,Ô"2Ñ3Ô3˜Ø—L’LðD cð Dð D°5´:ð Dð DØ',¤|Ô'>ðDð Dð Dñô ð øð
 × Ò   r§w¢w¨u¡~¤~×'<Ò'<¸WÑ'EÔ'EÑFÔFÐFÐFÐFÐFÐFÐFÐFØˆr0   r—   c           
   ƒ   ó´  K  — t          |j        t          ¦  «        s|S |                      ¦   «         }t	          j        |j        j                             ¦   «         ¦  «                             ¦   «         }t          j
        |j        j        ¦  «        pd}t          › d|› |› �}|                     | j        |› |› �¦  «        }|                     |¦  «        ƒ d{V —†s8|                     |t#          j        |j        j        ¦  «        ¦  «        ƒ d{V —† t'          |j        |j        t-          t/          d|› �¦  «        |j        j        ¬¦  «        ¬¦  «        S )uÇ  Persist a base64 :class:`DataBlock` under ``data/``.

        The decoded payload is stored at
        ``${workdir}/data/<sha256-of-base64>.<ext>``. Hashing the
        *base64* text rather than the decoded bytes lets a second
        offload of the same block short-circuit (same key â†’ same
        file â†’ no write).

        Args:
            block (`DataBlock`):
                A data block. Blocks already backed by a
                :class:`URLSource` are returned unchanged.

        Returns:
            `DataBlock`:
                A new :class:`DataBlock` whose source is a portable
                ``workspace://`` URL pointing at the persisted file
                inside the workspace.
        z.binrH   Nzworkspace:///)r¢   r£   )rž   rp   r‹   )rˆ   r‹   r   r;   ÚhashlibÚsha256Údatar}   Ú	hexdigestÚ	mimetypesÚguess_extensionr£   r   r<   r=   rŸ   r~   Úbase64Ú	b64decoder   rž   rp   r   r	   )r,   r—   re   Úhash_strÚextÚrelr4   s          r-   rŒ   z WorkspaceBase.offload_data_block:  sw  è è € õ( ˜%œ,­Ñ5Ô5ð 	ØˆLà×"Ò"Ñ$Ô$ˆÝ”> %¤,Ô"3×":Ò":Ñ"<Ô"<Ñ=Ô=×GÒGÑIÔIˆÝÔ'¨¬Ô(?Ñ@Ô@ÐJÀFˆÝ!Ð3Ð3 HÐ3¨cÐ3Ð3ˆØ× Ò  ¤°HÐ1C¸cÐ1CÐ1CÑDÔDˆà×(Ò(¨Ñ.Ô.Ð.Ð.Ð.Ð.Ð.Ð.ð 	Ø×$Ò$ØÝÔ  ¤Ô!2Ñ3Ô3ñô ð ð ð ð ð ð ð õ ØŒxØ”ÝÝÐ0¨3Ð0Ð0Ñ1Ô1Ø œ<Ô2ðñ ô ð
ñ 
ô 
ð 	
r0   c           
   ƒ   óþ  K  — ddl }|                      ¦   «         }|                     | j        ¦  «        ƒ d{V —†sg S |                     | j        d¬¦  «        ƒ d{V —†}g }|D �]}|                     |¦  «        dk    rŒ	 |                     |¦  «        ƒ d{V —†}|                     |                     d¦  «        ¦  «        }| 	                    d¦  «        }| 	                    d¦  «        }	|r|	sŒ�| 
                    t          t          |¦  «        t          |	¦  «        |                     |¦  «        |j        pd	d
¬¦  «        ¦  «         Œì# t          $ r!}
t!          j        d||
¦  «         Y d}
~
�Œd}
~
ww xY w|S )a3  Enumerate skills under ``${workdir}/skills``.

        Walks ``skills/`` recursively, parses every ``SKILL.md``'s
        YAML front matter, and yields one :class:`Skill` per file
        that has both ``name`` and ``description``.

        Subclasses with richer indexing (e.g.
        :class:`LocalWorkspace` with its ``.skills`` hash index)
        override this method.

        Returns:
            `list[Skill]`:
                Skills available to the agent. Empty when ``skills/``
                is missing or contains no parseable ``SKILL.md``.
        r   NT)Ú	recursiveúSKILL.mdr{   rp   Údescriptionr�   g        )rp   r·   ÚdirÚmarkdownÚ
updated_atzFailed to load skill %s: %s)Úfrontmatterr;   Úis_dirr@   Úlist_dirÚbasenamer�   ÚloadsÚdecodeÚgetr�   r   rŠ   Údirnamer‰   r   r   r€   )r,   Úfmre   ÚentriesÚskillsÚmd_pathÚrawÚdocrp   Údescr‚   s              r-   Úlist_skillszWorkspaceBase.list_skillsm  sÕ  è è € ð  	!Ð Ð Ð à×"Ò"Ñ$Ô$ˆØ—^’^ DÔ$4Ñ5Ô5Ð5Ð5Ð5Ð5Ð5Ð5ð 	ØˆIà×(Ò(¨Ô)9ÀTÐ(ÑJÔJÐJÐJÐJÐJÐJÐJˆà ˆØð 	Jñ 	JˆGØ×Ò Ñ(Ô(¨JÒ6Ð6ØðJØ#×-Ò-¨gÑ6Ô6Ð6Ð6Ð6Ð6Ð6Ð6�Ø—h’h˜sŸzšz¨'Ñ2Ô2Ñ3Ô3�Ø—w’w˜v‘”�Ø—w’w˜}Ñ-Ô-�Øð  4ð ØØ—’ÝÝ  ™YœYÝ$'¨¡I¤IØ#ŸOšO¨GÑ4Ô4Ø!$¤Ð!2°Ø#&ðñ ô ñô ð ð øõ ð Jð Jð JÝ”Ð<¸gÀqÑIÔIÐIÐIÐIÐIÐIÑIøøøøðJøøøàˆs    ÂA1EÃ3AEÅ
E:ÅE5Å5E:Ú
skill_pathc           
   ƒ   óÐ  K  — t           j                             |d¦  «        }t           j                             |¦  «        st	          d|›d�¦  «        ‚|                      ¦   «         }| j        4 ƒd{V —† t           j                             t           j                             |¦  «        ¦  «        }| 	                    | j
        |¦  «        }|                     |¦  «        ƒ d{V —†rt	          d|›d| j
        › �¦  «        ‚t          j        ¦   «         }t          j        |d¬¦  «        5 }|                     ||¬	¦  «         ddd¦  «         n# 1 swxY w Y   |                     ¦   «         }d
t%          ¦   «         › d�}	|                     |	|¦  «        ƒ d{V —† |                     dd| j
        g¦  «        ƒ d{V —† |                     ddt*          |	| j
        g¦  «        ƒ d{V —†}
|
                     ¦   «         s.t/          d|›d|
j                             dd¦  «        › �¦  «        ‚t5          j        d||¦  «         ddd¦  «        ƒd{V —† dS # 1 ƒd{V —†swxY w Y   dS )uu  Copy a local skill directory into ``${workdir}/skills``.

        Tars the directory on the host, writes the archive to the
        backend's tmp area, and extracts it via ``python3 -c`` inside
        the sandbox â€” two round trips regardless of skill size, and
        portable across any backend whose image ships ``python3``
        (same contract as the gateway shim).

        Subclasses with richer dedup (e.g. :class:`LocalWorkspace`
        with hash-indexed conflict resolution) override this method.

        Args:
            skill_path (`str`):
                Path to a skill directory on the local filesystem.

        Raises:
            ValueError:
                If ``SKILL.md`` is missing or a directory with the
                same basename already exists in ``skills/``.
            RuntimeError:
                If extraction inside the sandbox fails.
        r¶   zInvalid skill at z: SKILL.md not foundNzSkill directory z already exists in Úw)Úfileobjrv   )Úarcnameú/tmp/skill-z.tarÚmkdirú-pÚpython3ú-czFailed to extract skill ú: r{   ÚreplacezAdded skill %r at %s)Úosr4   r�   ÚisfileÚ
ValueErrorr;   r'   r¾   Úabspathr<   r@   rŸ   ÚioÚBytesIOÚtarfileÚopenÚaddÚgetvaluer   r~   Ú
exec_shellÚ_EXTRACT_TAR_SHIMÚokrT   ÚstderrrÀ   r   Úinfo)r,   rË   Úskill_mdre   Údir_nameÚ
remote_dirÚbufÚtfÚ	tar_bytesÚtmp_pathÚresults              r-   Ú	add_skillzWorkspaceBase.add_skill�  s¼  è è € õ. ”7—<’< 
¨JÑ7Ô7ˆÝŒw�~Š~˜hÑ'Ô'ð 	ÝØF JÐFÐFÐFñô ð ð ×"Ò"Ñ$Ô$ˆàÔ#ð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	FÝ”w×'Ò'­¬¯ª¸
Ñ(CÔ(CÑDÔDˆHØ ×*Ò*¨4Ô+;¸XÑFÔFˆJà×(Ò(¨Ñ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4ð Ý ð* xð *ð *ØÔ'ð*ð *ñô ð õ
 ”*‘,”,ˆCÝ” c°Ð4Ñ4Ô4ð 5¸Ø—’�z¨8�Ñ4Ô4Ð4ð5ð 5ð 5ñ 5ô 5ð 5ð 5ð 5ð 5ð 5ð 5øøøð 5ð 5ð 5ð 5àŸš™œˆIà9¥\¡^¤^Ð9Ð9Ð9ˆHØ×$Ò$ X¨yÑ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9Ð9à×$Ò$Ø˜$ Ô 0Ð1ñô ð ð ð ð ð ð ð ð #×-Ò-àØÝ%ØØÔ$ðñô ð ð ð ð ð ð ˆFð —9’9‘;”;ð Ý"ðB¨xð Bð BØ”}×+Ò+¨G°YÑ?Ô?ðBð Bñô ð õ
 ŒKÐ.°¸*ÑEÔEÐEðI$	Fð $	Fð $	Fñ $	Fô $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fð $	Føøøð $	Fð $	Fð $	Fð $	Fð $	Fð $	Fs8   Á6B6IÄ,EÅIÅE	ÅIÅE	ÅC*IÉ
IÉ"IÚstreamÚfmt)ÚzipÚtarútar.gzrç   Úmax_extracted_bytesc              ƒ   óz  K  — |dv s#t           j                             |¦  «        |k    rt          d|›d�¦  «        ‚|                      ¦   «         }|dk    rdn|}dt          ¦   «         › d|› �}| j        4 ƒd{V —† |                     dd	| j        g¦  «        ƒ d{V —† | 	                    | j
        d
t          ¦   «         › �¦  «        }|                     ||¦  «        ƒ d{V —† 	 |                     ddt          |||t          |¦  «        g¦  «        ƒ d{V —†}	|	                     ¦   «         s+t          d|	j                             dd¦  «        › �¦  «        ‚|                      |¦  «        ƒ d{V —†}
|                      |¦  «        ƒ d{V —†}|                     d|
| 	                    | j        |¦  «        g¦  «        ƒ d{V —†}|                     ¦   «         s.t          d|›d|j                             dd¦  «        › �¦  «        ‚	 |                     |¦  «        ƒ d{V —† |                     |¦  «        ƒ d{V —† n;# |                     |¦  «        ƒ d{V —† |                     |¦  «        ƒ d{V —† w xY w	 ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   t+          j        d|| j        ¦  «         dS )u  Install a skill from an archive stream into ``skills/``.

        The archive is piped straight into the sandbox and expanded
        there, so the server never holds it whole. Expansion lands in a
        staging directory that is renamed into place only once it is
        known to hold a ``SKILL.md`` â€” a failed install leaves nothing
        behind.

        Args:
            stream (`AsyncIterator[bytes]`):
                The archive bytes, in order.
            fmt (`Literal["zip", "tar", "tar.gz"]`):
                The archive format.
            dir_name (`str`):
                Directory name to install as. A numeric suffix is
                appended when it is taken.
            max_extracted_bytes (`int`):
                Ceiling on the archive's expanded size.

        Raises:
            ValueError:
                If ``dir_name`` is not a plain name, or the archive
                holds no ``SKILL.md``.
            RuntimeError:
                If expanding the archive inside the sandbox fails.
        )ú.z..zSkill directory name z. must be a plain name without path separators.ró   rÐ   rö   NrÑ   rÒ   z.skill-staging-rÓ   rÔ   z Failed to expand skill archive: r{   rÖ   ÚmvzFailed to install skill rÕ   zAdded skill %r to %s)r×   r4   r¾   rÙ   r;   r   r'   rá   r@   r<   r    Úwrite_streamÚ_EXTRACT_ARCHIVE_SHIMrŠ   rã   rT   rä   rÀ   Ú_find_skill_rootÚ_free_skill_dirÚdelete_pathr   rå   )r,   rï   rð   rç   rô   re   ÚsuffixÚarchive_pathÚstagingrí   ÚrootÚ	installedÚmoves                r-   Úadd_skill_archivezWorkspaceBase.add_skill_archiveâ  s?  è è € ðB �{Ð"Ð"¥b¤g×&6Ò&6°xÑ&@Ô&@ÀHÒ&LÐ&LÝð,¨ð ,ð ,ð ,ñô ð ð
 ×"Ò"Ñ$Ô$ˆØ  Hš_˜_��°#ˆØ>¥\¡^¤^Ð>Ð>°fÐ>Ð>ˆàÔ#ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8Ø×$Ò$ g¨t°TÔ5EÐ%FÑGÔGÐGÐGÐGÐGÐGÐGÐGð ×'Ò'Ø”Ø2¥,¡.¤.Ð2Ð2ñô ˆGð ×&Ò& |°VÑ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<Ð<ð"8Ø&×1Ò1à!ØÝ-Ø$ØØÝÐ/Ñ0Ô0ðñ
 ô 
 ð 
ð 
ð 
ð 
ð 
ð 
�ð —y’y‘{”{ð Ý&ðFØ!œ=×/Ò/°¸ÑCÔCðFð Fñô ð ð
 "×2Ò2°7Ñ;Ô;Ð;Ð;Ð;Ð;Ð;Ð;�Ø"&×"6Ò"6°xÑ"@Ô"@Ð@Ð@Ð@Ð@Ð@Ð@�	Ø$×/Ò/àØØ×)Ò)¨$Ô*:¸IÑFÔFðñô ð ð ð ð ð ð �ð —w’w‘y”yð Ý&ðD°9ð Dð DØœ;×-Ò-¨g°yÑAÔAðDð Dñô ð ðð ×)Ò)¨'Ñ2Ô2Ð2Ð2Ð2Ð2Ð2Ð2Ð2Ø×)Ò)¨,Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7øð ×)Ò)¨'Ñ2Ô2Ð2Ð2Ð2Ð2Ð2Ð2Ð2Ø×)Ò)¨,Ñ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7øøøÐ7ðY,	8ð ,	8ð ,	8ñ ,	8ô ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8ð ,	8øøøð ,	8ð ,	8ð ,	8ð ,	8õ\ 	ŒÐ*¨I°tÔ7GÑHÔHÐHÐHÐHs,   Á<A*JÃ'D"IÈ
7JÉ8I9É9JÊ
JÊJrÿ   c              ƒ   ó|  K  — |                       ¦   «         }|                     |                     |d¦  «        ¦  «        ƒ d{V —†r|S |                     |¦  «        ƒ d{V —†D ]K}|                     ||¦  «        }|                     |                     |d¦  «        ¦  «        ƒ d{V —†r|c S ŒLt	          d¦  «        ‚)aÎ  Return the directory under ``staging`` holding ``SKILL.md``.

        Archives arrive flat or wrapped in a single top-level folder;
        both are accepted.

        Args:
            staging (`str`):
                The directory the archive was expanded into.

        Returns:
            `str`:
                The path of the directory containing ``SKILL.md``.

        Raises:
            ValueError:
                If no ``SKILL.md`` is found.
        r¶   Nz&The skill archive contains no SKILL.md)r;   rŸ   r<   r½   rÙ   )r,   rÿ   re   ÚentryÚ	candidates        r-   rú   zWorkspaceBase._find_skill_root=  s  è è € ð$ ×"Ò"Ñ$Ô$ˆØ×$Ò$ W×%6Ò%6°wÀ
Ñ%KÔ%KÑLÔLÐLÐLÐLÐLÐLÐLð 	ØˆNà"×+Ò+¨GÑ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4ð 	!ð 	!ˆEØ×)Ò)¨'°5Ñ9Ô9ˆIØ×(Ò(Ø×!Ò! )¨ZÑ8Ô8ñô ð ð ð ð ð ð ð !ð !Ð Ð Ð ð!õ
 ÐAÑBÔBÐBr0   c              ƒ   ó"  K  — |                       ¦   «         }|d}}|                     |                     | j        |¦  «        ¦  «        ƒ d{V —†r@|› d|› �}|dz  }|                     |                     | j        |¦  «        ¦  «        ƒ d{V —†°@|S )z÷Return ``dir_name``, suffixed until it is unused.

        Args:
            dir_name (`str`):
                The preferred directory name.

        Returns:
            `str`:
                A directory name not present in ``skills/``.
        r   Nú-)r;   rŸ   r<   r@   )r,   rç   re   r  Úcounters        r-   rû   zWorkspaceBase._free_skill_dir\  sâ   è è € ð ×"Ò"Ñ$Ô$ˆØ% q�7ˆ	Ø×'Ò'Ø×Ò˜dÔ.°	Ñ:Ô:ñ
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	ð $Ð/Ð/ gÐ/Ð/ˆIØ�q‰LˆGð	 ×'Ò'Ø×Ò˜dÔ.°	Ñ:Ô:ñ
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	ð
 Ðr0   c              ƒ   óD  K  — |                       ¦   «         }|                      ¦   «         ƒ d{V —†}d}|D ]}|j        |k    r	|j        } nŒ|€!d„ |D ¦   «         }t	          d|›d|› �¦  «        ‚|                     |¦  «        ƒ d{V —† t          j        d||¦  «         dS )aŠ  Remove a skill by its agent-facing ``name`` (front matter).

        Looks up the skill via :meth:`list_skills` and ``rm -rf``-style
        deletes its directory through the backend.

        Args:
            name (`str`):
                The agent-facing name of the skill to remove.

        Raises:
            KeyError:
                If the skill is not found in the workspace.
        Nc                 ó   — g | ]	}|j         ‘Œ
S r*   )rp   )r3   Úss     r-   r5   z.WorkspaceBase.remove_skill.<locals>.<listcomp>†  s   € Ð0Ð0Ð0 A˜œÐ0Ð0Ð0r0   zSkill z not found. Available: zRemoved skill %r at %s)r;   rÊ   rp   r¸   ÚKeyErrorrü   r   rå   )r,   rp   re   rÅ   Ú
target_dirr  Ú	availables          r-   Úremove_skillzWorkspaceBase.remove_skillp  sï   è è € ð ×"Ò"Ñ$Ô$ˆØ×'Ò'Ñ)Ô)Ð)Ð)Ð)Ð)Ð)Ð)ˆØ!%ˆ
Øð 	ð 	ˆAØŒv˜Š~ˆ~ØœU�
Ø�ð ð ÐØ0Ð0¨Ð0Ñ0Ô0ˆIÝØC˜ÐCÐC¸	ÐCÐCñô ð ð ×!Ò! *Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-Ð-ÝŒÐ,¨d°JÑ?Ô?Ð?Ð?Ð?r0   c              ƒ   ó   K  — | j         sdS | j        }|€dS |                     | j        ¦  «        ƒ d{V —†}|rdS | j         D ]K}	 |                      |¦  «        ƒ d{V —† Œ# t
          $ r }t          j        d||¦  «         Y d}~ŒDd}~ww xY wdS )uŽ  Copy :attr:`skill_paths` into ``${workdir}/skills`` once.

        Skips seeding when:

        - :attr:`skill_paths` is empty;
        - the backend is not bound; or
        - ``skills/`` already contains entries (assume the prior
          run, or the user, is the source of truth).

        Individual failures are logged and skipped â€” a single bad
        skill cannot block startup.
        NzSkip skill %r: %s)r$   r"   r½   r@   rî   r   r   r€   )r,   re   rÄ   r4   r‚   s        r-   Ú_setup_skillszWorkspaceBase._setup_skills�  sû   è è € ð Ôð 	ØˆFØ”-ˆØˆ?ØˆFØ×(Ò(¨Ô)9Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:ˆØð 	ØˆFØÔ$ð 	ð 	ˆDðØ—n’n TÑ*Ô*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ð*øÝð ð ð Ý”Ø'ØØñô ð ð ð ð ð ð øøøøðøøøð	ð 	s   ÁA!Á!
BÁ+BÂB)r(   N)9rV   Ú
__module__Ú__qualname__Ú__doc__rŠ   Ú__annotations__Úboolr   r6   r   r7   r8   Úpropertyr.   r9   r=   r@   rB   rD   rF   ÚstaticmethodrK   r   rM   rP   rR   r;   r   rX   Úobjectr[   r]   r   rj   rl   ro   rr   rƒ   r   r™   r   r§   r   rŒ   r   rÊ   rî   ÚDEFAULT_MAX_EXTRACTED_BYTESr   Úbytesr   Úintr  rú   rû   r  r  r*   r0   r-   r   r   ‘   sž  € € € € € € ðð ð ÐÐÑØ8à€L€L�LØEà€N€N�NØ0à˜DÑ Ð Ð Ñ ØKà�y”/Ð!Ð!Ñ!ð*ð �c”ÐÐÑØFà�	Œ?ÐÐÑðð Œ|ÐÐÑØAà”ÐÐÑØ7àð 3¨¡:ð ð ð ñ „Xðð $(Ø/3Ø(,ð%*ð %*ð %*ð ˜D‘jð%*ð ˜9”o¨Ñ,ð	%*ð
 ˜#”Y Ñ%ð%*ð 
ð%*ð %*ð %*ð %*ðR ðL˜3ð Lð Lð Lñ „XðLð ð
˜Sð 
ð 
ð 
ñ „Xð
ð ð
˜sð 
ð 
ð 
ñ „Xð
ð ðL˜3ð Lð Lð Lñ „XðLð ð˜tð ð ð ñ „Xðð ð# ð #¨ð #ð #ð #ñ „\ð#ð ðEð Eð Eñ „^ðEð ð5ð 5ð 5ñ „^ð5ðð ð ð ð˜[ð ð ð ð ð& $ð ð ð ð ð Fð ¨tð ð ð ð ð ð9¨ð 9ð 9ð 9ñ „^ð9ð

 $ x¤.ð 
ð 
ð 
ð 
ð<   i¤ð  ð  ð  ð  ð ð
¨	ð 
°dð 
ð 
ð 
ñ „^ð
ð ð Sð ¨Tð ð ð ñ „^ððð ð ð ð@6àð6ð �3Œið6ð 
ð	6ð 6ð 6ð 6ðp9àð9ð %ð9ð 
ð	9ð 9ð 9ð 9ðv/
¨ið /
¸Ið /
ð /
ð /
ð /
ðf. 4¨¤;ð .ð .ð .ð .ð`CF¨#ð CF°$ð CFð CFð CFð CFðT $?ðYIð YIà˜eÔ$ðYIð Ð+Ô,ðYIð ð	YIð
 !ðYIð 
ðYIð YIð YIð YIðvC¨cð C°cð Cð Cð Cð Cð>¨cð °cð ð ð ð ð(@ sð @¨tð @ð @ð @ð @ð:ð ð ð ð ð r0   r   )1r  r7   r¯   r©   rÛ   ru   r­   r×   rÝ   Úabcr   Úcopyr   Úpathlibr   Útypingr   r   r   Úpydanticr	   Ú_loggingr   Ú_utils._commonr   r   Úmcpr   Úmessager   r   r   r   r   r   Úskillr   rh   r   r   Ú_utilsr   r   r   r   râ   rù   r  r   r*   r0   r-   ú<module>r)     s  ðð,ð ,ð\ €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ø 	€	€	€	Ø €€€Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø /Ð /Ð /Ð /Ð /Ð /Ð /Ð /Ð /Ð /à Ð Ð Ð Ð Ð à Ð Ð Ð Ð Ð Ø @Ð @Ð @Ð @Ð @Ð @Ð @Ð @Ø Ð Ð Ð Ð Ð ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð Ð Ð Ð Ð Ð Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (ðð ð ð ð ð ð ð ð ð ð ð ðð ð. ð ðJ 0Ð ðYð Yð Yð Yð Yñ Yô Yð Yð Yð Yr0   