Skip to content

Commit ae7635d

Browse files
tclemCopilot
andcommitted
Remove Rust-only Session convenience methods for cross-SDK parity
Same audit shape as the send_telemetry removal: a sweep of public methods that exist as Rust convenience wrappers on top of a typed RPC, with no equivalent surface in the Node, Python, Go, or .NET SDKs (zero matches across all four SDK source trees). The methods are reachable cross-SDK through the typed `rpc()` namespace — Rust callers simply switch from `session.method()` to `session.rpc().<namespace>().<method>()`, mirroring how Node/Python/Go/.NET consumers already drive these. Removed from `Session`: - get_model - set_mode / get_mode - set_name / get_name - read_plan / update_plan / delete_plan - list_workspace_files / read_workspace_file / create_workspace_file - start_fleet - set_approve_all_permissions - call_rpc (generic forwarder; the typed rpc() namespace replaces it) Removed from `Client`: - get_quota (typed `client.rpc().account().get_quota()` already available everywhere including Rust) Kept on Session: send, send_and_wait, abort, set_model, log, disconnect/destroy, subscribe, capabilities, cancellation_token, stop_event_loop, ui (and the field accessors id/cwd/workspace_path/ remote_url). These are either present in every SDK already (abort, set_model, send/send_and_wait, log, disconnect) or are Rust-shape helpers that don't have a typed RPC equivalent (subscribe, cancellation_token, ui sub-API). Updated: - README "Rust-only API" section: dropped the "First-class Session convenience methods" bullet and the Client::get_quota bullet. What remains as Rust-only is now strictly language-shape items (newtypes, Transport enum, permission builders, from_streams, on_auto_mode_switch). - CHANGELOG: dropped enumeration of the removed methods; restated what stays. - 5 integration tests removed (get_name, set_name, list_workspace_files, read_workspace_file, create_workspace_file) plus the dispatch-table case for session.plan.delete. - Unused imports cleaned up in session.rs. Migration for the github-app consumer (the only known caller): - session.set_approve_all_permissions(b) -> session.rpc().permissions().set_approve_all( PermissionsSetApproveAllRequest { enabled: b }) - session.set_mode("plan") -> session.rpc().mode().set(ModeSetRequest { mode: ... }) - session.read_plan() -> session.rpc().plan().read() - (etc — all typed RPC namespace calls) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d511976 commit ae7635d

5 files changed

Lines changed: 6 additions & 276 deletions

File tree

rust/CHANGELOG.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public surface.
4242
`get_last_session_id`, `get_foreground_session_id`,
4343
`set_foreground_session_id`.
4444
- `Client::list_models`, `get_status` (typed `GetStatusResponse`),
45-
`get_auth_status` (typed `GetAuthStatusResponse`), `get_quota`.
45+
`get_auth_status` (typed `GetAuthStatusResponse`).
4646

4747
#### Sessions
4848
- `Client::create_session` and `Client::resume_session` accepting
@@ -54,19 +54,14 @@ public surface.
5454
- `Session::subscribe` returning an `EventSubscription` for observe-only
5555
access to the session's event stream. Implements `tokio_stream::Stream`
5656
and offers an inherent `recv()`; drop the value to unsubscribe.
57-
- Mode + model controls: `get_mode` / `set_mode`, `get_model` /
58-
`set_model(model, SetModelOptions)` with `reasoning_effort` and
59-
`model_capabilities` overrides.
60-
- Plan helpers: `read_plan`, `delete_plan`.
61-
- Workspace helpers: `list_workspace_files`, `read_workspace_file`,
62-
`create_workspace_file`, `cwd`, `remote_url`.
57+
- `Session::set_model(model, SetModelOptions)` with `reasoning_effort`
58+
and `model_capabilities` overrides (matches Node/Python/.NET).
6359
- UI primitives: `session.ui().elicitation()`, `confirm()`, `select()`,
6460
`input()` — grouped under a `SessionUi` sub-API to mirror .NET / Python /
6561
Go.
6662
- `Session::log(message, LogOptions)` with optional severity and
6763
ephemeral flag.
68-
- `Session::start_fleet`, `abort`, `set_approve_all_permissions`,
69-
`set_name`.
64+
- `Session::abort` (matches all other SDKs).
7065
- `Session::disconnect` (canonical) and `Session::destroy` (alias)
7166
preserve on-disk session state for later resume.
7267
- `Session::stop_event_loop` for shutting down the per-session loop.

