diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-02 02:45:20 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-02 02:45:20 +0300 |
| commit | cc88b3b8e5bb9f7d9f23ed6ae85a52fd7b5b9ed6 (patch) | |
| tree | 4d37c760099070f1a1488b61c7b7271875aafef8 /src/client/shell/state.rs | |
| parent | 18e69891dca486d669a584facd80644bb51f54a2 (diff) | |
feat: add stable client endpoint compatibility (#3509)
* feat: add stable client endpoint compatibility
* fix: keep portable-pty patch checks composable
* ci: run maintenance checks in pull requests
* fix: reject endpoint errors from terminal clients
* feat: keep compatible servers running across updates
Diffstat (limited to 'src/client/shell/state.rs')
| -rw-r--r-- | src/client/shell/state.rs | 49 |
1 files changed, 48 insertions, 1 deletions
diff --git a/src/client/shell/state.rs b/src/client/shell/state.rs index 81173c18..69e77dc5 100644 --- a/src/client/shell/state.rs +++ b/src/client/shell/state.rs @@ -636,10 +636,33 @@ pub(super) enum PendingEndpointKind { pub(super) struct PendingEndpointRequest { pub(super) boot_id: String, + pub(super) method_name: String, pub(super) confirmation_workspace_id: Option<String>, pub(super) kind: PendingEndpointKind, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub(super) enum ClientEndpointNoticeKind { + Unsupported, + Rejected, + Timeout, + Unavailable, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub(super) struct ClientEndpointNoticeKey { + pub(super) boot_id: String, + pub(super) kind: ClientEndpointNoticeKind, + pub(super) code: String, +} + +pub(super) struct ClientVisibleEndpointNotice { + pub(super) key: ClientEndpointNoticeKey, + pub(super) title: String, + pub(super) body: String, + pub(super) deadline: std::time::Instant, +} + pub(crate) struct ClientShellEndpointError { pub code: Option<String>, pub message: String, @@ -836,10 +859,16 @@ pub(crate) struct ClientShellState { pub(super) popup_pending: bool, pub(super) popup_pending_deadline: Option<std::time::Instant>, pub(super) next_request_id: u64, + /// Methods advertised by this endpoint. `None` is used only by local tests + /// and legacy construction paths; negotiated endpoint connections always + /// install an explicit set. + pub(super) endpoint_methods: Option<HashSet<String>>, pub(super) pending_requests: HashMap<String, PendingEndpointRequest>, pub(super) pending_integration_installs: usize, pub(super) pending_notifications: Vec<ClientPendingNotification>, pub(super) visible_notification: Option<ClientVisibleNotification>, + pub(super) endpoint_notice_seen: HashSet<ClientEndpointNoticeKey>, + pub(super) visible_endpoint_notice: Option<ClientVisibleEndpointNotice>, pub(super) outer_focused: Option<bool>, pub(super) ascii_input_source_active: bool, pub(super) pending_input_source_changes: Vec<bool>, @@ -971,10 +1000,13 @@ impl ClientShellState { popup_pending: false, popup_pending_deadline: None, next_request_id: 1, + endpoint_methods: None, pending_requests: HashMap::new(), pending_integration_installs: 0, pending_notifications: Vec::new(), visible_notification: None, + endpoint_notice_seen: HashSet::new(), + visible_endpoint_notice: None, outer_focused: None, ascii_input_source_active: false, pending_input_source_changes: Vec::new(), @@ -987,6 +1019,16 @@ impl ClientShellState { } } + pub(crate) fn set_endpoint_methods(&mut self, methods: Option<Vec<String>>) { + self.endpoint_methods = methods.map(|methods| methods.into_iter().collect()); + } + + pub(super) fn supports_endpoint_method(&self, method: &crate::api::schema::Method) -> bool { + self.endpoint_methods + .as_ref() + .is_none_or(|methods| methods.contains(crate::api::api_method_name(method))) + } + fn focused_tab_count(&self) -> usize { let Some(snapshot) = self.snapshot.as_deref() else { return 0; @@ -1072,7 +1114,10 @@ impl ClientShellState { } } - pub(crate) fn set_snapshot(&mut self, snapshot: Box<ClientShellSnapshot>) { + pub(crate) fn set_snapshot(&mut self, mut snapshot: Box<ClientShellSnapshot>) { + snapshot + .commands + .retain(|command| command.action != crate::protocol::ClientShellCommandAction::Unknown); if self.snapshot.as_ref().is_some_and(|current| { current.boot_id == snapshot.boot_id && snapshot.revision < current.revision }) { @@ -1154,6 +1199,8 @@ impl ClientShellState { self.pending_integration_installs = 0; self.pending_notifications.clear(); self.visible_notification = None; + self.endpoint_notice_seen.clear(); + self.visible_endpoint_notice = None; self.endpoint_error = None; self.navigate_workspace_id = None; self.overlay = self |
