Skip to content

Commit a10d2a3

Browse files
authored
Delegate sub-agent enablement to language server (#349)
1 parent 65db401 commit a10d2a3

12 files changed

Lines changed: 106 additions & 210 deletions

File tree

CONTEXT.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Context & Glossary
2+
3+
This file is a glossary of the ubiquitous language used in this codebase. It is
4+
intentionally free of implementation detail — it defines *what words mean*, not
5+
*how things are built*.
6+
7+
## Terms
8+
9+
### Sub-agent
10+
A delegated agent that the main agent spawns to carry out a scoped sub-task on
11+
its behalf. The main agent invokes it through the `run_subagent` tool and
12+
receives the sub-agent's result back as part of its own turn.
13+
14+
### Sub-agent policy
15+
Organization-level governance that decides whether sub-agents are permitted for
16+
a user. It is authoritative and is **enforced by the language server**, not by
17+
the editor client: when the policy forbids sub-agents, the server refuses to
18+
offer the `run_subagent` tool regardless of what the client advertises.
19+
20+
### Capability
21+
A feature the editor client declares support for when it initializes its
22+
connection to the language server. A capability is an *offer of support*, not a
23+
*grant of permission* — the server may still withhold the corresponding feature
24+
(for example, because the sub-agent policy forbids it).
25+
26+
### Client preview feature
27+
A gate that exposes in-development features to a subset of users. It is distinct
28+
from the sub-agent policy: after this change, the availability of sub-agents no
29+
longer depends on the client preview feature.

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ private Constants() {
2626
public static final String PROXY_KERBEROS_SP = "proxyKerberosSp";
2727
public static final String GITHUB_ENTERPRISE = "githubEnterprise";
2828
public static final String WORKSPACE_CONTEXT_ENABLED = "workspaceContextEnabled";
29-
public static final String SUB_AGENT_ENABLED = "subAgentEnabled";
3029
public static final String AGENT_MAX_REQUESTS = "agentMaxRequests";
3130
public static final String ENABLE_SKILLS = "enableSkills";
3231
public static final String TRANSCRIPT_SUBDIR = ".copilot/eclipse";

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

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
77
import org.eclipse.core.runtime.preferences.InstanceScope;
8-
import org.osgi.service.prefs.BackingStoreException;
98

109
/**
1110
* Class to manage feature flags for the Copilot plugin. This class allows enabling or disabling features.
@@ -21,8 +20,6 @@ public class FeatureFlags {
2120

2221
private boolean mcpContributionPointEnabled = false;
2322

24-
private boolean subAgentPolicyEnabled = true;
25-
2623
private boolean customAgentPolicyEnabled = true;
2724

2825
private boolean autoApprovalTokenEnabled = true;
@@ -61,25 +58,6 @@ public void setMcpContributionPointEnabled(boolean mcpContributionPointEnabled)
6158
this.mcpContributionPointEnabled = mcpContributionPointEnabled;
6259
}
6360

64-
public boolean isSubAgentPolicyEnabled() {
65-
return subAgentPolicyEnabled;
66-
}
67-
68-
/**
69-
* Sets whether the sub-agent policy is enabled.
70-
* When the policy is disabled, it also disables the user preference for sub-agent.
71-
*
72-
* @param subAgentPolicyEnabled true to enable the sub-agent policy, false to disable it
73-
*/
74-
public void setSubAgentPolicyEnabled(boolean subAgentPolicyEnabled) {
75-
this.subAgentPolicyEnabled = subAgentPolicyEnabled;
76-
77-
// When policy disables subagent, also disable the user preference
78-
if (!subAgentPolicyEnabled) {
79-
disableSubAgentPreference();
80-
}
81-
}
82-
8361
public boolean isCustomAgentPolicyEnabled() {
8462
return customAgentPolicyEnabled;
8563
}
@@ -113,17 +91,11 @@ public boolean isClientPreviewFeatureEnabled() {
11391

11492
/**
11593
* Sets whether the client preview feature is enabled.
116-
* When the feature is disabled, it also disables the user preference for sub-agent.
11794
*
11895
* @param clientPreviewFeatureEnabled true to enable the client preview feature, false to disable it
11996
*/
12097
public void setClientPreviewFeatureEnabled(boolean clientPreviewFeatureEnabled) {
12198
this.clientPreviewFeatureEnabled = clientPreviewFeatureEnabled;
122-
123-
// When client preview feature is disabled, also disable the user preference for subagent
124-
if (!clientPreviewFeatureEnabled) {
125-
disableSubAgentPreference();
126-
}
12799
}
128100

129101
/**
@@ -143,46 +115,6 @@ public static boolean isWorkspaceContextEnabled() {
143115
return false;
144116
}
145117

146-
/**
147-
* Disables the user preference for sub-agent.
148-
* This method accesses the UI plugin's preference store to set the sub-agent preference to false.
149-
*
150-
* <p>Note: The sub-agent preference is used to initialize capabilities which happens before policies
151-
* are sent to us. Therefore, we need to flush the preference immediately when policy changes occur
152-
* to ensure the next capability initialization reflects the updated policy state.
153-
*/
154-
private void disableSubAgentPreference() {
155-
// The preference is stored in the UI plugin's preference store, which uses InstanceScope internally
156-
IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode("com.microsoft.copilot.eclipse.ui");
157-
if (prefs != null) {
158-
// Only update and flush if the current setting is true
159-
boolean currentValue = prefs.getBoolean(Constants.SUB_AGENT_ENABLED, false);
160-
if (currentValue) {
161-
prefs.putBoolean(Constants.SUB_AGENT_ENABLED, false);
162-
try {
163-
prefs.flush();
164-
} catch (BackingStoreException e) {
165-
CopilotCore.LOGGER.error("Failed to save subagent preference when disabled by policy", e);
166-
}
167-
}
168-
}
169-
}
170-
171-
/**
172-
* Checks if the sub-agent is enabled.
173-
* Sub-agent is enabled only if both the user preference is enabled AND the organization policy allows it.
174-
*
175-
* @return true if the sub-agent is enabled, false otherwise.
176-
*/
177-
public static boolean isSubAgentEnabled() {
178-
IEclipsePreferences uiPrefs = InstanceScope.INSTANCE.getNode("com.microsoft.copilot.eclipse.ui");
179-
if (uiPrefs != null) {
180-
return uiPrefs.getBoolean(Constants.SUB_AGENT_ENABLED, false);
181-
}
182-
183-
return false;
184-
}
185-
186118
/**
187119
* Checks if the custom agent is enabled.
188120
* Custom agent is enabled only if the organization policy allows it.

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/events/CopilotEventConstants.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ public class CopilotEventConstants {
8686
public static final String TOPIC_DID_CHANGE_MCP_CONTRIBUTION_POINT_POLICY = TOPIC_POLICY
8787
+ "MCP_CONTRIBUTION_POINT_ENABLED";
8888

89-
/**
90-
* Event when the sub-agent policy flag is updated.
91-
*/
92-
public static final String TOPIC_DID_CHANGE_SUB_AGENT_POLICY = TOPIC_POLICY + "SUB_AGENT_ENABLED";
93-
9489
/**
9590
* Event when the custom agent policy flag is updated.
9691
*/

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/CopilotLanguageClient.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,10 +351,6 @@ public void onDidChangePolicy(DidChangePolicyParams params) {
351351
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_MCP_CONTRIBUTION_POINT_POLICY,
352352
params.isMcpContributionPointEnabled());
353353
}
354-
if (flags.isSubAgentPolicyEnabled() != params.isSubAgentEnabled()) {
355-
flags.setSubAgentPolicyEnabled(params.isSubAgentEnabled());
356-
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_SUB_AGENT_POLICY, params.isSubAgentEnabled());
357-
}
358354
if (flags.isCustomAgentPolicyEnabled() != params.isCustomAgentEnabled()) {
359355
flags.setCustomAgentPolicyEnabled(params.isCustomAgentEnabled());
360356
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_CUSTOM_AGENT_POLICY, params.isCustomAgentEnabled());

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/LsStreamConnectionProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public Object getInitializationOptions(@Nullable URI rootUri) {
5555
NameAndVersion editorPluginInfo = new NameAndVersion(EDITOR_PLUGIN_NAME, bundleVersion);
5656
List<String> supportedUriSchemes = PlatformUtils.getSupportedUriSchemes();
5757
CopilotCapabilities capabilities = new CopilotCapabilities(false, FeatureFlags.isWorkspaceContextEnabled(),
58-
FeatureFlags.isSubAgentEnabled(), supportedUriSchemes);
58+
true /*isSubAgentEnabled*/, supportedUriSchemes);
5959
return new InitializationOptions(editorInfo, editorPluginInfo, capabilities);
6060
}
6161

