Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .github/workflows/dotnet-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ jobs:
cache: "npm"
cache-dependency-path: "./nodejs/package-lock.json"

- name: Install bubblewrap
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict

- name: Install Node.js dependencies (for CLI version extraction)
working-directory: ./nodejs
run: npm ci --ignore-scripts
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/go-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ jobs:
with:
go-version: "1.24"

- name: Install bubblewrap
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict

- name: Run go fmt
if: runner.os == 'Linux'
working-directory: ./go
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/java-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ jobs:
with:
node-version: 22

- name: Install bubblewrap
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict

- name: Validate native host
run: node copilot-native/scripts/validate-native-host.mjs ${{ matrix.classifier }}

Expand Down Expand Up @@ -215,6 +223,13 @@ jobs:
with:
node-version: 22

- name: Install bubblewrap
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict

- name: Test documentation version updater
if: matrix.test-jdk == '25'
run: ./scripts/test-update-documentation-versions.sh
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/nodejs-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ jobs:
cache: "npm"
cache-dependency-path: "./nodejs/package-lock.json"
node-version: 22
- name: Install bubblewrap
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict
- name: Install dependencies
run: npm ci --ignore-scripts

Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/python-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ jobs:
cache: "npm"
cache-dependency-path: "./nodejs/package-lock.json"

- name: Install bubblewrap
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict

- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/rust-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ jobs:
toolchain: "1.94.0"
components: rustfmt, clippy

- name: Install bubblewrap
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict

# Nightly rustfmt for unstable format options (group_imports,
# imports_granularity, reorder_impl_items) — pinned in
# `.rustfmt.nightly.toml`.
Expand Down Expand Up @@ -148,6 +156,14 @@ jobs:
with:
toolchain: "1.94.0"

- name: Install bubblewrap
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install --yes apparmor-profiles bubblewrap
sudo install -m 0644 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict

