feat(runtime): bind process and job resources to authority

This commit is contained in:
Alexandre Teixeira
2026-10-02 18:54:09 +01:00
parent 7b8ac6f631
commit db41d7e822
36 changed files with 2251 additions and 174 deletions
+19 -3
View File
@@ -67,8 +67,20 @@ class ManageBgJobsTool:
if not session_id:
return {"error": "manage_bg_jobs: no active chat session; background jobs are scoped to a chat.", "exit_code": 1}
from src.agent_runtime.process_resources import active_process_operation, expected_job, require_process_admission
from src.agent_runtime.resources import ResourceIdentityError
bound = active_process_operation()
if bound is None or (bound.owner, bound.thread_id) != (str(ctx.get("owner") or "").strip().casefold(), session_id):
return {"error": "manage_bg_jobs: no exact server resource binding", "exit_code": 1,
"blocked": True, "failure_kind": "resource_identity_denied"}
from src.agent_runtime.authority import ExactOperation
if bound.operation != ExactOperation.normalize("manage_bg_jobs", raw or "{}"):
return {"error": "Job operation changed at producer entry", "exit_code": 1, "blocked": True}
require_process_admission(bound)
if action in _LIST_ACTIONS:
jobs: List[Dict[str, Any]] = bg_jobs.list_for_session(session_id)
bound.validate()
jobs: List[Dict[str, Any]] = [bg_jobs.peek(j.job_id) for j in bound.jobs]
if not jobs:
return {"output": "No background jobs in this chat.", "exit_code": 0}
jobs.sort(key=lambda r: r.get("started_at") or 0, reverse=True)
@@ -78,7 +90,11 @@ class ManageBgJobsTool:
if action in _OUTPUT_ACTIONS or action in _KILL_ACTIONS:
if not job_id:
return {"error": f"manage_bg_jobs: action '{action}' requires a job_id (see action='list').", "exit_code": 1}
rec = bg_jobs.get(job_id)
try:
resource = expected_job(job_id, action=action)
rec = bg_jobs.get(job_id, expected=resource)
except (ResourceIdentityError, OSError, ValueError) as error:
return {"error": str(error), "exit_code": 1, "blocked": True, "failure_kind": "resource_identity_denied"}
# Scope: only the chat that launched a job may see or control it.
if rec is None or rec.get("session_id") != session_id:
return {"error": f"manage_bg_jobs: no background job '{job_id}' in this chat.", "exit_code": 1}
@@ -86,7 +102,7 @@ class ManageBgJobsTool:
if action in _KILL_ACTIONS:
if rec.get("status") != "running":
return {"output": f"Job `{job_id}` already {_status_label(rec)}; nothing to kill.", "exit_code": 0}
killed = bg_jobs.kill(job_id)
killed = bg_jobs.kill(job_id, expected=resource)
if not killed or not killed.get("killed"):
return {"error": f"Could not verify termination of background job `{job_id}`.",
"exit_code": 1, "teardown": (killed or {}).get("teardown")}
+38 -5
View File
@@ -511,26 +511,47 @@ async def _run_owned_command(command, ctx: dict, *, tool: str, timeout: int, arg
from src.tool_execution import agent_cwd, _truncate
grant = None
result = None
try:
from src.agent_runtime.process_resources import require_launch, publish_launch, validate_launch_spec
from src.agent_runtime.authority import active_request_authority
launch = require_launch(tool, cwd=agent_cwd())
authority = active_request_authority()
if (str(ctx.get("owner") or "").strip().casefold(), str(ctx.get("session_id") or "")) != (
authority.owner, authority.session_id):
raise ValueError("Native producer owner or session changed")
spec = _owned_spec(agent_cwd(), ctx.get("subproc_env"), timeout, readonly_extra)
validate_launch_spec(launch, spec)
grant = containment.acquire(
_owned_spec(agent_cwd(), ctx.get("subproc_env"), timeout, readonly_extra),
spec,
owner=str(ctx.get("session_id") or ctx.get("owner") or tool),
)
containment._update_record(grant.id, launch_generation=launch.generation)
publish_launch(launch, authority, grant.id)
if containment.FILESYSTEM not in grant.enforced:
if argv:
command = [*command[:-1], _replace_workspace_alias(command[-1], grant.workspace)]
else:
command = _replace_workspace_alias(command, grant.workspace)
result = await containment.run(grant, command, argv=argv, progress_cb=ctx.get("progress_cb"))
from src.agent_runtime.process_resources import attach_containment_processes
attach_containment_processes(launch, grant.id)
except containment.ContainmentUnavailable as exc:
return containment.unavailable_tool_result(exc, tool=tool)
except (OSError, RuntimeError, ValueError) as exc:
boundary = grant.to_dict() if grant else {}
boundary["executed"] = bool(getattr(exc, "containment_executed", False))
if not getattr(exc, "containment_established", False):
if grant is not None:
record = containment._load_records().get(grant.id, {})
if not record.get("pid") and not record.get("release"):
containment.release(grant, grace_s=0)
boundary = result.grant.to_dict() if result is not None else grant.to_dict() if grant else {}
boundary["executed"] = result is not None or bool(getattr(exc, "containment_executed", False))
if result is None and not getattr(exc, "containment_established", False):
boundary.update(contained=False, enforced=[])
return {"error": f"{tool}: execution failed: {exc}", "exit_code": 1,
"containment": boundary}
"containment": boundary,
**({"failure_kind": "resource_linkage_unavailable",
"teardown": result.release.to_dict() if result.release else {"dead": False}}
if result is not None else {})}
boundary = result.grant.to_dict()
boundary["executed"] = True
@@ -590,6 +611,12 @@ class BashTool:
),
"exit_code": 1,
}
from src.agent_runtime.process_resources import require_launch
from src.agent_runtime.resources import ResourceIdentityError
try:
require_launch("bash", cwd=agent_cwd(), content=content)
except ResourceIdentityError as error:
return {"error": str(error), "exit_code": 1, "blocked": True, "failure_kind": "resource_identity_denied"}
if _ffmpeg_unicode_drawtext_needs_fontfile(content):
resolved_font = _resolve_fontfile_for_text(content)
resolved_hint = (
@@ -879,6 +906,12 @@ class PythonTool:
),
"exit_code": 1,
}
from src.agent_runtime.process_resources import require_launch
from src.agent_runtime.resources import ResourceIdentityError
try:
require_launch("python", cwd=agent_cwd(), content=content)
except ResourceIdentityError as error:
return {"error": str(error), "exit_code": 1, "blocked": True, "failure_kind": "resource_identity_denied"}
if "/tmp/" in content:
isolated_tmp = _isolated_tmp_dir(agent_cwd())
content = content.replace("/tmp/", isolated_tmp.rstrip("/") + "/")