diff --git a/crates/client/src/backend_runtime.rs b/crates/client/src/backend_runtime.rs index 76cc5884a..3acf5a36f 100644 --- a/crates/client/src/backend_runtime.rs +++ b/crates/client/src/backend_runtime.rs @@ -5,7 +5,6 @@ pub use server_api::{ CreateWorkspaceWorkerRequest as BackendCreateWorkerRequest, Diagnostic as BackendDiagnostic, DiagnosticSeverity as BackendDiagnosticSeverity, ListResponse as BackendRuntimeListResponse, RuntimeSummary as BackendRuntimeSummary, - WorkerCapabilitySummary as BackendWorkerCapabilitySummary, WorkerImplementationSummary as BackendWorkerImplementationSummary, WorkerLaunchOptionsResponse as BackendWorkerLaunchOptions, WorkerLaunchProfileCandidate as BackendWorkerLaunchProfileCandidate, @@ -869,7 +868,6 @@ mod tests { "pinned": false, "retention_state": "resident", "implementation": {"kind": "embedded", "display_hint": "Embedded"}, - "capabilities": {"can_stop": true, "can_spawn_followup": false}, "diagnostics": [] }, "diagnostics": [] @@ -952,10 +950,11 @@ mod tests { "label": "Coder", "workspace": {"visibility": "workspace", "identity": "workspace"}, "state": "idle", + "availability": "observed", "implementation": {"kind": "worker", "display_hint": "Coder"}, - "capabilities": {"can_stop": true, "can_spawn_followup": false}, "workdir_attachments": [{ "alias": "checkout", + "effective_permissions": {"read": true, "write": true, "command": true}, "working_directory": { "working_directory_id": "wd-1", "source": {"kind": "repository", "repository_key": "main"}, diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 4627b87ff..32e1c1a10 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -24,7 +24,7 @@ pub use backend_runtime::{ BackendCreateWorkerRequest, BackendCreateWorkerResponse, BackendDiagnostic, BackendDiagnosticSeverity, BackendRuntimeClientError, BackendRuntimeListResponse, BackendRuntimeListTarget, BackendRuntimeSummary, BackendRuntimeTarget, - BackendWorkerCapabilitySummary, BackendWorkerImplementationSummary, BackendWorkerLaunchOptions, + BackendWorkerImplementationSummary, BackendWorkerLaunchOptions, BackendWorkerLaunchProfileCandidate, BackendWorkerLaunchRuntimeOption, BackendWorkerLaunchTarget, BackendWorkerOperationState, BackendWorkerRestoreResponse, BackendWorkerRestoreResult, BackendWorkerRestoreState, BackendWorkerSummary, diff --git a/crates/client/src/workspace_product.rs b/crates/client/src/workspace_product.rs index 32a360451..6426efafd 100644 --- a/crates/client/src/workspace_product.rs +++ b/crates/client/src/workspace_product.rs @@ -1029,7 +1029,7 @@ mod tests { ), ( "200 OK", - r#"{"workspace_id":"workspace-a","runtime_id":"embedded","worker_id":"worker-1","console_href":"/w/workspace-a/workers/worker-1","worker":{"runtime_id":"embedded","worker_id":"worker-1","host_id":"embedded","display_name":"Intake","label":"worker-1","profile":"builtin:intake","singleton_key":null,"tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":false,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"capabilities":{"can_stop":true,"can_spawn_followup":false},"diagnostics":[]},"diagnostics":[]}"#, + r#"{"workspace_id":"workspace-a","runtime_id":"embedded","worker_id":"worker-1","console_href":"/w/workspace-a/workers/worker-1","worker":{"runtime_id":"embedded","worker_id":"worker-1","host_id":"embedded","display_name":"Intake","label":"worker-1","profile":"builtin:intake","singleton_key":null,"tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":false,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"diagnostics":[]},"diagnostics":[]}"#, ), ]); let client = BackendWorkspaceProductClient::new_with_access_token( @@ -1057,7 +1057,7 @@ mod tests { #[test] fn workspace_orchestrator_launch_uses_scoped_backend_route() { - let body = r#"{"workspace_id":"workspace-a","online":true,"disposition":"created","worker":{"runtime_id":"embedded","worker_id":"worker-2","host_id":"embedded","display_name":"Orchestrator","label":"worker-2","profile":"builtin:orchestrator","singleton_key":"workspace-orchestrator","tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":true,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"capabilities":{"can_stop":true,"can_spawn_followup":false},"diagnostics":[]},"diagnostics":[]}"#; + let body = r#"{"workspace_id":"workspace-a","online":true,"disposition":"created","worker":{"runtime_id":"embedded","worker_id":"worker-2","host_id":"embedded","display_name":"Orchestrator","label":"worker-2","profile":"builtin:orchestrator","singleton_key":"workspace-orchestrator","tags":[],"workspace":{"visibility":"workspace","identity":"workspace-a","workspace_id":"workspace-a"},"state":"idle","last_seen_at":null,"pinned":true,"retention_state":"active","implementation":{"kind":"runtime","display_hint":"Runtime Worker"},"diagnostics":[]},"diagnostics":[]}"#; let (base_url, request, handle) = one_response_server("200 OK", body); let client = BackendWorkspaceProductClient::new_with_access_token( base_url, diff --git a/crates/server-api/src/lib.rs b/crates/server-api/src/lib.rs index 5b14e83c9..bff7dbe1e 100644 --- a/crates/server-api/src/lib.rs +++ b/crates/server-api/src/lib.rs @@ -6892,7 +6892,6 @@ pub struct RuntimeWorkerSummary { #[serde(default)] pub retention_state: String, pub implementation: WorkerImplementationSummary, - pub capabilities: WorkerCapabilitySummary, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub workdir_attachments: Vec, #[serde(default)] @@ -7888,13 +7887,6 @@ pub struct WorkerImplementationSummary { pub display_hint: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] -#[cfg_attr(feature = "typescript", derive(ts_rs::TS))] -pub struct WorkerCapabilitySummary { - pub can_stop: bool, - pub can_spawn_followup: bool, -} - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[cfg_attr(feature = "typescript", derive(ts_rs::TS))] #[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] @@ -8120,7 +8112,6 @@ pub struct WorkerSummary { #[serde(default)] pub retention_state: String, pub implementation: WorkerImplementationSummary, - pub capabilities: WorkerCapabilitySummary, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub workdir_attachments: Vec, #[serde(default)] @@ -8163,7 +8154,6 @@ pub struct WorkerLaunchWorkerSummary { pub pinned: bool, pub retention_state: String, pub implementation: WorkerImplementationSummary, - pub capabilities: WorkerCapabilitySummary, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub workdir_attachments: Vec, #[serde(default)] @@ -9339,7 +9329,6 @@ pub fn worker_launch_api_typescript() -> String { WorkingDirectorySummary::decl(&config), WorkerWorkspaceSummary::decl(&config), WorkerImplementationSummary::decl(&config), - WorkerCapabilitySummary::decl(&config), RuntimeWorkerWorkdirAttachmentSummary::decl(&config), WorkerLaunchWorkerSummary::decl(&config), WorkerLaunchRuntimeOption::decl(&config), @@ -10308,10 +10297,6 @@ mod tests { kind: "runtime".to_string(), display_hint: "Runtime Worker".to_string(), }, - capabilities: WorkerCapabilitySummary { - can_stop: true, - can_spawn_followup: false, - }, workdir_attachments: Vec::new(), diagnostics: Vec::new(), } @@ -10791,7 +10776,6 @@ mod tests { }, "state": "idle", "implementation": {"kind": "worker", "display_hint": "Coder"}, - "capabilities": {"can_stop": true, "can_spawn_followup": false} }); assert!(serde_json::from_value::(payload).is_err()); diff --git a/crates/tui/src/backend_worker_picker.rs b/crates/tui/src/backend_worker_picker.rs index 8fa950f51..0b67597cb 100644 --- a/crates/tui/src/backend_worker_picker.rs +++ b/crates/tui/src/backend_worker_picker.rs @@ -552,9 +552,8 @@ fn working_directory_text(worker: &BackendWorkerSummary) -> String { mod tests { use super::*; use client::{ - BackendDiagnostic, BackendDiagnosticSeverity, BackendWorkerCapabilitySummary, - BackendWorkerImplementationSummary, BackendWorkerRestoreResult, - BackendWorkerWorkspaceSummary, + BackendDiagnostic, BackendDiagnosticSeverity, BackendWorkerImplementationSummary, + BackendWorkerRestoreResult, BackendWorkerWorkspaceSummary, }; fn worker(runtime_id: &str, worker_id: &str, profile: Option<&str>) -> BackendWorkerSummary { @@ -588,10 +587,6 @@ mod tests { kind: "embedded".to_string(), display_hint: "embedded".to_string(), }, - capabilities: BackendWorkerCapabilitySummary { - can_stop: true, - can_spawn_followup: false, - }, workdir_attachments: Vec::new(), diagnostics: Vec::new(), } diff --git a/crates/worker-runtime/src/worker_source.rs b/crates/worker-runtime/src/worker_source.rs index cb4d322f2..1a7e5d4da 100644 --- a/crates/worker-runtime/src/worker_source.rs +++ b/crates/worker-runtime/src/worker_source.rs @@ -1298,7 +1298,9 @@ mod tests { #[test] fn runtime_owned_workspace_client_has_no_fixed_request_timeout() { - let (base_url, server) = delayed_workspace_response(Duration::from_millis(75)); + // This must exceed reqwest::blocking::Client's default 30-second timeout. + // A short delay only checks our Option field, not the effective HTTP deadline. + let (base_url, server) = delayed_workspace_response(Duration::from_secs(31)); let client = RuntimeOwnedWorkspaceClient::new("workspace-a", base_url, "runtime-a", "worker-a"); assert_eq!(client.request_timeout, None); diff --git a/crates/workspace-server/src/hosts.rs b/crates/workspace-server/src/hosts.rs index 2e8fd65b1..6e86ec96a 100644 --- a/crates/workspace-server/src/hosts.rs +++ b/crates/workspace-server/src/hosts.rs @@ -240,12 +240,6 @@ pub struct InternalWorkerImplementationSummary { pub display_hint: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct InternalWorkerCapabilitySummary { - pub can_stop: bool, - pub can_spawn_followup: bool, -} - #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct InternalWorkerSummary { #[serde(flatten)] @@ -273,7 +267,6 @@ pub struct InternalWorkerSummary { #[serde(default)] pub retention_state: String, pub implementation: InternalWorkerImplementationSummary, - pub capabilities: InternalWorkerCapabilitySummary, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub workdir_attachments: Vec, pub diagnostics: Vec, @@ -384,10 +377,6 @@ pub(crate) fn workspace_worker_summary( kind: summary.implementation.kind, display_hint: summary.implementation.display_hint, }, - capabilities: server_api::WorkerCapabilitySummary { - can_stop: summary.capabilities.can_stop, - can_spawn_followup: summary.capabilities.can_spawn_followup, - }, workdir_attachments, diagnostics: summary.diagnostics.into_iter().map(Into::into).collect(), } @@ -2231,10 +2220,6 @@ impl EmbeddedWorkerRuntime { Some(EmbeddedWorkerRef::new(EmbeddedWorkerId::parse(worker_id)?)) } - fn can_stop_embedded_worker(&self, status: EmbeddedWorkerStatus) -> bool { - runtime_worker_can_stop(self.execution_enabled, status) - } - fn map_worker_summary( &self, summary: worker_runtime::catalog::WorkerSummary, @@ -2274,11 +2259,6 @@ impl EmbeddedWorkerRuntime { kind: "embedded_worker_runtime".to_string(), display_hint: "backend-internal worker-runtime Worker".to_string(), }, - capabilities: InternalWorkerCapabilitySummary { - can_stop: summary.execution_metadata_available - && self.can_stop_embedded_worker(summary.status), - can_spawn_followup: false, - }, workdir_attachments: summary.workdir_attachments, diagnostics: embedded_worker_projection_diagnostics( summary.execution_metadata_available, @@ -2319,11 +2299,6 @@ impl EmbeddedWorkerRuntime { kind: "embedded_worker_runtime".to_string(), display_hint: "backend-internal worker-runtime Worker".to_string(), }, - capabilities: InternalWorkerCapabilitySummary { - can_stop: detail.execution_metadata_available - && self.can_stop_embedded_worker(detail.status), - can_spawn_followup: false, - }, workdir_attachments: detail.workdir_attachments, diagnostics: embedded_worker_projection_diagnostics( detail.execution_metadata_available, @@ -4127,11 +4102,6 @@ impl RemoteWorkerRuntime { kind: "remote_worker_runtime".to_string(), display_hint: "Backend-proxied remote worker-runtime Worker".to_string(), }, - capabilities: InternalWorkerCapabilitySummary { - can_stop: summary.execution_metadata_available - && runtime_worker_can_stop(true, summary.status), - can_spawn_followup: false, - }, workdir_attachments: summary.workdir_attachments, diagnostics: remote_worker_projection_diagnostics(summary.execution_metadata_available), } @@ -4170,11 +4140,6 @@ impl RemoteWorkerRuntime { kind: "remote_worker_runtime".to_string(), display_hint: "Backend-proxied remote worker-runtime Worker".to_string(), }, - capabilities: InternalWorkerCapabilitySummary { - can_stop: detail.execution_metadata_available - && runtime_worker_can_stop(true, detail.status), - can_spawn_followup: false, - }, workdir_attachments: detail.workdir_attachments, diagnostics: remote_worker_projection_diagnostics(detail.execution_metadata_available), } @@ -5152,10 +5117,6 @@ fn embedded_runtime_status_label(status: RuntimeStatus) -> &'static str { } } -fn runtime_worker_can_stop(execution_enabled: bool, status: EmbeddedWorkerStatus) -> bool { - execution_enabled && status.is_active() -} - fn embedded_worker_status_label(status: EmbeddedWorkerStatus) -> &'static str { match status { EmbeddedWorkerStatus::Idle => "idle", @@ -5963,10 +5924,6 @@ pub fn placeholder_worker(host_id: impl Into) -> InternalWorkerSummary { kind: "placeholder".to_string(), display_hint: "unsupported".to_string(), }, - capabilities: InternalWorkerCapabilitySummary { - can_stop: false, - can_spawn_followup: false, - }, workdir_attachments: Vec::new(), diagnostics: vec![diagnostic( "runtime_capability_unsupported", @@ -6621,10 +6578,6 @@ mod tests { kind: "fixture".to_string(), display_hint: "test fixture".to_string(), }, - capabilities: InternalWorkerCapabilitySummary { - can_stop: false, - can_spawn_followup: false, - }, workdir_attachments: Vec::new(), diagnostics: Vec::new(), }], @@ -7123,7 +7076,12 @@ mod tests { let spawned = runtime.spawn_worker(test_create_binding(), embedded_spawn_request()); assert_eq!(spawned.state, InternalWorkerOperationState::Accepted); let worker = spawned.worker.expect("created embedded worker"); - assert!(worker.capabilities.can_stop); + assert!( + serde_json::to_value(&worker) + .unwrap() + .get("capabilities") + .is_none() + ); let input = runtime.send_input( &worker.worker.worker_id, @@ -7496,7 +7454,12 @@ mod tests { workers.items[0].workspace.identity, "runtime_registry_worker" ); - assert!(workers.items[0].capabilities.can_stop); + assert!( + serde_json::to_value(&workers.items[0]) + .unwrap() + .get("capabilities") + .is_none() + ); let input = registry .send_input( @@ -7581,11 +7544,14 @@ mod tests { let workers = registry.list_workers(10); assert_eq!(workers.items.len(), 5); - assert!(!workers.items[0].capabilities.can_stop); - assert!(workers.items[1].capabilities.can_stop); - assert!(workers.items[2].capabilities.can_stop); - assert!(workers.items[3].capabilities.can_stop); - assert!(!workers.items[4].capabilities.can_stop); + for worker in &workers.items { + assert!( + serde_json::to_value(worker) + .unwrap() + .get("capabilities") + .is_none() + ); + } assert_eq!(workers.items[0].state, "stopped"); assert_eq!(workers.items[1].state, "running"); assert_eq!(workers.items[2].state, "paused"); @@ -7601,7 +7567,12 @@ mod tests { let stopped_detail = registry .worker(&RuntimeWorkerRef::new("remote:primary", &worker_id)) .unwrap(); - assert!(!stopped_detail.capabilities.can_stop); + assert!( + serde_json::to_value(&stopped_detail) + .unwrap() + .get("capabilities") + .is_none() + ); assert_eq!(stopped_detail.state, "stopped"); server.join().expect("mock remote server finished"); diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index 8e38123e9..d933b23b4 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -152,17 +152,16 @@ use crate::companion::{ use crate::config_source::ConfigCommitRequest; use crate::hosts::{ EMBEDDED_RUNTIME_ID, EmbeddedWorkerRuntime, HostDiagnosticSeverity, InternalHostSummary, - InternalWorkerCapabilitySummary, InternalWorkerImplementationSummary, - InternalWorkerOperationState, InternalWorkerRestoreResult, InternalWorkerSummary, - InternalWorkerWorkspaceSummary, RemoteRuntimeConfig, RemoteWorkerRuntime, RuntimeDiagnostic, - RuntimePingFailureKind, RuntimeRegistry, RuntimeRegistryError, RuntimeRegistryUnregisterResult, - TicketWorkerRole, WorkerCompletionsRequest, WorkerCompletionsResult, WorkerControlOperation, - WorkerCreateBinding, WorkerInputKind, WorkerInputRequest, WorkerInputResult, - WorkerLifecycleRequest, WorkerLifecycleResult, WorkerSpawnAcceptanceRequirement, - WorkerSpawnIntent, WorkerSpawnRequest, WorkerSpawnResult, WorkerSpawnWorkingDirectoryRequest, - WorkerTicketAssignmentRequest, WorkspaceRuntimeAuthorization, - is_disallowed_remote_runtime_address, is_loopback_runtime_origin, - worker_spawn_create_fingerprint, workspace_worker_summary, + InternalWorkerImplementationSummary, InternalWorkerOperationState, InternalWorkerRestoreResult, + InternalWorkerSummary, InternalWorkerWorkspaceSummary, RemoteRuntimeConfig, + RemoteWorkerRuntime, RuntimeDiagnostic, RuntimePingFailureKind, RuntimeRegistry, + RuntimeRegistryError, RuntimeRegistryUnregisterResult, TicketWorkerRole, + WorkerCompletionsRequest, WorkerCompletionsResult, WorkerControlOperation, WorkerCreateBinding, + WorkerInputKind, WorkerInputRequest, WorkerInputResult, WorkerLifecycleRequest, + WorkerLifecycleResult, WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, WorkerSpawnRequest, + WorkerSpawnResult, WorkerSpawnWorkingDirectoryRequest, WorkerTicketAssignmentRequest, + WorkspaceRuntimeAuthorization, is_disallowed_remote_runtime_address, + is_loopback_runtime_origin, worker_spawn_create_fingerprint, workspace_worker_summary, }; use crate::memory_backend::execute_memory_backend_operation_with_authority; use crate::memory_staging::{ @@ -5175,10 +5174,6 @@ fn runtime_worker_summary_to_api( kind: worker.implementation.kind, display_hint: worker.implementation.display_hint, }, - capabilities: server_api::WorkerCapabilitySummary { - can_stop: worker.capabilities.can_stop, - can_spawn_followup: worker.capabilities.can_spawn_followup, - }, workdir_attachments: worker .workdir_attachments .into_iter() @@ -16956,10 +16951,6 @@ fn worker_launch_worker_summary(worker: InternalWorkerSummary) -> WorkerLaunchWo kind: worker.implementation.kind, display_hint: worker.implementation.display_hint, }, - capabilities: server_api::WorkerCapabilitySummary { - can_stop: worker.capabilities.can_stop, - can_spawn_followup: worker.capabilities.can_spawn_followup, - }, workdir_attachments: worker .workdir_attachments .into_iter() @@ -24806,10 +24797,6 @@ fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> InternalWorker last_seen_at: Some(record.updated_at.clone()), pinned: record.retention_state == "pinned", retention_state: record.retention_state.clone(), - capabilities: InternalWorkerCapabilitySummary { - can_stop: false, - can_spawn_followup: false, - }, workspace: InternalWorkerWorkspaceSummary { visibility: "backend_registry".to_string(), identity: record.workspace_id.clone(), @@ -24855,8 +24842,6 @@ fn worker_summary_from_projection( .then(|| observation.worker.worker_state.clone()) .flatten(); summary.last_seen_at = Some(observation.observed_at.clone()); - summary.capabilities.can_stop = observed - && observation.worker.state != protocol::subscription::SubscriptionWorkerState::Stopped; summary.implementation.display_hint = if observed { "Runtime-backed Worker".to_string() } else { @@ -26709,7 +26694,12 @@ mod tests { assert_eq!(unavailable.availability, availability); assert_eq!(unavailable.state, "unavailable"); assert_eq!(unavailable.worker_state, None); - assert!(!unavailable.capabilities.can_stop); + assert!( + serde_json::to_value(&unavailable) + .unwrap() + .get("capabilities") + .is_none() + ); let mut observed_stopped = projection; let observation = observed_stopped.observation.as_mut().unwrap(); @@ -28645,10 +28635,6 @@ mod tests { kind: "fixture".to_string(), display_hint: "Workdirless fixture".to_string(), }, - capabilities: InternalWorkerCapabilitySummary { - can_stop: true, - can_spawn_followup: false, - }, workdir_attachments: Vec::new(), diagnostics: Vec::new(), } @@ -40983,10 +40969,6 @@ mod tests { kind: "remote".to_string(), display_hint: "remote".to_string(), }, - capabilities: server_api::WorkerCapabilitySummary { - can_stop: true, - can_spawn_followup: false, - }, workdir_attachments: Vec::new(), diagnostics: Vec::new(), } @@ -41081,10 +41063,6 @@ mod tests { kind: "remote".to_string(), display_hint: "remote".to_string(), }, - capabilities: server_api::WorkerCapabilitySummary { - can_stop: true, - can_spawn_followup: false, - }, workdir_attachments: Vec::new(), diagnostics: Vec::new(), }], @@ -43445,7 +43423,12 @@ mod tests { // catalog remains controllable while its failed execution is represented in // `worker_state` rather than by removing the catalog Worker. assert_eq!(restored_worker.state, "idle"); - assert!(restored_worker.capabilities.can_stop); + assert!( + serde_json::to_value(&restored_worker) + .unwrap() + .get("capabilities") + .is_none() + ); let bundles = restored .runtime diff --git a/openapi/server-api.json b/openapi/server-api.json index bc652123f..197279460 100644 --- a/openapi/server-api.json +++ b/openapi/server-api.json @@ -7448,9 +7448,6 @@ "$ref": "#/components/schemas/SubscriptionWorkerAvailability", "description": "Freshness of the Runtime observation used to construct `worker_state`.\n`Unavailable` means no current controller snapshot is being asserted." }, - "capabilities": { - "$ref": "#/components/schemas/WorkerCapabilitySummary" - }, "diagnostics": { "default": [], "items": { @@ -7550,7 +7547,6 @@ "availability", "state", "implementation", - "capabilities", "updated_at" ], "type": "object" @@ -7791,9 +7787,6 @@ "RuntimeWorkerSummary": { "additionalProperties": false, "properties": { - "capabilities": { - "$ref": "#/components/schemas/WorkerCapabilitySummary" - }, "diagnostics": { "default": [], "items": { @@ -7872,8 +7865,7 @@ "label", "workspace", "state", - "implementation", - "capabilities" + "implementation" ], "type": "object" }, @@ -11254,21 +11246,6 @@ } ] }, - "WorkerCapabilitySummary": { - "properties": { - "can_spawn_followup": { - "type": "boolean" - }, - "can_stop": { - "type": "boolean" - } - }, - "required": [ - "can_stop", - "can_spawn_followup" - ], - "type": "object" - }, "WorkerFileDeleteResponse": { "additionalProperties": false, "properties": { @@ -11447,9 +11424,6 @@ "additionalProperties": false, "description": "Runtime-owned Worker summary embedded in Worker launch responses.\n\nThis preserves the existing launch wire shape. Workspace-owned Worker list\nand detail responses use [`WorkerSummary`] instead.", "properties": { - "capabilities": { - "$ref": "#/components/schemas/WorkerCapabilitySummary" - }, "diagnostics": { "default": [], "items": { @@ -11529,8 +11503,7 @@ "state", "pinned", "retention_state", - "implementation", - "capabilities" + "implementation" ], "type": "object" }, @@ -11830,9 +11803,6 @@ "$ref": "#/components/schemas/SubscriptionWorkerAvailability", "description": "Freshness of the Runtime observation used to construct `worker_state`.\n`Unavailable` means no current controller snapshot is being asserted." }, - "capabilities": { - "$ref": "#/components/schemas/WorkerCapabilitySummary" - }, "diagnostics": { "default": [], "items": { @@ -11928,8 +11898,7 @@ "workspace", "availability", "state", - "implementation", - "capabilities" + "implementation" ], "type": "object" }, @@ -14020,7 +13989,7 @@ "info": { "title": "Yoi Server API", "version": "0.1.0", - "x-yoi-source-digest": "sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0" + "x-yoi-source-digest": "sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043" }, "openapi": "3.1.0", "paths": { diff --git a/tools/web-ux/browser-tests/console_history_fixture_server.ts b/tools/web-ux/browser-tests/console_history_fixture_server.ts index 53035f71b..22830dcbf 100644 --- a/tools/web-ux/browser-tests/console_history_fixture_server.ts +++ b/tools/web-ux/browser-tests/console_history_fixture_server.ts @@ -31,7 +31,6 @@ const worker = { profile: "default", tags: [], diagnostics: [], - capabilities: { can_stop: false, can_spawn_followup: false }, workspace: { workspace_id: workspaceId, visibility: "workspace", diff --git a/web/workspace/src/lib/generated/companion-api.ts b/web/workspace/src/lib/generated/companion-api.ts index 428524f66..5bf2a6295 100644 --- a/web/workspace/src/lib/generated/companion-api.ts +++ b/web/workspace/src/lib/generated/companion-api.ts @@ -1,6 +1,6 @@ // @generated by yoi-companion-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0) +// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043) // Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output // Output: web/workspace/src/lib/generated/companion-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check diff --git a/web/workspace/src/lib/generated/repository-api.ts b/web/workspace/src/lib/generated/repository-api.ts index 8f11ef780..06819d7c5 100644 --- a/web/workspace/src/lib/generated/repository-api.ts +++ b/web/workspace/src/lib/generated/repository-api.ts @@ -1,6 +1,6 @@ // @generated by yoi-repository-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0) +// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043) // Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output // Output: web/workspace/src/lib/generated/repository-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check @@ -61,8 +61,6 @@ export type SubscriptionWorkerAvailability = "observed" | "unavailable"; export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; }; -export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; }; - export type WorkerImplementationSummary = { display_hint: string; kind: string; }; export type WorkerMaintenanceState = "compacting"; @@ -73,7 +71,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; }; -export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; +export type WorkerSummary = { availability: SubscriptionWorkerAvailability; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; }; diff --git a/web/workspace/src/lib/generated/runtime-api.ts b/web/workspace/src/lib/generated/runtime-api.ts index 686626a14..c18141c5c 100644 --- a/web/workspace/src/lib/generated/runtime-api.ts +++ b/web/workspace/src/lib/generated/runtime-api.ts @@ -1,6 +1,6 @@ // @generated by yoi-runtime-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0) +// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043) // Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output // Output: web/workspace/src/lib/generated/runtime-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check @@ -107,7 +107,7 @@ export type RuntimeWorkerLifecycleResult = { diagnostics?: Array; ru export type RuntimeWorkerListResponse = { diagnostics?: Array; items: Array; limit: number; source: string; workspace_id: string; }; -export type RuntimeWorkerShowResponse = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; updated_at: string; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; +export type RuntimeWorkerShowResponse = { availability: SubscriptionWorkerAvailability; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; updated_at: string; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; export type RuntimeWorkerSpawnAcceptanceEvidence = { detail: string; kind: string; }; @@ -123,7 +123,7 @@ export type RuntimeWorkerSpawnWorkingDirectoryAttachmentRequest = { alias: strin export type RuntimeWorkerSpawnWorkingDirectoryRequest = { repository_key: string; selector?: string | null; }; -export type RuntimeWorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: unknown; workspace: WorkerWorkspaceSummary; }; +export type RuntimeWorkerSummary = { diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: unknown; workspace: WorkerWorkspaceSummary; }; export type RuntimeWorkerTicketAssignmentRequest = { operation_id: string; ticket_id: string; }; @@ -137,8 +137,6 @@ export type UploadedFileRef = { artifact_id: string; availability: UploadedFileA export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; }; -export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; }; - export type WorkerFileDeleteResponse = { deleted: boolean; }; export type WorkerFileUploadResponse = { file: UploadedFileRef; }; @@ -163,7 +161,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; }; -export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; +export type WorkerSummary = { availability: SubscriptionWorkerAvailability; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; }; diff --git a/web/workspace/src/lib/generated/ticket-api.ts b/web/workspace/src/lib/generated/ticket-api.ts index 875171af6..5b2262e05 100644 --- a/web/workspace/src/lib/generated/ticket-api.ts +++ b/web/workspace/src/lib/generated/ticket-api.ts @@ -1,6 +1,6 @@ // @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0) +// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043) // Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output // Output: web/workspace/src/lib/generated/ticket-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check diff --git a/web/workspace/src/lib/generated/worker-launch-api.ts b/web/workspace/src/lib/generated/worker-launch-api.ts index 8692625e9..61770c180 100644 --- a/web/workspace/src/lib/generated/worker-launch-api.ts +++ b/web/workspace/src/lib/generated/worker-launch-api.ts @@ -1,6 +1,6 @@ // @generated by yoi-worker-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:49856606e4a16d138cc04a62a6096fd40fff213587b1cbf7f0594f28994a45b0) +// Input contract: 0.1.0 (sha256:ae559492220ec118f04c2c0c9593e8b3cb44c3cb857be3ae85715ace26da8043) // Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output // Output: web/workspace/src/lib/generated/worker-launch-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check @@ -45,8 +45,6 @@ export type UploadedFileRef = { artifact_id: string; availability: UploadedFileA export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; }; -export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; }; - export type WorkerImplementationSummary = { display_hint: string; kind: string; }; export type WorkerLaunchOptionsResponse = { default_profile?: string | null; diagnostics: Array; profiles: Array; repositories: Array; runtimes: Array; working_directories: Array; workspace_id: string; }; @@ -55,7 +53,7 @@ export type WorkerLaunchProfileCandidate = { description: string; id: string; la export type WorkerLaunchRuntimeOption = { built_in: boolean; diagnostics: Array; display_name: string; runtime_id: string; status: string; worker_creation_available: boolean; working_directory_required: boolean; }; -export type WorkerLaunchWorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned: boolean; profile?: string | null; retention_state: string; runtime_id: string; singleton_key?: string | null; state: string; tags: Array; workdir_attachments?: Array; worker_id: string; workspace: WorkerWorkspaceSummary; }; +export type WorkerLaunchWorkerSummary = { diagnostics?: Array; display_name: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned: boolean; profile?: string | null; retention_state: string; runtime_id: string; singleton_key?: string | null; state: string; tags: Array; workdir_attachments?: Array; worker_id: string; workspace: WorkerWorkspaceSummary; }; export type WorkerListResponse = { diagnostics?: Array; items: Array; limit: number; source: string; workspace_id: string; }; @@ -67,7 +65,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; }; -export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; +export type WorkerSummary = { availability: SubscriptionWorkerAvailability; diagnostics?: Array; display_name?: string; host_id: string; implementation: WorkerImplementationSummary; label: string; last_seen_at?: string | null; pinned?: boolean; profile?: string | null; resource_key: string; retention_state?: string; runtime_id: string; singleton_key?: string | null; state: string; tags?: Array; workdir_attachments?: Array; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; }; export type WorkerWorkdirAttachmentSummary = { alias: string; effective_permissions: ExternalWorkdirPermissions; working_directory: WorkingDirectorySummary; }; diff --git a/web/workspace/src/lib/workspace/api/workers.test.ts b/web/workspace/src/lib/workspace/api/workers.test.ts index 66b1bada0..c4ca9bdd3 100644 --- a/web/workspace/src/lib/workspace/api/workers.test.ts +++ b/web/workspace/src/lib/workspace/api/workers.test.ts @@ -56,13 +56,28 @@ const worker = { kind: "runtime", display_hint: "Runtime Worker", }, - capabilities: { - can_stop: true, - can_spawn_followup: false, - }, diagnostics: [], }; +Deno.test("Worker summaries omit the removed management capabilities field", () => { + const summary = { ...worker, resource_key: "W-1", availability: "observed" }; + assertEquals("capabilities" in parseWorkerSummary(summary), false); + assertThrows( + () => parseWorkerSummary({ ...summary, capabilities: {} }), + Error, + "unknown field capabilities", + ); + const created = parseBrowserCreateWorkerResponse({ + workspace_id: "workspace-a", + runtime_id: "runtime-a", + worker_id: "worker-a", + console_href: "/w/workspace-a/workers/W-1/console", + worker, + diagnostics: [], + }); + assertEquals("capabilities" in created.worker, false); +}); + Deno.test("Worker summary parser enforces observation freshness", () => { const unavailable = { ...worker, diff --git a/web/workspace/src/lib/workspace/api/workers.ts b/web/workspace/src/lib/workspace/api/workers.ts index 96ac01d23..467a8030f 100644 --- a/web/workspace/src/lib/workspace/api/workers.ts +++ b/web/workspace/src/lib/workspace/api/workers.ts @@ -10,7 +10,6 @@ import type { RuntimeWorkerWorkdirAttachmentSummary, RuntimeWorkingDirectoryCleanupTarget, RuntimeWorkingDirectorySummary, - WorkerCapabilitySummary, WorkerImplementationSummary, WorkerLaunchOptionsResponse, WorkerLaunchProfileCandidate, @@ -251,21 +250,6 @@ function implementationSummary( }; } -function capabilitySummary( - value: unknown, - label: string, -): WorkerCapabilitySummary { - const item = record(value, label); - exact(item, ["can_stop", "can_spawn_followup"], label); - return { - can_stop: boolean(item.can_stop, `${label}.can_stop`), - can_spawn_followup: boolean( - item.can_spawn_followup, - `${label}.can_spawn_followup`, - ), - }; -} - function workerStateSnapshot( value: unknown, label: string, @@ -374,7 +358,6 @@ export function parseWorkerSummary( "pinned", "retention_state", "implementation", - "capabilities", "workdir_attachments", "diagnostics", ], @@ -423,7 +406,6 @@ export function parseWorkerSummary( item.implementation, `${label}.implementation`, ), - capabilities: capabilitySummary(item.capabilities, `${label}.capabilities`), workdir_attachments: item.workdir_attachments === undefined ? undefined : array( @@ -617,7 +599,6 @@ function workerSummary( "pinned", "retention_state", "implementation", - "capabilities", "workdir_attachments", "diagnostics", ], @@ -641,7 +622,6 @@ function workerSummary( item.implementation, `${label}.implementation`, ), - capabilities: capabilitySummary(item.capabilities, `${label}.capabilities`), workdir_attachments: item.workdir_attachments === undefined ? undefined : array( diff --git a/web/workspace/src/lib/workspace/sidebar/WorkersNavSection.svelte b/web/workspace/src/lib/workspace/sidebar/WorkersNavSection.svelte index cfa355a68..c155c1985 100644 --- a/web/workspace/src/lib/workspace/sidebar/WorkersNavSection.svelte +++ b/web/workspace/src/lib/workspace/sidebar/WorkersNavSection.svelte @@ -3,6 +3,7 @@ import { workerConsoleHref } from '$lib/workspace/resource-links'; import { pushWorkspaceAlert } from '$lib/workspace/alerts/store'; import { + canStopSidebarWorker, canDeleteSidebarWorker, deleteSidebarWorker, stopSidebarWorker, @@ -91,14 +92,14 @@ } async function stopWorker(worker: SidebarWorker) { - if (busyAction || !worker.capabilities.can_stop) return; + if (busyAction || !canStopSidebarWorker(worker)) return; closeWorkerMenu(); busyAction = { workerKey: workerKey(worker), kind: 'stop' }; try { await stopSidebarWorker(workspaceId, worker); workers = workers.map((item) => workerKey(item) === workerKey(worker) - ? { ...item, state: 'stopped', capabilities: { ...item.capabilities, can_stop: false } } + ? { ...item, state: 'stopped', lifecycleState: 'stopped' } : item ); pushWorkspaceAlert('info', `${worker.display_name || worker.label} stopped`, { @@ -226,7 +227,7 @@