- uses: Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae # v2
with:
workspaces: "rust"
Expand Down
10 changes: 10 additions & 0 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,11 @@ private async Task UpdateSessionOptionsForModeAsync(CopilotSession session, Sess
bool? manageScheduleEnabled = null;
IList<SessionInstalledPlugin>? installedPlugins = null;

if (config.SandboxConfig is not null)
{
hasAnyPatch = true;
}

if (_options.Mode == CopilotClientMode.Empty)
{
skipCustomInstructions = config.SkipCustomInstructions ?? true;
Expand Down Expand Up @@ -1070,6 +1075,7 @@ await session.Rpc.Options.UpdateAsync(
coauthorEnabled: coauthorEnabled,
manageScheduleEnabled: manageScheduleEnabled,
installedPlugins: installedPlugins,
sandboxConfig: config.SandboxConfig,
cancellationToken: cancellationToken).ConfigureAwait(false);
#pragma warning restore GHCP001
}
Expand Down Expand Up @@ -1215,6 +1221,7 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig config, Cance
config.DisabledSkills,
config.InfiniteSessions,
config.SessionLimits,
SandboxConfig: config.SandboxConfig,
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description ?? string.Empty)).ToList(),
RequestElicitation: config.OnElicitationRequest != null,
RequestMcpApps: config.EnableMcpApps ? true : null,
Expand Down Expand Up @@ -1436,6 +1443,7 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
config.DisabledSkills,
config.InfiniteSessions,
config.SessionLimits,
SandboxConfig: config.SandboxConfig,
Commands: config.Commands?.Select(c => new CommandWireDefinition(c.Name, c.Description ?? string.Empty)).ToList(),
RequestElicitation: config.OnElicitationRequest != null,
RequestMcpApps: config.EnableMcpApps ? true : null,
Expand Down Expand Up @@ -2795,6 +2803,7 @@ internal record CreateSessionRequest(
IList<string>? DisabledSkills,
InfiniteSessionConfig? InfiniteSessions,
SessionLimitsConfig? SessionLimits,
SandboxConfig? SandboxConfig = null,
IList<CommandWireDefinition>? Commands = null,
bool? RequestElicitation = null,
bool? RequestMcpApps = null,
Expand Down Expand Up @@ -2910,6 +2919,7 @@ internal record ResumeSessionRequest(
IList<string>? DisabledSkills,
InfiniteSessionConfig? InfiniteSessions,
SessionLimitsConfig? SessionLimits,
SandboxConfig? SandboxConfig = null,
IList<CommandWireDefinition>? Commands = null,
bool? RequestElicitation = null,
bool? RequestMcpApps = null,
Expand Down
7 changes: 7 additions & 0 deletions dotnet/src/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3223,6 +3223,7 @@ protected SessionConfigBase(SessionConfigBase? other)
PluginDirectories = other.PluginDirectories is not null ? [.. other.PluginDirectories] : null;
InstructionDirectories = other.InstructionDirectories is not null ? [.. other.InstructionDirectories] : null;
SessionLimits = other.SessionLimits;
SandboxConfig = other.SandboxConfig;
Streaming = other.Streaming;
IncludeSubAgentStreamingEvents = other.IncludeSubAgentStreamingEvents;
SystemMessage = other.SystemMessage;
Expand Down Expand Up @@ -3609,6 +3610,12 @@ protected SessionConfigBase(SessionConfigBase? other)
[Experimental(Diagnostics.Experimental)]
public SessionLimitsConfig? SessionLimits { get; set; }

/// <summary>
/// Resolved sandbox configuration applied when the session is created or resumed.
/// </summary>
[Experimental(Diagnostics.Experimental)]
public SandboxConfig? SandboxConfig { get; set; }

/// <summary>
/// Configuration for handling large tool outputs. When a tool produces
/// output exceeding the configured size, the output is written to a temp
Expand Down
83 changes: 83 additions & 0 deletions dotnet/test/E2E/SessionConfigE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ public class SessionConfigE2ETests(E2ETestFixture fixture, ITestOutputHelper out
private static readonly byte[] Png1X1 = Convert.FromBase64String(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==");

private static async Task AssertNextShellExecutionResultAsync(
CopilotSession session,
string prompt,
string expected)
{
var eventCount = (await session.GetEventsAsync()).Count;
await session.SendAndWaitAsync(new MessageOptions { Prompt = prompt });
var completion = Assert.Single(
(await session.GetEventsAsync()).Skip(eventCount).OfType<ToolExecutionCompleteEvent>());
Assert.Contains(expected, completion.Data.Result?.Content ?? string.Empty, StringComparison.Ordinal);
}

[Fact]
// TODO(BYOK): Anthropic Messages history diverged after enabling vision via SetModel. Verify
// that model capability overrides work for provider-backed sessions before keeping this CAPI-only.
Expand Down Expand Up @@ -524,6 +536,77 @@ public async Task Should_Apply_Session_Limits_On_Resume()
}
}

[Fact]
public async Task Should_Apply_Sandbox_Config_On_Create_And_Resume()
{
if (OperatingSystem.IsWindows())
{
return;
}

var enabledProbe = "/var/tmp/sandbox-create-enabled.txt";
var disabledProbe = "/var/tmp/sandbox-create-disabled.txt";
var resumeProbe = "/var/tmp/sandbox-resume-enabled.txt";
var probes = new[] { enabledProbe, disabledProbe, resumeProbe };
foreach (var probe in probes) File.Delete(probe);
await using var enabledSession = await CreateSessionAsync(new SessionConfig
{
WorkingDirectory = Ctx.WorkDir,
SandboxConfig = new SandboxConfig
{
Enabled = true,
UserPolicy = new SandboxConfigUserPolicy
{
Filesystem = new SandboxConfigUserPolicyFilesystem { DeniedPaths = [enabledProbe] },
},
},
});
await AssertNextShellExecutionResultAsync(
enabledSession,
"Check sandbox access for sandbox-create-enabled.txt.",
"sandbox-blocked");

await using var disabledSession = await CreateSessionAsync(new SessionConfig
{
WorkingDirectory = Ctx.WorkDir,
SandboxConfig = new SandboxConfig
{
Enabled = false,
UserPolicy = new SandboxConfigUserPolicy
{
Filesystem = new SandboxConfigUserPolicyFilesystem { DeniedPaths = [disabledProbe] },
},
},
});
await AssertNextShellExecutionResultAsync(
disabledSession,
"Check sandbox access for sandbox-create-disabled.txt.",
"sandbox-accessible");
var sessionId = disabledSession.SessionId;
await SuspendAndUntrackSessionForResumeAsync(disabledSession);

var session2 = await ResumeSessionAsync(sessionId, new ResumeSessionConfig
{
WorkingDirectory = Ctx.WorkDir,
SandboxConfig = new SandboxConfig
{
Enabled = true,
UserPolicy = new SandboxConfigUserPolicy
{
Filesystem = new SandboxConfigUserPolicyFilesystem { DeniedPaths = [resumeProbe] },
},
},
});
await AssertNextShellExecutionResultAsync(
session2,
"Check sandbox access for sandbox-resume-enabled.txt.",
"sandbox-blocked");

Assert.Equal(sessionId, session2.SessionId);
await session2.DisposeAsync();
foreach (var probe in probes) File.Delete(probe);
}

[Fact]
public async Task Should_Apply_Excluded_Built_In_Agents_On_Create()
{
Expand Down
32 changes: 30 additions & 2 deletions dotnet/test/Unit/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,18 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO
("EnableCitations", true),
("EnableFileChangeTracking", true),
("ExcludedBuiltInAgents", excludedAgents),
("SessionLimits", new SessionLimitsConfig { MaxAiCredits = 12.5 }));
("SessionLimits", new SessionLimitsConfig { MaxAiCredits = 12.5 }),
("SandboxConfig", new SandboxConfig
{
Enabled = true,
UserPolicy = new SandboxConfigUserPolicy
{
Network = new SandboxConfigUserPolicyNetwork
{
Proxy = new SandboxConfigUserPolicyNetworkProxy { Url = "http://127.0.0.1:4321" },
},
},
}));

var createJson = JsonSerializer.Serialize(createRequest, createRequestType, options);
using var createDocument = JsonDocument.Parse(createJson);
Expand All @@ -494,6 +505,9 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO
Assert.True(createRoot.GetProperty("enableFileChangeTracking").GetBoolean());
Assert.Equal("explore", createRoot.GetProperty("excludedBuiltinAgents")[0].GetString());
Assert.Equal(12.5, createRoot.GetProperty("sessionLimits").GetProperty("maxAiCredits").GetDouble());
Assert.Equal(
"http://127.0.0.1:4321",
createRoot.GetProperty("sandboxConfig").GetProperty("userPolicy").GetProperty("network").GetProperty("proxy").GetProperty("url").GetString());

var resumeRequestType = GetNestedType(typeof(CopilotClient), "ResumeSessionRequest");
var resumeRequest = CreateInternalRequest(
Expand All @@ -502,7 +516,18 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO
("EnableCitations", true),
("EnableFileChangeTracking", true),
("ExcludedBuiltInAgents", excludedAgents),
("SessionLimits", new SessionLimitsConfig { MaxAiCredits = 7.25 }));
("SessionLimits", new SessionLimitsConfig { MaxAiCredits = 7.25 }),
("SandboxConfig", new SandboxConfig
{
Enabled = true,
UserPolicy = new SandboxConfigUserPolicy
{
Network = new SandboxConfigUserPolicyNetwork
{
Proxy = new SandboxConfigUserPolicyNetworkProxy { Url = "http://127.0.0.1:4322" },
},
},
}));

