Skip to content
Merged
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
4 changes: 2 additions & 2 deletions crates/protocol/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ pub enum Command {
/// provider profile (Codex, Claude Code).
RefreshProviderUsage,
CheckProviderVersions,
UpdateProvider {
provider: ProviderKind,
UpdateProviders {
providers: Vec<ProviderKind>,
},
SetSidebarCollapsed {
collapsed: bool,
Expand Down
48 changes: 32 additions & 16 deletions crates/protocol/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ pub struct ProvidersStatus {
pub model_catalogs: HashMap<ProviderKind, Vec<agent::ModelSpec>>,
pub models_loading: HashMap<ProviderKind, bool>,
pub provider_versions: HashMap<ProviderKind, ProviderVersionStatus>,
pub update_run: Option<ProviderUpdateRun>,
pub tcode_update: TcodeUpdateStatus,
pub provider_snapshots: HashMap<String, ProviderSnapshot>,
/// Latest account usage per profile id (Codex / Claude Code only).
Expand Down Expand Up @@ -291,6 +292,21 @@ pub enum PluginOperationTarget {
},
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProviderUpdateRun {
pub total: usize,
pub completed: usize,
pub current: Option<ProviderKind>,
pub failed: Vec<ProviderKind>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProviderUpdateAvailable {
pub provider: ProviderKind,
pub version: String,
pub automatic: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct ProviderVersionStatus {
pub installed: Option<String>,
Expand Down Expand Up @@ -501,6 +517,21 @@ pub enum MergeWorktreeFailure {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", content = "content", rename_all = "snake_case")]
pub enum RuntimeToast {
ProviderUpdatesAvailable {
updates: Vec<ProviderUpdateAvailable>,
},
ProviderUpdateStarted {
operation: RuntimeOperationId,
run: ProviderUpdateRun,
},
ProviderUpdateProgress {
operation: RuntimeOperationId,
run: ProviderUpdateRun,
},
ProviderUpdateFinished {
operation: RuntimeOperationId,
run: ProviderUpdateRun,
},
GitBusy,
GitStarted {
operation: RuntimeOperationId,
Expand Down Expand Up @@ -558,8 +589,6 @@ pub enum RuntimeToast {
pub enum RuntimeError {
External(String),
PersistSettings { error: String },
UpdateUnknown { provider: ProviderKind },
UpdateFailed { provider: ProviderKind },
TerminalStart { error: String },
TerminalRestart { error: String },
PersistProject { error: String },
Expand All @@ -586,19 +615,9 @@ pub enum RuntimeError {
#[serde(tag = "type", content = "content", rename_all = "snake_case")]
pub enum RuntimeNotice {
ProviderMessage(String),
UpdateAvailable {
provider: ProviderKind,
version: String,
},
TcodeUpdateAvailable {
version: String,
},
UpdatingProvider {
provider: ProviderKind,
},
UpdateDone {
provider: ProviderKind,
},
NativeRewindCompleted {
mode: RewindMode,
},
Expand Down Expand Up @@ -631,10 +650,7 @@ impl RuntimeNotice {
pub fn severity(&self) -> NoticeSeverity {
match self {
Self::ProviderMessage(_) | Self::WorktreeMergeFailed { .. } => NoticeSeverity::Warning,
Self::UpdateAvailable { .. }
| Self::TcodeUpdateAvailable { .. }
| Self::UpdatingProvider { .. }
| Self::UpdateDone { .. }
Self::TcodeUpdateAvailable { .. }
| Self::NativeRewindCompleted { .. }
| Self::PlanSaved { .. }
| Self::SwitchedBranch { .. }
Expand Down
13 changes: 8 additions & 5 deletions crates/protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ pub use event::{
AcpMarketplaceItem, EventEnvelope, ExternalImportState, ExternalImportStatus, GitActionRequest,
GitStatusStatus, IndexSnapshot, IndexSummary, MergeWorktreeFailure, NoticeSeverity,
PluginCatalogState, PluginChallenge, PluginChallengeKind, PluginOperationTarget,
PluginStaleReason, ProviderPluginCatalog, ProviderVersionStatus, ProvidersStatus,
QueuedMessageStatus, RuntimeEffect, RuntimeError, RuntimeNotice, RuntimeNotification,
RuntimeOperationId, RuntimeToast, ServerEvent, SessionEventRecord, SessionStatus,
TcodeUpdateStatus, TerminalContextStatus, TerminalSplitStatus, TerminalStatus, Topic,
PluginStaleReason, ProviderPluginCatalog, ProviderUpdateAvailable, ProviderUpdateRun,
ProviderVersionStatus, ProvidersStatus, QueuedMessageStatus, RuntimeEffect, RuntimeError,
RuntimeNotice, RuntimeNotification, RuntimeOperationId, RuntimeToast, ServerEvent,
SessionEventRecord, SessionStatus, TcodeUpdateStatus, TerminalContextStatus,
TerminalSplitStatus, TerminalStatus, Topic,
};
pub use query::{
ExternalThread, GitDiffResult, GitDiffScope, GitFileText, HostedDevice, HostingAction,
Expand Down Expand Up @@ -49,7 +50,9 @@ pub use wire::{
// `ProviderKind` has no unknown fallback; version 9 sends Orchestrate rows that
// leave bundled guidance out, which an older peer would read as no guidance.
//
// Unreleased: provider plugin catalogs, their commands, challenges and switches.
// Unreleased: provider plugin catalogs, their commands, challenges and switches;
// provider update availability merged per check round, UpdateProvider replaced
// by UpdateProviders, and sequential update run status with toast progress.
pub const PROTOCOL_VERSION: u32 = 9;

#[cfg(test)]
Expand Down
57 changes: 57 additions & 0 deletions crates/protocol/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,63 @@ use serde_json::json;

use super::*;

#[test]
fn provider_update_command_and_toasts_have_literal_wire_contracts() {
let command = Command::UpdateProviders {
providers: vec![agent::ProviderKind::ClaudeCode, agent::ProviderKind::Codex],
};
let wire =
json!({"type": "update_providers", "content": {"providers": ["claude_code", "codex"]}});
assert_eq!(serde_json::to_value(&command).unwrap(), wire);
assert_eq!(serde_json::from_value::<Command>(wire).unwrap(), command);
let updates = RuntimeToast::ProviderUpdatesAvailable {
updates: vec![ProviderUpdateAvailable {
provider: agent::ProviderKind::Codex,
version: "2.0.0".into(),
automatic: true,
}],
};
let wire = json!({"type": "provider_updates_available", "content": {"updates": [{"provider": "codex", "version": "2.0.0", "automatic": true}]}});
assert_eq!(serde_json::to_value(&updates).unwrap(), wire);
assert_eq!(
serde_json::from_value::<RuntimeToast>(wire).unwrap(),
updates
);
let run = ProviderUpdateRun {
total: 2,
completed: 1,
current: Some(agent::ProviderKind::Codex),
failed: vec![agent::ProviderKind::ClaudeCode],
};
for (name, toast) in [
(
"provider_update_started",
RuntimeToast::ProviderUpdateStarted {
operation: RuntimeOperationId(7),
run: run.clone(),
},
),
(
"provider_update_progress",
RuntimeToast::ProviderUpdateProgress {
operation: RuntimeOperationId(7),
run: run.clone(),
},
),
(
"provider_update_finished",
RuntimeToast::ProviderUpdateFinished {
operation: RuntimeOperationId(7),
run,
},
),
] {
let wire = json!({"type": name, "content": {"operation": 7, "run": {"total": 2, "completed": 1, "current": "codex", "failed": ["claude_code"]}}});
assert_eq!(serde_json::to_value(&toast).unwrap(), wire);
assert_eq!(serde_json::from_value::<RuntimeToast>(wire).unwrap(), toast);
}
}

#[test]
fn provider_update_status_accepts_older_hosts_and_preserves_terminal_requirement() {
let older = json!({
Expand Down
Loading