Bind Guardian async scores to target environment permissions (#47830)

## Why

Asynchronous Guardian scoring omitted the target environment's denied-read restrictions, and cached score authorization did not include that permission context. A score from one environment could therefore be reused for an action in another environment with different restrictions.

## What changed

- Share permission resolution between synchronous review and asynchronous scoring, including resolving `write_stdin` targets from the owning terminal session.
- Include environment identity and denied-read paths/globs in async scoring context and cached score authorization.
- Fall back to synchronous review when permission evidence cannot be resolved or exceeds the async limit of 3,000 bytes, rather than omitting restrictions.

## Testing

Add regression coverage for rejecting cached scores with different environments or denied paths, passing the target environment's restrictions to the classifier, and rejecting oversized async permission evidence.

GitOrigin-RevId: 14b6f3454bbea458be037560eb1da47d22874df1
This commit is contained in:
jif
2026-09-24 12:34:04 +00:00
committed by copyberry
parent b15718acfc
commit 61e23bc6a1
35 changed files with 426 additions and 120 deletions
@@ -1816,6 +1816,16 @@ impl UnifiedExecProcessManager {
}
}
/// Resolves stdin's target from the host-owned terminal, not model-supplied metadata.
pub(crate) async fn environment_id_for_process(&self, process_id: i32) -> Option<String> {
self.process_store
.lock()
.await
.processes
.get(&process_id)
.map(|entry| entry.environment_id.clone())
}
pub(crate) async fn list_processes(&self) -> Vec<BackgroundTerminalInfo> {
let store = self.process_store.lock().await;
let mut entries = store