var resumeJson = JsonSerializer.Serialize(resumeRequest, resumeRequestType, options);
using var resumeDocument = JsonDocument.Parse(resumeJson);
Expand All @@ -511,6 +536,9 @@ public void SessionRequests_CanSerializeCitationAgentExclusionAndLimits_WithSdkO
Assert.True(resumeRoot.GetProperty("enableFileChangeTracking").GetBoolean());
Assert.Equal("task", resumeRoot.GetProperty("excludedBuiltinAgents")[1].GetString());
Assert.Equal(7.25, resumeRoot.GetProperty("sessionLimits").GetProperty("maxAiCredits").GetDouble());
Assert.Equal(
"http://127.0.0.1:4322",
resumeRoot.GetProperty("sandboxConfig").GetProperty("userPolicy").GetProperty("network").GetProperty("proxy").GetProperty("url").GetString());
}

[Fact]
Expand Down
4 changes: 4 additions & 0 deletions go/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses
req.EnableCitations = config.EnableCitations
req.EnableFileChangeTracking = config.EnableFileChangeTracking
req.SessionLimits = config.SessionLimits
req.SandboxConfig = config.SandboxConfig
req.IsExperimentalMode = config.EnableExperimentalMode
req.SkipCustomInstructions = config.SkipCustomInstructions
req.CustomAgentsLocalOnly = config.CustomAgentsLocalOnly
Expand Down Expand Up @@ -1099,6 +1100,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses
CustomAgentsLocalOnly: config.CustomAgentsLocalOnly,
CoauthorEnabled: config.CoauthorEnabled,
ManageScheduleEnabled: config.ManageScheduleEnabled,
SandboxConfig: config.SandboxConfig,
}); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1173,6 +1175,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string,
req.EnableCitations = config.EnableCitations
req.EnableFileChangeTracking = config.EnableFileChangeTracking
req.SessionLimits = config.SessionLimits
req.SandboxConfig = config.SandboxConfig
if config.Streaming != nil {
req.Streaming = config.Streaming
}
Expand Down Expand Up @@ -1377,6 +1380,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string,
CustomAgentsLocalOnly: config.CustomAgentsLocalOnly,
CoauthorEnabled: config.CoauthorEnabled,
ManageScheduleEnabled: config.ManageScheduleEnabled,
SandboxConfig: config.SandboxConfig,
}); err != nil {
return nil, err
}
Expand Down
Loading
Loading