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
3 changes: 3 additions & 0 deletions rust/src/generated/api_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14500,6 +14500,9 @@ pub struct SandboxConfig {
/// Whether to auto-add the current working directory to readwritePaths. Default: true.
#[serde(skip_serializing_if = "Option::is_none")]
pub add_current_working_directory: Option<bool>,
/// Host capability flag (not part of the sandbox policy): when true, exposes a per-command escape hatch so the model can request individual commands run outside the sandbox.
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_bypass: Option<bool>,
/// Whether to auto-grant read access to the tool directories discovered on PATH and in toolchain environment variables (GOROOT, CARGO_HOME, JAVA_HOME, VIRTUAL_ENV, and similar), and to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Set to false to disable every grant listed above: user-installed toolchains (rustup, nvm, pyenv, conda, pipx) then need explicit userPolicy.filesystem entries — readonlyPaths to read them, plus readwriteFiles for a relocated CARGO_HOME's .package-cache and .global-cache, which Cargo locks on every build. Only these developer-tool grants are affected: the working directory (see addCurrentWorkingDirectory), temporary storage, session log paths, and system locations follow their own rules and stay granted, so commands still run. Default: true (enabled by default; set to false to opt out).
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_dev_tool_access: Option<bool>,
Expand Down
29 changes: 28 additions & 1 deletion rust/tests/api_types_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

use github_copilot_sdk::rpc::{
Extension, ExtensionList, ExtensionSource, ExtensionStatus, ExtensionsDisableRequest,
ExtensionsEnableRequest, FleetStartRequest, FleetStartResult, TasksStartAgentRequest,
ExtensionsEnableRequest, FleetStartRequest, FleetStartResult, SandboxConfig,
TasksStartAgentRequest,
};
use github_copilot_sdk::session_events::{PermissionRequest, PermissionRequestedData};

Expand Down Expand Up @@ -104,6 +105,32 @@ fn permission_event_exposes_managed_approval_required() {
assert_eq!(request.managed_approval_required, Some(true));
}

#[test]
fn sandbox_allow_bypass_serializes_as_optional_camel_case() {
let enabled = SandboxConfig {
enabled: true,
allow_bypass: Some(true),
..Default::default()
};
assert_eq!(
serde_json::to_value(enabled).unwrap(),
serde_json::json!({
"allowBypass": true,
"enabled": true,
})
);

let omitted = SandboxConfig {
enabled: true,
allow_bypass: None,
..Default::default()
};
assert_eq!(
serde_json::to_value(omitted).unwrap(),
serde_json::json!({ "enabled": true })
);
}

fn running_extension(id: &str, name: &str) -> Extension {
Extension {
id: id.to_string(),
Expand Down
5 changes: 4 additions & 1 deletion scripts/codegen/rust.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { promisify } from "util";
import type { JSONSchema7, JSONSchema7Definition } from "json-schema";
import {
addManagedApprovalRequiredToPermissionRequests,
addSandboxAllowBypass,
type ApiSchema,
type DefinitionCollections,
EXCLUDED_EVENT_TYPES,
Expand Down Expand Up @@ -2235,7 +2236,9 @@ async function generate(): Promise<void> {
);
const apiSchema = propagateInternalVisibility(
postProcessSchema(
stripBooleanLiterals(apiRaw) as JSONSchema7,
stripBooleanLiterals(
addSandboxAllowBypass(apiRaw as JSONSchema7),
) as JSONSchema7,
),
) as unknown as ApiSchema;

Expand Down
26 changes: 26 additions & 0 deletions scripts/codegen/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,32 @@ export function addManagedApprovalRequiredToPermissionRequests<T extends JSONSch
return cloned;
}

/**
* Add the sandbox bypass host capability until the pinned CLI schema includes the field.
*/
export function addSandboxAllowBypass<T extends JSONSchema7>(schema: T): T {
const cloned = cloneSchemaForCodegen(schema);
const property: JSONSchema7 = {
description:
"Host capability flag (not part of the sandbox policy): when true, exposes a per-command escape hatch so the model can request individual commands run outside the sandbox.",
type: "boolean",
};

for (const definitions of [cloned.definitions, cloned.$defs]) {
if (!definitions) continue;
const definition = definitions.SandboxConfig;
if (!definition || typeof definition !== "object") continue;
const objectDefinition = definition as JSONSchema7;
objectDefinition.properties = {
...objectDefinition.properties,
allowBypass:
objectDefinition.properties?.allowBypass ?? cloneSchemaForCodegen(property),
};
}

return cloned;
}

export function getEnumValueDescriptions(schema: JSONSchema7 | null | undefined): EnumValueDescriptions | undefined {
if (!schema || typeof schema !== "object") return undefined;

Expand Down
Loading