diff --git a/src/OneWare.Copilot/CopilotModule.cs b/src/OneWare.Copilot/CopilotModule.cs
index ceb8289d0..63ab035e1 100644
--- a/src/OneWare.Copilot/CopilotModule.cs
+++ b/src/OneWare.Copilot/CopilotModule.cs
@@ -162,13 +162,13 @@ ABSOLUTE PROHIBITIONS
[
new PackageVersion()
{
- Version = "1.0.37",
+ Version = "1.0.54",
Targets =
[
new PackageTarget()
{
Target = "win-x64",
- Url = "https://github.com/github/copilot-cli/releases/download/v1.0.37/copilot-win32-x64.zip",
+ Url = "https://github.com/github/copilot-cli/releases/download/v1.0.54/copilot-win32-x64.zip",
AutoSetting =
[
new PackageAutoSetting
@@ -181,7 +181,7 @@ ABSOLUTE PROHIBITIONS
new PackageTarget()
{
Target = "win-arm64",
- Url = "https://github.com/github/copilot-cli/releases/download/v1.0.37/copilot-win32-arm64.zip",
+ Url = "https://github.com/github/copilot-cli/releases/download/v1.0.54/copilot-win32-arm64.zip",
AutoSetting =
[
new PackageAutoSetting
@@ -194,7 +194,7 @@ ABSOLUTE PROHIBITIONS
new PackageTarget()
{
Target = "linux-x64",
- Url = "https://github.com/github/copilot-cli/releases/download/v1.0.37/copilot-linux-x64.tar.gz",
+ Url = "https://github.com/github/copilot-cli/releases/download/v1.0.54/copilot-linux-x64.tar.gz",
AutoSetting =
[
new PackageAutoSetting
@@ -207,7 +207,7 @@ ABSOLUTE PROHIBITIONS
new PackageTarget()
{
Target = "linux-arm64",
- Url = "https://github.com/github/copilot-cli/releases/download/v1.0.37/copilot-linux-arm64.tar.gz",
+ Url = "https://github.com/github/copilot-cli/releases/download/v1.0.54/copilot-linux-arm64.tar.gz",
AutoSetting =
[
new PackageAutoSetting
@@ -220,7 +220,7 @@ ABSOLUTE PROHIBITIONS
new PackageTarget()
{
Target = "osx-x64",
- Url = "https://github.com/github/copilot-cli/releases/download/v1.0.37/copilot-darwin-x64.tar.gz",
+ Url = "https://github.com/github/copilot-cli/releases/download/v1.0.54/copilot-darwin-x64.tar.gz",
AutoSetting =
[
new PackageAutoSetting
@@ -233,7 +233,7 @@ ABSOLUTE PROHIBITIONS
new PackageTarget()
{
Target = "osx-arm64",
- Url = "https://github.com/github/copilot-cli/releases/download/v1.0.37/copilot-darwin-arm64.tar.gz",
+ Url = "https://github.com/github/copilot-cli/releases/download/v1.0.54/copilot-darwin-arm64.tar.gz",
AutoSetting =
[
new PackageAutoSetting
diff --git a/src/OneWare.Copilot/OneWare.Copilot.csproj b/src/OneWare.Copilot/OneWare.Copilot.csproj
index c487a8b5a..4be4bc897 100644
--- a/src/OneWare.Copilot/OneWare.Copilot.csproj
+++ b/src/OneWare.Copilot/OneWare.Copilot.csproj
@@ -8,10 +8,11 @@
true
+ $(NoWarn);GHCP001
-
+
\ No newline at end of file
diff --git a/src/OneWare.Copilot/Services/CopilotChatService.cs b/src/OneWare.Copilot/Services/CopilotChatService.cs
index 5382ec2a3..0084b73f7 100644
--- a/src/OneWare.Copilot/Services/CopilotChatService.cs
+++ b/src/OneWare.Copilot/Services/CopilotChatService.cs
@@ -1,13 +1,16 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Text.RegularExpressions;
using Avalonia.Controls;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DynamicData;
-using GitHub.Copilot.SDK;
+using GitHub.Copilot;
+using GitHub.Copilot.Rpc;
+using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using OneWare.Copilot.Models;
using OneWare.Copilot.ViewModels;
@@ -36,6 +39,20 @@ public sealed class CopilotChatService(
private string? _requestedSessionId;
private readonly HashSet _allowedPermissionScopes = new(StringComparer.OrdinalIgnoreCase);
+ // Usage tracking
+ public long LastInputTokens { get; private set => SetProperty(ref field, value); }
+ public long LastOutputTokens { get; private set => SetProperty(ref field, value); }
+ public long? LastReasoningTokens { get; private set => SetProperty(ref field, value); }
+ public long SessionTotalRequests { get; private set => SetProperty(ref field, value); }
+ public long SessionTotalInputTokens { get; private set => SetProperty(ref field, value); }
+ public long SessionTotalOutputTokens { get; private set => SetProperty(ref field, value); }
+ public long ContextCurrentTokens { get; private set => SetProperty(ref field, value); }
+ public long ContextTokenLimit { get; private set => SetProperty(ref field, value); }
+ public double? QuotaRemainingPercent { get; private set => SetProperty(ref field, value); }
+ public bool QuotaIsUnlimited { get; private set => SetProperty(ref field, value); }
+ public DateTimeOffset? QuotaResetDate { get; private set => SetProperty(ref field, value); }
+ public bool HasUsageData { get; private set => SetProperty(ref field, value); }
+
private static readonly Regex DeviceLoginUrlRegex = new(@"https?://\S+", RegexOptions.Compiled);
private static readonly Regex DeviceLoginCodeRegex = new(@"\bcode\s+([A-Z0-9\-]+)\b",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
@@ -197,8 +214,8 @@ public async Task InitializeAsync()
_client = new CopilotClient(new CopilotClientOptions()
{
- Cwd = paths.ProjectsDirectory,
- CliPath = cliPath
+ WorkingDirectory = paths.ProjectsDirectory,
+ Connection = RuntimeConnection.ForStdio(cliPath, [])
});
bool isAuthenticated;
@@ -279,7 +296,7 @@ private async Task InitializeSessionAsync()
if (string.IsNullOrWhiteSpace(sessionId))
{
- var tools = toolProvider.GetTools();
+ var tools = toolProvider.GetTools().Cast().ToList();
_session = await _client.CreateSessionAsync(new SessionConfig
{
Model = SelectedModel.Id,
@@ -304,7 +321,7 @@ private async Task InitializeSessionAsync()
{
Streaming = true,
IncludeSubAgentStreamingEvents = false,
- Tools = toolProvider.GetTools(),
+ Tools = toolProvider.GetTools().Cast().ToList(),
OnPermissionRequest = OnPermissionRequestAsync,
OnUserInputRequest = OnUserInputRequestAsync
});
@@ -320,7 +337,7 @@ private async Task InitializeSessionAsync()
return;
}
- _subscription = _session.On(HandleSessionEvent);
+ _subscription = _session.On(HandleSessionEvent);
}
private string BuildSystemMessage()
@@ -429,6 +446,23 @@ private async Task DisposeSessionAsync()
}
CurrentSessionId = null;
+ ResetUsageStats();
+ }
+
+ private void ResetUsageStats()
+ {
+ LastInputTokens = 0;
+ LastOutputTokens = 0;
+ LastReasoningTokens = null;
+ SessionTotalRequests = 0;
+ SessionTotalInputTokens = 0;
+ SessionTotalOutputTokens = 0;
+ ContextCurrentTokens = 0;
+ ContextTokenLimit = 0;
+ QuotaRemainingPercent = null;
+ QuotaIsUnlimited = false;
+ QuotaResetDate = null;
+ HasUsageData = false;
}
private void HandleSessionEvent(SessionEvent evt)
@@ -478,10 +512,29 @@ private void HandleSessionEvent(SessionEvent evt)
case SessionIdleEvent:
EventReceived?.Invoke(this, new ChatIdleEvent());
break;
+ case AssistantUsageEvent usage:
+ UpdateUsageFromAssistantEvent(usage.Data);
+ break;
+ case SessionUsageInfoEvent info:
+ ContextCurrentTokens = info.Data.CurrentTokens;
+ ContextTokenLimit = info.Data.TokenLimit;
+ break;
}
}
- private Task OnPermissionRequestAsync(
+ private void UpdateUsageFromAssistantEvent(AssistantUsageData data)
+ {
+ LastInputTokens = data.InputTokens ?? 0;
+ LastOutputTokens = data.OutputTokens ?? 0;
+ LastReasoningTokens = data.ReasoningTokens is > 0 ? data.ReasoningTokens : null;
+ SessionTotalRequests++;
+ SessionTotalInputTokens += data.InputTokens ?? 0;
+ SessionTotalOutputTokens += data.OutputTokens ?? 0;
+
+ HasUsageData = true;
+ }
+
+ private Task OnPermissionRequestAsync(
PermissionRequest request,
PermissionInvocation invocation)
{
@@ -496,7 +549,7 @@ private Task OnPermissionRequestAsync(
return Task.FromResult(CreateAllowPermissionResult());
}
- var responseSource = new TaskCompletionSource(
+ var responseSource = new TaskCompletionSource(
TaskCreationOptions.RunContinuationsAsynchronously);
var context = BuildPermissionContext(request, invocation);
@@ -611,22 +664,14 @@ private static void AddDetail(ICollection details, string label, string?
details.Add($"{label}: `{trimmed}`");
}
- private static PermissionRequestResult CreateAllowPermissionResult()
+ private static PermissionDecision CreateAllowPermissionResult()
{
- return new PermissionRequestResult
- {
- Kind = PermissionRequestResultKind.Approved,
- Rules = null
- };
+ return PermissionDecision.ApproveOnce();
}
- private static PermissionRequestResult CreateDenyPermissionResult()
+ private static PermissionDecision CreateDenyPermissionResult()
{
- return new PermissionRequestResult
- {
- Kind = PermissionRequestResultKind.Rejected,
- Rules = null
- };
+ return PermissionDecision.Reject("");
}
private static bool IsCustomToolPermissionRequest(PermissionRequest request)
diff --git a/src/OneWare.Copilot/Views/CopilotChatExtensionView.axaml b/src/OneWare.Copilot/Views/CopilotChatExtensionView.axaml
index 5e1d3a2b0..bd1933b43 100644
--- a/src/OneWare.Copilot/Views/CopilotChatExtensionView.axaml
+++ b/src/OneWare.Copilot/Views/CopilotChatExtensionView.axaml
@@ -7,40 +7,138 @@
xmlns:behaviors="clr-namespace:OneWare.Essentials.Behaviors;assembly=OneWare.Essentials"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="OneWare.Copilot.Views.CopilotChatExtensionView" x:DataType="services:CopilotChatService">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file