rust/README.md

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -681,21 +681,6 @@ gets to be Rust here — cross-SDK parity for these is a post-release
681681
conversation, not a release blocker. None of these are deprecated and
682682
none of them are scheduled for removal.
683683

684-
- **`Client::get_quota`** — top-level convenience wrapper for fetching
685-
account-level request quota snapshots. Rust-only as of 0.1.0; the other
686-
SDKs do not expose a client-level shortcut. The underlying
687-
`account.getQuota` JSON-RPC endpoint itself is available cross-SDK via
688-
each SDK's typed `rpc()` namespace (Node
689-
`client.rpc().account().getQuota()`, Python
690-
`client.rpc().account.get_quota()`, Go
691-
`client.Rpc().Account().GetQuota()`, .NET
692-
`client.Rpc().Account().GetQuotaAsync()`), including in Rust at
693-
`client.rpc().account().get_quota()`.
694-
- **First-class `Session` convenience methods**`set_mode` / `get_mode`,
695-
`set_name` / `get_name`, `get_model`, `read_plan` / `update_plan` /
696-
`delete_plan`, `start_fleet`, `list_workspace_files` /
697-
`read_workspace_file` / `create_workspace_file`. The other SDKs require
698-
the consumer to drive the typed JSON-RPC namespace directly for these.
699684
- **Typed newtypes**`SessionId` and `RequestId` are `#[serde(transparent)]`
700685
newtypes around `String`, so the type system distinguishes a session
701686
identifier from an arbitrary `String` at compile time. Node/Python/Go

rust/src/lib.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,21 +1680,6 @@ impl Client {
16801680
}
16811681
}
16821682

1683-
/// Fetch account-level quota snapshots (request-based usage).
1684-
///
1685-
/// This top-level convenience wrapper is Rust-only as of 0.1.0; the Node,
1686-
/// Python, Go, and .NET SDKs do not expose a client-level shortcut for
1687-
/// quota lookup. The underlying `account.getQuota` JSON-RPC endpoint is
1688-
/// itself available cross-SDK via each SDK's typed `rpc()` namespace
1689-
/// (Node `client.rpc().account().getQuota()`, Python
1690-
/// `client.rpc().account.get_quota()`, Go `client.Rpc().Account().GetQuota()`,
1691-
/// .NET `client.Rpc().Account().GetQuotaAsync()`), and in Rust at
1692-
/// `client.rpc().account().get_quota()`. This wrapper is a thin shortcut
1693-
/// for that same call.
1694-
pub async fn get_quota(&self) -> Result<generated::api_types::AccountGetQuotaResult, Error> {
1695-
self.rpc().account().get_quota().await
1696-
}
1697-
16981683
/// Return the OS process ID of the CLI child process, if one was spawned.
16991684
pub fn pid(&self) -> Option<u32> {
17001685
self.inner.child.lock().as_ref().and_then(|c| c.id())

rust/src/session.rs

Lines changed: 2 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ use tokio_util::sync::CancellationToken;
1111
use tracing::{Instrument, warn};
1212

1313
use crate::generated::api_types::{
14-
LogRequest, ModeSetRequest, ModelSwitchToRequest, NameSetRequest, PermissionDecision,
15-
PermissionDecisionApproveOnce, PermissionDecisionApproveOnceKind, PermissionDecisionReject,
16-
PermissionDecisionRejectKind, PlanUpdateRequest, SessionMode, WorkspacesCreateFileRequest,
17-
WorkspacesReadFileRequest,
14+
LogRequest, ModelSwitchToRequest, PermissionDecision, PermissionDecisionApproveOnce,
15+
PermissionDecisionApproveOnceKind, PermissionDecisionReject, PermissionDecisionRejectKind,
1816
};
1917
use crate::generated::session_events::{
2018
CommandExecuteData, ElicitationRequestedData, ExternalToolRequestedData, SessionErrorData,
@@ -320,17 +318,6 @@ impl Session {
320318
Ok(message_id)
321319
}
322320

323-
/// Enable or disable session-wide auto-approval for tool permission requests.
324-
pub async fn set_approve_all_permissions(&self, enabled: bool) -> Result<(), Error> {
325-
self.rpc()
326-
.permissions()
327-
.set_approve_all(
328-
crate::generated::api_types::PermissionsSetApproveAllRequest { enabled },
329-
)
330-
.await?;
331-
Ok(())
332-
}
333-
334321
/// Send a user message and wait for the agent to finish processing.
335322
///
336323
/// Accepts anything convertible to [`MessageOptions`] — pass a `&str` for the
@@ -436,45 +423,6 @@ impl Session {
436423
Ok(())
437424
}
438425

439-
/// Get the current model.
440-
pub async fn get_model(&self) -> Result<Option<String>, Error> {
441-
Ok(self.rpc().model().get_current().await?.model_id)
442-
}
443-
444-
/// Set the session mode (e.g. "interactive", "plan", "autopilot").
445-
pub async fn set_mode(&self, mode: &str) -> Result<String, Error> {
446-
let parsed: SessionMode = serde_json::from_value(Value::String(mode.to_string()))?;
447-
self.rpc()
448-
.mode()
449-
.set(ModeSetRequest { mode: parsed })
450-
.await?;
451-
Ok(mode.to_string())
452-
}
453-
454-
/// Get the current session mode.
455-
pub async fn get_mode(&self) -> Result<String, Error> {
456-
let mode = self.rpc().mode().get().await?;
457-
Ok(serde_json::to_value(mode)?
458-
.as_str()
459-
.unwrap_or("interactive")
460-
.to_string())
461-
}
462-
463-
/// Get the current session name.
464-
pub async fn get_name(&self) -> Result<Option<String>, Error> {
465-
Ok(self.rpc().name().get().await?.name)
466-
}
467-
468-
/// Set the current session name.
469-
pub async fn set_name(&self, name: &str) -> Result<(), Error> {
470-
self.rpc()
471-
.name()
472-
.set(NameSetRequest {
473-
name: name.to_string(),
474-
})
475-
.await
476-
}
477-
478426
/// Disconnect this session from the CLI.
479427
///
480428
/// Sends the `session.destroy` RPC, stops the event loop, and unregisters
@@ -512,55 +460,6 @@ impl Session {
512460
self.disconnect().await
513461
}
514462

515-
/// List files in the session workspace.
516-
pub async fn list_workspace_files(&self) -> Result<Vec<String>, Error> {
517-
Ok(self.rpc().workspaces().list_files().await?.files)
518-
}
519-
520-
/// Read a file from the session workspace.
521-
pub async fn read_workspace_file(&self, path: &Path) -> Result<String, Error> {
522-
Ok(self
523-
.rpc()
524-
.workspaces()
525-
.read_file(WorkspacesReadFileRequest {
526-
path: path.to_string_lossy().into_owned(),
527-
})
528-
.await?
529-
.content)
530-
}
531-
532-
/// Create a file in the session workspace.
533-
pub async fn create_workspace_file(&self, path: &Path, content: &str) -> Result<(), Error> {
534-
self.rpc()
535-
.workspaces()
536-
.create_file(WorkspacesCreateFileRequest {
537-
path: path.to_string_lossy().into_owned(),
538-
content: content.to_string(),
539-
})
540-
.await
541-
}
542-
543-
/// Read the session plan.
544-
pub async fn read_plan(&self) -> Result<(bool, Option<String>), Error> {
545-
let r = self.rpc().plan().read().await?;
546-
Ok((r.exists, r.content))
547-
}
548-
549-
/// Update the session plan.
550-
pub async fn update_plan(&self, content: &str) -> Result<(), Error> {
551-
self.rpc()
552-
.plan()
553-
.update(PlanUpdateRequest {
554-
content: content.to_string(),
555-
})
556-
.await
557-
}
558-
559-
/// Delete the session plan.
560-
pub async fn delete_plan(&self) -> Result<(), Error> {
561-
self.rpc().plan().delete().await
562-
}
563-
564463
/// Write a log message to the session.
565464
///
566465
/// Pass `None` for `opts` to use defaults (info level, persisted).
@@ -607,34 +506,6 @@ impl Session {
607506
}
608507
Ok(())
609508
}
610-
611-
/// Start a fleet of sub-agents.
612-
pub async fn start_fleet(&self, prompt: Option<&str>) -> Result<bool, Error> {
613-
Ok(self
614-
.rpc()
615-
.fleet()
616-
.start(crate::generated::api_types::FleetStartRequest {
617-
prompt: prompt.map(|s| s.to_string()),
618-
})
619-
.await?
620-
.started)
621-
}
622-
623-
/// Generic RPC forwarder — auto-injects sessionId into params.
624-
pub async fn call_rpc(
625-
&self,
626-
method: &str,
627-
extra_params: Option<Value>,
628-
) -> Result<Value, Error> {
629-
let mut params = serde_json::json!({ "sessionId": self.id });
630-
let extra_obj = extra_params.as_ref().and_then(Value::as_object);
631-
if let (Some(base), Some(extra_obj)) = (params.as_object_mut(), extra_obj) {
632-
for (k, v) in extra_obj {
633-
base.insert(k.clone(), v.clone());
634-
}
635-
}
636-
self.client.call(method, Some(params)).await
637-
}
638509
}
639510

640511
impl Drop for Session {

rust/tests/session_test.rs

Lines changed: 0 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,6 @@ async fn session_rpc_methods_send_correct_method_names() {
377377

378378
let cases: Vec<(&str, Option<&str>)> = vec![
379379
("session.abort", None),
380-
("session.plan.delete", None),
381380
("session.log", Some("message")),
382381
("session.destroy", None),
383382
];
@@ -387,7 +386,6 @@ async fn session_rpc_methods_send_correct_method_names() {
387386
let handle = tokio::spawn(async move {
388387
match expected_method {
389388
"session.abort" => s.abort().await.map(|_| ()),
390-
"session.plan.delete" => s.delete_plan().await,
391389
"session.log" => s.log("test msg", None).await,
392390
"session.destroy" => s.destroy().await,
393391
_ => unreachable!(),
@@ -1008,46 +1006,6 @@ async fn set_model_sends_switch_to_request() {
10081006
timeout(TIMEOUT, handle).await.unwrap().unwrap();
10091007
}
10101008

1011-
#[tokio::test]
1012-
async fn get_name_returns_name() {
1013-
let (session, mut server) = create_session_pair(Arc::new(NoopHandler)).await;
1014-
let session = Arc::new(session);
1015-
1016-
let handle = tokio::spawn({
1017-
let session = session.clone();
1018-
async move { session.get_name().await.unwrap() }
1019-
});
1020-
1021-
let request = server.read_request().await;
1022-
assert_eq!(request["method"], "session.name.get");
1023-
server
1024-
.respond(&request, serde_json::json!({ "name": "Fix input flicker" }))
1025-
.await;
1026-
1027-
assert_eq!(
1028-
timeout(TIMEOUT, handle).await.unwrap().unwrap(),
1029-
Some("Fix input flicker".to_string())
1030-
);
1031-
}
1032-
1033-
#[tokio::test]
1034-
async fn set_name_sends_name() {
1035-
let (session, mut server) = create_session_pair(Arc::new(NoopHandler)).await;
1036-
let session = Arc::new(session);
1037-
1038-
let handle = tokio::spawn({
1039-
let session = session.clone();
1040-
async move { session.set_name("Fix input flicker").await.unwrap() }
1041-
});
1042-
1043-
let request = server.read_request().await;
1044-
assert_eq!(request["method"], "session.name.set");
1045-
assert_eq!(request["params"]["name"], "Fix input flicker");
1046-
server.respond(&request, serde_json::json!(null)).await;
1047-
1048-
timeout(TIMEOUT, handle).await.unwrap().unwrap();
1049-
}
1050-
10511009
#[tokio::test]
10521010
async fn elicitation_returns_typed_result() {
10531011
let (session, mut server) = create_session_pair_with_capabilities(
@@ -2407,70 +2365,6 @@ async fn system_message_transform_returns_error_for_missing_sections() {
24072365
assert_eq!(response["error"]["code"], -32602);
24082366
}
24092367

2410-
#[tokio::test]
2411-
async fn list_workspace_files_uses_plural_method_name() {
2412-
let (session, mut server) = create_session_pair(Arc::new(NoopHandler)).await;
2413-
let session = Arc::new(session);
2414-
2415-
let s = session.clone();
2416-
let handle = tokio::spawn(async move { s.list_workspace_files().await });
2417-
2418-
let request = server.read_request().await;
2419-
assert_eq!(request["method"], "session.workspaces.listFiles");
2420-
assert_eq!(request["params"]["sessionId"], server.session_id);
2421-
server
2422-
.respond(
2423-
&request,
2424-
serde_json::json!({ "files": ["a.txt", "subdir/b.txt"] }),
2425-
)
2426-
.await;
2427-
2428-
let files = timeout(TIMEOUT, handle).await.unwrap().unwrap().unwrap();
2429-
assert_eq!(files, vec!["a.txt".to_string(), "subdir/b.txt".to_string()]);
2430-
}
2431-
2432-
#[tokio::test]
2433-
async fn read_workspace_file_uses_plural_method_name_and_forwards_path() {
2434-
let (session, mut server) = create_session_pair(Arc::new(NoopHandler)).await;
2435-
let session = Arc::new(session);
2436-
2437-
let s = session.clone();
2438-
let handle =
2439-
tokio::spawn(async move { s.read_workspace_file(Path::new("notes/plan.md")).await });
2440-
2441-
let request = server.read_request().await;
2442-
assert_eq!(request["method"], "session.workspaces.readFile");
2443-
assert_eq!(request["params"]["sessionId"], server.session_id);
2444-
assert_eq!(request["params"]["path"], "notes/plan.md");
2445-
server
2446-
.respond(&request, serde_json::json!({ "content": "hello" }))
2447-
.await;
2448-
2449-
let content = timeout(TIMEOUT, handle).await.unwrap().unwrap().unwrap();
2450-
assert_eq!(content, "hello");
2451-
}
2452-
2453-
#[tokio::test]
2454-
async fn create_workspace_file_uses_plural_method_name_and_forwards_payload() {
2455-
let (session, mut server) = create_session_pair(Arc::new(NoopHandler)).await;
2456-
let session = Arc::new(session);
2457-
2458-
let s = session.clone();
2459-
let handle = tokio::spawn(async move {
2460-
s.create_workspace_file(Path::new("notes/plan.md"), "body")
2461-
.await
2462-
});
2463-
2464-
let request = server.read_request().await;
2465-
assert_eq!(request["method"], "session.workspaces.createFile");
2466-
assert_eq!(request["params"]["sessionId"], server.session_id);
2467-
assert_eq!(request["params"]["path"], "notes/plan.md");
2468-
assert_eq!(request["params"]["content"], "body");
2469-
server.respond(&request, serde_json::json!({})).await;
2470-
2471-
timeout(TIMEOUT, handle).await.unwrap().unwrap().unwrap();
2472-
}
2473-
24742368
#[tokio::test]
24752369
async fn rpc_namespace_session_agent_list_dispatches_correctly() {
24762370
let (session, mut server) = create_session_pair(Arc::new(NoopHandler)).await;

0 commit comments

Comments
 (0)