feat(runtime): enforce server request authority

This commit is contained in:
Alexandre Teixeira
2026-10-01 18:35:41 +01:00
parent 05c6cfcfa1
commit 9d0257134f
37 changed files with 1683 additions and 39 deletions
+32 -2
View File
@@ -1316,6 +1316,17 @@ class TaskScheduler:
async def _execute_action(self, task, run_id: str | None = None) -> tuple:
"""Execute a built-in action (no LLM needed)."""
from src.builtin_actions import BUILTIN_ACTIONS
from src.agent_runtime.authority import (
bind_request_authority, restore_task_authority, task_operation,
)
authority = restore_task_authority(
getattr(task, "request_authority_json", None), task.prompt, task.task_type,
task.action, owner=task.owner)
from src.settings import get_setting
authority = authority.restrict(disabled_tools=get_setting("disabled_tools", []) or ())
operation = task_operation(task.task_type, task.action, task.prompt)
if operation is None or not authority.permits(operation):
return "Scheduled action has no matching server request authority.", False
action_fn = BUILTIN_ACTIONS.get(task.action)
if not action_fn:
@@ -1342,7 +1353,8 @@ class TaskScheduler:
if getattr(task, "model", None):
kwargs["model"] = task.model
kwargs["endpoint_url"] = getattr(task, "endpoint_url", None)
result, success = await action_fn(**kwargs)
with bind_request_authority(authority):
result, success = await action_fn(**kwargs)
if getattr(task, "model", None):
self._last_run_model = task.model
return result, success
@@ -1945,6 +1957,7 @@ class TaskScheduler:
datetime_context_msg: dict | None = None) -> str:
"""Run the full agent loop with tool access, collecting the final text."""
from src.agent_loop import stream_agent_loop
from src.agent_runtime.authority import restore_task_authority
system_content = system_prompt or "You are a helpful assistant executing a scheduled task. Use available tools to complete the task thoroughly."
user_content = override_user_message or task.prompt
@@ -2000,6 +2013,10 @@ class TaskScheduler:
_task_fallbacks = []
# Close the stream in this task on every exit, including the
# approval-pause break, so the agent run's context state unwinds here.
request_authority = restore_task_authority(
getattr(task, "request_authority_json", None), task.prompt,
getattr(task, "task_type", "llm"), getattr(task, "action", None),
owner=task.owner, session_id=session_id)
async with contextlib.aclosing(stream_agent_loop(
endpoint_url=endpoint_url,
model=model,
@@ -2007,11 +2024,13 @@ class TaskScheduler:
max_rounds=_task_max_rounds,
session_id=session_id,
owner=task.owner,
workspace=request_authority.workspace or None,
headers=headers,
disabled_tools=disabled_tools,
relevant_tools=relevant_tools,
fallbacks=_task_fallbacks,
workload="background",
request_authority=request_authority,
)) as agent_stream:
async for event_str in agent_stream:
if event_str.startswith("data: ") and not event_str.startswith("data: [DONE]"):
@@ -2109,6 +2128,14 @@ class TaskScheduler:
async def _execute_research_task(self, task, db) -> str:
"""Execute a deep research task using DeepResearcher."""
from src.agent_runtime.authority import bind_request_authority, restore_task_authority, task_operation
from src.settings import get_setting
authority = restore_task_authority(
getattr(task, "request_authority_json", None), task.prompt, task.task_type,
getattr(task, "action", None), owner=task.owner)
authority = authority.restrict(disabled_tools=get_setting("disabled_tools", []) or ())
if not authority.permits(task_operation(task.task_type, getattr(task, "action", None), task.prompt)):
raise PermissionError("Scheduled research has no matching server request authority.")
from core.database import Session as DbSession, ChatMessage
from src.deep_research import DeepResearcher
from src.research_handler import RESEARCH_DATA_DIR, ResearchHandler
@@ -2180,7 +2207,8 @@ class TaskScheduler:
)
started_ts = time.time()
report = await researcher.research(task.prompt)
with bind_request_authority(authority):
report = await researcher.research(task.prompt)
completed_ts = time.time()
try:
stats = researcher.get_stats() or {}
@@ -2603,6 +2631,7 @@ class TaskScheduler:
if (task.output_target or "session") == "session":
task.output_target = defs.get("output_target", "none")
seeded = []
from src.agent_runtime.authority import seal_task_authority
for action, defs in HOUSEKEEPING_DEFAULTS.items():
if action in existing_actions:
continue
@@ -2620,6 +2649,7 @@ class TaskScheduler:
name=defs["name"],
task_type="action",
action=action,
request_authority_json=seal_task_authority(None, "action", action, owner=owner),
trigger_type=trigger_type,
trigger_event=defs.get("trigger_event"),
trigger_count=defs.get("trigger_count"),