Skip to content

Commit 8c0cddb

Browse files
MorabbinCopilot
andcommitted
Use generated session capability enum
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8d4d3fa commit 8c0cddb

4 files changed

Lines changed: 95 additions & 374 deletions

File tree

rust/README.md

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -135,36 +135,14 @@ let session = client.resume_session(
135135
| `SessionStore` | `session-store` | Cross-session history tools and session-store metadata |
136136
| `McpApps` | `mcp-apps` | MCP-Apps `ui://` resource passthrough (SEP-1865) |
137137
| `CanvasRenderer` | `canvas-renderer` | Host-rendered extension canvases |
138-
| `Other(String)` | *(verbatim)* | Forward-compat escape hatch for unknown future names |
138+
| `Unknown` | *(none)* | Deserialization fallback; rejected for outbound config |
139139

140140
**Disable-wins semantics.** If the same capability appears in both
141141
`enabled_capabilities` and `disabled_capabilities`, disable wins. The runtime
142142
starts from an `SDK_CAPABILITIES` baseline; enabled capabilities extend it and
143-
disabled capabilities remove from it, in that order.
144-
145-
**Forward compatibility.** The enum is `#[non_exhaustive]` and carries an
146-
`Other(String)` variant so callers on older SDK builds can opt into
147-
capabilities that the runtime adds ahead of a new SDK release, without any
148-
recompile-blocking enum-variant additions:
149-
150-
```rust,ignore
151-
use github_copilot_sdk::{SessionCapability, SessionConfig};
152-
153-
// Opt into a capability the SDK doesn't know about yet.
154-
let config = SessionConfig::default()
155-
.with_enable_capability(SessionCapability::Other("future-cap".to_string()));
156-
```
157-
158-
`&str` and `String` implement `Into<SessionCapability>`, so you can also pass
159-
string literals directly to the builders:
160-
161-
```rust,ignore
162-
use github_copilot_sdk::SessionConfig;
163-
164-
let config = SessionConfig::default()
165-
.with_enable_capability("memory") // &str coerces to SessionCapability
166-
.with_disable_capability("plan-mode");
167-
```
143+
disabled capabilities remove from it, in that order. `SessionCapability::Unknown`
144+
exists only as a generated deserialization fallback and is rejected if supplied
145+
to the create/resume capability builders.
168146

169147
### Session
170148

@@ -802,9 +780,8 @@ gets to be Rust here — cross-SDK parity for these is a post-release
802780
conversation, not a release blocker. None of these are deprecated and
803781
none of them are scheduled for removal.
804782

805-
- **`SessionCapability` enum** -- typed, `#[non_exhaustive]` enum for per-session
806-
capability opt-in / opt-out, with an `Other(String)` escape hatch for
807-
forward compatibility. Sent via `enabledCapabilities` /
783+
- **`SessionCapability` enum** -- generated typed enum for per-session
784+
capability opt-in / opt-out. Sent via `enabledCapabilities` /
808785
`disabledCapabilities` on the `session.create` and `session.resume` wire
809786
calls -- works for all transports including `Transport::External`. See
810787
[Session capabilities](#session-capabilities) above. Marked

rust/src/lib.rs

Lines changed: 0 additions & 256 deletions
Original file line numberDiff line numberDiff line change
@@ -402,162 +402,6 @@ impl OtelExporterType {
402402
}
403403
}
404404

405-
/// A named session capability sent in the `session.create` and
406-
/// `session.resume` wire payloads.
407-
///
408-
/// Capabilities gate optional CLI features (extra tools, system-prompt
409-
/// sections, host-rendered surfaces). The runtime starts from a
410-
/// hard-coded `SDK_CAPABILITIES` set; use
411-
/// [`SessionConfig::with_enable_capability`] /
412-
/// [`SessionConfig::with_disable_capability`] (and their plural
413-
/// counterparts) to opt individual sessions in or out.
414-
///
415-
/// > **Not** the same as [`SessionCapabilities`] — that struct is the
416-
/// > *runtime-negotiated* capability descriptor reported by the CLI on
417-
/// > `session.create`. [`SessionCapability`] is the *opt-in / opt-out
418-
/// > toggle name* sent with each `session.create` / `session.resume`.
419-
/// >
420-
/// > This public type is also separate from the generated protocol enum:
421-
/// > unknown generated enum values collapse to `Unknown`, while callers
422-
/// > need [`Other`](Self::Other) to preserve and send capability names
423-
/// > introduced by newer runtimes.
424-
///
425-
/// The runtime's overlap semantics are **disable-wins**: if a capability
426-
/// appears in both the enabled and disabled lists, the disable wins.
427-
/// The SDK preserves the order callers add capabilities in so the
428-
/// resulting wire payload is deterministic.
429-
///
430-
/// The enum is `#[non_exhaustive]` and carries an [`Other`](Self::Other)
431-
/// variant so forward-compat capabilities the runtime grows ahead of an
432-
/// SDK release can still be opted into without waiting for a new
433-
/// enum variant.
434-
///
435-
/// Requires runtime support for per-session capability controls.
436-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
437-
#[non_exhaustive]
438-
pub enum SessionCapability {
439-
/// TUI-only prompt hints (keyboard shortcuts).
440-
TuiHints,
441-
/// `[[PLAN]]` handling and plan-mode instructions.
442-
PlanMode,
443-
/// `store_memory` tool and the `<memories>` system-prompt section.
444-
Memory,
445-
/// `fetch_copilot_cli_documentation` tool plus the
446-
/// `<self_documentation>` system-prompt section.
447-
CliDocumentation,
448-
/// `ask_user` tool for interactive clarification.
449-
AskUser,
450-
/// Interactive-CLI identity (vs non-interactive / headless).
451-
InteractiveMode,
452-
/// Automatic system notifications to the agent (batched, hidden
453-
/// from the user timeline).
454-
SystemNotifications,
455-
/// Elicitation support (confirm / select / input prompts).
456-
Elicitation,
457-
/// Cross-session history tools and session-store prompt/tool metadata.
458-
SessionStore,
459-
/// MCP-Apps (SEP-1865) `ui://` resource passthrough.
460-
McpApps,
461-
/// Extension-provided canvases rendered by the host.
462-
CanvasRenderer,
463-
/// A capability name the SDK doesn't have a typed variant for yet.
464-
///
465-
/// Pass any kebab-case capability string here to forward it
466-
/// verbatim to the runtime.
467-
Other(String),
468-
}
469-
470-
impl SessionCapability {
471-
/// The kebab-case wire string sent in `enabledCapabilities` /
472-
/// `disabledCapabilities` on `session.create` and `session.resume`.
473-
pub fn as_str(&self) -> &str {
474-
match self {
475-
Self::TuiHints => "tui-hints",
476-
Self::PlanMode => "plan-mode",
477-
Self::Memory => "memory",
478-
Self::CliDocumentation => "cli-documentation",
479-
Self::AskUser => "ask-user",
480-
Self::InteractiveMode => "interactive-mode",
481-
Self::SystemNotifications => "system-notifications",
482-
Self::Elicitation => "elicitation",
483-
Self::SessionStore => "session-store",
484-
Self::McpApps => "mcp-apps",
485-
Self::CanvasRenderer => "canvas-renderer",
486-
Self::Other(name) => name.as_str(),
487-
}
488-
}
489-
490-
fn from_known_name(name: &str) -> Option<Self> {
491-
Some(match name {
492-
"tui-hints" => Self::TuiHints,
493-
"plan-mode" => Self::PlanMode,
494-
"memory" => Self::Memory,
495-
"cli-documentation" => Self::CliDocumentation,
496-
"ask-user" => Self::AskUser,
497-
"interactive-mode" => Self::InteractiveMode,
498-
"system-notifications" => Self::SystemNotifications,
499-
"elicitation" => Self::Elicitation,
500-
"session-store" => Self::SessionStore,
501-
"mcp-apps" => Self::McpApps,
502-
"canvas-renderer" => Self::CanvasRenderer,
503-
_ => return None,
504-
})
505-
}
506-
}
507-
508-
impl std::fmt::Display for SessionCapability {
509-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510-
f.write_str(self.as_str())
511-
}
512-
}
513-
514-
impl std::str::FromStr for SessionCapability {
515-
type Err = std::convert::Infallible;
516-
517-
/// Parse a kebab-case capability name. Unknown names round-trip
518-
/// through [`SessionCapability::Other`] so old SDK builds stay
519-
/// useful against CLIs that add new capabilities. Always returns
520-
/// `Ok` — the error type is [`Infallible`](std::convert::Infallible).
521-
fn from_str(s: &str) -> std::result::Result<Self, std::convert::Infallible> {
522-
Ok(Self::from(s))
523-
}
524-
}
525-
526-
impl From<&str> for SessionCapability {
527-
fn from(s: &str) -> Self {
528-
Self::from_known_name(s).unwrap_or_else(|| Self::Other(s.to_owned()))
529-
}
530-
}
531-
532-
impl From<String> for SessionCapability {
533-
fn from(s: String) -> Self {
534-
if let Some(capability) = Self::from_known_name(&s) {
535-
capability
536-
} else {
537-
Self::Other(s)
538-
}
539-
}
540-
}
541-
542-
impl Serialize for SessionCapability {
543-
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
544-
where
545-
S: serde::Serializer,
546-
{
547-
serializer.serialize_str(self.as_str())
548-
}
549-
}
550-
551-
impl<'de> Deserialize<'de> for SessionCapability {
552-
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
553-
where
554-
D: serde::Deserializer<'de>,
555-
{
556-
let capability = String::deserialize(deserializer)?;
557-
Ok(Self::from(capability))
558-
}
559-
}
560-
561405
/// OpenTelemetry configuration forwarded to the spawned GitHub Copilot CLI
562406
/// process.
563407
///
@@ -2535,106 +2379,6 @@ mod tests {
25352379
assert_eq!(Client::remote_args(&opts), vec!["--remote".to_string()]);
25362380
}
25372381

