or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

apis

agents.mdhooks.mdmcp.mdmessages.mdoptions.mdpermissions.mdquery-api.mdsandbox.mdtools.md
index.mdpatterns.mdquick-reference.mdtypes.md
tile.json

permissions.mddocs/apis/

Permission System

Fine-grained control over tool execution.

Permission Modes

type PermissionMode =
  | 'default'           // Prompt for dangerous operations
  | 'acceptEdits'       // Auto-accept file edits
  | 'bypassPermissions' // Bypass all checks
  | 'plan'              // Planning mode, no execution
  | 'dontAsk';          // Deny if not pre-approved

Usage:

{permissionMode: 'acceptEdits'}

Custom Permission Handler

type CanUseTool = (
  toolName: string,
  input: Record<string, unknown>,
  options: {
    signal: AbortSignal;
    suggestions?: PermissionUpdate[];
    blockedPath?: string;
    decisionReason?: string;
    toolUseID: string;
    agentID?: string;
  }
) => Promise<PermissionResult>;

type PermissionResult =
  | {behavior: 'allow'; updatedInput: Record<string, unknown>; updatedPermissions?: PermissionUpdate[]; toolUseID?: string}
  | {behavior: 'deny'; message: string; interrupt?: boolean; toolUseID?: string};

Example:

canUseTool: async (toolName, input, {decisionReason}) => {
  // Block dangerous commands
  if (toolName === 'Bash' && (input.command as string).includes('rm -rf')) {
    return {
      behavior: 'deny',
      message: 'Dangerous command not allowed',
      interrupt: true
    };
  }

  // Block sensitive files
  if ((toolName === 'Write' || toolName === 'Edit') &&
      (input.file_path as string).includes('.env')) {
    return {
      behavior: 'deny',
      message: 'Cannot modify .env files'
    };
  }

  // Allow with optional updates
  return {
    behavior: 'allow',
    updatedInput: input,
    updatedPermissions: suggestions // Apply suggestions
  };
}

Permission Updates

type PermissionUpdate =
  | {type: 'addRules'; rules: PermissionRuleValue[]; behavior: PermissionBehavior; destination: PermissionUpdateDestination}
  | {type: 'replaceRules'; rules: PermissionRuleValue[]; behavior: PermissionBehavior; destination: PermissionUpdateDestination}
  | {type: 'removeRules'; rules: PermissionRuleValue[]; behavior: PermissionBehavior; destination: PermissionUpdateDestination}
  | {type: 'setMode'; mode: PermissionMode; destination: PermissionUpdateDestination}
  | {type: 'addDirectories'; directories: string[]; destination: PermissionUpdateDestination}
  | {type: 'removeDirectories'; directories: string[]; destination: PermissionUpdateDestination};

interface PermissionRuleValue {
  toolName: string;
  ruleContent?: string;
}

type PermissionBehavior = 'allow' | 'deny' | 'ask';
type PermissionUpdateDestination = 'userSettings' | 'projectSettings' | 'localSettings' | 'session' | 'cliArg';

MCP Permission Tool

Route prompts through custom MCP tool:

{
  permissionPromptToolName: 'my-permission-handler',
  mcpServers: {
    'my-server': {
      command: 'node',
      args: ['./server.js']
    }
  }
}

Complete Example

const allowedCommands = new Set(['ls', 'pwd', 'cat', 'grep']);

const result = query({
  prompt: 'Task',
  options: {
    permissionMode: 'default',
    canUseTool: async (toolName, input, {blockedPath}) => {
      console.log(`Permission: ${toolName}`);
      if (blockedPath) console.log(`  Path: ${blockedPath}`);

      // Bash whitelist
      if (toolName === 'Bash') {
        const cmd = (input.command as string).trim().split(' ')[0];
        if (!allowedCommands.has(cmd)) {
          return {
            behavior: 'deny',
            message: `Command "${cmd}" not allowed. Use: ${Array.from(allowedCommands).join(', ')}`
          };
        }
      }

      // File protection
      if (toolName === 'Write' || toolName === 'Edit') {
        const path = input.file_path as string;
        if (path.includes('.env') || path.includes('secrets')) {
          return {behavior: 'deny', message: 'Cannot modify sensitive files'};
        }
        if (path.startsWith('/etc/') || path.startsWith('/sys/')) {
          return {behavior: 'deny', message: 'Cannot modify system files', interrupt: true};
        }
      }

      return {behavior: 'allow', updatedInput: input};
    }
  }
});

for await (const msg of result) {
  if (msg.type === 'result' && msg.permission_denials.length > 0) {
    console.log('Denied operations:');
    msg.permission_denials.forEach(d => {
      console.log(`  ${d.tool_name}:`, d.tool_input);
    });
  }
}

Types

interface SDKPermissionDenial {
  tool_name: string;
  tool_use_id: string;
  tool_input: Record<string, unknown>;
}