feat(domain): DTO ToolPolicyGateway — bản đề xuất, gỡ chốt cho N3
N3 (Co4E) cần gọi tool nhưng Team Hoa chưa bắt đầu. Ba đường: N3 ngồi đợi
(trái nguyên tắc không team nào chặn team nào), N3 tự phỏng đoán (không ai
soi, chắc chắn phải sửa), hoặc viết một bản đề xuất để Hoa duyệt. Chọn cái
thứ ba.
Ranh giới giữ đúng sơ đồ phân hệ trong plan.md: domain/security/ là của
Gamma, application/conversations/tool_policy_gateway.py là của Hoa. Nên Gamma
định nghĩa hình dạng, Hoa cài đặt. Không đụng file nào của họ.
Hình dạng bám vào code đang chạy: SecurityVerdict (allowed/reason/layer) và
hộp thoại xin phép ở chat_panel.py:1312. Khác biệt duy nhất là gộp thành một
câu trả lời ba trạng thái ALLOW/DENY/ASK, thay vì bắt chỗ gọi tự nhớ hỏi hai
nơi.
Hai ràng buộc đưa vào có chủ đích, mỗi cái một test:
- DENY và ASK bắt buộc có reason, ném lỗi ngay lúc dựng. Người dùng cần
biết vì sao bị chặn và audit_log cần ghi lại.
- ASK không phải allowed. Đây là bẫy dễ mắc nhất: coi ASK như ALLOW thì
tool chạy trước khi có ai đồng ý.
Kèm FakeToolPolicyGateway lập trình được theo tên tool hoặc theo hàm, có ghi
lại đã hỏi những gì — test khẳng định được "có hỏi cổng không", không chỉ
"kết quả đúng không".
docs/refactor/GammaTeam_decisions.md thêm quyết định 3, kèm nguyên văn tin
nhắn cần gửi Hoa và ô đánh dấu đã gửi / đã xác nhận.
102 test xanh (96 + 6 mới). CASAN Check 1 sạch. domain/ và application/ có 0
import PySide6 — kiểm bằng AST, vì grep đếm ra 4 mà cả 4 là chữ "PySide6"
nằm trong chính docstring cảnh báo. Check 3 của Team Duy nên phân tích cú
pháp chứ đừng grep.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a164f32bfb
commit
3138856741
@@ -0,0 +1,44 @@
|
||||
"""ToolPolicyGateway giả — để N3 (Co4E) chạy được khi Team Hoa chưa cài đặt.
|
||||
|
||||
Mặc định cho qua hết, vì phần lớn test Co4E quan tâm tới luồng workflow chứ
|
||||
không phải chính sách. Test nào cần kiểm nhánh bị chặn thì lập trình câu trả
|
||||
lời::
|
||||
|
||||
gate = FakeToolPolicyGateway(rules={"run_command": deny("cấm trong Co4E")})
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Callable, Dict
|
||||
|
||||
from cowork_local.domain.security.tool_policy import (
|
||||
PolicyDecision, ToolCallRequest, allow,
|
||||
)
|
||||
|
||||
|
||||
class FakeToolPolicyGateway:
|
||||
"""Cổng chính sách trong bộ nhớ, có ghi lại đã hỏi những gì."""
|
||||
|
||||
def __init__(self, rules: Dict[str, PolicyDecision] | None = None,
|
||||
default: PolicyDecision | None = None,
|
||||
decide: Callable[[ToolCallRequest], PolicyDecision] | None = None):
|
||||
#: {tên tool: quyết định} — tra trước default
|
||||
self.rules = dict(rules or {})
|
||||
self.default = default or allow()
|
||||
#: hàm tự quyết, dùng khi cần logic phức tạp hơn tra bảng
|
||||
self._decide = decide
|
||||
#: mọi lời gọi đã đi qua — để test khẳng định "có hỏi cổng không"
|
||||
self.seen: list[ToolCallRequest] = []
|
||||
|
||||
def check(self, request: ToolCallRequest) -> PolicyDecision:
|
||||
self.seen.append(request)
|
||||
if self._decide is not None:
|
||||
return self._decide(request)
|
||||
return self.rules.get(request.name, self.default)
|
||||
|
||||
# ---- tiện cho test --------------------------------------------------
|
||||
def asked_for(self, name: str) -> bool:
|
||||
return any(r.name == name for r in self.seen)
|
||||
|
||||
@property
|
||||
def call_count(self) -> int:
|
||||
return len(self.seen)
|
||||
Reference in New Issue
Block a user