Configure permissions
Permissions decide whether the agent can run a tool now, must ask first, or is blocked entirely. The SDK exposes a mode at session start, granular allow and deny lists, runtime overrides, and a per-request response API.
Permission modes
Set the mode when the SDK starts. The mode applies to every tool call until you change it.
- interactive (default): emit `permission_request` events and wait for your code to allow or deny.
- unrestricted: allow every tool call without prompting. Use only inside a sandbox.
- restricted: deny risky tools automatically. Reads still pass through.
- external: hand the decision to a configured callback or hook.
Legacy aliases like `default` and `bypassPermissions` still work, but new code should use the names above.
Set the mode at session start
Pass the mode into the SDK constructor. The default is `interactive`, which is the safest choice for any agent that runs untrusted prompts.
TypeScript
import { AutohandSDK } from '@autohandai/agent-sdk';
const sdk = new AutohandSDK({
cwd: '.',
permissionMode: 'interactive',
});
await sdk.start();
Python
from autohand_sdk import AutohandSDK
async with AutohandSDK(
cwd=".",
permission_mode="interactive",
) as sdk:
...
Go
package main
import (
"context"
autohand "github.com/autohandai/agent-sdk-go"
)
ctx := context.Background()
sdk := autohand.NewSDK(&autohand.Config{
CWD: ".",
PermissionMode: autohand.PermissionInteractive,
})
_ = sdk.Start(ctx)
defer sdk.Close()
Swift
import AgentSDK
let manager = PermissionManager(
hookManager: HookManager(),
mode: .interactive
)
Runner.setPermissionManager(manager)
Java
import ai.autohand.sdk.sdk.AutohandSDK;
import ai.autohand.sdk.types.SDKConfig;
import ai.autohand.sdk.types.PermissionMode;
AutohandSDK sdk = new AutohandSDK(new SDKConfig(
".", null, false, 300_000, PermissionMode.INTERACTIVE
));
sdk.start();Respond to permission requests
In `interactive` mode the SDK emits a `permission_request` event whenever the agent reaches a guarded tool. Your code decides what to do, then calls back with `allowed` and an optional `remember` scope.
TypeScript
for await (const event of sdk.streamPrompt({
message: 'Run the test suite and report failures.'
})) {
if (event.type === 'permission_request') {
const allowReads = event.tool === 'read_file' || event.tool === 'git_status';
await sdk.permissionResponse({
requestId: event.requestId,
allowed: allowReads,
remember: allowReads,
});
continue;
}
if (event.type === 'message_update') {
process.stdout.write(event.delta);
}
}
Python
async for event in sdk.stream_prompt("Run the test suite and report failures."):
if event["type"] == "permission_request":
tool = event.get("tool", "")
allow_reads = tool in ("read_file", "git_status")
await sdk.respond_to_permission(
event["request_id"],
decision="allow" if allow_reads else "deny",
allowed=allow_reads,
remember=allow_reads,
)
continue
if event["type"] == "message_update":
print(event.get("delta", ""), end="")
Go
events, _ := sdk.StreamPrompt(ctx, &autohand.PromptParams{
Message: "Run the test suite and report failures.",
})
for event := range events {
switch e := event.(type) {
case autohand.PermissionRequestEvent:
allow := e.Tool == "read_file" || e.Tool == "git_status"
scope := autohand.ScopeOnce
if allow {
scope = autohand.ScopeSession
}
_ = sdk.PermissionResponse(ctx, e.RequestID, allow, scope)
case autohand.MessageUpdateEvent:
fmt.Print(e.Delta)
}
}
Swift
let stream = Runner.runStream(
agent: agent,
prompt: "Run the test suite and report failures."
)
for try await event in stream {
if event.type == .content, let data = event.data {
print(data, terminator: "")
}
}
Java
sdk.streamPrompt(
new PromptParams("Run the test suite and report failures."),
event -> {
if (event instanceof Events.PermissionRequestEvent pre) {
boolean allowReads = pre.tool().equals("read_file");
if (allowReads) {
sdk.allowPermission(pre.requestId(), DecisionScope.SESSION);
} else {
sdk.denyPermission(pre.requestId(), DecisionScope.ONCE);
}
} else if (event instanceof Events.MessageUpdateEvent mue) {
System.out.print(mue.delta());
}
}
);Use the ergonomic helpers when they read better than `permissionResponse`:
TypeScript
await sdk.allowPermission(event.requestId, 'session');
await sdk.denyPermission(event.requestId, 'once');
await sdk.suggestPermissionAlternative(
event.requestId,
'Run bun run typecheck before bun run build.'
);
Python
await sdk.allow_permission(event["request_id"], scope="session")
await sdk.deny_permission(event["request_id"], scope="once")
await sdk.suggest_permission_alternative(
event["request_id"],
"Run bun run typecheck before bun run build.",
)Allow and deny lists
Use `PermissionSettings` to lock down which tools can run, by name or by pattern. Allow lists pass through, deny lists block.
TypeScript
import { AutohandSDK } from '@autohandai/agent-sdk';
const sdk = new AutohandSDK({
cwd: '.',
permissions: {
mode: 'interactive',
allowList: ['read_file', 'write_file', 'git_status'],
denyList: ['delete_path', 'run_command'],
allowPatterns: ['git *', 'npm install'],
denyPatterns: ['rm -rf', 'sudo'],
},
});
Python
from autohand_sdk import AutohandSDK, PermissionSettings
sdk = AutohandSDK(
cwd=".",
permissions=PermissionSettings(
mode="interactive",
allow_list=["read_file", "write_file", "git_status"],
deny_list=["delete_path", "run_command"],
),
)Yolo patterns for unattended runs
For headless or batch jobs, set a yolo pattern that auto-approves whitelisted tools and expires after a timeout.
TypeScript
const sdk = new AutohandSDK({
cwd: '.',
yolo: 'allow:read,write',
yoloTimeout: 60, // seconds
});
Python
sdk = AutohandSDK(
cwd=".",
yolo="allow:read,write",
yolo_timeout=60,
)Change the mode at runtime
The TypeScript SDK exposes a runtime override that calls `autohand.permissionModeSet` over JSON-RPC. Other SDKs require restarting the session for the new mode to take effect.
TypeScript
await sdk.setPermissionMode('unrestricted');
// later
await sdk.setPermissionMode('interactive');Best practices
- Default to `interactive` mode in any agent that accepts untrusted prompts.
- Show the tool name, description, and working directory in your approval UI so reviewers can decide quickly.
- Auto-allow read-only tools and ask before shell or write operations unless the run is inside a sandbox.
- Pair `unrestricted` mode with a `yoloTimeout` so a forgotten run cannot keep approving forever.
- Log every allow or deny decision when the run is part of CI, audit, or customer support flows.