fix: separate worker observation freshness
This commit is contained in:
@@ -564,6 +564,7 @@ pub enum SubscriptionWorkerState {
|
|||||||
/// `Unavailable` deliberately does not imply `Stopped`: it means the last observed
|
/// `Unavailable` deliberately does not imply `Stopped`: it means the last observed
|
||||||
/// lifecycle state is retained while the Runtime observation source is unavailable.
|
/// lifecycle state is retained while the Runtime observation source is unavailable.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
|
||||||
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum SubscriptionWorkerAvailability {
|
pub enum SubscriptionWorkerAvailability {
|
||||||
|
|||||||
@@ -7931,6 +7931,9 @@ pub struct WorkerSummary {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
pub workspace: WorkerWorkspaceSummary,
|
pub workspace: WorkerWorkspaceSummary,
|
||||||
|
/// Freshness of the Runtime observation used to construct `worker_state`.
|
||||||
|
/// `Unavailable` means no current controller snapshot is being asserted.
|
||||||
|
pub availability: protocol::subscription::SubscriptionWorkerAvailability,
|
||||||
/// Runtime catalog lifecycle compatibility state. Live foreground state, when
|
/// Runtime catalog lifecycle compatibility state. Live foreground state, when
|
||||||
/// available, is carried separately in `worker_state`.
|
/// available, is carried separately in `worker_state`.
|
||||||
pub state: String,
|
pub state: String,
|
||||||
|
|||||||
@@ -260,6 +260,9 @@ pub struct InternalWorkerSummary {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
pub workspace: InternalWorkerWorkspaceSummary,
|
pub workspace: InternalWorkerWorkspaceSummary,
|
||||||
|
/// Freshness of the Runtime observation carried by `worker_state`.
|
||||||
|
#[serde(default)]
|
||||||
|
pub availability: protocol::subscription::SubscriptionWorkerAvailability,
|
||||||
/// Runtime catalog lifecycle compatibility state.
|
/// Runtime catalog lifecycle compatibility state.
|
||||||
pub state: String,
|
pub state: String,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
@@ -371,6 +374,7 @@ pub(crate) fn workspace_worker_summary(
|
|||||||
identity: summary.workspace.identity,
|
identity: summary.workspace.identity,
|
||||||
workspace_id: summary.workspace.workspace_id,
|
workspace_id: summary.workspace.workspace_id,
|
||||||
},
|
},
|
||||||
|
availability: summary.availability,
|
||||||
state: summary.state,
|
state: summary.state,
|
||||||
worker_state: summary.worker_state,
|
worker_state: summary.worker_state,
|
||||||
last_seen_at: summary.last_seen_at,
|
last_seen_at: summary.last_seen_at,
|
||||||
@@ -2218,6 +2222,8 @@ impl EmbeddedWorkerRuntime {
|
|||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
workspace_id: summary.workspace_id.clone(),
|
workspace_id: summary.workspace_id.clone(),
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: embedded_worker_state_label(
|
state: embedded_worker_state_label(
|
||||||
summary.status,
|
summary.status,
|
||||||
summary.execution_metadata_available,
|
summary.execution_metadata_available,
|
||||||
@@ -2265,6 +2271,8 @@ impl EmbeddedWorkerRuntime {
|
|||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
workspace_id: detail.workspace_id.clone(),
|
workspace_id: detail.workspace_id.clone(),
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: embedded_worker_state_label(detail.status, detail.execution_metadata_available)
|
state: embedded_worker_state_label(detail.status, detail.execution_metadata_available)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
worker_state: detail.worker_state.clone(),
|
worker_state: detail.worker_state.clone(),
|
||||||
@@ -4056,6 +4064,8 @@ impl RemoteWorkerRuntime {
|
|||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
workspace_id: summary.workspace_id.clone(),
|
workspace_id: summary.workspace_id.clone(),
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: embedded_worker_state_label(
|
state: embedded_worker_state_label(
|
||||||
summary.status,
|
summary.status,
|
||||||
summary.execution_metadata_available,
|
summary.execution_metadata_available,
|
||||||
@@ -4101,6 +4111,8 @@ impl RemoteWorkerRuntime {
|
|||||||
identity: "runtime_registry_worker".to_string(),
|
identity: "runtime_registry_worker".to_string(),
|
||||||
workspace_id: detail.workspace_id.clone(),
|
workspace_id: detail.workspace_id.clone(),
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: embedded_worker_state_label(detail.status, detail.execution_metadata_available)
|
state: embedded_worker_state_label(detail.status, detail.execution_metadata_available)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
worker_state: detail.worker_state.clone(),
|
worker_state: detail.worker_state.clone(),
|
||||||
@@ -5867,6 +5879,7 @@ pub fn placeholder_worker(host_id: impl Into<String>) -> InternalWorkerSummary {
|
|||||||
identity: "unsupported".to_string(),
|
identity: "unsupported".to_string(),
|
||||||
workspace_id: None,
|
workspace_id: None,
|
||||||
},
|
},
|
||||||
|
availability: protocol::subscription::SubscriptionWorkerAvailability::Unavailable,
|
||||||
state: "unsupported".to_string(),
|
state: "unsupported".to_string(),
|
||||||
worker_state: None,
|
worker_state: None,
|
||||||
last_seen_at: None,
|
last_seen_at: None,
|
||||||
@@ -6509,6 +6522,8 @@ mod tests {
|
|||||||
identity: host_id.to_string(),
|
identity: host_id.to_string(),
|
||||||
workspace_id: None,
|
workspace_id: None,
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: "available".to_string(),
|
state: "available".to_string(),
|
||||||
worker_state: None,
|
worker_state: None,
|
||||||
last_seen_at: None,
|
last_seen_at: None,
|
||||||
|
|||||||
@@ -24059,6 +24059,7 @@ fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> InternalWorker
|
|||||||
identity: record.workspace_id.clone(),
|
identity: record.workspace_id.clone(),
|
||||||
workspace_id: Some(record.workspace_id.clone()),
|
workspace_id: Some(record.workspace_id.clone()),
|
||||||
},
|
},
|
||||||
|
availability: protocol::subscription::SubscriptionWorkerAvailability::Unavailable,
|
||||||
profile: record.profile.clone(),
|
profile: record.profile.clone(),
|
||||||
implementation: InternalWorkerImplementationSummary {
|
implementation: InternalWorkerImplementationSummary {
|
||||||
kind: "backend_worker_registry".to_string(),
|
kind: "backend_worker_registry".to_string(),
|
||||||
@@ -24082,6 +24083,7 @@ fn worker_summary_from_projection(
|
|||||||
let mut summary = worker_summary_from_registry(&projection.registry);
|
let mut summary = worker_summary_from_registry(&projection.registry);
|
||||||
let observed = observation.availability
|
let observed = observation.availability
|
||||||
== protocol::subscription::SubscriptionWorkerAvailability::Observed;
|
== protocol::subscription::SubscriptionWorkerAvailability::Observed;
|
||||||
|
summary.availability = observation.availability;
|
||||||
summary.state = if observed {
|
summary.state = if observed {
|
||||||
match observation.worker.state {
|
match observation.worker.state {
|
||||||
protocol::subscription::SubscriptionWorkerState::Idle => "idle",
|
protocol::subscription::SubscriptionWorkerState::Idle => "idle",
|
||||||
@@ -24093,7 +24095,9 @@ fn worker_summary_from_projection(
|
|||||||
"unavailable"
|
"unavailable"
|
||||||
}
|
}
|
||||||
.to_string();
|
.to_string();
|
||||||
summary.worker_state = observation.worker.worker_state.clone();
|
summary.worker_state = observed
|
||||||
|
.then(|| observation.worker.worker_state.clone())
|
||||||
|
.flatten();
|
||||||
summary.last_seen_at = Some(observation.observed_at.clone());
|
summary.last_seen_at = Some(observation.observed_at.clone());
|
||||||
summary.capabilities.can_stop = observed
|
summary.capabilities.can_stop = observed
|
||||||
&& observation.worker.state != protocol::subscription::SubscriptionWorkerState::Stopped;
|
&& observation.worker.state != protocol::subscription::SubscriptionWorkerState::Stopped;
|
||||||
@@ -25798,9 +25802,79 @@ mod tests {
|
|||||||
use crate::store::{
|
use crate::store::{
|
||||||
AccountRecord, ApiTokenRecord, BrowserSessionRecord, MemoryDocumentRecord,
|
AccountRecord, ApiTokenRecord, BrowserSessionRecord, MemoryDocumentRecord,
|
||||||
MemoryStagingRecord, ObjectiveRecord, ObjectiveResourceRecord, ObjectiveTicketLinkRecord,
|
MemoryStagingRecord, ObjectiveRecord, ObjectiveResourceRecord, ObjectiveTicketLinkRecord,
|
||||||
SqliteWorkspaceStore, UserRecord, WorkspaceRecord, WorkspaceRuntimeBinding,
|
SqliteWorkspaceStore, UserRecord, WorkerRegistryObservationRecord,
|
||||||
|
WorkerRegistryProjectionRecord, WorkerRegistryRecord, WorkspaceRecord,
|
||||||
|
WorkspaceRuntimeBinding,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn worker_projection_does_not_expose_unavailable_snapshot_as_current() {
|
||||||
|
let availability =
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Unavailable;
|
||||||
|
let worker_ref = RuntimeWorkerRef::new("runtime-a", "worker-a");
|
||||||
|
let projection = WorkerRegistryProjectionRecord {
|
||||||
|
registry: WorkerRegistryRecord {
|
||||||
|
workspace_id: TEST_WORKSPACE_ID.to_string(),
|
||||||
|
worker: worker_ref,
|
||||||
|
display_name: "Worker A".to_string(),
|
||||||
|
profile: Some("builtin:coder".to_string()),
|
||||||
|
retention_state: "normal".to_string(),
|
||||||
|
transcript_ref: None,
|
||||||
|
session_ref: None,
|
||||||
|
summary_ref: None,
|
||||||
|
diagnostics_ref: None,
|
||||||
|
created_at: "2026-09-24T00:00:00Z".to_string(),
|
||||||
|
updated_at: "2026-09-24T00:00:00Z".to_string(),
|
||||||
|
},
|
||||||
|
resource_key: Some("W-1".to_string()),
|
||||||
|
observation: Some(WorkerRegistryObservationRecord {
|
||||||
|
worker: protocol::subscription::SubscriptionWorker {
|
||||||
|
worker_id: protocol::subscription::SubscriptionWorkerId::new("worker-a")
|
||||||
|
.unwrap(),
|
||||||
|
runtime_id: Some("runtime-a".to_string()),
|
||||||
|
resource_key: Some("W-1".to_string()),
|
||||||
|
availability,
|
||||||
|
subject_revision: 4,
|
||||||
|
worker_state: Some(protocol::WorkerStateSnapshot::initial()),
|
||||||
|
state: protocol::subscription::SubscriptionWorkerState::Idle,
|
||||||
|
has_running_internal_workers: false,
|
||||||
|
workspace_id: Some(TEST_WORKSPACE_ID.to_string()),
|
||||||
|
display_name: Some("Worker A".to_string()),
|
||||||
|
profile: Some("builtin:coder".to_string()),
|
||||||
|
workdir_attachments: Vec::new(),
|
||||||
|
},
|
||||||
|
availability,
|
||||||
|
connection_generation: 2,
|
||||||
|
subject_revision: 4,
|
||||||
|
snapshot_revision: 7,
|
||||||
|
projection_revision: 9,
|
||||||
|
observed_at: "2026-09-24T00:00:01Z".to_string(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let unavailable = worker_summary_from_projection(&projection).unwrap();
|
||||||
|
assert_eq!(unavailable.availability, availability);
|
||||||
|
assert_eq!(unavailable.state, "unavailable");
|
||||||
|
assert_eq!(unavailable.worker_state, None);
|
||||||
|
assert!(!unavailable.capabilities.can_stop);
|
||||||
|
|
||||||
|
let mut observed_stopped = projection;
|
||||||
|
let observation = observed_stopped.observation.as_mut().unwrap();
|
||||||
|
observation.availability =
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed;
|
||||||
|
observation.worker.availability =
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed;
|
||||||
|
observation.worker.state = protocol::subscription::SubscriptionWorkerState::Stopped;
|
||||||
|
observation.worker.worker_state = None;
|
||||||
|
let observed_stopped = worker_summary_from_projection(&observed_stopped).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
observed_stopped.availability,
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed
|
||||||
|
);
|
||||||
|
assert_eq!(observed_stopped.state, "stopped");
|
||||||
|
assert_eq!(observed_stopped.worker_state, None);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn external_provider_session_reuses_operation_contract_and_read_only_capabilities() {
|
async fn external_provider_session_reuses_operation_contract_and_read_only_capabilities() {
|
||||||
let (sender, mut commands) = tokio::sync::mpsc::channel(1);
|
let (sender, mut commands) = tokio::sync::mpsc::channel(1);
|
||||||
@@ -26717,6 +26791,8 @@ mod tests {
|
|||||||
identity: TEST_WORKSPACE_ID.to_string(),
|
identity: TEST_WORKSPACE_ID.to_string(),
|
||||||
workspace_id: Some(TEST_WORKSPACE_ID.to_string()),
|
workspace_id: Some(TEST_WORKSPACE_ID.to_string()),
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: "idle".to_string(),
|
state: "idle".to_string(),
|
||||||
worker_state: None,
|
worker_state: None,
|
||||||
last_seen_at: None,
|
last_seen_at: None,
|
||||||
@@ -38514,6 +38590,8 @@ mod tests {
|
|||||||
identity: "workspace-test".to_string(),
|
identity: "workspace-test".to_string(),
|
||||||
workspace_id: Some(TEST_WORKSPACE_ID.to_string()),
|
workspace_id: Some(TEST_WORKSPACE_ID.to_string()),
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: "idle".to_string(),
|
state: "idle".to_string(),
|
||||||
worker_state: None,
|
worker_state: None,
|
||||||
last_seen_at: None,
|
last_seen_at: None,
|
||||||
@@ -38611,6 +38689,8 @@ mod tests {
|
|||||||
identity: "workspace-test".to_string(),
|
identity: "workspace-test".to_string(),
|
||||||
workspace_id: Some(TEST_WORKSPACE_ID.to_string()),
|
workspace_id: Some(TEST_WORKSPACE_ID.to_string()),
|
||||||
},
|
},
|
||||||
|
availability:
|
||||||
|
protocol::subscription::SubscriptionWorkerAvailability::Observed,
|
||||||
state: "idle".to_string(),
|
state: "idle".to_string(),
|
||||||
worker_state: None,
|
worker_state: None,
|
||||||
last_seen_at: None,
|
last_seen_at: None,
|
||||||
|
|||||||
+19
-1
@@ -7367,6 +7367,10 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Workspace-authoritative Worker projection.\n\n`resource_key` is required here even though Runtime-internal Worker summaries\ndo not carry one. The Workspace Server must resolve it from Workspace\nauthority before constructing this response.",
|
"description": "Workspace-authoritative Worker projection.\n\n`resource_key` is required here even though Runtime-internal Worker summaries\ndo not carry one. The Workspace Server must resolve it from Workspace\nauthority before constructing this response.",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"availability": {
|
||||||
|
"$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": {
|
"capabilities": {
|
||||||
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
||||||
},
|
},
|
||||||
@@ -7466,6 +7470,7 @@
|
|||||||
"host_id",
|
"host_id",
|
||||||
"label",
|
"label",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
"availability",
|
||||||
"state",
|
"state",
|
||||||
"implementation",
|
"implementation",
|
||||||
"capabilities",
|
"capabilities",
|
||||||
@@ -8605,6 +8610,14 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"SubscriptionWorkerAvailability": {
|
||||||
|
"description": "Whether the Workspace Server has a current Runtime observation for a Worker.\n\n`Unavailable` deliberately does not imply `Stopped`: it means the last observed\nlifecycle state is retained while the Runtime observation source is unavailable.",
|
||||||
|
"enum": [
|
||||||
|
"observed",
|
||||||
|
"unavailable"
|
||||||
|
],
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"Ticket": {
|
"Ticket": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"artifacts": {
|
"artifacts": {
|
||||||
@@ -11722,6 +11735,10 @@
|
|||||||
"WorkerSummary": {
|
"WorkerSummary": {
|
||||||
"description": "Workspace-authoritative Worker projection.\n\n`resource_key` is required here even though Runtime-internal Worker summaries\ndo not carry one. The Workspace Server must resolve it from Workspace\nauthority before constructing this response.",
|
"description": "Workspace-authoritative Worker projection.\n\n`resource_key` is required here even though Runtime-internal Worker summaries\ndo not carry one. The Workspace Server must resolve it from Workspace\nauthority before constructing this response.",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"availability": {
|
||||||
|
"$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": {
|
"capabilities": {
|
||||||
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
"$ref": "#/components/schemas/WorkerCapabilitySummary"
|
||||||
},
|
},
|
||||||
@@ -11818,6 +11835,7 @@
|
|||||||
"host_id",
|
"host_id",
|
||||||
"label",
|
"label",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
"availability",
|
||||||
"state",
|
"state",
|
||||||
"implementation",
|
"implementation",
|
||||||
"capabilities"
|
"capabilities"
|
||||||
@@ -13901,7 +13919,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Yoi Server API",
|
"title": "Yoi Server API",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"x-yoi-source-digest": "sha256:80be292e32441730f7de890e64836f4ca9655576d655e60e6d44daf5227a1b1c"
|
"x-yoi-source-digest": "sha256:a92b7346166ec2316878ef38b0426090cb4bdd0d5b96cae3014bef35c60df02a"
|
||||||
},
|
},
|
||||||
"openapi": "3.1.0",
|
"openapi": "3.1.0",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:80be292e32441730f7de890e64836f4ca9655576d655e60e6d44daf5227a1b1c)
|
// Input contract: 0.1.0 (sha256:a92b7346166ec2316878ef38b0426090cb4bdd0d5b96cae3014bef35c60df02a)
|
||||||
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
|
// 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
|
// Output: web/workspace/src/lib/generated/repository-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||||
@@ -55,6 +55,8 @@ export type RepositorySshHostTrust = { created_at: string; current_revision: num
|
|||||||
|
|
||||||
export type RepositorySummary = { default_selector?: string | null; diagnostics?: Array<RepositoryDiagnostic> | null; git?: GitRepositorySummary | null; kind: string; observed_at?: string | null; observed_status: RepositoryObservedStatus; provider: string; record_authority: string; repository_key: string; source: RepositorySource; source_fingerprint: string; source_revision: number; };
|
export type RepositorySummary = { default_selector?: string | null; diagnostics?: Array<RepositoryDiagnostic> | null; git?: GitRepositorySummary | null; kind: string; observed_at?: string | null; observed_status: RepositoryObservedStatus; provider: string; record_authority: string; repository_key: string; source: RepositorySource; source_fingerprint: string; source_revision: number; };
|
||||||
|
|
||||||
|
export type SubscriptionWorkerAvailability = "observed" | "unavailable";
|
||||||
|
|
||||||
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
export type WorkerBusyState = { kind: "run"; state: WorkerRunState; } | { kind: "maintenance"; state: WorkerMaintenanceState; };
|
||||||
|
|
||||||
export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; };
|
export type WorkerCapabilitySummary = { can_spawn_followup: boolean; can_stop: boolean; };
|
||||||
@@ -69,7 +71,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS
|
|||||||
|
|
||||||
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
||||||
|
|
||||||
export type WorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type WorkerWorkdirAttachmentSummary = { alias: string; working_directory: WorkingDirectorySummary; };
|
export type WorkerWorkdirAttachmentSummary = { alias: string; working_directory: WorkingDirectorySummary; };
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:80be292e32441730f7de890e64836f4ca9655576d655e60e6d44daf5227a1b1c)
|
// Input contract: 0.1.0 (sha256:a92b7346166ec2316878ef38b0426090cb4bdd0d5b96cae3014bef35c60df02a)
|
||||||
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||||
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
// Output: web/workspace/src/lib/generated/runtime-api.ts
|
||||||
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
|
||||||
@@ -105,7 +105,7 @@ export type RuntimeWorkerLifecycleResult = { diagnostics?: Array<Diagnostic>; ru
|
|||||||
|
|
||||||
export type RuntimeWorkerListResponse = { diagnostics?: Array<Diagnostic>; items: Array<WorkerSummary>; limit: number; source: string; workspace_id: string; };
|
export type RuntimeWorkerListResponse = { diagnostics?: Array<Diagnostic>; items: Array<WorkerSummary>; limit: number; source: string; workspace_id: string; };
|
||||||
|
|
||||||
export type RuntimeWorkerShowResponse = { capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; updated_at: string; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type RuntimeWorkerShowResponse = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; updated_at: string; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type RuntimeWorkerSpawnAcceptanceEvidence = { detail: string; kind: string; };
|
export type RuntimeWorkerSpawnAcceptanceEvidence = { detail: string; kind: string; };
|
||||||
|
|
||||||
@@ -125,6 +125,8 @@ export type RuntimeWorkerSummary = { capabilities: WorkerCapabilitySummary; diag
|
|||||||
|
|
||||||
export type RuntimeWorkerTicketAssignmentRequest = { operation_id: string; ticket_id: string; };
|
export type RuntimeWorkerTicketAssignmentRequest = { operation_id: string; ticket_id: string; };
|
||||||
|
|
||||||
|
export type SubscriptionWorkerAvailability = "observed" | "unavailable";
|
||||||
|
|
||||||
export type UpdateRemoteRuntimeRequest = { display_name?: string | null; endpoint: string; };
|
export type UpdateRemoteRuntimeRequest = { display_name?: string | null; endpoint: string; };
|
||||||
|
|
||||||
export type UploadedFileAvailability = "available" | "unavailable" | "integrity_failed";
|
export type UploadedFileAvailability = "available" | "unavailable" | "integrity_failed";
|
||||||
@@ -159,7 +161,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS
|
|||||||
|
|
||||||
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
||||||
|
|
||||||
export type WorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type WorkerWorkdirAttachmentSummary = { alias: string; working_directory: WorkingDirectorySummary; };
|
export type WorkerWorkdirAttachmentSummary = { alias: string; working_directory: WorkingDirectorySummary; };
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
|
||||||
// Input: openapi/server-api.json
|
// Input: openapi/server-api.json
|
||||||
// Input contract: 0.1.0 (sha256:80be292e32441730f7de890e64836f4ca9655576d655e60e6d44daf5227a1b1c)
|
// Input contract: 0.1.0 (sha256:a92b7346166ec2316878ef38b0426090cb4bdd0d5b96cae3014bef35c60df02a)
|
||||||
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
|
||||||
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
|
// 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
|
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
|
||||||
@@ -35,6 +35,8 @@ export type RuntimeWorkingDirectorySummary = { cleanliness?: string | null; clea
|
|||||||
|
|
||||||
export type Segment = { content: string; kind: "text"; } | { chars: number; content: string; id: number; kind: "paste"; lines: number; } | { artifact: PasteArtifactRef; kind: "paste_artifact"; } | { file: UploadedFileRef; kind: "uploaded_file"; } | { kind: "file_ref"; path: string; } | { kind: "flow"; selector: string; } | { kind: "unknown"; };
|
export type Segment = { content: string; kind: "text"; } | { chars: number; content: string; id: number; kind: "paste"; lines: number; } | { artifact: PasteArtifactRef; kind: "paste_artifact"; } | { file: UploadedFileRef; kind: "uploaded_file"; } | { kind: "file_ref"; path: string; } | { kind: "flow"; selector: string; } | { kind: "unknown"; };
|
||||||
|
|
||||||
|
export type SubscriptionWorkerAvailability = "observed" | "unavailable";
|
||||||
|
|
||||||
export type UploadedFileAvailability = "available" | "unavailable" | "integrity_failed";
|
export type UploadedFileAvailability = "available" | "unavailable" | "integrity_failed";
|
||||||
|
|
||||||
export type UploadedFileRef = { artifact_id: string; availability: UploadedFileAvailability; byte_len: number; created_at_ms: number; file_name: string; media_type: string; sha256: string; source_entry_id?: string | null; };
|
export type UploadedFileRef = { artifact_id: string; availability: UploadedFileAvailability; byte_len: number; created_at_ms: number; file_name: string; media_type: string; sha256: string; source_entry_id?: string | null; };
|
||||||
@@ -63,7 +65,7 @@ export type WorkerState = { kind: "idle"; } | { kind: "busy"; state: WorkerBusyS
|
|||||||
|
|
||||||
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
export type WorkerStateSnapshot = { last_command_id: number; state: WorkerState; };
|
||||||
|
|
||||||
export type WorkerSummary = { capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
export type WorkerSummary = { availability: SubscriptionWorkerAvailability; capabilities: WorkerCapabilitySummary; diagnostics?: Array<Diagnostic>; 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<string>; workdir_attachments?: Array<WorkerWorkdirAttachmentSummary>; worker_id: string; worker_state?: WorkerStateSnapshot | null; workspace: WorkerWorkspaceSummary; };
|
||||||
|
|
||||||
export type WorkerWorkdirAttachmentSummary = { alias: string; working_directory: WorkingDirectorySummary; };
|
export type WorkerWorkdirAttachmentSummary = { alias: string; working_directory: WorkingDirectorySummary; };
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
parseBrowserWorkspaceOrchestratorResponse,
|
parseBrowserWorkspaceOrchestratorResponse,
|
||||||
parseCreateWorkspaceWorkerRequest,
|
parseCreateWorkspaceWorkerRequest,
|
||||||
parseWorkerLaunchOptionsResponse,
|
parseWorkerLaunchOptionsResponse,
|
||||||
|
parseWorkerSummary,
|
||||||
} from "./workers.ts";
|
} from "./workers.ts";
|
||||||
|
|
||||||
const worker = {
|
const worker = {
|
||||||
@@ -62,6 +63,39 @@ const worker = {
|
|||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Deno.test("Worker summary parser enforces observation freshness", () => {
|
||||||
|
const unavailable = {
|
||||||
|
...worker,
|
||||||
|
resource_key: "W-1",
|
||||||
|
availability: "unavailable",
|
||||||
|
state: "unavailable",
|
||||||
|
};
|
||||||
|
const parsed = parseWorkerSummary(unavailable);
|
||||||
|
assertEquals(parsed.availability, "unavailable");
|
||||||
|
assertEquals(parsed.worker_state, undefined);
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
() =>
|
||||||
|
parseWorkerSummary({
|
||||||
|
...unavailable,
|
||||||
|
worker_state: {
|
||||||
|
last_command_id: 0,
|
||||||
|
state: { kind: "idle" },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Error,
|
||||||
|
"worker_state must be absent",
|
||||||
|
);
|
||||||
|
|
||||||
|
const observedStopped = parseWorkerSummary({
|
||||||
|
...unavailable,
|
||||||
|
availability: "observed",
|
||||||
|
state: "stopped",
|
||||||
|
});
|
||||||
|
assertEquals(observedStopped.availability, "observed");
|
||||||
|
assertEquals(observedStopped.state, "stopped");
|
||||||
|
});
|
||||||
|
|
||||||
Deno.test("Worker launch options parser accepts the generated wire shape", () => {
|
Deno.test("Worker launch options parser accepts the generated wire shape", () => {
|
||||||
const parsed = parseWorkerLaunchOptionsResponse({
|
const parsed = parseWorkerLaunchOptionsResponse({
|
||||||
workspace_id: "workspace-a",
|
workspace_id: "workspace-a",
|
||||||
|
|||||||
@@ -343,6 +343,10 @@ export function parseWorkerSummary(
|
|||||||
label = "Worker summary",
|
label = "Worker summary",
|
||||||
): ParsedWorkerSummary {
|
): ParsedWorkerSummary {
|
||||||
const item = record(value, label);
|
const item = record(value, label);
|
||||||
|
const availability = string(item.availability, `${label}.availability`);
|
||||||
|
if (availability !== "observed" && availability !== "unavailable") {
|
||||||
|
throw new Error(`${label}.availability is invalid`);
|
||||||
|
}
|
||||||
exact(
|
exact(
|
||||||
item,
|
item,
|
||||||
[
|
[
|
||||||
@@ -356,6 +360,7 @@ export function parseWorkerSummary(
|
|||||||
"singleton_key",
|
"singleton_key",
|
||||||
"tags",
|
"tags",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
"availability",
|
||||||
"state",
|
"state",
|
||||||
"worker_state",
|
"worker_state",
|
||||||
"last_seen_at",
|
"last_seen_at",
|
||||||
@@ -368,6 +373,16 @@ export function parseWorkerSummary(
|
|||||||
],
|
],
|
||||||
label,
|
label,
|
||||||
);
|
);
|
||||||
|
const workerState = optional(
|
||||||
|
item.worker_state,
|
||||||
|
`${label}.worker_state`,
|
||||||
|
workerStateSnapshot,
|
||||||
|
);
|
||||||
|
if (availability === "unavailable" && workerState != null) {
|
||||||
|
throw new Error(
|
||||||
|
`${label}.worker_state must be absent when availability is unavailable`,
|
||||||
|
);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
runtime_id: string(item.runtime_id, `${label}.runtime_id`),
|
runtime_id: string(item.runtime_id, `${label}.runtime_id`),
|
||||||
worker_id: string(item.worker_id, `${label}.worker_id`),
|
worker_id: string(item.worker_id, `${label}.worker_id`),
|
||||||
@@ -387,12 +402,9 @@ export function parseWorkerSummary(
|
|||||||
? []
|
? []
|
||||||
: array(item.tags, `${label}.tags`, string),
|
: array(item.tags, `${label}.tags`, string),
|
||||||
workspace: workspaceSummary(item.workspace, `${label}.workspace`),
|
workspace: workspaceSummary(item.workspace, `${label}.workspace`),
|
||||||
|
availability,
|
||||||
state: string(item.state, `${label}.state`),
|
state: string(item.state, `${label}.state`),
|
||||||
worker_state: optional(
|
worker_state: workerState,
|
||||||
item.worker_state,
|
|
||||||
`${label}.worker_state`,
|
|
||||||
workerStateSnapshot,
|
|
||||||
),
|
|
||||||
last_seen_at: optional(item.last_seen_at, `${label}.last_seen_at`, string),
|
last_seen_at: optional(item.last_seen_at, `${label}.last_seen_at`, string),
|
||||||
pinned: item.pinned === undefined
|
pinned: item.pinned === undefined
|
||||||
? false
|
? false
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ function projectWorker(
|
|||||||
visibility: "workspace",
|
visibility: "workspace",
|
||||||
identity: "runtime_subscription_worker",
|
identity: "runtime_subscription_worker",
|
||||||
},
|
},
|
||||||
|
availability: worker.availability,
|
||||||
state: liveWorkerState(worker),
|
state: liveWorkerState(worker),
|
||||||
worker_state: worker.worker_state,
|
worker_state: worker.worker_state,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user