---
url: >-
  https://adk.nht.io/api/@nhtio/adk/batteries/sandbox/type-aliases/RunShellCommandPolicyFn.md
description: A policy resolved PER CALL rather than captured at construction.
---

# Type Alias: RunShellCommandPolicyFn

```ts
type RunShellCommandPolicyFn = (
  context: RunShellCommandPolicyContext,
) => SandboxPolicy | Promise<SandboxPolicy>;
```

Defined in: [src/batteries/sandbox/tool.ts:100](https://github.com/NHTIO/ADK/blob/v1.20261003.0/src/src/batteries/sandbox/tool.ts#L100)

A policy resolved PER CALL rather than captured at construction.

## Parameters

| Parameter | Type                                                                            |
| --------- | ------------------------------------------------------------------------------- |
| `context` | [`RunShellCommandPolicyContext`](../interfaces/RunShellCommandPolicyContext.md) |

## Returns

| [`SandboxPolicy`](../interfaces/SandboxPolicy.md)
| `Promise`<[`SandboxPolicy`](../interfaces/SandboxPolicy.md)>

## Remarks

Evaluated once per invocation, AFTER the gate has approved and AFTER `cwd` has passed the path
gauntlet, and its return value is passed to `enforcer.run` for that one child only — a policy
object captured at construction would keep a revoked grant alive forever. A per-call policy must
hand the ENFORCER a fresh object per call (`{ ...basePolicy }` at minimum): the enforcer stores
what it is handed, so a shared mutable object collapses concurrent runs back into one shared
policy.

There is NO gate decision in the context. `ToolGateFn` approves with `void` and denies by
THROWING, and a thrown denial returns before this callback runs — so at the moment a policy is
resolved there is no approval artefact to hand over, and a denied call never reaches here.
