Skip to content

Commit 9d1d42f

Browse files
committed
Release 0.40.0
1 parent afbbdad commit 9d1d42f

File tree

10 files changed

+164
-49
lines changed

10 files changed

+164
-49
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.40.0 - July 24, 2025
9+
### Added
10+
- Support disabling Agent mode when it's disabled by policy.
11+
812
## 0.39.0 - July 23, 2025
913
### Fixed
1014
- Performance: Fixed a freezing issue in 'Add Context' view when opening large projects.
Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,95 @@
11
import SwiftUI
22
import Persist
33
import ConversationServiceProvider
4+
import GitHubCopilotService
5+
import Combine
46

57
public extension Notification.Name {
68
static let gitHubCopilotChatModeDidChange = Notification
79
.Name("com.github.CopilotForXcode.ChatModeDidChange")
810
}
911

12+
public enum ChatMode: String {
13+
case Ask = "Ask"
14+
case Agent = "Agent"
15+
}
16+
1017
public struct ChatModePicker: View {
1118
@Binding var chatMode: String
1219
@Environment(\.colorScheme) var colorScheme
20+
@State var isAgentModeFFEnabled: Bool
21+
@State private var cancellables = Set<AnyCancellable>()
1322
var onScopeChange: (PromptTemplateScope) -> Void
1423

1524
public init(chatMode: Binding<String>, onScopeChange: @escaping (PromptTemplateScope) -> Void = { _ in }) {
1625
self._chatMode = chatMode
1726
self.onScopeChange = onScopeChange
27+
self.isAgentModeFFEnabled = FeatureFlagNotifierImpl.shared.featureFlags.agent_mode != false
28+
}
29+
30+
private func setChatMode(mode: ChatMode) {
31+
chatMode = mode.rawValue
32+
AppState.shared.setSelectedChatMode(mode.rawValue)
33+
onScopeChange(mode == .Ask ? .chatPanel : .agentPanel)
34+
NotificationCenter.default.post(
35+
name: .gitHubCopilotChatModeDidChange,
36+
object: nil
37+
)
38+
}
39+
40+
private func subscribeToFeatureFlagsDidChangeEvent() {
41+
FeatureFlagNotifierImpl.shared.featureFlagsDidChange.sink(receiveValue: { (featureFlags) in
42+
isAgentModeFFEnabled = featureFlags.agent_mode ?? true
43+
})
44+
.store(in: &cancellables)
1845
}
1946

2047
public var body: some View {
21-
HStack(spacing: -1) {
22-
ModeButton(
23-
title: "Ask",
24-
isSelected: chatMode == "Ask",
25-
activeBackground: colorScheme == .dark ? Color.white.opacity(0.25) : Color.white,
26-
activeTextColor: Color.primary,
27-
inactiveTextColor: Color.primary.opacity(0.5),
28-
action: {
29-
chatMode = "Ask"
30-
AppState.shared.setSelectedChatMode("Ask")
31-
onScopeChange(.chatPanel)
32-
NotificationCenter.default.post(
33-
name: .gitHubCopilotChatModeDidChange,
34-
object: nil
48+
VStack {
49+
if isAgentModeFFEnabled {
50+
HStack(spacing: -1) {
51+
ModeButton(
52+
title: "Ask",
53+
isSelected: chatMode == "Ask",
54+
activeBackground: colorScheme == .dark ? Color.white.opacity(0.25) : Color.white,
55+
activeTextColor: Color.primary,
56+
inactiveTextColor: Color.primary.opacity(0.5),
57+
action: {
58+
setChatMode(mode: .Ask)
59+
}
3560
)
36-
}
37-
)
38-
39-
ModeButton(
40-
title: "Agent",
41-
isSelected: chatMode == "Agent",
42-
activeBackground: Color.blue,
43-
activeTextColor: Color.white,
44-
inactiveTextColor: Color.primary.opacity(0.5),
45-
action: {
46-
chatMode = "Agent"
47-
AppState.shared.setSelectedChatMode("Agent")
48-
onScopeChange(.agentPanel)
49-
NotificationCenter.default.post(
50-
name: .gitHubCopilotChatModeDidChange,
51-
object: nil
61+
62+
ModeButton(
63+
title: "Agent",
64+
isSelected: chatMode == "Agent",
65+
activeBackground: Color.blue,
66+
activeTextColor: Color.white,
67+
inactiveTextColor: Color.primary.opacity(0.5),
68+
action: {
69+
setChatMode(mode: .Agent)
70+
}
5271
)
5372
}
54-
)
73+
.padding(1)
74+
.frame(height: 20, alignment: .topLeading)
75+
.background(.primary.opacity(0.1))
76+
.cornerRadius(5)
77+
.padding(4)
78+
.help("Set Mode")
79+
} else {
80+
EmptyView()
81+
}
82+
}
83+
.task {
84+
subscribeToFeatureFlagsDidChangeEvent()
85+
if !isAgentModeFFEnabled {
86+
setChatMode(mode: .Ask)
87+
}
88+
}
89+
.onChange(of: isAgentModeFFEnabled) { newAgentModeFFEnabled in
90+
if !newAgentModeFFEnabled {
91+
setChatMode(mode: .Ask)
92+
}
5593
}
56-
.padding(1)
57-
.frame(height: 20, alignment: .topLeading)
58-
.background(.primary.opacity(0.1))
59-
.cornerRadius(5)
60-
.padding(4)
61-
.help("Set Mode")
6294
}
6395
}

Core/Sources/HostApp/TabContainer.swift

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import LaunchAgentManager
55
import SwiftUI
66
import Toast
77
import UpdateChecker
8+
import Client
9+
import Logger
10+
import Combine
811

912
@MainActor
1013
public let hostAppStore: StoreOf<HostApp> = .init(initialState: .init(), reducer: { HostApp() })
@@ -13,6 +16,7 @@ public struct TabContainer: View {
1316
let store: StoreOf<HostApp>
1417
@ObservedObject var toastController: ToastController
1518
@State private var tabBarItems = [TabBarItem]()
19+
@State private var isAgentModeFFEnabled = true
1620
@Binding var tag: Int
1721

1822
public init() {
@@ -32,6 +36,19 @@ public struct TabContainer: View {
3236
set: { store.send(.setActiveTab($0)) }
3337
)
3438
}
39+
40+
private func updateAgentModeFeatureFlag() async {
41+
do {
42+
let service = try getService()
43+
let featureFlags = try await service.getCopilotFeatureFlags()
44+
isAgentModeFFEnabled = featureFlags?.agent_mode ?? true
45+
if hostAppStore.activeTabIndex == 2 && !isAgentModeFFEnabled {
46+
hostAppStore.send(.setActiveTab(0))
47+
}
48+
} catch {
49+
Logger.client.error("Failed to get copilot feature flags: \(error)")
50+
}
51+
}
3552

3653
public var body: some View {
3754
WithPerceptionTracking {
@@ -51,11 +68,13 @@ public struct TabContainer: View {
5168
title: "Advanced",
5269
image: "gearshape.2.fill"
5370
)
54-
MCPConfigView().tabBarItem(
55-
tag: 2,
56-
title: "MCP",
57-
image: "wrench.and.screwdriver.fill"
58-
)
71+
if isAgentModeFFEnabled {
72+
MCPConfigView().tabBarItem(
73+
tag: 2,
74+
title: "MCP",
75+
image: "wrench.and.screwdriver.fill"
76+
)
77+
}
5978
}
6079
.environment(\.tabBarTabTag, tag)
6180
.frame(minHeight: 400)
@@ -70,7 +89,16 @@ public struct TabContainer: View {
7089
}
7190
.onAppear {
7291
store.send(.appear)
92+
Task {
93+
await updateAgentModeFeatureFlag()
94+
}
7395
}
96+
.onReceive(DistributedNotificationCenter.default()
97+
.publisher(for: .gitHubCopilotFeatureFlagsDidChange)) { _ in
98+
Task {
99+
await updateAgentModeFeatureFlag()
100+
}
101+
}
74102
}
75103
}
76104
}

Core/Sources/Service/XPCService.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,15 @@ public class XPCService: NSObject, XPCServiceProtocol {
308308
}
309309
}
310310

311+
// MARK: - FeatureFlags
312+
public func getCopilotFeatureFlags(
313+
withReply reply: @escaping (Data?) -> Void
314+
) {
315+
let featureFlags = FeatureFlagNotifierImpl.shared.featureFlags
316+
let data = try? JSONEncoder().encode(featureFlags)
317+
reply(data)
318+
}
319+
311320
// MARK: - Auth
312321
public func signOutAllGitHubCopilotService() {
313322
Task { @MainActor in

ReleaseNotes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
### GitHub Copilot for Xcode 0.39.0
1+
### GitHub Copilot for Xcode 0.40.0
22

33
**🚀 Highlights**
44

55
* Performance: Fixed a freezing issue in 'Add Context' view when opening large projects.
6+
* Support disabling Agent mode when it's disabled by policy.
67

78
**🛠️ Bug Fixes**
89

Server/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"build": "webpack"
88
},
99
"dependencies": {
10-
"@github/copilot-language-server": "^1.347.0",
10+
"@github/copilot-language-server": "^1.348.0",
1111
"@xterm/addon-fit": "^0.10.0",
1212
"@xterm/xterm": "^5.5.0",
1313
"monaco-editor": "0.52.2"

Tool/Sources/GitHubCopilotService/Services/FeatureFlagNotifier.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
import Combine
22
import SwiftUI
33

4+
public extension Notification.Name {
5+
static let gitHubCopilotFeatureFlagsDidChange = Notification
6+
.Name("com.github.CopilotForXcode.CopilotFeatureFlagsDidChange")
7+
}
8+
9+
public enum ExperimentValue: Hashable, Codable {
10+
case string(String)
11+
case number(Double)
12+
case boolean(Bool)
13+
case stringArray([String])
14+
}
15+
16+
public typealias ActiveExperimentForFeatureFlags = [String: ExperimentValue]
17+
418
public struct FeatureFlags: Hashable, Codable {
519
public var rt: Bool
620
public var sn: Bool
721
public var chat: Bool
22+
public var ic: Bool
23+
public var pc: Bool
824
public var xc: Bool?
25+
public var ae: ActiveExperimentForFeatureFlags
26+
public var agent_mode: Bool?
927
}
1028

1129
public protocol FeatureFlagNotifier {
@@ -19,7 +37,7 @@ public class FeatureFlagNotifierImpl: FeatureFlagNotifier {
1937
public static let shared = FeatureFlagNotifierImpl()
2038
public var featureFlagsDidChange: PassthroughSubject<FeatureFlags, Never>
2139

22-
init(featureFlags: FeatureFlags = FeatureFlags(rt: false, sn: false, chat: true),
40+
init(featureFlags: FeatureFlags = FeatureFlags(rt: false, sn: false, chat: true, ic: true, pc: true, ae: [:]),
2341
featureFlagsDidChange: PassthroughSubject<FeatureFlags, Never> = PassthroughSubject<FeatureFlags, Never>()) {
2442
self.featureFlags = featureFlags
2543
self.featureFlagsDidChange = featureFlagsDidChange
@@ -31,6 +49,7 @@ public class FeatureFlagNotifierImpl: FeatureFlagNotifier {
3149
DispatchQueue.main.async { [weak self] in
3250
guard let self else { return }
3351
self.featureFlagsDidChange.send(self.featureFlags)
52+
DistributedNotificationCenter.default().post(name: .gitHubCopilotFeatureFlagsDidChange, object: nil)
3453
}
3554
}
3655
}

Tool/Sources/XPCShared/XPCExtensionService.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,26 @@ extension XPCExtensionService {
392392
}
393393
}
394394

395+
@XPCServiceActor
396+
public func getCopilotFeatureFlags() async throws -> FeatureFlags? {
397+
return try await withXPCServiceConnected {
398+
service, continuation in
399+
service.getCopilotFeatureFlags { data in
400+
guard let data else {
401+
continuation.resume(nil)
402+
return
403+
}
404+
405+
do {
406+
let tools = try JSONDecoder().decode(FeatureFlags.self, from: data)
407+
continuation.resume(tools)
408+
} catch {
409+
continuation.reject(error)
410+
}
411+
}
412+
}
413+
}
414+
395415
@XPCServiceActor
396416
public func signOutAllGitHubCopilotService() async throws {
397417
return try await withXPCServiceConnected {

Tool/Sources/XPCShared/XPCServiceProtocol.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public protocol XPCServiceProtocol {
2424
func getXcodeInspectorData(withReply reply: @escaping (Data?, Error?) -> Void)
2525
func getAvailableMCPServerToolsCollections(withReply reply: @escaping (Data?) -> Void)
2626
func updateMCPServerToolsStatus(tools: Data)
27+
28+
func getCopilotFeatureFlags(withReply reply: @escaping (Data?) -> Void)
2729

2830
func signOutAllGitHubCopilotService()
2931
func getXPCServiceAuthStatus(withReply reply: @escaping (Data?) -> Void)

0 commit comments

Comments
 (0)