Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c8ba6e2
updated git ignore
robgruen Jul 7, 2026
726f602
fixed agent circle background color
robgruen Jul 8, 2026
f61084c
fixed gh when doing a search for "no assignee"
robgruen Jul 8, 2026
1dbeacb
added message delete when dev mode is on.
robgruen Jul 8, 2026
0c9ff92
better search file filtering
robgruen Jul 8, 2026
361fbc5
Implemented clientIO in vscode-shell and added new --confirm option f…
robgruen Jul 8, 2026
fb33729
fixed dangling chat bubble after confirmation
robgruen Jul 8, 2026
5d167b5
added back the history separator
robgruen Jul 8, 2026
f93184e
pnpm update
robgruen Jul 8, 2026
0d212f1
extension and settings updates
robgruen Jul 8, 2026
24da7c0
Merge remote-tracking branch 'origin' into dev/robgruen/dogfooding3
robgruen Jul 8, 2026
0a80e81
implemented missing clientIO functionality in vscode-shell and added …
robgruen Jul 8, 2026
daf7097
added windows service stop/start/restart with fuzzy matching to autos…
robgruen Jul 9, 2026
9dff423
allowed for chaining multiple choices together.
robgruen Jul 9, 2026
3a71009
can now queue messages while the dispatcher is loading.
robgruen Jul 9, 2026
f00515d
Merge branch 'main' into dev/robgruen/dogfooding3
robgruen Jul 9, 2026
8590491
style: apply prettier formatting and policy fixes
typeagent-bot[bot] Jul 9, 2026
9d3f50d
Potential fix for pull request finding 'CodeQL / Unvalidated dynamic …
robgruen Jul 9, 2026
5f35341
Merge branch 'main' into dev/robgruen/dogfooding3
robgruen Jul 9, 2026
b18ddcc
Potential fix for pull request finding
robgruen Jul 9, 2026
3292572
Address review thread fixes in vscode-shell and autoShell
Copilot Jul 9, 2026
a183eee
fix: normalize copilot platform lockfile resolutions
Copilot Jul 9, 2026
205bf79
updated ratchet baseline
robgruen Jul 9, 2026
6ba187c
Potential fix for pull request finding
robgruen Jul 9, 2026
a9a8e7a
Potential fix for pull request finding
robgruen Jul 9, 2026
77e5977
Potential fix for pull request finding
robgruen Jul 9, 2026
dbf2813
style: apply prettier formatting and policy fixes
typeagent-bot[bot] Jul 9, 2026
2b306fa
Potential fix for pull request finding
robgruen Jul 9, 2026
7d67f49
Potential fix for pull request finding
robgruen Jul 9, 2026
9401444
Potential fix for pull request finding
robgruen Jul 9, 2026
619e8b9
Potential fix for pull request finding
robgruen Jul 9, 2026
8b5f82e
Potential fix for pull request finding
robgruen Jul 9, 2026
629f6db
Potential fix for pull request finding
robgruen Jul 9, 2026
a5b204b
Potential fix for pull request finding
robgruen Jul 9, 2026
eb1895c
Potential fix for pull request finding
robgruen Jul 9, 2026
77276c1
fix: remove unused ILogger parameter from ServiceActionHandler
Copilot Jul 9, 2026
ad20876
added additional auth options
robgruen Jul 9, 2026
2628f19
Merge branch 'dev/robgruen/dogfooding3' of https://github.com/microso…
robgruen Jul 9, 2026
c579188
fix(vscode-shell): close requestInteraction async IIFE in webview
Copilot Jul 9, 2026
40917f6
fix(lockfile): avoid pinned rollup linux tarball URL
Copilot Jul 9, 2026
322cee7
Merge branch 'main' into dev/robgruen/dogfooding3
robgruen Jul 9, 2026
0faf13a
fix(lint): replace explicit-any with proper types in bridgeRpcRequest…
Copilot Jul 9, 2026
2cf66c0
style: apply prettier formatting and policy fixes
typeagent-bot[bot] Jul 9, 2026
a499586
fix: add circular dep exception for actionHandlers→memory→internal→di…
Copilot Jul 9, 2026
e07c39e
style: apply prettier formatting and policy fixes
typeagent-bot[bot] Jul 9, 2026
921a7a1
Merge branch 'main' into dev/robgruen/dogfooding3
robgruen Jul 10, 2026
f78d5c4
Merge branch 'main' into dev/robgruen/dogfooding3
robgruen Jul 10, 2026
f946adb
Update request to move file instead of renaming
robgruen Jul 10, 2026
7a7acbc
Merge branch 'main' into dev/robgruen/dogfooding3
robgruen Jul 10, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/build-ts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ jobs:
shell: bash
run: |
git fetch --no-tags origin "${{ github.base_ref }}"
npm run code-circular -- --ratchet --base "origin/${{ github.base_ref }}"
npm run code-circular -- --ratchet --base "origin/${{ github.base_ref }}" --exceptions-file tools/scripts/code/circular-baseline-exception.json
# Test-debt gate (PRs only): zero tolerance for focused tests
# (.only/fit/fdescribe) and no newly skipped tests (.skip/xit/xdescribe)
# in changed files. A small, fixable problem -> a hard gate, not a ratchet.
Expand Down
4 changes: 3 additions & 1 deletion dotnet/autoShell.Tests/ActionDispatcherIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class ActionDispatcherIntegrationTests
private readonly Mock<IWindowService> _windowMock = new();
private readonly Mock<INetworkService> _networkMock = new();
private readonly Mock<IVirtualDesktopService> _virtualDesktopMock = new();
private readonly Mock<IServiceControlService> _serviceControlMock = new();
private readonly Mock<ILogger> _loggerMock = new();
private readonly ActionDispatcher _dispatcher;

