v2.10.0 (#697)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Corentin André <corentin.andre@mistral.ai> Co-authored-by: Guillaume LE GOFF <guillaume.lgf@gmail.com> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Maxime Dolores <maxime.dolores@ext.mistral.ai> Co-authored-by: Nelson PROIA <144663685+Nelson-PROIA@users.noreply.github.com> Co-authored-by: Peter Evers <pevers90@gmail.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Quentin <quentin.torroba@mistral.ai> Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com> Co-authored-by: MichisGitIsKing <MichisGitIsKing@users.noreply.github.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
626f905186
commit
228f3c65a9
158 changed files with 7235 additions and 916 deletions
166
tests/test_permission_store.py
Normal file
166
tests/test_permission_store.py
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_agent_loop, build_test_vibe_config
|
||||
from tests.mock.utils import mock_llm_chunk
|
||||
from tests.stubs.fake_backend import FakeBackend
|
||||
from vibe.core.tools.base import ToolPermission
|
||||
from vibe.core.tools.permissions import (
|
||||
ApprovedRule,
|
||||
PermissionScope,
|
||||
PermissionStore,
|
||||
RequiredPermission,
|
||||
)
|
||||
from vibe.core.types import ApprovalResponse, FunctionCall, ToolCall, ToolResultEvent
|
||||
|
||||
|
||||
class TestPermissionStore:
|
||||
def test_covers_returns_false_when_empty(self):
|
||||
store = PermissionStore()
|
||||
rp = RequiredPermission(
|
||||
scope=PermissionScope.COMMAND_PATTERN,
|
||||
invocation_pattern="npm install foo",
|
||||
session_pattern="npm install *",
|
||||
label="npm install *",
|
||||
)
|
||||
|
||||
assert not store.covers("bash", rp)
|
||||
|
||||
def test_covers_returns_true_after_matching_rule_added(self):
|
||||
store = PermissionStore()
|
||||
store.add_rule(
|
||||
ApprovedRule(
|
||||
tool_name="bash",
|
||||
scope=PermissionScope.COMMAND_PATTERN,
|
||||
session_pattern="npm install *",
|
||||
)
|
||||
)
|
||||
rp = RequiredPermission(
|
||||
scope=PermissionScope.COMMAND_PATTERN,
|
||||
invocation_pattern="npm install foo",
|
||||
session_pattern="npm install *",
|
||||
label="npm install *",
|
||||
)
|
||||
|
||||
assert store.covers("bash", rp)
|
||||
|
||||
def test_covers_isolates_by_tool_name(self):
|
||||
store = PermissionStore()
|
||||
store.add_rule(
|
||||
ApprovedRule(
|
||||
tool_name="bash",
|
||||
scope=PermissionScope.COMMAND_PATTERN,
|
||||
session_pattern="npm install *",
|
||||
)
|
||||
)
|
||||
rp = RequiredPermission(
|
||||
scope=PermissionScope.COMMAND_PATTERN,
|
||||
invocation_pattern="npm install foo",
|
||||
session_pattern="npm install *",
|
||||
label="npm install *",
|
||||
)
|
||||
|
||||
assert not store.covers("read_file", rp)
|
||||
|
||||
def test_tool_permission_round_trip(self):
|
||||
store = PermissionStore()
|
||||
assert store.get_tool_permission("bash") is None
|
||||
|
||||
store.set_tool_permission("bash", ToolPermission.ALWAYS)
|
||||
|
||||
assert store.get_tool_permission("bash") == ToolPermission.ALWAYS
|
||||
|
||||
|
||||
class TestAgentLoopSharesStore:
|
||||
def test_subagent_inherits_parent_session_rules(self):
|
||||
store = PermissionStore()
|
||||
parent = build_test_agent_loop(permission_store=store)
|
||||
subagent = build_test_agent_loop(permission_store=store)
|
||||
|
||||
parent.approve_always(
|
||||
"bash",
|
||||
[
|
||||
RequiredPermission(
|
||||
scope=PermissionScope.COMMAND_PATTERN,
|
||||
invocation_pattern="npm install foo",
|
||||
session_pattern="npm install *",
|
||||
label="npm install *",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
rp = RequiredPermission(
|
||||
scope=PermissionScope.COMMAND_PATTERN,
|
||||
invocation_pattern="npm install bar",
|
||||
session_pattern="npm install *",
|
||||
label="npm install *",
|
||||
)
|
||||
assert subagent._permission_store.covers("bash", rp)
|
||||
|
||||
def test_subagent_inherits_parent_tool_permission(self):
|
||||
store = PermissionStore()
|
||||
parent = build_test_agent_loop(permission_store=store)
|
||||
subagent = build_test_agent_loop(permission_store=store)
|
||||
|
||||
parent.approve_always("bash", None)
|
||||
|
||||
assert (
|
||||
subagent._permission_store.get_tool_permission("bash")
|
||||
== ToolPermission.ALWAYS
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_subagent_applies_parent_tool_permission_before_resolution(self):
|
||||
store = PermissionStore()
|
||||
parent = build_test_agent_loop(permission_store=store)
|
||||
parent.approve_always("bash", None)
|
||||
approval_requested = False
|
||||
|
||||
async def approval_callback(
|
||||
tool_name: str,
|
||||
args: BaseModel,
|
||||
tool_call_id: str,
|
||||
required_permissions: list[RequiredPermission] | None,
|
||||
) -> tuple[ApprovalResponse, str | None]:
|
||||
nonlocal approval_requested
|
||||
approval_requested = True
|
||||
return ApprovalResponse.NO, None
|
||||
|
||||
tool_call = ToolCall(
|
||||
id="call_1",
|
||||
index=0,
|
||||
function=FunctionCall(name="bash", arguments='{"command":"true"}'),
|
||||
)
|
||||
subagent = build_test_agent_loop(
|
||||
config=build_test_vibe_config(enabled_tools=["bash"]),
|
||||
backend=FakeBackend([
|
||||
[mock_llm_chunk(content="Running it.", tool_calls=[tool_call])],
|
||||
[mock_llm_chunk(content="Done.")],
|
||||
]),
|
||||
permission_store=store,
|
||||
)
|
||||
subagent.set_approval_callback(approval_callback)
|
||||
|
||||
events = [event async for event in subagent.act("run true")]
|
||||
tool_results = [event for event in events if isinstance(event, ToolResultEvent)]
|
||||
|
||||
assert not approval_requested
|
||||
assert len(tool_results) == 1
|
||||
assert tool_results[0].skipped is False
|
||||
assert "permission" not in subagent.config.tools.get("bash", {})
|
||||
|
||||
def test_default_store_is_per_loop_when_not_shared(self):
|
||||
a = build_test_agent_loop()
|
||||
b = build_test_agent_loop()
|
||||
|
||||
assert a._permission_store is not b._permission_store
|
||||
assert a._permission_store.lock is not b._permission_store.lock
|
||||
|
||||
def test_subagent_shares_parent_approval_lock(self):
|
||||
store = PermissionStore()
|
||||
parent = build_test_agent_loop(permission_store=store)
|
||||
subagent = build_test_agent_loop(permission_store=store)
|
||||
|
||||
assert parent._permission_store.lock is subagent._permission_store.lock
|
||||
Loading…
Add table
Add a link
Reference in a new issue