2538-
#[test]
2539-
fn session_capability_round_trips_via_str() {
2540-
for cap in [
2541-
SessionCapability::TuiHints,
2542-
SessionCapability::PlanMode,
2543-
SessionCapability::Memory,
2544-
SessionCapability::CliDocumentation,
2545-
SessionCapability::AskUser,
2546-
SessionCapability::InteractiveMode,
2547-
SessionCapability::SystemNotifications,
2548-
SessionCapability::Elicitation,
2549-
SessionCapability::SessionStore,
2550-
SessionCapability::McpApps,
2551-
SessionCapability::CanvasRenderer,
2552-
] {
2553-
let s = cap.to_string();
2554-
let parsed: SessionCapability = s.parse().unwrap();
2555-
assert_eq!(parsed, cap, "round-trip failed for {s}");
2556-
}
2557-
}
2558-
2559-
#[test]
2560-
fn session_capability_from_str_falls_back_to_other_for_unknown_names() {
2561-
let parsed: SessionCapability = "brand-new-cap".parse().unwrap();
2562-
assert_eq!(
2563-
parsed,
2564-
SessionCapability::Other("brand-new-cap".to_string())
2565-
);
2566-
assert_eq!(parsed.as_str(), "brand-new-cap");
2567-
}
2568-
2569-
#[test]
2570-
fn session_capability_into_from_str_and_string() {
2571-
let from_str: SessionCapability = "memory".into();
2572-
let from_string: SessionCapability = "memory".to_string().into();
2573-
assert_eq!(from_str, SessionCapability::Memory);
2574-
assert_eq!(from_string, SessionCapability::Memory);
2575-
// Unknown names go to Other
2576-
let other: SessionCapability = "future-cap".into();
2577-
assert_eq!(other, SessionCapability::Other("future-cap".to_string()));
2578-
}
2579-
2580-
#[test]
2581-
fn session_capability_serializes_as_wire_string() {
2582-
assert_eq!(
2583-
serde_json::to_value(SessionCapability::Memory).unwrap(),
2584-
serde_json::json!("memory")
2585-
);
2586-
assert_eq!(
2587-
serde_json::to_value(SessionCapability::Other("future-cap".to_string())).unwrap(),
2588-
serde_json::json!("future-cap")
2589-
);
2590-
}
2591-
2592-
#[test]
2593-
fn session_capability_deserializes_unknown_as_other() {
2594-
let parsed: SessionCapability =
2595-
serde_json::from_value(serde_json::json!("future-cap")).unwrap();
2596-
assert_eq!(parsed, SessionCapability::Other("future-cap".to_string()));
2597-
}
2598-
2599-
#[test]
2600-
fn generated_session_capabilities_have_public_variants() {
2601-
use crate::generated::api_types::SessionCapability as GeneratedSessionCapability;
2602-
2603-
fn expected_wire_name(capability: GeneratedSessionCapability) -> Option<&'static str> {
2604-
match capability {
2605-
GeneratedSessionCapability::TuiHints => Some("tui-hints"),
2606-
GeneratedSessionCapability::PlanMode => Some("plan-mode"),
2607-
GeneratedSessionCapability::Memory => Some("memory"),
2608-
GeneratedSessionCapability::CliDocumentation => Some("cli-documentation"),
2609-
GeneratedSessionCapability::AskUser => Some("ask-user"),
2610-
GeneratedSessionCapability::InteractiveMode => Some("interactive-mode"),
2611-
GeneratedSessionCapability::SystemNotifications => Some("system-notifications"),
2612-
GeneratedSessionCapability::Elicitation => Some("elicitation"),
2613-
GeneratedSessionCapability::SessionStore => Some("session-store"),
2614-
GeneratedSessionCapability::McpApps => Some("mcp-apps"),
2615-
GeneratedSessionCapability::CanvasRenderer => Some("canvas-renderer"),
2616-
GeneratedSessionCapability::Unknown => None,
2617-
}
2618-
}
2619-
2620-
for generated in [
2621-
GeneratedSessionCapability::TuiHints,
2622-
GeneratedSessionCapability::PlanMode,
2623-
GeneratedSessionCapability::Memory,
2624-
GeneratedSessionCapability::CliDocumentation,
2625-
GeneratedSessionCapability::AskUser,
2626-
GeneratedSessionCapability::InteractiveMode,
2627-
GeneratedSessionCapability::SystemNotifications,
2628-
GeneratedSessionCapability::Elicitation,
2629-
GeneratedSessionCapability::SessionStore,
2630-
GeneratedSessionCapability::McpApps,
2631-
GeneratedSessionCapability::CanvasRenderer,
2632-
] {
2633-
let wire_name = expected_wire_name(generated).unwrap();
2634-
assert_eq!(SessionCapability::from(wire_name).as_str(), wire_name);
2635-
}
2636-
}
2637-
26382382
#[test]
26392383
fn log_level_args_omitted_when_unset() {
26402384
let opts = ClientOptions::default();

0 commit comments

Comments
 (0)