diff --git a/README.md b/README.md
index 52a5560..b9a727e 100644
--- a/README.md
+++ b/README.md
@@ -222,9 +222,9 @@ Passed as `SessionConfig.ReasoningEffort`. Not all models respect this setting.
### Hooks and User-Input Handlers
-The Copilot SDK provides `OnPreToolUse`, `OnPostToolUse`, and `OnUserInputRequest` callbacks on `SessionConfig`. Coralph intentionally does not implement them:
+The Copilot SDK exposes tool hooks through `SessionConfig.Hooks` and user-input callbacks through `SessionConfig.OnUserInputRequest`. Coralph intentionally does not configure them:
-- **Tool-use hooks** are unnecessary because `CopilotSessionEventRouter` already captures all tool events via `session.On()`.
+- **Tool-use hooks** are unnecessary because `CopilotSessionEventRouter` already captures all tool events via `SessionConfig.OnEvent`.
- **User-input handlers** are inappropriate for an unattended loop; models should not prompt for interactive input during an automated run.
### OpenAI-Compatible Providers
diff --git a/docs/architecture.md b/docs/architecture.md
index 945747c..27bed55 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -55,7 +55,7 @@ flowchart TD
end
subgraph External["External Dependencies"]
- CopilotSDK["GitHub.Copilot.SDK 0.1.32
AI Runtime"]
+ CopilotSDK["GitHub.Copilot.SDK 1.0.0
AI Runtime"]
Hex1b["Hex1b 0.83.0
TUI Framework"]
ConfigJson["Microsoft.Extensions.Configuration.Json 8.0.0
JSON Config Loading"]
SpectreConsole["Spectre.Console 0.49.1
Rich Terminal UI"]
@@ -169,7 +169,7 @@ sequenceDiagram
participant Config as ConfigurationService
participant Runner as LoopOrchestrator
participant Tasks as TaskBacklog
- participant SDK as GitHub.Copilot.SDK
+ participant SDK as GitHub.Copilot
participant Output as ConsoleOutput
participant Git as GitService
participant Files as File System
@@ -194,7 +194,7 @@ sequenceDiagram
| Package | Version | Purpose |
|---------|---------|---------|
-| **GitHub.Copilot.SDK** | 0.1.32 | AI runtime for Copilot integration |
+| **GitHub.Copilot.SDK** | 1.0.0 | AI runtime for Copilot integration |
| **Hex1b** | 0.83.0 | Interactive split-pane TUI rendering |
| **Microsoft.Extensions.Configuration.Json** | 8.0.0 | Load configuration from JSON files |
| **Microsoft.Extensions.Options.ConfigurationExtensions** | 8.0.0 | Bind configuration to options classes |
@@ -213,7 +213,7 @@ sequenceDiagram
## Key Design Decisions
-1. **Streaming Architecture**: Uses event-based streaming from GitHub.Copilot.SDK for real-time output
+1. **Streaming Architecture**: Uses event-based streaming from GitHub.Copilot for real-time output
2. **Pluggable Output Backends**: Runtime logic writes to a single facade; rendering is switched between classic and TUI modes
3. **Stream Compatibility Guardrail**: `--stream-events` forces classic output to preserve JSONL integrations
4. **Tool Extensibility**: Custom AI tools exposed via `AIFunctionFactory.Create()` pattern
diff --git a/src/Coralph.Tests/CopilotClientFactoryTests.cs b/src/Coralph.Tests/CopilotClientFactoryTests.cs
index c72cba8..808d08d 100644
--- a/src/Coralph.Tests/CopilotClientFactoryTests.cs
+++ b/src/Coralph.Tests/CopilotClientFactoryTests.cs
@@ -1,8 +1,11 @@
using Coralph;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
+using GitHub.Copilot.Rpc;
namespace Coralph.Tests;
+#pragma warning disable GHCP001
+
public class CopilotClientFactoryTests
{
[Fact]
@@ -20,10 +23,11 @@ public void CreateClientOptions_WithExplicitValues_CopiesKnownFields()
var clientOptions = CopilotClientFactory.CreateClientOptions(options);
- Assert.False(string.IsNullOrWhiteSpace(clientOptions.Cwd));
- Assert.True(Path.IsPathRooted(clientOptions.Cwd));
- Assert.Equal("/usr/local/bin/copilot", clientOptions.CliPath);
- Assert.Equal("http://localhost:3000", clientOptions.CliUrl);
+ Assert.False(string.IsNullOrWhiteSpace(clientOptions.WorkingDirectory));
+ Assert.True(Path.IsPathRooted(clientOptions.WorkingDirectory));
+ var connection = Assert.IsType(clientOptions.Connection);
+ Assert.Equal("http://localhost:3000", connection.Url);
+ Assert.Null(connection.ConnectionToken);
Assert.Equal("ghp_test_token", clientOptions.GitHubToken);
Assert.NotNull(clientOptions.Telemetry);
Assert.Equal("http://localhost:4318", clientOptions.Telemetry!.OtlpEndpoint);
@@ -31,15 +35,27 @@ public void CreateClientOptions_WithExplicitValues_CopiesKnownFields()
Assert.True(clientOptions.Telemetry.CaptureContent);
}
+ [Fact]
+ public void CreateClientOptions_WithCliPath_UsesStdioConnection()
+ {
+ var clientOptions = CopilotClientFactory.CreateClientOptions(new LoopOptions
+ {
+ CliPath = "/usr/local/bin/copilot"
+ });
+
+ var connection = Assert.IsType(clientOptions.Connection);
+ Assert.Equal("/usr/local/bin/copilot", connection.Path);
+ Assert.Null(connection.Args);
+ }
+
[Fact]
public void CreateClientOptions_WithNullOptionalValues_LeavesThemUnset()
{
var clientOptions = CopilotClientFactory.CreateClientOptions(new LoopOptions());
- Assert.False(string.IsNullOrWhiteSpace(clientOptions.Cwd));
- Assert.True(Path.IsPathRooted(clientOptions.Cwd));
- Assert.Null(clientOptions.CliPath);
- Assert.Null(clientOptions.CliUrl);
+ Assert.False(string.IsNullOrWhiteSpace(clientOptions.WorkingDirectory));
+ Assert.True(Path.IsPathRooted(clientOptions.WorkingDirectory));
+ Assert.Null(clientOptions.Connection);
Assert.Null(clientOptions.GitHubToken);
Assert.Null(clientOptions.Telemetry);
}
@@ -48,7 +64,7 @@ public void CreateClientOptions_WithNullOptionalValues_LeavesThemUnset()
public void CreateSessionConfig_WithToolsAndProvider_CopiesExpectedFields()
{
var tools = CustomTools.GetDefaultTools("issues.json", "progress.txt", "generated_tasks.json");
- SessionEventHandler onEvent = _ => { };
+ Action onEvent = _ => { };
var options = new LoopOptions
{
Model = "GPT-5.1-Codex",
@@ -77,10 +93,10 @@ public void CreateSessionConfig_WithToolsAndProvider_CopiesExpectedFields()
Assert.NotNull(config.SystemMessage);
Assert.Equal(SystemMessageMode.Customize, config.SystemMessage!.Mode);
Assert.NotNull(config.SystemMessage.Sections);
- Assert.Contains(SystemPromptSections.Tone, config.SystemMessage.Sections.Keys);
- Assert.Contains(SystemPromptSections.Guidelines, config.SystemMessage.Sections.Keys);
- Assert.Contains(SystemPromptSections.ToolInstructions, config.SystemMessage.Sections.Keys);
- Assert.Contains(SystemPromptSections.Safety, config.SystemMessage.Sections.Keys);
+ Assert.Contains(SystemMessageSection.Tone, config.SystemMessage.Sections.Keys);
+ Assert.Contains(SystemMessageSection.Guidelines, config.SystemMessage.Sections.Keys);
+ Assert.Contains(SystemMessageSection.ToolInstructions, config.SystemMessage.Sections.Keys);
+ Assert.Contains(SystemMessageSection.Safety, config.SystemMessage.Sections.Keys);
}
[Fact]
diff --git a/src/Coralph.Tests/CopilotModelDiscoveryTests.cs b/src/Coralph.Tests/CopilotModelDiscoveryTests.cs
index 38983c9..87c1839 100644
--- a/src/Coralph.Tests/CopilotModelDiscoveryTests.cs
+++ b/src/Coralph.Tests/CopilotModelDiscoveryTests.cs
@@ -1,6 +1,6 @@
using System.Text.Json;
using Coralph;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
using Spectre.Console.Testing;
namespace Coralph.Tests;
diff --git a/src/Coralph.Tests/CopilotSessionEventRouterTests.cs b/src/Coralph.Tests/CopilotSessionEventRouterTests.cs
index 40236d8..45a77a1 100644
--- a/src/Coralph.Tests/CopilotSessionEventRouterTests.cs
+++ b/src/Coralph.Tests/CopilotSessionEventRouterTests.cs
@@ -3,7 +3,7 @@
using System.Text.Json;
using Coralph;
using Coralph.Ui;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
using Spectre.Console;
using Spectre.Console.Testing;
@@ -107,10 +107,10 @@ public async Task HandleEvent_ToolExecutionComplete_ForReportIntent_SuppressesCo
{
ToolCallId = "tool-1",
ToolName = "report_intent",
- Arguments = new Dictionary
+ Arguments = JsonSerializer.SerializeToElement(new Dictionary
{
["intent"] = "test"
- }
+ })
}
});
router.HandleEvent(new ToolExecutionCompleteEvent
@@ -344,10 +344,10 @@ public async Task HandleEvent_AssistantMessage_WithToolRequests_EmitsStableToolR
ToolCallId = "tool-call-1",
Name = "list_open_issues",
Type = AssistantMessageToolRequestType.Function,
- Arguments = new Dictionary
+ Arguments = JsonSerializer.SerializeToElement(new Dictionary
{
["includeClosed"] = false
- },
+ }),
ToolTitle = "List issues",
McpServerName = "coralph",
IntentionSummary = "Read current issue state"
@@ -364,7 +364,7 @@ public async Task HandleEvent_AssistantMessage_WithToolRequests_EmitsStableToolR
Assert.Equal("tool-call-1", request.GetProperty("toolCallId").GetString());
Assert.Equal("list_open_issues", request.GetProperty("name").GetString());
- Assert.Equal("Function", request.GetProperty("type").GetString());
+ Assert.Equal("function", request.GetProperty("type").GetString());
Assert.False(request.GetProperty("arguments").GetProperty("includeClosed").GetBoolean());
Assert.Equal("List issues", request.GetProperty("toolTitle").GetString());
Assert.Equal("coralph", request.GetProperty("mcpServerName").GetString());
diff --git a/src/Coralph.Tests/CopilotSystemMessageFactoryTests.cs b/src/Coralph.Tests/CopilotSystemMessageFactoryTests.cs
index 33d46a0..9d6a9b8 100644
--- a/src/Coralph.Tests/CopilotSystemMessageFactoryTests.cs
+++ b/src/Coralph.Tests/CopilotSystemMessageFactoryTests.cs
@@ -1,5 +1,5 @@
using Coralph;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
namespace Coralph.Tests;
@@ -12,10 +12,10 @@ public void Create_UsesCustomizeModeWithExpectedSections()
Assert.Equal(SystemMessageMode.Customize, config.Mode);
Assert.NotNull(config.Sections);
- Assert.Contains(SystemPromptSections.Tone, config.Sections.Keys);
- Assert.Contains(SystemPromptSections.Guidelines, config.Sections.Keys);
- Assert.Contains(SystemPromptSections.ToolInstructions, config.Sections.Keys);
- Assert.Contains(SystemPromptSections.Safety, config.Sections.Keys);
+ Assert.Contains(SystemMessageSection.Tone, config.Sections.Keys);
+ Assert.Contains(SystemMessageSection.Guidelines, config.Sections.Keys);
+ Assert.Contains(SystemMessageSection.ToolInstructions, config.Sections.Keys);
+ Assert.Contains(SystemMessageSection.Safety, config.Sections.Keys);
}
[Fact]
@@ -24,7 +24,7 @@ public void Create_WithDryRun_AppendsDryRunSafetyInstruction()
var config = CopilotSystemMessageFactory.Create(new LoopOptions { DryRun = true });
Assert.NotNull(config.Sections);
- Assert.True(config.Sections.TryGetValue(SystemPromptSections.Safety, out var safety));
+ Assert.True(config.Sections.TryGetValue(SystemMessageSection.Safety, out var safety));
Assert.NotNull(safety);
Assert.Contains("Dry-run mode is enabled", safety!.Content);
}
diff --git a/src/Coralph.Tests/PermissionPolicyTests.cs b/src/Coralph.Tests/PermissionPolicyTests.cs
index 4e38137..ad3fa80 100644
--- a/src/Coralph.Tests/PermissionPolicyTests.cs
+++ b/src/Coralph.Tests/PermissionPolicyTests.cs
@@ -1,8 +1,11 @@
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
+using GitHub.Copilot.Rpc;
using Xunit;
namespace Coralph.Tests;
+#pragma warning disable GHCP001
+
public sealed class PermissionPolicyTests
{
[Fact]
@@ -262,14 +265,15 @@ private static PermissionRequest CreateRequest(string kind)
return new PermissionRequest { Kind = kind };
}
- private static void AssertApproved(PermissionRequestResult result)
+ private static void AssertApproved(PermissionDecision result)
{
- Assert.Equal(PermissionRequestResultKind.Approved, result.Kind);
+ Assert.IsType(result);
}
- private static void AssertRejected(PermissionRequestResult result)
+ private static void AssertRejected(PermissionDecision result)
{
- Assert.Equal(PermissionRequestResultKind.Rejected, result.Kind);
+ var rejected = Assert.IsType(result);
+ Assert.Equal("Rejected by Coralph permission policy.", rejected.Feedback);
}
private static PermissionInvocation CreateInvocation()
diff --git a/src/Coralph.Tests/packages.lock.json b/src/Coralph.Tests/packages.lock.json
index 72cf5db..624909e 100644
--- a/src/Coralph.Tests/packages.lock.json
+++ b/src/Coralph.Tests/packages.lock.json
@@ -41,12 +41,11 @@
},
"GitHub.Copilot.SDK": {
"type": "Transitive",
- "resolved": "0.3.0",
- "contentHash": "zTmlbLWsmmZT/v/vbvP66d73t3sJ92gSavCe+52MtD1jwOJsGv5ExL8CY/jxA9/3GQt50LZs3p+ntjBpe/+Rvw==",
+ "resolved": "1.0.0",
+ "contentHash": "hAeuf54OVdFxEtf0AlMgiQBNpeMy7rSbr6Y1bJWjeln8U71D5QEryje8auVQATt3l5ubdbmWGFnO9VOnBhWCfw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.2.0",
- "Microsoft.Extensions.Logging.Abstractions": "10.0.2",
- "StreamJsonRpc": "2.24.84"
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.2"
}
},
"Hex1b": {
@@ -58,20 +57,6 @@
"QRCoder": "1.7.0"
}
},
- "MessagePack": {
- "type": "Transitive",
- "resolved": "2.5.198",
- "contentHash": "ul2rGISMatBL4AbXTteEml4xtOsKF8yClEkS0rnrMyFs6Cu3KTbdZwOXrcbEUjGFIDiCwHuYIGyS55FeiaYzBw==",
- "dependencies": {
- "MessagePack.Annotations": "2.5.198",
- "Microsoft.NET.StringTools": "17.6.3"
- }
- },
- "MessagePack.Annotations": {
- "type": "Transitive",
- "resolved": "2.5.198",
- "contentHash": "3U9OvqQGTra+Mz1k1zfNAScSdNHobnqtQ51qdMGUZppkNDZJl0X/igq6Qz5zDBLEZoYqZrFtZwFx6wBJHHI8BA=="
- },
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "17.14.1",
@@ -192,11 +177,6 @@
"resolved": "8.0.0",
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
},
- "Microsoft.NET.StringTools": {
- "type": "Transitive",
- "resolved": "18.0.2",
- "contentHash": "cTZw3GHkAlqZACYGeQT3niS3UfVQ8CH0O5+zUdhxstrg1Z8Q2ViXYFKjSxHmEXTX85mrOT/QnHZOeQhhSsIrkQ=="
- },
"Microsoft.TestPlatform.ObjectModel": {
"type": "Transitive",
"resolved": "17.14.1",
@@ -211,53 +191,16 @@
"Newtonsoft.Json": "13.0.3"
}
},
- "Microsoft.VisualStudio.Threading.Only": {
- "type": "Transitive",
- "resolved": "17.14.15",
- "contentHash": "NqONyw1RXyj9P3k5e1uU2k9kc1ptwuU5NJQzG+MPq7vQVHUzBY8HLuJf/N2Rw5H/myD96CVxziDxmjawPuzntw==",
- "dependencies": {
- "Microsoft.VisualStudio.Validation": "17.8.8"
- }
- },
- "Microsoft.VisualStudio.Validation": {
- "type": "Transitive",
- "resolved": "17.13.22",
- "contentHash": "fC20ITOxlUpGQ0ltAxITQbeFxwuB6OjLT3lByC4+/Gm46o/Mwv9hlIVrBhiUhnqdQzUUvr4EHkdiYe7WOSMLmw=="
- },
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
},
- "Nerdbank.MessagePack": {
- "type": "Transitive",
- "resolved": "1.0.2",
- "contentHash": "mUcRO0trgc85qKPR360ybSvZTbm3nlSr1aK0JlKVWC5iC9OYxVLL1AbJxaW2Pbj4EQNCP8dKiz/SIlFPy67Ixw==",
- "dependencies": {
- "Microsoft.NET.StringTools": "18.0.2",
- "Microsoft.VisualStudio.Validation": "17.13.22",
- "PolyType": "1.0.0"
- }
- },
- "Nerdbank.Streams": {
- "type": "Transitive",
- "resolved": "2.13.16",
- "contentHash": "GjifQ5M4IpQWjGNNbIrsj8M/M7ESb2328OeoGeqxk5rOJiuaAJ5zFc6CyqB+5UWKr1KEGK23hKoxA+z1gooAKA==",
- "dependencies": {
- "Microsoft.VisualStudio.Threading.Only": "17.13.61",
- "Microsoft.VisualStudio.Validation": "17.8.8"
- }
- },
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "13.0.3",
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
},
- "PolyType": {
- "type": "Transitive",
- "resolved": "1.0.0",
- "contentHash": "q8vTQfDoKa/vCNMT+TvKbzsl+Z/s4k/qaP8WQGvNDIILkxKhP+N3NbFAOiglQljNmAj/ei51wO9jwcAI9jhtHw=="
- },
"QRCoder": {
"type": "Transitive",
"resolved": "1.7.0",
@@ -308,19 +251,6 @@
"Spectre.Console": "0.49.1"
}
},
- "StreamJsonRpc": {
- "type": "Transitive",
- "resolved": "2.24.84",
- "contentHash": "7PtQlA7wYFBzOrcuALWGs1uPJI712KEYOMZXxxSKel9C0y5TPyjxXLkpw7d4O175rUKNbLHHbB89fPzMC2SLPg==",
- "dependencies": {
- "MessagePack": "2.5.198",
- "Microsoft.VisualStudio.Threading.Only": "17.14.15",
- "Microsoft.VisualStudio.Validation": "17.13.22",
- "Nerdbank.MessagePack": "1.0.2",
- "Nerdbank.Streams": "2.13.16",
- "Newtonsoft.Json": "13.0.3"
- }
- },
"System.CommandLine": {
"type": "Transitive",
"resolved": "2.0.0-beta4.22272.1",
@@ -377,7 +307,7 @@
"coralph": {
"type": "Project",
"dependencies": {
- "GitHub.Copilot.SDK": "[0.3.0, )",
+ "GitHub.Copilot.SDK": "[1.0.0, )",
"Hex1b": "[0.83.0, )",
"Microsoft.Extensions.Configuration.Json": "[8.0.0, )",
"Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )",
diff --git a/src/Coralph/CopilotClientFactory.cs b/src/Coralph/CopilotClientFactory.cs
index ac1125a..ddc3237 100644
--- a/src/Coralph/CopilotClientFactory.cs
+++ b/src/Coralph/CopilotClientFactory.cs
@@ -1,6 +1,9 @@
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
+using GitHub.Copilot.Rpc;
using Microsoft.Extensions.AI;
+#pragma warning disable GHCP001
+
namespace Coralph;
internal static class CopilotClientFactory
@@ -9,18 +12,18 @@ internal static CopilotClientOptions CreateClientOptions(LoopOptions options)
{
var clientOptions = new CopilotClientOptions
{
- Cwd = Directory.GetCurrentDirectory(),
+ WorkingDirectory = Directory.GetCurrentDirectory(),
Telemetry = CreateTelemetryConfig(options)
};
if (!string.IsNullOrWhiteSpace(options.CliPath))
{
- clientOptions.CliPath = options.CliPath;
+ clientOptions.Connection = RuntimeConnection.ForStdio(options.CliPath, args: null);
}
if (!string.IsNullOrWhiteSpace(options.CliUrl))
{
- clientOptions.CliUrl = options.CliUrl;
+ clientOptions.Connection = RuntimeConnection.ForUri(options.CliUrl, connectionToken: null);
}
if (!string.IsNullOrWhiteSpace(options.CopilotToken))
@@ -34,8 +37,8 @@ internal static CopilotClientOptions CreateClientOptions(LoopOptions options)
internal static SessionConfig CreateSessionConfig(
LoopOptions options,
AIFunction[] tools,
- PermissionRequestHandler onPermissionRequest,
- SessionEventHandler? onEvent = null)
+ Func> onPermissionRequest,
+ Action? onEvent = null)
{
return new SessionConfig
{
diff --git a/src/Coralph/CopilotModelDiscovery.cs b/src/Coralph/CopilotModelDiscovery.cs
index cde2e86..0478d5b 100644
--- a/src/Coralph/CopilotModelDiscovery.cs
+++ b/src/Coralph/CopilotModelDiscovery.cs
@@ -1,5 +1,5 @@
using System.Text.Json;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
using Serilog;
namespace Coralph;
@@ -183,5 +183,5 @@ private sealed record ModelPolicyDto(
string? Terms);
private sealed record ModelBillingDto(
- double Multiplier);
+ double? Multiplier);
}
diff --git a/src/Coralph/CopilotRunner.cs b/src/Coralph/CopilotRunner.cs
index 762acf7..49cfbd6 100644
--- a/src/Coralph/CopilotRunner.cs
+++ b/src/Coralph/CopilotRunner.cs
@@ -1,6 +1,6 @@
using System.Linq;
using Serilog;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
namespace Coralph;
diff --git a/src/Coralph/CopilotSessionEventRouter.cs b/src/Coralph/CopilotSessionEventRouter.cs
index 82c15d2..05e1d3b 100644
--- a/src/Coralph/CopilotSessionEventRouter.cs
+++ b/src/Coralph/CopilotSessionEventRouter.cs
@@ -1,9 +1,11 @@
using System.Text;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
using Serilog;
namespace Coralph;
+#pragma warning disable GHCP001
+
internal sealed class CopilotSessionEventRouter(
LoopOptions opt,
EventStreamWriter? eventStream,
@@ -292,7 +294,7 @@ internal void HandleEvent(SessionEvent evt)
["cost"] = assistantUsage.Data.Cost,
["duration"] = assistantUsage.Data.Duration,
["initiator"] = assistantUsage.Data.Initiator,
- ["quotaSnapshots"] = assistantUsage.Data.QuotaSnapshots
+ ["quotaSnapshots"] = TryGetPropertyValue(assistantUsage.Data, "QuotaSnapshots")
}, state: state);
break;
case SystemNotificationEvent notification:
@@ -480,6 +482,11 @@ private void Emit(
};
}
+ private static object? TryGetPropertyValue(object target, string propertyName)
+ {
+ return target.GetType().GetProperty(propertyName)?.GetValue(target);
+ }
+
private string ResolveAssistantMessageId(TurnState state, string? messageId)
{
if (!string.IsNullOrWhiteSpace(messageId))
diff --git a/src/Coralph/CopilotSessionRunner.cs b/src/Coralph/CopilotSessionRunner.cs
index 91e01b9..afbff4b 100644
--- a/src/Coralph/CopilotSessionRunner.cs
+++ b/src/Coralph/CopilotSessionRunner.cs
@@ -1,4 +1,4 @@
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
using Serilog;
namespace Coralph;
diff --git a/src/Coralph/CopilotSystemMessageFactory.cs b/src/Coralph/CopilotSystemMessageFactory.cs
index 655b6fc..e331908 100644
--- a/src/Coralph/CopilotSystemMessageFactory.cs
+++ b/src/Coralph/CopilotSystemMessageFactory.cs
@@ -1,4 +1,4 @@
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
namespace Coralph;
@@ -15,14 +15,14 @@ internal static SystemMessageConfig Create(LoopOptions options)
return new SystemMessageConfig
{
Mode = SystemMessageMode.Customize,
- Sections = new Dictionary
+ Sections = new Dictionary
{
- [SystemPromptSections.Tone] = new()
+ [SystemMessageSection.Tone] = new()
{
Action = SectionOverrideAction.Append,
Content = "\n- Be concise, direct, and execution-focused."
},
- [SystemPromptSections.Guidelines] = new()
+ [SystemMessageSection.Guidelines] = new()
{
Action = SectionOverrideAction.Append,
Content =
@@ -30,14 +30,14 @@ internal static SystemMessageConfig Create(LoopOptions options)
"\n- Match the repository's existing conventions, libraries, and coding style." +
"\n- Prefer small, targeted changes that preserve existing behavior unless the task requires broader edits."
},
- [SystemPromptSections.ToolInstructions] = new()
+ [SystemMessageSection.ToolInstructions] = new()
{
Action = SectionOverrideAction.Append,
Content =
"\n- Prefer Coralph's internal read-only tools and available repository context before broader exploration when they can answer the question." +
"\n- Do not stop to ask for interactive input during a loop run."
},
- [SystemPromptSections.Safety] = new()
+ [SystemMessageSection.Safety] = new()
{
Action = SectionOverrideAction.Append,
Content =
diff --git a/src/Coralph/Coralph.csproj b/src/Coralph/Coralph.csproj
index e3730a3..a7bc4b8 100644
--- a/src/Coralph/Coralph.csproj
+++ b/src/Coralph/Coralph.csproj
@@ -43,7 +43,7 @@
-
+
diff --git a/src/Coralph/PermissionPolicy.cs b/src/Coralph/PermissionPolicy.cs
index 7594893..72e25c2 100644
--- a/src/Coralph/PermissionPolicy.cs
+++ b/src/Coralph/PermissionPolicy.cs
@@ -1,9 +1,12 @@
using System.Collections;
+using GitHub.Copilot;
+using GitHub.Copilot.Rpc;
using Serilog;
-using GitHub.Copilot.SDK;
namespace Coralph;
+#pragma warning disable GHCP001
+
///
/// Coralph permission policy for Copilot tool requests.
///
@@ -54,7 +57,7 @@ internal PermissionPolicy(LoopOptions opt, EventStreamWriter? eventStream)
_eventStream = eventStream;
}
- internal Task HandleAsync(PermissionRequest request, PermissionInvocation invocation)
+ internal Task HandleAsync(PermissionRequest request, PermissionInvocation invocation)
{
var kind = request.Kind;
if (string.IsNullOrWhiteSpace(kind))
@@ -66,50 +69,49 @@ internal Task HandleAsync(PermissionRequest request, Pe
var candidates = BuildCandidates(kind, toolName);
var decision = EvaluateDecision(candidates, out var matchedRule);
- var resultKind = decision == PermissionDecision.Allow
- ? PermissionRequestResultKind.Approved
- : PermissionRequestResultKind.Rejected;
EmitDecision(kind, toolName, candidates, decision, matchedRule);
- return Task.FromResult(new PermissionRequestResult { Kind = resultKind });
+ return Task.FromResult(decision == ToolPermissionDecision.Allow
+ ? PermissionDecision.ApproveOnce()
+ : PermissionDecision.Reject("Rejected by Coralph permission policy."));
}
- private PermissionDecision EvaluateDecision(IReadOnlyList candidates, out string? matchedRule)
+ private ToolPermissionDecision EvaluateDecision(IReadOnlyList candidates, out string? matchedRule)
{
if (MatchesRuleSet(_userDeny, candidates, out matchedRule))
{
- return PermissionDecision.Deny;
+ return ToolPermissionDecision.Deny;
}
if (MatchesRuleSet(_userAllow, candidates, out matchedRule))
{
- return PermissionDecision.Allow;
+ return ToolPermissionDecision.Allow;
}
if (MatchesRuleSet(DefaultDangerousRules, candidates, out matchedRule))
{
- return PermissionDecision.Deny;
+ return ToolPermissionDecision.Deny;
}
if (_hasExplicitAllowList)
{
matchedRule = null;
- return PermissionDecision.Deny;
+ return ToolPermissionDecision.Deny;
}
matchedRule = null;
- return PermissionDecision.Allow;
+ return ToolPermissionDecision.Allow;
}
private void EmitDecision(
string? kind,
string? toolName,
IReadOnlyList candidates,
- PermissionDecision decision,
+ ToolPermissionDecision decision,
string? matchedRule)
{
- if (decision == PermissionDecision.Deny)
+ if (decision == ToolPermissionDecision.Deny)
{
Log.Warning(
"Permission denied (Kind={Kind}, Tool={Tool}, Rule={Rule})",
@@ -123,7 +125,7 @@ private void EmitDecision(
["kind"] = kind,
["toolName"] = toolName,
["candidates"] = candidates,
- ["decision"] = decision == PermissionDecision.Allow ? "approved" : "denied",
+ ["decision"] = decision == ToolPermissionDecision.Allow ? "approved" : "denied",
["matchedRule"] = matchedRule
});
}
@@ -219,6 +221,12 @@ private static HashSet NormalizeEntries(IEnumerable? entries)
private static string? TryGetToolName(PermissionRequest request)
{
+ var toolNameProperty = TryGetStringProperty(request, "ToolName");
+ if (!string.IsNullOrWhiteSpace(toolNameProperty))
+ {
+ return toolNameProperty;
+ }
+
var extra = TryGetExtraDictionary(request);
if (extra is null)
{
@@ -277,7 +285,7 @@ private static bool TryGetStringFromDictionary(IDictionary dict, string key, out
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
- private enum PermissionDecision
+ private enum ToolPermissionDecision
{
Allow,
Deny
diff --git a/src/Coralph/ProviderConfigFactory.cs b/src/Coralph/ProviderConfigFactory.cs
index 218d222..3c105f0 100644
--- a/src/Coralph/ProviderConfigFactory.cs
+++ b/src/Coralph/ProviderConfigFactory.cs
@@ -1,4 +1,4 @@
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
namespace Coralph;
diff --git a/src/Coralph/packages.lock.json b/src/Coralph/packages.lock.json
index 51bbce0..8712189 100644
--- a/src/Coralph/packages.lock.json
+++ b/src/Coralph/packages.lock.json
@@ -4,13 +4,12 @@
"net10.0": {
"GitHub.Copilot.SDK": {
"type": "Direct",
- "requested": "[0.3.0, )",
- "resolved": "0.3.0",
- "contentHash": "zTmlbLWsmmZT/v/vbvP66d73t3sJ92gSavCe+52MtD1jwOJsGv5ExL8CY/jxA9/3GQt50LZs3p+ntjBpe/+Rvw==",
+ "requested": "[1.0.0, )",
+ "resolved": "1.0.0",
+ "contentHash": "hAeuf54OVdFxEtf0AlMgiQBNpeMy7rSbr6Y1bJWjeln8U71D5QEryje8auVQATt3l5ubdbmWGFnO9VOnBhWCfw==",
"dependencies": {
"Microsoft.Extensions.AI.Abstractions": "10.2.0",
- "Microsoft.Extensions.Logging.Abstractions": "10.0.2",
- "StreamJsonRpc": "2.24.84"
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.2"
}
},
"Hex1b": {
@@ -99,20 +98,6 @@
"resolved": "2.0.0-beta4.22272.1",
"contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg=="
},
- "MessagePack": {
- "type": "Transitive",
- "resolved": "2.5.198",
- "contentHash": "ul2rGISMatBL4AbXTteEml4xtOsKF8yClEkS0rnrMyFs6Cu3KTbdZwOXrcbEUjGFIDiCwHuYIGyS55FeiaYzBw==",
- "dependencies": {
- "MessagePack.Annotations": "2.5.198",
- "Microsoft.NET.StringTools": "17.6.3"
- }
- },
- "MessagePack.Annotations": {
- "type": "Transitive",
- "resolved": "2.5.198",
- "contentHash": "3U9OvqQGTra+Mz1k1zfNAScSdNHobnqtQ51qdMGUZppkNDZJl0X/igq6Qz5zDBLEZoYqZrFtZwFx6wBJHHI8BA=="
- },
"Microsoft.Extensions.AI.Abstractions": {
"type": "Transitive",
"resolved": "10.2.0",
@@ -205,58 +190,11 @@
"resolved": "8.0.0",
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
},
- "Microsoft.NET.StringTools": {
- "type": "Transitive",
- "resolved": "18.0.2",
- "contentHash": "cTZw3GHkAlqZACYGeQT3niS3UfVQ8CH0O5+zUdhxstrg1Z8Q2ViXYFKjSxHmEXTX85mrOT/QnHZOeQhhSsIrkQ=="
- },
- "Microsoft.VisualStudio.Threading.Only": {
- "type": "Transitive",
- "resolved": "17.14.15",
- "contentHash": "NqONyw1RXyj9P3k5e1uU2k9kc1ptwuU5NJQzG+MPq7vQVHUzBY8HLuJf/N2Rw5H/myD96CVxziDxmjawPuzntw==",
- "dependencies": {
- "Microsoft.VisualStudio.Validation": "17.8.8"
- }
- },
- "Microsoft.VisualStudio.Validation": {
- "type": "Transitive",
- "resolved": "17.13.22",
- "contentHash": "fC20ITOxlUpGQ0ltAxITQbeFxwuB6OjLT3lByC4+/Gm46o/Mwv9hlIVrBhiUhnqdQzUUvr4EHkdiYe7WOSMLmw=="
- },
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
},
- "Nerdbank.MessagePack": {
- "type": "Transitive",
- "resolved": "1.0.2",
- "contentHash": "mUcRO0trgc85qKPR360ybSvZTbm3nlSr1aK0JlKVWC5iC9OYxVLL1AbJxaW2Pbj4EQNCP8dKiz/SIlFPy67Ixw==",
- "dependencies": {
- "Microsoft.NET.StringTools": "18.0.2",
- "Microsoft.VisualStudio.Validation": "17.13.22",
- "PolyType": "1.0.0"
- }
- },
- "Nerdbank.Streams": {
- "type": "Transitive",
- "resolved": "2.13.16",
- "contentHash": "GjifQ5M4IpQWjGNNbIrsj8M/M7ESb2328OeoGeqxk5rOJiuaAJ5zFc6CyqB+5UWKr1KEGK23hKoxA+z1gooAKA==",
- "dependencies": {
- "Microsoft.VisualStudio.Threading.Only": "17.13.61",
- "Microsoft.VisualStudio.Validation": "17.8.8"
- }
- },
- "Newtonsoft.Json": {
- "type": "Transitive",
- "resolved": "13.0.3",
- "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
- },
- "PolyType": {
- "type": "Transitive",
- "resolved": "1.0.0",
- "contentHash": "q8vTQfDoKa/vCNMT+TvKbzsl+Z/s4k/qaP8WQGvNDIILkxKhP+N3NbFAOiglQljNmAj/ei51wO9jwcAI9jhtHw=="
- },
"QRCoder": {
"type": "Transitive",
"resolved": "1.7.0",
@@ -265,19 +203,6 @@
"System.Drawing.Common": "6.0.0"
}
},
- "StreamJsonRpc": {
- "type": "Transitive",
- "resolved": "2.24.84",
- "contentHash": "7PtQlA7wYFBzOrcuALWGs1uPJI712KEYOMZXxxSKel9C0y5TPyjxXLkpw7d4O175rUKNbLHHbB89fPzMC2SLPg==",
- "dependencies": {
- "MessagePack": "2.5.198",
- "Microsoft.VisualStudio.Threading.Only": "17.14.15",
- "Microsoft.VisualStudio.Validation": "17.13.22",
- "Nerdbank.MessagePack": "1.0.2",
- "Nerdbank.Streams": "2.13.16",
- "Newtonsoft.Json": "13.0.3"
- }
- },
"System.Drawing.Common": {
"type": "Transitive",
"resolved": "6.0.0",