Skip to content

Commit 957d533

Browse files
committed
nuget
1 parent 5a4ed2d commit 957d533

File tree

27 files changed

+644
-122
lines changed

27 files changed

+644
-122
lines changed

AGENTS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ Ask first:
393393
### Likes
394394

395395
- Keep regression coverage tied to real operator flows: when agent creation changes, tests should cover creating an agent, choosing a valid provider model, and sending at least one message through the resulting session path.
396+
- Keep the first provider baseline deliberately small: the operator-visible provider list should stay focused on the three real console providers, and each one needs automated create-agent plus `hello -> hello reply` smoke coverage before extra provider features are added.
397+
- Keep operator-visible provider models unseeded: supported and suggested model lists for real providers must come from live CLI metadata or explicit operator input, not from hardcoded fallback catalogs.
398+
- Keep provider, model, and runtime state honest: when a value should come from a live provider, workspace, or operator choice, do not hardcode it into production paths.
396399
- Follow the canonical MCAF tutorial when bootstrapping or upgrading the agent workflow.
397400
- Commit cohesive code-change batches promptly while debugging, especially before switching focus or starting long verification runs, so the branch state stays inspectable and pushable.
398401
- After opening or updating a PR, create a fresh working branch before continuing with the next slice of work so follow-up changes do not pile onto the already-reviewed branch.
@@ -417,6 +420,7 @@ Ask first:
417420
### Dislikes
418421

419422
- Installing stale, non-canonical, or non-`mcaf-*` skills into the repo-local agent skill directory.
423+
- Shipping fake, mock, stub, pretend, or synthetic runtime paths where the product or verification is supposed to exercise the real contract.
420424
- Moving root governance out of the repository root.
421425
- Mixing multiple `.NET` test frameworks in the active solution without a documented migration plan.
422426
- Creating auxiliary `git worktree` directories for normal PR follow-up when straightforward branch switching in the main checkout is enough.

Directory.Build.props

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
</PropertyGroup>
2222

2323
<ItemGroup>
24-
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeMetricsConfig.txt" Link="CodeMetricsConfig.txt" />
24+
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeMetricsConfig.txt"
25+
Link="CodeMetricsConfig.txt"
26+
Condition="Exists('$(MSBuildThisFileDirectory)CodeMetricsConfig.txt')" />
2527
</ItemGroup>
2628
</Project>

Directory.Packages.props

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
-->
1111
<ItemGroup>
1212
<PackageVersion Include="coverlet.collector" Version="8.0.0" />
13+
<PackageVersion Include="FluentAssertions" Version="8.9.0" />
1314
<PackageVersion Include="ManagedCode.ClaudeCodeSharpSDK.Extensions.AgentFramework" Version="1.1.0-rc4" />
1415
<PackageVersion Include="ManagedCode.ClaudeCodeSharpSDK.Extensions.AI" Version="1.1.0" />
1516
<PackageVersion Include="ManagedCode.CodexSharpSDK.Extensions.AgentFramework" Version="1.1.0-rc4" />
@@ -22,16 +23,16 @@
2223
<PackageVersion Include="NUnit" Version="4.5.1" />
2324
<PackageVersion Include="NUnit3TestAdapter" Version="6.1.0" />
2425
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.1" />
25-
<PackageVersion Include="GitHub.Copilot.SDK" Version="0.1.32" />
26+
<PackageVersion Include="GitHub.Copilot.SDK" Version="0.1.33-preview.1" />
2627
<PackageVersion Include="ManagedCode.Communication" Version="10.0.1" />
2728
<PackageVersion Include="ManagedCode.ClaudeCodeSharpSDK" Version="1.1.0" />
2829
<PackageVersion Include="ManagedCode.CodexSharpSDK" Version="1.1.0" />
2930
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
3031
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.5" />
3132
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
3233
<PackageVersion Include="Microsoft.Extensions.AI" Version="10.4.0" />
33-
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
34+
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
3435
<PackageVersion Include="Uno.UITest.Helpers" Version="1.1.0-dev.70" />
35-
<PackageVersion Include="Xamarin.UITest" Version="4.3.4" />
36+
<PackageVersion Include="Xamarin.UITest" Version="4.4.2" />
3637
</ItemGroup>
3738
</Project>

