mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-04 16:38:49 +00:00
120 lines
3.3 KiB
Python
120 lines
3.3 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from nanobot.agent.tools.registry import ToolRegistry
|
|
from nanobot.command.router import CommandRouter
|
|
from nanobot.config.schema import Config
|
|
from nanobot.extensions import (
|
|
ExtensionCandidate,
|
|
ExtensionManifest,
|
|
ExtensionRuntime,
|
|
ExtensionRuntimeManager,
|
|
ExtensionScope,
|
|
ExtensionSnapshot,
|
|
)
|
|
|
|
|
|
def _snapshot(candidate: ExtensionCandidate) -> ExtensionSnapshot:
|
|
return ExtensionSnapshot((candidate,), (), ())
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_runtime_activation_and_close_are_transactional(tmp_path: Path) -> None:
|
|
(tmp_path / "index.mjs").write_text(
|
|
"""
|
|
export default function (pi) {
|
|
pi.registerTool({
|
|
name: "remote_echo",
|
|
description: "Echo",
|
|
parameters: { type: "object", properties: {} },
|
|
execute: async () => ({ content: [{ type: "text", text: "ok" }] })
|
|
});
|
|
pi.registerCommand("remote", {
|
|
description: "Remote command",
|
|
handler: async () => undefined
|
|
});
|
|
pi.on("agent_start", () => undefined);
|
|
}
|
|
"""
|
|
)
|
|
candidate = ExtensionCandidate(
|
|
ExtensionManifest(
|
|
id="test.remote",
|
|
name="Remote",
|
|
version="1.0.0",
|
|
runtime=ExtensionRuntime.PI,
|
|
entry="index.mjs",
|
|
),
|
|
ExtensionScope.USER,
|
|
location=tmp_path,
|
|
trusted=True,
|
|
)
|
|
tools = ToolRegistry()
|
|
commands = CommandRouter()
|
|
manager = ExtensionRuntimeManager(
|
|
tools=tools,
|
|
commands=commands,
|
|
config=Config(),
|
|
)
|
|
|
|
result = await manager.activate(_snapshot(candidate))
|
|
|
|
assert not result.diagnostics
|
|
assert tools.owner("remote_echo") == "test.remote"
|
|
assert commands.owner("exact", "/remote") == "test.remote"
|
|
assert len(result.hook_factories) == 1
|
|
|
|
await manager.close()
|
|
|
|
assert tools.owner("remote_echo") is None
|
|
assert commands.owner("exact", "/remote") is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_runtime_rolls_back_partial_registration(tmp_path: Path) -> None:
|
|
(tmp_path / "index.mjs").write_text(
|
|
"""
|
|
export default function (pi) {
|
|
pi.registerTool({
|
|
name: "duplicate",
|
|
description: "Duplicate",
|
|
parameters: {},
|
|
execute: async () => ({ content: [] })
|
|
});
|
|
pi.registerCommand("duplicate", {
|
|
handler: async () => undefined
|
|
});
|
|
}
|
|
"""
|
|
)
|
|
candidate = ExtensionCandidate(
|
|
ExtensionManifest(
|
|
id="test.duplicate",
|
|
name="Duplicate",
|
|
version="1.0.0",
|
|
runtime=ExtensionRuntime.PI,
|
|
entry="index.mjs",
|
|
),
|
|
ExtensionScope.USER,
|
|
location=tmp_path,
|
|
trusted=True,
|
|
)
|
|
commands = CommandRouter()
|
|
|
|
async def core_handler(_ctx):
|
|
return None
|
|
|
|
commands.exact("/duplicate", core_handler)
|
|
manager = ExtensionRuntimeManager(
|
|
tools=ToolRegistry(),
|
|
commands=commands,
|
|
config=Config(),
|
|
)
|
|
|
|
result = await manager.activate(_snapshot(candidate))
|
|
|
|
assert result.extensions == ()
|
|
assert result.diagnostics[0].code == "activation_failed"
|
|
assert commands.owner("exact", "/duplicate") == "nanobot.core"
|