Skip to content

Commit 9289af0

Browse files
authored
feat - support auto approve (#255)
* feat - LSP data layer + confirmationService skeleton (Auto-approve Part 1 ) (#132) * feat: Terminal auto-approve with confirmation dialog enhancement (Auto-approve Part 2) (#171) * revert splitDropdownButton padding change (#184) * feat: File operation auto-approve with glob pattern rules (Auto-approve Part 3) (#216) * add file operation approve logic * resolve comments * change default rules color * resolve comments * update test cases * feat: MCP tool auto-approve and global auto-approve (Auto-approve Part 4) (#218) * add mcp approve support and global approve support * resolve comments * fix test cases * resolve comments and add token/policy check * fix compile error * Fix auto-approve preference page layout instability * fix copilot comments
1 parent 790bff8 commit 9289af0

62 files changed

Lines changed: 8493 additions & 174 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/Constants.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ private Constants() {
5454
public static final String GITHUB_JOBS_VIEW_ID = "com.microsoft.copilot.eclipse.ui.jobs.JobsView";
5555
public static final String SUPPRESS_TERMINAL_DEPENDENCY_DIALOG = "suppressTerminalDependencyDialog";
5656

57+
// Auto-Approve settings
58+
public static final String AUTO_APPROVE_TERMINAL_RULES = "autoApproveTerminalRules";
59+
public static final String AUTO_APPROVE_UNMATCHED_TERMINAL = "autoApproveUnmatchedTerminal";
60+
public static final String AUTO_APPROVE_FILE_OP_RULES = "autoApproveEditRules";
61+
public static final String AUTO_APPROVE_UNMATCHED_FILE_OP = "autoApproveUnmatchedFileOp";
62+
public static final String AUTO_APPROVE_MCP_SERVERS = "autoApproveMcpServers";
63+
public static final String AUTO_APPROVE_MCP_TOOLS = "autoApproveMcpTools";
64+
public static final String AUTO_APPROVE_TRUST_TOOL_ANNOTATIONS = "autoApproveTrustToolAnnotations";
65+
public static final String AUTO_APPROVE_YOLO_MODE = "autoApproveYoloMode";
66+
5767
// Base excluded file types shared by both
5868
// Copied from InelliJ, excluded file extension list
5969
// https://github.com/microsoft/copilot-intellij/blob/main/core/src/main/kotlin/com/github/copilot/chat/references/FileSearchService.kt

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/FeatureFlags.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public class FeatureFlags {
2525

2626
private boolean customAgentPolicyEnabled = true;
2727

28+
private boolean autoApprovalTokenEnabled = true;
29+
30+
private boolean autoApprovalPolicyEnabled = true;
31+
2832
public boolean isAgentModeEnabled() {
2933
return agentModeEnabled;
3034
}
@@ -84,6 +88,25 @@ public void setCustomAgentPolicyEnabled(boolean customAgentPolicyEnabled) {
8488
this.customAgentPolicyEnabled = customAgentPolicyEnabled;
8589
}
8690

91+
/**
92+
* Returns true if the auto-approval feature is available.
93+
* Requires both the server token ({@code agent_mode_auto_approval}) and
94+
* the organization policy ({@code agentMode.autoApproval.enabled}) to permit it.
95+
*
96+
* @return true if auto-approval is permitted
97+
*/
98+
public boolean isAutoApprovalEnabled() {
99+
return autoApprovalTokenEnabled && autoApprovalPolicyEnabled;
100+
}
101+
102+
public void setAutoApprovalTokenEnabled(boolean autoApprovalTokenEnabled) {
103+
this.autoApprovalTokenEnabled = autoApprovalTokenEnabled;
104+
}
105+
106+
public void setAutoApprovalPolicyEnabled(boolean autoApprovalPolicyEnabled) {
107+
this.autoApprovalPolicyEnabled = autoApprovalPolicyEnabled;
108+
}
109+
87110
public boolean isClientPreviewFeatureEnabled() {
88111
return clientPreviewFeatureEnabled;
89112
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package com.microsoft.copilot.eclipse.core.chat;
5+
6+
import java.util.Map;
7+
import java.util.Objects;
8+
9+
import org.apache.commons.lang3.builder.ToStringBuilder;
10+
11+
/**
12+
* Represents a button action in the confirmation UI. Each action has a label,
13+
* a decision (accept or dismiss), a persistence scope, and optional metadata
14+
* for the handler to know what to persist.
15+
*/
16+
public class ConfirmationAction {
17+
18+
/** Metadata key for the action type enum name. */
19+
public static final String META_ACTION = "action";
20+
21+
private final String label;
22+
private final boolean accept;
23+
private final ConfirmationActionScope scope;
24+
private final Map<String, String> metadata;
25+
private final boolean primary;
26+
27+
/**
28+
* Creates a new confirmation action.
29+
*
30+
* @param label the button label
31+
* @param accept true for accept, false for dismiss
32+
* @param scope the persistence scope (null for dismiss actions)
33+
* @param metadata extra data for the handler (e.g., command names, server name)
34+
* @param primary whether this is the primary/default button
35+
*/
36+
public ConfirmationAction(String label, boolean accept,
37+
ConfirmationActionScope scope, Map<String, String> metadata,
38+
boolean primary) {
39+
this.label = label;
40+
this.accept = accept;
41+
this.scope = scope;
42+
this.metadata = metadata != null ? metadata : Map.of();
43+
this.primary = primary;
44+
}
45+
46+
public String getLabel() {
47+
return label;
48+
}
49+
50+
public boolean isAccept() {
51+
return accept;
52+
}
53+
54+
public ConfirmationActionScope getScope() {
55+
return scope;
56+
}
57+
58+
public Map<String, String> getMetadata() {
59+
return metadata;
60+
}
61+
62+
public boolean isPrimary() {
63+
return primary;
64+
}
65+
66+
/** Creates a primary accept action (scope = ONCE). */
67+
public static ConfirmationAction allowOnce(String label) {
68+
return new ConfirmationAction(label, true,
69+
ConfirmationActionScope.ONCE, null, true);
70+
}
71+
72+
/** Creates a dismiss action. */
73+
public static ConfirmationAction skip(String label) {
74+
return new ConfirmationAction(label, false, null, null, false);
75+
}
76+
77+
@Override
78+
public int hashCode() {
79+
return Objects.hash(accept, label, metadata, primary, scope);
80+
}
81+
82+
@Override
83+
public boolean equals(Object obj) {
84+
if (this == obj) {
85+
return true;
86+
}
87+
if (obj == null) {
88+
return false;
89+
}
90+
if (getClass() != obj.getClass()) {
91+
return false;
92+
}
93+
ConfirmationAction other = (ConfirmationAction) obj;
94+
return accept == other.accept
95+
&& Objects.equals(label, other.label)
96+
&& Objects.equals(metadata, other.metadata)
97+
&& primary == other.primary && scope == other.scope;
98+
}
99+
100+
@Override
101+
public String toString() {
102+
return new ToStringBuilder(this)
103+
.append("label", label)
104+
.append("accept", accept)
105+
.append("scope", scope)
106+
.append("metadata", metadata)
107+
.append("primary", primary)
108+
.toString();
109+
}
110+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package com.microsoft.copilot.eclipse.core.chat;
5+
6+
/**
7+
* Scope of how a confirmation decision should be persisted.
8+
*/
9+
public enum ConfirmationActionScope {
10+
/** One-time acceptance for this single invocation. */
11+
ONCE,
12+
/** Remember for the current conversation session (in-memory). */
13+
SESSION,
14+
/** Remember globally (application-level persistent, synced to CLS). */
15+
GLOBAL
16+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package com.microsoft.copilot.eclipse.core.chat;
5+
6+
import java.util.List;
7+
import java.util.Objects;
8+
9+
import org.apache.commons.lang3.builder.ToStringBuilder;
10+
11+
/**
12+
* Complete content for rendering a confirmation UI. Returned by handlers when a tool call
13+
* needs user confirmation. Contains the display text and the list of action buttons.
14+
*/
15+
public class ConfirmationContent {
16+
17+
private final String title;
18+
private final String message;
19+
private final List<ConfirmationAction> actions;
20+
21+
/**
22+
* Creates a new confirmation content.
23+
*
24+
* @param title bold title text displayed at the top
25+
* @param message description text (may be null)
26+
* @param actions list of button actions for the confirmation UI
27+
*/
28+
public ConfirmationContent(String title, String message,
29+
List<ConfirmationAction> actions) {
30+
this.title = title;
31+
this.message = message;
32+
this.actions = actions;
33+
}
34+
35+
public String getTitle() {
36+
return title;
37+
}
38+
39+
public String getMessage() {
40+
return message;
41+
}
42+
43+
public List<ConfirmationAction> getActions() {
44+
return actions;
45+
}
46+
47+
@Override
48+
public int hashCode() {
49+
return Objects.hash(actions, message, title);
50+
}
51+
52+
@Override
53+
public boolean equals(Object obj) {
54+
if (this == obj) {
55+
return true;
56+
}
57+
if (obj == null) {
58+
return false;
59+
}
60+
if (getClass() != obj.getClass()) {
61+
return false;
62+
}
63+
ConfirmationContent other = (ConfirmationContent) obj;
64+
return Objects.equals(actions, other.actions)
65+
&& Objects.equals(message, other.message)
66+
&& Objects.equals(title, other.title);
67+
}
68+
69+
@Override
70+
public String toString() {
71+
return new ToStringBuilder(this)
72+
.append("title", title)
73+
.append("message", message)
74+
.append("actions", actions)
75+
.toString();
76+
}
77+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package com.microsoft.copilot.eclipse.core.chat;
5+
6+
import com.microsoft.copilot.eclipse.core.lsp.protocol.InvokeClientToolConfirmationParams;
7+
8+
/**
9+
* Evaluates whether a tool confirmation request can be auto-approved.
10+
* Each implementation handles a specific category of tool (terminal, file operations, MCP, etc.).
11+
*/
12+
public interface ConfirmationHandler {
13+
14+
/**
15+
* Evaluates whether the given confirmation request should be auto-approved.
16+
*
17+
* @param params the confirmation request parameters from CLS
18+
* @return ConfirmationResult.AUTO_APPROVED if the tool call can proceed without user
19+
* confirmation, or ConfirmationResult.NEEDS_CONFIRMATION if the user must approve
20+
*/
21+
ConfirmationResult evaluate(InvokeClientToolConfirmationParams params);
22+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
package com.microsoft.copilot.eclipse.core.chat;
5+
6+
import java.util.Objects;
7+
8+
import org.apache.commons.lang3.builder.ToStringBuilder;
9+
10+
/**
11+
* Result of evaluating an auto-approve confirmation request.
12+
* Either AUTO_APPROVED (no UI needed) or NEEDS_CONFIRMATION with content for the dialog.
13+
*/
14+
public class ConfirmationResult {
15+
16+
/** Auto-approved, no user confirmation needed. */
17+
public static final ConfirmationResult AUTO_APPROVED = new ConfirmationResult(true, false, null);
18+
19+
/** Dismissed — malformed or unhandleable request; CLS should be told to skip the tool. */
20+
public static final ConfirmationResult DISMISSED = new ConfirmationResult(false, true, null);
21+
22+
private final boolean autoApproved;
23+
private final boolean dismissed;
24+
private final ConfirmationContent content;
25+
26+
private ConfirmationResult(boolean autoApproved, boolean dismissed, ConfirmationContent content) {
27+
this.autoApproved = autoApproved;
28+
this.dismissed = dismissed;
29+
this.content = content;
30+
}
31+
32+
/** Creates a result that requires user confirmation with the given content. */
33+
public static ConfirmationResult needsConfirmation(
34+
ConfirmationContent content) {
35+
return new ConfirmationResult(false, false, content);
36+
}
37+
38+
public boolean isAutoApproved() {
39+
return autoApproved;
40+
}
41+
42+
/** Returns true if the request should be dismissed without showing UI. */
43+
public boolean isDismissed() {
44+
return dismissed;
45+
}
46+
47+
/** Returns the confirmation content, or null if auto-approved or using defaults. */
48+
public ConfirmationContent getContent() {
49+
return content;
50+
}
51+
52+
@Override
53+
public int hashCode() {
54+
return Objects.hash(autoApproved, dismissed, content);
55+
}
56+
57+
@Override
58+
public boolean equals(Object obj) {
59+
if (this == obj) {
60+
return true;
61+
}
62+
if (obj == null) {
63+
return false;
64+
}
65+
if (getClass() != obj.getClass()) {
66+
return false;
67+
}
68+
ConfirmationResult other = (ConfirmationResult) obj;
69+
return autoApproved == other.autoApproved
70+
&& dismissed == other.dismissed
71+
&& Objects.equals(content, other.content);
72+
}
73+
74+
@Override
75+
public String toString() {
76+
return new ToStringBuilder(this)
77+
.append("autoApproved", autoApproved)
78+
.append("dismissed", dismissed)
79+
.append("content", content)
80+
.toString();
81+
}
82+
}

0 commit comments

Comments
 (0)