chore: import upstream snapshot with attribution
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:30:44 +08:00
commit bcbd1bdb22
5748 changed files with 562488 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import importlib.util
collect_ignore_glob: list[str] = []
if importlib.util.find_spec("camel") is None:
collect_ignore_glob = ["test_*.py"]
+54
View File
@@ -0,0 +1,54 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import asyncio
import pytest
from mirage.agents.camel._async import AsyncRunner
def test_run_from_sync_with_no_loop():
runner = AsyncRunner()
async def coro():
await asyncio.sleep(0)
return 42
assert runner.run(coro()) == 42
runner.close()
def test_run_returns_exception():
runner = AsyncRunner()
async def boom():
raise ValueError("nope")
with pytest.raises(ValueError, match="nope"):
runner.run(boom())
runner.close()
@pytest.mark.asyncio
async def test_run_from_inside_running_loop():
runner = AsyncRunner()
async def coro():
await asyncio.sleep(0)
return "from-loop"
result = await asyncio.to_thread(runner.run, coro())
assert result == "from-loop"
runner.close()
+67
View File
@@ -0,0 +1,67 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import inspect
from camel.toolkits import BaseToolkit, FileToolkit, FunctionTool
def test_base_toolkit_has_get_tools():
assert hasattr(BaseToolkit, "get_tools")
def test_file_toolkit_private_hooks_exist():
expected = [
"_resolve_filepath",
"_resolve_existing_filepath",
"_resolve_search_path",
"_sanitize_filename",
"_create_backup",
"_write_text_file",
"_write_simple_text_file",
"_write_csv_file",
"_write_json_file",
"_write_docx_file",
"_write_pdf_file",
"_normalize_notebook_source",
"_build_notebook_cell",
]
missing = [name for name in expected if not hasattr(FileToolkit, name)]
assert not missing, f"FileToolkit missing hooks: {missing}"
def test_file_toolkit_public_methods_signatures():
public = [
"write_to_file",
"read_file",
"edit_file",
"search_files",
"notebook_edit_cell",
"glob_files",
"grep_files",
]
missing = [name for name in public if not hasattr(FileToolkit, name)]
assert not missing, f"FileToolkit missing public methods: {missing}"
for name in public:
sig = inspect.signature(getattr(FileToolkit, name))
assert "self" in sig.parameters
def test_function_tool_constructible():
def sample() -> str:
return "ok"
tool = FunctionTool(sample)
assert tool is not None
+99
View File
@@ -0,0 +1,99 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import json
import pytest
from mirage import MountMode, Workspace
from mirage.agents.camel import MirageFileToolkit
from mirage.resource.ram import RAMResource
@pytest.fixture
def workspace():
ram = RAMResource()
yield Workspace({"/": ram}, mode=MountMode.WRITE)
@pytest.fixture
def toolkit(workspace):
tk = MirageFileToolkit(workspace)
yield tk
tk.close()
def _strip_markdown_code_fence(text: str) -> str:
text = text.strip()
if text.startswith("```"):
lines = text.splitlines()
return "\n".join(
lines[1:-1] if lines[-1].startswith("```") else lines[1:])
return text
def test_write_text_file_then_read(toolkit):
msg = toolkit.write_to_file(title="Hello",
content="hi there",
filename="/notes/hello.md")
assert "hello.md" in msg
out = toolkit.read_file(file_paths="/notes/hello.md")
assert "hi there" in out
def test_write_json_file_then_read(toolkit):
msg = toolkit.write_to_file(title="data",
content={
"a": 1,
"b": [2, 3]
},
filename="/data.json")
assert "data.json" in msg
out = toolkit.read_file(file_paths="/data.json")
parsed = json.loads(_strip_markdown_code_fence(out))
assert parsed == {"a": 1, "b": [2, 3]}
def test_edit_file_replaces_content(toolkit):
toolkit.write_to_file(title="t", content="old\nkeep\n", filename="/e.txt")
msg = toolkit.edit_file(file_path="/e.txt",
old_content="old",
new_content="new")
assert "successfully" in msg.lower() or "edited" in msg.lower()
out = toolkit.read_file(file_paths="/e.txt")
assert "new" in out and "keep" in out and "old" not in out.split("keep")[0]
def test_search_files_by_name(toolkit):
toolkit.write_to_file(title="a", content="x", filename="/dir/a.txt")
toolkit.write_to_file(title="b", content="y", filename="/dir/b.md")
out = toolkit.search_files(file_name="a.txt", path="/dir")
assert "a.txt" in out
def test_glob_files(toolkit):
toolkit.write_to_file(title="a", content="x", filename="/g/x.py")
toolkit.write_to_file(title="a", content="y", filename="/g/y.py")
out = toolkit.glob_files(pattern="*.py", path="/g")
assert "x.py" in out and "y.py" in out
def test_grep_files(toolkit):
toolkit.write_to_file(title="a",
content="needle here\n",
filename="/q/a.txt")
toolkit.write_to_file(title="b", content="haystack\n", filename="/q/b.txt")
out = toolkit.grep_files(pattern="needle", path="/q")
assert "needle" in out
assert "a.txt" in out
@@ -0,0 +1,80 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import pytest
from mirage import MountMode, Workspace
from mirage.agents.camel import MirageTerminalToolkit
from mirage.resource.ram import RAMResource
@pytest.fixture
def workspace():
ram = RAMResource()
ws = Workspace({"/": ram}, mode=MountMode.WRITE)
yield ws
@pytest.fixture
def toolkit(workspace):
tk = MirageTerminalToolkit(workspace)
yield tk
tk.close()
def test_shell_exec_blocking_returns_stdout(toolkit):
out = toolkit.shell_exec(id="t1", command="echo hello", block=True)
assert "hello" in out
def test_shell_exec_blocking_captures_stderr(toolkit):
out = toolkit.shell_exec(id="t1",
command="ls /nonexistent-zzz",
block=True)
assert out, "expected non-empty output for failing command"
def test_shell_write_content_to_file(toolkit, workspace):
msg = toolkit.shell_write_content_to_file(content="line1\nline2\n",
file_path="/note.txt")
assert "note.txt" in msg
out = toolkit.shell_exec(id="t1", command="cat /note.txt", block=True)
assert "line1" in out and "line2" in out
def test_shell_exec_nonblocking_returns_session_id(toolkit):
msg = toolkit.shell_exec(id="bg1", command="sleep 0.5", block=False)
assert "bg1" in msg
def test_shell_view_after_completion(toolkit):
toolkit.shell_exec(id="bg2", command="echo done", block=False)
out = toolkit.shell_view(id="bg2")
assert "done" in out
def test_shell_kill_unknown_session(toolkit):
msg = toolkit.shell_kill_process(id="never-started")
assert "Error" in msg
def test_shell_write_to_process_returns_clear_error(toolkit):
toolkit.shell_exec(id="bg3", command="sleep 0.5", block=False)
msg = toolkit.shell_write_to_process(id="bg3", command="anything")
assert "not interactive" in msg.lower()
def test_get_tools_returns_six(toolkit):
tools = toolkit.get_tools()
assert len(tools) == 6
@@ -0,0 +1,42 @@
import pytest
claude_agent_sdk = pytest.importorskip("claude_agent_sdk")
from mirage import MountMode, RAMResource, Workspace # noqa: E402
from mirage.agents.claude_agent_sdk.options import build_options # noqa: E402
@pytest.fixture
def workspace():
return Workspace({"/": RAMResource()}, mode=MountMode.WRITE)
def test_build_options_returns_claude_agent_options(workspace):
options = build_options(workspace)
assert isinstance(options, claude_agent_sdk.ClaudeAgentOptions)
def test_build_options_has_mirage_server(workspace):
options = build_options(workspace)
assert "mirage" in options.mcp_servers
def test_build_options_allowed_tools(workspace):
options = build_options(workspace)
assert "mcp__mirage__*" in options.allowed_tools
def test_build_options_disables_builtin_tools(workspace):
options = build_options(workspace)
assert options.tools == []
def test_build_options_custom_system_prompt(workspace):
options = build_options(workspace, system_prompt="custom prompt")
assert options.system_prompt == "custom prompt"
def test_build_options_default_system_prompt(workspace):
options = build_options(workspace)
assert options.system_prompt is not None
assert len(options.system_prompt) > 0
@@ -0,0 +1,167 @@
import pytest
pytest.importorskip("claude_agent_sdk")
from mirage import MountMode, RAMResource, Workspace # noqa: E402
from mirage.agents.claude_agent_sdk.server import _MirageTools # noqa: E402
@pytest.fixture
def workspace():
return Workspace({"/": RAMResource()}, mode=MountMode.WRITE)
@pytest.fixture
def tools(workspace):
return _MirageTools(workspace)
@pytest.mark.asyncio
async def test_execute_command_echo(tools):
result = await tools.execute_command({"command": "echo hello"})
assert "hello" in result["content"][0]["text"]
assert result.get("is_error") is not True
@pytest.mark.asyncio
async def test_execute_command_pipe(tools, workspace):
await workspace.ops.write("/pipe.txt", b"aaa\nbbb\naaa\n")
result = await tools.execute_command(
{"command": "cat /pipe.txt | sort | uniq | wc -l"})
assert "2" in result["content"][0]["text"]
@pytest.mark.asyncio
async def test_read_file(tools, workspace):
await workspace.ops.write("/hello.txt", b"line1\nline2\nline3\n")
result = await tools.read({"path": "/hello.txt"})
text = result["content"][0]["text"]
assert "line1" in text
assert "line2" in text
assert result.get("is_error") is not True
@pytest.mark.asyncio
async def test_read_file_with_offset_and_limit(tools, workspace):
await workspace.ops.write("/multi.txt", b"a\nb\nc\nd\ne\n")
result = await tools.read({"path": "/multi.txt", "offset": 1, "limit": 2})
text = result["content"][0]["text"]
assert "b" in text
assert "c" in text
assert "a" not in text
assert "d" not in text
@pytest.mark.asyncio
async def test_read_file_not_found(tools):
result = await tools.read({"path": "/nonexistent.txt"})
assert result["is_error"] is True
assert "not found" in result["content"][0]["text"]
@pytest.mark.asyncio
async def test_write_file(tools, workspace):
result = await tools.write({"path": "/new.txt", "content": "hello world"})
assert result.get("is_error") is not True
data = await workspace.ops.read("/new.txt")
assert data == b"hello world"
@pytest.mark.asyncio
async def test_write_file_already_exists(tools, workspace):
await workspace.ops.write("/exists.txt", b"first")
result = await tools.write({"path": "/exists.txt", "content": "second"})
assert result["is_error"] is True
assert "already exists" in result["content"][0]["text"]
@pytest.mark.asyncio
async def test_edit_file(tools, workspace):
await workspace.ops.write("/edit.txt", b"foo bar baz")
result = await tools.edit({
"path": "/edit.txt",
"old_string": "bar",
"new_string": "qux"
})
assert result.get("is_error") is not True
data = await workspace.ops.read("/edit.txt")
assert data == b"foo qux baz"
@pytest.mark.asyncio
async def test_edit_file_not_found(tools):
result = await tools.edit({
"path": "/missing.txt",
"old_string": "x",
"new_string": "y"
})
assert result["is_error"] is True
assert "not found" in result["content"][0]["text"]
@pytest.mark.asyncio
async def test_edit_string_not_found(tools, workspace):
await workspace.ops.write("/nostr.txt", b"hello world")
result = await tools.edit({
"path": "/nostr.txt",
"old_string": "xyz",
"new_string": "abc"
})
assert result["is_error"] is True
assert "not found" in result["content"][0]["text"]
@pytest.mark.asyncio
async def test_edit_multiple_occurrences_without_replace_all(tools, workspace):
await workspace.ops.write("/multi.txt", b"aa bb aa")
result = await tools.edit({
"path": "/multi.txt",
"old_string": "aa",
"new_string": "cc"
})
assert result["is_error"] is True
assert "replace_all" in result["content"][0]["text"]
@pytest.mark.asyncio
async def test_edit_replace_all(tools, workspace):
await workspace.ops.write("/all.txt", b"aa bb aa")
result = await tools.edit({
"path": "/all.txt",
"old_string": "aa",
"new_string": "cc",
"replace_all": True
})
assert result.get("is_error") is not True
data = await workspace.ops.read("/all.txt")
assert data == b"cc bb cc"
@pytest.mark.asyncio
async def test_write_creates_parent_dirs(tools, workspace):
result = await tools.write({
"path": "/nested/deep/file.txt",
"content": "hi"
})
assert result.get("is_error") is not True
data = await workspace.ops.read("/nested/deep/file.txt")
assert data == b"hi"
@pytest.mark.asyncio
async def test_ls(tools, workspace):
await tools.write({"path": "/dir/a.txt", "content": "a"})
await tools.write({"path": "/dir/b.txt", "content": "b"})
result = await tools.ls({"path": "/dir"})
text = result["content"][0]["text"]
assert "a.txt" in text
assert "b.txt" in text
@pytest.mark.asyncio
async def test_grep(tools, workspace):
await workspace.ops.write("/search.txt",
b"hello world\ngoodbye world\nhello again\n")
result = await tools.grep({"pattern": "hello", "path": "/"})
text = result["content"][0]["text"]
assert "hello" in text
@@ -0,0 +1,123 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import pytest
from mirage import MountMode, RAMResource, Workspace
from mirage.agents.langchain.backend import LangchainWorkspace
@pytest.fixture
def workspace():
return Workspace({"/": RAMResource()}, mode=MountMode.WRITE)
@pytest.fixture
def backend(workspace):
return LangchainWorkspace(workspace)
def test_id(backend):
assert backend.id == "mirage"
def test_custom_id(workspace):
b = LangchainWorkspace(workspace, sandbox_id="custom")
assert b.id == "custom"
@pytest.mark.asyncio
async def test_aexecute_echo(backend):
resp = await backend.aexecute("echo hello")
assert resp.exit_code == 0
assert "hello" in resp.output
@pytest.mark.asyncio
async def test_aexecute_failing_command(backend):
resp = await backend.aexecute("cat /nonexistent")
assert resp.exit_code != 0
@pytest.mark.asyncio
async def test_awrite_and_aread(backend):
result = await backend.awrite("/test.txt", "hello world")
assert result.error is None
content = await backend.aread("/test.txt")
assert "hello world" in content
@pytest.mark.asyncio
async def test_awrite_existing_file_errors(backend):
await backend.awrite("/exists.txt", "first")
result = await backend.awrite("/exists.txt", "second")
assert result.error is not None
@pytest.mark.asyncio
async def test_aedit(backend):
await backend.awrite("/edit.txt", "foo bar baz")
result = await backend.aedit("/edit.txt", "bar", "qux")
assert result.error is None
content = await backend.aread("/edit.txt")
assert "qux" in content
assert "bar" not in content
@pytest.mark.asyncio
async def test_als_info(backend):
await backend.awrite("/dir/a.txt", "a")
await backend.awrite("/dir/b.txt", "b")
entries = await backend.als_info("/dir")
paths = [e["path"] for e in entries]
assert len(paths) == 2
@pytest.mark.asyncio
async def test_agrep_raw(backend):
await backend.awrite("/search.txt",
"hello world\ngoodbye world\nhello again")
result = await backend.agrep_raw("hello", path="/")
assert isinstance(result, list)
assert len(result) >= 2
@pytest.mark.asyncio
async def test_aglob_info(backend):
await backend.awrite("/data/a.txt", "a")
await backend.awrite("/data/b.py", "b")
entries = await backend.aglob_info("*.txt", path="/data")
paths = [e["path"] for e in entries]
assert any("a.txt" in p for p in paths)
assert not any("b.py" in p for p in paths)
@pytest.mark.asyncio
async def test_execute_pipe(backend):
await backend.awrite("/pipe.txt", "aaa\nbbb\nccc\naaa\n")
resp = await backend.aexecute("cat /pipe.txt | sort | uniq | wc -l")
assert resp.exit_code == 0
assert "3" in resp.output
@pytest.mark.asyncio
async def test_upload_and_download(backend):
files = [("/up1.txt", b"content1"), ("/up2.txt", b"content2")]
up_results = await backend.aupload_files(files)
assert all(r.error is None for r in up_results)
down_results = await backend.adownload_files(["/up1.txt", "/up2.txt"])
assert down_results[0].content == b"content1"
assert down_results[1].content == b"content2"
@@ -0,0 +1,72 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
from mirage.agents.langchain._convert import (io_to_execute_response,
io_to_file_infos,
io_to_grep_matches)
from mirage.io.types import IOResult
def test_io_to_execute_response_basic():
io = IOResult(stdout=b"hello world\n", exit_code=0)
resp = io_to_execute_response(io)
assert resp.output == "hello world\n"
assert resp.exit_code == 0
assert resp.truncated is False
def test_io_to_execute_response_none_stdout():
io = IOResult(stdout=None, exit_code=1)
resp = io_to_execute_response(io)
assert resp.output == ""
assert resp.exit_code == 1
def test_io_to_execute_response_stderr_appended():
io = IOResult(stdout=b"out", stderr=b"err", exit_code=1)
resp = io_to_execute_response(io)
assert "out" in resp.output
assert "err" in resp.output
def test_io_to_grep_matches():
io = IOResult(stdout=b"/a.txt:1:hello world\n/b.txt:5:hello there\n",
exit_code=0)
matches = io_to_grep_matches(io)
assert len(matches) == 2
assert matches[0]["path"] == "/a.txt"
assert matches[0]["line"] == 1
assert matches[0]["text"] == "hello world"
def test_io_to_grep_matches_empty():
io = IOResult(stdout=b"", exit_code=1)
matches = io_to_grep_matches(io)
assert matches == []
def test_io_to_file_infos():
io = IOResult(stdout=b"/foo/bar.txt\n/foo/baz.py\n/foo/sub/\n",
exit_code=0)
infos = io_to_file_infos(io)
assert len(infos) == 3
dirs = [i for i in infos if i.get("is_dir")]
assert len(dirs) == 1
assert dirs[0]["path"] == "/foo/sub"
def test_io_to_file_infos_empty():
io = IOResult(stdout=b"", exit_code=0)
infos = io_to_file_infos(io)
assert infos == []
@@ -0,0 +1,70 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
from mirage.agents.langchain._messages import extract_text
def test_extract_text_from_string():
class Msg:
content = "hello world"
assert extract_text([Msg()]) == ["hello world"]
def test_extract_text_from_blocks():
class Msg:
content = [
{
"type": "text",
"text": "hello"
},
{
"type": "tool_use",
"name": "ls",
"input": {}
},
{
"type": "text",
"text": "goodbye"
},
]
assert extract_text([Msg()]) == ["hello", "goodbye"]
def test_extract_text_skips_empty():
class Msg:
content = [
{
"type": "text",
"text": " "
},
{
"type": "text",
"text": "real"
},
]
assert extract_text([Msg()]) == ["real"]
def test_extract_text_no_content():
class Msg:
pass
assert extract_text([Msg()]) == []
@@ -0,0 +1,103 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import base64
from unittest.mock import AsyncMock, MagicMock
import pytest
from mirage.agents.openai_agents.runner import MirageRunner
from mirage.types import FileStat, FileType
def _stat(file_type: FileType) -> FileStat:
return FileStat(name="x", type=file_type)
@pytest.fixture
def ws():
workspace = MagicMock()
workspace.ops = MagicMock()
workspace.ops.stat = AsyncMock()
workspace.ops.read = AsyncMock()
return workspace
@pytest.mark.asyncio
async def test_build_blocks_image_inlines_base64(ws):
ws.ops.stat.return_value = _stat(FileType.IMAGE_PNG)
ws.ops.read.return_value = b"\x89PNG\r\n\x1a\n"
runner = MirageRunner(ws)
blocks = await runner.build_blocks("hi", ["/img.png"])
assert blocks[0] == {"type": "input_text", "text": "hi"}
assert blocks[1]["type"] == "input_image"
expected_b64 = base64.b64encode(b"\x89PNG\r\n\x1a\n").decode()
assert blocks[1]["image_url"] == f"data:image/png;base64,{expected_b64}"
@pytest.mark.asyncio
async def test_build_blocks_pdf_uploads_to_files_api(ws):
ws.ops.stat.return_value = _stat(FileType.PDF)
ws.ops.read.return_value = b"%PDF-1.4 ..."
fake_client = MagicMock()
fake_client.files = MagicMock()
fake_client.files.create = AsyncMock(return_value=MagicMock(id="file-abc"))
runner = MirageRunner(ws, client=fake_client)
blocks = await runner.build_blocks("read", ["/doc.pdf"])
assert blocks[1] == {"type": "input_file", "file_id": "file-abc"}
fake_client.files.create.assert_awaited_once()
args, kwargs = fake_client.files.create.call_args
assert kwargs["purpose"] == "user_data"
fname, fdata = kwargs["file"]
assert fname == "doc.pdf"
assert fdata == b"%PDF-1.4 ..."
@pytest.mark.asyncio
async def test_build_blocks_text_decoded_inline(ws):
ws.ops.stat.return_value = _stat(FileType.TEXT)
ws.ops.read.return_value = b"hello world"
runner = MirageRunner(ws)
blocks = await runner.build_blocks("look", ["/notes.txt"])
assert blocks[1] == {"type": "input_text", "text": "hello world"}
@pytest.mark.asyncio
async def test_build_blocks_jpeg(ws):
ws.ops.stat.return_value = _stat(FileType.IMAGE_JPEG)
ws.ops.read.return_value = b"\xff\xd8\xff..."
runner = MirageRunner(ws)
blocks = await runner.build_blocks("see", ["/photo.jpg"])
assert blocks[1]["type"] == "input_image"
assert blocks[1]["image_url"].startswith("data:image/jpeg;base64,")
@pytest.mark.asyncio
async def test_build_blocks_multiple_paths_in_order(ws):
types = [FileType.TEXT, FileType.IMAGE_PNG]
bytes_seq = [b"first", b"\x89PNG\r\n\x1a\n"]
async def fake_stat(p):
return _stat(types.pop(0))
async def fake_read(p):
return bytes_seq.pop(0)
ws.ops.stat.side_effect = fake_stat
ws.ops.read.side_effect = fake_read
runner = MirageRunner(ws)
blocks = await runner.build_blocks("two", ["/a.txt", "/b.png"])
assert len(blocks) == 3
assert blocks[1] == {"type": "input_text", "text": "first"}
assert blocks[2]["type"] == "input_image"
@@ -0,0 +1,87 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import asyncio
import io
from pathlib import Path
from mirage.agents.openai_agents.sandbox import MirageSandboxClient
from mirage.resource.ram import RAMResource
from mirage.types import MountMode
from mirage.workspace import Workspace
def _make_client() -> MirageSandboxClient:
ram = RAMResource()
ws = Workspace(
{"/": (ram, MountMode.WRITE)},
mode=MountMode.WRITE,
)
return MirageSandboxClient(ws)
def test_create_session_with_default_resources():
async def _run():
client = _make_client()
session = await client.create()
result = await session.exec("echo hello", shell=False)
assert result.exit_code == 0
assert b"hello" in result.stdout
asyncio.run(_run())
def test_session_file_read_write():
async def _run():
client = _make_client()
session = await client.create()
content = b"test file content"
await session.write(Path("myfile.txt"), io.BytesIO(content))
stream = await session.read(Path("myfile.txt"))
assert stream.read() == content
asyncio.run(_run())
def test_persist_and_hydrate_workspace():
async def _run():
client = _make_client()
session = await client.create()
await session.write(Path("data.txt"), io.BytesIO(b"persist me"))
snapshot = await session.persist_workspace()
client2 = _make_client()
session2 = await client2.create()
await session2.hydrate_workspace(snapshot)
stream = await session2.read(Path("data.txt"))
assert stream.read() == b"persist me"
asyncio.run(_run())
def test_session_running_state():
async def _run():
client = _make_client()
session = await client.create()
assert await session.running() is True
asyncio.run(_run())
@@ -0,0 +1,108 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
from pathlib import Path
import pytest
pytest.importorskip("openhands")
from mirage.agents.openhands import MirageWorkspace # noqa: E402
from mirage.resource.ram import RAMResource # noqa: E402
from mirage.types import MountMode # noqa: E402
from mirage.workspace import Workspace # noqa: E402
def _make_backing() -> Workspace:
ram = RAMResource()
return Workspace({"/": (ram, MountMode.WRITE)}, mode=MountMode.WRITE)
def test_execute_command_basic():
with MirageWorkspace(workspace=_make_backing()) as mw:
result = mw.execute_command("echo hello")
assert result.exit_code == 0
assert "hello" in result.stdout
assert result.command == "echo hello"
assert result.timeout_occurred is False
def test_execute_command_with_cwd():
with MirageWorkspace(workspace=_make_backing()) as mw:
mw.execute_command("echo data > /file.txt")
result = mw.execute_command("cat file.txt", cwd="/")
assert result.exit_code == 0, result.stderr
assert "data" in result.stdout
def test_execute_command_nonzero_exit():
with MirageWorkspace(workspace=_make_backing()) as mw:
result = mw.execute_command("cat /no/such/path")
assert result.exit_code != 0
def test_file_upload_then_download(tmp_path: Path):
src = tmp_path / "src.bin"
src.write_bytes(b"abc 123")
with MirageWorkspace(workspace=_make_backing()) as mw:
up = mw.file_upload(src, "/uploaded.bin")
assert up.success is True
assert up.file_size == 7
cat = mw.execute_command("cat /uploaded.bin")
assert cat.exit_code == 0
assert "abc 123" in cat.stdout
out = tmp_path / "out.bin"
dn = mw.file_download("/uploaded.bin", out)
assert dn.success is True
assert dn.file_size == 7
assert out.read_bytes() == b"abc 123"
def test_file_upload_into_nested_dir(tmp_path: Path):
src = tmp_path / "src.txt"
src.write_bytes(b"nested")
with MirageWorkspace(workspace=_make_backing()) as mw:
up = mw.file_upload(src, "/sub/dir/file.txt")
assert up.success is True
cat = mw.execute_command("cat /sub/dir/file.txt")
assert cat.exit_code == 0
assert "nested" in cat.stdout
def test_file_download_missing_source_returns_error(tmp_path: Path):
out = tmp_path / "out.txt"
with MirageWorkspace(workspace=_make_backing()) as mw:
dn = mw.file_download("/does/not/exist.txt", out)
assert dn.success is False
assert dn.error
assert out.exists() is False
def test_context_manager_closes_backing_workspace():
backing = _make_backing()
with MirageWorkspace(workspace=backing) as mw:
result = mw.execute_command("echo ctx")
assert result.exit_code == 0
assert backing._closed is True
def test_git_methods_not_supported():
with MirageWorkspace(workspace=_make_backing()) as mw:
with pytest.raises(NotImplementedError):
mw.git_changes("/")
with pytest.raises(NotImplementedError):
mw.git_diff("/some/path")
@@ -0,0 +1,136 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
from pathlib import Path
import pytest
from pydantic_ai import ToolReturn
from mirage import MountMode, RAMResource, Workspace
from mirage.agents.pydantic_ai.backend import PydanticAIWorkspace
DATA_DIR = Path(__file__).resolve().parents[4] / "data"
@pytest.fixture
def workspace():
return Workspace({"/": RAMResource()}, mode=MountMode.WRITE)
@pytest.fixture
def backend(workspace):
return PydanticAIWorkspace(workspace)
def test_id(backend):
assert backend.id == "mirage"
def test_custom_id(workspace):
b = PydanticAIWorkspace(workspace, sandbox_id="custom")
assert b.id == "custom"
@pytest.mark.asyncio
async def test_aexecute_echo(backend):
resp = await backend.aexecute("echo hello")
assert resp.exit_code == 0
assert "hello" in resp.output
@pytest.mark.asyncio
async def test_aexecute_failing_command(backend):
resp = await backend.aexecute("cat /nonexistent")
assert resp.exit_code != 0
@pytest.mark.asyncio
async def test_awrite_and_aread(backend):
result = await backend.awrite("/test.txt", "hello world")
assert result.error is None
content = await backend.aread("/test.txt")
assert "hello world" in content
@pytest.mark.asyncio
async def test_awrite_existing_file_errors(backend):
await backend.awrite("/exists.txt", "first")
result = await backend.awrite("/exists.txt", "second")
assert result.error is not None
@pytest.mark.asyncio
async def test_aedit(backend):
await backend.awrite("/edit.txt", "foo bar baz")
result = await backend.aedit("/edit.txt", "bar", "qux")
assert result.error is None
content = await backend.aread("/edit.txt")
assert "qux" in content
assert "bar" not in content
@pytest.mark.asyncio
async def test_als_info(backend):
await backend.awrite("/dir/a.txt", "a")
await backend.awrite("/dir/b.txt", "b")
entries = await backend.als_info("/dir")
paths = [e["path"] for e in entries]
assert len(paths) == 2
@pytest.mark.asyncio
async def test_agrep_raw(backend):
await backend.awrite("/search.txt",
"hello world\ngoodbye world\nhello again")
result = await backend.agrep_raw("hello", path="/")
assert isinstance(result, list)
assert len(result) >= 2
@pytest.mark.asyncio
async def test_aglob_info(backend):
await backend.awrite("/data/a.txt", "a")
await backend.awrite("/data/b.py", "b")
entries = await backend.aglob_info("*.txt", path="/data")
paths = [e["path"] for e in entries]
assert any("a.txt" in p for p in paths)
assert not any("b.py" in p for p in paths)
@pytest.mark.asyncio
async def test_execute_pipe(backend):
await backend.awrite("/pipe.txt", "aaa\nbbb\nccc\naaa\n")
resp = await backend.aexecute("cat /pipe.txt | sort | uniq | wc -l")
assert resp.exit_code == 0
assert "3" in resp.output
@pytest.mark.asyncio
async def test_read_bytes(backend):
await backend.awrite("/bytes.txt", "binary content")
data = await backend._aread_bytes("/bytes.txt")
assert data == b"binary content"
@pytest.mark.asyncio
async def test_aread_pdf_returns_tool_return(backend, workspace):
with open(DATA_DIR / "example.pdf", "rb") as f:
pdf_bytes = f.read()
await workspace.ops.write("/report.pdf", pdf_bytes)
result = await backend.aread("/report.pdf")
assert isinstance(result, ToolReturn)
assert "report.pdf" in result.return_value
assert len(result.content) > 0
@@ -0,0 +1,98 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
from dataclasses import dataclass
import pytest
from pydantic_ai import Agent
from pydantic_ai.models.test import TestModel
from pydantic_ai_backends import create_console_toolset
from mirage import MountMode, RAMResource, Workspace
from mirage.agents.pydantic_ai.backend import PydanticAIWorkspace
@dataclass
class Deps:
backend: PydanticAIWorkspace
@pytest.fixture
def workspace():
return Workspace({"/": RAMResource()}, mode=MountMode.WRITE)
@pytest.fixture
def backend(workspace):
return PydanticAIWorkspace(workspace)
def test_agent_write_and_read(backend):
agent = Agent(
TestModel(call_tools=["write_file", "read_file"]),
deps_type=Deps,
toolsets=[create_console_toolset()],
)
result = agent.run_sync("write a file", deps=Deps(backend=backend))
assert result.output
def test_agent_ls(backend, workspace):
import asyncio
asyncio.run(backend.awrite("/data/file.txt", "hello"))
agent = Agent(
TestModel(call_tools=["ls"]),
deps_type=Deps,
toolsets=[create_console_toolset()],
)
result = agent.run_sync("list files in /data", deps=Deps(backend=backend))
assert result.output
def test_agent_edit(backend):
import asyncio
asyncio.run(backend.awrite("/code.py", "x = 1\ny = 2\n"))
agent = Agent(
TestModel(call_tools=["edit_file"]),
deps_type=Deps,
toolsets=[create_console_toolset()],
)
result = agent.run_sync("change x to 42", deps=Deps(backend=backend))
assert result.output
def test_agent_grep(backend):
import asyncio
asyncio.run(backend.awrite("/hello.txt", "hello world\ngoodbye world\n"))
agent = Agent(
TestModel(call_tools=["grep"]),
deps_type=Deps,
toolsets=[create_console_toolset()],
)
result = agent.run_sync("search for hello", deps=Deps(backend=backend))
assert result.output
def test_no_real_filesystem(backend, workspace):
import asyncio
asyncio.run(backend.awrite("/test.txt", "in-memory content"))
content = asyncio.run(workspace.ops.read("/test.txt"))
assert content == b"in-memory content"
import os
assert not os.path.exists("/test.txt")
+34
View File
@@ -0,0 +1,34 @@
from mirage.agents.io_text import decode, io_to_str
from mirage.io.types import IOResult
def test_decode_none_returns_empty():
assert decode(None) == ""
def test_decode_bytes():
assert decode(b"hello") == "hello"
def test_decode_invalid_utf8_replaces():
assert decode(b"\xff") == ""
def test_io_to_str_stdout_only():
assert io_to_str(IOResult(stdout=b"out")) == "out"
def test_io_to_str_stderr_only():
assert io_to_str(IOResult(stderr=b"err")) == "err"
def test_io_to_str_combines_stdout_and_stderr():
assert io_to_str(IOResult(stdout=b"out", stderr=b"err")) == "out\nerr"
async def _stream():
yield b"streamed"
def test_io_to_str_ignores_unmaterialized_stream():
assert io_to_str(IOResult(stdout=_stream())) == ""
@@ -0,0 +1,98 @@
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import importlib
import sys
import pytest
class _BlockDeepagents:
def find_spec(self, name, path=None, target=None):
if name == "deepagents" or name.startswith("deepagents."):
raise ModuleNotFoundError(f"blocked import of {name}")
return None
def _evict_agents_and_deepagents(saved):
for name in list(sys.modules):
if (name == "deepagents" or name.startswith("deepagents.")
or name == "mirage.agents"
or name.startswith("mirage.agents.")):
saved[name] = sys.modules.pop(name)
@pytest.fixture
def deepagents_blocked():
"""Simulate an environment where `deepagents` is not installed.
Yields:
None: The fixture only manipulates import state; nothing is returned.
"""
blocker = _BlockDeepagents()
sys.meta_path.insert(0, blocker)
saved = {}
_evict_agents_and_deepagents(saved)
try:
yield
finally:
sys.meta_path.remove(blocker)
for name in list(sys.modules):
if (name == "deepagents" or name.startswith("deepagents.")
or name == "mirage.agents"
or name.startswith("mirage.agents.")):
del sys.modules[name]
sys.modules.update(saved)
def test_pydantic_ai_prompt_imports_without_deepagents(deepagents_blocked):
mod = importlib.import_module("mirage.agents.pydantic_ai.prompt")
assert isinstance(mod.MIRAGE_SYSTEM_PROMPT, str)
assert callable(mod.build_system_prompt)
assert mod.build_system_prompt() == mod.MIRAGE_SYSTEM_PROMPT
def test_openai_agents_prompt_imports_without_deepagents(deepagents_blocked):
mod = importlib.import_module("mirage.agents.openai_agents.prompt")
assert isinstance(mod.MIRAGE_SYSTEM_PROMPT, str)
assert callable(mod.build_system_prompt)
def test_pydantic_ai_package_imports_without_deepagents(deepagents_blocked):
mod = importlib.import_module("mirage.agents.pydantic_ai")
assert isinstance(mod.MIRAGE_SYSTEM_PROMPT, str)
assert mod.PydanticAIWorkspace is not None
def test_openai_agents_package_imports_without_deepagents(deepagents_blocked):
mod = importlib.import_module("mirage.agents.openai_agents")
assert isinstance(mod.MIRAGE_SYSTEM_PROMPT, str)
assert mod.MirageRunner is not None
def test_langchain_full_import_fails_without_deepagents(deepagents_blocked):
with pytest.raises(ModuleNotFoundError):
importlib.import_module("mirage.agents.langchain")
def test_all_prompts_share_same_content():
from mirage.agents.langchain import prompt as lc
from mirage.agents.openai_agents import prompt as oa
from mirage.agents.prompts import MIRAGE_SYSTEM_PROMPT
from mirage.agents.pydantic_ai import prompt as pa
assert pa.MIRAGE_SYSTEM_PROMPT == MIRAGE_SYSTEM_PROMPT
assert oa.MIRAGE_SYSTEM_PROMPT == MIRAGE_SYSTEM_PROMPT
assert lc.MIRAGE_SYSTEM_PROMPT == MIRAGE_SYSTEM_PROMPT