From 7682348bf1ba6cead28ba7304f3edfa5acb42d0e Mon Sep 17 00:00:00 2001
From: David Fowler <95136+davidfowl@users.noreply.github.com>
Date: Tue, 6 Oct 2026 12:59:05 +0000
Subject: [PATCH] feat(rust): add original-session remote policy inputs
Apply the canonical machine-generated Rust RPC and DTO delta from runtime eaec03ae972f75282fe5122bebd54cba3c6cae01 (schema producer 9d3047795c634841ce94718d66759e3b7ca15d23). Preserve the standalone CLI pin and unrelated SDK snapshot differences.
api.schema.json SHA256: 7f2f51e8ea294ec8fa116da77ff58bf0e1fea4d8fb0b4bcfd460195e2fd667f6
session-events.schema.json SHA256: 1e363c28ca3b2197ea4baf2ba25980138eda63213cd8a6585151deeb111a882d
Cover Content-Length framed transport with the original session ID, all boolean combinations, malformed required fields, and RPC error propagation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
rust/README.md | 13 +++
rust/src/generated/api_types.rs | 59 +++++++++++++
rust/src/generated/rpc.rs | 28 ++++++
rust/tests/session_test.rs | 150 +++++++++++++++++++++++++++++++-
4 files changed, 249 insertions(+), 1 deletion(-)
diff --git a/rust/README.md b/rust/README.md
index e2dcdb087e..9d3986f20c 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -517,6 +517,19 @@ New RPCs land in the namespace immediately as the schema regenerates;
helpers are added on top only when an ergonomic story is worth the
maintenance.
+The experimental `session.rpc().remote().get_policy_inputs().await?` returns
+`github_copilot_sdk::rpc::RemotePolicyInputs` for that original live session,
+using its existing subscription. Its four required booleans are
+`managed_remote_control_setting`, `managed_remote_control_staff_override`,
+`owner_adc_sandbox`, and `owner_codespaces`. The managed flags include native
+defaults; the environment booleans describe the session's owning runtime
+process (`ADC_SANDBOX_ID` nonempty and `CODESPACES` exactly `"true"`).
+The call does not enable export or steering, apply managed policy, or expose
+raw flags or environment values. Missing or unattached sessions, unsupported
+runtimes, and malformed responses return errors rather than inferred inputs.
+Use a matching runtime and its emitted schemas for this unreleased API; the
+SDK change alone does not add support to an older pinned CLI.
+
#### Typed MCP installation and removal payloads (breaking change)
Three payloads in the experimental MCP installation and removal workflow are now typed
diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs
index 32c6e7f6ca..c8a919a7c5 100644
--- a/rust/src/generated/api_types.rs
+++ b/rust/src/generated/api_types.rs
@@ -973,6 +973,8 @@ pub mod rpc_methods {
pub const SESSION_REMOTE_ENABLE: &str = "session.remote.enable";
/// `session.remote.disable`
pub const SESSION_REMOTE_DISABLE: &str = "session.remote.disable";
+ /// `session.remote.getPolicyInputs`
+ pub const SESSION_REMOTE_GETPOLICYINPUTS: &str = "session.remote.getPolicyInputs";
/// `session.remote.notifySteerableChanged`
pub const SESSION_REMOTE_NOTIFYSTEERABLECHANGED: &str = "session.remote.notifySteerableChanged";
/// `session.visibility.get`
@@ -20120,6 +20122,27 @@ pub struct RemoteNotifySteerableChangedRequest {
#[serde(rename_all = "camelCase")]
pub struct RemoteNotifySteerableChangedResult {}
+/// Read-only remote-policy inputs from the original live session and its owning runtime process.
+///
+///
+///
+/// **Experimental.** This type is part of an experimental wire-protocol surface
+/// and may change or be removed in future SDK or CLI releases.
+///
+///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RemotePolicyInputs {
+ /// Resolved MANAGED_REMOTE_CONTROL_SETTING flag, including the runtime default when no boolean override exists.
+ pub managed_remote_control_setting: bool,
+ /// Resolved MANAGED_REMOTE_CONTROL_SETTING_STAFF_OVERRIDE flag, including the runtime default when no boolean override exists.
+ pub managed_remote_control_staff_override: bool,
+ /// Whether ADC_SANDBOX_ID is nonempty in the original session's owning runtime process.
+ pub owner_adc_sandbox: bool,
+ /// Whether CODESPACES is the literal string "true" in the original session's owning runtime process.
+ pub owner_codespaces: bool,
+}
+
/// Remote session connection result.
///
///
@@ -36534,6 +36557,42 @@ pub struct SessionRemoteDisableParams {
pub session_id: SessionId,
}
+/// Identifies the target session.
+///
+///
+///
+/// **Experimental.** This type is part of an experimental wire-protocol surface
+/// and may change or be removed in future SDK or CLI releases.
+///
+///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SessionRemoteGetPolicyInputsParams {
+ /// Target session identifier
+ pub session_id: SessionId,
+}
+
+/// Read-only remote-policy inputs from the original live session and its owning runtime process.
+///
+///
+///
+/// **Experimental.** This type is part of an experimental wire-protocol surface
+/// and may change or be removed in future SDK or CLI releases.
+///
+///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SessionRemoteGetPolicyInputsResult {
+ /// Resolved MANAGED_REMOTE_CONTROL_SETTING flag, including the runtime default when no boolean override exists.
+ pub managed_remote_control_setting: bool,
+ /// Resolved MANAGED_REMOTE_CONTROL_SETTING_STAFF_OVERRIDE flag, including the runtime default when no boolean override exists.
+ pub managed_remote_control_staff_override: bool,
+ /// Whether ADC_SANDBOX_ID is nonempty in the original session's owning runtime process.
+ pub owner_adc_sandbox: bool,
+ /// Whether CODESPACES is the literal string "true" in the original session's owning runtime process.
+ pub owner_codespaces: bool,
+}
+
/// Persist a steerability change as a `session.remote_steerable_changed` event. Used by the host (CLI / SDK consumer) when it has just finished enabling or disabling steering on a remote exporter that the runtime does not directly own.
///
///
diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs
index 766e1908a2..9e8452ad1d 100644
--- a/rust/src/generated/rpc.rs
+++ b/rust/src/generated/rpc.rs
@@ -13168,6 +13168,34 @@ impl<'a> SessionRpcRemote<'a> {
Ok(())
}
+ /// Reads the original live session's resolved remote-policy feature flags and owner-process environment booleans without changing remote export or steering. Requires the caller's already-established session subscription.
+ ///
+ /// Wire method: `session.remote.getPolicyInputs`.
+ ///
+ /// # Returns
+ ///
+ /// Read-only remote-policy inputs from the original live session and its owning runtime process.
+ ///
+ ///
+ ///
+ /// **Experimental.** This API is part of an experimental wire-protocol surface
+ /// and may change or be removed in future SDK or CLI releases. Pin both the
+ /// SDK and CLI versions if your code depends on it.
+ ///
+ ///
+ pub async fn get_policy_inputs(&self) -> Result
{
+ let wire_params = serde_json::json!({ "sessionId": self.session.id() });
+ let _value = self
+ .session
+ .client()
+ .call(
+ rpc_methods::SESSION_REMOTE_GETPOLICYINPUTS,
+ Some(wire_params),
+ )
+ .await?;
+ Ok(serde_json::from_value(_value)?)
+ }
+
/// Persists a remote-steerability change emitted by the host as a session event.
///
/// Wire method: `session.remote.notifySteerableChanged`.
diff --git a/rust/tests/session_test.rs b/rust/tests/session_test.rs
index 23a254d9e9..52b4438628 100644
--- a/rust/tests/session_test.rs
+++ b/rust/tests/session_test.rs
@@ -24,7 +24,7 @@ use github_copilot_sdk::rpc::{
ConnectorConnectResult, ConnectorContinueRequest, ConnectorDisconnectResult,
ConnectorMcpStatus, ConnectorReconcileOptions, ConnectorReconcileRequest,
ConnectorSessionAccount, ConnectorStatus, ModelSetAllowedModelsRequest, OpenCanvasInstance,
- SendAgentMode, SendMode, SendRequest, SessionRpcConnectors,
+ RemotePolicyInputs, SendAgentMode, SendMode, SendRequest, SessionRpcConnectors,
};
use github_copilot_sdk::session_events::{
ManagedSettingsResolvedSource, McpOauthRequiredData, ReasoningSummary, SessionLimitsConfig,
@@ -7420,6 +7420,154 @@ async fn rpc_namespace_session_connectors_dispatches_all_methods() {
timeout(TIMEOUT, server_handle).await.unwrap().unwrap();
}
+#[tokio::test]
+async fn remote_policy_inputs_preserves_original_session_and_all_boolean_combinations() {
+ let (session, mut server) =
+ create_session_pair_with_config(|cfg| cfg.with_session_id("original-policy-session")).await;
+ let session = Arc::new(session);
+ assert_eq!(session.id().as_str(), "original-policy-session");
+
+ for bits in 0..16 {
+ let expected = serde_json::json!({
+ "managedRemoteControlSetting": bits & 1 != 0,
+ "managedRemoteControlStaffOverride": bits & 2 != 0,
+ "ownerAdcSandbox": bits & 4 != 0,
+ "ownerCodespaces": bits & 8 != 0,
+ });
+ let handle = tokio::spawn({
+ let session = session.clone();
+ async move { session.rpc().remote().get_policy_inputs().await }
+ });
+ let request = timeout(TIMEOUT, server.read_request()).await.unwrap();
+ assert_eq!(request["method"], "session.remote.getPolicyInputs");
+ assert_eq!(
+ request["params"],
+ serde_json::json!({ "sessionId": "original-policy-session" })
+ );
+ server.respond(&request, expected.clone()).await;
+
+ let result: RemotePolicyInputs = timeout(TIMEOUT, handle).await.unwrap().unwrap().unwrap();
+ assert_eq!(result.managed_remote_control_setting, bits & 1 != 0);
+ assert_eq!(result.managed_remote_control_staff_override, bits & 2 != 0);
+ assert_eq!(result.owner_adc_sandbox, bits & 4 != 0);
+ assert_eq!(result.owner_codespaces, bits & 8 != 0);
+ assert_eq!(serde_json::to_value(result).unwrap(), expected);
+ }
+}
+
+#[tokio::test]
+async fn remote_policy_inputs_rejects_missing_and_malformed_required_booleans() {
+ let (session, mut server) = create_session_pair().await;
+ let session = Arc::new(session);
+ let valid = serde_json::json!({
+ "managedRemoteControlSetting": false,
+ "managedRemoteControlStaffOverride": false,
+ "ownerAdcSandbox": false,
+ "ownerCodespaces": false,
+ });
+ let mut malformed = vec![Value::Null, serde_json::json!([]), serde_json::json!({})];
+ for field in [
+ "managedRemoteControlSetting",
+ "managedRemoteControlStaffOverride",
+ "ownerAdcSandbox",
+ "ownerCodespaces",
+ ] {
+ let mut missing = valid.clone();
+ missing.as_object_mut().unwrap().remove(field);
+ malformed.push(missing);
+ for value in [
+ Value::Null,
+ serde_json::json!("false"),
+ serde_json::json!(0),
+ serde_json::json!([]),
+ serde_json::json!({}),
+ ] {
+ let mut response = valid.clone();
+ response[field] = value;
+ malformed.push(response);
+ }
+ }
+
+ for response in malformed {
+ let handle = tokio::spawn({
+ let session = session.clone();
+ async move { session.rpc().remote().get_policy_inputs().await }
+ });
+ let request = timeout(TIMEOUT, server.read_request()).await.unwrap();
+ assert_eq!(request["method"], "session.remote.getPolicyInputs");
+ assert_eq!(
+ request["params"],
+ serde_json::json!({ "sessionId": server.session_id })
+ );
+ server.respond(&request, response.clone()).await;
+
+ let error = timeout(TIMEOUT, handle)
+ .await
+ .unwrap()
+ .unwrap()
+ .expect_err("malformed policy inputs must not default to false");
+ assert_eq!(error.kind(), &ErrorKind::Json, "response: {response}");
+ }
+}
+
+#[tokio::test]
+async fn remote_policy_inputs_propagates_rpc_errors_without_fallback() {
+ let (session, mut server) = create_session_pair().await;
+ let session = Arc::new(session);
+ for (code, message) in [
+ (-32601, "Method not found"),
+ (-32000, "Original session not found"),
+ (-32000, "Original session is not attached"),
+ ] {
+ let handle = tokio::spawn({
+ let session = session.clone();
+ async move { session.rpc().remote().get_policy_inputs().await }
+ });
+ let request = timeout(TIMEOUT, server.read_request()).await.unwrap();
+ assert_eq!(request["method"], "session.remote.getPolicyInputs");
+ assert_eq!(
+ request["params"],
+ serde_json::json!({ "sessionId": server.session_id })
+ );
+ let data = serde_json::json!({ "sessionId": server.session_id });
+ let response = serde_json::json!({
+ "jsonrpc": "2.0",
+ "id": request["id"],
+ "error": { "code": code, "message": message, "data": data },
+ });
+ write_framed(&mut server.write, &serde_json::to_vec(&response).unwrap()).await;
+
+ let error = timeout(TIMEOUT, handle)
+ .await
+ .unwrap()
+ .unwrap()
+ .expect_err("RPC rejection must propagate");
+ assert_eq!(error.kind(), &ErrorKind::Rpc { code });
+ assert_eq!(error.message(), Some(message));
+ assert_eq!(error.rpc_data(), Some(&data));
+ }
+
+ let handle = tokio::spawn(async move { session.rpc().remote().get_policy_inputs().await });
+ let request = timeout(TIMEOUT, server.read_request()).await.unwrap();
+ assert_eq!(request["method"], "session.remote.getPolicyInputs");
+ assert_eq!(
+ request["params"],
+ serde_json::json!({ "sessionId": server.session_id })
+ );
+ server
+ .respond(
+ &request,
+ serde_json::json!({
+ "managedRemoteControlSetting": false,
+ "managedRemoteControlStaffOverride": false,
+ "ownerAdcSandbox": false,
+ "ownerCodespaces": false,
+ }),
+ )
+ .await;
+ timeout(TIMEOUT, handle).await.unwrap().unwrap().unwrap();
+}
+
#[tokio::test]
async fn rpc_namespace_client_models_list_dispatches_correctly() {
let (session, mut server) = create_session_pair().await;