DotPilot.Core/ChatSessions/Execution/AgentRuntimeConversationFactory.cs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using GitHub.Copilot.SDK;
44
using ManagedCode.ClaudeCodeSharpSDK.Configuration;
55
using ManagedCode.ClaudeCodeSharpSDK.Extensions.AI;
6+
using ManagedCode.CodexSharpSDK.Client;
67
using ManagedCode.CodexSharpSDK.Configuration;
78
using ManagedCode.CodexSharpSDK.Extensions.AI;
89
using Microsoft.Agents.AI;
@@ -138,7 +139,7 @@ private async ValueTask<AIAgent> CreateAgentAsync(
138139
_ => CreateChatClientAgent(
139140
agentRecord,
140141
descriptor,
141-
historyProvider,
142+
ShouldUseFolderChatHistory(descriptor.ProviderKind) ? historyProvider : null,
142143
CreateChatClient(descriptor.ProviderKind, agentRecord.Name, sessionId, agentRecord.ModelName)),
143144
};
144145

@@ -173,23 +174,33 @@ private IChatClient CreateChatClient(
173174

174175
if (providerKind == AgentProviderKind.Codex)
175176
{
177+
var codexExecutablePath = ResolveExecutablePath(providerKind);
176178
return new CodexChatClient(new CodexChatClientOptions
177179
{
178-
CodexOptions = new CodexOptions(),
180+
CodexOptions = new CodexOptions
181+
{
182+
CodexExecutablePath = codexExecutablePath,
183+
},
179184
DefaultModel = modelName,
180185
DefaultThreadOptions = new CodexThreadOptions
181186
{
182187
Model = modelName,
188+
ModelReasoningEffort = ModelReasoningEffort.High,
189+
SkipGitRepoCheck = true,
183190
WorkingDirectory = ResolvePlaygroundDirectory(sessionId),
184191
},
185192
});
186193
}
187194

188195
if (providerKind == AgentProviderKind.ClaudeCode)
189196
{
197+
var claudeExecutablePath = ResolveExecutablePath(providerKind);
190198
return new ClaudeChatClient(new ClaudeChatClientOptions
191199
{
192-
ClaudeOptions = new ClaudeOptions(),
200+
ClaudeOptions = new ClaudeOptions
201+
{
202+
ClaudeExecutablePath = claudeExecutablePath,
203+
},
193204
DefaultModel = modelName,
194205
DefaultThreadOptions = new ClaudeThreadOptions
195206
{
@@ -213,7 +224,7 @@ private IChatClient CreateChatClient(
213224
private ChatClientAgent CreateChatClientAgent(
214225
AgentProfileRecord agentRecord,
215226
AgentExecutionDescriptor descriptor,
216-
FolderChatHistoryProvider historyProvider,
227+
FolderChatHistoryProvider? historyProvider,
217228
IChatClient chatClient)
218229
{
219230
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
@@ -222,14 +233,17 @@ private ChatClientAgent CreateChatClientAgent(
222233
Id = agentRecord.Id.ToString("N", CultureInfo.InvariantCulture),
223234
Name = agentRecord.Name,
224235
Description = descriptor.ProviderDisplayName,
225-
ChatHistoryProvider = historyProvider,
226236
UseProvidedChatClientAsIs = true,
227237
ChatOptions = new ChatOptions
228238
{
229239
Instructions = agentRecord.SystemPrompt,
230240
ModelId = agentRecord.ModelName,
231241
},
232242
};
243+
if (historyProvider is not null)
244+
{
245+
options.ChatHistoryProvider = historyProvider;
246+
}
233247

234248
return (ChatClientAgent)chatClient.AsAIAgent(options, loggerFactory, serviceProvider);
235249
}
@@ -241,8 +255,11 @@ private async ValueTask<AIAgent> CreateGitHubCopilotAgentAsync(
241255
CancellationToken cancellationToken)
242256
{
243257
var workingDirectory = ResolvePlaygroundDirectory(sessionId);
258+
var copilotExecutablePath = ResolveExecutablePath(AgentProviderKind.GitHubCopilot) ??
259+
AgentProviderKind.GitHubCopilot.GetCommandName();
244260
var copilotClient = new CopilotClient(new CopilotClientOptions
245261
{
262+
CliPath = copilotExecutablePath,
246263
AutoStart = false,
247264
UseStdio = true,
248265
});
@@ -253,6 +270,7 @@ private async ValueTask<AIAgent> CreateGitHubCopilotAgentAsync(
253270
new SessionConfig
254271
{
255272
Model = agentRecord.ModelName,
273+
OnPermissionRequest = PermissionHandler.ApproveAll,
256274
SystemMessage = new SystemMessageConfig
257275
{
258276
Content = agentRecord.SystemPrompt,
@@ -271,4 +289,52 @@ private string ResolvePlaygroundDirectory(SessionId sessionId)
271289
Directory.CreateDirectory(directory);
272290
return directory;
273291
}
292+
293+
private static bool ShouldUseFolderChatHistory(AgentProviderKind providerKind)
294+
{
295+
return providerKind == AgentProviderKind.Debug;
296+
}
297+
298+
private static string? ResolveExecutablePath(AgentProviderKind providerKind)
299+
{
300+
if (OperatingSystem.IsBrowser())
301+
{
302+
return null;
303+
}
304+
305+
var commandName = providerKind.GetCommandName();
306+
var searchPaths = (Environment.GetEnvironmentVariable("PATH") ?? string.Empty)
307+
.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
308+
309+
foreach (var searchPath in searchPaths)
310+
{
311+
foreach (var candidate in EnumerateCandidates(searchPath, commandName))
312+
{
313+
if (File.Exists(candidate))
314+
{
315+
return candidate;
316+
}
317+
}
318+
}
319+
320+
return null;
321+
}
322+
323+
private static IEnumerable<string> EnumerateCandidates(string searchPath, string commandName)
324+
{
325+
yield return Path.Combine(searchPath, commandName);
326+
327+
if (!OperatingSystem.IsWindows())
328+
{
329+
yield break;
330+
}
331+
332+
var pathext = (Environment.GetEnvironmentVariable("PATHEXT") ?? ".EXE;.CMD;.BAT")
333+
.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
334+
335+
foreach (var extension in pathext)
336+
{
337+
yield return Path.Combine(searchPath, commandName + extension);
338+
}
339+
}
274340
}

DotPilot.Core/DotPilot.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<PackageReference Include="ManagedCode.CodexSharpSDK.Extensions.AgentFramework" />
1515
<PackageReference Include="ManagedCode.CodexSharpSDK.Extensions.AI" />
1616
<PackageReference Include="ManagedCode.Communication" />
17+
<PackageReference Include="GitHub.Copilot.SDK" />
1718
<PackageReference Include="Microsoft.Agents.AI" />
1819
<PackageReference Include="Microsoft.Agents.AI.GitHub.Copilot" />
1920
<PackageReference Include="Microsoft.Agents.AI.Workflows" />

DotPilot.Core/Providers/Configuration/AgentProviderKindExtensions.cs

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,6 @@ namespace DotPilot.Core.Providers;
22

33
internal static class AgentProviderKindExtensions
44
{
5-
private static readonly IReadOnlyList<string> DebugModels =
6-
[
7-
"debug-echo",
8-
];
9-
10-
private static readonly IReadOnlyList<string> CodexModels =
11-
[
12-
"gpt-5",
13-
];
14-
15-
private static readonly IReadOnlyList<string> ClaudeModels =
16-
[
17-
"claude-opus-4-6",
18-
"claude-opus-4-5",
19-
"claude-sonnet-4-5",
20-
"claude-haiku-4-5",
21-
"claude-sonnet-4",
22-
];
23-
24-
private static readonly IReadOnlyList<string> CopilotModels =
25-
[
26-
"claude-sonnet-4.6",
27-
"claude-sonnet-4.5",
28-
"claude-haiku-4.5",
29-
"claude-opus-4.6",
30-
"claude-opus-4.6-fast",
31-
"claude-opus-4.5",
32-
"claude-sonnet-4",
33-
"gemini-3-pro-preview",
34-
"gpt-5.4",
35-
"gpt-5.3-codex",
36-
"gpt-5.2-codex",
37-
"gpt-5.2",
38-
"gpt-5.1-codex-max",
39-
"gpt-5.1-codex",
40-
"gpt-5.1",
41-
"gpt-5.1-codex-mini",
42-
"gpt-5-mini",
43-
"gpt-4.1",
44-
];
45-
465
public static string GetCommandName(this AgentProviderKind kind)
476
{
487
return kind switch
@@ -95,10 +54,10 @@ public static IReadOnlyList<string> GetSupportedModelNames(this AgentProviderKin
9554
{
9655
return kind switch
9756
{
98-
AgentProviderKind.Debug => DebugModels,
99-
AgentProviderKind.Codex => CodexModels,
100-
AgentProviderKind.ClaudeCode => ClaudeModels,
101-
AgentProviderKind.GitHubCopilot => CopilotModels,
57+
AgentProviderKind.Debug => [kind.GetDefaultModelName()],
58+
AgentProviderKind.Codex => [],
59+
AgentProviderKind.ClaudeCode => [],
60+
AgentProviderKind.GitHubCopilot => [],
10261
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),
10362
};
10463
}

DotPilot.Core/Providers/Services/AgentProviderStatusSnapshotReader.cs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ private static async ValueTask<ProviderStatusProbeResult> BuildProviderStatusAsy
7171
ProviderPreferenceRecord preference,
7272
CancellationToken cancellationToken)
7373
{
74+
var isBuiltIn = providerKind.IsBuiltIn();
7475
var commandName = providerKind.GetCommandName();
7576
var displayName = providerKind.GetDisplayName();
76-
var defaultModelName = providerKind.GetDefaultModelName();
77+
var defaultModelName = isBuiltIn ? providerKind.GetDefaultModelName() : string.Empty;
7778
var installCommand = providerKind.GetInstallCommand();
78-
var fallbackModels = providerKind.GetSupportedModelNames();
79-
var isBuiltIn = providerKind.IsBuiltIn();
79+
var fallbackModels = isBuiltIn ? providerKind.GetSupportedModelNames() : [];
8080
var providerId = AgentSessionDeterministicIdentity.CreateProviderId(commandName);
8181
var actions = new List<ProviderActionDescriptor>();
8282
var details = new List<ProviderDetailDescriptor>();
@@ -121,7 +121,10 @@ private static async ValueTask<ProviderStatusProbeResult> BuildProviderStatusAsy
121121
}
122122

123123
actions.Add(new ProviderActionDescriptor("Open CLI", "CLI detected on PATH.", $"{commandName} --version"));
124-
suggestedModelName = ResolveSuggestedModel(defaultModelName, metadata.SuggestedModelName);
124+
suggestedModelName = ResolveSuggestedModel(
125+
defaultModelName,
126+
metadata.SuggestedModelName,
127+
metadata.SupportedModels);
125128
supportedModelNames = ResolveSupportedModels(
126129
defaultModelName,
127130
suggestedModelName,
@@ -182,11 +185,20 @@ private static ProviderCliMetadataSnapshot CreateCodexSnapshot(CodexCliMetadataS
182185
metadata?.AvailableModels ?? []);
183186
}
184187

185-
private static string ResolveSuggestedModel(string defaultModelName, string? suggestedModelName)
188+
private static string ResolveSuggestedModel(
189+
string defaultModelName,
190+
string? suggestedModelName,
191+
IReadOnlyList<string> discoveredModels)
186192
{
187-
return string.IsNullOrWhiteSpace(suggestedModelName)
193+
if (!string.IsNullOrWhiteSpace(suggestedModelName))
194+
{
195+
return suggestedModelName;
196+
}
197+
198+
var discoveredModel = discoveredModels.FirstOrDefault(static model => !string.IsNullOrWhiteSpace(model));
199+
return string.IsNullOrWhiteSpace(discoveredModel)
188200
? defaultModelName
189-
: suggestedModelName;
201+
: discoveredModel;
190202
}
191203

192204
private static bool LooksLikeInstalledVersion(string? installedVersion, string commandName)
@@ -244,7 +256,10 @@ private static List<ProviderDetailDescriptor> CreateProviderDetails(
244256
details.Add(new ProviderDetailDescriptor("Installed version", installedVersion));
245257
}
246258

247-
details.Add(new ProviderDetailDescriptor("Suggested model", suggestedModelName));
259+
if (!string.IsNullOrWhiteSpace(suggestedModelName))
260+
{
261+
details.Add(new ProviderDetailDescriptor("Suggested model", suggestedModelName));
262+
}
248263

249264
var supportedModels = FormatSupportedModels(supportedModelNames);
250265
if (!string.IsNullOrWhiteSpace(supportedModels))

DotPilot.Tests/AgentBuilder/ViewModels/AgentBuilderModelTests.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,11 @@ public async Task BuildManuallyWithoutEnabledRealProviderFallsBackToTheFirstProv
107107
await model.BuildManually(CancellationToken.None);
108108

109109
(await model.BuilderProviderDisplayName).Should().Be("Codex");
110-
(await model.BuilderSuggestedModelName).Should().Be("gpt-5");
111-
(await model.BuilderModelHelperText).Should().Be("Choose one of the supported models for this provider. Suggested: gpt-5.");
110+
(await model.BuilderSuggestedModelName).Should().BeEmpty();
111+
(await model.BuilderModelHelperText).Should().Be("Select an enabled provider to load its supported models.");
112+
(await model.BuilderHasSupportedModels).Should().BeFalse();
112113
(await model.BuilderCanCreateAgent).Should().BeFalse();
113-
(await model.ModelName).Should().Be("gpt-5");
114+
(await model.ModelName).Should().BeNullOrEmpty();
114115
}
115116

116117
[Test]
@@ -121,7 +122,7 @@ public async Task HandleSelectedProviderChangedUpdatesModelSuggestionToTheChosen
121122
var model = ActivatorUtilities.CreateInstance<AgentBuilderModel>(fixture.Provider);
122123

123124
await model.BuildManually(CancellationToken.None);
124-
(await model.ModelName).Should().Be("gpt-5");
125+
(await model.ModelName).Should().BeNullOrEmpty();
125126
await model.SelectedProvider.UpdateAsync(
126127
_ => new AgentProviderOption(
127128
AgentProviderKind.Codex,
@@ -176,7 +177,7 @@ await model.HandleSelectedProviderChanged(
176177
AgentProviderKind.GitHubCopilot,
177178
"GitHub Copilot",
178179
"copilot",
179-
"GitHub Copilot profile authoring is available.",
180+
"GitHub Copilot CLI is ready for local desktop execution.",
180181
"claude-opus-4.6",
181182
["claude-opus-4.6", "gpt-5"],
182183
"1.0.3",
@@ -242,7 +243,7 @@ await model.HandleProviderSelectionChanged(
242243
AgentProviderKind.ClaudeCode,
243244
"Claude Code",
244245
"claude",
245-
"Claude Code profile authoring is available.",
246+
"Claude Code CLI is ready for local desktop execution.",
246247
"claude-opus-4-6",
247248
["claude-opus-4-6", "claude-sonnet-4-5"],
248249
"2.0.75",
@@ -394,6 +395,7 @@ private static async Task<TestFixture> CreateFixtureAsync()
394395
});
395396
services.AddSingleton<WorkspaceProjectionNotifier>();
396397
services.AddSingleton<ShellNavigationNotifier>();
398+
services.AddSingleton<SessionSelectionNotifier>();
397399

398400
var provider = services.BuildServiceProvider();
399401
var workspaceState = provider.GetRequiredService<IAgentWorkspaceState>();

0 commit comments

Comments
 (0)