docs(deploy): finalize multi-agent surface image handoff

This commit is contained in:
Mikhail Putilovskij 2026-04-28 20:11:27 +03:00
parent 51241d79e0
commit 5b537880ae
11 changed files with 361 additions and 27 deletions

View file

@ -15,8 +15,10 @@ from nio import (
from nio.api import RoomVisibility
from nio.responses import SyncResponse
from adapter.matrix.agent_registry import AgentDefinition, AgentRegistry
from adapter.matrix.bot import MatrixBot, build_runtime, prepare_live_sync
from adapter.matrix.handlers.auth import handle_invite
from adapter.matrix.routed_platform import RoutedPlatformClient
from adapter.matrix.store import (
add_staged_attachment,
get_platform_chat_id,
@ -36,7 +38,6 @@ from core.protocol import (
)
from sdk.interface import PlatformError
from sdk.mock import MockPlatformClient
from adapter.matrix.routed_platform import RoutedPlatformClient
async def test_matrix_dispatcher_registers_custom_handlers():
@ -107,7 +108,10 @@ async def test_new_chat_creates_real_matrix_room_when_client_available():
assert client.room_create.await_count >= 1
client.room_put_state.assert_awaited_once()
put_call = client.room_put_state.call_args
assert put_call.kwargs.get("room_id") == "!space:example" or put_call.args[0] == "!space:example"
assert (
put_call.kwargs.get("room_id") == "!space:example"
or put_call.args[0] == "!space:example"
)
chats = await runtime.chat_mgr.list_active("u1")
assert [c.chat_id for c in chats] == ["C7"]
assert [c.surface_ref for c in chats] == ["!r2:example"]
@ -333,6 +337,119 @@ async def test_bot_downloads_matrix_file_to_workspace_before_staging(tmp_path, m
bot._send_all.assert_not_awaited()
async def test_bot_downloads_matrix_file_to_configured_agent_workspace(tmp_path, monkeypatch):
monkeypatch.setenv("SURFACES_WORKSPACE_DIR", str(tmp_path / "agents"))
runtime = build_runtime(platform=MockPlatformClient())
runtime.registry = AgentRegistry(
[
AgentDefinition(
agent_id="agent-17",
label="Agent 17",
base_url="http://lambda.coredump.ru:7000/agent_17/",
workspace_path=str(tmp_path / "agents" / "17"),
)
]
)
await set_room_meta(
runtime.store,
"!chat17:example.org",
{
"chat_id": "C17",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "17",
"agent_id": "agent-17",
},
)
client = SimpleNamespace(
user_id="@bot:example.org",
download=AsyncMock(return_value=SimpleNamespace(body=b"%PDF-1.7")),
)
bot = MatrixBot(client, runtime)
runtime.dispatcher.dispatch = AsyncMock(return_value=[])
room = SimpleNamespace(room_id="!chat17:example.org")
event = SimpleNamespace(
sender="@alice:example.org",
body="report.pdf",
msgtype="m.file",
replyto_event_id=None,
url="mxc://server/id",
mimetype="application/pdf",
)
await bot.on_room_message(room, event)
staged = await get_staged_attachments(
runtime.store, "!chat17:example.org", "@alice:example.org"
)
assert staged[0]["workspace_path"].startswith("incoming/")
assert (
tmp_path / "agents" / "17" / staged[0]["workspace_path"]
).read_bytes() == b"%PDF-1.7"
async def test_bot_uploads_agent_output_from_configured_agent_workspace(tmp_path, monkeypatch):
monkeypatch.setenv("SURFACES_WORKSPACE_DIR", str(tmp_path / "agents"))
output_file = tmp_path / "agents" / "17" / "output" / "result.txt"
output_file.parent.mkdir(parents=True)
output_file.write_text("ready", encoding="utf-8")
runtime = build_runtime(platform=MockPlatformClient())
runtime.registry = AgentRegistry(
[
AgentDefinition(
agent_id="agent-17",
label="Agent 17",
base_url="http://lambda.coredump.ru:7000/agent_17/",
workspace_path=str(tmp_path / "agents" / "17"),
)
]
)
await set_room_meta(
runtime.store,
"!chat17:example.org",
{
"chat_id": "C17",
"matrix_user_id": "@alice:example.org",
"platform_chat_id": "17",
"agent_id": "agent-17",
},
)
client = SimpleNamespace(
user_id="@bot:example.org",
upload=AsyncMock(return_value=(SimpleNamespace(content_uri="mxc://server/result"), {})),
room_send=AsyncMock(),
)
bot = MatrixBot(client, runtime)
runtime.dispatcher.dispatch = AsyncMock(
return_value=[
OutgoingMessage(
chat_id="C17",
text="Файл готов",
attachments=[
Attachment(
type="document",
filename="result.txt",
mime_type="text/plain",
workspace_path="output/result.txt",
)
],
)
]
)
room = SimpleNamespace(room_id="!chat17:example.org")
event = SimpleNamespace(
sender="@alice:example.org",
body="сделай отчёт",
msgtype="m.text",
replyto_event_id=None,
)
await bot.on_room_message(room, event)
uploaded_handle = client.upload.await_args.args[0]
assert uploaded_handle.name == str(output_file)
assert client.room_send.await_args_list[1].args[2]["url"] == "mxc://server/result"
async def test_file_only_event_is_staged_and_does_not_dispatch():
runtime = build_runtime(platform=MockPlatformClient())
client = SimpleNamespace(user_id="@bot:example.org", room_send=AsyncMock())

View file

@ -3,7 +3,11 @@ from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
from adapter.matrix.files import build_workspace_attachment_path, download_matrix_attachment
from adapter.matrix.files import (
build_agent_incoming_path,
build_workspace_attachment_path,
download_matrix_attachment,
)
from core.protocol import Attachment
@ -65,3 +69,37 @@ def test_build_workspace_attachment_path_keeps_room_safe_agents_relative_contrac
)
assert not Path(rel_path).is_absolute()
assert abs_path == tmp_path / "agents" / "7" / rel_path
def test_build_agent_incoming_path_uses_agent_workspace_volume(tmp_path: Path):
rel_path, abs_path = build_agent_incoming_path(
workspace_root=tmp_path / "agents" / "17",
filename="quarterly status.pdf",
timestamp="20260428-110000",
)
assert rel_path == "incoming/20260428-110000-quarterly_status.pdf"
assert abs_path == tmp_path / "agents" / "17" / rel_path
async def test_download_matrix_attachment_uses_agent_workspace_incoming_dir(tmp_path: Path):
async def download(url: str):
assert url == "mxc://server/id"
return SimpleNamespace(body=b"%PDF-1.7")
saved = await download_matrix_attachment(
client=SimpleNamespace(download=download),
workspace_root=tmp_path / "agents" / "17",
matrix_user_id="@alice:example.org",
room_id="!room:example.org",
attachment=Attachment(
type="document",
url="mxc://server/id",
filename="report.pdf",
mime_type="application/pdf",
),
timestamp="20260428-110000",
)
assert saved.workspace_path == "incoming/20260428-110000-report.pdf"
assert (tmp_path / "agents" / "17" / saved.workspace_path).read_bytes() == b"%PDF-1.7"