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
disabledbase 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.tsthat 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
evaluateToolPoliciesthunk determines whether tools aredisabled,allowedWithPermission, orallowedWithoutPermissionbefore execution. - Terminal commands undergo additional security evaluation via
evaluateTerminalCommandSecurity.tsto prevent unauthorized system access. - The
ToolPermissionServiceenables 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →