How Continue Enforces Rules and Policies for Code Modification

Continue treats every code-modification request as a policy-governed operation using a three-layer architecture—declarative YAML configuration, runtime API retrieval, and client-side evaluation—that applies the most restrictive rule available to determine whether tools are blocked, require user approval, or execute automatically.

Continue, an open-source AI code assistant in the continuedev/continue repository, implements a policy engine that governs file edits, refactoring operations, and terminal commands. Rather than executing AI-generated changes blindly, the system evaluates each request against organization-wide and local rules stored in continue.yaml, ensuring developers maintain explicit control over automated modifications.

The Three-Layer Policy Architecture

Continue's policy system operates across distinct layers that transform static configuration into runtime enforcement decisions.

Policy Definition via YAML Schema

The foundation lives in packages/config-yaml/src/schemas/policy.ts, which defines a Zod schema for the Policy type. Developers declare rules in .continue/policy.yaml or embedded within continue.yml, specifying whether individual tools are disabled, allowedWithPermission, or allowedWithoutPermission.


# .continue/policy.yaml

tools:
  # Block all write operations

  write: disabled
  # Allow read-only tools automatically

  read: allowedWithoutPermission
  # Require approval for test runners

  testRunner:
    permission: allowedWithPermission

Runtime Policy Retrieval

When the IDE extension or CLI initializes, it fetches the effective organizational policy from the Continue backend. The TypeScript SDK wrapper in packages/continue-sdk/typescript/api/src/apis/DefaultApi.ts exposes this via the GET /ide/policy endpoint:

import { DefaultApi } from "continue-sdk/typescript/api";

async function loadEffectivePolicy() {
  const api = new DefaultApi();
  const response = await api.getIdePolicy();
  return response.data?.policy; // Merges with local config
}

Client-Side Policy Evaluation

The core evaluator resides in gui/src/redux/thunks/evaluateToolPolicies.ts. This module merges the base policy from local YAML with any dynamic server-provided overrides, applying a most-restrictive-wins algorithm. For terminal commands specifically, packages/terminal-security/src/evaluateTerminalCommandSecurity.ts performs additional token-level analysis to calculate the final policy state.

Evaluating Tool Calls Against Policies

Before executing any tool, Continue calls evaluateToolPolicies(toolName, args) to determine the permission state. The function returns one of three enum values defined in gui/src/util/toolCallState.ts:

  • disabled: The tool is blocked and Continue displays an error explaining the security risk.
  • allowedWithPermission: The UI renders an approval button; execution pauses until the user clicks it.
  • allowedWithoutPermission: The tool runs immediately without interruption.
import { evaluateToolPolicies } from "./evaluateToolPolicies";

async function runTool(toolName: string, args: any) {
  const { policy } = await evaluateToolPolicies(toolName, args);
  
  if (policy === "disabled") {
    showError(`Tool "${toolName}" is disabled by policy`);
    return;
  }
  
  if (policy === "allowedWithPermission") {
    const approved = await askUserApproval(toolName);
    if (!approved) return;
  }
  
  // Execute if allowedWithoutPermission or after approval
  await executeTool(toolName, args);
}

The UI layer in gui/src/redux/thunks/streamNormalInput.ts consumes this result to decide whether to auto-execute a stream or present a permission dialog.

Special Handling for Terminal Commands

Raw terminal commands receive additional scrutiny because they can affect the host system. The evaluateTerminalCommandSecurity module in packages/terminal-security/src/evaluateTerminalCommandSecurity.ts walks parsed command tokens, calculates the most restrictive policy for each segment, and returns the overall security classification.

Commands classified as allowedWithoutPermission proceed to the backend immediately. All others trigger the permission flow or block execution entirely, preventing potentially destructive shell operations from running without explicit consent.

Updating Policies at Runtime

Continue supports dynamic policy changes without requiring an IDE restart. The CLI offers continue policy add … commands that write new rules to the YAML configuration. These changes trigger ToolPermissionService.reloadPermissions() in extensions/cli/src/services/ToolPermissionService.ts, which rebuilds the in-memory permission list so subsequent tool calls respect the updated rules instantly.

Security Guarantees

The policy engine provides three critical safety mechanisms:

  • Least-privilege default: Unlisted tools default to disabled, requiring explicit enablement.
  • No escalation: Dynamic policies from the server cannot override a disabled base rule; the merge logic always selects the most restrictive option.
  • Visibility: When tools are blocked or require approval, the UI displays clear messages constructed in evaluateToolPolicies.ts that include the specific policy reason.

Summary

  • Continue governs code-modification requests through a declarative YAML policy system defined in packages/config-yaml/src/schemas/policy.ts.
  • The effective policy merges local configuration with organization-wide rules retrieved via DefaultApi.getIdePolicy(), with the most restrictive rule taking precedence.
  • The evaluateToolPolicies thunk determines whether tools are disabled, allowedWithPermission, or allowedWithoutPermission before execution.
  • Terminal commands undergo additional security evaluation via evaluateTerminalCommandSecurity.ts to prevent unauthorized system access.
  • The ToolPermissionService enables runtime policy updates without restarting the IDE or CLI.

Frequently Asked Questions

How do I disable a specific tool in Continue?

Create a .continue/policy.yaml file in your project root and set the tool to disabled. For example, add write: disabled under the tools key to block all write operations. The configuration is parsed by the Zod schema in packages/config-yaml/src/schemas/policy.ts and enforced immediately.

Can server-side policies override local restrictions?

No. When the client merges the base policy from continue.yaml with dynamic rules from the GET /ide/policy endpoint, it selects the most restrictive option available. A disabled rule in your local YAML cannot be overridden by an allowed rule from the server, ensuring that local security constraints remain authoritative.

What happens when a tool requires permission?

The UI displays an "Approve?" button generated by the stream handling logic in gui/src/redux/thunks/streamNormalInput.ts. The tool execution pauses until you explicitly click to approve. If you deny the request or the policy evaluation returns disabled, Continue shows an error message constructed in evaluateToolPolicies.ts explaining the block.

How does Continue secure terminal commands?

Terminal commands are evaluated by evaluateTerminalCommandSecurity in packages/terminal-security/src/evaluateTerminalCommandSecurity.ts, which analyzes command tokens to determine the applicable policy. Unlike standard tools, commands must be explicitly allowedWithoutPermission to execute automatically; otherwise, they follow the standard approval flow or are blocked.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →