Skip to content

Commit 9775195

Browse files
committed
feat: Terminal auto-approve with confirmation dialog enhancement (Auto-approve Part 2) (#171)
1 parent 36a1a32 commit 9775195

34 files changed

Lines changed: 3219 additions & 192 deletions

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ 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+
5761
// Base excluded file types shared by both
5862
// Copied from InelliJ, excluded file extension list
5963
// https://github.com/microsoft/copilot-intellij/blob/main/core/src/main/kotlin/com/github/copilot/chat/references/FileSearchService.kt
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+
}

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/chat/ConfirmationResult.java

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,68 @@
33

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

6+
import java.util.Objects;
7+
8+
import org.apache.commons.lang3.builder.ToStringBuilder;
9+
610
/**
711
* Result of evaluating an auto-approve confirmation request.
12+
* Either AUTO_APPROVED (no UI needed) or NEEDS_CONFIRMATION with content for the dialog.
813
*/
9-
public enum ConfirmationResult {
10-
/**
11-
* The tool invocation is automatically approved and no user confirmation is needed.
12-
*/
13-
AUTO_APPROVED,
14-
15-
/**
16-
* The tool invocation requires user confirmation before proceeding.
17-
*/
18-
NEEDS_CONFIRMATION
14+
public class ConfirmationResult {
15+
16+
/** Auto-approved, no user confirmation needed. */
17+
public static final ConfirmationResult AUTO_APPROVED = new ConfirmationResult(true, null);
18+
19+
private final boolean autoApproved;
20+
private final ConfirmationContent content;
21+
22+
private ConfirmationResult(boolean autoApproved, ConfirmationContent content) {
23+
this.autoApproved = autoApproved;
24+
this.content = content;
25+
}
26+
27+
/** Creates a result that requires user confirmation with the given content. */
28+
public static ConfirmationResult needsConfirmation(
29+
ConfirmationContent content) {
30+
return new ConfirmationResult(false, content);
31+
}
32+
33+
public boolean isAutoApproved() {
34+
return autoApproved;
35+
}
36+
37+
/** Returns the confirmation content, or null if auto-approved or using defaults. */
38+
public ConfirmationContent getContent() {
39+
return content;
40+
}
41+
42+
@Override
43+
public int hashCode() {
44+
return Objects.hash(autoApproved, content);
45+
}
46+
47+
@Override
48+
public boolean equals(Object obj) {
49+
if (this == obj) {
50+
return true;
51+
}
52+
if (obj == null) {
53+
return false;
54+
}
55+
if (getClass() != obj.getClass()) {
56+
return false;
57+
}
58+
ConfirmationResult other = (ConfirmationResult) obj;
59+
return autoApproved == other.autoApproved
60+
&& Objects.equals(content, other.content);
61+
}
62+
63+
@Override
64+
public String toString() {
65+
return new ToStringBuilder(this)
66+
.append("autoApproved", autoApproved)
67+
.append("content", content)
68+
.toString();
69+
}
1970
}
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.Objects;
7+
8+
import org.apache.commons.lang3.builder.ToStringBuilder;
9+
10+
/**
11+
* A single terminal auto-approve rule mapping a command name or regex pattern to an
12+
* allow/deny decision.
13+
*/
14+
public class TerminalAutoApproveRule {
15+
private String command;
16+
private boolean autoApprove;
17+
18+
/**
19+
* Creates a new rule.
20+
*
21+
* @param command the command name or regex pattern
22+
* @param autoApprove true to auto-approve, false to always require confirmation
23+
*/
24+
public TerminalAutoApproveRule(String command, boolean autoApprove) {
25+
this.command = command;
26+
this.autoApprove = autoApprove;
27+
}
28+
29+
/** Default constructor for Gson deserialization. */
30+
public TerminalAutoApproveRule() {
31+
}
32+
33+
public String getCommand() {
34+
return command;
35+
}
36+
37+
public void setCommand(String command) {
38+
this.command = command;
39+
}
40+
41+
public boolean isAutoApprove() {
42+
return autoApprove;
43+
}
44+
45+
public void setAutoApprove(boolean autoApprove) {
46+
this.autoApprove = autoApprove;
47+
}
48+
49+
@Override
50+
public int hashCode() {
51+
return Objects.hash(autoApprove, command);
52+
}
53+
54+
@Override
55+
public boolean equals(Object obj) {
56+
if (this == obj) {
57+
return true;
58+
}
59+
if (obj == null) {
60+
return false;
61+
}
62+
if (getClass() != obj.getClass()) {
63+
return false;
64+
}
65+
TerminalAutoApproveRule other = (TerminalAutoApproveRule) obj;
66+
return autoApprove == other.autoApprove
67+
&& Objects.equals(command, other.command);
68+
}
69+
70+
@Override
71+
public String toString() {
72+
return new ToStringBuilder(this)
73+
.append("command", command)
74+
.append("autoApprove", autoApprove)
75+
.toString();
76+
}
77+
}

0 commit comments

Comments
 (0)