Expand All @@ -43,7 +44,8 @@ public ActionDispatcherIntegrationTests()
_displayMock.Object,
_windowMock.Object,
_networkMock.Object,
_virtualDesktopMock.Object);
_virtualDesktopMock.Object,
_serviceControlMock.Object);
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions dotnet/autoShell.Tests/HandlerRegistrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public HandlerRegistrationTests()
var windowMock = new Mock<IWindowService>();
var networkMock = new Mock<INetworkService>();
var virtualDesktopMock = new Mock<IVirtualDesktopService>();
var serviceControlMock = new Mock<IServiceControlService>();
var loggerMock = new Mock<ILogger>();

_handlers =
Expand All @@ -50,6 +51,7 @@ public HandlerRegistrationTests()
new PrivacySettingsHandler(registryMock.Object),
new SystemSettingsHandler(registryMock.Object, processMock.Object),
new SystemActionHandler(processMock.Object, debuggerMock.Object),
new ServiceActionHandler(serviceControlMock.Object),
];
}

Expand Down
180 changes: 180 additions & 0 deletions dotnet/autoShell.Tests/ServiceActionHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json;
using autoShell.Handlers;
using autoShell.Services;
using Moq;

namespace autoShell.Tests;

public class ServiceActionHandlerTests
{
private readonly Mock<IServiceControlService> _serviceMock = new();
private readonly ServiceActionHandler _handler;

public ServiceActionHandlerTests()
{
_handler = new ServiceActionHandler(_serviceMock.Object);
}

/// <summary>
/// Verifies that RestartService with matchBy "name" locates the service by name.
/// </summary>
[Fact]
public void RestartService_MatchByName_CallsServiceByName()
{
_serviceMock
.Setup(s => s.RestartService("Spooler", false, false))
.Returns(ServiceControlResult.Ok("Print Spooler"));

var json = JsonDocument.Parse("""{"service":"Spooler","matchBy":"name"}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.True(result.Success);
_serviceMock.Verify(s => s.RestartService("Spooler", false, false), Times.Once);
}

/// <summary>
/// Verifies that RestartService with matchBy "description" locates the service by description.
/// </summary>
[Fact]
public void RestartService_MatchByDescription_CallsServiceByDescription()
{
_serviceMock
.Setup(s => s.RestartService("windows update", true, false))
.Returns(ServiceControlResult.Ok("Windows Update"));

var json = JsonDocument.Parse("""{"service":"windows update","matchBy":"description"}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.True(result.Success);
_serviceMock.Verify(s => s.RestartService("windows update", true, false), Times.Once);
}

/// <summary>
/// Verifies that RestartService without matchBy defaults to matching by name.
/// </summary>
[Fact]
public void RestartService_NoMatchBy_DefaultsToName()
{
_serviceMock
.Setup(s => s.RestartService("Audiosrv", false, false))
.Returns(ServiceControlResult.Ok("Windows Audio"));

var json = JsonDocument.Parse("""{"service":"Audiosrv"}""").RootElement;
_handler.Handle("RestartService", json);

_serviceMock.Verify(s => s.RestartService("Audiosrv", false, false), Times.Once);
}

/// <summary>
/// Verifies that the resolved service display name is included in the success message.
/// </summary>
[Fact]
public void RestartService_Success_ReturnsDisplayNameInMessage()
{
_serviceMock
.Setup(s => s.RestartService(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>()))
.Returns(ServiceControlResult.Ok("Print Spooler"));

var json = JsonDocument.Parse("""{"service":"Spooler"}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.True(result.Success);
Assert.Contains("Print Spooler", result.Message);
}

/// <summary>
/// Verifies that a failure from the service is surfaced as a failed result with its error message.
/// </summary>
[Fact]
public void RestartService_ServiceFails_ReturnsFailureWithError()
{
_serviceMock
.Setup(s => s.RestartService(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>()))
.Returns(ServiceControlResult.Fail("No Windows service found with the name or display name 'bogus'."));

var json = JsonDocument.Parse("""{"service":"bogus"}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.False(result.Success);
Assert.Contains("bogus", result.Message);
}

/// <summary>
/// Verifies that an empty service identifier returns a failure without calling the service.
/// </summary>
[Fact]
public void RestartService_EmptyService_ReturnsFailure()
{
var json = JsonDocument.Parse("""{"service":""}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.False(result.Success);
_serviceMock.Verify(s => s.RestartService(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<bool>()), Times.Never);
}

/// <summary>
/// Verifies that a fuzzy match returns a success result carrying a confirmation payload
/// (rather than restarting anything) so the agent can confirm the target with the user.
/// </summary>
[Fact]
public void RestartService_FuzzyMatch_ReturnsConfirmationData()
{
_serviceMock
.Setup(s => s.RestartService("spool", false, false))
.Returns(ServiceControlResult.Confirm("Spooler", "Print Spooler"));

var json = JsonDocument.Parse("""{"service":"spool"}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.True(result.Success);
Assert.NotNull(result.Data);
JsonElement data = result.Data.Value;
Assert.True(data.GetProperty("needsConfirmation").GetBoolean());
Assert.Equal("Spooler", data.GetProperty("resolvedServiceName").GetString());
Assert.Equal("Print Spooler", data.GetProperty("resolvedDisplayName").GetString());
Assert.Equal("restart", data.GetProperty("operation").GetString());
}

/// <summary>
/// Verifies that when the service reports elevation is required, the handler returns a success
/// result carrying an elevation-confirmation payload (rather than restarting anything).
/// </summary>
[Fact]
public void RestartService_NeedsElevation_ReturnsElevationData()
{
_serviceMock
.Setup(s => s.RestartService("Spooler", false, false))
.Returns(ServiceControlResult.Elevate("Spooler", "Print Spooler"));

var json = JsonDocument.Parse("""{"service":"Spooler"}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.True(result.Success);
Assert.NotNull(result.Data);
JsonElement data = result.Data.Value;
Assert.True(data.GetProperty("needsElevation").GetBoolean());
Assert.Equal("Spooler", data.GetProperty("resolvedServiceName").GetString());
Assert.Equal("Print Spooler", data.GetProperty("resolvedDisplayName").GetString());
Assert.Equal("restart", data.GetProperty("operation").GetString());
}

/// <summary>
/// Verifies that once the user has consented, the elevate flag is forwarded to the service.
/// </summary>
[Fact]
public void RestartService_ElevateTrue_PassesElevateToService()
{
_serviceMock
.Setup(s => s.RestartService("Spooler", false, true))
.Returns(ServiceControlResult.Ok("Print Spooler"));

var json = JsonDocument.Parse("""{"service":"Spooler","matchBy":"name","elevate":true}""").RootElement;
var result = _handler.Handle("RestartService", json);

Assert.True(result.Success);
_serviceMock.Verify(s => s.RestartService("Spooler", false, true), Times.Once);
}
}
Loading
Loading