Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ private Constants() {
public static final String GITHUB_JOBS_VIEW_ID = "com.microsoft.copilot.eclipse.ui.jobs.JobsView";
public static final String SUPPRESS_TERMINAL_DEPENDENCY_DIALOG = "suppressTerminalDependencyDialog";

// Auto-Approve settings
public static final String AUTO_APPROVE_TERMINAL_RULES = "autoApproveTerminalRules";
public static final String AUTO_APPROVE_UNMATCHED_TERMINAL = "autoApproveUnmatchedTerminal";
public static final String AUTO_APPROVE_FILE_OP_RULES = "autoApproveEditRules";
public static final String AUTO_APPROVE_UNMATCHED_FILE_OP = "autoApproveUnmatchedFileOp";
public static final String AUTO_APPROVE_MCP_SERVERS = "autoApproveMcpServers";
public static final String AUTO_APPROVE_MCP_TOOLS = "autoApproveMcpTools";
public static final String AUTO_APPROVE_TRUST_TOOL_ANNOTATIONS = "autoApproveTrustToolAnnotations";
public static final String AUTO_APPROVE_YOLO_MODE = "autoApproveYoloMode";

// Base excluded file types shared by both
// Copied from InelliJ, excluded file extension list
// https://github.com/microsoft/copilot-intellij/blob/main/core/src/main/kotlin/com/github/copilot/chat/references/FileSearchService.kt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public class FeatureFlags {

private boolean customAgentPolicyEnabled = true;

private boolean autoApprovalTokenEnabled = true;

private boolean autoApprovalPolicyEnabled = true;

public boolean isAgentModeEnabled() {
return agentModeEnabled;
}
Expand Down Expand Up @@ -84,6 +88,25 @@ public void setCustomAgentPolicyEnabled(boolean customAgentPolicyEnabled) {
this.customAgentPolicyEnabled = customAgentPolicyEnabled;
}

/**
* Returns true if the auto-approval feature is available.
* Requires both the server token ({@code agent_mode_auto_approval}) and
* the organization policy ({@code agentMode.autoApproval.enabled}) to permit it.
*
* @return true if auto-approval is permitted
*/
public boolean isAutoApprovalEnabled() {
return autoApprovalTokenEnabled && autoApprovalPolicyEnabled;
}

public void setAutoApprovalTokenEnabled(boolean autoApprovalTokenEnabled) {
this.autoApprovalTokenEnabled = autoApprovalTokenEnabled;
}

public void setAutoApprovalPolicyEnabled(boolean autoApprovalPolicyEnabled) {
this.autoApprovalPolicyEnabled = autoApprovalPolicyEnabled;
}

public boolean isClientPreviewFeatureEnabled() {
return clientPreviewFeatureEnabled;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package com.microsoft.copilot.eclipse.core.chat;

import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.builder.ToStringBuilder;

/**
* Represents a button action in the confirmation UI. Each action has a label,
* a decision (accept or dismiss), a persistence scope, and optional metadata
* for the handler to know what to persist.
*/
public class ConfirmationAction {

/** Metadata key for the action type enum name. */
public static final String META_ACTION = "action";

private final String label;
private final boolean accept;
private final ConfirmationActionScope scope;
private final Map<String, String> metadata;
private final boolean primary;

/**
* Creates a new confirmation action.
*
* @param label the button label
* @param accept true for accept, false for dismiss
* @param scope the persistence scope (null for dismiss actions)
* @param metadata extra data for the handler (e.g., command names, server name)
* @param primary whether this is the primary/default button
*/
public ConfirmationAction(String label, boolean accept,
ConfirmationActionScope scope, Map<String, String> metadata,
boolean primary) {
this.label = label;
this.accept = accept;
this.scope = scope;
this.metadata = metadata != null ? metadata : Map.of();
this.primary = primary;
}

public String getLabel() {
return label;
}

public boolean isAccept() {
return accept;
}

public ConfirmationActionScope getScope() {
return scope;
}

public Map<String, String> getMetadata() {
return metadata;
}

public boolean isPrimary() {
return primary;
}

/** Creates a primary accept action (scope = ONCE). */
public static ConfirmationAction allowOnce(String label) {
return new ConfirmationAction(label, true,
ConfirmationActionScope.ONCE, null, true);
}

/** Creates a dismiss action. */
public static ConfirmationAction skip(String label) {
return new ConfirmationAction(label, false, null, null, false);
}

@Override
public int hashCode() {
return Objects.hash(accept, label, metadata, primary, scope);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ConfirmationAction other = (ConfirmationAction) obj;
return accept == other.accept
&& Objects.equals(label, other.label)
&& Objects.equals(metadata, other.metadata)
&& primary == other.primary && scope == other.scope;
}

@Override
public String toString() {
return new ToStringBuilder(this)
.append("label", label)
.append("accept", accept)
.append("scope", scope)
.append("metadata", metadata)
.append("primary", primary)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package com.microsoft.copilot.eclipse.core.chat;

/**
* Scope of how a confirmation decision should be persisted.
*/
public enum ConfirmationActionScope {
/** One-time acceptance for this single invocation. */
ONCE,
/** Remember for the current conversation session (in-memory). */
SESSION,
/** Remember globally (application-level persistent, synced to CLS). */
GLOBAL
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package com.microsoft.copilot.eclipse.core.chat;

import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.builder.ToStringBuilder;

/**
* Complete content for rendering a confirmation UI. Returned by handlers when a tool call
* needs user confirmation. Contains the display text and the list of action buttons.
*/
public class ConfirmationContent {

private final String title;
private final String message;
private final List<ConfirmationAction> actions;

/**
* Creates a new confirmation content.
*
* @param title bold title text displayed at the top
* @param message description text (may be null)
* @param actions list of button actions for the confirmation UI
*/
public ConfirmationContent(String title, String message,
List<ConfirmationAction> actions) {
this.title = title;
this.message = message;
this.actions = actions;
}

public String getTitle() {
return title;
}

public String getMessage() {
return message;
}

public List<ConfirmationAction> getActions() {
return actions;
}

@Override
public int hashCode() {
return Objects.hash(actions, message, title);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ConfirmationContent other = (ConfirmationContent) obj;
return Objects.equals(actions, other.actions)
&& Objects.equals(message, other.message)
&& Objects.equals(title, other.title);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.append("title", title)
.append("message", message)
.append("actions", actions)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package com.microsoft.copilot.eclipse.core.chat;

import com.microsoft.copilot.eclipse.core.lsp.protocol.InvokeClientToolConfirmationParams;

/**
* Evaluates whether a tool confirmation request can be auto-approved.
* Each implementation handles a specific category of tool (terminal, file operations, MCP, etc.).
*/
public interface ConfirmationHandler {

/**
* Evaluates whether the given confirmation request should be auto-approved.
*
* @param params the confirmation request parameters from CLS
* @return ConfirmationResult.AUTO_APPROVED if the tool call can proceed without user
* confirmation, or ConfirmationResult.NEEDS_CONFIRMATION if the user must approve
*/
ConfirmationResult evaluate(InvokeClientToolConfirmationParams params);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

package com.microsoft.copilot.eclipse.core.chat;

import java.util.Objects;

import org.apache.commons.lang3.builder.ToStringBuilder;

/**
* Result of evaluating an auto-approve confirmation request.
* Either AUTO_APPROVED (no UI needed) or NEEDS_CONFIRMATION with content for the dialog.
*/
public class ConfirmationResult {

/** Auto-approved, no user confirmation needed. */
public static final ConfirmationResult AUTO_APPROVED = new ConfirmationResult(true, false, null);

/** Dismissed — malformed or unhandleable request; CLS should be told to skip the tool. */
public static final ConfirmationResult DISMISSED = new ConfirmationResult(false, true, null);

private final boolean autoApproved;
private final boolean dismissed;
private final ConfirmationContent content;

private ConfirmationResult(boolean autoApproved, boolean dismissed, ConfirmationContent content) {
this.autoApproved = autoApproved;
this.dismissed = dismissed;
this.content = content;
}

/** Creates a result that requires user confirmation with the given content. */
public static ConfirmationResult needsConfirmation(
ConfirmationContent content) {
return new ConfirmationResult(false, false, content);
}

public boolean isAutoApproved() {
return autoApproved;
}

/** Returns true if the request should be dismissed without showing UI. */
public boolean isDismissed() {
return dismissed;
}

/** Returns the confirmation content, or null if auto-approved or using defaults. */
public ConfirmationContent getContent() {
return content;
}

@Override
public int hashCode() {
return Objects.hash(autoApproved, dismissed, content);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ConfirmationResult other = (ConfirmationResult) obj;
return autoApproved == other.autoApproved
&& dismissed == other.dismissed
&& Objects.equals(content, other.content);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.append("autoApproved", autoApproved)
.append("dismissed", dismissed)
.append("content", content)
.toString();
}
}
Loading