Skip to content
Merged
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
29 changes: 29 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Context & Glossary

This file is a glossary of the ubiquitous language used in this codebase. It is
intentionally free of implementation detail — it defines *what words mean*, not
*how things are built*.

## Terms

### Sub-agent
A delegated agent that the main agent spawns to carry out a scoped sub-task on
its behalf. The main agent invokes it through the `run_subagent` tool and
receives the sub-agent's result back as part of its own turn.

### Sub-agent policy
Organization-level governance that decides whether sub-agents are permitted for
a user. It is authoritative and is **enforced by the language server**, not by
the editor client: when the policy forbids sub-agents, the server refuses to
offer the `run_subagent` tool regardless of what the client advertises.

### Capability
A feature the editor client declares support for when it initializes its
connection to the language server. A capability is an *offer of support*, not a
*grant of permission* — the server may still withhold the corresponding feature
(for example, because the sub-agent policy forbids it).

### Client preview feature
A gate that exposes in-development features to a subset of users. It is distinct
from the sub-agent policy: after this change, the availability of sub-agents no
longer depends on the client preview feature.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ private Constants() {
public static final String PROXY_KERBEROS_SP = "proxyKerberosSp";
public static final String GITHUB_ENTERPRISE = "githubEnterprise";
public static final String WORKSPACE_CONTEXT_ENABLED = "workspaceContextEnabled";
public static final String SUB_AGENT_ENABLED = "subAgentEnabled";
public static final String AGENT_MAX_REQUESTS = "agentMaxRequests";
public static final String ENABLE_SKILLS = "enableSkills";
public static final String TRANSCRIPT_SUBDIR = ".copilot/eclipse";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.osgi.service.prefs.BackingStoreException;

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

private boolean mcpContributionPointEnabled = false;

private boolean subAgentPolicyEnabled = true;

private boolean customAgentPolicyEnabled = true;

private boolean autoApprovalTokenEnabled = true;
Expand Down Expand Up @@ -61,25 +58,6 @@ public void setMcpContributionPointEnabled(boolean mcpContributionPointEnabled)
this.mcpContributionPointEnabled = mcpContributionPointEnabled;
}

public boolean isSubAgentPolicyEnabled() {
return subAgentPolicyEnabled;
}

/**
* Sets whether the sub-agent policy is enabled.
* When the policy is disabled, it also disables the user preference for sub-agent.
*
* @param subAgentPolicyEnabled true to enable the sub-agent policy, false to disable it
*/
public void setSubAgentPolicyEnabled(boolean subAgentPolicyEnabled) {
this.subAgentPolicyEnabled = subAgentPolicyEnabled;

// When policy disables subagent, also disable the user preference
if (!subAgentPolicyEnabled) {
disableSubAgentPreference();
}
}