com.microsoft.copilot.eclipse.core/src/com/microsoft/copilot/eclipse/core/lsp/protocol/policy/DidChangePolicyParams.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ public class DidChangePolicyParams {
1616
@SerializedName("mcp.contributionPoint.enabled")
1717
private boolean mcpContributionPointEnabled;
1818

19-
@SerializedName("subAgent.enabled")
20-
private boolean subAgentEnabled = true;
21-
2219
@SerializedName("customAgent.enabled")
2320
private boolean customAgentEnabled = true;
2421

@@ -33,14 +30,6 @@ public void setMcpContributionPointEnabled(boolean mcpContributionPointEnabled)
3330
this.mcpContributionPointEnabled = mcpContributionPointEnabled;
3431
}
3532

36-
public boolean isSubAgentEnabled() {
37-
return subAgentEnabled;
38-
}
39-
40-
public void setSubAgentEnabled(boolean subAgentEnabled) {
41-
this.subAgentEnabled = subAgentEnabled;
42-
}
43-
4433
public boolean isCustomAgentEnabled() {
4534
return customAgentEnabled;
4635
}
@@ -59,7 +48,7 @@ public void setAutoApprovalPolicyEnabled(boolean autoApprovalPolicyEnabled) {
5948

6049
@Override
6150
public int hashCode() {
62-
return Objects.hash(mcpContributionPointEnabled, subAgentEnabled, customAgentEnabled,
51+
return Objects.hash(mcpContributionPointEnabled, customAgentEnabled,
6352
autoApprovalPolicyEnabled);
6453
}
6554

@@ -76,7 +65,6 @@ public boolean equals(Object obj) {
7665
}
7766
DidChangePolicyParams other = (DidChangePolicyParams) obj;
7867
return mcpContributionPointEnabled == other.mcpContributionPointEnabled
79-
&& subAgentEnabled == other.subAgentEnabled
8068
&& customAgentEnabled == other.customAgentEnabled
8169
&& autoApprovalPolicyEnabled == other.autoApprovalPolicyEnabled;
8270
}
@@ -85,7 +73,6 @@ public boolean equals(Object obj) {
8573
public String toString() {
8674
ToStringBuilder builder = new ToStringBuilder(this);
8775
builder.append("mcpContributionPointEnabled", mcpContributionPointEnabled);
88-
builder.append("subAgentEnabled", subAgentEnabled);
8976
builder.append("customAgentEnabled", customAgentEnabled);
9077
builder.append("autoApprovalPolicyEnabled", autoApprovalPolicyEnabled);
9178
return builder.toString();

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/preferences/ChatPreferencesPage.java

Lines changed: 0 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33

44
package com.microsoft.copilot.eclipse.ui.preferences;
55

6-
import java.lang.reflect.Type;
7-
import java.util.HashMap;
8-
import java.util.Map;
9-
10-
import com.google.gson.Gson;
11-
import com.google.gson.reflect.TypeToken;
126
import org.eclipse.core.runtime.preferences.InstanceScope;
137
import org.eclipse.jface.dialogs.MessageDialog;
148
import org.eclipse.jface.layout.GridDataFactory;
@@ -60,29 +54,6 @@ public void createFieldEditors() {
6054
addNote(parent, Messages.preferences_page_watched_files_note_content);
6155
addSeparator(parent);
6256

63-
// Add sub-agent toggle
64-
Composite subAgentComposite = createSectionComposite(parent, gdf);
65-
boolean policyAllowsSubAgent = isPolicyAllowsSubAgent();
66-
if (!policyAllowsSubAgent) {
67-
Composite disabledComposite = new Composite(subAgentComposite, SWT.NONE);
68-
GridLayout disabledCompositeLayout = new GridLayout(1, false);
69-
disabledCompositeLayout.marginWidth = 0;
70-
disabledCompositeLayout.marginHeight = 0;
71-
disabledComposite.setLayout(disabledCompositeLayout);
72-
disabledComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
73-
WrappableIconLink.createWithCustomizedImage(disabledComposite, "/icons/information.png",
74-
Messages.setting_managed_by_organization);
75-
}
76-
77-
BooleanFieldEditor subAgentField = new BooleanFieldEditor(Constants.SUB_AGENT_ENABLED,
78-
Messages.preferences_page_sub_agent, SWT.WRAP, subAgentComposite);
79-
subAgentField.setEnabled(policyAllowsSubAgent, subAgentComposite);
80-
applyFieldWidthHint(subAgentField, subAgentComposite);
81-
addField(subAgentField);
82-
83-
addNote(parent, Messages.preferences_page_sub_agent_note_content);
84-
addSeparator(parent);
85-
8657
if (isClientPreviewFeatureEnabled()) {
8758
// Add Enable Skills toggle
8859
Composite skillsComposite = createSectionComposite(parent, gdf);
@@ -111,51 +82,23 @@ public void createFieldEditors() {
11182
@Override
11283
public void init(IWorkbench workbench) {
11384
setPreferenceStore(CopilotUi.getPlugin().getPreferenceStore());
114-
115-
// Ensure run_subagent tool configuration is consistent with sub-agent preference
116-
// Only check if sub-agent is policy-enabled
117-
if (isPolicyAllowsSubAgent()) {
118-
boolean subAgentEnabled = getPreferenceStore().getBoolean(Constants.SUB_AGENT_ENABLED);
119-
updateSubAgentToolConfiguration(subAgentEnabled);
120-
}
12185
}
12286

12387
@Override
12488
public boolean performOk() {
12589
final boolean oldWorkspaceContextValue = getPreferenceStore().getBoolean(Constants.WORKSPACE_CONTEXT_ENABLED);
12690

127-
// Check if sub-agent is policy-enabled before handling sub-agent preferences
128-
boolean policyAllowsSubAgent = isPolicyAllowsSubAgent();
129-
130-
boolean oldSubAgentValue = false;
131-
if (policyAllowsSubAgent) {
132-
oldSubAgentValue = getPreferenceStore().getBoolean(Constants.SUB_AGENT_ENABLED);
133-
}
134-
13591
final boolean result = super.performOk();
13692
boolean newWorkspaceContextValue = getPreferenceStore().getBoolean(Constants.WORKSPACE_CONTEXT_ENABLED);
13793

138-
boolean newSubAgentValue = false;
139-
if (policyAllowsSubAgent) {
140-
newSubAgentValue = getPreferenceStore().getBoolean(Constants.SUB_AGENT_ENABLED);
141-
}
142-
143-
// Handle sub-agent preference change
144-
boolean isSubAgentChanged = policyAllowsSubAgent && (oldSubAgentValue ^ newSubAgentValue);
145-
if (isSubAgentChanged) {
146-
updateSubAgentToolConfiguration(newSubAgentValue);
147-
}
148-
14994
boolean isWorkspaceContextChanged = oldWorkspaceContextValue ^ newWorkspaceContextValue;
15095
if (isWorkspaceContextChanged) {
15196
try {
15297
InstanceScope.INSTANCE.getNode(CopilotUi.getPlugin().getBundle().getSymbolicName()).flush();
15398
} catch (BackingStoreException e) {
15499
CopilotCore.LOGGER.error("Failed to save preference 'Enable workspace context'", e);
155100
}
156-
}
157101

158-
if (isSubAgentChanged || isWorkspaceContextChanged) {
159102
boolean restart = MessageDialog.openQuestion(getShell(), Messages.preferences_page_restart_required,
160103
Messages.preferences_page_restart_question);
161104

@@ -196,63 +139,8 @@ private void addSeparator(Composite parent) {
196139
separator.setLayoutData(gridData);
197140
}
198141

199-
private boolean isPolicyAllowsSubAgent() {
200-
FeatureFlags flags = CopilotCore.getPlugin().getFeatureFlags();
201-
return isClientPreviewFeatureEnabled() && flags != null && flags.isSubAgentPolicyEnabled();
202-
}
203-
204142
private boolean isClientPreviewFeatureEnabled() {
205143
FeatureFlags flags = CopilotCore.getPlugin().getFeatureFlags();
206144
return flags != null && flags.isClientPreviewFeatureEnabled();
207145
}
208-
209-
/**
210-
* Updates the MCP tool configuration to include or exclude the run_subagent tool for agent mode based on the
211-
* sub-agent preference setting.
212-
*
213-
* @param subAgentEnabled true if sub-agent is enabled, false otherwise
214-
*/
215-
private void updateSubAgentToolConfiguration(boolean subAgentEnabled) {
216-
try {
217-
// Load existing MCP tools mode status
218-
String existingJson = getPreferenceStore().getString(Constants.MCP_TOOLS_MODE_STATUS);
219-
220-
// Parse existing configuration or create new one
221-
Map<String, Map<String, Map<String, Boolean>>> modeToolStatus;
222-
if (existingJson != null && !existingJson.trim().isEmpty()) {
223-
Type type = new TypeToken<Map<String, Map<String, Map<String, Boolean>>>>() {
224-
}.getType();
225-
modeToolStatus = new Gson().fromJson(existingJson, type);
226-
} else {
227-
modeToolStatus = new HashMap<>();
228-
}
229-
230-
// Ensure agent-mode map exists
231-
if (!modeToolStatus.containsKey("agent-mode")) {
232-
modeToolStatus.put("agent-mode", new HashMap<>());
233-
}
234-
235-
// Get or create the Built-in Tools server map
236-
Map<String, Map<String, Boolean>> agentModeTools = modeToolStatus.get("agent-mode");
237-
String builtInToolsKey = Messages.preferences_page_mcp_tools_builtin;
238-
if (!agentModeTools.containsKey(builtInToolsKey)) {
239-
agentModeTools.put(builtInToolsKey, new HashMap<>());
240-
}
241-
242-
// Update the run_subagent tool status
243-
Map<String, Boolean> builtInTools = agentModeTools.get(builtInToolsKey);
244-
if (subAgentEnabled) {
245-
builtInTools.put("run_subagent", true);
246-
} else {
247-
builtInTools.remove("run_subagent");
248-
}
249-
250-
// Save back to preferences
251-
String updatedJson = new Gson().toJson(modeToolStatus);
252-
getPreferenceStore().setValue(Constants.MCP_TOOLS_MODE_STATUS, updatedJson);
253-
254-
} catch (Exception e) {
255-
CopilotCore.LOGGER.error("Failed to update sub-agent tool configuration", e);
256-
}
257-
}
258146
}

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/preferences/CopilotPreferenceInitializer.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public void initializeDefaultPreferences() {
3131
pref.setDefault(Constants.PROXY_KERBEROS_SP, "");
3232
pref.setDefault(Constants.GITHUB_ENTERPRISE, "");
3333
pref.setDefault(Constants.WORKSPACE_CONTEXT_ENABLED, false);
34-
pref.setDefault(Constants.SUB_AGENT_ENABLED, true);
3534
pref.setDefault(Constants.AGENT_MAX_REQUESTS, 25);
3635
pref.setDefault(Constants.ENABLE_SKILLS, true);
3736
pref.setDefault(Constants.CUSTOM_INSTRUCTIONS_WORKSPACE_ENABLED, false);

0 commit comments

Comments
 (0)