fix(core): skip exec approval for permissionless skill scripts (#13791)

## Summary

- Treat skill scripts with no permission profile, or an explicitly empty
one, as permissionless and run them with the turn's existing sandbox
instead of forcing an exec approval prompt.
- Keep the approval flow unchanged for skills that do declare additional
permissions.
- Update the skill approval tests to assert that permissionless skill
scripts do not prompt on either the initial run or a rerun.

## Why

Permissionless skills should inherit the current turn sandbox directly.
Prompting for exec approval in that case adds friction without granting
any additional capability.
This commit is contained in:
Celia Chen
2026-03-06 16:40:41 -08:00
committed by GitHub
Unverified
parent 0243734300
commit 8b81284975
2 changed files with 33 additions and 41 deletions
@@ -570,9 +570,22 @@ impl EscalationPolicy for CoreShellActionProvider {
// In the usual case, the execve wrapper reports the command being
// executed in `program`, so a direct skill lookup is sufficient.
if let Some(skill) = self.find_skill(program).await {
// For now, we always prompt for scripts that look like they belong
// to skills, which means we ignore exec policy rules for those
// scripts.
// For now, scripts that look like they belong to skills bypass
// general exec policy evaluation. Permissionless skills inherit the
// turn sandbox directly; skills with declared permissions still
// prompt here before applying their permission profile.
let prompt_permissions = skill.permission_profile.clone();
if prompt_permissions
.as_ref()
.is_none_or(PermissionProfile::is_empty)
{
tracing::debug!(
"Matched {program:?} to permissionless skill {skill:?}, inheriting turn sandbox"
);
return Ok(EscalationDecision::escalate(
EscalationExecution::TurnDefault,
));
}
tracing::debug!("Matched {program:?} to skill {skill:?}, prompting for approval");
let needs_escalation = true;
let decision_source = DecisionSource::SkillScript {
@@ -585,7 +598,7 @@ impl EscalationPolicy for CoreShellActionProvider {
program,
argv,
workdir,
skill.permission_profile.clone(),
prompt_permissions,
Self::skill_escalation_execution(&skill),
decision_source,
)