feat(orchestrator): inbox console, deny by channel and agent rule requests
The inbox shows the console output of a command under its "Ausführen" row, kept to the same tail the job list shows. A deny picks its radio channel (post by default, call, newsletter, broadcast, pan-pan, security) and an optional reason; the passkey signs denial, channel and reason, the hub verifies the proof before it stores or sends anything, and the PermissionRequest hook quotes a reason only when it verifies. Agents ask for an auto-approval rule through the MCP tool rule_request; the request shows in the inbox with a dry run over the last seven days and is accepted only with a passkey assertion the hub verifies against the pinned keys and the hub origin, for every rule kind. Requests lapse with the ask lifetime and are capped per agent and in total. The image runs as nobody by default and carries the health check command; compose keeps the timing. 🎯 Fix: 93 % 💥 Break: 4 % 💸 Cost: 98 % ⏱️ Time: 75 % 🛡️ Security: 100 % 🏆 Score: 100 🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 % Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1d311a3382
commit
58187e8323
37 files changed
+1387
-100
No files matched your search
@@ -7,6 +7,7 @@ from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from .council import NEWS, ordered, read_claims
|
||||
from .denying import DENIABLE
|
||||
from .errors import ToolError
|
||||
from .events import VIEWS
|
||||
from .fields import bounded, decode, expect, message_text
|
||||
@@ -29,7 +30,7 @@ SCREENS = ("stream", "inbox", "rules", "agents", "artifacts")
|
||||
IDS = {"type": "array", "items": {"type": "string"}, "minItems": 1, "description": "Ids from items."}
|
||||
NL_ACTIONS = [
|
||||
{"name": "approve", "description": "Approve inbox items; a decision takes its recommended option.", "parameters": {"ids": IDS}},
|
||||
{"name": "reject", "description": "Reject reviews or deny asks.", "parameters": {"ids": IDS}},
|
||||
{"name": "reject", "description": "Reject reviews, or deny asks, operator rows and rule requests; the requesting agent gets a post.", "parameters": {"ids": IDS}},
|
||||
{"name": "choose", "description": "Pick an option label of a decision.", "parameters": {"id": {"type": "string"}, "label": {"type": "string"}}},
|
||||
{"name": "snooze", "description": "Hide inbox items for some hours.", "parameters": {"ids": IDS, "hours": {"type": "number", "minimum": 1, "maximum": 192}}},
|
||||
{"name": "pin", "description": "Pin inbox items to the top.", "parameters": {"ids": IDS}},
|
||||
@@ -62,7 +63,8 @@ def nl_ids(arguments: dict, items: dict[str, dict]) -> list[dict]:
|
||||
|
||||
|
||||
def nl_item(item: dict, verb: str, label: str) -> dict:
|
||||
return {"kind": item["kind"], "id": item["id"], "verb": verb, "label": label, "sha256": item["sha256"]}
|
||||
entry = {"kind": item["kind"], "id": item["id"], "verb": verb, "label": label, "sha256": item["sha256"]}
|
||||
return {**entry, "channel": "post", "reason": ""} if verb == "reject" and item["kind"] in DENIABLE else entry
|
||||
|
||||
|
||||
def nl_action(raw: object, items: dict[str, dict], agents: list[str]) -> dict:
|
||||
@@ -75,8 +77,10 @@ def nl_action(raw: object, items: dict[str, dict], agents: list[str]) -> dict:
|
||||
expect(arguments, tuple(schema["parameters"]))
|
||||
if name in ("approve", "reject"):
|
||||
picked = nl_ids(arguments, items)
|
||||
if name == "reject" and any(item["kind"] not in ("review", "ask") for item in picked):
|
||||
raise ToolError("reject takes reviews and asks only")
|
||||
if name == "reject" and any(item["kind"] not in ("review", *DENIABLE) or (item["kind"] != "review" and not item["deniable"]) for item in picked):
|
||||
raise ToolError("reject takes reviews, asks, operator rows and rule requests that wait")
|
||||
if name == "approve" and any(item["kind"] == "rule" for item in picked):
|
||||
raise ToolError("a rule request is accepted in its row with the passkey")
|
||||
if name == "approve" and any(item["kind"] == "decision" and not item["recommended"] for item in picked):
|
||||
raise ToolError("a decision without a recommended option needs choose")
|
||||
if name == "approve" and any(item["kind"] == "operator" and not item["runnable"] for item in picked):
|
||||
|
||||
Reference in new issue
Block a user