public boolean isCustomAgentPolicyEnabled() {
return customAgentPolicyEnabled;
}
Expand Down Expand Up @@ -113,17 +91,11 @@ public boolean isClientPreviewFeatureEnabled() {

/**
* Sets whether the client preview feature is enabled.
* When the feature is disabled, it also disables the user preference for sub-agent.
*
* @param clientPreviewFeatureEnabled true to enable the client preview feature, false to disable it
*/
public void setClientPreviewFeatureEnabled(boolean clientPreviewFeatureEnabled) {
this.clientPreviewFeatureEnabled = clientPreviewFeatureEnabled;

// When client preview feature is disabled, also disable the user preference for subagent
if (!clientPreviewFeatureEnabled) {
disableSubAgentPreference();
}
}

/**
Expand All @@ -143,46 +115,6 @@ public static boolean isWorkspaceContextEnabled() {
return false;
}

/**
* Disables the user preference for sub-agent.
* This method accesses the UI plugin's preference store to set the sub-agent preference to false.
*
* <p>Note: The sub-agent preference is used to initialize capabilities which happens before policies
* are sent to us. Therefore, we need to flush the preference immediately when policy changes occur
* to ensure the next capability initialization reflects the updated policy state.
*/
private void disableSubAgentPreference() {
// The preference is stored in the UI plugin's preference store, which uses InstanceScope internally
IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode("com.microsoft.copilot.eclipse.ui");
if (prefs != null) {
// Only update and flush if the current setting is true
boolean currentValue = prefs.getBoolean(Constants.SUB_AGENT_ENABLED, false);
if (currentValue) {
prefs.putBoolean(Constants.SUB_AGENT_ENABLED, false);
try {
prefs.flush();
} catch (BackingStoreException e) {
CopilotCore.LOGGER.error("Failed to save subagent preference when disabled by policy", e);
}
}
}
}

/**
* Checks if the sub-agent is enabled.
* Sub-agent is enabled only if both the user preference is enabled AND the organization policy allows it.
*
* @return true if the sub-agent is enabled, false otherwise.
*/
public static boolean isSubAgentEnabled() {
IEclipsePreferences uiPrefs = InstanceScope.INSTANCE.getNode("com.microsoft.copilot.eclipse.ui");
if (uiPrefs != null) {
return uiPrefs.getBoolean(Constants.SUB_AGENT_ENABLED, false);
}

return false;
}

/**
* Checks if the custom agent is enabled.
* Custom agent is enabled only if the organization policy allows it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ public class CopilotEventConstants {
public static final String TOPIC_DID_CHANGE_MCP_CONTRIBUTION_POINT_POLICY = TOPIC_POLICY
+ "MCP_CONTRIBUTION_POINT_ENABLED";

/**
* Event when the sub-agent policy flag is updated.
*/
public static final String TOPIC_DID_CHANGE_SUB_AGENT_POLICY = TOPIC_POLICY + "SUB_AGENT_ENABLED";

/**
* Event when the custom agent policy flag is updated.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,6 @@ public void onDidChangePolicy(DidChangePolicyParams params) {
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_MCP_CONTRIBUTION_POINT_POLICY,
params.isMcpContributionPointEnabled());
}
if (flags.isSubAgentPolicyEnabled() != params.isSubAgentEnabled()) {
flags.setSubAgentPolicyEnabled(params.isSubAgentEnabled());
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_SUB_AGENT_POLICY, params.isSubAgentEnabled());
}
if (flags.isCustomAgentPolicyEnabled() != params.isCustomAgentEnabled()) {
flags.setCustomAgentPolicyEnabled(params.isCustomAgentEnabled());
eventBroker.post(CopilotEventConstants.TOPIC_DID_CHANGE_CUSTOM_AGENT_POLICY, params.isCustomAgentEnabled());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public Object getInitializationOptions(@Nullable URI rootUri) {
NameAndVersion editorPluginInfo = new NameAndVersion(EDITOR_PLUGIN_NAME, bundleVersion);
List<String> supportedUriSchemes = PlatformUtils.getSupportedUriSchemes();
CopilotCapabilities capabilities = new CopilotCapabilities(false, FeatureFlags.isWorkspaceContextEnabled(),
FeatureFlags.isSubAgentEnabled(), supportedUriSchemes);
true /*isSubAgentEnabled*/, supportedUriSchemes);
return new InitializationOptions(editorInfo, editorPluginInfo, capabilities);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ public class DidChangePolicyParams {
@SerializedName("mcp.contributionPoint.enabled")
private boolean mcpContributionPointEnabled;

@SerializedName("subAgent.enabled")
private boolean subAgentEnabled = true;

@SerializedName("customAgent.enabled")
private boolean customAgentEnabled = true;

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

public boolean isSubAgentEnabled() {
return subAgentEnabled;
}

public void setSubAgentEnabled(boolean subAgentEnabled) {
this.subAgentEnabled = subAgentEnabled;
}

public boolean isCustomAgentEnabled() {
return customAgentEnabled;
}
Expand All @@ -59,7 +48,7 @@ public void setAutoApprovalPolicyEnabled(boolean autoApprovalPolicyEnabled) {

@Override
public int hashCode() {
return Objects.hash(mcpContributionPointEnabled, subAgentEnabled, customAgentEnabled,
return Objects.hash(mcpContributionPointEnabled, customAgentEnabled,
autoApprovalPolicyEnabled);
}

Expand All @@ -76,7 +65,6 @@ public boolean equals(Object obj) {
}
DidChangePolicyParams other = (DidChangePolicyParams) obj;
return mcpContributionPointEnabled == other.mcpContributionPointEnabled
&& subAgentEnabled == other.subAgentEnabled
&& customAgentEnabled == other.customAgentEnabled
&& autoApprovalPolicyEnabled == other.autoApprovalPolicyEnabled;
}
Expand All @@ -85,7 +73,6 @@ public boolean equals(Object obj) {
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
builder.append("mcpContributionPointEnabled", mcpContributionPointEnabled);
builder.append("subAgentEnabled", subAgentEnabled);
builder.append("customAgentEnabled", customAgentEnabled);
builder.append("autoApprovalPolicyEnabled", autoApprovalPolicyEnabled);
return builder.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@

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

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
Expand Down Expand Up @@ -60,29 +54,6 @@ public void createFieldEditors() {
addNote(parent, Messages.preferences_page_watched_files_note_content);
addSeparator(parent);

// Add sub-agent toggle
Composite subAgentComposite = createSectionComposite(parent, gdf);
boolean policyAllowsSubAgent = isPolicyAllowsSubAgent();
if (!policyAllowsSubAgent) {
Composite disabledComposite = new Composite(subAgentComposite, SWT.NONE);
GridLayout disabledCompositeLayout = new GridLayout(1, false);
disabledCompositeLayout.marginWidth = 0;
disabledCompositeLayout.marginHeight = 0;
disabledComposite.setLayout(disabledCompositeLayout);
disabledComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
WrappableIconLink.createWithCustomizedImage(disabledComposite, "/icons/information.png",
Messages.setting_managed_by_organization);
}

BooleanFieldEditor subAgentField = new BooleanFieldEditor(Constants.SUB_AGENT_ENABLED,
Messages.preferences_page_sub_agent, SWT.WRAP, subAgentComposite);
subAgentField.setEnabled(policyAllowsSubAgent, subAgentComposite);
applyFieldWidthHint(subAgentField, subAgentComposite);
addField(subAgentField);

addNote(parent, Messages.preferences_page_sub_agent_note_content);
addSeparator(parent);

if (isClientPreviewFeatureEnabled()) {
// Add Enable Skills toggle
Composite skillsComposite = createSectionComposite(parent, gdf);
Expand Down Expand Up @@ -111,51 +82,23 @@ public void createFieldEditors() {
@Override
public void init(IWorkbench workbench) {
setPreferenceStore(CopilotUi.getPlugin().getPreferenceStore());

// Ensure run_subagent tool configuration is consistent with sub-agent preference
// Only check if sub-agent is policy-enabled
if (isPolicyAllowsSubAgent()) {
boolean subAgentEnabled = getPreferenceStore().getBoolean(Constants.SUB_AGENT_ENABLED);
updateSubAgentToolConfiguration(subAgentEnabled);
}
}

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

// Check if sub-agent is policy-enabled before handling sub-agent preferences
boolean policyAllowsSubAgent = isPolicyAllowsSubAgent();

boolean oldSubAgentValue = false;
if (policyAllowsSubAgent) {
oldSubAgentValue = getPreferenceStore().getBoolean(Constants.SUB_AGENT_ENABLED);
}

final boolean result = super.performOk();
boolean newWorkspaceContextValue = getPreferenceStore().getBoolean(Constants.WORKSPACE_CONTEXT_ENABLED);

boolean newSubAgentValue = false;
if (policyAllowsSubAgent) {
newSubAgentValue = getPreferenceStore().getBoolean(Constants.SUB_AGENT_ENABLED);
}

// Handle sub-agent preference change
boolean isSubAgentChanged = policyAllowsSubAgent && (oldSubAgentValue ^ newSubAgentValue);
if (isSubAgentChanged) {
updateSubAgentToolConfiguration(newSubAgentValue);
}

boolean isWorkspaceContextChanged = oldWorkspaceContextValue ^ newWorkspaceContextValue;
if (isWorkspaceContextChanged) {
try {
InstanceScope.INSTANCE.getNode(CopilotUi.getPlugin().getBundle().getSymbolicName()).flush();
} catch (BackingStoreException e) {
CopilotCore.LOGGER.error("Failed to save preference 'Enable workspace context'", e);
}
}

if (isSubAgentChanged || isWorkspaceContextChanged) {
boolean restart = MessageDialog.openQuestion(getShell(), Messages.preferences_page_restart_required,
Messages.preferences_page_restart_question);

Expand Down Expand Up @@ -196,63 +139,8 @@ private void addSeparator(Composite parent) {
separator.setLayoutData(gridData);
}

private boolean isPolicyAllowsSubAgent() {
FeatureFlags flags = CopilotCore.getPlugin().getFeatureFlags();
return isClientPreviewFeatureEnabled() && flags != null && flags.isSubAgentPolicyEnabled();
}

private boolean isClientPreviewFeatureEnabled() {
FeatureFlags flags = CopilotCore.getPlugin().getFeatureFlags();
return flags != null && flags.isClientPreviewFeatureEnabled();
}

/**
* Updates the MCP tool configuration to include or exclude the run_subagent tool for agent mode based on the
* sub-agent preference setting.
*
* @param subAgentEnabled true if sub-agent is enabled, false otherwise
*/
private void updateSubAgentToolConfiguration(boolean subAgentEnabled) {
try {
// Load existing MCP tools mode status
String existingJson = getPreferenceStore().getString(Constants.MCP_TOOLS_MODE_STATUS);

// Parse existing configuration or create new one
Map<String, Map<String, Map<String, Boolean>>> modeToolStatus;
if (existingJson != null && !existingJson.trim().isEmpty()) {
Type type = new TypeToken<Map<String, Map<String, Map<String, Boolean>>>>() {
}.getType();
modeToolStatus = new Gson().fromJson(existingJson, type);
} else {
modeToolStatus = new HashMap<>();
}

// Ensure agent-mode map exists
if (!modeToolStatus.containsKey("agent-mode")) {
modeToolStatus.put("agent-mode", new HashMap<>());
}

// Get or create the Built-in Tools server map
Map<String, Map<String, Boolean>> agentModeTools = modeToolStatus.get("agent-mode");
String builtInToolsKey = Messages.preferences_page_mcp_tools_builtin;
if (!agentModeTools.containsKey(builtInToolsKey)) {
agentModeTools.put(builtInToolsKey, new HashMap<>());
}

// Update the run_subagent tool status
Map<String, Boolean> builtInTools = agentModeTools.get(builtInToolsKey);
if (subAgentEnabled) {
builtInTools.put("run_subagent", true);
} else {
builtInTools.remove("run_subagent");
}

// Save back to preferences
String updatedJson = new Gson().toJson(modeToolStatus);
getPreferenceStore().setValue(Constants.MCP_TOOLS_MODE_STATUS, updatedJson);

} catch (Exception e) {
CopilotCore.LOGGER.error("Failed to update sub-agent tool configuration", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public void initializeDefaultPreferences() {
pref.setDefault(Constants.PROXY_KERBEROS_SP, "");
pref.setDefault(Constants.GITHUB_ENTERPRISE, "");
pref.setDefault(Constants.WORKSPACE_CONTEXT_ENABLED, false);
pref.setDefault(Constants.SUB_AGENT_ENABLED, true);
pref.setDefault(Constants.AGENT_MAX_REQUESTS, 25);
pref.setDefault(Constants.ENABLE_SKILLS, true);
pref.setDefault(Constants.CUSTOM_INSTRUCTIONS_WORKSPACE_ENABLED, false);
Expand Down
Loading
Loading