diff --git a/crates/server-api/src/lib.rs b/crates/server-api/src/lib.rs index f5b6ed71c..239025fc3 100644 --- a/crates/server-api/src/lib.rs +++ b/crates/server-api/src/lib.rs @@ -537,8 +537,13 @@ pub trait ServerApi { #[extension] context: ServerRequestContext, #[path] operation_id: String, ) -> Result; - - #[get("/api/workspace", status = 200, error_status = 400)] + #[get( + "/api/workspace", + status = 200, + error_status = 400, + bearer_auth = true, + browser_auth = true + )] async fn workspace_current( &self, #[extension] context: ServerRequestContext, @@ -833,12 +838,13 @@ pub trait ServerApi { &self, #[path] workspace_id: String, ) -> Result; - #[get( "/api/w/{workspace_id}/config/projections/prompts", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 500] + additional_error_statuses = [401, 403, 404, 500], + bearer_auth = true, + browser_auth = true, )] async fn workspace_prompt_projection( &self, @@ -899,47 +905,51 @@ pub trait ServerApi { &self, #[path] workspace_id: String, ) -> Result; - #[get( "/api/w/{workspace_id}/flows", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 500] + additional_error_statuses = [401, 403, 404, 500], + bearer_auth = true, + browser_auth = true, )] async fn flow_list( &self, #[path] workspace_id: String, ) -> Result; - #[put( "/api/w/{workspace_id}/flows", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500] + additional_error_statuses = [401, 403, 404, 409, 500], + bearer_auth = true, + browser_auth = true, )] async fn flow_put( &self, #[path] workspace_id: String, #[body] request: PutFlowRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/flows/resolve", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 500] + additional_error_statuses = [401, 403, 404, 500], + bearer_auth = true, + browser_auth = true, )] async fn flow_resolve( &self, #[path] workspace_id: String, #[body] request: FlowSourceResolveRequest, ) -> Result; - #[get( "/api/w/{workspace_id}/flows/{flow_id}", status = 200, error_status = 404, - additional_error_statuses = [400, 401, 403, 500] + additional_error_statuses = [400, 401, 403, 500], + bearer_auth = true, + browser_auth = true, )] async fn flow_get( &self, @@ -973,24 +983,26 @@ pub trait ServerApi { #[path] workspace_id: String, #[query] query: MemoryStagingQuery, ) -> Result; - #[post( "/api/w/{workspace_id}/memory/backend", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 500] + additional_error_statuses = [401, 403, 404, 500], + bearer_auth = true, + browser_auth = true, )] async fn memory_backend( &self, #[path] workspace_id: String, #[body] request: MemoryBackendRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/memory/consolidation", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500, 502, 503] + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + bearer_auth = true, + browser_auth = true, )] async fn memory_consolidation( &self, @@ -1037,12 +1049,13 @@ pub trait ServerApi { #[path] workspace_id: String, #[path] name: String, ) -> Result; - #[get( "/api/w/{workspace_id}/skills/{name}/activate", status = 200, error_status = 404, - additional_error_statuses = [400, 401, 403, 409, 500] + additional_error_statuses = [400, 401, 403, 409, 500], + bearer_auth = true, + browser_auth = true, )] async fn skill_activate( &self, @@ -1112,24 +1125,24 @@ pub trait ServerApi { &self, #[path] workspace_id: String, ) -> Result; - #[get( "/api/w/{workspace_id}/worker-control/workers", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 500] + additional_error_statuses = [401, 403, 404, 500], + openapi = false, )] async fn worker_control_list( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, ) -> Result; - #[post( "/api/w/{workspace_id}/worker-control/workers", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500, 502, 503] + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + openapi = false, )] async fn worker_control_spawn( &self, @@ -1137,12 +1150,12 @@ pub trait ServerApi { #[path] workspace_id: String, #[body] request: CreateWorkspaceWorkerRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/input", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500, 502, 503] + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + openapi = false, )] async fn worker_control_input( &self, @@ -1152,12 +1165,12 @@ pub trait ServerApi { #[path] worker_id: String, #[body] request: RuntimeWorkerInputRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/cancel", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500, 502, 503] + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], + openapi = false, )] async fn worker_control_cancel( &self, @@ -1167,12 +1180,12 @@ pub trait ServerApi { #[path] worker_id: String, #[body] request: RuntimeWorkerLifecycleRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/stop", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504] + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504], + openapi = false, )] async fn worker_control_stop( &self, @@ -1182,12 +1195,12 @@ pub trait ServerApi { #[path] worker_id: String, #[body] request: RuntimeWorkerLifecycleRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/restore", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504] + additional_error_statuses = [401, 403, 404, 409, 500, 502, 503, 504], + openapi = false, )] async fn worker_control_restore( &self, @@ -1196,26 +1209,24 @@ pub trait ServerApi { #[path] runtime_id: String, #[path] worker_id: String, ) -> Result; - #[get( "/api/w/{workspace_id}/worker-observation/sessions", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], - openapi = false + openapi = false, )] async fn worker_observation_sessions( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, ) -> Result; - #[post( "/api/w/{workspace_id}/worker-observation/session", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500, 502, 504], - openapi = false + openapi = false, )] async fn worker_observation_capture( &self, @@ -1223,12 +1234,12 @@ pub trait ServerApi { #[path] workspace_id: String, #[body] request: WorkerObservationSubjectRef, ) -> Result; - #[get( "/api/w/{workspace_id}/worker-discovery/workers", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 500] + additional_error_statuses = [401, 403, 404, 500], + openapi = false, )] async fn workspace_worker_discovery( &self, @@ -1303,12 +1314,12 @@ pub trait ServerApi { &self, #[path] workspace_id: String, ) -> Result; - #[post( "/api/runtime/v1/workspaces/{workspace_id}/resources/fetch", status = 200, error_status = 400, - additional_error_statuses = [401, 404, 410, 502, 504] + additional_error_statuses = [401, 404, 410, 502, 504], + openapi = false, )] async fn runtime_resource_fetch( &self, @@ -1420,12 +1431,12 @@ pub trait ServerApi { #[path] workspace_id: String, #[body] request: CompanionCancelRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/workers/remove", status = 200, error_status = 400, - additional_error_statuses = [401, 403, 404, 409, 500, 503] + additional_error_statuses = [401, 403, 404, 409, 500, 503], + openapi = false, )] async fn workspace_worker_remove( &self, @@ -2214,13 +2225,12 @@ pub trait ServerApi { #[path] working_directory_id: String, #[body] request: WorkingDirectoryRemovalRequest, ) -> Result; - #[post( "/api/w/{workspace_id}/workers/self/workdir-attachments", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], - bearer_auth = true + openapi = false, )] async fn current_worker_workdir_attach( &self, @@ -2228,13 +2238,12 @@ pub trait ServerApi { #[path] workspace_id: String, #[body] request: CurrentWorkerWorkdirAttachRequest, ) -> Result; - #[delete( "/api/w/{workspace_id}/workers/self/workdir-attachments/{alias}", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], - bearer_auth = true + openapi = false, )] async fn current_worker_workdir_detach( &self, @@ -2242,13 +2251,12 @@ pub trait ServerApi { #[path] workspace_id: String, #[path] alias: String, ) -> Result; - #[post( "/api/w/{workspace_id}/workers/self/workdir-session/operations", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500, 502, 503], - bearer_auth = true + openapi = false, )] async fn current_worker_workdir_operation( &self, @@ -2359,8 +2367,7 @@ pub trait ServerApi { #[path] working_directory_id: String, #[body] request: WorkingDirectoryRemovalRequest, ) -> Result; - - #[get("/api/tickets", status = 200, error_status = 400, additional_error_statuses = [500])] + #[get("/api/tickets", status = 200, error_status = 400, additional_error_statuses = [500], bearer_auth = true, browser_auth = true)] async fn ticket_list_alias( &self, #[query] query: TicketListHttpQuery, @@ -2387,61 +2394,53 @@ pub trait ServerApi { #[path] workspace_id: String, #[body] request: TicketQueryRequest, ) -> Result; - - #[get("/api/tickets/{id}", status = 200, error_status = 404, additional_error_statuses = [400, 500])] + #[get("/api/tickets/{id}", status = 200, error_status = 404, additional_error_statuses = [400, 500], bearer_auth = true, browser_auth = true)] async fn ticket_get_alias( &self, #[path] id: String, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/default-intake-ready-body", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[post("/api/w/{workspace_id}/tickets/default-intake-ready-body", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_default_intake_ready_body( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[body] request: DefaultIntakeReadyBodyRequest, ) -> Result; - - #[get("/api/w/{workspace_id}/tickets/search", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[get("/api/w/{workspace_id}/tickets/search", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_summary_search( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[query] query: TicketSummarySearchQuery, ) -> Result; - - #[get("/api/w/{workspace_id}/tickets/doctor", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[get("/api/w/{workspace_id}/tickets/doctor", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_doctor( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/relations/search", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[post("/api/w/{workspace_id}/tickets/relations/search", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_relation_query( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[body] request: TicketRelationSearchRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/orchestration-plans/search", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[post("/api/w/{workspace_id}/tickets/orchestration-plans/search", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_orchestration_plan_query( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[body] request: TicketOrchestrationPlanSearchRequest, ) -> Result; - - #[get("/api/w/{workspace_id}/tickets/{id}/record", status = 200, error_status = 404, additional_error_statuses = [400, 401, 403, 500])] + #[get("/api/w/{workspace_id}/tickets/{id}/record", status = 200, error_status = 404, additional_error_statuses = [400, 401, 403, 500], bearer_auth = true, browser_auth = true)] async fn ticket_record_get( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[path] id: String, ) -> Result; - - #[patch("/api/w/{workspace_id}/tickets/{id}/item", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[patch("/api/w/{workspace_id}/tickets/{id}/item", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_record_item_edit( &self, #[extension] context: ServerRequestContext, @@ -2449,16 +2448,14 @@ pub trait ServerApi { #[path] id: String, #[body] request: EditTicketRecordItemRequest, ) -> Result; - - #[get("/api/w/{workspace_id}/tickets/{id}/dependency-check", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[get("/api/w/{workspace_id}/tickets/{id}/dependency-check", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_dependency_check( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[path] id: String, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/thread-events", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/thread-events", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_thread_event_add( &self, #[extension] context: ServerRequestContext, @@ -2466,8 +2463,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: TicketThreadEventRequest, ) -> Result<(), RepositoryApiError>; - - #[post("/api/w/{workspace_id}/tickets/{id}/state-changes", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/state-changes", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_state_change_add( &self, #[extension] context: ServerRequestContext, @@ -2475,8 +2471,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: TicketStateChangeRequest, ) -> Result<(), RepositoryApiError>; - - #[post("/api/w/{workspace_id}/tickets/{id}/intake-summaries", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/intake-summaries", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_intake_summary_add( &self, #[extension] context: ServerRequestContext, @@ -2484,8 +2479,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: TicketIntakeSummaryRequest, ) -> Result<(), RepositoryApiError>; - - #[post("/api/w/{workspace_id}/tickets/{id}/state-fields/{field}", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/state-fields/{field}", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_state_field_set( &self, #[extension] context: ServerRequestContext, @@ -2494,8 +2488,7 @@ pub trait ServerApi { #[path] field: String, #[body] request: TicketStateChangeRequest, ) -> Result<(), RepositoryApiError>; - - #[post("/api/w/{workspace_id}/tickets/{id}/workflow-state", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/workflow-state", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_workflow_state_set( &self, #[extension] context: ServerRequestContext, @@ -2503,8 +2496,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: TicketStateChangeRequest, ) -> Result<(), RepositoryApiError>; - - #[post("/api/w/{workspace_id}/tickets/{id}/workflow/mark-ready", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/workflow/mark-ready", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_mark_ready_record( &self, #[extension] context: ServerRequestContext, @@ -2512,8 +2504,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: TicketMarkReadyRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/workflow/queue", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/workflow/queue", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_queue_record( &self, #[extension] context: ServerRequestContext, @@ -2535,8 +2526,7 @@ pub trait ServerApi { #[path] merge_request_id: String, #[query] query: MergeRequestThreadQuery, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/merge-request", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/merge-request", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], openapi = false)] async fn merge_request_open( &self, #[extension] context: ServerRequestContext, @@ -2544,23 +2534,20 @@ pub trait ServerApi { #[path] id: String, #[body] request: OpenMergeRequestRequest, ) -> Result; - - #[get("/api/w/{workspace_id}/tickets/{id}/merge-request/readiness", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[get("/api/w/{workspace_id}/tickets/{id}/merge-request/readiness", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn merge_request_readiness( &self, #[path] workspace_id: String, #[path] id: String, ) -> Result; - - #[get("/api/w/{workspace_id}/tickets/{id}/merge-request/thread", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[get("/api/w/{workspace_id}/tickets/{id}/merge-request/thread", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn merge_request_thread( &self, #[path] workspace_id: String, #[path] id: String, #[query] query: MergeRequestThreadQuery, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/repair-source", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] + #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/repair-source", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], browser_auth = true)] async fn merge_request_selector_repair( &self, #[extension] context: ServerRequestContext, @@ -2568,16 +2555,14 @@ pub trait ServerApi { #[path] id: String, #[body] request: RepairMergeRequestSelectorRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/internal/reviewer-child-sessions", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/internal/reviewer-child-sessions", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], openapi = false)] async fn merge_request_reviewer_child_register( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[body] request: RegisterReviewerChildSessionRequest, ) -> Result<(), RepositoryApiError>; - - #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/review-capabilities", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/review-capabilities", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], openapi = false)] async fn merge_request_review_capability_register( &self, #[extension] context: ServerRequestContext, @@ -2585,16 +2570,14 @@ pub trait ServerApi { #[path] id: String, #[body] request: RegisterMergeRequestReviewCapabilityRequest, ) -> Result<(), RepositoryApiError>; - - #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/reviews", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/reviews", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], openapi = false)] async fn merge_request_review_submit( &self, #[path] workspace_id: String, #[path] id: String, #[body] request: SubmitMergeRequestReviewRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/reviews/revoke", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/reviews/revoke", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], openapi = false)] async fn merge_request_review_revoke( &self, #[extension] context: ServerRequestContext, @@ -2602,8 +2585,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: RevokeMergeRequestReviewRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/complete", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/merge-request/complete", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], openapi = false)] async fn merge_request_complete( &self, #[extension] context: ServerRequestContext, @@ -2611,8 +2593,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: CompleteMergeRequestRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/workflow/close", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/workflow/close", status = 204, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_close_record( &self, #[extension] context: ServerRequestContext, @@ -2620,16 +2601,14 @@ pub trait ServerApi { #[path] id: String, #[body] request: TicketCloseRecordRequest, ) -> Result<(), RepositoryApiError>; - - #[get("/api/w/{workspace_id}/tickets/{id}/relation-view", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[get("/api/w/{workspace_id}/tickets/{id}/relation-view", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_relation_view( &self, #[extension] context: ServerRequestContext, #[path] workspace_id: String, #[path] id: String, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/relations", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/relations", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_relation_record( &self, #[extension] context: ServerRequestContext, @@ -2637,8 +2616,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: CreateTicketRelationRequest, ) -> Result; - - #[delete("/api/w/{workspace_id}/tickets/{id}/relations", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[delete("/api/w/{workspace_id}/tickets/{id}/relations", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_relation_remove( &self, #[extension] context: ServerRequestContext, @@ -2646,8 +2624,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: TicketRelationRemoveRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/orchestration-plans", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/orchestration-plans", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 409, 500], bearer_auth = true, browser_auth = true)] async fn ticket_orchestration_plan_record( &self, #[extension] context: ServerRequestContext, @@ -2670,8 +2647,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: BrowserEditTicketRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/tickets/{id}/show", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[post("/api/w/{workspace_id}/tickets/{id}/show", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn ticket_show( &self, #[path] workspace_id: String, @@ -2751,8 +2727,7 @@ pub trait ServerApi { #[path] id: String, #[body] request: BrowserCloseTicketRequest, ) -> Result; - - #[get("/api/objectives", status = 200, error_status = 400, additional_error_statuses = [500])] + #[get("/api/objectives", status = 200, error_status = 400, additional_error_statuses = [500], bearer_auth = true, browser_auth = true)] async fn objective_list_alias( &self, #[query] query: ObjectiveListQuery, @@ -2771,15 +2746,13 @@ pub trait ServerApi { #[path] workspace_id: String, #[body] request: ObjectiveCreateRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/objectives/query", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[post("/api/w/{workspace_id}/objectives/query", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn objective_query( &self, #[path] workspace_id: String, #[body] request: ObjectiveQueryRequest, ) -> Result; - - #[get("/api/objectives/{id}", status = 200, error_status = 404, additional_error_statuses = [400, 500])] + #[get("/api/objectives/{id}", status = 200, error_status = 404, additional_error_statuses = [400, 500], bearer_auth = true, browser_auth = true)] async fn objective_get_alias( &self, #[path] id: String, @@ -2799,8 +2772,7 @@ pub trait ServerApi { #[path] objective_id: String, #[body] request: ObjectiveEditRequest, ) -> Result; - - #[post("/api/w/{workspace_id}/objectives/{objective_id}/show", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500])] + #[post("/api/w/{workspace_id}/objectives/{objective_id}/show", status = 200, error_status = 400, additional_error_statuses = [401, 403, 404, 500], bearer_auth = true, browser_auth = true)] async fn objective_show( &self, #[path] workspace_id: String, @@ -10555,6 +10527,130 @@ mod openapi_artifact_tests { ); } + #[test] + fn server_api_openapi_security_matches_workspace_server_auth_semantics() { + const PUBLIC_UNAUTHENTICATED: &[&str] = &[ + "health", + "auth_config", + "auth_bootstrap_user", + "auth_passkey_registration_options", + "auth_passkey_login_options", + "auth_device_login_start", + "auth_device_login_poll", + "auth_whoami", + ]; + // These operations are authenticated by Runtime request-source proof, Worker source + // identity, or a one-use Reviewer capability. They remain generated Rust client/Axum + // operations, but must not appear as unauthenticated operations in the public OpenAPI. + const SIGNED_INTERNAL: &[&str] = &[ + "current_worker_workdir_attach", + "current_worker_workdir_detach", + "current_worker_workdir_operation", + "merge_request_complete", + "merge_request_open", + "merge_request_review_capability_register", + "merge_request_review_revoke", + "merge_request_review_submit", + "merge_request_reviewer_child_register", + "runtime_resource_fetch", + "worker_control_cancel", + "worker_control_input", + "worker_control_list", + "worker_control_restore", + "worker_control_spawn", + "worker_control_stop", + "worker_observation_capture", + "worker_observation_sessions", + "workspace_worker_discovery", + "workspace_worker_remove", + ]; + // Session observation retains its existing explicit non-OpenAPI boundary independently of + // the Runtime/Worker-source signed operation inventory above. + const OTHER_OPENAPI_EXCLUDED: &[&str] = &["worker_session"]; + const BROWSER_ONLY: &[&str] = &["merge_request_selector_repair"]; + + let document = canonical_openapi_document().expect("canonical OpenAPI contract must build"); + let value: serde_json::Value = + serde_json::from_str(&document.to_json().expect("document must serialize")) + .expect("document must be JSON"); + let paths = value["paths"] + .as_object() + .expect("OpenAPI paths must exist"); + let mut documented = std::collections::BTreeMap::new(); + for path in paths.values() { + for operation in path + .as_object() + .expect("OpenAPI path item must be an object") + .values() + { + let operation_id = operation["operationId"] + .as_str() + .expect("OpenAPI operationId must be a string"); + assert!( + documented.insert(operation_id, operation).is_none(), + "duplicate OpenAPI operationId {operation_id}" + ); + } + } + + let operations = ServerApiMetadata::OPERATIONS; + assert_eq!( + documented.len() + SIGNED_INTERNAL.len() + OTHER_OPENAPI_EXCLUDED.len(), + operations.len(), + "every ServerApi operation must have one bounded OpenAPI auth disposition" + ); + for operation in operations { + let operation_id = operation.operation_id; + if SIGNED_INTERNAL.contains(&operation_id) + || OTHER_OPENAPI_EXCLUDED.contains(&operation_id) + { + assert!( + !documented.contains_key(operation_id), + "internal/excluded operation {operation_id} must not appear public in OpenAPI" + ); + continue; + } + + let documented_operation = documented + .get(operation_id) + .unwrap_or_else(|| panic!("missing public OpenAPI operation {operation_id}")); + let expected = if PUBLIC_UNAUTHENTICATED.contains(&operation_id) { + &[][..] + } else if BROWSER_ONLY.contains(&operation_id) { + &["browserSession"][..] + } else { + &["bearerAuth", "browserSession"][..] + }; + let security = documented_operation.get("security"); + if expected.is_empty() { + assert!( + security.is_none(), + "public unauthenticated operation {operation_id} must not require security" + ); + continue; + } + let security = security + .and_then(serde_json::Value::as_array) + .unwrap_or_else(|| panic!("authenticated operation {operation_id} lacks security")); + let mut actual = security + .iter() + .map(|requirement| { + let requirement = requirement.as_object().unwrap_or_else(|| { + panic!("security requirement for {operation_id} must be an object") + }); + assert_eq!( + requirement.len(), + 1, + "security alternatives for {operation_id} must remain independent OR choices" + ); + requirement.keys().next().expect("one security scheme").as_str() + }) + .collect::>(); + actual.sort_unstable(); + assert_eq!(actual, expected, "security mismatch for {operation_id}"); + } + } + #[test] fn auth_and_workspace_catalog_contract_is_strict_and_excludes_cookie_routes() { let document = canonical_openapi_document().expect("canonical OpenAPI contract must build"); diff --git a/crates/server-api/src/repository_openapi_typescript.rs b/crates/server-api/src/repository_openapi_typescript.rs index cec65125a..33a57282f 100644 --- a/crates/server-api/src/repository_openapi_typescript.rs +++ b/crates/server-api/src/repository_openapi_typescript.rs @@ -662,27 +662,6 @@ fn validate_workdir_operations( request: Some("WorkingDirectoryRemovalRequest"), successes: &[("200", "WorkingDirectoryRemovalResponse")], }, - OperationContract { - path: "/api/w/{workspace_id}/workers/self/workdir-attachments", - method: "post", - operation_id: "current_worker_workdir_attach", - request: Some("CurrentWorkerWorkdirAttachRequest"), - successes: &[("200", "CurrentWorkerWorkdirAttachmentResponse")], - }, - OperationContract { - path: "/api/w/{workspace_id}/workers/self/workdir-attachments/{alias}", - method: "delete", - operation_id: "current_worker_workdir_detach", - request: None, - successes: &[("200", "CurrentWorkerWorkdirAttachmentResponse")], - }, - OperationContract { - path: "/api/w/{workspace_id}/workers/self/workdir-session/operations", - method: "post", - operation_id: "current_worker_workdir_operation", - request: Some("CurrentWorkerWorkdirOperationRequest"), - successes: &[("200", "WorkdirSessionOperationResult")], - }, OperationContract { path: "/api/w/{workspace_id}/external-workdir-grants", method: "post", @@ -1140,13 +1119,10 @@ fn validate_operation_contracts( &format!("{} response {status}", operation.operation_id), )?; let schema = json_content_schema(response, operation.operation_id)?; - let expected = successes.get(status.as_str()).copied().unwrap_or_else(|| { - if operation.operation_id == "current_worker_workdir_operation" { - "WorkdirOperationApiError" - } else { - "RepositoryApiError" - } - }); + let expected = successes + .get(status.as_str()) + .copied() + .unwrap_or("RepositoryApiError"); require_schema_ref(schema, expected, operation.operation_id)?; roots.insert(expected.to_owned()); } diff --git a/crates/workspace-server/src/authority.rs b/crates/workspace-server/src/authority.rs index 5069fe9dc..7d2c370c1 100644 --- a/crates/workspace-server/src/authority.rs +++ b/crates/workspace-server/src/authority.rs @@ -19,10 +19,10 @@ use crate::records::{ ObjectiveQueryItem, ObjectiveQueryRequest, ObjectiveQueryResponse, ObjectiveResourceSummary, ObjectiveShowRequest, ObjectiveSummary, ProjectRecordList, QueryPage, TicketActionEligibility, TicketAssignmentPrincipalSummary, TicketAssignmentSummary, TicketDetail, TicketEventDetail, - TicketEvidenceEvent, TicketEvidenceSummary, TicketListPageRequest, TicketMergeRequestSummary, - TicketQueryItem, TicketQueryRequest, TicketQueryResponse, TicketRelationView, + TicketEvidenceEvent, TicketEvidenceSummary, TicketListProjectionRequest, + TicketMergeRequestSummary, TicketQueryItem, TicketQueryRequest, TicketQueryResponse, TicketRoleAssignmentSummary, TicketShowRequest, TicketSummary, TicketSummaryPage, - summarize_body, truncate_body, + summarize_body, ticket_relation_view_from_domain, truncate_body, }; use crate::store::{ ControlPlaneStore, MemoryDocumentRecord, MemoryStagingRecord, MemoryStagingResolutionRecord, @@ -48,7 +48,7 @@ impl WorkspaceAuthority for T where T: ObjectiveAuthority + TicketAuthority + pub trait TicketAuthority { fn list_tickets(&self, limit: usize) -> Result>; - fn list_ticket_page(&self, request: TicketListPageRequest) -> Result; + fn list_ticket_page(&self, request: TicketListProjectionRequest) -> Result; fn query_tickets(&self, query: TicketQueryRequest) -> Result; fn ticket(&self, id: &str) -> Result; fn show_ticket(&self, id: &str, query: TicketShowRequest) -> Result; @@ -86,14 +86,14 @@ pub trait MemoryAuthority { fn ensure_memory_document(&self) -> Result; fn memory_document(&self) -> Result; fn update_memory_document(&self, body_md: &str) -> Result; - fn list_memory_staging_records(&self, limit: usize) -> Result>; - fn memory_staging_record(&self, candidate_id: &str) -> Result; + fn list_memory_staging_records(&self, limit: usize) -> Result>; + fn memory_staging_record(&self, candidate_id: &str) -> Result; fn upsert_memory_staging_record( &self, candidate_id: &str, raw_json: &str, source_path: Option<&str>, - ) -> Result; + ) -> Result; fn close_memory_staging_record( &self, candidate_id: &str, @@ -114,7 +114,7 @@ pub struct MemoryDocument { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct MemoryStagingEntry { +pub struct InternalMemoryStagingEntry { pub candidate_id: String, pub raw_json: String, pub source_path: Option, @@ -966,7 +966,7 @@ impl SqliteWorkspaceAuthority { &ticket.meta.id, )?) .ok_or_else(|| Error::Store(format!("missing resource key for {}", ticket.meta.id)))?; - let mut relations: TicketRelationView = ticket.relations.into(); + let mut relations = ticket_relation_view_from_domain(ticket.relations); for relation in &mut relations.outgoing { relation.target_resource_key = self.store.resource_key( &self.workspace_id, @@ -1073,7 +1073,7 @@ impl TicketAuthority for SqliteWorkspaceAuthority { }) } - fn list_ticket_page(&self, request: TicketListPageRequest) -> Result { + fn list_ticket_page(&self, request: TicketListProjectionRequest) -> Result { let limit = request.limit.unwrap_or(30).clamp(1, 100); let mut states = request.states; states.sort(); @@ -1587,7 +1587,7 @@ impl MemoryAuthority for SqliteWorkspaceAuthority { Ok(memory_document_from_record(record)) } - fn list_memory_staging_records(&self, limit: usize) -> Result> { + fn list_memory_staging_records(&self, limit: usize) -> Result> { self.store .list_memory_staging_records(&self.workspace_id, limit) .map(|records| { @@ -1598,7 +1598,7 @@ impl MemoryAuthority for SqliteWorkspaceAuthority { }) } - fn memory_staging_record(&self, candidate_id: &str) -> Result { + fn memory_staging_record(&self, candidate_id: &str) -> Result { validate_memory_candidate_id(candidate_id)?; self.store .get_memory_staging_record(&self.workspace_id, candidate_id)? @@ -1613,7 +1613,7 @@ impl MemoryAuthority for SqliteWorkspaceAuthority { candidate_id: &str, raw_json: &str, source_path: Option<&str>, - ) -> Result { + ) -> Result { validate_memory_candidate_id(candidate_id)?; validate_json_object(raw_json, "raw_json")?; let imported_at = now_rfc3339(); @@ -2634,8 +2634,8 @@ fn memory_document_from_record(record: MemoryDocumentRecord) -> MemoryDocument { } } -fn memory_staging_from_record(record: MemoryStagingRecord) -> MemoryStagingEntry { - MemoryStagingEntry { +fn memory_staging_from_record(record: MemoryStagingRecord) -> InternalMemoryStagingEntry { + InternalMemoryStagingEntry { candidate_id: record.candidate_id, raw_json: record.raw_json, source_path: record.source_path, @@ -3330,7 +3330,7 @@ VALUES ('workspace-test', 'ticket', 4); assert_eq!(incoming_relation.items.len(), 1); assert_eq!(incoming_relation.items[0].id, "00000000001J5"); let summary_page = authority - .list_ticket_page(TicketListPageRequest { + .list_ticket_page(TicketListProjectionRequest { states: vec!["planning".to_string(), "ready".to_string()], limit: Some(1), cursor: None, @@ -3338,7 +3338,7 @@ VALUES ('workspace-test', 'ticket', 4); .unwrap(); assert_eq!(summary_page.items.len(), 1); assert!(summary_page.page.has_more); - let mismatched_summary_cursor = authority.list_ticket_page(TicketListPageRequest { + let mismatched_summary_cursor = authority.list_ticket_page(TicketListProjectionRequest { states: vec!["done".to_string()], limit: Some(1), cursor: summary_page.page.next_cursor, diff --git a/crates/workspace-server/src/hosts.rs b/crates/workspace-server/src/hosts.rs index dfe5d72b9..e08194c3c 100644 --- a/crates/workspace-server/src/hosts.rs +++ b/crates/workspace-server/src/hosts.rs @@ -107,16 +107,16 @@ where #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct RuntimeDiagnostic { pub code: String, - pub severity: DiagnosticSeverity, + pub severity: HostDiagnosticSeverity, pub message: String, } impl RuntimeDiagnostic { pub fn new(code: impl Into, severity: &str, message: impl Into) -> Self { let severity = match severity { - "error" => DiagnosticSeverity::Error, - "warning" => DiagnosticSeverity::Warning, - _ => DiagnosticSeverity::Info, + "error" => HostDiagnosticSeverity::Error, + "warning" => HostDiagnosticSeverity::Warning, + _ => HostDiagnosticSeverity::Info, }; diagnostic(code, severity, message) } @@ -124,7 +124,7 @@ impl RuntimeDiagnostic { #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum DiagnosticSeverity { +pub enum HostDiagnosticSeverity { Info, Warning, Error, @@ -132,79 +132,79 @@ pub enum DiagnosticSeverity { #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum RuntimeSourceKind { +pub enum InternalRuntimeSourceKind { EmbeddedWorkerRuntime, RemoteHttp, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum RuntimeSourceStatus { +pub enum InternalRuntimeSourceStatus { Active, Reserved, } #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum RuntimeIdentityAuthority { +pub enum InternalRuntimeIdentityAuthority { /// Public Runtime/Host/Worker ids are registry projections, never raw /// socket addresses, session ids, credentials, or paths. RuntimeRegistryProjection, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct RuntimeSourceSummary { - pub kind: RuntimeSourceKind, - pub status: RuntimeSourceStatus, - pub identity_authority: RuntimeIdentityAuthority, +pub struct InternalRuntimeSourceSummary { + pub kind: InternalRuntimeSourceKind, + pub status: InternalRuntimeSourceStatus, + pub identity_authority: InternalRuntimeIdentityAuthority, pub note: String, } -impl RuntimeSourceSummary { +impl InternalRuntimeSourceSummary { pub fn embedded_worker_runtime() -> Self { Self { - kind: RuntimeSourceKind::EmbeddedWorkerRuntime, - status: RuntimeSourceStatus::Active, - identity_authority: RuntimeIdentityAuthority::RuntimeRegistryProjection, + kind: InternalRuntimeSourceKind::EmbeddedWorkerRuntime, + status: InternalRuntimeSourceStatus::Active, + identity_authority: InternalRuntimeIdentityAuthority::RuntimeRegistryProjection, note: "backend-internal embedded worker-runtime Runtime exposed only through runtime_id plus worker_id projections".to_string(), } } pub fn embedded_worker_runtime_reserved() -> Self { Self { - kind: RuntimeSourceKind::EmbeddedWorkerRuntime, - status: RuntimeSourceStatus::Reserved, - identity_authority: RuntimeIdentityAuthority::RuntimeRegistryProjection, + kind: InternalRuntimeSourceKind::EmbeddedWorkerRuntime, + status: InternalRuntimeSourceStatus::Reserved, + identity_authority: InternalRuntimeIdentityAuthority::RuntimeRegistryProjection, note: "reserved boundary for an embedded worker-runtime adapter; not connected by this fixture source".to_string(), } } pub fn remote_http() -> Self { Self { - kind: RuntimeSourceKind::RemoteHttp, - status: RuntimeSourceStatus::Active, - identity_authority: RuntimeIdentityAuthority::RuntimeRegistryProjection, + kind: InternalRuntimeSourceKind::RemoteHttp, + status: InternalRuntimeSourceStatus::Active, + identity_authority: InternalRuntimeIdentityAuthority::RuntimeRegistryProjection, note: "backend-owned remote worker-runtime REST/WS client; endpoints and credentials remain backend-private".to_string(), } } pub fn remote_http_reserved() -> Self { Self { - kind: RuntimeSourceKind::RemoteHttp, - status: RuntimeSourceStatus::Reserved, - identity_authority: RuntimeIdentityAuthority::RuntimeRegistryProjection, + kind: InternalRuntimeSourceKind::RemoteHttp, + status: InternalRuntimeSourceStatus::Reserved, + identity_authority: InternalRuntimeIdentityAuthority::RuntimeRegistryProjection, note: "reserved boundary for a future remote Runtime adapter; no HTTP client or REST server is implemented here".to_string(), } } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct RuntimeSummary { +pub struct InternalRuntimeSummary { pub runtime_id: String, pub label: String, pub kind: String, pub status: String, - pub source: RuntimeSourceSummary, + pub source: InternalRuntimeSourceSummary, pub host_ids: Vec, pub worker_creation_available: bool, pub os: String, @@ -213,7 +213,7 @@ pub struct RuntimeSummary { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct HostSummary { +pub struct InternalHostSummary { pub runtime_id: String, pub host_id: String, pub label: String, @@ -227,7 +227,7 @@ pub struct HostSummary { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct WorkerWorkspaceSummary { +pub struct InternalWorkerWorkspaceSummary { pub visibility: String, pub identity: String, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -235,19 +235,19 @@ pub struct WorkerWorkspaceSummary { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct WorkerImplementationSummary { +pub struct InternalWorkerImplementationSummary { pub kind: String, pub display_hint: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct WorkerCapabilitySummary { +pub struct InternalWorkerCapabilitySummary { pub can_stop: bool, pub can_spawn_followup: bool, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct WorkerSummary { +pub struct InternalWorkerSummary { #[serde(flatten)] pub worker: RuntimeWorkerRef, pub host_id: String, @@ -259,7 +259,7 @@ pub struct WorkerSummary { pub singleton_key: Option, #[serde(default)] pub tags: Vec, - pub workspace: WorkerWorkspaceSummary, + pub workspace: InternalWorkerWorkspaceSummary, /// Runtime catalog lifecycle compatibility state. pub state: String, #[serde(default, skip_serializing_if = "Option::is_none")] @@ -269,8 +269,8 @@ pub struct WorkerSummary { pub pinned: bool, #[serde(default)] pub retention_state: String, - pub implementation: WorkerImplementationSummary, - pub capabilities: WorkerCapabilitySummary, + pub implementation: InternalWorkerImplementationSummary, + pub capabilities: InternalWorkerCapabilitySummary, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub workdir_attachments: Vec, pub diagnostics: Vec, @@ -279,9 +279,9 @@ pub struct WorkerSummary { impl From for server_api::Diagnostic { fn from(diagnostic: RuntimeDiagnostic) -> Self { let severity = match diagnostic.severity { - DiagnosticSeverity::Info => server_api::DiagnosticSeverity::Info, - DiagnosticSeverity::Warning => server_api::DiagnosticSeverity::Warning, - DiagnosticSeverity::Error => server_api::DiagnosticSeverity::Error, + HostDiagnosticSeverity::Info => server_api::DiagnosticSeverity::Info, + HostDiagnosticSeverity::Warning => server_api::DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Error => server_api::DiagnosticSeverity::Error, }; Self { code: diagnostic.code, @@ -291,8 +291,8 @@ impl From for server_api::Diagnostic { } } -impl From for server_api::HostSummary { - fn from(host: HostSummary) -> Self { +impl From for server_api::HostSummary { + fn from(host: InternalHostSummary) -> Self { Self { runtime_id: host.runtime_id, host_id: host.host_id, @@ -308,20 +308,20 @@ impl From for server_api::HostSummary { } } -impl From for server_api::RuntimeSourceSummary { - fn from(source: RuntimeSourceSummary) -> Self { +impl From for server_api::RuntimeSourceSummary { + fn from(source: InternalRuntimeSourceSummary) -> Self { let kind = match source.kind { - RuntimeSourceKind::EmbeddedWorkerRuntime => { + InternalRuntimeSourceKind::EmbeddedWorkerRuntime => { server_api::RuntimeSourceKind::EmbeddedWorkerRuntime } - RuntimeSourceKind::RemoteHttp => server_api::RuntimeSourceKind::RemoteHttp, + InternalRuntimeSourceKind::RemoteHttp => server_api::RuntimeSourceKind::RemoteHttp, }; let status = match source.status { - RuntimeSourceStatus::Active => server_api::RuntimeSourceStatus::Active, - RuntimeSourceStatus::Reserved => server_api::RuntimeSourceStatus::Reserved, + InternalRuntimeSourceStatus::Active => server_api::RuntimeSourceStatus::Active, + InternalRuntimeSourceStatus::Reserved => server_api::RuntimeSourceStatus::Reserved, }; let identity_authority = match source.identity_authority { - RuntimeIdentityAuthority::RuntimeRegistryProjection => { + InternalRuntimeIdentityAuthority::RuntimeRegistryProjection => { server_api::RuntimeIdentityAuthority::RuntimeRegistryProjection } }; @@ -334,8 +334,8 @@ impl From for server_api::RuntimeSourceSummary { } } -impl From for server_api::RuntimeSummary { - fn from(runtime: RuntimeSummary) -> Self { +impl From for server_api::RuntimeSummary { + fn from(runtime: InternalRuntimeSummary) -> Self { Self { runtime_id: runtime.runtime_id, label: runtime.label, @@ -352,7 +352,7 @@ impl From for server_api::RuntimeSummary { } pub(crate) fn workspace_worker_summary( - summary: WorkerSummary, + summary: InternalWorkerSummary, resource_key: String, workdir_attachments: Vec, ) -> server_api::WorkerSummary { @@ -391,18 +391,18 @@ pub(crate) fn workspace_worker_summary( #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(deny_unknown_fields)] -pub struct WorkerRestoreResult { +pub struct InternalWorkerRestoreResult { pub state: server_api::WorkerRestoreState, #[serde(skip_serializing_if = "Option::is_none")] - pub worker: Option, + pub worker: Option, pub diagnostics: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorkerWorkspaceApiResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(skip_serializing_if = "Option::is_none")] - pub worker: Option, + pub worker: Option, pub diagnostics: Vec, } @@ -421,13 +421,13 @@ impl RuntimeList { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorkerLookupResult { #[serde(skip_serializing_if = "Option::is_none")] - pub worker: Option, + pub worker: Option, pub diagnostics: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct RuntimeWorkingDirectoryResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(skip_serializing_if = "Option::is_none")] pub working_directory: Option, pub diagnostics: Vec, @@ -587,16 +587,16 @@ fn initial_worker_input(segments: &[Segment]) -> Option { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorkerSpawnResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(skip_serializing_if = "Option::is_none")] - pub worker: Option, + pub worker: Option, pub acceptance_evidence: Vec, pub diagnostics: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct ConfigBundleSyncResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(skip_serializing_if = "Option::is_none")] pub availability: Option, pub diagnostics: Vec, @@ -604,7 +604,7 @@ pub struct ConfigBundleSyncResult { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct ConfigBundleCheckResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(skip_serializing_if = "Option::is_none")] pub availability: Option, pub diagnostics: Vec, @@ -622,7 +622,7 @@ fn required_worker_workspace_api( request.resolved_workspace_api.clone().ok_or_else(|| { diagnostic( "worker_workspace_api_missing", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Workspace-bound Worker spawn requires a resolved Workspace API binding", ) }) @@ -630,18 +630,18 @@ fn required_worker_workspace_api( #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -pub enum WorkerOperationState { +pub enum InternalWorkerOperationState { Accepted, Unsupported, Rejected, } -impl From for server_api::WorkerOperationState { - fn from(state: WorkerOperationState) -> Self { +impl From for server_api::WorkerOperationState { + fn from(state: InternalWorkerOperationState) -> Self { match state { - WorkerOperationState::Accepted => Self::Accepted, - WorkerOperationState::Unsupported => Self::Unsupported, - WorkerOperationState::Rejected => Self::Rejected, + InternalWorkerOperationState::Accepted => Self::Accepted, + InternalWorkerOperationState::Unsupported => Self::Unsupported, + InternalWorkerOperationState::Rejected => Self::Rejected, } } } @@ -667,7 +667,7 @@ pub enum WorkerStopMode { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorkerStopResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, pub diagnostics: Vec, } @@ -681,7 +681,7 @@ pub struct WorkerLifecycleRequest { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorkerLifecycleResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(flatten)] pub worker: RuntimeWorkerRef, pub diagnostics: Vec, @@ -699,7 +699,7 @@ pub enum WorkerInputKind { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorkerDeleteResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(flatten)] pub worker: RuntimeWorkerRef, pub deleted: bool, @@ -734,7 +734,7 @@ pub struct WorkerCompletionsResult { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct WorkerInputResult { - pub state: WorkerOperationState, + pub state: InternalWorkerOperationState, #[serde(flatten)] pub worker: RuntimeWorkerRef, pub diagnostics: Vec, @@ -833,7 +833,7 @@ impl RuntimePingFailure { ) -> Self { Self { kind, - diagnostic: diagnostic(code, DiagnosticSeverity::Error, message.into()), + diagnostic: diagnostic(code, HostDiagnosticSeverity::Error, message.into()), } } } @@ -841,7 +841,7 @@ impl RuntimePingFailure { pub trait WorkspaceWorkerRuntime: Send + Sync { fn runtime_id(&self) -> &str; - fn runtime_summary(&self, limit: usize) -> RuntimeSummary; + fn runtime_summary(&self, limit: usize) -> InternalRuntimeSummary; fn ping(&self) -> Result { Err(RuntimePingFailure::new( @@ -888,23 +888,23 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { )) } - fn list_hosts(&self, limit: usize) -> RuntimeList; + fn list_hosts(&self, limit: usize) -> RuntimeList; - fn list_workers(&self, limit: usize) -> RuntimeList; + fn list_workers(&self, limit: usize) -> RuntimeList; - fn list_stopped_workers(&self, _limit: usize) -> RuntimeList { + fn list_stopped_workers(&self, _limit: usize) -> RuntimeList { RuntimeList::new(Vec::new(), Vec::new()) } fn worker(&self, worker_id: &str) -> WorkerLookupResult; - fn restore_worker(&self, worker_id: &str) -> WorkerRestoreResult { - WorkerRestoreResult { + fn restore_worker(&self, worker_id: &str) -> InternalWorkerRestoreResult { + InternalWorkerRestoreResult { state: server_api::WorkerRestoreState::Rejected, worker: None, diagnostics: vec![diagnostic( "worker_restore_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!("runtime does not implement worker restore for `{worker_id}`"), )], } @@ -916,11 +916,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { _workspace_api: WorkspaceApiRef, ) -> WorkerWorkspaceApiResult { WorkerWorkspaceApiResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: None, diagnostics: vec![diagnostic( "worker_workspace_api_replace_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "runtime does not support replacing the Workspace API for worker `{worker_id}`" ), @@ -934,11 +934,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { _attachments: Vec, ) -> WorkerWorkspaceApiResult { WorkerWorkspaceApiResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: None, diagnostics: vec![diagnostic( "worker_workdir_attachments_replace_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "runtime does not support replacing Workdir attachments for worker `{worker_id}`" ), @@ -951,11 +951,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { _request: WorkingDirectoryRequest, ) -> RuntimeWorkingDirectoryResult { RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, working_directory: None, diagnostics: vec![diagnostic( "runtime_working_directory_create_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "runtime does not implement working directory creation".to_string(), )], } @@ -988,11 +988,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { fn working_directory(&self, working_directory_id: &str) -> RuntimeWorkingDirectoryResult { RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, working_directory: None, diagnostics: vec![diagnostic( "runtime_working_directory_lookup_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "runtime does not implement working directory lookup for `{working_directory_id}`" ), @@ -1019,11 +1019,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { working_directory_id: &str, ) -> RuntimeWorkingDirectoryResult { RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, working_directory: None, diagnostics: vec![diagnostic( "runtime_working_directory_cleanup_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "runtime does not implement working directory cleanup for `{working_directory_id}`" ), @@ -1037,12 +1037,12 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { request: WorkerSpawnRequest, ) -> WorkerSpawnResult { WorkerSpawnResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: None, acceptance_evidence: Vec::new(), diagnostics: vec![diagnostic( "worker_spawn_resolver_pending", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker spawn intent '{}' was accepted as a typed request shape, but launch resolution is not implemented by this registry surface", worker_spawn_intent_label(&request.intent) @@ -1060,11 +1060,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { fn sync_config_bundle(&self, _bundle: ConfigBundle) -> ConfigBundleSyncResult { ConfigBundleSyncResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, availability: None, diagnostics: vec![diagnostic( "config_bundle_sync_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "runtime does not implement config bundle sync".to_string(), )], } @@ -1072,11 +1072,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { fn check_config_bundle(&self, _reference: ConfigBundleRef) -> ConfigBundleCheckResult { ConfigBundleCheckResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, availability: None, diagnostics: vec![diagnostic( "config_bundle_check_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "runtime does not implement config bundle availability checks".to_string(), )], } @@ -1087,7 +1087,7 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { bundles: Vec::new(), diagnostics: vec![diagnostic( "config_bundle_list_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "runtime does not implement config bundle listing".to_string(), )], } @@ -1111,11 +1111,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { _request: WorkerLifecycleRequest, ) -> WorkerLifecycleResult { WorkerLifecycleResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: RuntimeWorkerRef::new(self.runtime_id().to_string(), worker_id.to_string()), diagnostics: vec![diagnostic( "worker_stop_pending", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker stop for '{worker_id}' is reserved for the runtime service boundary and is not implemented by this registry surface" ), @@ -1129,11 +1129,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { _request: WorkerLifecycleRequest, ) -> WorkerLifecycleResult { WorkerLifecycleResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: RuntimeWorkerRef::new(self.runtime_id().to_string(), worker_id.to_string()), diagnostics: vec![diagnostic( "worker_cancel_pending", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker cancel for '{worker_id}' is reserved for the runtime service boundary and is not implemented by this registry surface" ), @@ -1143,12 +1143,12 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { fn delete_worker(&self, worker_id: &str) -> WorkerDeleteResult { WorkerDeleteResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: RuntimeWorkerRef::new(self.runtime_id().to_string(), worker_id.to_string()), deleted: false, diagnostics: vec![diagnostic( "worker_delete_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!("runtime does not implement worker deletion for '{worker_id}'"), )], } @@ -1193,11 +1193,11 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { fn send_input(&self, worker_id: &str, _request: WorkerInputRequest) -> WorkerInputResult { WorkerInputResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: RuntimeWorkerRef::new(self.runtime_id().to_string(), worker_id.to_string()), diagnostics: vec![diagnostic( "worker_input_pending", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker input for '{worker_id}' is reserved for the runtime service boundary and is not implemented by this registry source" ), @@ -1246,7 +1246,7 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { entries: Vec::new(), diagnostics: vec![diagnostic( "worker_completions_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!("runtime does not implement completions for worker '{worker_id}'"), )], } @@ -1258,7 +1258,7 @@ pub trait WorkspaceWorkerRuntime: Send + Sync { status: "not_implemented".to_string(), diagnostics: vec![diagnostic( "worker_proxy_pending", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker proxy connect points for '{}' are not implemented by this overview-only registry surface", worker_id @@ -1378,7 +1378,7 @@ impl RuntimeRegistry { } } - pub fn list_runtimes(&self, limit: usize) -> RuntimeList { + pub fn list_runtimes(&self, limit: usize) -> RuntimeList { let mut diagnostics = Vec::new(); let mut items = Vec::new(); for runtime in self.runtimes_snapshot().iter().take(limit) { @@ -1390,7 +1390,7 @@ impl RuntimeRegistry { RuntimeList::new(items, diagnostics) } - pub fn list_hosts(&self, limit: usize) -> RuntimeList { + pub fn list_hosts(&self, limit: usize) -> RuntimeList { let mut items = Vec::new(); let mut diagnostics = Vec::new(); for runtime in self.runtimes_snapshot() { @@ -1405,7 +1405,7 @@ impl RuntimeRegistry { RuntimeList::new(items, diagnostics) } - pub fn list_workers(&self, limit: usize) -> RuntimeList { + pub fn list_workers(&self, limit: usize) -> RuntimeList { let mut items = Vec::new(); let mut diagnostics = Vec::new(); for runtime in self.runtimes_snapshot() { @@ -1428,7 +1428,7 @@ impl RuntimeRegistry { &self, runtime_id: &str, limit: usize, - ) -> Result, RuntimeRegistryError> { + ) -> Result, RuntimeRegistryError> { validate_backend_identifier("runtime_id", runtime_id)?; let runtime = self.runtime(runtime_id)?; let worker_list = runtime.list_workers(limit); @@ -1443,7 +1443,7 @@ impl RuntimeRegistry { &self, runtime_id: &str, limit: usize, - ) -> Result, RuntimeRegistryError> { + ) -> Result, RuntimeRegistryError> { validate_backend_identifier("runtime_id", runtime_id)?; let runtime = self.runtime(runtime_id)?; let worker_list = runtime.list_stopped_workers(limit); @@ -1458,7 +1458,7 @@ impl RuntimeRegistry { &self, host_id: &str, limit: usize, - ) -> Result, RuntimeRegistryError> { + ) -> Result, RuntimeRegistryError> { validate_backend_identifier("host_id", host_id)?; let mut host_found = false; @@ -1492,7 +1492,10 @@ impl RuntimeRegistry { } } - pub fn worker(&self, worker: &RuntimeWorkerRef) -> Result { + pub fn worker( + &self, + worker: &RuntimeWorkerRef, + ) -> Result { let runtime_id = worker.runtime_id.as_str(); let worker_id = worker.worker_id.as_str(); validate_backend_identifier("runtime_id", runtime_id)?; @@ -1508,7 +1511,7 @@ impl RuntimeRegistry { pub fn restore_worker( &self, worker: &RuntimeWorkerRef, - ) -> Result { + ) -> Result { let runtime_id = worker.runtime_id.as_str(); let worker_id = worker.worker_id.as_str(); validate_backend_identifier("runtime_id", runtime_id)?; @@ -1556,7 +1559,7 @@ impl RuntimeRegistry { .map(|message| { diagnostic( "workspace_prompt_projection_notification_failed", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, format!( "runtime '{}' rejected Workspace Prompt projection revision {}: {message}", runtime.runtime_id(), projection.config_revision @@ -1604,7 +1607,7 @@ impl RuntimeRegistry { && let Some(bundle) = request.resolved_config_bundle.clone() { let sync = runtime.sync_config_bundle(bundle); - if sync.state != WorkerOperationState::Accepted { + if sync.state != InternalWorkerOperationState::Accepted { let message = sync .diagnostics .first() @@ -2190,7 +2193,10 @@ impl EmbeddedWorkerRuntime { runtime_worker_can_stop(self.execution_enabled, status) } - fn map_worker_summary(&self, summary: worker_runtime::catalog::WorkerSummary) -> WorkerSummary { + fn map_worker_summary( + &self, + summary: worker_runtime::catalog::WorkerSummary, + ) -> InternalWorkerSummary { let worker_id = summary.worker_ref.worker_id.to_string(); let profile = embedded_profile_label(&summary.profile); let display = worker_display_metadata( @@ -2199,7 +2205,7 @@ impl EmbeddedWorkerRuntime { summary.display_name.as_deref(), true, ); - WorkerSummary { + InternalWorkerSummary { worker: RuntimeWorkerRef::new(&self.runtime_id, worker_id.clone()), host_id: self.host_id.clone(), display_name: display.display_name.clone(), @@ -2207,7 +2213,7 @@ impl EmbeddedWorkerRuntime { profile, singleton_key: display.singleton_key, tags: display.tags, - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "backend_internal".to_string(), identity: "runtime_registry_worker".to_string(), workspace_id: summary.workspace_id.clone(), @@ -2221,11 +2227,11 @@ impl EmbeddedWorkerRuntime { last_seen_at: None, pinned: false, retention_state: "transient".to_string(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "embedded_worker_runtime".to_string(), display_hint: "backend-internal worker-runtime Worker".to_string(), }, - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: summary.execution_metadata_available && self.can_stop_embedded_worker(summary.status), can_spawn_followup: false, @@ -2237,7 +2243,7 @@ impl EmbeddedWorkerRuntime { } } - fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> WorkerSummary { + fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> InternalWorkerSummary { let worker_id = detail.worker_id.to_string(); let profile = embedded_profile_label(&detail.profile); let display = worker_display_metadata( @@ -2246,7 +2252,7 @@ impl EmbeddedWorkerRuntime { detail.display_name.as_deref(), true, ); - WorkerSummary { + InternalWorkerSummary { worker: RuntimeWorkerRef::new(&self.runtime_id, worker_id.clone()), host_id: self.host_id.clone(), display_name: display.display_name.clone(), @@ -2254,7 +2260,7 @@ impl EmbeddedWorkerRuntime { profile, singleton_key: display.singleton_key, tags: display.tags, - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "backend_internal".to_string(), identity: "runtime_registry_worker".to_string(), workspace_id: detail.workspace_id.clone(), @@ -2265,11 +2271,11 @@ impl EmbeddedWorkerRuntime { last_seen_at: None, pinned: false, retention_state: "transient".to_string(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "embedded_worker_runtime".to_string(), display_hint: "backend-internal worker-runtime Worker".to_string(), }, - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: detail.execution_metadata_available && self.can_stop_embedded_worker(detail.status), can_spawn_followup: false, @@ -2287,18 +2293,18 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { &self.runtime_id } - fn runtime_summary(&self, limit: usize) -> RuntimeSummary { + fn runtime_summary(&self, limit: usize) -> InternalRuntimeSummary { let mut diagnostics = Vec::new(); let summary = match self.runtime.summary() { Ok(summary) => summary, Err(err) => { diagnostics.push(embedded_runtime_diagnostic(&err)); - return RuntimeSummary { + return InternalRuntimeSummary { runtime_id: self.runtime_id.clone(), label: "Embedded backend Runtime".to_string(), kind: "embedded_worker_runtime".to_string(), status: "unavailable".to_string(), - source: RuntimeSourceSummary::embedded_worker_runtime(), + source: InternalRuntimeSourceSummary::embedded_worker_runtime(), host_ids: Vec::new(), worker_creation_available: false, os: std::env::consts::OS.to_string(), @@ -2308,7 +2314,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { } }; - RuntimeSummary { + InternalRuntimeSummary { runtime_id: self.runtime_id.clone(), label: summary .display_name @@ -2316,7 +2322,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { .unwrap_or_else(|| "Embedded backend Runtime".to_string()), kind: "embedded_worker_runtime".to_string(), status: embedded_runtime_status_label(summary.status).to_string(), - source: RuntimeSourceSummary::embedded_worker_runtime(), + source: InternalRuntimeSourceSummary::embedded_worker_runtime(), host_ids: if limit == 0 { Vec::new() } else { @@ -2329,12 +2335,12 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { } } - fn list_hosts(&self, limit: usize) -> RuntimeList { + fn list_hosts(&self, limit: usize) -> RuntimeList { if limit == 0 { return RuntimeList::new(Vec::new(), Vec::new()); } RuntimeList::new( - vec![HostSummary { + vec![InternalHostSummary { runtime_id: self.runtime_id.clone(), host_id: self.host_id.clone(), label: "embedded".to_string(), @@ -2346,7 +2352,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { arch: std::env::consts::ARCH.to_string(), diagnostics: vec![diagnostic( "embedded_runtime_host_boundary", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "Backend-internal host exposes only bounded runtime and worker projections" .to_string(), )], @@ -2355,7 +2361,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { ) } - fn list_workers(&self, limit: usize) -> RuntimeList { + fn list_workers(&self, limit: usize) -> RuntimeList { if limit == 0 { return RuntimeList::new(Vec::new(), Vec::new()); } @@ -2372,7 +2378,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { } } - fn list_stopped_workers(&self, limit: usize) -> RuntimeList { + fn list_stopped_workers(&self, limit: usize) -> RuntimeList { if limit == 0 { return RuntimeList::new(Vec::new(), Vec::new()); } @@ -2395,7 +2401,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { worker: None, diagnostics: vec![diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot be resolved".to_string(), )], }; @@ -2416,14 +2422,14 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { } } - fn restore_worker(&self, worker_id: &str) -> WorkerRestoreResult { + fn restore_worker(&self, worker_id: &str) -> InternalWorkerRestoreResult { let Some(worker_ref) = self.worker_ref(worker_id) else { - return WorkerRestoreResult { + return InternalWorkerRestoreResult { state: server_api::WorkerRestoreState::Rejected, worker: None, diagnostics: vec![diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot be restored".to_string(), )], }; @@ -2432,17 +2438,17 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { Ok(result) => { let diagnostics = match (result.reason_code, result.message) { (Some(code), Some(message)) => { - vec![diagnostic(code, DiagnosticSeverity::Warning, message)] + vec![diagnostic(code, HostDiagnosticSeverity::Warning, message)] } _ => Vec::new(), }; - WorkerRestoreResult { + InternalWorkerRestoreResult { state: result.state, worker: result.worker.map(|detail| self.map_worker_detail(detail)), diagnostics, } } - Err(err) => WorkerRestoreResult { + Err(err) => InternalWorkerRestoreResult { state: server_api::WorkerRestoreState::ReconciliationRequired, worker: None, diagnostics: vec![embedded_runtime_diagnostic(&err)], @@ -2457,11 +2463,11 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { ) -> WorkerWorkspaceApiResult { let Some(worker_ref) = self.worker_ref(worker_id) else { return WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot receive Workspace access".to_string(), )], }; @@ -2471,12 +2477,12 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { .replace_worker_workspace_api(&worker_ref, workspace_api) { Ok(detail) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: Some(self.map_worker_detail(detail)), diagnostics: Vec::new(), }, Err(err) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![embedded_runtime_diagnostic(&err)], }, @@ -2490,11 +2496,11 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { ) -> WorkerWorkspaceApiResult { let Some(worker_ref) = self.worker_ref(worker_id) else { return WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot change Workdir attachments".to_string(), )], }; @@ -2504,12 +2510,12 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { .replace_worker_workdir_attachments(&worker_ref, attachments) { Ok(detail) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: Some(self.map_worker_detail(detail)), diagnostics: Vec::new(), }, Err(err) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![embedded_runtime_diagnostic(&err)], }, @@ -2521,7 +2527,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { _request: WorkingDirectoryRequest, ) -> RuntimeWorkingDirectoryResult { RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![embedded_workdir_unsupported_diagnostic()], } @@ -2555,7 +2561,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { fn working_directory(&self, _working_directory_id: &str) -> RuntimeWorkingDirectoryResult { RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![embedded_workdir_unsupported_diagnostic()], } @@ -2566,7 +2572,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { _working_directory_id: &str, ) -> RuntimeWorkingDirectoryResult { RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![embedded_workdir_unsupported_diagnostic()], } @@ -2583,7 +2589,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { { diagnostics.push(embedded_workdir_unsupported_diagnostic()); return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics, @@ -2595,11 +2601,11 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { ) { diagnostics.push(diagnostic( "embedded_runtime_no_socket", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Embedded backend Runtime is transportless; use run_accepted/create acceptance for backend-internal Workers".to_string(), )); return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics, @@ -2608,7 +2614,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { if request.requested_worker_name.is_some() { diagnostics.push(diagnostic( "embedded_worker_name_display_only", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "requested_worker_name is used only as display_name; Worker identity is allocated by Workspace authority".to_string(), )); } @@ -2616,7 +2622,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { { diagnostics.push(diagnostic( "embedded_runtime_acceptance_projection", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "Embedded Runtime accepts creation through a runtime execution backend; provider segment counts are observed after execution, not faked at create time".to_string(), )); } @@ -2627,11 +2633,11 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { Err(error) => { diagnostics.push(diagnostic( "embedded_profile_source_archive_invalid", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error, )); return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics, @@ -2643,7 +2649,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { Err(diagnostic) => { diagnostics.push(diagnostic); return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics, @@ -2665,7 +2671,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { .create_worker_scoped(&workspace_scope, create_request) { Ok(detail) => WorkerSpawnResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: Some(self.map_worker_detail(detail)), acceptance_evidence: vec![ WorkerSpawnAcceptanceEvidence { @@ -2685,7 +2691,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { Err(err) => { diagnostics.push(embedded_runtime_diagnostic(&err)); WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics, @@ -2706,12 +2712,12 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { fn sync_config_bundle(&self, bundle: ConfigBundle) -> ConfigBundleSyncResult { match self.runtime.store_config_bundle(bundle) { Ok(availability) => ConfigBundleSyncResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, availability: Some(availability), diagnostics: Vec::new(), }, Err(error) => ConfigBundleSyncResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, availability: None, diagnostics: vec![embedded_runtime_diagnostic(&error)], }, @@ -2721,12 +2727,12 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { fn check_config_bundle(&self, reference: ConfigBundleRef) -> ConfigBundleCheckResult { match self.runtime.check_config_bundle(&reference) { Ok(availability) => ConfigBundleCheckResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, availability: Some(availability), diagnostics: Vec::new(), }, Err(error) => ConfigBundleCheckResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, availability: None, diagnostics: vec![embedded_runtime_diagnostic(&error)], }, @@ -2757,7 +2763,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { worker_id, diagnostic( "embedded_worker_execution_unavailable", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!("worker stop for '{worker_id}' requires an embedded execution backend"), ), ); @@ -2768,14 +2774,14 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { worker_id, diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot be resolved".to_string(), ), ); }; match self.runtime.stop_worker(&worker_ref, request.reason) { Ok(_) => WorkerLifecycleResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), diagnostics: Vec::new(), }, @@ -2798,7 +2804,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { worker_id, diagnostic( "embedded_worker_execution_unavailable", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker cancel for '{worker_id}' requires an embedded execution backend" ), @@ -2811,14 +2817,14 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { worker_id, diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot be resolved".to_string(), ), ); }; match self.runtime.cancel_worker(&worker_ref, request.reason) { Ok(_) => WorkerLifecycleResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), diagnostics: Vec::new(), }, @@ -2833,19 +2839,19 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { fn delete_worker(&self, worker_id: &str) -> WorkerDeleteResult { let Some(worker_ref) = self.worker_ref(worker_id) else { return WorkerDeleteResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), deleted: false, diagnostics: vec![diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot be resolved".to_string(), )], }; }; match self.runtime.delete_worker(&worker_ref) { Ok(result) => WorkerDeleteResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new( self.runtime_id.clone(), result.worker_id.to_string(), @@ -2854,7 +2860,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { diagnostics: Vec::new(), }, Err(error) => WorkerDeleteResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), deleted: false, diagnostics: vec![embedded_runtime_diagnostic(&error)], @@ -2949,7 +2955,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { worker_id, diagnostic( "embedded_worker_execution_unavailable", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker input for '{worker_id}' requires an embedded execution backend" ), @@ -2962,7 +2968,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { worker_id, diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot be resolved".to_string(), ), ); @@ -2981,7 +2987,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { }; match self.runtime.send_input(&worker_ref, input) { Ok(_) => WorkerInputResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), diagnostics: Vec::new(), }, @@ -3057,7 +3063,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { entries: Vec::new(), diagnostics: vec![diagnostic( "embedded_worker_execution_unavailable", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "worker completions for '{worker_id}' require an embedded execution backend" ), @@ -3072,7 +3078,7 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { entries: Vec::new(), diagnostics: vec![diagnostic( "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Worker id was empty and cannot be resolved".to_string(), )], }; @@ -3168,7 +3174,7 @@ impl WorkspaceRuntimeAuthorization { .map_err(|_| { diagnostic( "workspace_runtime_authorization_unavailable", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Workspace Runtime authorization is unavailable".to_string(), ) })? @@ -3176,7 +3182,7 @@ impl WorkspaceRuntimeAuthorization { .ok_or_else(|| { diagnostic( "workspace_runtime_verification_required", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Workspace Runtime binding is not verified".to_string(), ) })?; @@ -3189,14 +3195,14 @@ impl WorkspaceRuntimeAuthorization { .map_err(|error| { diagnostic( "workspace_runtime_authorization_unavailable", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) })? { return Err(diagnostic( "workspace_runtime_authorization_stale", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Workspace Runtime binding changed or was revoked".to_string(), )); } @@ -3206,21 +3212,21 @@ impl WorkspaceRuntimeAuthorization { .map_err(|error| { diagnostic( "workspace_runtime_authorization_unavailable", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) })?; let workspace_key_id = binding.workspace_key_id.as_deref().ok_or_else(|| { diagnostic( "workspace_runtime_authorization_invalid", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Workspace Runtime binding is missing its Workspace key".to_string(), ) })?; let trust_generation = binding.workspace_key_generation.ok_or_else(|| { diagnostic( "workspace_runtime_authorization_invalid", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Workspace Runtime binding is missing its trust generation".to_string(), ) })?; @@ -3237,14 +3243,14 @@ impl WorkspaceRuntimeAuthorization { .map_err(|error| { diagnostic( "workspace_runtime_authorization_unavailable", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) })? { return Err(diagnostic( "workspace_runtime_authorization_stale", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Workspace signing identity no longer matches the verified binding".to_string(), )); } @@ -3271,7 +3277,7 @@ impl WorkspaceRuntimeAuthorization { .map_err(|error| { diagnostic( "workspace_runtime_authorization_sign_failed", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) }) @@ -3756,7 +3762,7 @@ impl RemoteWorkerRuntime { .map_err(|error| { diagnostic( "remote_runtime_client_build_failed", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) })? @@ -3772,7 +3778,7 @@ impl RemoteWorkerRuntime { .map_err(|error| { diagnostic( "remote_runtime_client_build_failed", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) }) @@ -3906,7 +3912,7 @@ impl RemoteWorkerRuntime { let body = serde_json::to_vec(body).map_err(|error| { diagnostic( "remote_runtime_request_encode_failed", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) })?; @@ -3988,7 +3994,7 @@ impl RemoteWorkerRuntime { .map_err(|_| { diagnostic( "remote_runtime_response_read_failed", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!( "Remote Runtime response could not be read for '{}'", runtime_id @@ -3998,7 +4004,7 @@ impl RemoteWorkerRuntime { if body.len() > MAX_REMOTE_RUNTIME_RESPONSE_BYTES { return Err(diagnostic( "remote_runtime_response_too_large", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!( "Remote Runtime response exceeded the allowed size for '{}'", runtime_id @@ -4008,7 +4014,7 @@ impl RemoteWorkerRuntime { serde_json::from_slice::(&body).map_err(|_| { diagnostic( "remote_runtime_malformed_response", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!( "Remote Runtime returned malformed JSON for '{}'", runtime_id @@ -4021,7 +4027,10 @@ impl RemoteWorkerRuntime { }) } - fn map_worker_summary(&self, summary: worker_runtime::catalog::WorkerSummary) -> WorkerSummary { + fn map_worker_summary( + &self, + summary: worker_runtime::catalog::WorkerSummary, + ) -> InternalWorkerSummary { let worker_id = summary.worker_ref.worker_id.to_string(); let profile = embedded_profile_label(&summary.profile); let display = worker_display_metadata( @@ -4030,7 +4039,7 @@ impl RemoteWorkerRuntime { summary.display_name.as_deref(), false, ); - WorkerSummary { + InternalWorkerSummary { worker: RuntimeWorkerRef::new(&self.runtime_id, worker_id.clone()), host_id: self.host_id.clone(), display_name: display.display_name.clone(), @@ -4038,7 +4047,7 @@ impl RemoteWorkerRuntime { profile, singleton_key: display.singleton_key, tags: display.tags, - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "remote_runtime".to_string(), identity: "runtime_registry_worker".to_string(), workspace_id: summary.workspace_id.clone(), @@ -4052,11 +4061,11 @@ impl RemoteWorkerRuntime { last_seen_at: None, pinned: false, retention_state: "transient".to_string(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "remote_worker_runtime".to_string(), display_hint: "Backend-proxied remote worker-runtime Worker".to_string(), }, - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: summary.execution_metadata_available && runtime_worker_can_stop(true, summary.status), can_spawn_followup: false, @@ -4066,7 +4075,7 @@ impl RemoteWorkerRuntime { } } - fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> WorkerSummary { + fn map_worker_detail(&self, detail: EmbeddedWorkerDetail) -> InternalWorkerSummary { let worker_id = detail.worker_id.to_string(); let profile = embedded_profile_label(&detail.profile); let display = worker_display_metadata( @@ -4075,7 +4084,7 @@ impl RemoteWorkerRuntime { detail.display_name.as_deref(), false, ); - WorkerSummary { + InternalWorkerSummary { worker: RuntimeWorkerRef::new(&self.runtime_id, worker_id.clone()), host_id: self.host_id.clone(), display_name: display.display_name.clone(), @@ -4083,7 +4092,7 @@ impl RemoteWorkerRuntime { profile, singleton_key: display.singleton_key, tags: display.tags, - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "remote_runtime".to_string(), identity: "runtime_registry_worker".to_string(), workspace_id: detail.workspace_id.clone(), @@ -4094,11 +4103,11 @@ impl RemoteWorkerRuntime { last_seen_at: None, pinned: false, retention_state: "transient".to_string(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "remote_worker_runtime".to_string(), display_hint: "Backend-proxied remote worker-runtime Worker".to_string(), }, - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: detail.execution_metadata_available && runtime_worker_can_stop(true, detail.status), can_spawn_followup: false, @@ -4114,11 +4123,11 @@ impl RemoteWorkerRuntime { response: worker_runtime::catalog::WorkerLifecycleAck, ) -> WorkerLifecycleResult { WorkerLifecycleResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), diagnostics: vec![diagnostic( "remote_runtime_lifecycle_accepted", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, format!( "Remote Runtime acknowledged lifecycle operation for '{worker_id}' with status {}", embedded_worker_status_label(response.status) @@ -4133,7 +4142,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { &self.runtime_id } - fn runtime_summary(&self, limit: usize) -> RuntimeSummary { + fn runtime_summary(&self, limit: usize) -> InternalRuntimeSummary { match self .run_runtime_api( self.request_timeout, @@ -4145,14 +4154,14 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { value.runtime, ) }) { - Ok(response) => RuntimeSummary { + Ok(response) => InternalRuntimeSummary { runtime_id: self.runtime_id.clone(), label: response .display_name .unwrap_or_else(|| self.display_name.clone()), kind: "remote_worker_runtime".to_string(), status: embedded_runtime_status_label(response.status).to_string(), - source: RuntimeSourceSummary::remote_http(), + source: InternalRuntimeSourceSummary::remote_http(), host_ids: if limit == 0 { Vec::new() } else { @@ -4163,12 +4172,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { arch: response.arch, diagnostics: Vec::new(), }, - Err(diagnostic) => RuntimeSummary { + Err(diagnostic) => InternalRuntimeSummary { runtime_id: self.runtime_id.clone(), label: self.display_name.clone(), kind: "remote_worker_runtime".to_string(), status: self.cached_status.clone(), - source: RuntimeSourceSummary::remote_http(), + source: InternalRuntimeSourceSummary::remote_http(), host_ids: if limit == 0 { Vec::new() } else { @@ -4259,12 +4268,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { ) } - fn list_hosts(&self, limit: usize) -> RuntimeList { + fn list_hosts(&self, limit: usize) -> RuntimeList { if limit == 0 { return RuntimeList::new(Vec::new(), Vec::new()); } RuntimeList::new( - vec![HostSummary { + vec![InternalHostSummary { runtime_id: self.runtime_id.clone(), host_id: self.host_id.clone(), label: self.display_name.clone(), @@ -4280,7 +4289,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { ) } - fn list_workers(&self, limit: usize) -> RuntimeList { + fn list_workers(&self, limit: usize) -> RuntimeList { if limit == 0 { return RuntimeList::new(Vec::new(), Vec::new()); } @@ -4311,7 +4320,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { } } - fn list_stopped_workers(&self, limit: usize) -> RuntimeList { + fn list_stopped_workers(&self, limit: usize) -> RuntimeList { if limit == 0 { return RuntimeList::new(Vec::new(), Vec::new()); } @@ -4370,7 +4379,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { } } - fn restore_worker(&self, worker_id: &str) -> WorkerRestoreResult { + fn restore_worker(&self, worker_id: &str) -> InternalWorkerRestoreResult { let worker_id_owned = worker_id.to_string(); match self .run_runtime_api( @@ -4392,11 +4401,11 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Ok((state, worker, reason_code, message)) => { let diagnostics = match (reason_code, message) { (Some(code), Some(message)) => { - vec![diagnostic(code, DiagnosticSeverity::Warning, message)] + vec![diagnostic(code, HostDiagnosticSeverity::Warning, message)] } _ => Vec::new(), }; - WorkerRestoreResult { + InternalWorkerRestoreResult { state: match state { runtime_api::WorkerRestoreState::Accepted => { server_api::WorkerRestoreState::Accepted @@ -4415,7 +4424,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { diagnostics, } } - Err(diagnostic) => WorkerRestoreResult { + Err(diagnostic) => InternalWorkerRestoreResult { // A transport/protocol failure cannot prove that the Runtime // rejected before side effects. Preserve uncertainty. state: server_api::WorkerRestoreState::ReconciliationRequired, @@ -4434,7 +4443,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Ok(workspace_api) => runtime_api::WorkerWorkspaceApiRequest { workspace_api }, Err(diagnostic) => { return WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![diagnostic], }; @@ -4455,12 +4464,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { runtime_contract_convert::<_, worker_runtime::catalog::WorkerDetail>(value.worker) }) { Ok(response) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: Some(self.map_worker_detail(response)), diagnostics: Vec::new(), }, Err(diagnostic) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![diagnostic], }, @@ -4476,7 +4485,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Ok(attachments) => attachments, Err(diagnostic) => { return WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![diagnostic], }; @@ -4500,12 +4509,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { runtime_contract_convert::<_, worker_runtime::catalog::WorkerDetail>(value.worker) }) { Ok(response) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: Some(self.map_worker_detail(response)), diagnostics: Vec::new(), }, Err(diagnostic) => WorkerWorkspaceApiResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, diagnostics: vec![diagnostic], }, @@ -4522,12 +4531,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Some(REMOTE_WORKING_DIRECTORY_CREATE_TIMEOUT), ) { Ok(response) => RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, working_directory: Some(response.working_directory), diagnostics: Vec::new(), }, Err(diagnostic) => RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![diagnostic], }, @@ -4582,12 +4591,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { "/v1/working-directories/{working_directory_id}" )) { Ok(response) => RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, working_directory: Some(response.working_directory), diagnostics: Vec::new(), }, Err(diagnostic) => RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![diagnostic], }, @@ -4620,12 +4629,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { "/v1/working-directories/{working_directory_id}" )) { Ok(response) => RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, working_directory: Some(response.working_directory), diagnostics: Vec::new(), }, Err(diagnostic) => RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![diagnostic], }, @@ -4642,12 +4651,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { WorkerSpawnAcceptanceRequirement::SocketReady ) { return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics: vec![diagnostic( "remote_runtime_no_socket_ready_acceptance", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Remote Runtime v0 exposes backend-proxied REST/WS control, not direct socket readiness".to_string(), )], }; @@ -4657,12 +4666,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Ok(archive) => archive, Err(error) => { return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics: vec![diagnostic( "remote_workspace_config_invalid", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error, )], }; @@ -4675,7 +4684,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Ok(workspace_api) => workspace_api, Err(diagnostic) => { return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics: vec![diagnostic], @@ -4694,7 +4703,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Ok(create) => create, Err(diagnostic) => { return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics: vec![diagnostic], @@ -4711,7 +4720,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { runtime_contract_convert::<_, worker_runtime::catalog::WorkerDetail>(value.worker) }) { Ok(response) => WorkerSpawnResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: Some(self.map_worker_detail(response)), acceptance_evidence: vec![WorkerSpawnAcceptanceEvidence { kind: "remote_runtime_worker_created".to_string(), @@ -4720,7 +4729,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { diagnostics: Vec::new(), }, Err(diagnostic) => WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics: vec![diagnostic], @@ -4747,12 +4756,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { &request, ) { Ok(response) => ConfigBundleSyncResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, availability: Some(response.availability), diagnostics: Vec::new(), }, Err(diagnostic) => ConfigBundleSyncResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, availability: None, diagnostics: vec![diagnostic], }, @@ -4763,12 +4772,12 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { let path = Self::bundle_availability_path(&reference); match self.get_json::(&path) { Ok(response) => ConfigBundleCheckResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, availability: Some(response.availability), diagnostics: Vec::new(), }, Err(diagnostic) => ConfigBundleCheckResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, availability: None, diagnostics: vec![diagnostic], }, @@ -4839,7 +4848,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { ) }) { Ok(response) => WorkerDeleteResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new( self.runtime_id.clone(), response.worker_id.to_string(), @@ -4848,7 +4857,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { diagnostics: Vec::new(), }, Err(diagnostic) => WorkerDeleteResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), deleted: false, diagnostics: vec![diagnostic], @@ -4940,7 +4949,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { Ok(input) => input, Err(diagnostic) => { return WorkerInputResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), diagnostics: vec![diagnostic], }; @@ -4953,7 +4962,7 @@ impl WorkspaceWorkerRuntime for RemoteWorkerRuntime { move |client| async move { client.send_worker_input(worker_id_owned, input).await }, ) { Ok(_) => WorkerInputResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(self.runtime_id.clone(), worker_id.to_string()), diagnostics: Vec::new(), }, @@ -5086,7 +5095,7 @@ fn execution_metadata_diagnostic(execution_metadata_available: bool) -> Option Vec { let mut diagnostics = vec![diagnostic( "embedded_runtime_projection", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "Worker identity is projected only as runtime_id plus worker_id; embedded runtime internals remain backend-private".to_string(), )]; diagnostics.extend(execution_metadata_diagnostic(execution_metadata_available)); @@ -5110,7 +5119,7 @@ fn remote_worker_projection_diagnostics( ) -> Vec { let mut diagnostics = vec![diagnostic( "remote_runtime_projection", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "Remote Worker identity is projected only as runtime_id plus worker_id; endpoint and credentials remain backend-private".to_string(), )]; diagnostics.extend(execution_metadata_diagnostic(execution_metadata_available)); @@ -5345,7 +5354,7 @@ fn embedded_input_rejected( diagnostic: RuntimeDiagnostic, ) -> WorkerInputResult { WorkerInputResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(runtime_id.to_string(), worker_id.to_string()), diagnostics: vec![diagnostic], } @@ -5357,7 +5366,7 @@ fn remote_input_rejected( diagnostic: RuntimeDiagnostic, ) -> WorkerInputResult { WorkerInputResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(runtime_id.to_string(), worker_id.to_string()), diagnostics: vec![diagnostic], } @@ -5369,7 +5378,7 @@ fn embedded_lifecycle_rejected( diagnostic: RuntimeDiagnostic, ) -> WorkerLifecycleResult { WorkerLifecycleResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(runtime_id.to_string(), worker_id.to_string()), diagnostics: vec![diagnostic], } @@ -5381,7 +5390,7 @@ fn remote_lifecycle_rejected( diagnostic: RuntimeDiagnostic, ) -> WorkerLifecycleResult { WorkerLifecycleResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: RuntimeWorkerRef::new(runtime_id.to_string(), worker_id.to_string()), diagnostics: vec![diagnostic], } @@ -5390,7 +5399,7 @@ fn remote_lifecycle_rejected( fn embedded_workdir_unsupported_diagnostic() -> RuntimeDiagnostic { diagnostic( "embedded_worker_workdir_unsupported", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Embedded Runtime is no-workdir only; choose a non-embedded Runtime for workspace-file Workers".to_string(), ) } @@ -5452,18 +5461,18 @@ fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnosti match error { EmbeddedRuntimeError::RuntimeStopped => diagnostic( "embedded_runtime_stopped", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Embedded Runtime is stopped".to_string(), ), EmbeddedRuntimeError::WorkerNotFound { .. } => diagnostic( "embedded_worker_not_found", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Embedded Runtime worker was not found".to_string(), ), EmbeddedRuntimeError::WorkerExecutionUnavailable { .. } | EmbeddedRuntimeError::ExecutionBackendUnavailable { .. } => diagnostic( "embedded_worker_execution_unavailable", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Embedded Worker has no execution backend attached".to_string(), ), EmbeddedRuntimeError::WorkerExecutionRejected { @@ -5473,22 +5482,22 @@ fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnosti .. } => diagnostic( "embedded_worker_execution_rejected", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, sanitize_embedded_execution_message(message, operation, outcome), ), EmbeddedRuntimeError::LimitTooLarge { requested, max } => diagnostic( "embedded_runtime_limit_too_large", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, format!("Requested limit {requested} exceeds embedded Runtime maximum {max}"), ), EmbeddedRuntimeError::InvalidInitialInputKind { .. } => diagnostic( "embedded_worker_initial_input_kind_invalid", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, error.to_string(), ), EmbeddedRuntimeError::WorkingDirectory(workdir_diagnostic) => diagnostic( workdir_diagnostic.code.clone(), - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, workdir_diagnostic.message.clone(), ), EmbeddedRuntimeError::InvalidRequest(_) @@ -5498,17 +5507,17 @@ fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnosti | EmbeddedRuntimeError::InvalidProfileSelector { .. } | EmbeddedRuntimeError::UnsupportedConfigDeclaration { .. } => diagnostic( "embedded_runtime_invalid_request", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Embedded Runtime rejected the request".to_string(), ), EmbeddedRuntimeError::RuntimeStoreAlreadyOpen { .. } => diagnostic( "embedded_runtime_store_already_open", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Embedded Runtime store is already owned by another Runtime process".to_string(), ), EmbeddedRuntimeError::WorkerDeletePersistenceFailed { .. } => diagnostic( "worker_delete_persistence_failed", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Worker metadata deletion failed; the persisted Worker identity was retained for retry" .to_string(), ), @@ -5516,12 +5525,12 @@ fn embedded_runtime_diagnostic(error: &EmbeddedRuntimeError) -> RuntimeDiagnosti | EmbeddedRuntimeError::StoreMissing { .. } | EmbeddedRuntimeError::StoreCorrupt { .. } => diagnostic( "embedded_runtime_store_error", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Embedded Runtime storage operation failed; internal paths are not exposed".to_string(), ), EmbeddedRuntimeError::StatePoisoned => diagnostic( "embedded_runtime_state_unavailable", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Embedded Runtime state is unavailable".to_string(), ), } @@ -5563,19 +5572,19 @@ fn remote_reqwest_diagnostic(runtime_id: &str, err: reqwest::Error) -> RuntimeDi if err.is_timeout() { diagnostic( "remote_runtime_timeout", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!("Timed out while contacting remote Runtime '{runtime_id}'"), ) } else if err.is_connect() || err.is_request() { diagnostic( "remote_runtime_network_error", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!("Failed to contact remote Runtime '{runtime_id}'"), ) } else { diagnostic( "remote_runtime_client_error", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!("Remote Runtime client error for '{runtime_id}'"), ) } @@ -5591,7 +5600,7 @@ where .map_err(|error| { diagnostic( "remote_runtime_contract_conversion_failed", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, error.to_string(), ) }) @@ -5608,14 +5617,14 @@ fn runtime_api_diagnostic( let (code, severity) = match status { StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => ( "remote_runtime_auth_failed".to_string(), - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, ), _ => ( remote_code, if status.is_server_error() { - DiagnosticSeverity::Error + HostDiagnosticSeverity::Error } else { - DiagnosticSeverity::Warning + HostDiagnosticSeverity::Warning }, ), }; @@ -5652,7 +5661,7 @@ fn runtime_api_diagnostic( }; diagnostic( code, - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!("Remote Runtime client error for '{runtime_id}': {failure}"), ) } @@ -5689,15 +5698,18 @@ fn remote_http_status_diagnostic( .unwrap_or_else(|| "remote Runtime did not return a typed error body".to_string()); let (code, severity) = match status { StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => { - ("remote_runtime_auth_failed", DiagnosticSeverity::Error) + ("remote_runtime_auth_failed", HostDiagnosticSeverity::Error) } - _ if error.is_some() => (remote_code, DiagnosticSeverity::Warning), - StatusCode::NOT_FOUND => ("remote_runtime_not_found", DiagnosticSeverity::Warning), - StatusCode::METHOD_NOT_ALLOWED | StatusCode::NOT_IMPLEMENTED => { - ("remote_runtime_unsupported", DiagnosticSeverity::Warning) + _ if error.is_some() => (remote_code, HostDiagnosticSeverity::Warning), + StatusCode::NOT_FOUND => ("remote_runtime_not_found", HostDiagnosticSeverity::Warning), + StatusCode::METHOD_NOT_ALLOWED | StatusCode::NOT_IMPLEMENTED => ( + "remote_runtime_unsupported", + HostDiagnosticSeverity::Warning, + ), + _ if status.is_server_error() => { + ("remote_runtime_http_error", HostDiagnosticSeverity::Error) } - _ if status.is_server_error() => ("remote_runtime_http_error", DiagnosticSeverity::Error), - _ => (remote_code, DiagnosticSeverity::Warning), + _ => (remote_code, HostDiagnosticSeverity::Warning), }; diagnostic( code, @@ -5708,7 +5720,7 @@ fn remote_http_status_diagnostic( fn diagnostic( code: impl Into, - severity: DiagnosticSeverity, + severity: HostDiagnosticSeverity, message: impl Into, ) -> RuntimeDiagnostic { RuntimeDiagnostic { @@ -5725,7 +5737,7 @@ fn operation_failed_or_unknown_worker( ) -> RuntimeRegistryError { diagnostics .into_iter() - .find(|diagnostic| matches!(diagnostic.severity, DiagnosticSeverity::Error)) + .find(|diagnostic| matches!(diagnostic.severity, HostDiagnosticSeverity::Error)) .map(|diagnostic| RuntimeRegistryError::RuntimeOperationFailed { runtime_id: runtime_id.to_string(), code: diagnostic.code, @@ -5836,9 +5848,9 @@ fn worker_spawn_intent_label(intent: &WorkerSpawnIntent) -> &'static str { } } -pub fn placeholder_worker(host_id: impl Into) -> WorkerSummary { +pub fn placeholder_worker(host_id: impl Into) -> InternalWorkerSummary { let host_id = host_id.into(); - WorkerSummary { + InternalWorkerSummary { worker: RuntimeWorkerRef::new("placeholder", "worker-placeholder"), host_id, display_name: "Worker runtime actions are not implemented".to_string(), @@ -5846,7 +5858,7 @@ pub fn placeholder_worker(host_id: impl Into) -> WorkerSummary { profile: None, singleton_key: None, tags: Vec::new(), - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "none".to_string(), identity: "unsupported".to_string(), workspace_id: None, @@ -5856,18 +5868,18 @@ pub fn placeholder_worker(host_id: impl Into) -> WorkerSummary { last_seen_at: None, pinned: false, retention_state: "transient".to_string(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "placeholder".to_string(), display_hint: "unsupported".to_string(), }, - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: false, can_spawn_followup: false, }, workdir_attachments: Vec::new(), diagnostics: vec![diagnostic( "runtime_capability_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "worker control is outside this overview-only registry surface".to_string(), )], } @@ -5875,12 +5887,12 @@ pub fn placeholder_worker(host_id: impl Into) -> WorkerSummary { pub fn placeholder_spawn_response(host_id: impl Into) -> WorkerSpawnResult { WorkerSpawnResult { - state: WorkerOperationState::Unsupported, + state: InternalWorkerOperationState::Unsupported, worker: Some(placeholder_worker(host_id)), acceptance_evidence: Vec::new(), diagnostics: vec![diagnostic( "worker_spawn_unsupported", - DiagnosticSeverity::Info, + HostDiagnosticSeverity::Info, "Workspace worker runtime control is not implemented yet".to_string(), )], } @@ -5937,7 +5949,7 @@ mod tests { }); assert_eq!(diagnostic.code, "worker_delete_persistence_failed"); - assert_eq!(diagnostic.severity, DiagnosticSeverity::Error); + assert_eq!(diagnostic.severity, HostDiagnosticSeverity::Error); assert!(diagnostic.message.len() <= 256); assert!(!diagnostic.message.contains("/private/runtime")); } @@ -6055,7 +6067,7 @@ mod tests { assert!(value.get("worker").is_none()); let lifecycle = WorkerLifecycleResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new("arcadia", "30"), diagnostics: Vec::new(), }; @@ -6471,7 +6483,7 @@ mod tests { struct FixtureRuntime { runtime_id: String, host_id: String, - workers: Vec, + workers: Vec, observed_prompt_revisions: Arc>>, } @@ -6480,7 +6492,7 @@ mod tests { Self { runtime_id: runtime_id.to_string(), host_id: host_id.to_string(), - workers: vec![WorkerSummary { + workers: vec![InternalWorkerSummary { worker: RuntimeWorkerRef::new(runtime_id, worker_id), host_id: host_id.to_string(), display_name: label.to_string(), @@ -6488,7 +6500,7 @@ mod tests { profile: None, singleton_key: None, tags: Vec::new(), - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "opaque".to_string(), identity: host_id.to_string(), workspace_id: None, @@ -6498,11 +6510,11 @@ mod tests { last_seen_at: None, pinned: false, retention_state: "transient".to_string(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "fixture".to_string(), display_hint: "test fixture".to_string(), }, - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: false, can_spawn_followup: false, }, @@ -6530,13 +6542,13 @@ mod tests { Ok(()) } - fn runtime_summary(&self, _limit: usize) -> RuntimeSummary { - RuntimeSummary { + fn runtime_summary(&self, _limit: usize) -> InternalRuntimeSummary { + InternalRuntimeSummary { runtime_id: self.runtime_id.clone(), label: self.runtime_id.clone(), kind: "fixture".to_string(), status: "available".to_string(), - source: RuntimeSourceSummary::embedded_worker_runtime_reserved(), + source: InternalRuntimeSourceSummary::embedded_worker_runtime_reserved(), host_ids: vec![self.host_id.clone()], worker_creation_available: false, os: "test".to_string(), @@ -6545,9 +6557,9 @@ mod tests { } } - fn list_hosts(&self, _limit: usize) -> RuntimeList { + fn list_hosts(&self, _limit: usize) -> RuntimeList { RuntimeList::new( - vec![HostSummary { + vec![InternalHostSummary { runtime_id: self.runtime_id.clone(), host_id: self.host_id.clone(), label: "fixture host".to_string(), @@ -6563,7 +6575,7 @@ mod tests { ) } - fn list_workers(&self, limit: usize) -> RuntimeList { + fn list_workers(&self, limit: usize) -> RuntimeList { RuntimeList::new( self.workers.iter().take(limit).cloned().collect(), Vec::new(), @@ -6893,7 +6905,7 @@ mod tests { let result = registry .spawn_worker("embedded-worker-runtime", binding.clone(), request) .expect("spawn request"); - assert_eq!(result.state, WorkerOperationState::Accepted); + assert_eq!(result.state, InternalWorkerOperationState::Accepted); assert_eq!( result.worker.as_ref().unwrap().worker.worker_id, binding.worker_id.to_string() @@ -6901,7 +6913,7 @@ mod tests { let check = registry .check_config_bundle("embedded-worker-runtime", bundle_ref) .expect("bundle check"); - assert_eq!(check.state, WorkerOperationState::Accepted); + assert_eq!(check.state, InternalWorkerOperationState::Accepted); assert!(check.availability.is_some()); } @@ -6917,7 +6929,7 @@ mod tests { let spawned = runtime.spawn_worker(test_create_binding(), request); - assert_eq!(spawned.state, WorkerOperationState::Rejected); + assert_eq!(spawned.state, InternalWorkerOperationState::Rejected); assert!( spawned .diagnostics @@ -6934,7 +6946,7 @@ mod tests { ) .expect("test backend should connect"); let spawned = runtime.spawn_worker(test_create_binding(), embedded_spawn_request()); - assert_eq!(spawned.state, WorkerOperationState::Rejected); + assert_eq!(spawned.state, InternalWorkerOperationState::Rejected); assert!(spawned.acceptance_evidence.is_empty()); assert!(spawned.diagnostics.iter().any(|diagnostic| { diagnostic.code == "embedded_worker_execution_rejected" @@ -7001,7 +7013,7 @@ mod tests { ) .expect("test backend should connect"); let spawned = runtime.spawn_worker(test_create_binding(), embedded_spawn_request()); - assert_eq!(spawned.state, WorkerOperationState::Accepted); + assert_eq!(spawned.state, InternalWorkerOperationState::Accepted); let worker = spawned.worker.expect("created embedded worker"); assert!(worker.capabilities.can_stop); @@ -7013,7 +7025,7 @@ mod tests { segments: None, }, ); - assert_eq!(input.state, WorkerOperationState::Accepted); + assert_eq!(input.state, InternalWorkerOperationState::Accepted); let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); loop { @@ -7050,9 +7062,12 @@ mod tests { .expect("embedded runtime summary"); assert_eq!( embedded_summary.source.kind, - RuntimeSourceKind::EmbeddedWorkerRuntime + InternalRuntimeSourceKind::EmbeddedWorkerRuntime + ); + assert_eq!( + embedded_summary.source.status, + InternalRuntimeSourceStatus::Active ); - assert_eq!(embedded_summary.source.status, RuntimeSourceStatus::Active); assert!(embedded_summary.worker_creation_available); let spawned = registry @@ -7083,7 +7098,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(spawned.state, WorkerOperationState::Accepted); + assert_eq!(spawned.state, InternalWorkerOperationState::Accepted); assert!( spawned .acceptance_evidence @@ -7106,7 +7121,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(input.state, WorkerOperationState::Accepted); + assert_eq!(input.state, InternalWorkerOperationState::Accepted); assert_eq!(input.worker.runtime_id, EMBEDDED_RUNTIME_ID); assert_eq!(input.worker.worker_id, worker.worker.worker_id); @@ -7145,7 +7160,7 @@ mod tests { let sync = registry .sync_config_bundle(EMBEDDED_RUNTIME_ID, bundle.clone()) .unwrap(); - assert_eq!(sync.state, WorkerOperationState::Accepted); + assert_eq!(sync.state, InternalWorkerOperationState::Accepted); let reference = sync.availability.expect("bundle availability").reference; assert_eq!(reference.id, bundle.metadata.id); assert_eq!(reference.digest, bundle.metadata.digest); @@ -7153,7 +7168,7 @@ mod tests { let check = registry .check_config_bundle(EMBEDDED_RUNTIME_ID, reference.clone()) .unwrap(); - assert_eq!(check.state, WorkerOperationState::Accepted); + assert_eq!(check.state, InternalWorkerOperationState::Accepted); let spawned = registry .spawn_worker( @@ -7183,7 +7198,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(spawned.state, WorkerOperationState::Accepted); + assert_eq!(spawned.state, InternalWorkerOperationState::Accepted); assert_eq!( spawned.worker.unwrap().profile.as_deref(), Some("builtin:coder") @@ -7222,7 +7237,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(result.state, WorkerOperationState::Rejected); + assert_eq!(result.state, InternalWorkerOperationState::Rejected); assert!(result.worker.is_none()); assert!( result @@ -7378,7 +7393,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(input.state, WorkerOperationState::Accepted); + assert_eq!(input.state, InternalWorkerOperationState::Accepted); server.join().expect("mock remote server finished"); let browser_payload = serde_json::to_string(&(workers, input)).unwrap(); @@ -7528,7 +7543,7 @@ mod tests { let sync = registry .sync_config_bundle("remote:primary", test_config_bundle()) .unwrap(); - assert_eq!(sync.state, WorkerOperationState::Rejected); + assert_eq!(sync.state, InternalWorkerOperationState::Rejected); let sync_payload = serde_json::to_string(&sync).unwrap(); assert!(!sync_payload.contains(leaked_store_path), "{sync_payload}"); @@ -7541,7 +7556,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(check.state, WorkerOperationState::Rejected); + assert_eq!(check.state, InternalWorkerOperationState::Rejected); let check_payload = serde_json::to_string(&check).unwrap(); assert!( !check_payload.contains(leaked_session_path), diff --git a/crates/workspace-server/src/lib.rs b/crates/workspace-server/src/lib.rs index 618947432..cda247556 100644 --- a/crates/workspace-server/src/lib.rs +++ b/crates/workspace-server/src/lib.rs @@ -36,7 +36,7 @@ pub mod workspace_signing_identity; mod workspace_subscription; pub use authority::{ - MemoryAuthority, MemoryDocument, MemoryStagingEntry, MemoryStagingResolution, + InternalMemoryStagingEntry, MemoryAuthority, MemoryDocument, MemoryStagingResolution, ObjectiveAuthority, SqliteWorkspaceAuthority, TicketAuthority, TicketMergeRevisionSource, WorkspaceAuthority, }; diff --git a/crates/workspace-server/src/records.rs b/crates/workspace-server/src/records.rs index bc6b44d64..d062b4301 100644 --- a/crates/workspace-server/src/records.rs +++ b/crates/workspace-server/src/records.rs @@ -1,19 +1,29 @@ -use serde::{Deserialize, Serialize}; pub use server_api::{ - ObjectiveDetail, ObjectiveEventDetail, ObjectiveLinkedTicketSummary, ObjectiveResourceSummary, - ObjectiveSummary, QueryPage, + InvalidProjectRecord, MergeRequestListItem, MergeRequestListResponse, + MergeRequestRefDiagnostic, ObjectiveDetail, ObjectiveEventDetail, ObjectiveLinkSummary, + ObjectiveLinkedTicketSummary, ObjectiveQueryItem, ObjectiveQueryRequest, + ObjectiveQueryResponse, ObjectiveResourceSummary, ObjectiveShowRequest, ObjectiveSummary, + QueryPage, TicketActionEligibility, + TicketAssignmentPrincipal as TicketAssignmentPrincipalSummary, TicketAssignmentSummary, + TicketDetail, TicketDetailDerivedRelation as DerivedTicketRelation, + TicketDetailRelation as TicketRelation, TicketDetailRelationBlocker as TicketRelationBlocker, + TicketDetailRelationNotice as TicketRelationNotice, + TicketDetailRelationView as TicketRelationView, TicketEventDetail, TicketEvidenceEvent, + TicketEvidenceSummary, TicketListItemSummary as TicketSummary, TicketListResponse, + TicketMergeRequestSummary, TicketQueryItem, TicketQueryRequest, TicketQueryResponse, + TicketRoleAssignmentSummary, TicketShowRequest, }; const SUMMARY_BODY_LIMIT: usize = 240; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ProjectRecordList { pub items: Vec, pub invalid_records: Vec, pub record_authority: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct TicketSummaryPage { pub items: Vec, pub page: QueryPage, @@ -21,418 +31,67 @@ pub struct TicketSummaryPage { pub record_authority: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct InvalidProjectRecord { - pub label: String, - pub reason: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketSummary { - pub id: String, - pub resource_key: String, - pub title: String, - pub state: String, - pub priority: String, - pub updated_at: Option, - pub queued_by: Option, - pub queued_at: Option, - pub workspace_action_priority: String, - pub record_source: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct TicketListPageRequest { - #[serde(default)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct TicketListProjectionRequest { pub states: Vec, - #[serde(default)] pub limit: Option, - #[serde(default)] pub cursor: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketListResponse { - pub workspace_id: String, - pub limit: usize, - pub items: Vec, - pub page: QueryPage, - pub invalid_records: Vec, - pub record_authority: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketDetail { - pub id: String, - pub resource_key: String, - pub title: String, - pub state: String, - pub readiness: Option, - pub priority: String, - pub created_at: Option, - pub updated_at: Option, - pub item_revision: String, - pub queued_by: Option, - pub queued_at: Option, - pub repository_key: Option, - pub ref_selector: Option, - pub risk_flags: Vec, - pub body: String, - pub body_truncated: bool, - pub event_count: usize, - pub events: Vec, - pub event_page: QueryPage, - pub artifact_count: usize, - pub artifacts: Vec, - pub relations: TicketRelationView, - pub linked_objectives: Vec, - pub implementation_reports: Vec, - pub assignments: Vec, - pub current_coder: Option, - pub assignment_diagnostics: Vec, - pub action_eligibility: TicketActionEligibility, - pub merge_request: Option, - pub evidence: TicketEvidenceSummary, - pub resolution: Option, - pub record_source: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketEventDetail { - pub sequence: usize, - pub event_ref: String, - pub kind: String, - pub author: Option, - pub at: Option, - pub status: Option, - pub from: Option, - pub to: Option, - pub reason: Option, - pub state_field: Option, - pub heading: Option, - pub body: Option, - pub attributes: std::collections::BTreeMap, - pub references: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketRelation { - pub ticket_id: String, - pub kind: String, - pub target: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub target_resource_key: Option, - pub note: Option, - pub author: String, - pub at: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct DerivedTicketRelation { - pub source_ticket: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub source_resource_key: Option, - pub inverse_kind: String, - pub forward_kind: String, - pub note: Option, - pub author: String, - pub at: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketRelationBlocker { - pub blocking_ticket: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub blocking_resource_key: Option, - pub reason_kind: String, - pub relation_kind: String, - pub note: Option, - pub blocking_state: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketRelationNotice { - pub related_ticket: String, - pub kind: String, - pub message: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketRelationView { - pub outgoing: Vec, - pub incoming: Vec, - pub blockers: Vec, - pub notices: Vec, -} - -impl From for TicketRelationView { - fn from(value: ticket::TicketRelationView) -> Self { - Self { - outgoing: value - .outgoing - .into_iter() - .map(|relation| TicketRelation { - ticket_id: relation.ticket_id, - kind: relation.kind.as_str().to_string(), - target: relation.target, - target_resource_key: None, - note: relation.note, - author: relation.author, - at: relation.at, - }) - .collect(), - incoming: value - .incoming - .into_iter() - .map(|relation| DerivedTicketRelation { - source_ticket: relation.source_ticket, - source_resource_key: None, - inverse_kind: relation.inverse_kind, - forward_kind: relation.forward_kind.as_str().to_string(), - note: relation.note, - author: relation.author, - at: relation.at, - }) - .collect(), - blockers: value - .blockers - .into_iter() - .map(|blocker| TicketRelationBlocker { - blocking_ticket: blocker.blocking_ticket, - blocking_resource_key: None, - reason_kind: blocker.reason_kind, - relation_kind: blocker.relation_kind.as_str().to_string(), - note: blocker.note, - blocking_state: blocker.blocking_state.as_str().to_string(), - }) - .collect(), - notices: value - .notices - .into_iter() - .map(|notice| TicketRelationNotice { - related_ticket: notice.related_ticket, - kind: notice.kind.as_str().to_string(), - message: notice.message, - }) - .collect(), - } +pub(crate) fn ticket_relation_view_from_domain( + value: ticket::TicketRelationView, +) -> TicketRelationView { + TicketRelationView { + outgoing: value + .outgoing + .into_iter() + .map(|relation| TicketRelation { + ticket_id: relation.ticket_id, + kind: relation.kind.as_str().to_string(), + target: relation.target, + target_resource_key: None, + note: relation.note, + author: relation.author, + at: relation.at, + }) + .collect(), + incoming: value + .incoming + .into_iter() + .map(|relation| DerivedTicketRelation { + source_ticket: relation.source_ticket, + source_resource_key: None, + inverse_kind: relation.inverse_kind, + forward_kind: relation.forward_kind.as_str().to_string(), + note: relation.note, + author: relation.author, + at: relation.at, + }) + .collect(), + blockers: value + .blockers + .into_iter() + .map(|blocker| TicketRelationBlocker { + blocking_ticket: blocker.blocking_ticket, + blocking_resource_key: None, + reason_kind: blocker.reason_kind, + relation_kind: blocker.relation_kind.as_str().to_string(), + note: blocker.note, + blocking_state: blocker.blocking_state.as_str().to_string(), + }) + .collect(), + notices: value + .notices + .into_iter() + .map(|notice| TicketRelationNotice { + related_ticket: notice.related_ticket, + kind: notice.kind.as_str().to_string(), + message: notice.message, + }) + .collect(), } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct ObjectiveLinkSummary { - pub id: String, - pub resource_key: String, - pub title: String, - pub state: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketEvidenceEvent { - pub event_ref: String, - pub sequence: usize, - pub kind: String, - pub at: Option, - pub author: Option, - pub excerpt: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketAssignmentSummary { - pub assignment_id: String, - pub runtime_id: String, - pub worker_id: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub worker_resource_key: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketRoleAssignmentSummary { - pub assignment_id: String, - pub role: String, - pub principal: TicketAssignmentPrincipalSummary, - pub assigned_by: String, - pub assigned_at: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum TicketAssignmentPrincipalSummary { - User { - account_id: String, - }, - Worker { - runtime_id: String, - worker_id: String, - }, - WorkspaceAgent { - agent_key: String, - }, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketActionEligibility { - pub can_assign_orchestrator: bool, - pub can_unassign_orchestrator: bool, - pub can_queue: bool, - pub can_start_manual_coder: bool, - pub queue_tickets: Vec, - pub blockers: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketMergeRequestSummary { - pub merge_request_id: String, - pub repository_key: String, - pub state: String, - pub review_status: String, - pub selector_from: Option, - pub selector_to: String, - pub updated_at: String, - pub current_subject_ref: Option, - pub review_subject_ref: Option, - pub review_requested_at: Option, - pub review_submitted_at: Option, - pub review_excerpt: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct MergeRequestRefDiagnostic { - pub code: String, - pub message: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct MergeRequestListItem { - pub summary: TicketMergeRequestSummary, - pub ticket_ids: Vec, - pub thread_event_count: usize, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub ref_diagnostics: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct MergeRequestListResponse { - pub items: Vec, - pub next_cursor: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct TicketEvidenceSummary { - pub has_merge_request: bool, - pub has_current_subject_ref: bool, - pub has_review_request: bool, - pub has_commit: bool, - pub review_status: Option, - pub approved_current_subject: bool, - pub review_after_rescope: bool, - pub unresolved_request_changes: bool, - pub complete_for_integration: bool, - pub missing: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct TicketQueryRequest { - pub query: Option, - #[serde(default)] - pub states: Vec, - #[serde(default)] - pub event_kinds: Vec, - #[serde(default)] - pub evidence: Vec, - pub review_status: Option, - #[serde(default)] - pub attention: Vec, - pub related_ticket_id: Option, - pub relation_kind: Option, - pub linked_objective_id: Option, - pub updated_after: Option, - pub updated_before: Option, - pub sort: Option, - pub limit: Option, - pub cursor: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketQueryItem { - pub id: String, - pub resource_key: String, - pub title: String, - pub state: String, - pub readiness: Option, - pub priority: String, - pub created_at: Option, - pub updated_at: Option, - pub item_revision: String, - pub workspace_action_priority: String, - pub matched_fields: Vec, - pub snippet: Option, - pub matching_event: Option, - pub linked_objective_ids: Vec, - pub linked_objective_keys: Vec, - pub relation_count: usize, - pub blocker_count: usize, - pub unresolved_blocker_count: usize, - pub unresolved_review_count: usize, - pub evidence: TicketEvidenceSummary, - pub merge_request: Option, - pub current_coder: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct TicketQueryResponse { - pub items: Vec, - pub page: QueryPage, - pub record_authority: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct TicketShowRequest { - pub event_limit: Option, - pub event_cursor: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct ObjectiveQueryRequest { - pub query: Option, - #[serde(default)] - pub states: Vec, - pub linked_ticket_id: Option, - pub updated_after: Option, - pub updated_before: Option, - pub sort: Option, - pub limit: Option, - pub cursor: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct ObjectiveQueryItem { - pub id: String, - pub resource_key: String, - pub title: String, - pub state: String, - pub created_at: Option, - pub updated_at: Option, - pub matched_fields: Vec, - pub snippet: Option, - pub linked_ticket_count: usize, - pub linked_tickets: Vec, - pub linked_ticket_keys: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct ObjectiveQueryResponse { - pub items: Vec, - pub page: QueryPage, - pub record_authority: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)] -pub struct ObjectiveShowRequest { - pub event_limit: Option, - pub event_cursor: Option, -} - pub(crate) fn summarize_body(body: &str) -> String { let summary = body .lines() diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index 64213ef4b..fc9839c18 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -26,19 +26,20 @@ use memory::backend::{ }; use protocol::Segment; use protocol::stream::{decode_method, encode_event}; -use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use server_api::{ ActorAuthMethod, AuthBootstrapUserRequest, AuthPublicConfig, AuthUserResponse, AuthenticatedUser, BrowserCreateWorkerResponse, BrowserWorkspaceOrchestratorResponse, - ConfirmRepositorySshHostTrustRequest, CreateRemoteRuntimeRequest, - CreateRepositorySshCredentialRequest, CreateWorkspaceRepositoryRequest, - CreateWorkspaceRepositoryResponse, CreateWorkspaceWorkerRequest, - CreateWorkspaceWorkerTicketAssignmentRequest, DeleteRepositorySshCredentialRequest, - DeleteRepositorySshHostTrustRequest, DeviceAccessTokenType, DeviceLoginApprovalStatus, - DeviceLoginApproveRequest, DeviceLoginApproveResponse, DeviceLoginPollRequest, - DeviceLoginPollResponse, DeviceLoginPollStatus, DeviceLoginStartRequest, - DeviceLoginStartResponse, ExternalWorkdirGrantCreateRequest, ExternalWorkdirGrantResponse, + CleanupTargetKind, CleanupWorkdirCandidate, CleanupWorkdirCleanliness, + CleanupWorkdirFileStatus, CleanupWorkerCandidate, ConfirmRepositorySshHostTrustRequest, + CreateRemoteRuntimeRequest, CreateRepositorySshCredentialRequest, + CreateWorkspaceRepositoryRequest, CreateWorkspaceRepositoryResponse, + CreateWorkspaceWorkerRequest, CreateWorkspaceWorkerTicketAssignmentRequest, + DeleteRepositorySshCredentialRequest, DeleteRepositorySshHostTrustRequest, + DeviceAccessTokenType, DeviceLoginApprovalStatus, DeviceLoginApproveRequest, + DeviceLoginApproveResponse, DeviceLoginPollRequest, DeviceLoginPollResponse, + DeviceLoginPollStatus, DeviceLoginStartRequest, DeviceLoginStartResponse, + ExecuteRuntimeCleanupRequest, ExternalWorkdirGrantCreateRequest, ExternalWorkdirGrantResponse, GenerateRepositorySshCredentialRequest, LogoutResponse, LogoutStatus, MemoryDocumentResponse, MemoryStagingListResponse, ObjectiveCreateRequest, ObjectiveEditRequest, ObjectiveLinkTicketRequest, ObjectiveStateRequest, PasskeyLoginCompleteRequest, @@ -49,14 +50,15 @@ use server_api::{ RepositorySshConnectionProbeRequest, RepositorySshConnectionProbeResponse, RepositorySshConnectionTrustState, RepositorySshCredential, RepositorySshHostKeyCandidate, RepositorySshHostTrust, RepositorySshPublicKey, RequestActor, - RotateRepositorySshCredentialRequest, RuntimeConnectionDisplayState, + RotateRepositorySshCredentialRequest, RuntimeCleanupExecutionResponse, + RuntimeCleanupExecutionResult, RuntimeCleanupPlanResponse, RuntimeConnectionDisplayState, RuntimeConnectionTestFailureKind, RuntimeConnectionTestResponse, RuntimeConnectionTestStatus, RuntimeManagementSummary, RuntimeRemovalOperationResponse, RuntimeRemovalOperationState as ApiRuntimeRemovalOperationState, RuntimeTrustAuditAction, RuntimeTrustAuditEntry, RuntimeTrustKeyRevealResponse, RuntimeTrustKeyState, RuntimeTrustKeyStatus, UpdateRemoteRuntimeRequest, UpdateWorkspaceMetadataRequest, WhoamiResponse, WorkerLaunchOptionsResponse, WorkerLaunchProfileCandidate, - WorkerLaunchRuntimeOption, WorkerLaunchWorkerSummary, + WorkerLaunchRuntimeOption, WorkerLaunchWorkerSummary, WorkerRetentionResponse, WorkingDirectoryCreateRequest as BrowserWorkingDirectoryCreateRequest, WorkingDirectoryCreateResponse as BrowserWorkingDirectoryCreateResponse, WorkingDirectoryDetailResponse as BrowserWorkingDirectoryDetailResponse, @@ -74,6 +76,11 @@ use server_api::{ WorkspaceSigningIdentityState, WorkspaceSummary, WorkspaceWorkerDiscoveryItem, WorkspaceWorkerDiscoveryPage, WorkspaceWorkerSubject, }; +use server_api::{ + AttachmentUploadCancelResponse, AttachmentUploadGrantResponse, RestoreTicketAssignmentQuery, + RuntimeWorkersQuery, RuntimeWorkersStatusFilter, WorkerFileDeleteResponse, + WorkerFileUploadQuery, WorkerFileUploadResponse, +}; #[cfg(test)] use server_api::{TICKET_ORCHESTRATION_PLANS_QUERY_PATH, TICKET_RELATIONS_QUERY_PATH}; use sha2::{Digest, Sha256}; @@ -110,7 +117,6 @@ use workdir::workspace::{ WorkspaceWorkdirSessionOperationRequest, }; use workdir::{CommandHandle, WorkdirSessionHandle}; -use worker::feature::builtin::{WorkerObservationSubject, WorkerObservationSubjectRef}; use worker_runtime::http_server::{ RUNTIME_HTTP_PROTOCOL_MAX_VERSION, RUNTIME_HTTP_PROTOCOL_MIN_VERSION, RUNTIME_HTTP_PROTOCOL_VERSION, @@ -141,17 +147,18 @@ use crate::companion::{ }; use crate::config_source::ConfigCommitRequest; use crate::hosts::{ - ConfigBundleCheckResult, ConfigBundleSyncResult, DiagnosticSeverity, EMBEDDED_RUNTIME_ID, - EmbeddedWorkerRuntime, HostSummary, RemoteRuntimeConfig, RemoteWorkerRuntime, - RuntimeDiagnostic, RuntimePingFailureKind, RuntimeRegistry, RuntimeRegistryError, - RuntimeRegistryUnregisterResult, TicketWorkerRole, WorkerCapabilitySummary, - WorkerCompletionsRequest, WorkerCompletionsResult, WorkerControlOperation, WorkerCreateBinding, - WorkerImplementationSummary, WorkerInputKind, WorkerInputRequest, WorkerInputResult, - WorkerLifecycleRequest, WorkerLifecycleResult, WorkerOperationState, WorkerRestoreResult, - WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, WorkerSpawnRequest, WorkerSpawnResult, - WorkerSpawnWorkingDirectoryRequest, WorkerSummary, WorkerTicketAssignmentRequest, - WorkerWorkspaceSummary, WorkspaceRuntimeAuthorization, is_disallowed_remote_runtime_address, - is_loopback_runtime_origin, worker_spawn_create_fingerprint, workspace_worker_summary, + 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, }; use crate::memory_backend::execute_memory_backend_operation_with_authority; use crate::memory_staging::{ @@ -1269,7 +1276,7 @@ impl WorkerRemovalService { let stop = runtime.stop_worker(worker_ref, lifecycle_request); let stop_accepted = stop .as_ref() - .is_ok_and(|result| result.state == WorkerOperationState::Accepted); + .is_ok_and(|result| result.state == InternalWorkerOperationState::Accepted); let termination_detail = (!stop_accepted).then(|| { let cancellation = lifecycle_failure_detail("cancel", &cancellation); let stop = lifecycle_failure_detail("stop", &stop); @@ -1277,7 +1284,11 @@ impl WorkerRemovalService { }); let runtime_deleted = match runtime.delete_worker(worker_ref) { - Ok(result) if result.state == WorkerOperationState::Accepted && result.deleted => true, + Ok(result) + if result.state == InternalWorkerOperationState::Accepted && result.deleted => + { + true + } Ok(result) => { let mut message = format!( "Runtime did not delete Worker {}:{}: state={:?}, deleted={}", @@ -3201,7 +3212,7 @@ impl WorkspaceApi { diagnostics, )); } - if result.state != WorkerOperationState::Accepted { + if result.state != InternalWorkerOperationState::Accepted { let diagnostics = compensate_failed_workspace_worker_create( self, runtime_id, @@ -3234,7 +3245,7 @@ impl WorkspaceApi { return Err(ApiError::with_diagnostics(error.into_error(), diagnostics)); } }; - if replacement.state != WorkerOperationState::Accepted { + if replacement.state != InternalWorkerOperationState::Accepted { let diagnostics = compensate_failed_workspace_worker_create( self, runtime_id, @@ -3359,7 +3370,7 @@ impl WorkspaceApi { fn restore_workspace_worker( &self, worker: &RuntimeWorkerRef, - ) -> ApiResult { + ) -> ApiResult { for link in self .store .list_worker_workdir_links(&self.config.workspace_id, worker)? @@ -3427,8 +3438,8 @@ impl WorkspaceApi { .runtime .replace_worker_workspace_api(worker, self.workspace_api_ref(&worker.runtime_id)) .map_err(|error| error.into_error())?; - if binding.state != WorkerOperationState::Accepted { - return Ok(WorkerRestoreResult { + if binding.state != InternalWorkerOperationState::Accepted { + return Ok(InternalWorkerRestoreResult { state: server_api::WorkerRestoreState::Rejected, worker: binding.worker, diagnostics: binding.diagnostics, @@ -4263,16 +4274,279 @@ fn generated_workspace_contract_router(service: ServerApiContractService) -> Rou )) } -fn project_server_dto(value: &T) -> Result { - let value = serde_json::to_value(value) - .map_err(|error| Error::RegistryInconsistency(error.to_string()))?; - serde_json::from_value(value).map_err(|error| Error::RegistryInconsistency(error.to_string())) +fn invalid_contract_value(context: &str, error: impl std::fmt::Display) -> Error { + Error::InvalidInput(format!("invalid {context}: {error}")) } -fn project_contract_dto( - value: &T, -) -> std::result::Result { - project_server_dto(value).map_err(|error| ApiError::from(error).into_repository_api_error()) +fn worker_ticket_assignment_from_api( + assignment: server_api::RuntimeWorkerTicketAssignmentRequest, +) -> WorkerTicketAssignmentRequest { + WorkerTicketAssignmentRequest { + ticket_id: assignment.ticket_id, + operation_id: assignment.operation_id, + } +} + +fn worker_spawn_request_from_api( + request: server_api::RuntimeWorkerSpawnRequest, +) -> Result { + let intent = match request.intent { + server_api::RuntimeWorkerSpawnIntent::WorkspaceCompanion => { + WorkerSpawnIntent::WorkspaceCompanion + } + server_api::RuntimeWorkerSpawnIntent::WorkspaceOrchestrator => { + WorkerSpawnIntent::WorkspaceOrchestrator + } + server_api::RuntimeWorkerSpawnIntent::WorkspaceCoding => WorkerSpawnIntent::WorkspaceCoding, + server_api::RuntimeWorkerSpawnIntent::TicketRole { ticket_id, role } => { + WorkerSpawnIntent::TicketRole { + ticket_id, + role: match role { + server_api::RuntimeTicketWorkerRole::Intake => TicketWorkerRole::Intake, + server_api::RuntimeTicketWorkerRole::Orchestrator => { + TicketWorkerRole::Orchestrator + } + server_api::RuntimeTicketWorkerRole::Coder => TicketWorkerRole::Coder, + server_api::RuntimeTicketWorkerRole::Reviewer => TicketWorkerRole::Reviewer, + }, + } + } + }; + let acceptance = match request.acceptance { + server_api::RuntimeWorkerSpawnAcceptanceRequirement::SocketReady => { + WorkerSpawnAcceptanceRequirement::SocketReady + } + server_api::RuntimeWorkerSpawnAcceptanceRequirement::RunAccepted { expected_segments } => { + WorkerSpawnAcceptanceRequirement::RunAccepted { expected_segments } + } + }; + let profile = match request.profile { + server_api::RuntimeProfileSelector::Builtin(name) => ProfileSelector::Builtin(name), + server_api::RuntimeProfileSelector::Named(name) => ProfileSelector::Named(name), + }; + let initial_submit = request + .initial_submit + .into_iter() + .map(|segment| { + serde_json::from_value::(segment) + .map_err(|error| invalid_contract_value("Worker initial Submit segment", error)) + }) + .collect::>>()?; + let workdir_attachment_requests = request + .workdir_attachment_requests + .into_iter() + .map(|attachment| { + Ok(crate::hosts::WorkerSpawnWorkingDirectoryAttachmentRequest { + alias: workdir::WorkdirAttachmentAlias::new(attachment.alias).map_err(|error| { + invalid_contract_value("Worker Workdir attachment alias", error) + })?, + working_directory: WorkerSpawnWorkingDirectoryRequest { + repository_key: attachment.working_directory.repository_key, + selector: attachment.working_directory.selector, + }, + }) + }) + .collect::>>()?; + Ok(WorkerSpawnRequest { + intent, + requested_worker_name: request.requested_worker_name, + acceptance, + profile, + ticket_assignment: request + .ticket_assignment + .map(worker_ticket_assignment_from_api), + initial_submit, + workdir_attachment_requests, + resolved_workdir_attachment_requests: Vec::new(), + resolved_workdir_attachments: Vec::new(), + resolved_config_bundle: None, + resolved_workspace_api: None, + resolved_memory_settings: None, + resolved_worker_observation_enabled: false, + resolved_worker_observation_grants: Vec::new(), + resolved_control_operation: None, + }) +} + +fn worker_input_request_from_api( + request: server_api::RuntimeWorkerInputRequest, +) -> Result { + let kind = match request.kind.as_deref().unwrap_or("user") { + "user" => WorkerInputKind::User, + "notify" => WorkerInputKind::Notify, + "compact" => WorkerInputKind::Compact, + "list_rewind_targets" => WorkerInputKind::ListRewindTargets, + "register_peer" => WorkerInputKind::RegisterPeer, + value => { + return Err(Error::InvalidInput(format!( + "invalid Worker input kind `{value}`" + ))); + } + }; + let segments = request + .segments + .map(|segments| { + segments + .into_iter() + .map(|segment| { + serde_json::from_value::(segment) + .map_err(|error| invalid_contract_value("Worker input segment", error)) + }) + .collect::>>() + }) + .transpose()?; + Ok(WorkerInputRequest { + kind, + content: request.content, + segments, + }) +} + +fn worker_lifecycle_request_from_api( + request: server_api::RuntimeWorkerLifecycleRequest, +) -> WorkerLifecycleRequest { + WorkerLifecycleRequest { + reason: request.reason, + ticket_assignment: request + .ticket_assignment + .map(worker_ticket_assignment_from_api), + } +} + +fn worker_completions_request_from_api( + request: server_api::RuntimeWorkerCompletionsRequest, +) -> Result { + Ok(WorkerCompletionsRequest { + kind: serde_json::from_value(request.kind) + .map_err(|error| invalid_contract_value("Worker completion kind", error))?, + prefix: request.prefix, + }) +} + +fn runtime_worker_summary_to_api( + worker: InternalWorkerSummary, +) -> Result { + Ok(server_api::RuntimeWorkerSummary { + runtime_id: worker.worker.runtime_id, + worker_id: worker.worker.worker_id, + host_id: worker.host_id, + display_name: worker.display_name, + label: worker.label, + profile: worker.profile, + singleton_key: worker.singleton_key, + tags: worker.tags, + workspace: server_api::WorkerWorkspaceSummary { + visibility: worker.workspace.visibility, + identity: worker.workspace.identity, + workspace_id: worker.workspace.workspace_id, + }, + state: worker.state, + worker_state: worker + .worker_state + .map(serde_json::to_value) + .transpose() + .map_err(|error| invalid_contract_value("Runtime Worker state", error))?, + last_seen_at: worker.last_seen_at, + pinned: worker.pinned, + retention_state: worker.retention_state, + implementation: server_api::WorkerImplementationSummary { + 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() + .map(|attachment| { + serde_json::to_value(attachment).map_err(|error| { + invalid_contract_value("Runtime Worker Workdir attachment", error) + }) + }) + .collect::>>()?, + diagnostics: worker.diagnostics.into_iter().map(Into::into).collect(), + }) +} + +fn worker_spawn_result_to_api( + result: WorkerSpawnResult, +) -> Result { + Ok(server_api::RuntimeWorkerSpawnResponse { + state: result.state.into(), + worker: result + .worker + .map(runtime_worker_summary_to_api) + .transpose()?, + acceptance_evidence: result + .acceptance_evidence + .into_iter() + .map( + |evidence| server_api::RuntimeWorkerSpawnAcceptanceEvidence { + kind: evidence.kind, + detail: evidence.detail, + }, + ) + .collect(), + diagnostics: result.diagnostics.into_iter().map(Into::into).collect(), + }) +} + +fn worker_input_result_to_api(result: WorkerInputResult) -> server_api::RuntimeWorkerInputResult { + server_api::RuntimeWorkerInputResult { + state: result.state.into(), + runtime_id: result.worker.runtime_id, + worker_id: result.worker.worker_id, + diagnostics: result.diagnostics.into_iter().map(Into::into).collect(), + } +} + +fn worker_lifecycle_result_to_api( + result: WorkerLifecycleResult, +) -> server_api::RuntimeWorkerLifecycleResult { + server_api::RuntimeWorkerLifecycleResult { + state: result.state.into(), + runtime_id: result.worker.runtime_id, + worker_id: result.worker.worker_id, + diagnostics: result.diagnostics.into_iter().map(Into::into).collect(), + } +} + +fn worker_completions_result_to_api( + result: WorkerCompletionsResult, +) -> Result { + Ok(server_api::RuntimeWorkerCompletionsResult { + runtime_id: result.worker.runtime_id, + worker_id: result.worker.worker_id, + kind: serde_json::to_value(result.kind) + .map_err(|error| invalid_contract_value("Worker completion kind", error))?, + prefix: result.prefix, + entries: result + .entries + .into_iter() + .map(|entry| { + serde_json::to_value(entry) + .map_err(|error| invalid_contract_value("Worker completion entry", error)) + }) + .collect::>>()?, + diagnostics: result.diagnostics.into_iter().map(Into::into).collect(), + }) +} + +fn runtime_config_bundle_result_to_api( + state: InternalWorkerOperationState, + availability: Option, + diagnostics: Vec, +) -> Result { + Ok(server_api::RuntimeConfigBundleResult { + state: state.into(), + availability: availability + .map(serde_json::to_value) + .transpose() + .map_err(|error| invalid_contract_value("Runtime ConfigBundle availability", error))?, + diagnostics: diagnostics.into_iter().map(Into::into).collect(), + }) } impl server_api::ServerApi for ServerApiContractService { @@ -4898,12 +5172,7 @@ impl server_api::ServerApi for ServerApiContractService { AxumPath(ScopedWorkspacePath { workspace_id }), ) .await - .and_then(|Json(response)| { - project_server_dto(&response) - .map(Json) - .map_err(ApiError::from) - }) - .map(|Json(response)| response) + .map(|Json(response)| prompt_projection_to_api(response)) .map_err(ApiError::into_repository_api_error) } @@ -4913,8 +5182,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::ConfigCommitRequest, ) -> std::result::Result { - let request = project_server_dto(&request) - .map_err(|error| ApiError::from(error).into_repository_api_error())?; scoped_commit_workspace_config_tree( State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), @@ -4944,11 +5211,6 @@ impl server_api::ServerApi for ServerApiContractService { }), ) .await - .and_then(|Json(response)| { - project_server_dto(&response) - .map(Json) - .map_err(ApiError::from) - }) .map(|Json(response)| response) .map_err(ApiError::into_repository_api_error) } @@ -4963,11 +5225,6 @@ impl server_api::ServerApi for ServerApiContractService { AxumPath(WorkspaceConfigEntryPath { workspace_id, path }), ) .await - .and_then(|Json(response)| { - project_server_dto(&response) - .map(Json) - .map_err(ApiError::from) - }) .map(|Json(response)| response) .map_err(ApiError::into_repository_api_error) } @@ -4995,12 +5252,14 @@ impl server_api::ServerApi for ServerApiContractService { AxumPath(ScopedWorkspacePath { workspace_id }), ) .await - .and_then(|Json(response)| { - project_server_dto(&response) - .map(Json) - .map_err(ApiError::from) + .map(|Json(response)| { + server_api::FlowSourceListResponse( + response + .into_iter() + .map(flow_source_record_to_api) + .collect(), + ) }) - .map(|Json(response)| server_api::FlowSourceListResponse(response)) .map_err(ApiError::into_repository_api_error) } @@ -5015,12 +5274,7 @@ impl server_api::ServerApi for ServerApiContractService { Json(request), ) .await - .and_then(|Json(response)| { - project_server_dto(&response) - .map(Json) - .map_err(ApiError::from) - }) - .map(|Json(response)| response) + .map(|Json(response)| flow_source_record_to_api(response)) .map_err(ApiError::into_repository_api_error) } @@ -5029,7 +5283,7 @@ impl server_api::ServerApi for ServerApiContractService { workspace_id: String, request: server_api::FlowSourceResolveRequest, ) -> std::result::Result { - let request = project_server_dto(&request) + let request = flow_source_resolve_request_from_api(request) .map_err(|error| ApiError::from(error).into_repository_api_error())?; scoped_resolve_flow_source( State(self.workspace_api()?.clone()), @@ -5037,12 +5291,7 @@ impl server_api::ServerApi for ServerApiContractService { Json(request), ) .await - .and_then(|Json(response)| { - project_server_dto(&response) - .map(Json) - .map_err(ApiError::from) - }) - .map(|Json(response)| response) + .map(|Json(response)| resolved_flow_source_to_api(response)) .map_err(ApiError::into_repository_api_error) } @@ -5059,12 +5308,7 @@ impl server_api::ServerApi for ServerApiContractService { }), ) .await - .and_then(|Json(response)| { - project_server_dto(&response) - .map(Json) - .map_err(ApiError::from) - }) - .map(|Json(response)| response) + .map(|Json(response)| flow_source_record_to_api(response)) .map_err(ApiError::into_repository_api_error) } @@ -5089,9 +5333,7 @@ impl server_api::ServerApi for ServerApiContractService { scoped_list_memory_staging( State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), - Query(MemoryStagingQuery { - limit: query.limit.map(|limit| limit as usize), - }), + Query(query), ) .await .map(|Json(response)| response) @@ -5239,7 +5481,7 @@ impl server_api::ServerApi for ServerApiContractService { let Json(response) = list_workers(State(self.workspace_api()?.clone())) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn workspace_worker_create_alias( @@ -5303,7 +5545,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn worker_control_spawn( @@ -5333,8 +5575,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerInputRequest, ) -> std::result::Result { - let request = project_server_dto(&request) - .map_err(|error| ApiError::from(error).into_repository_api_error())?; let Json(response) = send_known_worker_input( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -5346,7 +5586,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn worker_control_cancel( @@ -5358,8 +5598,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerLifecycleRequest, ) -> std::result::Result { - let request = project_server_dto(&request) - .map_err(|error| ApiError::from(error).into_repository_api_error())?; let Json(response) = cancel_known_worker( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -5371,7 +5609,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn worker_control_stop( @@ -5383,8 +5621,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerLifecycleRequest, ) -> std::result::Result { - let request = project_server_dto(&request) - .map_err(|error| ApiError::from(error).into_repository_api_error())?; let Json(response) = stop_known_worker( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -5396,7 +5632,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn worker_control_restore( @@ -5435,9 +5671,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - serde_json::from_value(response) - .map_err(|error| Error::RegistryInconsistency(error.to_string())) - .map_err(|error| ApiError::from(error).into_repository_api_error()) + Ok(response) } async fn worker_observation_capture( @@ -5449,8 +5683,6 @@ impl server_api::ServerApi for ServerApiContractService { server_api::WorkerObservationCaptureResponse, server_api::RepositoryApiError, > { - let request = project_server_dto(&request) - .map_err(|error| ApiError::from(error).into_repository_api_error())?; let Json(response) = scoped_capture_worker_observation_session( State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), @@ -5459,9 +5691,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - serde_json::from_value(response) - .map_err(|error| Error::RegistryInconsistency(error.to_string())) - .map_err(|error| ApiError::from(error).into_repository_api_error()) + Ok(response) } async fn workspace_worker_discovery( @@ -5491,7 +5721,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn workspace_worker_create( @@ -5713,7 +5943,7 @@ impl server_api::ServerApi for ServerApiContractService { let Json(response) = list_runtimes(State(self.workspace_api()?.clone())) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_list( @@ -5727,7 +5957,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_create( @@ -5954,7 +6184,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::RuntimeWorkersQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; let Json(response) = list_runtime_workers( State(self.workspace_api()?.clone()), AxumPath(runtime_id), @@ -5962,7 +6191,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_create_alias( @@ -5971,7 +6200,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerSpawnRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = create_runtime_worker( State(self.workspace_api()?.clone()), AxumPath(runtime_id), @@ -5979,7 +6207,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_list( @@ -5989,7 +6217,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::RuntimeWorkersQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; let Json(response) = scoped_list_runtime_workers( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimePath { @@ -6000,7 +6227,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_create( @@ -6010,7 +6237,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerSpawnRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_create_runtime_worker( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimePath { @@ -6021,7 +6247,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_config_bundle_sync_alias( @@ -6030,7 +6256,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeConfigBundleSyncRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = sync_runtime_config_bundle( State(self.workspace_api()?.clone()), AxumPath(runtime_id), @@ -6038,7 +6263,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_config_bundle_sync( @@ -6048,7 +6273,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeConfigBundleSyncRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_sync_runtime_config_bundle( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimePath { @@ -6059,7 +6283,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_config_bundle_availability_alias( @@ -6069,7 +6293,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::RuntimeConfigBundleAvailabilityQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; let Json(response) = check_runtime_config_bundle( State(self.workspace_api()?.clone()), AxumPath((runtime_id, bundle_id)), @@ -6077,7 +6300,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_config_bundle_availability( @@ -6088,7 +6311,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::RuntimeConfigBundleAvailabilityQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; let Json(response) = scoped_check_runtime_config_bundle( State(self.workspace_api()?.clone()), AxumPath(ScopedConfigBundlePath { @@ -6100,7 +6322,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_detail_alias( @@ -6115,7 +6337,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_detail( @@ -6134,7 +6356,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_restore_alias( @@ -6160,7 +6382,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::RestoreTicketAssignmentQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; scoped_restore_runtime_worker( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -6190,7 +6411,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_unpin( @@ -6209,7 +6430,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_cleanup_plan( @@ -6227,7 +6448,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_cleanup_execute( @@ -6239,7 +6460,6 @@ impl server_api::ServerApi for ServerApiContractService { server_api::RuntimeCleanupExecutionResponse, server_api::RepositoryApiError, > { - let request = project_contract_dto(&request)?; let Json(response) = scoped_execute_runtime_cleanup( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimePath { @@ -6250,7 +6470,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_input_alias( @@ -6260,7 +6480,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerInputRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = send_runtime_worker_input( State(self.workspace_api()?.clone()), AxumPath((runtime_id, worker_id)), @@ -6268,7 +6487,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_input( @@ -6279,7 +6498,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerInputRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_send_runtime_worker_input( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -6290,7 +6508,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_attachment_upload_grant( @@ -6304,7 +6522,6 @@ impl server_api::ServerApi for ServerApiContractService { server_api::AttachmentUploadGrantResponse, server_api::RepositoryApiError, > { - let query = project_contract_dto(&query)?; let Json(response) = scoped_create_attachment_upload_grant( State(self.workspace_api()?.clone()), Extension(actor), @@ -6316,7 +6533,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_attachment_upload_cancel( @@ -6342,7 +6559,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_attachment_delete( @@ -6365,7 +6582,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_completions_alias( @@ -6377,7 +6594,6 @@ impl server_api::ServerApi for ServerApiContractService { server_api::RuntimeWorkerCompletionsResult, server_api::RepositoryApiError, > { - let request = project_contract_dto(&request)?; let Json(response) = runtime_worker_completions( State(self.workspace_api()?.clone()), AxumPath((runtime_id, worker_id)), @@ -6385,7 +6601,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_completions( @@ -6398,7 +6614,6 @@ impl server_api::ServerApi for ServerApiContractService { server_api::RuntimeWorkerCompletionsResult, server_api::RepositoryApiError, > { - let request = project_contract_dto(&request)?; let Json(response) = scoped_runtime_worker_completions( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -6409,7 +6624,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_stop_alias( @@ -6419,7 +6634,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerLifecycleRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = stop_runtime_worker( State(self.workspace_api()?.clone()), AxumPath((runtime_id, worker_id)), @@ -6427,7 +6641,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_stop( @@ -6438,7 +6652,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerLifecycleRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_stop_runtime_worker( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -6449,7 +6662,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_cancel_alias( @@ -6459,7 +6672,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerLifecycleRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = cancel_runtime_worker( State(self.workspace_api()?.clone()), AxumPath((runtime_id, worker_id)), @@ -6467,7 +6679,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn runtime_worker_cancel( @@ -6478,7 +6690,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::RuntimeWorkerLifecycleRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_cancel_runtime_worker( State(self.workspace_api()?.clone()), AxumPath(ScopedRuntimeWorkerPath { @@ -6489,7 +6700,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn repository_list( @@ -6577,7 +6788,7 @@ impl server_api::ServerApi for ServerApiContractService { let Json(response) = list_tickets(State(self.workspace_api()?.clone()), Query(query)) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_list( @@ -6597,7 +6808,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_create_record( @@ -6622,7 +6833,6 @@ impl server_api::ServerApi for ServerApiContractService { workspace_id: String, request: server_api::TicketQueryRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_query_tickets( State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), @@ -6630,7 +6840,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_get_alias( @@ -6640,7 +6850,7 @@ impl server_api::ServerApi for ServerApiContractService { let Json(response) = get_ticket(State(self.workspace_api()?.clone()), AxumPath(id)) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_default_intake_ready_body( @@ -6653,7 +6863,7 @@ impl server_api::ServerApi for ServerApiContractService { State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), contract_request_headers(&context)?, - Json(DefaultIntakeReadyBodyRequest { from: request.from }), + Json(InternalDefaultIntakeReadyBodyRequest { from: request.from }), ) .await .map_err(ApiError::into_repository_api_error)?; @@ -6671,7 +6881,7 @@ impl server_api::ServerApi for ServerApiContractService { State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), contract_request_headers(&context)?, - Query(TicketSummarySearchQuery { state: query.state }), + Query(query), ) .await .map_err(ApiError::into_repository_api_error)?; @@ -6704,10 +6914,7 @@ impl server_api::ServerApi for ServerApiContractService { State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), contract_request_headers(&context)?, - Json(TicketRelationSearchRequest { - ticket: request.ticket, - kind: request.kind, - }), + Json(request), ) .await .map_err(ApiError::into_repository_api_error)?; @@ -6727,10 +6934,7 @@ impl server_api::ServerApi for ServerApiContractService { State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), contract_request_headers(&context)?, - Json(TicketOrchestrationPlanSearchRequest { - ticket: request.ticket, - kind: request.kind, - }), + Json(request), ) .await .map_err(ApiError::into_repository_api_error)?; @@ -6892,7 +7096,7 @@ impl server_api::ServerApi for ServerApiContractService { State(self.workspace_api()?.clone()), AxumPath((workspace_id, id)), contract_request_headers(&context)?, - Json(TicketMarkReadyRequest { + Json(InternalTicketMarkReadyRequest { operation_key: request.operation_key, reason: request.reason, intake_summary: request.intake_summary, @@ -6925,7 +7129,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::MergeRequestListQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; let Json(response) = scoped_list_merge_requests( State(self.workspace_api()?.clone()), AxumPath(workspace_id), @@ -6933,7 +7136,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn merge_request_show( @@ -6943,7 +7146,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::MergeRequestThreadQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; let Json(response) = scoped_show_merge_request( State(self.workspace_api()?.clone()), AxumPath((workspace_id, merge_request_id)), @@ -6951,7 +7153,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn merge_request_open( @@ -6961,7 +7163,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::OpenMergeRequestRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_open_merge_request( State(self.workspace_api()?.clone()), contract_request_headers(&context)?, @@ -6970,7 +7171,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn merge_request_readiness( @@ -6987,7 +7188,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn merge_request_thread( @@ -6997,7 +7198,6 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::MergeRequestThreadQuery, ) -> std::result::Result { - let query = project_contract_dto(&query)?; let Json(response) = scoped_merge_request_thread( State(self.workspace_api()?.clone()), AxumPath((workspace_id, id)), @@ -7005,7 +7205,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response).map(server_api::MergeRequestThreadResponse) + Ok(server_api::MergeRequestThreadResponse(response)) } async fn merge_request_selector_repair( @@ -7015,7 +7215,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::RepairMergeRequestSelectorRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_repair_merge_request_selector( State(self.workspace_api()?.clone()), contract_request_headers(&context)?, @@ -7024,7 +7223,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn merge_request_reviewer_child_register( @@ -7033,7 +7232,6 @@ impl server_api::ServerApi for ServerApiContractService { workspace_id: String, request: server_api::RegisterReviewerChildSessionRequest, ) -> std::result::Result<(), server_api::RepositoryApiError> { - let request = project_contract_dto(&request)?; scoped_register_reviewer_child_session( State(self.workspace_api()?.clone()), contract_request_headers(&context)?, @@ -7052,7 +7250,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::RegisterMergeRequestReviewCapabilityRequest, ) -> std::result::Result<(), server_api::RepositoryApiError> { - let request = project_contract_dto(&request)?; scoped_register_merge_request_review_capability( State(self.workspace_api()?.clone()), contract_request_headers(&context)?, @@ -7070,7 +7267,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::SubmitMergeRequestReviewRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_submit_merge_request_review( State(self.workspace_api()?.clone()), AxumPath((workspace_id, id)), @@ -7078,7 +7274,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn merge_request_review_revoke( @@ -7088,7 +7284,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::RevokeMergeRequestReviewRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_revoke_merge_request_review( State(self.workspace_api()?.clone()), contract_request_headers(&context)?, @@ -7097,7 +7292,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn merge_request_complete( @@ -7107,7 +7302,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::CompleteMergeRequestRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_complete_merge_request( State(self.workspace_api()?.clone()), contract_request_headers(&context)?, @@ -7116,7 +7310,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_close_record( @@ -7183,10 +7377,7 @@ impl server_api::ServerApi for ServerApiContractService { State(self.workspace_api()?.clone()), AxumPath((workspace_id, id)), contract_request_headers(&context)?, - Json(TicketRelationRemoveRequest { - kind: request.kind, - target: request.target, - }), + Json(request), ) .await .map_err(ApiError::into_repository_api_error)?; @@ -7225,7 +7416,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_edit( @@ -7234,7 +7425,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::BrowserEditTicketRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_edit_ticket_item( State(self.workspace_api()?.clone()), AxumPath(ScopedRecordPath { workspace_id, id }), @@ -7242,7 +7432,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_show( @@ -7251,7 +7441,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::TicketShowRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_show_ticket( State(self.workspace_api()?.clone()), AxumPath(ScopedRecordPath { workspace_id, id }), @@ -7259,7 +7448,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_assignment_list( @@ -7276,7 +7465,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_assignment_set( @@ -7289,7 +7478,6 @@ impl server_api::ServerApi for ServerApiContractService { server_api::TicketRoleAssignmentMutationResponse, server_api::RepositoryApiError, > { - let request = project_contract_dto(&request)?; let Json(response) = scoped_set_ticket_assignment( State(self.workspace_api()?.clone()), AxumPath((workspace_id, id, role)), @@ -7297,7 +7485,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_assignment_clear( @@ -7310,7 +7498,6 @@ impl server_api::ServerApi for ServerApiContractService { server_api::TicketRoleAssignmentMutationResponse, server_api::RepositoryApiError, > { - let query = project_contract_dto(&query)?; let Json(response) = scoped_clear_ticket_assignment( State(self.workspace_api()?.clone()), AxumPath((workspace_id, id, role)), @@ -7318,7 +7505,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_implementation_cancel( @@ -7327,7 +7514,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::CancelTicketImplementationRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_cancel_ticket_implementation( State(self.workspace_api()?.clone()), AxumPath(ScopedRecordPath { workspace_id, id }), @@ -7335,7 +7521,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_state_transition( @@ -7344,7 +7530,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::BrowserTransitionTicketStateRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_transition_ticket_state( State(self.workspace_api()?.clone()), AxumPath(ScopedRecordPath { workspace_id, id }), @@ -7352,7 +7537,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_ready( @@ -7361,7 +7546,7 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::TicketMarkReadyRequest, ) -> std::result::Result { - let request = TicketMarkReadyRequest { + let request = InternalTicketMarkReadyRequest { operation_key: request.operation_key, reason: request.reason, intake_summary: request.intake_summary, @@ -7373,7 +7558,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_event_append( @@ -7382,7 +7567,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::BrowserAppendTicketEventRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_append_ticket_event( State(self.workspace_api()?.clone()), AxumPath(ScopedRecordPath { workspace_id, id }), @@ -7390,7 +7574,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn ticket_queue( @@ -7399,7 +7583,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::BrowserQueueTicketRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_queue_ticket( State(self.workspace_api()?.clone()), AxumPath(ScopedRecordPath { workspace_id, id }), @@ -7416,7 +7599,6 @@ impl server_api::ServerApi for ServerApiContractService { id: String, request: server_api::BrowserCloseTicketRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_close_ticket( State(self.workspace_api()?.clone()), AxumPath(ScopedRecordPath { workspace_id, id }), @@ -7424,7 +7606,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_list_alias( @@ -7432,13 +7614,10 @@ impl server_api::ServerApi for ServerApiContractService { query: server_api::ObjectiveListQuery, ) -> std::result::Result { - let Json(response) = list_objectives( - State(self.workspace_api()?.clone()), - Query(ObjectiveListQuery { limit: query.limit }), - ) - .await - .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + let Json(response) = list_objectives(State(self.workspace_api()?.clone()), Query(query)) + .await + .map_err(ApiError::into_repository_api_error)?; + Ok(response) } async fn objective_list( @@ -7450,11 +7629,11 @@ impl server_api::ServerApi for ServerApiContractService { let Json(response) = scoped_list_objectives( State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), - Query(ObjectiveListQuery { limit: query.limit }), + Query(query), ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_create( @@ -7469,7 +7648,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_query( @@ -7478,7 +7657,6 @@ impl server_api::ServerApi for ServerApiContractService { request: server_api::ObjectiveQueryRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_query_objectives( State(self.workspace_api()?.clone()), AxumPath(ScopedWorkspacePath { workspace_id }), @@ -7486,7 +7664,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_get_alias( @@ -7496,7 +7674,7 @@ impl server_api::ServerApi for ServerApiContractService { let Json(response) = get_objective(State(self.workspace_api()?.clone()), AxumPath(id)) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_get( @@ -7513,7 +7691,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_edit( @@ -7532,7 +7710,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_show( @@ -7541,7 +7719,6 @@ impl server_api::ServerApi for ServerApiContractService { objective_id: String, request: server_api::ObjectiveShowRequest, ) -> std::result::Result { - let request = project_contract_dto(&request)?; let Json(response) = scoped_show_objective( State(self.workspace_api()?.clone()), AxumPath(ScopedObjectivePath { @@ -7552,7 +7729,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_state_set( @@ -7571,7 +7748,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_ticket_link( @@ -7590,7 +7767,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn objective_ticket_unlink( @@ -7609,7 +7786,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - project_contract_dto(&response) + Ok(response) } async fn repository_ssh_connection_probe( @@ -7863,13 +8040,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - Ok(server_api::CurrentWorkerWorkdirAttachmentResponse { - workspace_id: response.workspace_id, - alias: response.alias, - working_directory_id: response.working_directory_id, - capabilities: response.capabilities, - attached: response.attached, - }) + Ok(response) } async fn current_worker_workdir_detach( @@ -7888,13 +8059,7 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(ApiError::into_repository_api_error)?; - Ok(server_api::CurrentWorkerWorkdirAttachmentResponse { - workspace_id: response.workspace_id, - alias: response.alias, - working_directory_id: response.working_directory_id, - capabilities: response.capabilities, - attached: response.attached, - }) + Ok(response) } async fn current_worker_workdir_operation( @@ -7922,10 +8087,10 @@ impl server_api::ServerApi for ServerApiContractService { ) .await .map_err(|error| match error { - WorkdirOperationApiError::Api(error) => { + InternalWorkdirOperationError::Api(error) => { server_api::WorkdirOperationApiError::api(error.into_repository_api_error()) } - WorkdirOperationApiError::Provider(error) => { + InternalWorkdirOperationError::Provider(error) => { server_api::WorkdirOperationApiError::provider(error) } })?; @@ -8216,7 +8381,7 @@ pub async fn serve( } #[derive(Debug, Serialize, Deserialize)] -pub struct ListResponse { +pub struct ProjectListResponse { pub workspace_id: String, pub limit: usize, pub items: Vec, @@ -8225,7 +8390,7 @@ pub struct ListResponse { } #[derive(Debug, Serialize, Deserialize)] -pub struct RuntimeListResponse { +pub struct InternalRuntimeListResponse { pub workspace_id: String, pub limit: usize, pub items: Vec, @@ -8233,184 +8398,57 @@ pub struct RuntimeListResponse { pub diagnostics: Vec, } -#[derive(Debug, Default, Deserialize)] -struct RuntimeWorkersQuery { - status: Option, -} - -#[derive(Debug, Clone, Copy, Deserialize)] -#[serde(rename_all = "snake_case")] -enum RuntimeWorkersStatusFilter { - Stopped, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum CleanupTargetKind { - WorkerDelete, - WorkdirCleanCleanup, - WorkdirDirtyDiscard, - WorkdirRecordDelete, -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum CleanupWorkdirFileStatus { - Pending, - Present, - Active, - CleanupPending, - NotFound, - Corrupted, - Failed, - Unknown, -} - -impl CleanupWorkdirFileStatus { - fn from_registry(value: &str) -> Self { - match value { - "pending" => Self::Pending, - "present" => Self::Present, - "active" => Self::Active, - "cleanup_pending" => Self::CleanupPending, - "not_found" => Self::NotFound, - "corrupted" => Self::Corrupted, - "failed" => Self::Failed, - "unknown" => Self::Unknown, - _ => Self::Unknown, - } - } - - fn from_runtime(value: &WorkingDirectoryStatusKind) -> Self { - match value { - WorkingDirectoryStatusKind::Active => Self::Active, - WorkingDirectoryStatusKind::CleanupPending => Self::CleanupPending, - WorkingDirectoryStatusKind::Corrupted => Self::Corrupted, - WorkingDirectoryStatusKind::NotFound => Self::NotFound, - WorkingDirectoryStatusKind::Unknown => Self::Unknown, - } - } - - fn is_record_only(self) -> bool { - matches!(self, Self::NotFound) - } - - fn is_corrupted(self) -> bool { - matches!(self, Self::Corrupted) +fn cleanup_workdir_file_status_from_registry(value: &str) -> CleanupWorkdirFileStatus { + match value { + "pending" => CleanupWorkdirFileStatus::Pending, + "present" => CleanupWorkdirFileStatus::Present, + "active" => CleanupWorkdirFileStatus::Active, + "cleanup_pending" => CleanupWorkdirFileStatus::CleanupPending, + "not_found" => CleanupWorkdirFileStatus::NotFound, + "corrupted" => CleanupWorkdirFileStatus::Corrupted, + "failed" => CleanupWorkdirFileStatus::Failed, + "unknown" => CleanupWorkdirFileStatus::Unknown, + _ => CleanupWorkdirFileStatus::Unknown, } } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum CleanupWorkdirCleanliness { - Clean, - Dirty, - Unknown, -} - -impl CleanupWorkdirCleanliness { - fn from_registry(value: &str) -> Self { - match value { - "clean" => Self::Clean, - "dirty" => Self::Dirty, - "unknown" => Self::Unknown, - _ => Self::Unknown, - } - } - - fn from_runtime(value: Option<&str>) -> Self { - value.map(Self::from_registry).unwrap_or(Self::Unknown) - } - - fn is_clean(self) -> bool { - matches!(self, Self::Clean) +fn cleanup_workdir_file_status_from_runtime( + value: &WorkingDirectoryStatusKind, +) -> CleanupWorkdirFileStatus { + match value { + WorkingDirectoryStatusKind::Active => CleanupWorkdirFileStatus::Active, + WorkingDirectoryStatusKind::CleanupPending => CleanupWorkdirFileStatus::CleanupPending, + WorkingDirectoryStatusKind::Corrupted => CleanupWorkdirFileStatus::Corrupted, + WorkingDirectoryStatusKind::NotFound => CleanupWorkdirFileStatus::NotFound, + WorkingDirectoryStatusKind::Unknown => CleanupWorkdirFileStatus::Unknown, } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct CleanupWorkerCandidate { - pub target_id: String, - pub action: CleanupTargetKind, - pub worker_id: String, - pub runtime_worker_id: String, - pub runtime_id: String, - pub reason: String, - pub blocking_reason: Option, - pub pinned: bool, - pub retention_state: String, - pub linked_workdir_ids: Vec, - pub running_linked: bool, - pub estimated_reclaim_bytes: Option, +fn cleanup_workdir_file_status_is_record_only(value: CleanupWorkdirFileStatus) -> bool { + matches!(value, CleanupWorkdirFileStatus::NotFound) } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct CleanupWorkdirCandidate { - pub target_id: String, - pub action: CleanupTargetKind, - pub workdir_id: String, - pub runtime_id: String, - pub repository_key: String, - pub reason: String, - pub blocking_reason: Option, - pub linked_worker_ids: Vec, - pub linked_running_worker_ids: Vec, - pub running_linked: bool, - pub pinned_linked: bool, - pub file_status: CleanupWorkdirFileStatus, - pub cleanliness: CleanupWorkdirCleanliness, - pub estimated_reclaim_bytes: Option, +fn cleanup_workdir_file_status_is_corrupted(value: CleanupWorkdirFileStatus) -> bool { + matches!(value, CleanupWorkdirFileStatus::Corrupted) } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct RuntimeCleanupPlanResponse { - pub workspace_id: String, - pub runtime_id: String, - pub generated_at: String, - pub revision: String, - pub digest: String, - pub workers: Vec, - pub workdirs: Vec, - pub diagnostics: Vec, +fn cleanup_workdir_cleanliness_from_registry(value: &str) -> CleanupWorkdirCleanliness { + match value { + "clean" => CleanupWorkdirCleanliness::Clean, + "dirty" => CleanupWorkdirCleanliness::Dirty, + "unknown" => CleanupWorkdirCleanliness::Unknown, + _ => CleanupWorkdirCleanliness::Unknown, + } } -#[derive(Debug, Clone, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct ExecuteRuntimeCleanupRequest { - pub expected_plan_revision: String, - pub expected_plan_digest: String, - #[serde(default)] - pub worker_target_ids: Vec, - #[serde(default)] - pub workdir_target_ids: Vec, - #[serde(default)] - pub confirm_dirty_discard_target_ids: Vec, +fn cleanup_workdir_cleanliness_from_runtime(value: Option<&str>) -> CleanupWorkdirCleanliness { + value + .map(cleanup_workdir_cleanliness_from_registry) + .unwrap_or(CleanupWorkdirCleanliness::Unknown) } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct RuntimeCleanupExecutionResult { - pub target_id: String, - pub action: CleanupTargetKind, - pub status: String, - pub message: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct RuntimeCleanupExecutionResponse { - pub workspace_id: String, - pub runtime_id: String, - pub executed_at: String, - pub results: Vec, - pub plan_after: RuntimeCleanupPlanResponse, - pub diagnostics: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct WorkerRetentionResponse { - pub workspace_id: String, - #[serde(flatten)] - pub worker_ref: RuntimeWorkerRef, - pub pinned: bool, - pub retention_state: String, +fn cleanup_workdir_cleanliness_is_clean(value: CleanupWorkdirCleanliness) -> bool { + matches!(value, CleanupWorkdirCleanliness::Clean) } #[derive(Debug, Deserialize)] @@ -8418,16 +8456,6 @@ struct LogQuery { limit: Option, } -#[derive(Debug, Deserialize)] -struct MemoryStagingQuery { - limit: Option, -} - -#[derive(Debug, Deserialize)] -struct ObjectiveListQuery { - limit: Option, -} - #[derive(Debug, Deserialize)] struct TicketListQuery { limit: Option, @@ -8473,15 +8501,6 @@ struct AttachCurrentWorkerWorkdirRequest { working_directory_id: String, } -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -struct CurrentWorkerWorkdirAttachmentResponse { - workspace_id: String, - alias: String, - working_directory_id: String, - capabilities: workdir::WorkdirSessionCapabilities, - attached: bool, -} - #[derive(Debug, Clone, Deserialize)] struct ScopedRecordPath { workspace_id: String, @@ -8832,13 +8851,13 @@ struct WorkspaceConfigRevisionPath { async fn scoped_get_workspace_config_revision( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let snapshot = api .config_store .load_workspace_config_revision(&path.workspace_id, path.revision)? .ok_or_else(|| ApiError::from(Error::InvalidRecordId(path.revision.to_string())))?; - Ok(Json(snapshot)) + Ok(Json(config_tree_snapshot_to_api(snapshot))) } #[derive(Debug, Deserialize)] @@ -9146,6 +9165,272 @@ async fn scoped_delete_repository_ssh_host_trust( Ok(StatusCode::NO_CONTENT) } +fn config_content_type_to_api( + content_type: config_source::ConfigContentType, +) -> server_api::ConfigContentType { + match content_type { + config_source::ConfigContentType::Decodal => server_api::ConfigContentType::Decodal, + config_source::ConfigContentType::Text => server_api::ConfigContentType::Text, + } +} + +fn config_entry_to_api(entry: config_source::ConfigEntry) -> server_api::ConfigEntry { + server_api::ConfigEntry { + path: entry.path.to_string(), + content_type: config_content_type_to_api(entry.content_type), + content: entry.content, + content_digest: entry.content_digest, + } +} + +fn config_tree_snapshot_to_api( + snapshot: config_source::ConfigTreeSnapshot, +) -> server_api::ConfigTreeSnapshot { + server_api::ConfigTreeSnapshot { + revision: snapshot.revision, + digest: snapshot.digest, + entries: snapshot + .entries + .into_iter() + .map(|(path, entry)| (path.to_string(), config_entry_to_api(entry))) + .collect(), + } +} + +fn config_projection_validator_to_api( + validator: config_source::ConfigProjectionValidator, +) -> server_api::ConfigProjectionValidator { + match validator { + config_source::ConfigProjectionValidator::StaticTemplateCatalog { + namespace, + key_aliases, + } => server_api::ConfigProjectionValidator::StaticTemplateCatalog { + namespace, + key_aliases, + }, + } +} + +fn config_schema_bundle_to_api( + bundle: config_source::WorkspaceConfigSchemaBundle, +) -> server_api::WorkspaceConfigSchemaBundle { + server_api::WorkspaceConfigSchemaBundle { + contributions: bundle + .contributions + .into_iter() + .map(|contribution| server_api::ConfigSchemaContribution { + provider_id: contribution.provider_id, + namespace: contribution.namespace, + version: contribution.version, + source: contribution.source, + projection_validator: contribution + .projection_validator + .map(config_projection_validator_to_api), + source_digest: contribution.source_digest, + }) + .collect(), + source: bundle.source, + fingerprint: bundle.fingerprint, + } +} + +fn toolchain_contract_to_api( + contract: config_source::ToolchainContract, +) -> server_api::ToolchainContract { + server_api::ToolchainContract { + contract_version: contract.contract_version, + decodal_version: contract.decodal_version, + schema_version: contract.schema_version, + entrypoints: contract + .entrypoints + .into_iter() + .map(|path| path.to_string()) + .collect(), + import_policy_version: contract.import_policy_version, + schema_bundle: config_schema_bundle_to_api(contract.schema_bundle), + fingerprint: contract.fingerprint, + } +} + +fn workspace_config_state_to_api( + state: crate::config_source::WorkspaceConfigState, +) -> server_api::WorkspaceConfigTreeResponse { + server_api::WorkspaceConfigTreeResponse { + snapshot: config_tree_snapshot_to_api(state.snapshot), + contract: toolchain_contract_to_api(state.contract), + projection_digest: state.projection_digest, + } +} + +fn config_content_type_from_api( + content_type: server_api::ConfigContentType, +) -> config_source::ConfigContentType { + match content_type { + server_api::ConfigContentType::Decodal => config_source::ConfigContentType::Decodal, + server_api::ConfigContentType::Text => config_source::ConfigContentType::Text, + } +} + +fn config_virtual_path(path: String) -> Result { + config_source::VirtualPath::parse(&path) + .map_err(|error| invalid_contract_value("Workspace config path", error)) +} + +fn config_commit_request_from_api( + request: server_api::ConfigCommitRequest, +) -> Result { + let changes = request + .changes + .into_iter() + .map(|change| { + Ok(match change { + server_api::ConfigTreeChange::Create { + path, + content_type, + content, + } => config_source::ConfigTreeChange::Create { + path: config_virtual_path(path)?, + content_type: config_content_type_from_api(content_type), + content, + }, + server_api::ConfigTreeChange::Update { + path, + expected_digest, + content, + } => config_source::ConfigTreeChange::Update { + path: config_virtual_path(path)?, + expected_digest, + content, + }, + server_api::ConfigTreeChange::Rename { + from, + to, + expected_digest, + } => config_source::ConfigTreeChange::Rename { + from: config_virtual_path(from)?, + to: config_virtual_path(to)?, + expected_digest, + }, + server_api::ConfigTreeChange::Delete { + path, + expected_digest, + } => config_source::ConfigTreeChange::Delete { + path: config_virtual_path(path)?, + expected_digest, + }, + }) + }) + .collect::>>()?; + let entrypoints = request + .entrypoints + .into_iter() + .map(config_virtual_path) + .collect::>>()?; + Ok(ConfigCommitRequest { + base_revision: request.base_revision, + base_digest: request.base_digest, + changes, + entrypoints, + }) +} + +fn prompt_projection_to_api( + projection: worker::WorkspacePromptProjection, +) -> server_api::WorkspacePromptProjection { + let catalog = projection.catalog; + server_api::WorkspacePromptProjection { + workspace_id: projection.workspace_id, + config_revision: projection.config_revision, + source_digest: projection.source_digest, + projection_digest: projection.projection_digest, + schema_fingerprint: projection.schema_fingerprint, + toolchain_fingerprint: projection.toolchain_fingerprint, + catalog: server_api::EffectivePromptCatalog { + templates: catalog.templates, + config_revision: catalog.config_revision, + source_digest: catalog.source_digest, + schema_fingerprint: catalog.schema_fingerprint, + toolchain_fingerprint: catalog.toolchain_fingerprint, + catalog_digest: catalog.catalog_digest, + }, + } +} + +fn flow_source_record_to_api(source: FlowSourceRecord) -> server_api::FlowSourceRecord { + server_api::FlowSourceRecord { + workspace_id: source.workspace_id, + flow_id: source.flow_id, + source_kind: match source.source_kind { + FlowSourceKind::Builtin => server_api::FlowSourceKind::Builtin, + FlowSourceKind::Workspace => server_api::FlowSourceKind::Workspace, + }, + name: source.name, + path: source.path, + content: source.content, + content_digest: source.content_digest, + revision: source.revision, + created_at: source.created_at, + updated_at: source.updated_at, + } +} + +fn flow_source_resolve_request_from_api( + request: server_api::FlowSourceResolveRequest, +) -> Result { + Ok(FlowSourceResolveRequest { + selector: request + .selector + .parse() + .map_err(|error| invalid_contract_value("Flow selector", error))?, + }) +} + +fn compiled_flow_definition_to_api( + definition: flow::CompiledFlowDefinition, +) -> server_api::CompiledFlowDefinition { + server_api::CompiledFlowDefinition { + schema_version: definition.schema_version, + name: definition.name, + initial: definition.initial.to_string(), + states: definition + .states + .into_iter() + .map(|(state_id, state)| { + ( + state_id.to_string(), + server_api::CompiledFlowState { + id: state.id.to_string(), + instructions: state.instructions, + terminal: state.terminal, + transitions: state + .transitions + .into_iter() + .map(|transition| server_api::CompiledFlowTransition { + id: transition.id.to_string(), + target: transition.target.to_string(), + condition: transition.condition, + synthetic: transition.synthetic, + }) + .collect(), + }, + ) + }) + .collect(), + content_digest: definition.content_digest, + } +} + +fn resolved_flow_source_to_api(source: ResolvedFlowSource) -> server_api::ResolvedFlowSource { + server_api::ResolvedFlowSource { + selector: source.selector.to_string(), + workspace_id: source.workspace_id, + flow_id: source.flow_id, + revision: source.revision, + content_digest: source.content_digest, + definition: compiled_flow_definition_to_api(source.definition), + } +} + async fn scoped_get_workspace_config_tree( State(api): State, AxumPath(path): AxumPath, @@ -9163,13 +9448,13 @@ async fn scoped_get_workspace_config_tree( ), projection_digest: config_source::digest_bytes(b"[]"), }); - Ok(Json(project_server_dto(&state)?)) + Ok(Json(workspace_config_state_to_api(state))) } async fn scoped_get_workspace_config_entry( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let virtual_path = config_source::VirtualPath::parse(&path.path) .map_err(|error| ApiError::from(Error::InvalidInput(error.to_string())))?; @@ -9184,14 +9469,15 @@ async fn scoped_get_workspace_config_entry( .get(&virtual_path) .cloned() .ok_or_else(|| ApiError::from(Error::InvalidRecordId(path.path)))?; - Ok(Json(entry)) + Ok(Json(config_entry_to_api(entry))) } async fn scoped_commit_workspace_config_tree( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, + Json(request): Json, ) -> ApiResult<(StatusCode, Json)> { + let request = config_commit_request_from_api(request)?; validate_workspace_scope(&api, &path.workspace_id)?; let candidate = api .config_store @@ -9218,7 +9504,10 @@ async fn scoped_commit_workspace_config_tree( .runtime .observe_workspace_prompt_projection((*projection).clone()); } - Ok((StatusCode::CREATED, Json(project_server_dto(&state)?))) + Ok(( + StatusCode::CREATED, + Json(workspace_config_state_to_api(state)), + )) } async fn scoped_get_profile_settings( @@ -9279,43 +9568,6 @@ async fn scoped_show_ticket( )?)) } -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -struct TicketRoleAssignmentsResponse { - workspace_id: String, - ticket_id: String, - assignments: Vec, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -struct TicketRoleAssignmentMutationResponse { - workspace_id: String, - ticket_id: String, - assignment: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct SetTicketRoleAssignmentRequest { - operation_id: String, - principal: TicketAssignmentPrincipal, - expected_assignment_id: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct CancelTicketImplementationRequest { - operation_id: String, - assignment_id: String, - reason: String, -} - -#[derive(Debug, Default, Deserialize)] -#[serde(deny_unknown_fields)] -struct ClearTicketRoleAssignmentQuery { - operation_id: Option, - assignment_id: Option, -} - fn parse_ticket_assignment_role(role: &str) -> ApiResult { match role { "orchestrator" => Ok(TicketAssignmentRole::Orchestrator), @@ -9326,27 +9578,89 @@ fn parse_ticket_assignment_role(role: &str) -> ApiResult { } } +fn project_ticket_role_assignment_record( + record: TicketRoleAssignmentRecord, +) -> server_api::TicketRoleAssignmentRecord { + let role = match record.role { + TicketAssignmentRole::Orchestrator => server_api::TicketAssignmentRole::Orchestrator, + TicketAssignmentRole::Coder => server_api::TicketAssignmentRole::Coder, + TicketAssignmentRole::Owner => server_api::TicketAssignmentRole::Owner, + TicketAssignmentRole::Contributor => server_api::TicketAssignmentRole::Contributor, + }; + let principal = match record.principal { + TicketAssignmentPrincipal::User { account_id } => { + server_api::TicketAssignmentPrincipal::User { account_id } + } + TicketAssignmentPrincipal::Worker { + runtime_id, + worker_id, + } => server_api::TicketAssignmentPrincipal::Worker { + runtime_id, + worker_id, + }, + TicketAssignmentPrincipal::WorkspaceAgent { agent_key } => { + server_api::TicketAssignmentPrincipal::WorkspaceAgent { agent_key } + } + }; + server_api::TicketRoleAssignmentRecord { + workspace_id: record.workspace_id, + ticket_id: record.ticket_id, + assignment_id: record.assignment_id, + role, + principal, + assigned_by: record.assigned_by, + assigned_at: record.assigned_at, + } +} + +fn internal_ticket_assignment_principal( + principal: server_api::TicketAssignmentPrincipal, +) -> ApiResult { + match principal { + server_api::TicketAssignmentPrincipal::User { .. } => Err( + Error::TicketAssignmentConflict( + "user-principal Ticket assignment requires an authenticated authoring boundary; weak Workspace Web access is not authority" + .to_string(), + ) + .into(), + ), + server_api::TicketAssignmentPrincipal::Worker { + runtime_id, + worker_id, + } => Ok(TicketAssignmentPrincipal::Worker { + runtime_id, + worker_id, + }), + server_api::TicketAssignmentPrincipal::WorkspaceAgent { agent_key } => { + Ok(TicketAssignmentPrincipal::WorkspaceAgent { agent_key }) + } + } +} + async fn scoped_list_ticket_assignments( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let ticket = api.authority.ticket(&path.id)?; let assignments = api .store .list_current_ticket_role_assignments(&path.workspace_id, &ticket.id)?; - Ok(Json(TicketRoleAssignmentsResponse { + Ok(Json(server_api::TicketRoleAssignmentsResponse { workspace_id: path.workspace_id, ticket_id: ticket.id, - assignments, + assignments: assignments + .into_iter() + .map(project_ticket_role_assignment_record) + .collect(), })) } async fn scoped_set_ticket_assignment( State(api): State, AxumPath((workspace_id, id, role)): AxumPath<(String, String, String)>, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &workspace_id)?; let ticket = api.authority.ticket(&id)?; let role = parse_ticket_assignment_role(&role)?; @@ -9355,20 +9669,14 @@ async fn scoped_set_ticket_assignment( .expected_assignment_id .map(|value| require_ticket_assignment_value("expected_assignment_id", value)) .transpose()?; - if matches!(request.principal, TicketAssignmentPrincipal::User { .. }) { - return Err(Error::TicketAssignmentConflict( - "user-principal Ticket assignment requires an authenticated authoring boundary; weak Workspace Web access is not authority" - .to_string(), - ) - .into()); - } + let principal = internal_ticket_assignment_principal(request.principal)?; let assigned_at = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true); let record = TicketRoleAssignmentRecord { workspace_id: workspace_id.clone(), ticket_id: ticket.id.clone(), assignment_id: new_id("tasg"), role, - principal: request.principal, + principal, assigned_by: "workspace-web".to_string(), assigned_at, }; @@ -9441,18 +9749,18 @@ async fn scoped_set_ticket_assignment( .into()); } }; - Ok(Json(TicketRoleAssignmentMutationResponse { + Ok(Json(server_api::TicketRoleAssignmentMutationResponse { workspace_id, ticket_id: ticket.id, - assignment: Some(assignment), + assignment: Some(project_ticket_role_assignment_record(assignment)), })) } async fn scoped_clear_ticket_assignment( State(api): State, AxumPath((workspace_id, id, role)): AxumPath<(String, String, String)>, - Query(query): Query, -) -> ApiResult> { + Query(query): Query, +) -> ApiResult> { validate_workspace_scope(&api, &workspace_id)?; let ticket = api.authority.ticket(&id)?; let role = parse_ticket_assignment_role(&role)?; @@ -9500,7 +9808,7 @@ async fn scoped_clear_ticket_assignment( )) .into()); } - Ok(Json(TicketRoleAssignmentMutationResponse { + Ok(Json(server_api::TicketRoleAssignmentMutationResponse { workspace_id, ticket_id: ticket.id, assignment: None, @@ -9510,7 +9818,7 @@ async fn scoped_clear_ticket_assignment( async fn scoped_cancel_ticket_implementation( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let ticket = api.authority.ticket(&path.id)?; @@ -9595,7 +9903,7 @@ async fn cancel_ticket_coder_worker( ticket_assignment: None, }, ) { - Ok(result) if result.state == WorkerOperationState::Accepted => {} + Ok(result) if result.state == InternalWorkerOperationState::Accepted => {} Ok(result) => { return Err(ApiError::with_diagnostics( Error::RuntimeOperationFailed { @@ -9845,7 +10153,7 @@ fn existing_lifecycle_assignment_worker( api: &WorkspaceApi, assignment: &crate::hosts::WorkerTicketAssignmentRequest, runtime_id: &str, -) -> Result> { +) -> Result> { let Some(operation) = api .store .get_ticket_assignment_operation(&api.config.workspace_id, &assignment.operation_id)? @@ -9886,66 +10194,6 @@ fn require_ticket_assignment_value(field: &str, value: String) -> Result Ok(value.to_string()) } -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct BrowserEditTicketRequest { - title: Option, - body: Option, - old_string: Option, - new_string: Option, - #[serde(default)] - replace_all: bool, - target: Option, - author: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct BrowserTransitionTicketStateRequest { - state: TicketWorkflowState, - reason: Option, - body: Option, - author: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "snake_case")] -enum BrowserTicketThreadRole { - Comment, - Plan, - Decision, - ImplementationReport, -} - -impl From for TicketEventKind { - fn from(role: BrowserTicketThreadRole) -> Self { - match role { - BrowserTicketThreadRole::Comment => Self::Comment, - BrowserTicketThreadRole::Plan => Self::Plan, - BrowserTicketThreadRole::Decision => Self::Decision, - BrowserTicketThreadRole::ImplementationReport => Self::ImplementationReport, - } - } -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct BrowserAppendTicketEventRequest { - role: BrowserTicketThreadRole, - body: String, - author: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct BrowserQueueTicketRequest {} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct BrowserCloseTicketRequest { - resolution: String, -} - #[derive(Clone)] struct WorkspaceTicketTargetAuthority { api: WorkspaceApi, @@ -10017,10 +10265,19 @@ fn browser_ticket_detail(api: &WorkspaceApi, ticket_id: &str) -> ApiResult, AxumPath(path): AxumPath, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; - let mut target = request.target; + let mut target = request.target.map(|target| match target { + server_api::BrowserTicketTargetEdit::Set { + repository_key, + ref_selector, + } => TicketTargetEdit::Set { + repository_id: repository_key, + ref_selector, + }, + server_api::BrowserTicketTargetEdit::Clear => TicketTargetEdit::Clear, + }); if let Some(TicketTargetEdit::Set { repository_id, .. }) = target.as_mut() { server_api::validate_repository_key(repository_id).map_err(|_| { settings_bad_request( @@ -10070,16 +10327,24 @@ async fn scoped_edit_ticket_item( async fn scoped_transition_ticket_state( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; - if request.state == TicketWorkflowState::Done { + let state = match request.state { + server_api::BrowserTicketWorkflowState::Planning => TicketWorkflowState::Planning, + server_api::BrowserTicketWorkflowState::Ready => TicketWorkflowState::Ready, + server_api::BrowserTicketWorkflowState::Queued => TicketWorkflowState::Queued, + server_api::BrowserTicketWorkflowState::Inprogress => TicketWorkflowState::InProgress, + server_api::BrowserTicketWorkflowState::Done => TicketWorkflowState::Done, + server_api::BrowserTicketWorkflowState::Closed => TicketWorkflowState::Closed, + }; + if state == TicketWorkflowState::Done { return Err(Error::TicketAssignmentConflict( "done is guarded by CompleteMergeRequest with an approved exact source ref and operation_id".to_string(), ).into()); } let current = api.authority.ticket(&path.id)?; - if request.state == TicketWorkflowState::InProgress + if state == TicketWorkflowState::InProgress && current.state != TicketWorkflowState::InProgress.as_str() { return Err(Error::TicketAssignmentConflict( @@ -10090,7 +10355,7 @@ async fn scoped_transition_ticket_state( } let mut change = TicketStateChange::new( current.state, - request.state.as_str(), + state.as_str(), request .reason .unwrap_or_else(|| "state changed from Web Ticket API".to_owned()), @@ -10106,10 +10371,18 @@ async fn scoped_transition_ticket_state( async fn scoped_append_ticket_event( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; - let mut event = NewTicketEvent::new(request.role.into(), request.body); + let role = match request.role { + server_api::BrowserTicketThreadRole::Comment => TicketEventKind::Comment, + server_api::BrowserTicketThreadRole::Plan => TicketEventKind::Plan, + server_api::BrowserTicketThreadRole::Decision => TicketEventKind::Decision, + server_api::BrowserTicketThreadRole::ImplementationReport => { + TicketEventKind::ImplementationReport + } + }; + let mut event = NewTicketEvent::new(role, request.body); event.author = request.author; browser_ticket_backend(&api)? .add_event(TicketIdOrSlug::Id(path.id.clone()), event) @@ -10120,7 +10393,7 @@ async fn scoped_append_ticket_event( async fn scoped_mark_ticket_ready_from_browser( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; browser_ticket_backend(&api)? @@ -10140,7 +10413,7 @@ async fn scoped_mark_ticket_ready_from_browser( async fn scoped_queue_ticket( State(api): State, AxumPath(path): AxumPath, - Json(_request): Json, + Json(_request): Json, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let result = execute_ticket_rest_operation( @@ -10162,7 +10435,7 @@ async fn scoped_queue_ticket( async fn scoped_close_ticket( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; browser_ticket_backend(&api)? @@ -10416,7 +10689,7 @@ fn ticket_rest_unit(result: TicketBackendOperationResult) -> ApiResult, AxumPath(path): AxumPath, headers: HeaderMap, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { let result = execute_ticket_rest_operation( &api, @@ -10439,17 +10712,13 @@ async fn scoped_default_intake_ready_body( }) } -#[derive(Debug, Default, Deserialize)] -struct TicketSummarySearchQuery { - state: Option, -} - async fn scoped_list_ticket_summaries( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, - Query(query): Query, + Query(query): Query, ) -> ApiResult>> { + let limit = query.limit.map(|limit| limit.min(1000)); let filter = match query.state.as_deref().unwrap_or("active") { "active" => ticket::TicketListQuery::active(), "all" => ticket::TicketListQuery::all(), @@ -10471,7 +10740,12 @@ async fn scoped_list_ticket_summaries( ) .await?; ticket_rest_result(result, |result| match result { - TicketBackendOperationResult::Tickets(tickets) => Some(tickets), + TicketBackendOperationResult::Tickets(mut tickets) => { + if let Some(limit) = limit { + tickets.truncate(limit); + } + Some(tickets) + } _ => None, }) } @@ -10624,7 +10898,7 @@ async fn scoped_add_ticket_intake_summary( } #[derive(Debug, Deserialize)] -struct TicketMarkReadyRequest { +struct InternalTicketMarkReadyRequest { operation_key: String, #[serde(default)] reason: Option, @@ -10675,7 +10949,7 @@ async fn scoped_mark_ticket_ready( State(api): State, AxumPath((workspace_id, id)): AxumPath<(String, String)>, headers: HeaderMap, - Json(request): Json, + Json(request): Json, ) -> ApiResult> { let result = execute_ticket_rest_operation( &api, @@ -10719,66 +10993,6 @@ async fn scoped_queue_ticket_record( }) } -#[derive(Debug, serde::Deserialize)] -struct OpenMergeRequestRequest { - repository_key: String, - selector_from: String, - selector_to: String, - #[serde(default)] - summary: String, -} - -#[derive(Debug, serde::Deserialize)] -struct RepairMergeRequestSelectorRequest { - selector_from: String, - reason: String, - explicit_confirmation: bool, -} - -#[derive(Debug, serde::Deserialize)] -struct RevokeMergeRequestReviewRequest { - review_event_id: String, - reason: String, - explicit_confirmation: bool, -} - -#[derive(Debug, serde::Deserialize)] -struct MergeRequestThreadQuery { - after: Option, - limit: Option, -} - -#[derive(Debug, serde::Deserialize)] -struct RegisterReviewerChildSessionRequest { - child_session_id: String, -} - -#[derive(Debug, serde::Deserialize)] -struct RegisterMergeRequestReviewCapabilityRequest { - child_session_id: String, - capability_token: String, -} - -#[derive(Debug, serde::Deserialize)] -struct SubmitMergeRequestReviewRequest { - capability_token: String, - decision: merge_request::ReviewDecision, - #[serde(default)] - body: String, - #[serde(default)] - findings: Vec, -} - -#[derive(Debug, serde::Deserialize)] -struct CompleteMergeRequestRequest { - operation_id: String, - approval_event_id: String, - target_ref_before: String, - target_ref_after: String, - strategy: merge_request::MergeStrategy, - resolution: merge_request::ConflictResolution, -} - fn parse_workspace_id(value: &str) -> ApiResult { if value.trim().is_empty() { return Err(Error::InvalidInput("workspace_id must not be empty".to_string()).into()); @@ -11154,84 +11368,234 @@ fn resolve_workspace_worker_ticket_assignment( Ok(()) } +fn public_merge_request_state( + state: merge_request::MergeRequestState, +) -> server_api::MergeRequestState { + match state { + merge_request::MergeRequestState::Open => server_api::MergeRequestState::Open, + merge_request::MergeRequestState::Merged => server_api::MergeRequestState::Merged, + merge_request::MergeRequestState::Closed => server_api::MergeRequestState::Closed, + } +} + +fn public_merge_request_worker_identity( + identity: merge_request::WorkerIdentity, +) -> server_api::MergeRequestWorkerIdentity { + server_api::MergeRequestWorkerIdentity { + runtime_id: identity.runtime_id, + worker_id: identity.worker_id, + } +} + +fn public_review_decision(decision: merge_request::ReviewDecision) -> server_api::ReviewDecision { + match decision { + merge_request::ReviewDecision::Approve => server_api::ReviewDecision::Approve, + merge_request::ReviewDecision::RequestChanges => server_api::ReviewDecision::RequestChanges, + } +} + +fn internal_review_decision(decision: server_api::ReviewDecision) -> merge_request::ReviewDecision { + match decision { + server_api::ReviewDecision::Approve => merge_request::ReviewDecision::Approve, + server_api::ReviewDecision::RequestChanges => merge_request::ReviewDecision::RequestChanges, + } +} + +fn public_review_finding(finding: merge_request::ReviewFinding) -> server_api::ReviewFinding { + let severity = match finding.severity { + merge_request::FindingSeverity::Blocker => server_api::FindingSeverity::Blocker, + merge_request::FindingSeverity::Major => server_api::FindingSeverity::Major, + merge_request::FindingSeverity::Minor => server_api::FindingSeverity::Minor, + merge_request::FindingSeverity::Note => server_api::FindingSeverity::Note, + }; + server_api::ReviewFinding { + severity, + code: finding.code, + path: finding.path, + line: finding.line, + body: finding.body, + } +} + +fn internal_review_finding(finding: server_api::ReviewFinding) -> merge_request::ReviewFinding { + let severity = match finding.severity { + server_api::FindingSeverity::Blocker => merge_request::FindingSeverity::Blocker, + server_api::FindingSeverity::Major => merge_request::FindingSeverity::Major, + server_api::FindingSeverity::Minor => merge_request::FindingSeverity::Minor, + server_api::FindingSeverity::Note => merge_request::FindingSeverity::Note, + }; + merge_request::ReviewFinding { + severity, + code: finding.code, + path: finding.path, + line: finding.line, + body: finding.body, + } +} + +fn public_review_event(event: merge_request::ReviewEvent) -> server_api::ReviewEvent { + server_api::ReviewEvent { + event_id: event.event_id, + sequence: event.sequence, + request_event_id: event.request_event_id, + subject_ref: event.subject_ref, + decision: public_review_decision(event.decision), + body: event.body, + findings: event + .findings + .into_iter() + .map(public_review_finding) + .collect(), + reviewer: public_merge_request_worker_identity(event.reviewer), + created_at: event.created_at.to_rfc3339(), + } +} + +fn public_review_revoked_event( + event: merge_request::ReviewRevokedEvent, +) -> server_api::ReviewRevokedEvent { + server_api::ReviewRevokedEvent { + event_id: event.event_id, + sequence: event.sequence, + review_event_id: event.review_event_id, + subject_ref: event.subject_ref, + reason: event.reason, + revoked_by: public_merge_request_worker_identity(event.revoked_by), + created_at: event.created_at.to_rfc3339(), + } +} + +fn public_merge_event(event: merge_request::MergeEvent) -> server_api::MergeEvent { + server_api::MergeEvent { + event_id: event.event_id, + sequence: event.sequence, + operation_id: event.operation_id, + approval_event_id: event.approval_event_id, + approved_source_ref: event.approved_source_ref, + target_ref_before: event.target_ref_before, + target_ref_after: event.target_ref_after, + strategy: public_merge_strategy(event.strategy), + resolution: public_conflict_resolution(event.resolution), + merged_by: public_merge_request_worker_identity(event.merged_by), + created_at: event.created_at.to_rfc3339(), + } +} + +fn public_merge_strategy(strategy: merge_request::MergeStrategy) -> server_api::MergeStrategy { + match strategy { + merge_request::MergeStrategy::FastForward => server_api::MergeStrategy::FastForward, + merge_request::MergeStrategy::Merge => server_api::MergeStrategy::Merge, + } +} + +fn internal_merge_strategy(strategy: server_api::MergeStrategy) -> merge_request::MergeStrategy { + match strategy { + server_api::MergeStrategy::FastForward => merge_request::MergeStrategy::FastForward, + server_api::MergeStrategy::Merge => merge_request::MergeStrategy::Merge, + } +} + +fn public_conflict_resolution( + resolution: merge_request::ConflictResolution, +) -> server_api::ConflictResolution { + match resolution { + merge_request::ConflictResolution::None => server_api::ConflictResolution::None, + merge_request::ConflictResolution::Clean => server_api::ConflictResolution::Clean, + merge_request::ConflictResolution::ConflictsResolved => { + server_api::ConflictResolution::ConflictsResolved + } + } +} + +fn internal_conflict_resolution( + resolution: server_api::ConflictResolution, +) -> merge_request::ConflictResolution { + match resolution { + server_api::ConflictResolution::None => merge_request::ConflictResolution::None, + server_api::ConflictResolution::Clean => merge_request::ConflictResolution::Clean, + server_api::ConflictResolution::ConflictsResolved => { + merge_request::ConflictResolution::ConflictsResolved + } + } +} + +fn public_merge_request_thread_event( + event: merge_request::MergeRequestThreadEvent, +) -> server_api::MergeRequestThreadEvent { + match event { + merge_request::MergeRequestThreadEvent::ReviewRequested(event) => { + server_api::MergeRequestThreadEvent::ReviewRequested(server_api::ReviewRequestedEvent { + event_id: event.event_id, + sequence: event.sequence, + subject_ref: event.subject_ref, + requested_by: public_merge_request_worker_identity(event.requested_by), + reviewer: public_merge_request_worker_identity(event.reviewer), + created_at: event.created_at.to_rfc3339(), + }) + } + merge_request::MergeRequestThreadEvent::Review(event) => { + server_api::MergeRequestThreadEvent::Review(public_review_event(event)) + } + merge_request::MergeRequestThreadEvent::ReviewRevoked(event) => { + server_api::MergeRequestThreadEvent::ReviewRevoked(public_review_revoked_event(event)) + } + merge_request::MergeRequestThreadEvent::ReviewCancelled(event) => { + server_api::MergeRequestThreadEvent::ReviewCancelled(server_api::ReviewCancelledEvent { + event_id: event.event_id, + sequence: event.sequence, + request_event_id: event.request_event_id, + subject_ref: event.subject_ref, + reason: event.reason, + created_at: event.created_at.to_rfc3339(), + }) + } + merge_request::MergeRequestThreadEvent::Comment(event) => { + server_api::MergeRequestThreadEvent::Comment(server_api::MergeRequestCommentEvent { + event_id: event.event_id, + sequence: event.sequence, + body: event.body, + author: public_merge_request_worker_identity(event.author), + created_at: event.created_at.to_rfc3339(), + }) + } + merge_request::MergeRequestThreadEvent::Merge(event) => { + server_api::MergeRequestThreadEvent::Merge(public_merge_event(event)) + } + } +} + fn public_merge_request( api: &WorkspaceApi, workspace_id: &str, mr: merge_request::MergeRequest, -) -> ApiResult { +) -> ApiResult { let repository_key = api .store .get_repository(workspace_id, &mr.repository_id)? .map(|repository| repository.repository_key) .ok_or_else(|| ApiError::from(Error::UnknownRepository(mr.repository_id.clone())))?; - Ok(PublicMergeRequest { + Ok(server_api::PublicMergeRequest { workspace_id: mr.workspace_id, merge_request_id: mr.merge_request_id, repository_key, - state: mr.state, + state: public_merge_request_state(mr.state), selector_from: mr.selector_from, selector_to: mr.selector_to, ticket_ids: mr.ticket_ids, - created_at: mr.created_at, - updated_at: mr.updated_at, - thread: mr.thread, + created_at: mr.created_at.to_rfc3339(), + updated_at: mr.updated_at.to_rfc3339(), + thread: mr + .thread + .into_iter() + .map(public_merge_request_thread_event) + .collect(), }) } -#[derive(Debug, serde::Deserialize)] -struct MergeRequestListHttpQuery { - state: Option, - repository_key: Option, - ticket_ref: Option, - selector_from: Option, - selector_to: Option, - cursor: Option, - limit: Option, -} - -#[derive(Debug, serde::Serialize)] -struct MergeRequestRefResponse { - status: String, - #[serde(rename = "ref")] - revision_ref: Option, - observed_at: String, - #[serde(skip_serializing_if = "Option::is_none")] - diagnostic: Option, -} - -#[derive(Debug, serde::Serialize)] -struct MergeRequestLinkedTicketResponse { - ticket_id: String, - key: Option, -} - -#[derive(Debug, serde::Serialize)] -struct MergeRequestDetailResponse { - #[serde(flatten)] - merge_request: PublicMergeRequest, - source: MergeRequestRefResponse, - target: MergeRequestRefResponse, - linked_tickets: Vec, -} - -#[derive(Debug, Clone, serde::Serialize)] -struct PublicMergeRequest { - workspace_id: String, - merge_request_id: String, - repository_key: String, - state: merge_request::MergeRequestState, - selector_from: Option, - selector_to: String, - ticket_ids: Vec, - created_at: chrono::DateTime, - updated_at: chrono::DateTime, - thread: Vec, -} - async fn scoped_list_merge_requests( State(api): State, AxumPath(workspace_id): AxumPath, - Query(query): Query, + Query(query): Query, ) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; require_workspace_access(&workspace_id, &api)?; @@ -11354,8 +11718,8 @@ fn merge_ref_diagnostic(error: ApiError) -> MergeRequestRefDiagnostic { }) } -fn unknown_merge_ref(code: &str, message: &str) -> MergeRequestRefResponse { - MergeRequestRefResponse { +fn unknown_merge_ref(code: &str, message: &str) -> server_api::MergeRequestRefResponse { + server_api::MergeRequestRefResponse { status: "unknown".to_string(), revision_ref: None, observed_at: Utc::now().to_rfc3339(), @@ -11366,8 +11730,8 @@ fn unknown_merge_ref(code: &str, message: &str) -> MergeRequestRefResponse { } } -fn unknown_merge_ref_response(error: ApiError) -> MergeRequestRefResponse { - MergeRequestRefResponse { +fn unknown_merge_ref_response(error: ApiError) -> server_api::MergeRequestRefResponse { + server_api::MergeRequestRefResponse { status: "unknown".to_string(), revision_ref: None, observed_at: Utc::now().to_rfc3339(), @@ -11375,12 +11739,14 @@ fn unknown_merge_ref_response(error: ApiError) -> MergeRequestRefResponse { } } -fn merge_ref_response(observation: RepositoryRefObservation) -> MergeRequestRefResponse { +fn merge_ref_response( + observation: RepositoryRefObservation, +) -> server_api::MergeRequestRefResponse { let observed_at = chrono::DateTime::::from_timestamp(observation.observed_at_epoch_seconds as i64, 0) .unwrap_or_else(Utc::now) .to_rfc3339(); - MergeRequestRefResponse { + server_api::MergeRequestRefResponse { status: "known".into(), revision_ref: Some(observation.revision_ref), observed_at, @@ -11391,8 +11757,8 @@ fn merge_ref_response(observation: RepositoryRefObservation) -> MergeRequestRefR async fn scoped_show_merge_request( State(api): State, AxumPath((workspace_id, merge_request_id)): AxumPath<(String, String)>, - Query(query): Query, -) -> ApiResult> { + Query(query): Query, +) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; require_workspace_access(&workspace_id, &api)?; let store = merge_request_store(&api, &workspace_id)?; @@ -11428,7 +11794,7 @@ async fn scoped_show_merge_request( "source_ref_runtime_unavailable", "No current Coder Runtime is available to observe the source ref", ), - (None, _) => MergeRequestRefResponse { + (None, _) => server_api::MergeRequestRefResponse { status: "requires_repair".into(), revision_ref: None, observed_at: Utc::now().to_rfc3339(), @@ -11455,7 +11821,7 @@ async fn scoped_show_merge_request( .ticket_ids .iter() .map(|ticket_id| { - Ok(MergeRequestLinkedTicketResponse { + Ok(server_api::MergeRequestLinkedTicketResponse { ticket_id: ticket_id.clone(), key: api.store.resource_key( &workspace_id, @@ -11466,7 +11832,7 @@ async fn scoped_show_merge_request( }) .collect::>>()?; let merge_request = public_merge_request(&api, &workspace_id, mr)?; - Ok(Json(MergeRequestDetailResponse { + Ok(Json(server_api::MergeRequestDetailResponse { merge_request, source, target, @@ -11477,7 +11843,7 @@ async fn scoped_show_merge_request( async fn scoped_merge_request_readiness( State(api): State, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, -) -> ApiResult> { +) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; let store = merge_request_store(&api, &workspace_id)?; @@ -11523,15 +11889,20 @@ async fn scoped_merge_request_readiness( } report.ready = false; } - Ok(Json(report)) + Ok(Json(server_api::MergeRequestReadinessResponse { + ready: report.ready, + blockers: report.blockers, + subject_ref: report.subject_ref, + review: report.review.map(public_review_event), + })) } async fn scoped_open_merge_request( State(api): State, headers: HeaderMap, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, - Json(input): Json, -) -> ApiResult> { + Json(input): Json, +) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; require_workspace_access(&workspace_id, &api)?; @@ -11612,17 +11983,21 @@ async fn scoped_open_merge_request( async fn scoped_merge_request_thread( State(api): State, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, - Query(query): Query, -) -> ApiResult>> { + Query(query): Query, +) -> ApiResult>> { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; + let events = merge_request_store(&api, &workspace_id)?.thread_page( + &workspace_id, + &ticket_id, + query.after, + query.limit.unwrap_or(100), + )?; Ok(Json( - merge_request_store(&api, &workspace_id)?.thread_page( - &workspace_id, - &ticket_id, - query.after, - query.limit.unwrap_or(100), - )?, + events + .into_iter() + .map(public_merge_request_thread_event) + .collect(), )) } @@ -11630,8 +12005,8 @@ async fn scoped_repair_merge_request_selector( State(api): State, headers: HeaderMap, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, - Json(input): Json, -) -> ApiResult> { + Json(input): Json, +) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; require_workspace_access(&workspace_id, &api)?; @@ -11675,7 +12050,7 @@ async fn scoped_register_reviewer_child_session( State(api): State, headers: HeaderMap, AxumPath(workspace_id): AxumPath, - Json(input): Json, + Json(input): Json, ) -> ApiResult { let workspace_id = parse_workspace_id(&workspace_id)?; require_workspace_access(&workspace_id, &api)?; @@ -11697,7 +12072,7 @@ async fn scoped_register_merge_request_review_capability( State(api): State, headers: HeaderMap, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, - Json(input): Json, + Json(input): Json, ) -> ApiResult { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; @@ -11758,8 +12133,8 @@ async fn scoped_register_merge_request_review_capability( async fn scoped_submit_merge_request_review( State(api): State, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, - Json(input): Json, -) -> ApiResult> { + Json(input): Json, +) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; let store = merge_request_store(&api, &workspace_id)?; @@ -11792,25 +12167,30 @@ async fn scoped_submit_merge_request_review( selector, )? .revision_ref; - Ok(Json(store.submit_review( - merge_request::SubmitMergeRequestReview { - ticket_id, - current_subject_ref, - capability_token: input.capability_token, - decision: input.decision, - body: input.body, - findings: input.findings, - now: Utc::now(), - }, - )?)) + let decision = internal_review_decision(input.decision); + let findings = input + .findings + .into_iter() + .map(internal_review_finding) + .collect(); + let event = store.submit_review(merge_request::SubmitMergeRequestReview { + ticket_id, + current_subject_ref, + capability_token: input.capability_token, + decision, + body: input.body, + findings, + now: Utc::now(), + })?; + Ok(Json(public_review_event(event))) } async fn scoped_revoke_merge_request_review( State(api): State, headers: HeaderMap, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, - Json(input): Json, -) -> ApiResult> { + Json(input): Json, +) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; require_workspace_access(&workspace_id, &api)?; @@ -11834,29 +12214,28 @@ async fn scoped_revoke_merge_request_review( } let store = merge_request_store(&api, &workspace_id)?; let mr = store.get(&workspace_id, &ticket_id)?; - Ok(Json(store.revoke_review( - merge_request::RevokeMergeRequestReview { - ticket_id, - review_event_id: input.review_event_id, - reason: input.reason, - auth: merge_request::MergeRequestAuth { - workspace_id, - repository_id: mr.repository_id, - runtime_id: source.runtime_id, - worker_id: source.worker_id, - assignment_id: assignment.assignment_id, - }, - now: Utc::now(), + let event = store.revoke_review(merge_request::RevokeMergeRequestReview { + ticket_id, + review_event_id: input.review_event_id, + reason: input.reason, + auth: merge_request::MergeRequestAuth { + workspace_id, + repository_id: mr.repository_id, + runtime_id: source.runtime_id, + worker_id: source.worker_id, + assignment_id: assignment.assignment_id, }, - )?)) + now: Utc::now(), + })?; + Ok(Json(public_review_revoked_event(event))) } async fn scoped_complete_merge_request( State(api): State, headers: HeaderMap, AxumPath((workspace_id, ticket_id)): AxumPath<(String, String)>, - Json(input): Json, -) -> ApiResult> { + Json(input): Json, +) -> ApiResult> { let workspace_id = parse_workspace_id(&workspace_id)?; let ticket_id = resolve_workspace_ticket_reference(&api, &workspace_id, &ticket_id)?; require_workspace_access(&workspace_id, &api)?; @@ -11872,8 +12251,8 @@ async fn scoped_complete_merge_request( current_subject_ref: existing.approved_source_ref.clone(), target_ref_before: input.target_ref_before, target_ref_after: input.target_ref_after, - strategy: input.strategy, - resolution: input.resolution, + strategy: internal_merge_strategy(input.strategy), + resolution: internal_conflict_resolution(input.resolution), auth: merge_request::MergeRequestAuth { workspace_id, repository_id: mr.repository_id.clone(), @@ -11883,7 +12262,11 @@ async fn scoped_complete_merge_request( }, now: Utc::now(), }; - return store.complete(replay).map(Json).map_err(Into::into); + return store + .complete(replay) + .map(public_merge_event) + .map(Json) + .map_err(Into::into); } let assignment = api .store @@ -11922,8 +12305,8 @@ async fn scoped_complete_merge_request( current_subject_ref: current_source_ref, target_ref_before: input.target_ref_before.clone(), target_ref_after: input.target_ref_after.clone(), - strategy: input.strategy, - resolution: input.resolution, + strategy: internal_merge_strategy(input.strategy), + resolution: internal_conflict_resolution(input.resolution), auth: merge_request::MergeRequestAuth { workspace_id, repository_id: mr.repository_id.clone(), @@ -11934,7 +12317,11 @@ async fn scoped_complete_merge_request( now: Utc::now(), }; store.validate_completion(&completion)?; - store.complete(completion).map(Json).map_err(Into::into) + store + .complete(completion) + .map(public_merge_event) + .map(Json) + .map_err(Into::into) } fn reject_non_browser_reopen_auth(headers: &HeaderMap) -> Result<()> { @@ -11985,17 +12372,11 @@ async fn scoped_record_ticket_relation( }) } -#[derive(Debug, Deserialize)] -struct TicketRelationRemoveRequest { - kind: ticket::TicketRelationKind, - target: String, -} - async fn scoped_remove_ticket_relation( State(api): State, AxumPath((workspace_id, id)): AxumPath<(String, String)>, headers: HeaderMap, - Json(relation): Json, + Json(relation): Json, ) -> ApiResult> { let result = execute_ticket_rest_operation( &api, @@ -12014,17 +12395,11 @@ async fn scoped_remove_ticket_relation( }) } -#[derive(Debug, Deserialize)] -struct TicketRelationSearchRequest { - ticket: Option, - kind: Option, -} - async fn scoped_query_ticket_relations( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, - Json(query): Json, + Json(query): Json, ) -> ApiResult>> { let result = execute_ticket_rest_operation( &api, @@ -12084,17 +12459,11 @@ async fn scoped_record_ticket_orchestration_plan( }) } -#[derive(Debug, Deserialize)] -struct TicketOrchestrationPlanSearchRequest { - ticket: Option, - kind: Option, -} - async fn scoped_query_ticket_orchestration_plans( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, - Json(query): Json, + Json(query): Json, ) -> ApiResult>> { let result = execute_ticket_rest_operation( &api, @@ -12411,9 +12780,9 @@ fn ticket_notification_delivery_error_category( ) -> Option<&'static str> { match result { Ok(result) => match result.state { - WorkerOperationState::Accepted => None, - WorkerOperationState::Rejected => Some("runtime_rejected"), - WorkerOperationState::Unsupported => Some("runtime_unsupported"), + InternalWorkerOperationState::Accepted => None, + InternalWorkerOperationState::Rejected => Some("runtime_rejected"), + InternalWorkerOperationState::Unsupported => Some("runtime_unsupported"), }, Err(RuntimeRegistryError::InvalidIdentifier { .. }) => Some("invalid_identifier"), Err(RuntimeRegistryError::UnknownRuntime(_)) => Some("unknown_runtime"), @@ -12807,7 +13176,7 @@ fn sync_runtime_worker_workdir_attachments( .runtime .replace_worker_workdir_attachments(worker, attachments) .map_err(RuntimeRegistryError::into_error)?; - if result.state != WorkerOperationState::Accepted { + if result.state != InternalWorkerOperationState::Accepted { return Err(Error::RuntimeOperationFailed { runtime_id: worker.runtime_id.clone(), code: "worker_workdir_attachments_replace_failed".to_string(), @@ -12844,7 +13213,7 @@ async fn scoped_attach_current_worker_workdir( AxumPath(path): AxumPath, headers: HeaderMap, Json(request): Json, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let worker = current_worker_identity(&api, &path.workspace_id, &headers)?; let alias = workdir::WorkdirAttachmentAlias::new(request.alias) @@ -12937,7 +13306,7 @@ async fn scoped_attach_current_worker_workdir( api.worker_projection .refresh(&worker) .map_err(ApiError::from)?; - return Ok(Json(CurrentWorkerWorkdirAttachmentResponse { + return Ok(Json(server_api::CurrentWorkerWorkdirAttachmentResponse { workspace_id: api.config.workspace_id.clone(), alias: alias.to_string(), working_directory_id: workdir_id.to_string(), @@ -12996,7 +13365,7 @@ async fn scoped_attach_current_worker_workdir( refresh_current_worker_session_locked(&api, &worker).await?; return Err(error); } - Ok(Json(CurrentWorkerWorkdirAttachmentResponse { + Ok(Json(server_api::CurrentWorkerWorkdirAttachmentResponse { workspace_id: api.config.workspace_id.clone(), alias: alias.to_string(), working_directory_id: workdir_id.to_string(), @@ -13009,7 +13378,7 @@ async fn scoped_detach_current_worker_workdir( State(api): State, AxumPath((workspace_id, alias)): AxumPath<(String, String)>, headers: HeaderMap, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &workspace_id)?; let alias = workdir::WorkdirAttachmentAlias::new(alias) .map_err(|error| Error::InvalidInput(error.to_string()))?; @@ -13069,7 +13438,7 @@ async fn scoped_detach_current_worker_workdir( refresh_current_worker_session_locked(&api, &worker).await?; return Err(error); } - Ok(Json(CurrentWorkerWorkdirAttachmentResponse { + Ok(Json(server_api::CurrentWorkerWorkdirAttachmentResponse { workspace_id: api.config.workspace_id.clone(), alias: alias.to_string(), working_directory_id: link.workdir_id, @@ -13121,30 +13490,30 @@ fn validated_current_worker_attachment( } #[derive(Debug)] -enum WorkdirOperationApiError { +enum InternalWorkdirOperationError { Api(ApiError), Provider(WorkdirTransportError), } -impl From for WorkdirOperationApiError { +impl From for InternalWorkdirOperationError { fn from(error: ApiError) -> Self { Self::Api(error) } } -impl From for WorkdirOperationApiError { +impl From for InternalWorkdirOperationError { fn from(error: Error) -> Self { Self::Api(error.into()) } } -impl From for WorkdirOperationApiError { +impl From for InternalWorkdirOperationError { fn from(error: WorkdirTransportError) -> Self { Self::Provider(error) } } -impl IntoResponse for WorkdirOperationApiError { +impl IntoResponse for InternalWorkdirOperationError { fn into_response(self) -> Response { match self { Self::Api(error) => error.into_response(), @@ -13169,7 +13538,7 @@ async fn scoped_execute_current_worker_workdir_operation( AxumPath(path): AxumPath, headers: HeaderMap, Json(request): Json, -) -> std::result::Result, WorkdirOperationApiError> { +) -> std::result::Result, InternalWorkdirOperationError> { validate_workspace_scope(&api, &path.workspace_id)?; let worker = current_worker_identity(&api, &path.workspace_id, &headers)?; let target_workdir = request.target_workdir; @@ -13260,7 +13629,7 @@ fn current_worker_command_session( worker: &RuntimeWorkerRef, target_workdir: &str, external_handle: &CommandHandle, -) -> std::result::Result<(WorkdirSessionHandle, CommandHandle), WorkdirOperationApiError> { +) -> std::result::Result<(WorkdirSessionHandle, CommandHandle), InternalWorkdirOperationError> { let _link = validated_current_worker_attachment(api, worker, target_workdir)?; let command = api .workdir_sessions @@ -13268,7 +13637,7 @@ fn current_worker_command_session( .expect("Workdir session registry lock poisoned") .command(worker, target_workdir, external_handle) .ok_or_else(|| { - WorkdirOperationApiError::Provider(current_worker_workdir_operation_error( + InternalWorkdirOperationError::Provider(current_worker_workdir_operation_error( worker, workdir::WorkdirError::UnknownCommand(external_handle.0.clone()), )) @@ -13529,7 +13898,7 @@ fn dispatch_orchestrator_queue_attention(api: &WorkspaceApi) { segments: None, }, ) - .is_ok_and(|result| result.state == WorkerOperationState::Accepted); + .is_ok_and(|result| result.state == InternalWorkerOperationState::Accepted); if accepted { *api.orchestrator_attention_fingerprint .lock() @@ -13576,7 +13945,7 @@ fn require_online_workspace_orchestrator_source( Ok(()) } -fn find_online_workspace_orchestrator(api: &WorkspaceApi) -> Option { +fn find_online_workspace_orchestrator(api: &WorkspaceApi) -> Option { api.runtime .list_workers(1000) .items @@ -13590,8 +13959,8 @@ fn find_online_workspace_orchestrator(api: &WorkspaceApi) -> Option Option { - let is_orchestrator = |worker: &WorkerSummary| { +fn find_workspace_orchestrator(api: &WorkspaceApi) -> Option { + let is_orchestrator = |worker: &InternalWorkerSummary| { worker.singleton_key.as_deref() == Some(crate::hosts::WORKSPACE_ORCHESTRATOR_SINGLETON_KEY) }; if let Some(worker) = find_online_workspace_orchestrator(api) { @@ -13628,12 +13997,14 @@ async fn scoped_get_memory_document( async fn scoped_list_memory_staging( State(api): State, AxumPath(path): AxumPath, - Query(query): Query, + Query(query): Query, ) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; Ok(Json(list_memory_staging_from_authority( &api.authority, - query.limit, + query.limit.map(usize::try_from).transpose().map_err(|_| { + Error::InvalidInput("Memory staging limit exceeds this host's usize range".to_string()) + })?, )?)) } @@ -13733,7 +14104,7 @@ fn start_memory_staging_consolidation( resolved_control_operation: None, }, )?; - if result.state != WorkerOperationState::Accepted { + if result.state != InternalWorkerOperationState::Accepted { return Ok(MemoryConsolidationOutput { status: "skipped_spawn_rejected".to_string(), summary: "Runtime rejected Memory consolidater spawn.".to_string(), @@ -13808,7 +14179,7 @@ fn try_reuse_memory_consolidation_worker( }, ) .map_err(|err| err.into_error())?; - if input.state != WorkerOperationState::Accepted { + if input.state != InternalWorkerOperationState::Accepted { return Ok(Some(MemoryConsolidationOutput { status: "skipped_existing_input_rejected".to_string(), summary: format!( @@ -13830,7 +14201,7 @@ fn try_reuse_memory_consolidation_worker( })) } -fn is_memory_consolidation_worker(worker: &WorkerSummary) -> bool { +fn is_memory_consolidation_worker(worker: &InternalWorkerSummary) -> bool { worker.singleton_key.as_deref() == Some(MEMORY_CONSOLIDATION_SINGLETON_KEY) || worker.profile.as_deref() == Some(MEMORY_CONSOLIDATION_PROFILE) } @@ -13948,7 +14319,7 @@ fn skill_api_error(error: skills::SkillError) -> ApiError { }, vec![RuntimeDiagnostic { code: "skill_not_found".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: format!("unknown Skill `{name}`"), }], ), @@ -13966,8 +14337,8 @@ fn skill_api_error(error: skills::SkillError) -> ApiError { async fn scoped_list_objectives( State(api): State, AxumPath(path): AxumPath, - Query(query): Query, -) -> ApiResult>> { + Query(query): Query, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; list_objectives(State(api), Query(query)).await } @@ -14322,7 +14693,7 @@ async fn scoped_repository_log( async fn scoped_list_hosts( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult>> { +) -> ApiResult>> { validate_workspace_scope(&api, &path.workspace_id)?; list_hosts(State(api)).await } @@ -14425,7 +14796,7 @@ async fn get_latest_workspace_runtime_config( async fn scoped_list_runtimes( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult>> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; Ok(Json( workspace_runtime_resources_response(&api, &api.config.workspace_id).await?, @@ -14771,7 +15142,7 @@ fn workspace_worker_discovery_filter_fingerprint(query: Option<&str>) -> u64 { async fn scoped_list_workers( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult>> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; list_workers(State(api)).await } @@ -14784,27 +15155,11 @@ async fn scoped_workspace_orchestrator_status( Ok(Json(workspace_orchestrator_response(&api, "observed"))) } -#[derive(Debug, Serialize, Deserialize)] -struct KnownWorkerRecord { - subject: RuntimeWorkerRef, - relation: String, - origin: String, - permissions: Vec, - summary: WorkerSummary, -} - -#[derive(Debug, Serialize, Deserialize)] -struct KnownWorkersResponse { - workspace_id: String, - items: Vec, - truncated: bool, -} - async fn list_known_workers( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let source = authenticate_worker_mutation_source(&api, &path.workspace_id, &headers)?; let controller = RuntimeWorkerRef::new(&source.runtime_id, &source.worker_id); @@ -14819,15 +15174,18 @@ async fn list_known_workers( .runtime .worker(&grant.subject) .map_err(|error| error.into_error())?; - items.push(KnownWorkerRecord { - subject: grant.subject, + items.push(server_api::WorkerControlRecord { + subject: server_api::WorkerControlSubject { + runtime_id: grant.subject.runtime_id, + worker_id: grant.subject.worker_id, + }, relation: grant.relation, origin: grant.origin, permissions: grant.permissions, - summary, + summary: worker_launch_worker_summary(summary), }); } - Ok(Json(KnownWorkersResponse { + Ok(Json(server_api::WorkerControlListResponse { workspace_id: path.workspace_id, items, truncated, @@ -14959,14 +15317,15 @@ async fn send_known_worker_input( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let source = authenticate_worker_mutation_source(&api, &path.workspace_id, &headers)?; let controller = RuntimeWorkerRef::new(&source.runtime_id, &source.worker_id); - let permission = match request.kind { - WorkerInputKind::Notify => "notify", - _ => "send_input", + let permission = if request.kind.as_deref() == Some("notify") { + "notify" + } else { + "send_input" }; let grant = authorize_known_worker_permission( &api, @@ -14991,8 +15350,8 @@ async fn cancel_known_worker( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let source = authenticate_worker_mutation_source(&api, &path.workspace_id, &headers)?; let controller = RuntimeWorkerRef::new(&source.runtime_id, &source.worker_id); @@ -15019,8 +15378,8 @@ async fn stop_known_worker( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let source = authenticate_worker_mutation_source(&api, &path.workspace_id, &headers)?; let subject = path.worker.clone(); @@ -15052,14 +15411,22 @@ async fn restore_known_worker( let lock = worker_control_lock(&api, &grant.grant_id); let _guard = lock.lock().await; authorize_known_worker_permission(&api, &path.workspace_id, &controller, &subject, "restore")?; - scoped_restore_runtime_worker(State(api), AxumPath(path), Query(Default::default())).await + scoped_restore_runtime_worker( + State(api), + AxumPath(path), + Query(RestoreTicketAssignmentQuery { + ticket_id: None, + assignment_operation_id: None, + }), + ) + .await } async fn scoped_list_worker_observation_sessions( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let source = authenticate_worker_mutation_source(&api, &path.workspace_id, &headers)?; let controller = RuntimeWorkerRef::new(&source.runtime_id, &source.worker_id); @@ -15081,8 +15448,8 @@ async fn scoped_list_worker_observation_sessions( ) { return None; } - Some(WorkerObservationSubject { - subject: WorkerObservationSubjectRef::RuntimeWorker { + Some(server_api::WorkerObservationSubject { + subject: server_api::WorkerObservationSubjectRef::RuntimeWorker { runtime_id: grant.subject.runtime_id, worker_id: grant.subject.worker_id, }, @@ -15092,18 +15459,20 @@ async fn scoped_list_worker_observation_sessions( }) }) .collect::>(); - Ok(Json(serde_json::json!({ "sessions": sessions }))) + Ok(Json(server_api::WorkerObservationSessionsResponse { + sessions, + })) } async fn scoped_capture_worker_observation_session( State(api): State, AxumPath(path): AxumPath, headers: HeaderMap, - Json(subject): Json, -) -> ApiResult> { + Json(subject): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; let source = authenticate_worker_mutation_source(&api, &path.workspace_id, &headers)?; - let WorkerObservationSubjectRef::RuntimeWorker { + let server_api::WorkerObservationSubjectRef::RuntimeWorker { runtime_id, worker_id, } = subject @@ -15159,10 +15528,10 @@ async fn scoped_capture_worker_observation_session( message: "worker protocol did not begin with a committed session snapshot".to_string(), })); }; - Ok(Json(serde_json::json!({ - "segment_id": format!("runtime:{}:worker:{}", target.runtime_id, target.worker_id), - "session": session, - }))) + Ok(Json(server_api::WorkerObservationCaptureResponse { + segment_id: format!("runtime:{}:worker:{}", target.runtime_id, target.worker_id), + session, + })) } fn diagnostics_indicate_unrecoverable_pending_workspace_restore( @@ -15222,7 +15591,7 @@ async fn scoped_start_workspace_orchestrator( .runtime .delete_worker(&existing.worker) .map_err(|error| error.into_error())?; - if deleted.state != WorkerOperationState::Accepted { + if deleted.state != InternalWorkerOperationState::Accepted { return Err(ApiError::with_diagnostics( Error::RuntimeOperationFailed { runtime_id: existing.worker.runtime_id.clone(), @@ -15259,7 +15628,7 @@ async fn scoped_start_workspace_orchestrator( resolved_control_operation: None, }, )?; - if result.state != WorkerOperationState::Accepted || result.worker.is_none() { + if result.state != InternalWorkerOperationState::Accepted || result.worker.is_none() { return Err(ApiError::with_diagnostics( Error::RuntimeOperationFailed { runtime_id: EMBEDDED_WORKER_RUNTIME_ID.to_string(), @@ -15284,7 +15653,7 @@ async fn scoped_start_workspace_orchestrator( Ok(Json(workspace_orchestrator_response(&api, disposition))) } -fn worker_launch_worker_summary(worker: WorkerSummary) -> WorkerLaunchWorkerSummary { +fn worker_launch_worker_summary(worker: InternalWorkerSummary) -> WorkerLaunchWorkerSummary { WorkerLaunchWorkerSummary { runtime_id: worker.worker.runtime_id, worker_id: worker.worker.worker_id, @@ -15357,7 +15726,7 @@ fn workspace_orchestrator_response( } } -fn workspace_orchestrator_is_online(worker: &WorkerSummary) -> bool { +fn workspace_orchestrator_is_online(worker: &InternalWorkerSummary) -> bool { matches!(worker.state.as_str(), "idle" | "running" | "paused") } @@ -15387,9 +15756,9 @@ fn working_directory_diagnostics( .map(|diagnostic| server_api::Diagnostic { code: diagnostic.code, severity: match diagnostic.severity { - DiagnosticSeverity::Info => server_api::DiagnosticSeverity::Info, - DiagnosticSeverity::Warning => server_api::DiagnosticSeverity::Warning, - DiagnosticSeverity::Error => server_api::DiagnosticSeverity::Error, + HostDiagnosticSeverity::Info => server_api::DiagnosticSeverity::Info, + HostDiagnosticSeverity::Warning => server_api::DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Error => server_api::DiagnosticSeverity::Error, }, message: diagnostic.message, }) @@ -16535,7 +16904,7 @@ fn workdir_rejection_failure_code(diagnostics: &[RuntimeDiagnostic]) -> String { diagnostics .iter() .find(|diagnostic| { - diagnostic.severity == DiagnosticSeverity::Error + diagnostic.severity == HostDiagnosticSeverity::Error && !diagnostic.code.is_empty() && diagnostic.code.len() <= 128 && diagnostic @@ -16697,7 +17066,7 @@ async fn create_workspace_working_directory( ), vec![RuntimeDiagnostic { code: "default_runtime_not_configured".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Workspace default Runtime is not configured".to_string(), }], ) @@ -16797,7 +17166,7 @@ async fn create_workspace_working_directory( Error::UnknownRuntime(reserved.resolved_runtime_id), vec![RuntimeDiagnostic { code: "runtime_unavailable".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Selected Runtime is not available".to_string(), }], )); @@ -16820,7 +17189,7 @@ async fn create_workspace_working_directory( }, vec![RuntimeDiagnostic { code: "runtime_unavailable".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Selected Runtime is not available".to_string(), }], )); @@ -16862,7 +17231,7 @@ async fn create_workspace_working_directory( error.into_error(), vec![RuntimeDiagnostic { code: failure_code, - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Selected Runtime could not inspect the reserved Workdir".to_string(), }], )); @@ -16893,7 +17262,7 @@ async fn create_workspace_working_directory( error.into_error(), vec![RuntimeDiagnostic { code: failure_code, - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Selected Runtime rejected Workdir creation".to_string(), }], )); @@ -16914,13 +17283,13 @@ async fn create_workspace_working_directory( &now_registry_timestamp(), )?; if !diagnostics.iter().any(|diagnostic| { - diagnostic.severity == DiagnosticSeverity::Error && diagnostic.code == failure_code + diagnostic.severity == HostDiagnosticSeverity::Error && diagnostic.code == failure_code }) { diagnostics.insert( 0, RuntimeDiagnostic { code: failure_code.clone(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Runtime did not create the reserved Workdir".to_string(), }, ); @@ -17279,7 +17648,7 @@ fn execute_reserved_workdir_removal_with_provider( .fail_workdir_removal_operation(&operation, category, retryable); } }; - if deleted.state != WorkerOperationState::Accepted + if deleted.state != InternalWorkerOperationState::Accepted && !runtime_reports_workdir_not_found(&deleted) { let category = if deleted @@ -17450,7 +17819,8 @@ async fn set_worker_retention( .map_err(ApiError::from)?; Ok(Json(WorkerRetentionResponse { workspace_id: api.config.workspace_id, - worker_ref, + runtime_id: worker_ref.runtime_id, + worker_id: worker_ref.worker_id, pinned, retention_state: retention_state.to_string(), })) @@ -17475,7 +17845,7 @@ fn build_runtime_cleanup_plan( if diagnostics.is_empty() { diagnostics.push(RuntimeDiagnostic { code: "workspace_cleanup_runtime_observation_unavailable".to_string(), - severity: DiagnosticSeverity::Warning, + severity: HostDiagnosticSeverity::Warning, message: sanitize_backend_error(&error.error.to_string()), }); } @@ -17582,17 +17952,19 @@ fn build_runtime_cleanup_plan( let running_linked = !linked_running_worker_ids.is_empty(); let observed_status = observed_workdirs .get(record.workdir_id.as_str()) - .map(|summary| CleanupWorkdirFileStatus::from_runtime(&summary.status)); + .map(|summary| cleanup_workdir_file_status_from_runtime(&summary.status)); let file_status = observed_status.unwrap_or_else(|| { - CleanupWorkdirFileStatus::from_registry(&record.materialization_status) + cleanup_workdir_file_status_from_registry(&record.materialization_status) }); let cleanliness = observed_workdirs .get(record.workdir_id.as_str()) - .map(|summary| CleanupWorkdirCleanliness::from_runtime(summary.cleanliness.as_deref())) - .unwrap_or_else(|| CleanupWorkdirCleanliness::from_registry(&record.cleanliness)); - let action = if file_status.is_record_only() { + .map(|summary| cleanup_workdir_cleanliness_from_runtime(summary.cleanliness.as_deref())) + .unwrap_or_else(|| cleanup_workdir_cleanliness_from_registry(&record.cleanliness)); + let action = if cleanup_workdir_file_status_is_record_only(file_status) { CleanupTargetKind::WorkdirRecordDelete - } else if file_status.is_corrupted() || cleanliness.is_clean() { + } else if cleanup_workdir_file_status_is_corrupted(file_status) + || cleanup_workdir_cleanliness_is_clean(cleanliness) + { CleanupTargetKind::WorkdirCleanCleanup } else { CleanupTargetKind::WorkdirDirtyDiscard @@ -17617,9 +17989,9 @@ fn build_runtime_cleanup_plan( repository_key, reason: if blocking_reason.is_some() { "Workdir cleanup is blocked until linked Worker state is safe".to_string() - } else if file_status.is_record_only() { + } else if cleanup_workdir_file_status_is_record_only(file_status) { "Not-found Workdir record can be deleted from the Backend registry".to_string() - } else if file_status.is_corrupted() { + } else if cleanup_workdir_file_status_is_corrupted(file_status) { "Corrupted Workdir can be deleted from Runtime storage and Backend registry" .to_string() } else if matches!(cleanliness, CleanupWorkdirCleanliness::Dirty) { @@ -17653,7 +18025,7 @@ fn build_runtime_cleanup_plan( digest, workers: worker_candidates, workdirs: workdir_candidates, - diagnostics, + diagnostics: diagnostics.into_iter().map(Into::into).collect(), }) } @@ -18036,7 +18408,7 @@ async fn scoped_list_runtime_workers( State(api): State, AxumPath(path): AxumPath, Query(query): Query, -) -> ApiResult>> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; list_runtime_workers(State(api), AxumPath(path.runtime_id), Query(query)).await } @@ -18044,8 +18416,8 @@ async fn scoped_list_runtime_workers( async fn scoped_create_runtime_worker( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; create_runtime_worker(State(api), AxumPath(path.runtime_id), Json(request)).await } @@ -18053,8 +18425,8 @@ async fn scoped_create_runtime_worker( async fn scoped_sync_runtime_config_bundle( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; sync_runtime_config_bundle(State(api), AxumPath(path.runtime_id), Json(request)).await } @@ -18062,8 +18434,8 @@ async fn scoped_sync_runtime_config_bundle( async fn scoped_check_runtime_config_bundle( State(api): State, AxumPath(path): AxumPath, - Query(query): Query, -) -> ApiResult> { + Query(query): Query, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; check_runtime_config_bundle( State(api), @@ -18076,7 +18448,7 @@ async fn scoped_check_runtime_config_bundle( async fn scoped_get_runtime_worker( State(api): State, AxumPath(path): AxumPath, -) -> ApiResult> { +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; get_runtime_worker( State(api), @@ -18085,12 +18457,6 @@ async fn scoped_get_runtime_worker( .await } -#[derive(Debug, Default, Deserialize)] -struct RestoreTicketAssignmentQuery { - ticket_id: Option, - assignment_operation_id: Option, -} - async fn scoped_restore_runtime_worker( State(api): State, AxumPath(path): AxumPath, @@ -18209,31 +18575,6 @@ async fn scoped_execute_runtime_cleanup( Ok(Json(response)) } -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct WorkerFileUploadQuery { - file_name: String, - media_type: String, - #[serde(default)] - upload_id: Option, -} - -#[derive(Debug, Serialize)] -struct WorkerFileUploadResponse { - file: protocol::UploadedFileRef, -} - -#[derive(Debug, Serialize)] -struct WorkerFileDeleteResponse { - deleted: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -struct AttachmentUploadGrantResponse { - upload_id: String, - expires_at_ms: u64, -} - #[derive(Debug, Deserialize)] struct ScopedAttachmentUploadPath { workspace_id: String, @@ -18394,11 +18735,6 @@ async fn scoped_upload_runtime_worker_file( } } -#[derive(Debug, Serialize)] -struct AttachmentUploadCancelResponse { - cancelled: bool, -} - async fn scoped_cancel_attachment_upload( State(api): State, Extension(actor): Extension, @@ -18464,8 +18800,8 @@ async fn scoped_delete_runtime_worker_uploaded_file( async fn scoped_send_runtime_worker_input( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; send_runtime_worker_input( State(api), @@ -18478,8 +18814,8 @@ async fn scoped_send_runtime_worker_input( async fn scoped_runtime_worker_completions( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; runtime_worker_completions( State(api), @@ -18492,8 +18828,8 @@ async fn scoped_runtime_worker_completions( async fn scoped_stop_runtime_worker( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; stop_runtime_worker( State(api), @@ -18506,8 +18842,8 @@ async fn scoped_stop_runtime_worker( async fn scoped_cancel_runtime_worker( State(api): State, AxumPath(path): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { validate_workspace_scope(&api, &path.workspace_id)?; cancel_runtime_worker( State(api), @@ -19294,7 +19630,7 @@ async fn list_tickets( record_authority, } = api .authority - .list_ticket_page(crate::records::TicketListPageRequest { + .list_ticket_page(crate::records::TicketListProjectionRequest { states, limit: Some(limit), cursor: query.cursor, @@ -19318,15 +19654,15 @@ async fn get_ticket( async fn list_objectives( State(api): State, - Query(query): Query, -) -> ApiResult>> { + Query(query): Query, +) -> ApiResult> { let limit = query.limit.unwrap_or(api.config.max_records).min(1000); let ProjectRecordList { items, invalid_records, record_authority, } = api.authority.list_objectives(limit)?; - Ok(Json(ListResponse { + Ok(Json(server_api::ObjectiveListResponse { workspace_id: api.config.workspace_id, limit, items, @@ -19406,10 +19742,10 @@ async fn repository_log( async fn list_hosts( State(api): State, -) -> ApiResult>> { +) -> ApiResult>> { let limit = api.config.max_records.min(200); let runtime_hosts = api.runtime.list_hosts(limit); - Ok(Json(RuntimeListResponse { + Ok(Json(InternalRuntimeListResponse { workspace_id: api.config.workspace_id, limit, items: runtime_hosts.items, @@ -19420,10 +19756,10 @@ async fn list_hosts( async fn list_runtimes( State(api): State, -) -> ApiResult>> { +) -> ApiResult> { let limit = api.config.max_records.min(200); let runtimes = api.runtime.list_runtimes(limit); - Ok(Json(server_api::ListResponse { + Ok(Json(server_api::RuntimeListResponse { workspace_id: api.config.workspace_id, limit, items: runtimes.items.into_iter().map(Into::into).collect(), @@ -19434,7 +19770,7 @@ async fn list_runtimes( async fn list_workers( State(api): State, -) -> ApiResult>> { +) -> ApiResult> { workers_response(api).map(Json) } @@ -19655,7 +19991,7 @@ async fn execute_runtime_removal( .await; diagnostics.push(settings_diagnostic( "runtime_removal_active_worker_blocked", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, format!( "Remote Runtime '{}' has {worker_count} active Worker(s); stop and remove them before removing the Runtime.", operation.runtime_id @@ -20480,7 +20816,7 @@ fn record_browser_worker_spawn( result: WorkerSpawnResult, assignment: Option<&WorkerTicketAssignmentRequest>, ) -> ApiResult { - if result.state != WorkerOperationState::Accepted { + if result.state != InternalWorkerOperationState::Accepted { return Err(worker_create_not_accepted_error( requested_runtime_id.clone(), result.diagnostics, @@ -20496,7 +20832,7 @@ fn record_browser_worker_spawn( fn browser_worker_response_from_summary( api: &WorkspaceApi, - worker: WorkerSummary, + worker: InternalWorkerSummary, display_name: String, diagnostics: Vec, assignment: Option<&WorkerTicketAssignmentRequest>, @@ -20582,7 +20918,7 @@ fn browser_worker_response_from_summary( fn finalize_spawned_worker_workdir_attachments( api: &WorkspaceApi, - worker: &WorkerSummary, + worker: &InternalWorkerSummary, worker_record: &WorkerRegistryRecord, compensation: &WorkerSpawnCompensationContext<'_>, ) -> ApiResult<()> { @@ -20655,6 +20991,123 @@ fn finalize_spawned_worker_workdir_attachments( Ok(()) } +fn backend_resource_request_from_api( + request: server_api::RuntimeResourceFetchRequest, +) -> BackendResourceFetchRequest { + let handle = request.handle; + BackendResourceFetchRequest { + handle: worker_runtime::resource::BackendResourceHandle { + kind: match handle.kind { + server_api::RuntimeResourceKind::ProfileSourceArchive => { + worker_runtime::resource::BackendResourceKind::ProfileSourceArchive + } + server_api::RuntimeResourceKind::RepositorySshAccess => { + worker_runtime::resource::BackendResourceKind::RepositorySshAccess + } + }, + workspace_id: handle.workspace_id, + scope_id: handle.scope_id, + runtime_id: handle.runtime_id, + worker_id: handle.worker_id, + resource_id: handle.resource_id, + digest: handle.digest, + operation: match handle.operation { + server_api::RuntimeResourceOperation::FetchArchive => { + worker_runtime::resource::BackendResourceOperation::FetchArchive + } + server_api::RuntimeResourceOperation::FetchOnce => { + worker_runtime::resource::BackendResourceOperation::FetchOnce + } + }, + expires_at_unix_seconds: handle.expires_at_unix_seconds, + nonce: handle.nonce, + revision: handle.revision, + generation: handle.generation, + max_bytes: handle.max_bytes, + content_type: handle.content_type, + redaction: match handle.redaction { + server_api::RuntimeResourceRedactionPolicy::RuntimeInternalOnly => { + worker_runtime::resource::ResourceRedactionPolicy::RuntimeInternalOnly + } + }, + audit_correlation_id: handle.audit_correlation_id, + profile_source_graph: handle.profile_source_graph.map(|graph| { + worker_runtime::profile_archive::ProfileSourceGraphSummary { + source_count: graph.source_count, + total_source_bytes: graph.total_source_bytes, + entrypoints: graph.entrypoints, + import_count: graph.import_count, + } + }), + }, + runtime_id: request.runtime_id, + worker_id: request.worker_id, + audit_correlation_id: request.audit_correlation_id, + } +} + +fn runtime_resource_error_to_api( + error: worker_runtime::resource::BackendResourceError, +) -> server_api::RuntimeResourceFetchApiError { + match error { + worker_runtime::resource::BackendResourceError::Expired => { + server_api::RuntimeResourceFetchApiError::Expired + } + worker_runtime::resource::BackendResourceError::Unauthorized { message } => { + server_api::RuntimeResourceFetchApiError::Unauthorized { message } + } + worker_runtime::resource::BackendResourceError::UnsupportedKind => { + server_api::RuntimeResourceFetchApiError::UnsupportedKind + } + worker_runtime::resource::BackendResourceError::MissingResource => { + server_api::RuntimeResourceFetchApiError::MissingResource + } + worker_runtime::resource::BackendResourceError::DigestMismatch { expected, actual } => { + server_api::RuntimeResourceFetchApiError::DigestMismatch { expected, actual } + } + worker_runtime::resource::BackendResourceError::Oversized { + max_bytes, + actual_bytes, + } => server_api::RuntimeResourceFetchApiError::Oversized { + max_bytes, + actual_bytes, + }, + worker_runtime::resource::BackendResourceError::ContentTypeMismatch { + expected, + actual, + } => server_api::RuntimeResourceFetchApiError::ContentTypeMismatch { expected, actual }, + worker_runtime::resource::BackendResourceError::Timeout => { + server_api::RuntimeResourceFetchApiError::Timeout + } + worker_runtime::resource::BackendResourceError::Transport { message } => { + server_api::RuntimeResourceFetchApiError::Transport { message } + } + worker_runtime::resource::BackendResourceError::InvalidResponse { message } => { + server_api::RuntimeResourceFetchApiError::InvalidResponse { message } + } + } +} + +fn runtime_resource_response_to_api( + mut response: worker_runtime::resource::BackendResourceFetchResponse, +) -> server_api::RuntimeResourceFetchResponse { + server_api::RuntimeResourceFetchResponse { + kind: match response.kind { + worker_runtime::resource::BackendResourceKind::ProfileSourceArchive => { + server_api::RuntimeResourceKind::ProfileSourceArchive + } + worker_runtime::resource::BackendResourceKind::RepositorySshAccess => { + server_api::RuntimeResourceKind::RepositorySshAccess + } + }, + resource_id: response.resource_id.clone(), + digest: response.digest.clone(), + content_type: response.content_type.clone(), + bytes: std::mem::take(&mut response.bytes), + audit_correlation_id: response.audit_correlation_id.clone(), + } +} + async fn runtime_resource_fetch_contract( api: &WorkspaceApi, context: server_api::ServerRequestContext, @@ -20715,26 +21168,12 @@ async fn runtime_resource_fetch_contract( message: "Runtime request proof subject does not match the request".to_owned(), }); } - let request: BackendResourceFetchRequest = project_server_dto(&request).map_err(|error| { - server_api::RuntimeResourceFetchApiError::InvalidResponse { - message: error.to_string(), - } - })?; + let request = backend_resource_request_from_api(request); let response = api .resource_broker .fetch_resource(request) - .map_err(|error| { - project_contract_dto(&error).unwrap_or_else(|projection_error| { - server_api::RuntimeResourceFetchApiError::InvalidResponse { - message: projection_error.message, - } - }) - })?; - project_contract_dto(&response).map_err(|error| { - server_api::RuntimeResourceFetchApiError::InvalidResponse { - message: error.message, - } - }) + .map_err(runtime_resource_error_to_api)?; + Ok(runtime_resource_response_to_api(response)) } #[cfg(test)] @@ -20782,13 +21221,6 @@ async fn post_companion_cancel( Ok(Json(api.companion.cancel(request))) } -#[derive(Debug, Serialize)] -struct WorkerShowProjection { - #[serde(flatten)] - worker: server_api::WorkerSummary, - updated_at: String, -} - fn resolve_workspace_worker_reference( api: &WorkspaceApi, runtime_id: &str, @@ -20817,7 +21249,7 @@ fn resolve_workspace_worker_reference( async fn get_runtime_worker( State(api): State, AxumPath((runtime_id, worker_id)): AxumPath<(String, String)>, -) -> ApiResult> { +) -> ApiResult> { let worker_ref = resolve_workspace_worker_reference(&api, &runtime_id, &worker_id)?; let worker = api .runtime @@ -20833,7 +21265,10 @@ async fn get_runtime_worker( let updated_at = record.updated_at.clone(); let worker = merge_worker_registry_projection(Some(&worker), &record, links, &workdirs); let worker = project_workspace_worker(&api, worker)?; - Ok(Json(WorkerShowProjection { worker, updated_at })) + Ok(Json(server_api::RuntimeWorkerShowResponse { + worker, + updated_at, + })) } async fn restore_runtime_worker( @@ -20867,16 +21302,6 @@ async fn restore_runtime_worker( })) } -#[derive(Debug, Serialize, Deserialize)] -pub struct RuntimeConfigBundleSyncRequest { - pub bundle: ConfigBundle, -} - -#[derive(Debug, Deserialize)] -struct RuntimeConfigBundleAvailabilityQuery { - digest: String, -} - fn reject_workdir_for_embedded_runtime(runtime_id: &str, has_workdir: bool) -> ApiResult<()> { if runtime_id != EMBEDDED_WORKER_RUNTIME_ID || !has_workdir { return Ok(()); @@ -20889,7 +21314,7 @@ fn reject_workdir_for_embedded_runtime(runtime_id: &str, has_workdir: bool) -> A }, vec![RuntimeDiagnostic { code: "embedded_worker_workdir_unsupported".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Choose a non-embedded Runtime for workspace-file Workers; embedded Workers are no-workdir Workspace-API workers.".to_string(), }], )) @@ -20899,7 +21324,7 @@ async fn list_runtime_workers( State(api): State, AxumPath(runtime_id): AxumPath, Query(query): Query, -) -> ApiResult>> { +) -> ApiResult> { let limit = api.config.max_records.min(200); let (runtime_workers, source) = match query.status { Some(RuntimeWorkersStatusFilter::Stopped) => ( @@ -20916,7 +21341,7 @@ async fn list_runtime_workers( ), }; let items = project_observed_workspace_workers(&api, runtime_workers.items)?; - Ok(Json(server_api::ListResponse { + Ok(Json(server_api::RuntimeWorkerListResponse { workspace_id: api.workspace_id().to_string(), limit, items, @@ -20959,7 +21384,7 @@ struct WorkerSpawnCompensationContext<'a> { fn finalize_worker_spawn_stage( api: &WorkspaceApi, - worker: &WorkerSummary, + worker: &InternalWorkerSummary, context: &WorkerSpawnCompensationContext<'_>, stage: WorkerSpawnFinalizeStage, result: ApiResult, @@ -20974,7 +21399,7 @@ fn finalize_worker_spawn_stage( .unwrap_or("none"); let mut diagnostics = vec![RuntimeDiagnostic { code: format!("worker_spawn_finalize_{}_failed", stage.as_str()), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: format!( "Worker spawn finalize failed at stage `{}` for Runtime Worker {}:{} (operation `{operation_id}`): {source_message}", stage.as_str(), @@ -20989,7 +21414,7 @@ fn finalize_worker_spawn_stage( if !compensation_failed { diagnostics.push(RuntimeDiagnostic { code: "worker_spawn_compensated".to_string(), - severity: DiagnosticSeverity::Info, + severity: HostDiagnosticSeverity::Info, message: format!( "Removed Runtime Worker {}:{} and rolled back Backend spawn state", worker.worker.runtime_id, worker.worker.worker_id @@ -21072,7 +21497,7 @@ fn compensate_failed_workspace_worker_create( reservation_worker_id: WorkerId, create_fingerprint: &str, failure_phase: &str, - worker: Option<&WorkerSummary>, + worker: Option<&InternalWorkerSummary>, context: &WorkerSpawnCompensationContext<'_>, attachment_reservations: &[(String, String, String)], ) -> Vec { @@ -21150,7 +21575,7 @@ fn compensate_failed_workspace_worker_create( fn compensate_failed_worker_spawn( api: &WorkspaceApi, - worker: &WorkerSummary, + worker: &InternalWorkerSummary, context: &WorkerSpawnCompensationContext<'_>, ) -> Vec { let (runtime_deleted, mut diagnostics) = @@ -21264,7 +21689,7 @@ fn lifecycle_failure_detail( fn spawn_compensation_diagnostic(code: &str, message: String) -> RuntimeDiagnostic { RuntimeDiagnostic { code: code.to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message, } } @@ -21284,8 +21709,9 @@ fn runtime_diagnostics_message(diagnostics: &[RuntimeDiagnostic]) -> String { async fn create_runtime_worker( State(api): State, AxumPath(runtime_id): AxumPath, - Json(mut request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { + let mut request = worker_spawn_request_from_api(request)?; validate_worker_initial_submit(&request.initial_submit)?; if let Some(assignment) = request.ticket_assignment.as_ref() && let Some(worker) = existing_lifecycle_assignment_worker(&api, assignment, &runtime_id)? @@ -21298,12 +21724,12 @@ async fn create_runtime_worker( &worker.worker.worker_id, )?; accept_queued_ticket_after_worker_spawn(&api, assignment)?; - return Ok(Json(WorkerSpawnResult { - state: WorkerOperationState::Accepted, + return Ok(Json(worker_spawn_result_to_api(WorkerSpawnResult { + state: InternalWorkerOperationState::Accepted, worker: Some(worker), acceptance_evidence: Vec::new(), diagnostics: Vec::new(), - })); + })?)); } let lifecycle_assignment = request.ticket_assignment.clone(); validate_ticket_assignment_spawn(&api, &runtime_id, &request)?; @@ -21441,26 +21867,32 @@ async fn create_runtime_worker( } } } - Ok(Json(result)) + Ok(Json(worker_spawn_result_to_api(result)?)) } async fn sync_runtime_config_bundle( State(api): State, AxumPath(runtime_id): AxumPath, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { + let bundle = serde_json::from_value::(request.bundle) + .map_err(|error| invalid_contract_value("Runtime ConfigBundle", error))?; let result = api .runtime - .sync_config_bundle(&runtime_id, request.bundle) + .sync_config_bundle(&runtime_id, bundle) .map_err(|err| err.into_error())?; - Ok(Json(result)) + Ok(Json(runtime_config_bundle_result_to_api( + result.state, + result.availability, + result.diagnostics, + )?)) } async fn check_runtime_config_bundle( State(api): State, AxumPath((runtime_id, bundle_id)): AxumPath<(String, String)>, - Query(query): Query, -) -> ApiResult> { + Query(query): Query, +) -> ApiResult> { let result = api .runtime .check_config_bundle( @@ -21471,20 +21903,25 @@ async fn check_runtime_config_bundle( }, ) .map_err(|err| err.into_error())?; - Ok(Json(result)) + Ok(Json(runtime_config_bundle_result_to_api( + result.state, + result.availability, + result.diagnostics, + )?)) } async fn send_runtime_worker_input( State(api): State, AxumPath((runtime_id, worker_id)): AxumPath<(String, String)>, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { + let request = worker_input_request_from_api(request)?; let worker = resolve_workspace_worker_reference(&api, &runtime_id, &worker_id)?; let result = api .runtime .send_input(&worker, request) .map_err(|err| err.into_error())?; - Ok(Json(result)) + Ok(Json(worker_input_result_to_api(result))) } async fn delete_runtime_worker_uploaded_file( @@ -21501,21 +21938,23 @@ async fn delete_runtime_worker_uploaded_file( async fn runtime_worker_completions( State(api): State, AxumPath((runtime_id, worker_id)): AxumPath<(String, String)>, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { + let request = worker_completions_request_from_api(request)?; let worker = resolve_workspace_worker_reference(&api, &runtime_id, &worker_id)?; let result = api .runtime .worker_completions(&worker, request) .map_err(|err| err.into_error())?; - Ok(Json(result)) + Ok(Json(worker_completions_result_to_api(result)?)) } async fn stop_runtime_worker( State(api): State, AxumPath((runtime_id, worker_id)): AxumPath<(String, String)>, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { + let request = worker_lifecycle_request_from_api(request); let worker = resolve_workspace_worker_reference(&api, &runtime_id, &worker_id)?; let result = api .runtime @@ -21531,20 +21970,21 @@ async fn stop_runtime_worker( { sync_linked_workdir_after_worker_stop(&api, &worker.runtime_id, &record)?; } - Ok(Json(result)) + Ok(Json(worker_lifecycle_result_to_api(result))) } async fn cancel_runtime_worker( State(api): State, AxumPath((runtime_id, worker_id)): AxumPath<(String, String)>, - Json(request): Json, -) -> ApiResult> { + Json(request): Json, +) -> ApiResult> { + let request = worker_lifecycle_request_from_api(request); let worker = resolve_workspace_worker_reference(&api, &runtime_id, &worker_id)?; let result = api .runtime .cancel_worker(&worker, request) .map_err(|err| err.into_error())?; - Ok(Json(result)) + Ok(Json(worker_lifecycle_result_to_api(result))) } fn authenticated_browser_input_source(actor: &RequestActor) -> protocol::AuthenticatedInputSource { @@ -22049,7 +22489,7 @@ async fn list_host_workers( fn project_workspace_worker( api: &WorkspaceApi, - summary: WorkerSummary, + summary: InternalWorkerSummary, ) -> ApiResult { let resource_key = api .store @@ -22088,7 +22528,7 @@ fn project_workspace_worker( fn project_observed_workspace_workers( api: &WorkspaceApi, - workers: Vec, + workers: Vec, ) -> ApiResult> { let workdirs = api .store @@ -22107,9 +22547,7 @@ fn project_observed_workspace_workers( .collect() } -fn workers_response( - api: WorkspaceApi, -) -> ApiResult> { +fn workers_response(api: WorkspaceApi) -> ApiResult { let limit = api.config.max_records.min(200); let (_, projections) = api .store @@ -22131,7 +22569,7 @@ fn workers_response( ); items.push(project_workspace_worker(&api, summary)?); } - Ok(server_api::ListResponse { + Ok(server_api::WorkerListResponse { workspace_id: api.config.workspace_id, limit, items, @@ -22143,7 +22581,7 @@ fn workers_response( async fn workspace_runtime_resources_response( api: &WorkspaceApi, workspace_id: &str, -) -> ApiResult> { +) -> ApiResult { let limit = api.config.max_records.min(200); let runtimes = api.runtime.list_runtimes(limit); let bindings = api @@ -22218,7 +22656,7 @@ async fn workspace_runtime_resources_response( diagnostics: vec![ settings_diagnostic( "registered_runtime_unavailable", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "The registered Runtime is not present in the active Runtime registry.", ) .into(), @@ -22239,7 +22677,7 @@ async fn workspace_runtime_resources_response( }); } - Ok(server_api::ListResponse { + Ok(server_api::WorkspaceRuntimeListResponse { workspace_id: workspace_id.to_string(), limit, items, @@ -22837,7 +23275,7 @@ enum WorkerRegistryDisplayNamePolicy { fn record_worker_summary( api: &WorkspaceApi, - worker: &WorkerSummary, + worker: &InternalWorkerSummary, display_name: &str, profile: Option, display_name_policy: WorkerRegistryDisplayNamePolicy, @@ -22918,8 +23356,8 @@ fn record_worker_summary( .unwrap_or(record)) } -fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> WorkerSummary { - WorkerSummary { +fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> InternalWorkerSummary { + InternalWorkerSummary { worker: record.worker.clone(), host_id: "backend-registry".to_string(), display_name: record.display_name.clone(), @@ -22931,24 +23369,24 @@ fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> WorkerSummary last_seen_at: Some(record.updated_at.clone()), pinned: record.retention_state == "pinned", retention_state: record.retention_state.clone(), - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: false, can_spawn_followup: false, }, - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "backend_registry".to_string(), identity: record.workspace_id.clone(), workspace_id: Some(record.workspace_id.clone()), }, profile: record.profile.clone(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "backend_worker_registry".to_string(), display_hint: "Missing Worker".to_string(), }, workdir_attachments: Vec::new(), diagnostics: vec![RuntimeDiagnostic { code: "backend_worker_missing".to_string(), - severity: DiagnosticSeverity::Info, + severity: HostDiagnosticSeverity::Info, message: "Worker is preserved in the Backend registry but the Runtime did not find it by id" .to_string(), @@ -22958,7 +23396,7 @@ fn worker_summary_from_registry(record: &WorkerRegistryRecord) -> WorkerSummary fn worker_summary_from_projection( projection: &crate::store::WorkerRegistryProjectionRecord, -) -> Option { +) -> Option { let observation = projection.observation.as_ref()?; let mut summary = worker_summary_from_registry(&projection.registry); let observed = observation.availability @@ -22988,7 +23426,7 @@ fn worker_summary_from_projection( } else { vec![RuntimeDiagnostic { code: "worker_observation_unavailable".to_string(), - severity: DiagnosticSeverity::Info, + severity: HostDiagnosticSeverity::Info, message: "The Worker remains in the Backend catalog, but its Runtime observation is currently unavailable.".to_string(), }] }; @@ -22996,11 +23434,11 @@ fn worker_summary_from_projection( } fn merge_worker_registry_projection( - live: Option<&WorkerSummary>, + live: Option<&InternalWorkerSummary>, record: &WorkerRegistryRecord, links: Vec, workdirs: &[WorkdirRegistryRecord], -) -> WorkerSummary { +) -> InternalWorkerSummary { let mut summary = live .cloned() .unwrap_or_else(|| worker_summary_from_registry(record)); @@ -23037,7 +23475,7 @@ fn merge_worker_registry_projection( fn sync_worker_observation( api: &WorkspaceApi, - worker: &WorkerSummary, + worker: &InternalWorkerSummary, ) -> ApiResult { let record = record_worker_summary( api, @@ -23553,7 +23991,7 @@ fn validate_working_directory_claims_for_browser( }, vec![RuntimeDiagnostic { code: "working_directory_relative_cwd_invalid".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "relative_cwd must be a relative path without parent traversal".to_string(), }], )); @@ -24145,7 +24583,7 @@ fn worker_create_not_accepted_error( ) -> ApiError { diagnostics.push(settings_diagnostic( "workspace_worker_create_not_accepted", - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, "Runtime did not accept worker creation; see diagnostics for sanitized Runtime compatibility details.", )); ApiError::with_diagnostics( @@ -24163,7 +24601,7 @@ fn settings_bad_request(code: &'static str, message: &'static str) -> ApiError { Error::InvalidInput(format!("{code}: {message}")), vec![settings_diagnostic( code, - DiagnosticSeverity::Error, + HostDiagnosticSeverity::Error, message, )], ) @@ -24171,7 +24609,7 @@ fn settings_bad_request(code: &'static str, message: &'static str) -> ApiError { fn settings_diagnostic( code: impl Into, - severity: DiagnosticSeverity, + severity: HostDiagnosticSeverity, message: impl Into, ) -> RuntimeDiagnostic { RuntimeDiagnostic { @@ -24197,7 +24635,7 @@ fn repository_lookup(result: std::result::Result) - }, vec![RuntimeDiagnostic { code: "repository_not_configured".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message, }], ) @@ -24214,7 +24652,7 @@ fn repository_lookup(result: std::result::Result) - }, vec![RuntimeDiagnostic { code: "repository_provider_unsupported".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message, }], ) @@ -24229,7 +24667,7 @@ fn repository_lookup(result: std::result::Result) - }, vec![RuntimeDiagnostic { code: "repository_evidence_invalid".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message, }], ) @@ -24389,7 +24827,7 @@ fn workspace_id_mismatch_error() -> ApiError { Error::WorkspaceIdMismatch, vec![RuntimeDiagnostic { code: "workspace_id_mismatch".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message, }], ) @@ -24419,12 +24857,12 @@ impl From for ApiError { let diagnostics = match &error { Error::RuntimeOperationFailed { code, message, .. } => vec![RuntimeDiagnostic { code: code.clone(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: sanitize_backend_error(message), }], Error::WorkspaceSigningIdentity { code, message } => vec![RuntimeDiagnostic { code: code.clone(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: sanitize_backend_error(message), }], Error::Ticket(ticket_error) => vec![RuntimeDiagnostic { @@ -24459,7 +24897,7 @@ impl From for ApiError { | ticket::TicketError::Sqlite(_) => "ticket_backend_error", } .to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: sanitize_backend_error(&ticket_error.to_string()), }], _ => Vec::new(), @@ -24662,8 +25100,8 @@ mod tests { use worker_runtime::working_directory::WorkingDirectoryMaterializer; use crate::hosts::{ - TicketWorkerRole, WorkerInputKind, WorkerOperationState, WorkerSpawnAcceptanceRequirement, - WorkerSpawnIntent, + InternalWorkerOperationState, TicketWorkerRole, WorkerInputKind, + WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, }; use crate::store::{ AccountRecord, ApiTokenRecord, BrowserSessionRecord, MemoryDocumentRecord, @@ -25466,7 +25904,7 @@ mod tests { #[derive(Clone, Default)] struct WorkdirlessFixtureRuntime { - workers: Arc>>, + workers: Arc>>, spawn_requests: Arc>>, reject_next_spawn: Arc>, workdir_repositories: Arc>>, @@ -25541,8 +25979,8 @@ mod tests { fn worker_summary( binding: WorkerCreateBinding, request: &WorkerSpawnRequest, - ) -> WorkerSummary { - WorkerSummary { + ) -> InternalWorkerSummary { + InternalWorkerSummary { worker: RuntimeWorkerRef::new(Self::RUNTIME_ID, binding.worker_id.to_string()), host_id: "workdirless-host".to_string(), display_name: request @@ -25560,7 +25998,7 @@ mod tests { }, singleton_key: None, tags: Vec::new(), - workspace: WorkerWorkspaceSummary { + workspace: InternalWorkerWorkspaceSummary { visibility: "workspace".to_string(), identity: TEST_WORKSPACE_ID.to_string(), workspace_id: Some(TEST_WORKSPACE_ID.to_string()), @@ -25570,11 +26008,11 @@ mod tests { last_seen_at: None, pinned: false, retention_state: "normal".to_string(), - implementation: WorkerImplementationSummary { + implementation: InternalWorkerImplementationSummary { kind: "fixture".to_string(), display_hint: "Workdirless fixture".to_string(), }, - capabilities: WorkerCapabilitySummary { + capabilities: InternalWorkerCapabilitySummary { can_stop: true, can_spawn_followup: false, }, @@ -25589,13 +26027,13 @@ mod tests { Self::RUNTIME_ID } - fn runtime_summary(&self, _limit: usize) -> crate::hosts::RuntimeSummary { - crate::hosts::RuntimeSummary { + fn runtime_summary(&self, _limit: usize) -> crate::hosts::InternalRuntimeSummary { + crate::hosts::InternalRuntimeSummary { runtime_id: Self::RUNTIME_ID.to_string(), label: "Workdirless Runtime".to_string(), kind: "fixture".to_string(), status: "active".to_string(), - source: crate::hosts::RuntimeSourceSummary::remote_http(), + source: crate::hosts::InternalRuntimeSourceSummary::remote_http(), host_ids: vec!["workdirless-host".to_string()], worker_creation_available: true, os: "linux".to_string(), @@ -25604,14 +26042,14 @@ mod tests { } } - fn list_hosts(&self, _limit: usize) -> crate::hosts::RuntimeList { + fn list_hosts(&self, _limit: usize) -> crate::hosts::RuntimeList { crate::hosts::RuntimeList { items: Vec::new(), diagnostics: Vec::new(), } } - fn list_workers(&self, limit: usize) -> crate::hosts::RuntimeList { + fn list_workers(&self, limit: usize) -> crate::hosts::RuntimeList { crate::hosts::RuntimeList { items: self .workers @@ -25643,7 +26081,7 @@ mod tests { working_directory_id: &str, ) -> crate::hosts::RuntimeWorkingDirectoryResult { crate::hosts::RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, working_directory: Some(self.configured_workdir_status(working_directory_id)), diagnostics: Vec::new(), } @@ -25654,7 +26092,7 @@ mod tests { _working_directory_id: &str, ) -> crate::hosts::RuntimeWorkingDirectoryResult { crate::hosts::RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, working_directory: None, diagnostics: Vec::new(), } @@ -25676,12 +26114,12 @@ mod tests { .unwrap_or_else(std::sync::PoisonError::into_inner), ) { return WorkerSpawnResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, worker: None, acceptance_evidence: Vec::new(), diagnostics: vec![RuntimeDiagnostic { code: "fixture_runtime_rejected".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "fixture rejected Worker create".to_string(), }], }; @@ -25703,7 +26141,7 @@ mod tests { .unwrap_or_else(std::sync::PoisonError::into_inner) .push(worker.clone()); WorkerSpawnResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: Some(worker), acceptance_evidence: Vec::new(), diagnostics: Vec::new(), @@ -25716,7 +26154,7 @@ mod tests { _request: WorkerLifecycleRequest, ) -> WorkerLifecycleResult { WorkerLifecycleResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(Self::RUNTIME_ID, worker_id), diagnostics: Vec::new(), } @@ -25728,7 +26166,7 @@ mod tests { _request: WorkerLifecycleRequest, ) -> WorkerLifecycleResult { WorkerLifecycleResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(Self::RUNTIME_ID, worker_id), diagnostics: Vec::new(), } @@ -25742,7 +26180,7 @@ mod tests { let previous_len = workers.len(); workers.retain(|worker| worker.worker.worker_id != worker_id); crate::hosts::WorkerDeleteResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: RuntimeWorkerRef::new(Self::RUNTIME_ID, worker_id), deleted: workers.len() != previous_len, diagnostics: Vec::new(), @@ -25755,7 +26193,7 @@ mod tests { _workspace_api: WorkspaceApiRef, ) -> crate::hosts::WorkerWorkspaceApiResult { crate::hosts::WorkerWorkspaceApiResult { - state: WorkerOperationState::Accepted, + state: InternalWorkerOperationState::Accepted, worker: self.worker(worker_id).worker, diagnostics: Vec::new(), } @@ -26503,7 +26941,7 @@ mod tests { message: "sqlite error: FOREIGN KEY constraint failed".to_string(), diagnostics: vec![RuntimeDiagnostic { code: "ticket_backend_error".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "FOREIGN KEY constraint failed".to_string(), }], }; @@ -26639,7 +27077,7 @@ mod tests { StatusCode::INTERNAL_SERVER_ERROR, ), ] { - let response = WorkdirOperationApiError::Provider(WorkdirTransportError { + let response = InternalWorkdirOperationError::Provider(WorkdirTransportError { code, message: "safe provider message".to_string(), }) @@ -26675,7 +27113,7 @@ mod tests { assert_eq!(local, remote); for public in [local, remote] { - let response = WorkdirOperationApiError::Provider(public).into_response(); + let response = InternalWorkdirOperationError::Provider(public).into_response(); assert_eq!(response.status(), StatusCode::BAD_REQUEST); let body = to_bytes(response.into_body(), usize::MAX).await.unwrap(); let decoded: WorkdirTransportError = serde_json::from_slice(&body).unwrap(); @@ -26696,7 +27134,7 @@ mod tests { &RuntimeWorkerRef::new("runtime", "worker"), error, ); - let response = WorkdirOperationApiError::Provider(public).into_response(); + let response = InternalWorkdirOperationError::Provider(public).into_response(); let body = to_bytes(response.into_body(), usize::MAX).await.unwrap(); let text = String::from_utf8(body.to_vec()).unwrap(); assert!(!text.contains("/host/private")); @@ -26708,7 +27146,7 @@ mod tests { &RuntimeWorkerRef::new("runtime", "worker"), workdir::WorkdirError::OperationFailed, ); - let response = WorkdirOperationApiError::Provider(remote).into_response(); + let response = InternalWorkdirOperationError::Provider(remote).into_response(); assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); } @@ -27795,7 +28233,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(stopped.state, WorkerOperationState::Accepted); + assert_eq!(stopped.state, InternalWorkerOperationState::Accepted); let Json(restored) = scoped_start_workspace_orchestrator( State(api.clone()), AxumPath(ScopedWorkspacePath { @@ -28708,7 +29146,8 @@ mod tests { .await .unwrap(); assert_eq!(known.items.len(), 1); - assert_eq!(known.items[0].subject, generic_ref); + assert_eq!(known.items[0].subject.runtime_id, generic_ref.runtime_id); + assert_eq!(known.items[0].subject.worker_id, generic_ref.worker_id); assert_eq!(known.items[0].permissions, ["observe"]); let Json(sessions) = scoped_list_worker_observation_sessions( @@ -28720,31 +29159,29 @@ mod tests { ) .await .unwrap(); - assert!( - sessions["sessions"] - .as_array() - .unwrap() - .iter() - .any(|session| { - session["subject"]["kind"] == "runtime_worker" - && session["subject"]["runtime_id"] == generic.runtime_id - && session["subject"]["worker_id"] == generic.worker_id - }) - ); + assert!(sessions.sessions.iter().any(|session| { + matches!( + &session.subject, + server_api::WorkerObservationSubjectRef::RuntimeWorker { + runtime_id, + worker_id, + } if runtime_id == &generic.runtime_id && worker_id == &generic.worker_id + ) + })); let Json(capture) = scoped_capture_worker_observation_session( State(api.clone()), AxumPath(ScopedWorkspacePath { workspace_id: workspace_id.clone(), }), observation_headers.clone(), - Json(WorkerObservationSubjectRef::RuntimeWorker { + Json(server_api::WorkerObservationSubjectRef::RuntimeWorker { runtime_id: generic.runtime_id.clone(), worker_id: generic.worker_id.clone(), }), ) .await .unwrap(); - assert!(capture["session"]["entries"].is_array()); + assert!(!capture.segment_id.is_empty()); let revoked = api .store @@ -28761,7 +29198,7 @@ mod tests { workspace_id: workspace_id.clone(), }), observation_headers.clone(), - Json(WorkerObservationSubjectRef::RuntimeWorker { + Json(server_api::WorkerObservationSubjectRef::RuntimeWorker { runtime_id: generic.runtime_id.clone(), worker_id: generic.worker_id.clone(), }), @@ -28791,7 +29228,7 @@ mod tests { ) .await .unwrap(); - assert!(unauthorized["sessions"].as_array().unwrap().is_empty()); + assert!(unauthorized.sessions.is_empty()); let Json(existing) = scoped_start_workspace_orchestrator( State(api.clone()), @@ -29365,7 +29802,7 @@ mod tests { fn unrecoverable_pending_workspace_restore_is_typed_for_replacement() { let diagnostics = [RuntimeDiagnostic { code: "embedded_worker_execution_rejected".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "Restore Errored: pending Workspace Worker restore requires operation-owned launch material; generic restore must not reconstruct it from current Workspace config".to_string(), }]; @@ -29376,7 +29813,7 @@ mod tests { fn workdir_runtime_miss_uses_exact_typed_code() { let typed_not_found = [RuntimeDiagnostic { code: "working_directory_not_found".to_string(), - severity: DiagnosticSeverity::Warning, + severity: HostDiagnosticSeverity::Warning, message: "missing".to_string(), }]; assert_eq!( @@ -29387,7 +29824,7 @@ mod tests { let unrelated = [RuntimeDiagnostic { code: "some_other_not_found".to_string(), - severity: DiagnosticSeverity::Warning, + severity: HostDiagnosticSeverity::Warning, message: "not a typed workdir miss".to_string(), }]; assert_eq!(workdir_status_from_runtime_miss(&unrelated), "unknown"); @@ -31359,7 +31796,7 @@ mod tests { .unwrap(); assert_eq!( existing.state, - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, "diagnostics: {:?}", existing.diagnostics ); @@ -31735,9 +32172,9 @@ mod tests { .unwrap() .create(ticket::NewTicket::new("Idempotent assignment")) .unwrap(); - let request = || SetTicketRoleAssignmentRequest { + let request = || server_api::SetTicketRoleAssignmentRequest { operation_id: "same-role-operation".to_string(), - principal: TicketAssignmentPrincipal::WorkspaceAgent { + principal: server_api::TicketAssignmentPrincipal::WorkspaceAgent { agent_key: "workspace-orchestrator".to_string(), }, expected_assignment_id: None, @@ -31790,9 +32227,9 @@ mod tests { ticket_id.clone(), "coder".to_string(), )), - Json(SetTicketRoleAssignmentRequest { + Json(server_api::SetTicketRoleAssignmentRequest { operation_id: "invalid-workspace-agent-coder".to_string(), - principal: TicketAssignmentPrincipal::WorkspaceAgent { + principal: server_api::TicketAssignmentPrincipal::WorkspaceAgent { agent_key: "workspace-orchestrator".to_string(), }, expected_assignment_id: None, @@ -31833,7 +32270,7 @@ mod tests { let coder = read .assignments .iter() - .find(|assignment| assignment.role == TicketAssignmentRole::Coder) + .find(|assignment| assignment.role == server_api::TicketAssignmentRole::Coder) .unwrap(); assert_eq!(coder.assignment_id, assignment.assignment_id); let Json(detail) = browser_ticket_detail(&api, &ticket_id).unwrap(); @@ -31854,7 +32291,7 @@ mod tests { ticket_id.clone(), "coder".to_string(), )), - Query(ClearTicketRoleAssignmentQuery { + Query(server_api::ClearTicketRoleAssignmentQuery { operation_id: Some("clear-stale".to_string()), assignment_id: Some("stale-assignment".to_string()), }), @@ -31871,7 +32308,7 @@ mod tests { ticket_id.clone(), "coder".to_string(), )), - Query(ClearTicketRoleAssignmentQuery { + Query(server_api::ClearTicketRoleAssignmentQuery { operation_id: Some("clear-current".to_string()), assignment_id: Some("assignment-api-1".to_string()), }), @@ -31886,7 +32323,7 @@ mod tests { ticket_id, "coder".to_string(), )), - Query(ClearTicketRoleAssignmentQuery { + Query(server_api::ClearTicketRoleAssignmentQuery { operation_id: Some("clear-current".to_string()), assignment_id: Some("assignment-api-1".to_string()), }), @@ -31977,7 +32414,7 @@ mod tests { }) }; let request = || { - Json(CancelTicketImplementationRequest { + Json(server_api::CancelTicketImplementationRequest { operation_id: "cancel-implementation-operation".to_string(), assignment_id: "cancelled-assignment".to_string(), reason: "redo with the corrected design".to_string(), @@ -32326,8 +32763,8 @@ mod tests { let planning = scoped_transition_ticket_state( State(api.clone()), AxumPath(path.clone()), - Json(BrowserTransitionTicketStateRequest { - state: TicketWorkflowState::InProgress, + Json(server_api::BrowserTransitionTicketStateRequest { + state: server_api::BrowserTicketWorkflowState::Inprogress, reason: None, body: None, author: None, @@ -32355,8 +32792,8 @@ mod tests { workspace_id: TEST_WORKSPACE_ID.to_string(), id: ready.id.clone(), }), - Json(BrowserTransitionTicketStateRequest { - state: TicketWorkflowState::InProgress, + Json(server_api::BrowserTransitionTicketStateRequest { + state: server_api::BrowserTicketWorkflowState::Inprogress, reason: None, body: None, author: None, @@ -32391,7 +32828,7 @@ mod tests { workspace_id: TEST_WORKSPACE_ID.to_string(), id: ticket.id.clone(), }), - Json(BrowserQueueTicketRequest {}), + Json(server_api::BrowserQueueTicketRequest {}), ) .await .unwrap_err() @@ -33042,32 +33479,27 @@ mod tests { first_ticket_input.workflow_state = Some(TicketWorkflowState::Queued); let first_ticket = backend.create(first_ticket_input).unwrap(); assign_test_orchestrator(&api, &first_ticket.id); - let request = WorkerSpawnRequest { + let request = server_api::RuntimeWorkerSpawnRequest { requested_worker_name: Some("assigned-spawn".to_string()), - intent: WorkerSpawnIntent::TicketRole { + intent: server_api::RuntimeWorkerSpawnIntent::TicketRole { ticket_id: first_ticket.id.clone(), - role: TicketWorkerRole::Coder, + role: server_api::RuntimeTicketWorkerRole::Coder, }, - acceptance: WorkerSpawnAcceptanceRequirement::RunAccepted { + acceptance: server_api::RuntimeWorkerSpawnAcceptanceRequirement::RunAccepted { expected_segments: 1, }, - profile: ProfileSelector::Builtin("builtin:coder".to_string()), - ticket_assignment: Some(crate::hosts::WorkerTicketAssignmentRequest { + profile: server_api::RuntimeProfileSelector::Builtin("builtin:coder".to_string()), + ticket_assignment: Some(server_api::RuntimeWorkerTicketAssignmentRequest { ticket_id: first_ticket.id.clone(), operation_id: "spawn-assignment-operation".to_string(), }), - initial_submit: vec![Segment::Flow { - selector: "builtin:coder-review".to_string(), - }], + initial_submit: vec![ + serde_json::to_value(Segment::Flow { + selector: "builtin:coder-review".to_string(), + }) + .unwrap(), + ], workdir_attachment_requests: Vec::new(), - resolved_workdir_attachment_requests: Vec::new(), - resolved_workdir_attachments: Vec::new(), - resolved_config_bundle: None, - resolved_worker_observation_enabled: false, - resolved_worker_observation_grants: Vec::new(), - resolved_workspace_api: None, - resolved_memory_settings: None, - resolved_control_operation: None, }; let Json(first) = scoped_create_runtime_worker( State(api.clone()), @@ -33080,6 +33512,8 @@ mod tests { .await .unwrap(); let first_worker = first.worker.unwrap(); + let first_worker_ref = + RuntimeWorkerRef::new(&first_worker.runtime_id, &first_worker.worker_id); let Json(projected) = scoped_list_ticket_assignments( State(api.clone()), AxumPath(ScopedRecordPath { @@ -33089,13 +33523,11 @@ mod tests { ) .await .unwrap(); - assert_eq!( - projected.assignments[0] - .principal - .worker() - .map(|worker| worker.worker_id), - Some(first_worker.worker.worker_id.clone()) - ); + assert!(matches!( + &projected.assignments[0].principal, + server_api::TicketAssignmentPrincipal::Worker { worker_id, .. } + if worker_id == &first_worker.worker_id + )); assert_eq!( backend .show(first_ticket.id.clone().into()) @@ -33114,10 +33546,7 @@ mod tests { ) .await .unwrap(); - assert_eq!( - retried.worker.unwrap().worker.worker_id, - first_worker.worker.worker_id - ); + assert_eq!(retried.worker.unwrap().worker_id, first_worker.worker_id); assert_eq!( api.store .list_ticket_coder_assignment_events(TEST_WORKSPACE_ID, &first_ticket.id, 10,) @@ -33144,7 +33573,7 @@ mod tests { .unwrap(); api.runtime .stop_worker( - &first_worker.worker, + &first_worker_ref, WorkerLifecycleRequest { reason: Some("restore assignment test".to_string()), ticket_assignment: None, @@ -33161,7 +33590,7 @@ mod tests { workspace_id: TEST_WORKSPACE_ID.to_string(), worker: RuntimeWorkerRef::new( EMBEDDED_WORKER_RUNTIME_ID, - first_worker.worker.worker_id.clone(), + first_worker.worker_id.clone(), ), }), Query(RestoreTicketAssignmentQuery { @@ -33177,7 +33606,7 @@ mod tests { workspace_id: TEST_WORKSPACE_ID.to_string(), worker: RuntimeWorkerRef::new( EMBEDDED_WORKER_RUNTIME_ID, - first_worker.worker.worker_id.clone(), + first_worker.worker_id.clone(), ), }), Query(RestoreTicketAssignmentQuery { @@ -33187,7 +33616,7 @@ mod tests { ) .await .unwrap(); - assert_eq!(retried_restore.worker_id, first_worker.worker.worker_id); + assert_eq!(retried_restore.worker_id, first_worker.worker_id); assert_eq!( retried_restore.result.state, server_api::WorkerRestoreState::Accepted @@ -33197,10 +33626,7 @@ mod tests { .get_current_ticket_coder_assignment(TEST_WORKSPACE_ID, &second_ticket.id) .unwrap() .unwrap(); - assert_eq!( - restored_assignment.worker.worker_id, - first_worker.worker.worker_id - ); + assert_eq!(restored_assignment.worker.worker_id, first_worker.worker_id); assert_eq!( backend .show(second_ticket.id.clone().into()) @@ -33221,17 +33647,19 @@ mod tests { TEST_CREATED_AT, ) .unwrap(); - let mut pending_request = WorkerSpawnRequest { - intent: WorkerSpawnIntent::TicketRole { + let pending_api_request = server_api::RuntimeWorkerSpawnRequest { + intent: server_api::RuntimeWorkerSpawnIntent::TicketRole { ticket_id: second_ticket.id.clone(), - role: TicketWorkerRole::Coder, + role: server_api::RuntimeTicketWorkerRole::Coder, }, - ticket_assignment: Some(crate::hosts::WorkerTicketAssignmentRequest { + ticket_assignment: Some(server_api::RuntimeWorkerTicketAssignmentRequest { ticket_id: second_ticket.id.clone(), operation_id: "pending-spawn-operation".to_string(), }), ..request }; + let mut pending_request = + worker_spawn_request_from_api(pending_api_request.clone()).unwrap(); pending_request.resolved_workspace_api = Some(test_worker_workspace_api(EMBEDDED_WORKER_RUNTIME_ID)); let (_, pending_fingerprint) = crate::hosts::worker_spawn_idempotency(&pending_request) @@ -33289,12 +33717,12 @@ mod tests { workspace_id: TEST_WORKSPACE_ID.to_string(), runtime_id: EMBEDDED_WORKER_RUNTIME_ID.to_string(), }), - Json(pending_request), + Json(pending_api_request), ) .await .unwrap(); assert_eq!( - reconciled.worker.unwrap().worker.worker_id, + reconciled.worker.unwrap().worker_id, spawned_before_backend_failure.worker.worker_id ); assert_eq!( @@ -33330,29 +33758,27 @@ mod tests { ticket_id: ticket_id.clone(), operation_id: "compensation-test-operation".to_string(), }; - let request = WorkerSpawnRequest { - intent: WorkerSpawnIntent::TicketRole { + let request = server_api::RuntimeWorkerSpawnRequest { + intent: server_api::RuntimeWorkerSpawnIntent::TicketRole { ticket_id: ticket_id.clone(), - role: TicketWorkerRole::Coder, + role: server_api::RuntimeTicketWorkerRole::Coder, }, requested_worker_name: Some("Compensation test Worker".to_string()), - acceptance: WorkerSpawnAcceptanceRequirement::RunAccepted { + acceptance: server_api::RuntimeWorkerSpawnAcceptanceRequirement::RunAccepted { expected_segments: 1, }, - profile: ProfileSelector::Builtin("builtin:coder".to_string()), - ticket_assignment: Some(assignment.clone()), - initial_submit: vec![Segment::Flow { - selector: "builtin:coder-review".to_string(), - }], + profile: server_api::RuntimeProfileSelector::Builtin("builtin:coder".to_string()), + ticket_assignment: Some(server_api::RuntimeWorkerTicketAssignmentRequest { + ticket_id: assignment.ticket_id.clone(), + operation_id: assignment.operation_id.clone(), + }), + initial_submit: vec![ + serde_json::to_value(Segment::Flow { + selector: "builtin:coder-review".to_string(), + }) + .unwrap(), + ], workdir_attachment_requests: Vec::new(), - resolved_workdir_attachment_requests: Vec::new(), - resolved_workdir_attachments: Vec::new(), - resolved_config_bundle: None, - resolved_worker_observation_enabled: false, - resolved_worker_observation_grants: Vec::new(), - resolved_workspace_api: None, - resolved_memory_settings: None, - resolved_control_operation: None, }; let Json(created) = scoped_create_runtime_worker( State(api.clone()), @@ -33364,7 +33790,10 @@ mod tests { ) .await .unwrap(); - let mut worker = created.worker.unwrap(); + let created_worker = created.worker.unwrap(); + let worker_ref = + RuntimeWorkerRef::new(&created_worker.runtime_id, &created_worker.worker_id); + let mut worker = api.runtime.worker(&worker_ref).unwrap(); assert!( api.store .get_worker_registry(TEST_WORKSPACE_ID, &worker.worker) @@ -33633,14 +34062,14 @@ mod tests { let Json(edited) = scoped_edit_ticket_item( State(api.clone()), AxumPath(path()), - Json(BrowserEditTicketRequest { + Json(server_api::BrowserEditTicketRequest { title: Some("Browser Ticket API edited".to_string()), body: Some("Updated from the Browser API.".to_string()), old_string: None, new_string: None, replace_all: false, - target: Some(TicketTargetEdit::Set { - repository_id: "test-repository".to_string(), + target: Some(server_api::BrowserTicketTargetEdit::Set { + repository_key: "test-repository".to_string(), ref_selector: Some("develop".to_string()), }), author: Some("browser-user".to_string()), @@ -33661,8 +34090,8 @@ mod tests { let Json(commented) = scoped_append_ticket_event( State(api.clone()), AxumPath(path()), - Json(BrowserAppendTicketEventRequest { - role: BrowserTicketThreadRole::Comment, + Json(server_api::BrowserAppendTicketEventRequest { + role: server_api::BrowserTicketThreadRole::Comment, body: "API comment".to_string(), author: Some("browser-user".to_string()), }), @@ -33676,7 +34105,7 @@ mod tests { let Json(ready) = scoped_mark_ticket_ready_from_browser( State(api.clone()), AxumPath(path()), - Json(TicketMarkReadyRequest { + Json(InternalTicketMarkReadyRequest { operation_key: "browser-ready".to_owned(), reason: Some("intake complete".to_owned()), intake_summary: None, @@ -33702,7 +34131,7 @@ mod tests { let Json(queue_outcome) = scoped_queue_ticket( State(api.clone()), AxumPath(path()), - Json(BrowserQueueTicketRequest {}), + Json(server_api::BrowserQueueTicketRequest {}), ) .await .unwrap(); @@ -33720,7 +34149,7 @@ mod tests { let Json(closed) = scoped_close_ticket( State(api), AxumPath(path()), - Json(BrowserCloseTicketRequest { + Json(server_api::BrowserCloseTicketRequest { resolution: "Closed through the Browser API.".to_string(), }), ) @@ -34496,7 +34925,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(stopped.state, WorkerOperationState::Accepted); + assert_eq!(stopped.state, InternalWorkerOperationState::Accepted); let worker_root = temp .path() .join(".test-embedded-runtime-store/workers") @@ -35180,7 +35609,7 @@ mod tests { deleted, &[RuntimeDiagnostic { code: "working_directory_not_found".to_string(), - severity: DiagnosticSeverity::Warning, + severity: HostDiagnosticSeverity::Warning, message: "missing".to_string(), }], ) @@ -35206,7 +35635,7 @@ mod tests { unknown, &[RuntimeDiagnostic { code: "runtime_unavailable".to_string(), - severity: DiagnosticSeverity::Warning, + severity: HostDiagnosticSeverity::Warning, message: "temporarily unavailable".to_string(), }], ) @@ -35413,7 +35842,7 @@ mod tests { } fn workdir_removal_result( - state: WorkerOperationState, + state: InternalWorkerOperationState, summary: Option, diagnostics: Vec, ) -> crate::hosts::RuntimeWorkingDirectoryResult { @@ -35461,11 +35890,11 @@ mod tests { let (clean_operation, clean_summary) = reserve_removal_fixture(&api, "clean-provider"); let clean_provider = FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, Some(clean_summary), Vec::new(), ), - workdir_removal_result(WorkerOperationState::Accepted, None, Vec::new()), + workdir_removal_result(InternalWorkerOperationState::Accepted, None, Vec::new()), ); let removed = execute_reserved_workdir_removal_with_provider( &api, @@ -35492,16 +35921,20 @@ mod tests { let (missing_operation, _) = reserve_removal_fixture(&api, "provider-not-found"); let not_found = RuntimeDiagnostic { code: "working_directory_not_found".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "missing".to_string(), }; let missing_provider = FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Rejected, + InternalWorkerOperationState::Rejected, None, vec![not_found.clone()], ), - workdir_removal_result(WorkerOperationState::Rejected, None, vec![not_found]), + workdir_removal_result( + InternalWorkerOperationState::Rejected, + None, + vec![not_found], + ), ); let removed = execute_reserved_workdir_removal_with_provider( &api, @@ -35519,15 +35952,15 @@ mod tests { let (unknown_operation, _) = reserve_removal_fixture(&api, "provider-unknown"); let unknown_provider = FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, None, vec![RuntimeDiagnostic { code: "working_directory_provider_timeout".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "timeout".to_string(), }], ), - workdir_removal_result(WorkerOperationState::Accepted, None, Vec::new()), + workdir_removal_result(InternalWorkerOperationState::Accepted, None, Vec::new()), ); let unknown = execute_reserved_workdir_removal_with_provider( &api, @@ -35545,11 +35978,11 @@ mod tests { dirty_summary.cleanliness = Some("dirty".to_string()); let dirty_provider = FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, Some(dirty_summary), Vec::new(), ), - workdir_removal_result(WorkerOperationState::Accepted, None, Vec::new()), + workdir_removal_result(InternalWorkerOperationState::Accepted, None, Vec::new()), ); let dirty = execute_reserved_workdir_removal_with_provider( &api, @@ -35563,11 +35996,11 @@ mod tests { let clean_retry_provider = FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, Some(clean_retry_summary), Vec::new(), ), - workdir_removal_result(WorkerOperationState::Accepted, None, Vec::new()), + workdir_removal_result(InternalWorkerOperationState::Accepted, None, Vec::new()), ); let removed_after_retry = execute_reserved_workdir_removal_with_provider( &api, @@ -35589,11 +36022,11 @@ mod tests { corrupted_summary.cleanliness = Some("unknown".to_string()); let corrupted_provider = FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, Some(corrupted_summary), Vec::new(), ), - workdir_removal_result(WorkerOperationState::Accepted, None, Vec::new()), + workdir_removal_result(InternalWorkerOperationState::Accepted, None, Vec::new()), ); let corrupted = execute_reserved_workdir_removal_with_provider( &api, @@ -35612,16 +36045,16 @@ mod tests { reserve_removal_fixture(&api, "provider-unsupported"); let unsupported_provider = FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, Some(unsupported_summary), Vec::new(), ), workdir_removal_result( - WorkerOperationState::Unsupported, + InternalWorkerOperationState::Unsupported, None, vec![RuntimeDiagnostic { code: "working_directory_unsupported".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "unsupported".to_string(), }], ), @@ -35663,11 +36096,11 @@ mod tests { let provider = Arc::new( FakeWorkdirRemovalProvider::new( workdir_removal_result( - WorkerOperationState::Accepted, + InternalWorkerOperationState::Accepted, Some(clean_summary), Vec::new(), ), - workdir_removal_result(WorkerOperationState::Accepted, None, Vec::new()), + workdir_removal_result(InternalWorkerOperationState::Accepted, None, Vec::new()), ) .with_observation_delay(std::time::Duration::from_millis(100)), ); @@ -35717,22 +36150,22 @@ mod tests { #[test] fn only_exact_provider_not_found_is_removal_evidence() { let not_found = crate::hosts::RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![RuntimeDiagnostic { code: "working_directory_not_found".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "missing".to_string(), }], }; assert!(runtime_reports_workdir_not_found(¬_found)); let unknown = crate::hosts::RuntimeWorkingDirectoryResult { - state: WorkerOperationState::Rejected, + state: InternalWorkerOperationState::Rejected, working_directory: None, diagnostics: vec![RuntimeDiagnostic { code: "working_directory_provider_timeout".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "timeout".to_string(), }], }; @@ -36155,7 +36588,7 @@ mod tests { }, ) .unwrap(); - assert_eq!(stopped.state, WorkerOperationState::Accepted); + assert_eq!(stopped.state, InternalWorkerOperationState::Accepted); let worker_root = workspace .path() .join(".test-embedded-runtime-store/workers") @@ -37275,12 +37708,12 @@ mod tests { let diagnostics = vec![ RuntimeDiagnostic { code: "runtime_capacity_warning".to_string(), - severity: DiagnosticSeverity::Warning, + severity: HostDiagnosticSeverity::Warning, message: "capacity is low".to_string(), }, RuntimeDiagnostic { code: "runtime_capacity_unavailable".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "capacity is exhausted".to_string(), }, ]; @@ -37342,7 +37775,7 @@ mod tests { .unwrap(); let diagnostics = vec![RuntimeDiagnostic { code: "runtime_capacity_unavailable".to_string(), - severity: DiagnosticSeverity::Error, + severity: HostDiagnosticSeverity::Error, message: "capacity is exhausted".to_string(), }]; @@ -38371,7 +38804,7 @@ mod tests { "unsupported-runtime".to_string(), vec![settings_diagnostic( "remote_runtime_unsupported", - DiagnosticSeverity::Warning, + HostDiagnosticSeverity::Warning, "Remote Runtime provisioning is unsupported by this v0 worker launch path.", )], ) @@ -38401,24 +38834,18 @@ mod tests { let Json(runtime_response) = create_runtime_worker( State(api.clone()), AxumPath(WorkdirlessFixtureRuntime::RUNTIME_ID.to_string()), - Json(WorkerSpawnRequest { - intent: WorkerSpawnIntent::WorkspaceCompanion, + Json(server_api::RuntimeWorkerSpawnRequest { + intent: server_api::RuntimeWorkerSpawnIntent::WorkspaceCompanion, requested_worker_name: Some("Runtime Worker".to_string()), - acceptance: WorkerSpawnAcceptanceRequirement::RunAccepted { + acceptance: server_api::RuntimeWorkerSpawnAcceptanceRequirement::RunAccepted { expected_segments: 0, }, - profile: ProfileSelector::Builtin("builtin:companion".to_string()), + profile: server_api::RuntimeProfileSelector::Builtin( + "builtin:companion".to_string(), + ), ticket_assignment: None, initial_submit: Vec::new(), workdir_attachment_requests: Vec::new(), - resolved_workdir_attachment_requests: Vec::new(), - resolved_workdir_attachments: Vec::new(), - resolved_config_bundle: None, - resolved_worker_observation_enabled: false, - resolved_worker_observation_grants: Vec::new(), - resolved_workspace_api: None, - resolved_memory_settings: None, - resolved_control_operation: None, }), ) .await @@ -39199,7 +39626,7 @@ mod tests { .runtime .sync_config_bundle("embedded-worker-runtime", bundle) .expect("sync config bundle"); - assert_eq!(synced.state, WorkerOperationState::Accepted); + assert_eq!(synced.state, InternalWorkerOperationState::Accepted); assert!(store_root.exists(), "fs-store root should be created"); let spawned = api @@ -39233,7 +39660,7 @@ mod tests { }, ) .expect("spawn worker"); - assert_eq!(spawned.state, WorkerOperationState::Accepted); + assert_eq!(spawned.state, InternalWorkerOperationState::Accepted); let worker_id = spawned.worker.expect("created worker").worker.worker_id; let worker_ref = RuntimeWorkerRef::new("embedded-worker-runtime", &worker_id); let sent = api @@ -39247,7 +39674,7 @@ mod tests { }, ) .expect("send input"); - assert_eq!(sent.state, WorkerOperationState::Accepted); + assert_eq!(sent.state, InternalWorkerOperationState::Accepted); let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); loop { @@ -39302,7 +39729,7 @@ mod tests { }, ) .expect("stale worker input is projected as an operation result"); - assert_eq!(rejected_input.state, WorkerOperationState::Rejected); + assert_eq!(rejected_input.state, InternalWorkerOperationState::Rejected); } #[tokio::test] @@ -39882,7 +40309,7 @@ mod tests { let spawned = api .spawn_workspace_worker(EMBEDDED_WORKER_RUNTIME_ID, spawn_request) .unwrap(); - assert_eq!(spawned.state, WorkerOperationState::Accepted); + assert_eq!(spawned.state, InternalWorkerOperationState::Accepted); let worker_id = spawned.worker.unwrap().worker.worker_id; let (_, projections) = api .store diff --git a/crates/yoi/src/workdir_share.rs b/crates/yoi/src/workdir_share.rs index 72438a218..1786e7524 100644 --- a/crates/yoi/src/workdir_share.rs +++ b/crates/yoi/src/workdir_share.rs @@ -7,7 +7,9 @@ use std::time::{Duration, Instant}; use chrono::Utc; use client::BackendApiClient; use futures::{SinkExt, StreamExt}; -use server_api::{ExternalWorkdirGrantCreateRequest, ExternalWorkdirGrantResponse}; +use server_api::{ + ExternalWorkdirGrantCreateRequest, ExternalWorkdirGrantResponse, ServerApiClient, +}; use tokio_tungstenite::connect_async_with_config; use tokio_tungstenite::tungstenite::{Message, protocol::WebSocketConfig}; use workdir::external::{ @@ -95,40 +97,57 @@ fn pin_selected_root_from( ExternalWorkdirRoot::pin(absolute).map_err(|error| error.to_string()) } +fn authenticated_server_api_client( + backend_client: &BackendApiClient, + backend_url: &str, +) -> Result { + // BackendApiClient remains the stored-credential authority used by the + // manual provider WebSocket. Reuse that transport's Authorization header + // for the generated JSON client instead of loading or interpreting the + // credential a second time here. + let authenticated_request = backend_client + .websocket_request("/") + .map_err(|error| error.to_string())?; + let authorization = authenticated_request + .headers() + .get(reqwest::header::AUTHORIZATION) + .cloned() + .ok_or_else(|| "stored Backend credential did not provide Authorization".to_string())?; + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert(reqwest::header::AUTHORIZATION, authorization); + let http_client = reqwest::Client::builder() + .default_headers(headers) + .timeout(Duration::from_secs(10)) + .build() + .map_err(|error| format!("failed to configure Backend client: {error}"))?; + let base_url = backend_url + .try_into() + .map_err(|error| format!("invalid Backend URL: {error}"))?; + Ok(ServerApiClient::with_client(base_url, http_client)) +} + pub(crate) async fn run(options: WorkdirShareOptions) -> Result<(), String> { // Pin before creating remote authority: the Backend grant can never outlive // a failed local approval/open race, and subsequent path replacement cannot // redirect the provider. let pinned_root = pin_selected_root(&options.path)?; - let client = BackendApiClient::from_stored_token(&options.backend_url) + let backend_client = BackendApiClient::from_stored_token(&options.backend_url) .map_err(|error| error.to_string())?; + let server_api_client = authenticated_server_api_client(&backend_client, &options.backend_url)?; let provider_instance_id = format!("cli-{}", uuid::Uuid::now_v7()); - let create_path = format!( - "/api/w/{}/external-workdir-grants", - encode_path_segment(&options.workspace_id) - ); - let response = client - .request(reqwest::Method::POST, &create_path) - .map_err(|error| error.to_string())? - .timeout(Duration::from_secs(10)) - .json(&ExternalWorkdirGrantCreateRequest { - provider_instance_id: provider_instance_id.clone(), - display_name: options.display_name.clone(), - ttl_seconds: options.ttl.as_secs(), - read_only: true, - }) - .send() + let grant = server_api_client + .external_workdir_grant_create( + options.workspace_id.clone(), + ExternalWorkdirGrantCreateRequest { + provider_instance_id: provider_instance_id.clone(), + display_name: options.display_name.clone(), + ttl_seconds: options.ttl.as_secs(), + read_only: true, + }, + ) .await .map_err(|error| format!("External Workdir grant request failed: {error}"))?; - let response = client - .require_success(response) - .await - .map_err(|error| error.to_string())?; - let grant = response - .json::() - .await - .map_err(|error| format!("Backend returned an invalid External Workdir grant: {error}"))?; let limits = BoundedReadLimits::EXTERNAL_DEFAULT; let session = LocalWorkdirSession::external_read_only_pinned( @@ -150,7 +169,8 @@ pub(crate) async fn run(options: WorkdirShareOptions) -> Result<(), String> { let mut generation = grant.generation; loop { let outcome = serve_provider_connection( - &client, + &backend_client, + &server_api_client, &options, &grant, &provider_instance_id, @@ -166,11 +186,12 @@ pub(crate) async fn run(options: WorkdirShareOptions) -> Result<(), String> { "External Workdir grant expired after provider connection failure: {connection_error}" )); } - let refresh = fetch_grant(&client, &options.workspace_id, &grant.grant_id); + let refresh = + fetch_grant(&server_api_client, &options.workspace_id, &grant.grant_id); let refreshed = tokio::select! { signal = tokio::signal::ctrl_c() => { signal.map_err(|error| format!("failed to wait for Ctrl-C: {error}"))?; - revoke(&client, &options.workspace_id, &grant.grant_id).await?; + revoke(&server_api_client, &options.workspace_id, &grant.grant_id).await?; println!("External Workdir grant revoked."); return Ok(()); } @@ -182,8 +203,12 @@ pub(crate) async fn run(options: WorkdirShareOptions) -> Result<(), String> { eprintln!( "Connection refresh failed; retrying: {connection_error}; {refresh_error}" ); - if wait_to_reconnect(&client, &options.workspace_id, &grant.grant_id) - .await? + if wait_to_reconnect( + &server_api_client, + &options.workspace_id, + &grant.grant_id, + ) + .await? { println!("External Workdir grant revoked."); return Ok(()); @@ -213,7 +238,9 @@ pub(crate) async fn run(options: WorkdirShareOptions) -> Result<(), String> { } } println!("Connection: reconnecting"); - if wait_to_reconnect(&client, &options.workspace_id, &grant.grant_id).await? { + if wait_to_reconnect(&server_api_client, &options.workspace_id, &grant.grant_id) + .await? + { println!("External Workdir grant revoked."); return Ok(()); } @@ -237,7 +264,9 @@ pub(crate) async fn run(options: WorkdirShareOptions) -> Result<(), String> { .checked_add(1) .ok_or_else(|| "External Workdir provider generation overflow".to_string())?; println!("Connection: reconnecting"); - if wait_to_reconnect(&client, &options.workspace_id, &grant.grant_id).await? { + if wait_to_reconnect(&server_api_client, &options.workspace_id, &grant.grant_id) + .await? + { println!("External Workdir grant revoked."); return Ok(()); } @@ -247,7 +276,8 @@ pub(crate) async fn run(options: WorkdirShareOptions) -> Result<(), String> { } async fn serve_provider_connection( - client: &BackendApiClient, + backend_client: &BackendApiClient, + server_api_client: &ServerApiClient, options: &WorkdirShareOptions, grant: &ExternalWorkdirGrantResponse, provider_instance_id: &str, @@ -259,7 +289,7 @@ async fn serve_provider_connection( encode_path_segment(&options.workspace_id), encode_path_segment(&grant.grant_id) ); - let request = client + let request = backend_client .websocket_request(&provider_path) .map_err(|error| error.to_string())?; let websocket_config = WebSocketConfig::default() @@ -269,7 +299,7 @@ async fn serve_provider_connection( let (mut socket, _) = tokio::select! { signal = tokio::signal::ctrl_c() => { signal.map_err(|error| format!("failed to wait for Ctrl-C: {error}"))?; - revoke(client, &options.workspace_id, &grant.grant_id).await?; + revoke(server_api_client, &options.workspace_id, &grant.grant_id).await?; return Ok(ProviderEnd::Interrupted); } connection = connection => connection @@ -292,7 +322,7 @@ async fn serve_provider_connection( let registered = tokio::select! { signal = tokio::signal::ctrl_c() => { signal.map_err(|error| format!("failed to wait for Ctrl-C: {error}"))?; - revoke(client, &options.workspace_id, &grant.grant_id).await?; + revoke(server_api_client, &options.workspace_id, &grant.grant_id).await?; return Ok(ProviderEnd::Interrupted); } registered = tokio::time::timeout(Duration::from_secs(10), socket.next()) => { @@ -324,7 +354,7 @@ async fn serve_provider_connection( signal = tokio::signal::ctrl_c() => { signal.map_err(|error| format!("failed to wait for Ctrl-C: {error}"))?; stop_operations(&mut operations, &mut completions).await?; - revoke(client, &options.workspace_id, &grant.grant_id).await?; + revoke(server_api_client, &options.workspace_id, &grant.grant_id).await?; return Ok(ProviderEnd::Interrupted); } completion = completions.recv() => { @@ -558,7 +588,7 @@ where } async fn wait_to_reconnect( - client: &BackendApiClient, + client: &ServerApiClient, workspace_id: &str, grant_id: &str, ) -> Result { @@ -573,51 +603,27 @@ async fn wait_to_reconnect( } async fn fetch_grant( - client: &BackendApiClient, + client: &ServerApiClient, workspace_id: &str, grant_id: &str, ) -> Result { - let path = format!( - "/api/w/{}/external-workdir-grants/{}", - encode_path_segment(workspace_id), - encode_path_segment(grant_id) - ); - let response = client - .request(reqwest::Method::GET, &path) - .map_err(|error| error.to_string())? - .timeout(Duration::from_secs(10)) - .send() - .await - .map_err(|error| format!("External Workdir grant refresh failed: {error}"))?; client - .require_success(response) + .external_workdir_grant_get(workspace_id.to_string(), grant_id.to_string()) .await - .map_err(|error| error.to_string())? - .json::() - .await - .map_err(|error| format!("Backend returned an invalid External Workdir grant: {error}")) + .map_err(|error| format!("External Workdir grant refresh failed: {error}")) } async fn revoke( - client: &BackendApiClient, + client: &ServerApiClient, workspace_id: &str, grant_id: &str, ) -> Result<(), String> { - let path = format!( - "/api/w/{}/external-workdir-grants/{}", - encode_path_segment(workspace_id), - encode_path_segment(grant_id) - ); - let response = client - .request(reqwest::Method::DELETE, &path) - .map_err(|error| error.to_string())? - .timeout(Duration::from_secs(10)) - .send() + client + .external_workdir_grant_revoke(workspace_id.to_string(), grant_id.to_string()) .await - .map_err(|error| format!("External Workdir revoke request failed: {error}"))?; - client.require_success(response).await.map_err(|error| { - format!("External Workdir revoke was not confirmed by the Backend: {error}") - })?; + .map_err(|error| { + format!("External Workdir revoke was not confirmed by the Backend: {error}") + })?; Ok(()) } @@ -636,6 +642,169 @@ fn encode_path_segment(value: &str) -> String { #[cfg(test)] mod tests { use super::*; + use std::io::{Read, Write}; + use std::net::{TcpListener, TcpStream}; + use std::sync::mpsc; + + fn test_server_api_client(base_url: &str) -> ServerApiClient { + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert( + reqwest::header::AUTHORIZATION, + reqwest::header::HeaderValue::from_static("Bearer test-token"), + ); + let http_client = reqwest::Client::builder() + .default_headers(headers) + .timeout(Duration::from_secs(10)) + .build() + .unwrap(); + ServerApiClient::with_client(base_url.try_into().unwrap(), http_client) + } + + fn read_http_request(stream: &mut TcpStream) -> String { + stream + .set_read_timeout(Some(Duration::from_secs(5))) + .unwrap(); + let mut request = Vec::new(); + let mut buffer = [0_u8; 4096]; + let mut expected_length = None; + loop { + let read = stream.read(&mut buffer).unwrap(); + assert!(read > 0, "HTTP client closed before completing request"); + request.extend_from_slice(&buffer[..read]); + if expected_length.is_none() + && let Some(header_end) = + request.windows(4).position(|window| window == b"\r\n\r\n") + { + let headers = String::from_utf8_lossy(&request[..header_end]); + let content_length = headers.lines().find_map(|line| { + let (name, value) = line.split_once(':')?; + name.eq_ignore_ascii_case("content-length") + .then(|| value.trim().parse::().unwrap()) + }); + expected_length = Some(header_end + 4 + content_length.unwrap_or(0)); + } + if expected_length.is_some_and(|length| request.len() >= length) { + break; + } + } + String::from_utf8(request).unwrap() + } + + fn spawn_json_server( + response_body: String, + response_count: usize, + ) -> (String, mpsc::Receiver, std::thread::JoinHandle<()>) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let (sender, receiver) = mpsc::channel(); + let thread = std::thread::spawn(move || { + for _ in 0..response_count { + let (mut stream, _) = listener.accept().unwrap(); + let request = read_http_request(&mut stream); + let status = if request.starts_with("POST ") { + "201 Created" + } else { + "200 OK" + }; + sender.send(request).unwrap(); + write!( + stream, + "HTTP/1.1 {status}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", + response_body.len(), + response_body + ) + .unwrap(); + } + }); + (format!("http://{address}"), receiver, thread) + } + + fn grant_response_json() -> String { + serde_json::json!({ + "grant_id": "grant/a b", + "workspace_id": "workspace/a b", + "working_directory_id": "workdir-a", + "provider_instance_id": "provider-a", + "display_name": "Shared files", + "permissions": "read_only", + "expires_at": "2026-09-23T03:00:00Z", + "generation": 7, + "status": "pending" + }) + .to_string() + } + + #[tokio::test] + async fn generated_grant_operations_preserve_auth_and_encode_contract_paths() { + let (base_url, requests, server) = spawn_json_server(grant_response_json(), 3); + let client = test_server_api_client(&base_url); + + let created = client + .external_workdir_grant_create( + "workspace/a b".to_string(), + ExternalWorkdirGrantCreateRequest { + provider_instance_id: "provider-a".to_string(), + display_name: "Shared files".to_string(), + ttl_seconds: 600, + read_only: true, + }, + ) + .await + .unwrap(); + let fetched = fetch_grant(&client, "workspace/a b", "grant/a b") + .await + .unwrap(); + revoke(&client, "workspace/a b", "grant/a b").await.unwrap(); + + assert_eq!(created.grant_id, "grant/a b"); + assert_eq!(fetched.generation, 7); + let requests = (0..3) + .map(|_| requests.recv_timeout(Duration::from_secs(5)).unwrap()) + .collect::>(); + server.join().unwrap(); + assert!( + requests[0] + .starts_with("POST /api/w/workspace%2Fa%20b/external-workdir-grants HTTP/1.1\r\n") + ); + assert!(requests[1].starts_with( + "GET /api/w/workspace%2Fa%20b/external-workdir-grants/grant%2Fa%20b HTTP/1.1\r\n" + )); + assert!(requests[2].starts_with( + "DELETE /api/w/workspace%2Fa%20b/external-workdir-grants/grant%2Fa%20b HTTP/1.1\r\n" + )); + for request in &requests { + assert!( + request + .to_ascii_lowercase() + .contains("\r\nauthorization: bearer test-token\r\n") + ); + } + assert!(requests[0].contains("\"provider_instance_id\":\"provider-a\"")); + assert!(requests[0].contains("\"ttl_seconds\":600")); + } + + #[tokio::test] + async fn generated_grant_response_is_bounded() { + let oversized_body = format!("{{\"padding\":\"{}\"}}", "x".repeat(1_048_576)); + let (base_url, requests, server) = spawn_json_server(oversized_body, 1); + let client = test_server_api_client(&base_url); + + let error = fetch_grant(&client, "workspace-a", "grant-a") + .await + .unwrap_err(); + + let request = requests.recv_timeout(Duration::from_secs(5)).unwrap(); + server.join().unwrap(); + assert!( + request + .starts_with("GET /api/w/workspace-a/external-workdir-grants/grant-a HTTP/1.1\r\n") + ); + assert!(error.contains("External Workdir grant refresh failed")); + assert!( + error.contains("response body exceeded 1048576 bytes"), + "{error}" + ); + } #[test] fn relative_share_path_is_resolved_and_pinned_before_backend_access() { diff --git a/openapi/server-api.json b/openapi/server-api.json index 0e072f255..3dc4f0db8 100644 --- a/openapi/server-api.json +++ b/openapi/server-api.json @@ -773,143 +773,6 @@ }, "type": "object" }, - "CommandOutput": { - "properties": { - "content": { - "type": "string" - }, - "exit_code": { - "format": "int32", - "maximum": 2147483647, - "minimum": -2147483648, - "type": [ - "integer", - "null" - ] - }, - "next_cursor": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": [ - "integer", - "null" - ] - }, - "output_path": { - "description": "Complete output retained by the provider when `truncated` is true.", - "type": [ - "string", - "null" - ] - }, - "status": { - "$ref": "#/components/schemas/CommandStatus" - }, - "timed_out": { - "type": "boolean" - }, - "truncated": { - "type": "boolean" - } - }, - "required": [ - "status", - "timed_out", - "content", - "truncated" - ], - "type": "object" - }, - "CommandOutputRequest": { - "properties": { - "cursor": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "handle": { - "type": "string" - }, - "limit": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "wait": { - "type": "boolean" - } - }, - "required": [ - "handle", - "cursor", - "limit", - "wait" - ], - "type": "object" - }, - "CommandRequest": { - "properties": { - "command": { - "type": "string" - }, - "cwd": { - "anyOf": [ - { - "$ref": "#/components/schemas/FsPath" - }, - { - "type": "null" - } - ], - "description": "Workdir-relative command directory. Providers validate it against the\nactive session before process start." - }, - "output_limit": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "spill_dir": { - "description": "Provider-local directory where complete output is retained when the\ninline result exceeds `output_limit`.", - "type": [ - "string", - "null" - ] - }, - "timeout_secs": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "tool_call_id": { - "description": "Optional caller-owned correlation id. Bash supplies its tool-call id so\nuser-facing command telemetry can update the corresponding Console row\nwithout exposing provider/session handles.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "command", - "timeout_secs", - "output_limit" - ], - "type": "object" - }, - "CommandStatus": { - "enum": [ - "running", - "completed", - "failed", - "timed_out", - "cancelled" - ], - "type": "string" - }, "CompanionCancelRequest": { "additionalProperties": false, "description": "Public Workspace Companion cancellation request.", @@ -1215,38 +1078,6 @@ ], "type": "object" }, - "CompleteMergeRequestRequest": { - "additionalProperties": false, - "properties": { - "approval_event_id": { - "type": "string" - }, - "operation_id": { - "type": "string" - }, - "resolution": { - "$ref": "#/components/schemas/ConflictResolution" - }, - "strategy": { - "$ref": "#/components/schemas/MergeStrategy" - }, - "target_ref_after": { - "type": "string" - }, - "target_ref_before": { - "type": "string" - } - }, - "required": [ - "operation_id", - "approval_event_id", - "target_ref_before", - "target_ref_after", - "strategy", - "resolution" - ], - "type": "object" - }, "ConfigCommitRequest": { "additionalProperties": false, "description": "Compare-and-swap request for applying a complete set of source-tree changes.", @@ -1758,66 +1589,6 @@ ], "type": "object" }, - "CurrentWorkerWorkdirAttachRequest": { - "additionalProperties": false, - "properties": { - "alias": { - "type": "string" - }, - "working_directory_id": { - "type": "string" - } - }, - "required": [ - "alias", - "working_directory_id" - ], - "type": "object" - }, - "CurrentWorkerWorkdirAttachmentResponse": { - "additionalProperties": false, - "properties": { - "alias": { - "type": "string" - }, - "attached": { - "type": "boolean" - }, - "capabilities": { - "$ref": "#/components/schemas/WorkdirSessionCapabilities" - }, - "working_directory_id": { - "type": "string" - }, - "workspace_id": { - "type": "string" - } - }, - "required": [ - "workspace_id", - "alias", - "working_directory_id", - "capabilities", - "attached" - ], - "type": "object" - }, - "CurrentWorkerWorkdirOperationRequest": { - "additionalProperties": false, - "properties": { - "operation": { - "$ref": "#/components/schemas/WorkdirSessionOperation" - }, - "target_workdir": { - "type": "string" - } - }, - "required": [ - "target_workdir", - "operation" - ], - "type": "object" - }, "DefaultIntakeReadyBodyRequest": { "additionalProperties": false, "properties": { @@ -2069,76 +1840,6 @@ ], "type": "string" }, - "EditRequest": { - "additionalProperties": false, - "properties": { - "expected_hash": { - "items": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - }, - "maxItems": 32, - "minItems": 32, - "type": "array" - }, - "new_string": { - "type": "string" - }, - "old_string": { - "type": "string" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - }, - "replace_all": { - "type": "boolean" - } - }, - "required": [ - "path", - "old_string", - "new_string", - "replace_all", - "expected_hash" - ], - "type": "object" - }, - "EditResult": { - "additionalProperties": false, - "properties": { - "bytes_written": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "content_hash": { - "items": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - }, - "maxItems": 32, - "minItems": 32, - "type": "array" - }, - "replacements": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "replacements", - "bytes_written", - "content_hash" - ], - "type": "object" - }, "EffectivePromptCatalog": { "additionalProperties": false, "properties": { @@ -2176,15 +1877,6 @@ ], "type": "object" }, - "EntryKind": { - "enum": [ - "file", - "directory", - "symlink", - "other" - ], - "type": "string" - }, "EvidenceKind": { "description": "Extensible evidence kind tag used by staging source anchors.\n\nKnown values include `message`, `tool_call`, `tool_result`, `file_ref`,\n`ticket_ref`, and `objective_ref`, but callers may use newer bounded tags\nwithout changing the schema.", "type": "string" @@ -2582,10 +2274,6 @@ ], "type": "object" }, - "FsPath": { - "description": "Scope-checked filesystem path. Relative paths resolve below the bound\nWorkdir root; absolute paths require an explicit matching scope rule.", - "type": "string" - }, "GenerateRepositorySshCredentialRequest": { "additionalProperties": false, "properties": { @@ -2703,155 +2391,6 @@ ], "type": "object" }, - "GlobRequest": { - "additionalProperties": false, - "properties": { - "limit": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - }, - "pattern": { - "type": "string" - } - }, - "required": [ - "pattern", - "path", - "limit" - ], - "type": "object" - }, - "GlobResult": { - "additionalProperties": false, - "properties": { - "paths": { - "items": { - "$ref": "#/components/schemas/FsPath" - }, - "type": "array" - }, - "truncated": { - "type": "boolean" - } - }, - "required": [ - "paths", - "truncated" - ], - "type": "object" - }, - "GrepOutputMode": { - "enum": [ - "content", - "files_with_matches", - "count" - ], - "type": "string" - }, - "GrepRequest": { - "additionalProperties": false, - "properties": { - "after_context": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "before_context": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "case_insensitive": { - "type": "boolean" - }, - "file_type": { - "type": [ - "string", - "null" - ] - }, - "glob": { - "type": [ - "string", - "null" - ] - }, - "limit": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "multiline": { - "type": "boolean" - }, - "offset": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "output_mode": { - "$ref": "#/components/schemas/GrepOutputMode" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - }, - "pattern": { - "type": "string" - } - }, - "required": [ - "pattern", - "path", - "case_insensitive", - "before_context", - "after_context", - "multiline", - "output_mode", - "limit", - "offset" - ], - "type": "object" - }, - "GrepResult": { - "additionalProperties": false, - "properties": { - "match_count": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "matched_files": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "output": { - "description": "Provider-rendered bounded grep report. Keeping rendering here avoids\ntransferring candidate files across a remote provider boundary.", - "type": "string" - }, - "truncated": { - "type": "boolean" - } - }, - "required": [ - "output", - "match_count", - "matched_files", - "truncated" - ], - "type": "object" - }, "HealthResponse": { "additionalProperties": false, "properties": { @@ -3032,81 +2571,6 @@ ], "type": "object" }, - "ListEntry": { - "additionalProperties": false, - "properties": { - "kind": { - "$ref": "#/components/schemas/EntryKind" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - }, - "size": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "path", - "kind", - "size" - ], - "type": "object" - }, - "ListRequest": { - "additionalProperties": false, - "properties": { - "limit": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - } - }, - "required": [ - "path", - "limit" - ], - "type": "object" - }, - "ListResult": { - "additionalProperties": false, - "properties": { - "entries": { - "items": { - "$ref": "#/components/schemas/ListEntry" - }, - "type": "array" - }, - "total_bytes": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "total_entries": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "truncated": { - "type": "boolean" - } - }, - "required": [ - "entries", - "total_entries", - "total_bytes", - "truncated" - ], - "type": "object" - }, "MarkdownText": { "type": "string" }, @@ -4053,61 +3517,6 @@ ], "type": "object" }, - "MergeEvent": { - "additionalProperties": false, - "properties": { - "approval_event_id": { - "type": "string" - }, - "approved_source_ref": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "event_id": { - "type": "string" - }, - "merged_by": { - "$ref": "#/components/schemas/MergeRequestWorkerIdentity" - }, - "operation_id": { - "type": "string" - }, - "resolution": { - "$ref": "#/components/schemas/ConflictResolution" - }, - "sequence": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "strategy": { - "$ref": "#/components/schemas/MergeStrategy" - }, - "target_ref_after": { - "type": "string" - }, - "target_ref_before": { - "type": "string" - } - }, - "required": [ - "event_id", - "sequence", - "operation_id", - "approval_event_id", - "approved_source_ref", - "target_ref_before", - "target_ref_after", - "strategy", - "resolution", - "merged_by", - "created_at" - ], - "type": "object" - }, "MergeRequestDetailResponse": { "additionalProperties": false, "properties": { @@ -5471,30 +4880,6 @@ ], "type": "object" }, - "OpenMergeRequestRequest": { - "additionalProperties": false, - "properties": { - "repository_key": { - "type": "string" - }, - "selector_from": { - "type": "string" - }, - "selector_to": { - "type": "string" - }, - "summary": { - "default": "", - "type": "string" - } - }, - "required": [ - "repository_key", - "selector_from", - "selector_to" - ], - "type": "object" - }, "OperationCounts": { "properties": { "delete": { @@ -5974,91 +5359,6 @@ ], "type": "object" }, - "ReadRequest": { - "additionalProperties": false, - "properties": { - "limit": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "max_bytes": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "offset": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - } - }, - "required": [ - "path", - "offset", - "limit", - "max_bytes" - ], - "type": "object" - }, - "ReadResult": { - "additionalProperties": false, - "properties": { - "bytes": { - "items": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - }, - "type": "array" - }, - "content_hash": { - "items": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - }, - "maxItems": 32, - "minItems": 32, - "type": "array" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - }, - "start_line": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "total_lines": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "truncated": { - "type": "boolean" - } - }, - "required": [ - "path", - "bytes", - "start_line", - "total_lines", - "content_hash", - "truncated" - ], - "type": "object" - }, "RecordOperationAudit": { "properties": { "after_hash": { @@ -6156,34 +5456,6 @@ ], "type": "object" }, - "RegisterMergeRequestReviewCapabilityRequest": { - "additionalProperties": false, - "properties": { - "capability_token": { - "type": "string" - }, - "child_session_id": { - "type": "string" - } - }, - "required": [ - "child_session_id", - "capability_token" - ], - "type": "object" - }, - "RegisterReviewerChildSessionRequest": { - "additionalProperties": false, - "properties": { - "child_session_id": { - "type": "string" - } - }, - "required": [ - "child_session_id" - ], - "type": "object" - }, "RemoveRuntimeRequest": { "additionalProperties": false, "properties": { @@ -7038,65 +6310,6 @@ ], "type": "object" }, - "ReviewRevokedEvent": { - "additionalProperties": false, - "properties": { - "created_at": { - "type": "string" - }, - "event_id": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "review_event_id": { - "type": "string" - }, - "revoked_by": { - "$ref": "#/components/schemas/MergeRequestWorkerIdentity" - }, - "sequence": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "subject_ref": { - "type": "string" - } - }, - "required": [ - "event_id", - "sequence", - "review_event_id", - "subject_ref", - "reason", - "revoked_by", - "created_at" - ], - "type": "object" - }, - "RevokeMergeRequestReviewRequest": { - "additionalProperties": false, - "properties": { - "explicit_confirmation": { - "type": "boolean" - }, - "reason": { - "type": "string" - }, - "review_event_id": { - "type": "string" - } - }, - "required": [ - "review_event_id", - "reason", - "explicit_confirmation" - ], - "type": "object" - }, "RevokeRuntimeTrustKeyRequest": { "additionalProperties": false, "properties": { @@ -7560,42 +6773,6 @@ } ] }, - "RuntimeProfileSourceGraphSummary": { - "additionalProperties": false, - "properties": { - "entrypoints": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "import_count": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "source_count": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "total_source_bytes": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "source_count", - "total_source_bytes", - "entrypoints", - "import_count" - ], - "type": "object" - }, "RuntimePublicIdentityBundle": { "additionalProperties": false, "properties": { @@ -7675,355 +6852,6 @@ ], "type": "string" }, - "RuntimeResourceFetchApiError": { - "oneOf": [ - { - "properties": { - "code": { - "const": "expired", - "type": "string" - } - }, - "required": [ - "code" - ], - "type": "object" - }, - { - "properties": { - "code": { - "const": "unauthorized", - "type": "string" - }, - "message": { - "type": "string" - } - }, - "required": [ - "code", - "message" - ], - "type": "object" - }, - { - "properties": { - "code": { - "const": "unsupported_kind", - "type": "string" - } - }, - "required": [ - "code" - ], - "type": "object" - }, - { - "properties": { - "code": { - "const": "missing_resource", - "type": "string" - } - }, - "required": [ - "code" - ], - "type": "object" - }, - { - "properties": { - "actual": { - "type": "string" - }, - "code": { - "const": "digest_mismatch", - "type": "string" - }, - "expected": { - "type": "string" - } - }, - "required": [ - "code", - "expected", - "actual" - ], - "type": "object" - }, - { - "properties": { - "actual_bytes": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "code": { - "const": "oversized", - "type": "string" - }, - "max_bytes": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "code", - "max_bytes", - "actual_bytes" - ], - "type": "object" - }, - { - "properties": { - "actual": { - "type": "string" - }, - "code": { - "const": "content_type_mismatch", - "type": "string" - }, - "expected": { - "type": "string" - } - }, - "required": [ - "code", - "expected", - "actual" - ], - "type": "object" - }, - { - "properties": { - "code": { - "const": "timeout", - "type": "string" - } - }, - "required": [ - "code" - ], - "type": "object" - }, - { - "properties": { - "code": { - "const": "transport", - "type": "string" - }, - "message": { - "type": "string" - } - }, - "required": [ - "code", - "message" - ], - "type": "object" - }, - { - "properties": { - "code": { - "const": "invalid_response", - "type": "string" - }, - "message": { - "type": "string" - } - }, - "required": [ - "code", - "message" - ], - "type": "object" - } - ] - }, - "RuntimeResourceFetchRequest": { - "additionalProperties": false, - "properties": { - "audit_correlation_id": { - "type": "string" - }, - "handle": { - "$ref": "#/components/schemas/RuntimeResourceHandle" - }, - "runtime_id": { - "type": "string" - }, - "worker_id": { - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "handle", - "runtime_id", - "audit_correlation_id" - ], - "type": "object" - }, - "RuntimeResourceFetchResponse": { - "additionalProperties": false, - "properties": { - "audit_correlation_id": { - "type": "string" - }, - "bytes": { - "items": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - }, - "type": "array" - }, - "content_type": { - "type": "string" - }, - "digest": { - "type": "string" - }, - "kind": { - "$ref": "#/components/schemas/RuntimeResourceKind" - }, - "resource_id": { - "type": "string" - } - }, - "required": [ - "kind", - "resource_id", - "digest", - "content_type", - "bytes", - "audit_correlation_id" - ], - "type": "object" - }, - "RuntimeResourceHandle": { - "additionalProperties": false, - "properties": { - "audit_correlation_id": { - "type": "string" - }, - "content_type": { - "type": "string" - }, - "digest": { - "type": "string" - }, - "expires_at_unix_seconds": { - "format": "int64", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "generation": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": [ - "integer", - "null" - ] - }, - "kind": { - "$ref": "#/components/schemas/RuntimeResourceKind" - }, - "max_bytes": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "nonce": { - "type": "string" - }, - "operation": { - "$ref": "#/components/schemas/RuntimeResourceOperation" - }, - "profile_source_graph": { - "anyOf": [ - { - "$ref": "#/components/schemas/RuntimeProfileSourceGraphSummary" - }, - { - "type": "null" - } - ] - }, - "redaction": { - "$ref": "#/components/schemas/RuntimeResourceRedactionPolicy" - }, - "resource_id": { - "type": "string" - }, - "revision": { - "type": "string" - }, - "runtime_id": { - "type": [ - "string", - "null" - ] - }, - "scope_id": { - "type": [ - "string", - "null" - ] - }, - "worker_id": { - "type": [ - "string", - "null" - ] - }, - "workspace_id": { - "type": "string" - } - }, - "required": [ - "kind", - "workspace_id", - "resource_id", - "digest", - "operation", - "expires_at_unix_seconds", - "nonce", - "revision", - "max_bytes", - "content_type", - "redaction", - "audit_correlation_id" - ], - "type": "object" - }, - "RuntimeResourceKind": { - "enum": [ - "profile_source_archive", - "repository_ssh_access" - ], - "type": "string" - }, - "RuntimeResourceOperation": { - "enum": [ - "fetch_archive", - "fetch_once" - ], - "type": "string" - }, - "RuntimeResourceRedactionPolicy": { - "enum": [ - "runtime_internal_only" - ], - "type": "string" - }, "RuntimeSourceKind": { "enum": [ "embedded_worker_runtime", @@ -9729,68 +8557,6 @@ ], "type": "object" }, - "StatRequest": { - "additionalProperties": false, - "properties": { - "path": { - "$ref": "#/components/schemas/FsPath" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "StatResult": { - "additionalProperties": false, - "properties": { - "kind": { - "$ref": "#/components/schemas/EntryKind" - }, - "path": { - "$ref": "#/components/schemas/FsPath" - }, - "size": { - "format": "uint64", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "path", - "kind", - "size" - ], - "type": "object" - }, - "SubmitMergeRequestReviewRequest": { - "additionalProperties": false, - "properties": { - "body": { - "default": "", - "type": "string" - }, - "capability_token": { - "type": "string" - }, - "decision": { - "$ref": "#/components/schemas/ReviewDecision" - }, - "findings": { - "default": [], - "items": { - "$ref": "#/components/schemas/ReviewFinding" - }, - "type": "array" - } - }, - "required": [ - "capability_token", - "decision" - ], - "type": "object" - }, "Ticket": { "properties": { "artifacts": { @@ -12284,606 +11050,6 @@ }, "type": "object" }, - "WorkdirOperationApiError": { - "additionalProperties": false, - "description": "Error body for current-Worker Workdir operations.\n\nDomain/service failures preserve the existing Repository-style API envelope,\nwhile provider failures preserve the established typed Workdir transport\nenvelope and HTTP status classification.", - "properties": { - "code": { - "anyOf": [ - { - "$ref": "#/components/schemas/WorkdirTransportErrorCode" - }, - { - "type": "null" - } - ] - }, - "diagnostics": { - "items": { - "$ref": "#/components/schemas/Diagnostic" - }, - "type": [ - "array", - "null" - ] - }, - "error": { - "type": [ - "string", - "null" - ] - }, - "message": { - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "WorkdirScopeAuthorizationRequest": { - "additionalProperties": false, - "description": "Provider-side check for one operation under an attenuated tool scope.", - "properties": { - "path": { - "$ref": "#/components/schemas/FsPath" - }, - "permission": { - "$ref": "#/components/schemas/WorkdirToolScopePermission" - }, - "rules": { - "items": { - "$ref": "#/components/schemas/WorkdirToolScopeRule" - }, - "type": "array" - } - }, - "required": [ - "rules", - "path", - "permission" - ], - "type": "object" - }, - "WorkdirScopeOverlapRequest": { - "additionalProperties": false, - "description": "Provider-side overlap comparison that keeps resolved host paths private.", - "properties": { - "left": { - "$ref": "#/components/schemas/WorkdirToolScopeRule" - }, - "right": { - "$ref": "#/components/schemas/WorkdirToolScopeRule" - } - }, - "required": [ - "left", - "right" - ], - "type": "object" - }, - "WorkdirSessionCapabilities": { - "properties": { - "bits": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "bits" - ], - "type": "object" - }, - "WorkdirSessionOperation": { - "description": "One provider-side Workdir operation.", - "oneOf": [ - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "authorize_scope", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/WorkdirScopeAuthorizationRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "scope_rules_overlap", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/WorkdirScopeOverlapRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "stat", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/StatRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "read", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/ReadRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "write", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/WriteRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "edit", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/EditRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "list", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/ListRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "glob", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/GlobRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "grep", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/GrepRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_start", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/CommandRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_status", - "type": "string" - }, - "request": { - "type": "string" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_output", - "type": "string" - }, - "request": { - "$ref": "#/components/schemas/CommandOutputRequest" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_cancel", - "type": "string" - }, - "request": { - "type": "string" - } - }, - "required": [ - "operation", - "request" - ], - "type": "object" - } - ] - }, - "WorkdirSessionOperationResult": { - "description": "Typed result paired with [`WorkdirSessionOperation`].", - "oneOf": [ - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "authorize_scope", - "type": "string" - } - }, - "required": [ - "operation" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "scope_rules_overlap", - "type": "string" - }, - "result": { - "additionalProperties": false, - "properties": { - "overlaps": { - "type": "boolean" - } - }, - "required": [ - "overlaps" - ], - "type": "object" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "stat", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/StatResult" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "read", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/ReadResult" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "write", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/WriteResult" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "edit", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/EditResult" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "list", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/ListResult" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "glob", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/GlobResult" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "grep", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/GrepResult" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_start", - "type": "string" - }, - "result": { - "type": "string" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_status", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/CommandStatus" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_output", - "type": "string" - }, - "result": { - "$ref": "#/components/schemas/CommandOutput" - } - }, - "required": [ - "operation", - "result" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "operation": { - "const": "command_cancel", - "type": "string" - } - }, - "required": [ - "operation" - ], - "type": "object" - } - ] - }, - "WorkdirToolScopePermission": { - "enum": [ - "read", - "write" - ], - "type": "string" - }, - "WorkdirToolScopeRule": { - "additionalProperties": false, - "properties": { - "permission": { - "$ref": "#/components/schemas/WorkdirToolScopePermission" - }, - "recursive": { - "type": "boolean" - }, - "symlink_policy": { - "default": "resolved", - "type": "string" - }, - "target": { - "$ref": "#/components/schemas/FsPath" - } - }, - "required": [ - "target", - "permission", - "recursive" - ], - "type": "object" - }, - "WorkdirTransportErrorCode": { - "description": "Stable, host-path-free error code crossing the Runtime boundary.", - "enum": [ - "not_found", - "conflict", - "unsupported", - "invalid_request", - "denied", - "out_of_scope", - "symlink_out_of_scope", - "broken_symlink", - "symlink_target_is_directory", - "read_only", - "is_directory", - "symlink_directory_not_traversed", - "unknown_command", - "unavailable", - "io", - "transport", - "internal" - ], - "type": "string" - }, "WorkerBusyState": { "oneOf": [ { @@ -12935,76 +11101,6 @@ ], "type": "object" }, - "WorkerControlListResponse": { - "additionalProperties": false, - "properties": { - "items": { - "items": { - "$ref": "#/components/schemas/WorkerControlRecord" - }, - "type": "array" - }, - "truncated": { - "type": "boolean" - }, - "workspace_id": { - "type": "string" - } - }, - "required": [ - "workspace_id", - "items", - "truncated" - ], - "type": "object" - }, - "WorkerControlRecord": { - "additionalProperties": false, - "properties": { - "origin": { - "type": "string" - }, - "permissions": { - "items": { - "type": "string" - }, - "type": "array" - }, - "relation": { - "type": "string" - }, - "subject": { - "$ref": "#/components/schemas/WorkerControlSubject" - }, - "summary": { - "$ref": "#/components/schemas/WorkerLaunchWorkerSummary" - } - }, - "required": [ - "subject", - "relation", - "origin", - "permissions", - "summary" - ], - "type": "object" - }, - "WorkerControlSubject": { - "additionalProperties": false, - "properties": { - "runtime_id": { - "type": "string" - }, - "worker_id": { - "type": "string" - } - }, - "required": [ - "runtime_id", - "worker_id" - ], - "type": "object" - }, "WorkerFileDeleteResponse": { "additionalProperties": false, "properties": { @@ -13396,79 +11492,6 @@ ], "type": "string" }, - "WorkerRemoveApiError": { - "additionalProperties": false, - "properties": { - "code": { - "type": [ - "string", - "null" - ] - }, - "diagnostics": { - "items": { - "$ref": "#/components/schemas/Diagnostic" - }, - "type": [ - "array", - "null" - ] - }, - "error": { - "type": [ - "string", - "null" - ] - }, - "message": { - "type": [ - "string", - "null" - ] - } - }, - "type": "object" - }, - "WorkerRemoveRequest": { - "additionalProperties": false, - "properties": { - "reason": { - "type": "string" - }, - "target_runtime_id": { - "type": "string" - }, - "target_worker_id": { - "type": "string" - } - }, - "required": [ - "target_runtime_id", - "target_worker_id", - "reason" - ], - "type": "object" - }, - "WorkerRemoveResponse": { - "additionalProperties": false, - "properties": { - "removed": { - "type": "boolean" - }, - "runtime_id": { - "type": "string" - }, - "worker_id": { - "type": "string" - } - }, - "required": [ - "removed", - "runtime_id", - "worker_id" - ], - "type": "object" - }, "WorkerRestoreResponse": { "additionalProperties": false, "properties": { @@ -15314,53 +13337,6 @@ ], "type": "object" }, - "WorkspaceWorkerDiscoveryPage": { - "properties": { - "next_cursor": { - "type": [ - "string", - "null" - ] - }, - "workers": { - "items": { - "$ref": "#/components/schemas/WorkspaceWorkerDiscoveryItem" - }, - "type": "array" - } - }, - "required": [ - "workers" - ], - "type": "object" - }, - "WorkspaceWorkerDiscoveryQuery": { - "additionalProperties": false, - "properties": { - "cursor": { - "type": [ - "string", - "null" - ] - }, - "limit": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": [ - "integer", - "null" - ] - }, - "query": { - "type": [ - "string", - "null" - ] - } - }, - "type": "object" - }, "WorkspaceWorkerSubject": { "oneOf": [ { @@ -15386,60 +13362,6 @@ } ] }, - "WriteRequest": { - "additionalProperties": false, - "properties": { - "content": { - "items": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - }, - "type": "array" - }, - "expected_hash": { - "items": { - "format": "uint8", - "maximum": 255, - "minimum": 0, - "type": "integer" - }, - "maxItems": 32, - "minItems": 32, - "type": [ - "array", - "null" - ] - }, - "path": { - "$ref": "#/components/schemas/FsPath" - } - }, - "required": [ - "path", - "content" - ], - "type": "object" - }, - "WriteResult": { - "properties": { - "bytes_written": { - "format": "uint", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "created": { - "type": "boolean" - } - }, - "required": [ - "bytes_written", - "created" - ], - "type": "object" - }, "string": { "type": "string" } @@ -15459,7 +13381,7 @@ "info": { "title": "Yoi Server API", "version": "0.1.0", - "x-yoi-source-digest": "sha256:550383b600fe2a2e55630849c7de7a6a24463ad41d0c18dc5d254a3d14328f07" + "x-yoi-source-digest": "sha256:6b26856793bfd6dd3d34461b3a7422adfdf0fab74c0ebab5ef6ab7fbdf034dfe" }, "openapi": "3.1.0", "paths": { @@ -16397,7 +14319,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/objectives/{id}": { @@ -16456,7 +14386,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/repositories": { @@ -16718,105 +14656,6 @@ ] } }, - "/api/runtime/v1/workspaces/{workspace_id}/resources/fetch": { - "post": { - "operationId": "runtime_resource_fetch", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchResponse" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchApiError" - } - } - }, - "description": "Error response" - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchApiError" - } - } - }, - "description": "Error response" - }, - "504": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeResourceFetchApiError" - } - } - }, - "description": "Error response" - } - } - } - }, "/api/runtimes": { "get": { "operationId": "runtime_list_alias", @@ -18256,7 +16095,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/tickets/{id}": { @@ -18315,7 +16162,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/companion/cancel": { @@ -18772,7 +16627,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/config/source-tree": { @@ -19548,7 +17411,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] }, "put": { "operationId": "flow_put", @@ -19645,7 +17516,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/flows/resolve": { @@ -19734,7 +17613,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/flows/{flow_id}": { @@ -19823,7 +17710,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/hosts": { @@ -20030,98 +17925,6 @@ ] } }, - "/api/w/{workspace_id}/internal/reviewer-child-sessions": { - "post": { - "operationId": "merge_request_reviewer_child_register", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterReviewerChildSessionRequest" - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, "/api/w/{workspace_id}/memory": { "get": { "operationId": "memory_document", @@ -20295,7 +18098,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/memory/consolidation": { @@ -20414,7 +18225,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/memory/staging": { @@ -21006,7 +18825,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/objectives/{objective_id}": { @@ -21317,7 +19144,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/objectives/{objective_id}/state": { @@ -28702,7 +26537,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets": { @@ -28993,7 +26836,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/doctor": { @@ -29072,7 +26923,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/orchestration-plans/search": { @@ -29161,7 +27020,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/query": { @@ -29347,7 +27214,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/search": { @@ -29436,7 +27311,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}": { @@ -30203,7 +28086,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/events": { @@ -30539,7 +28430,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/item": { @@ -30648,225 +28547,15 @@ }, "description": "Error response" } - } - } - }, - "/api/w/{workspace_id}/tickets/{id}/merge-request": { - "post": { - "operationId": "merge_request_open", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OpenMergeRequestRequest" - } - } - }, - "required": true }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicMergeRequest" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/tickets/{id}/merge-request/complete": { - "post": { - "operationId": "merge_request_complete", - "parameters": [ + "security": [ { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" + "bearerAuth": [] }, { - "explode": false, - "in": "path", - "name": "id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" + "browserSession": [] } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CompleteMergeRequestRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MergeEvent" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } + ] } }, "/api/w/{workspace_id}/tickets/{id}/merge-request/readiness": { @@ -30965,7 +28654,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/merge-request/repair-source": { @@ -31076,335 +28773,12 @@ } }, "security": [ - { - "bearerAuth": [] - }, { "browserSession": [] } ] } }, - "/api/w/{workspace_id}/tickets/{id}/merge-request/review-capabilities": { - "post": { - "operationId": "merge_request_review_capability_register", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterMergeRequestReviewCapabilityRequest" - } - } - }, - "required": true - }, - "responses": { - "204": { - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/tickets/{id}/merge-request/reviews": { - "post": { - "operationId": "merge_request_review_submit", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubmitMergeRequestReviewRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReviewEvent" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/tickets/{id}/merge-request/reviews/revoke": { - "post": { - "operationId": "merge_request_review_revoke", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RevokeMergeRequestReviewRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReviewRevokedEvent" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, "/api/w/{workspace_id}/tickets/{id}/merge-request/thread": { "get": { "operationId": "merge_request_thread", @@ -31501,7 +28875,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/orchestration-plans": { @@ -31610,7 +28992,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/queue": { @@ -31933,7 +29323,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/relation-view": { @@ -32022,7 +29420,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/relations": { @@ -32131,7 +29537,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] }, "post": { "operationId": "ticket_relation_record", @@ -32238,7 +29652,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/show": { @@ -32337,7 +29759,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/state": { @@ -32556,7 +29986,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/state-fields/{field}": { @@ -32668,7 +30106,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/thread-events": { @@ -32770,7 +30216,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/workflow-state": { @@ -32872,7 +30326,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/workflow/close": { @@ -32974,7 +30436,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/workflow/mark-ready": { @@ -33083,7 +30553,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/w/{workspace_id}/tickets/{id}/workflow/queue": { @@ -33182,858 +30660,15 @@ }, "description": "Error response" } - } - } - }, - "/api/w/{workspace_id}/worker-control/workers": { - "get": { - "operationId": "worker_control_list", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerControlListResponse" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - }, - "post": { - "operationId": "worker_control_spawn", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateWorkspaceWorkerRequest" - } - } - }, - "required": true }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BrowserCreateWorkerResponse" - } - } - }, - "description": "Successful response" + "security": [ + { + "bearerAuth": [] }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" + { + "browserSession": [] } - } - } - }, - "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/cancel": { - "post": { - "operationId": "worker_control_cancel", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "runtime_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "worker_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeWorkerLifecycleRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeWorkerLifecycleResult" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/input": { - "post": { - "operationId": "worker_control_input", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "runtime_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "worker_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeWorkerInputRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeWorkerInputResult" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/restore": { - "post": { - "operationId": "worker_control_restore", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "runtime_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "worker_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRestoreResponse" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "504": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/worker-control/workers/{runtime_id}/{worker_id}/stop": { - "post": { - "operationId": "worker_control_stop", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "runtime_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "worker_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeWorkerLifecycleRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RuntimeWorkerLifecycleResult" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "504": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/worker-discovery/workers": { - "get": { - "operationId": "workspace_worker_discovery", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": true, - "in": "query", - "name": "query", - "required": true, - "schema": { - "$ref": "#/components/schemas/WorkspaceWorkerDiscoveryQuery" - }, - "style": "form" - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkspaceWorkerDiscoveryPage" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - } + ] } }, "/api/w/{workspace_id}/workers": { @@ -34375,487 +31010,6 @@ ] } }, - "/api/w/{workspace_id}/workers/remove": { - "post": { - "operationId": "workspace_worker_remove", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveResponse" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkerRemoveApiError" - } - } - }, - "description": "Error response" - } - } - } - }, - "/api/w/{workspace_id}/workers/self/workdir-attachments": { - "post": { - "operationId": "current_worker_workdir_attach", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CurrentWorkerWorkdirAttachRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CurrentWorkerWorkdirAttachmentResponse" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/w/{workspace_id}/workers/self/workdir-attachments/{alias}": { - "delete": { - "operationId": "current_worker_workdir_detach", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - }, - { - "explode": false, - "in": "path", - "name": "alias", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CurrentWorkerWorkdirAttachmentResponse" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RepositoryApiError" - } - } - }, - "description": "Error response" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, - "/api/w/{workspace_id}/workers/self/workdir-session/operations": { - "post": { - "operationId": "current_worker_workdir_operation", - "parameters": [ - { - "explode": false, - "in": "path", - "name": "workspace_id", - "required": true, - "schema": { - "$ref": "#/components/schemas/string" - }, - "style": "simple" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CurrentWorkerWorkdirOperationRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirSessionOperationResult" - } - } - }, - "description": "Successful response" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WorkdirOperationApiError" - } - } - }, - "description": "Error response" - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, "/api/w/{workspace_id}/workers/{worker_ref}": { "get": { "operationId": "workspace_worker_detail", @@ -35861,7 +32015,15 @@ }, "description": "Error response" } - } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "browserSession": [] + } + ] } }, "/api/workspace-deletions/{operation_id}": { diff --git a/web/workspace/src/lib/generated/companion-api.ts b/web/workspace/src/lib/generated/companion-api.ts index 2f2719bea..d51e24ef3 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:550383b600fe2a2e55630849c7de7a6a24463ad41d0c18dc5d254a3d14328f07) +// Input contract: 0.1.0 (sha256:6b26856793bfd6dd3d34461b3a7422adfdf0fab74c0ebab5ef6ab7fbdf034dfe) // 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 0b2531745..591b9e117 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:550383b600fe2a2e55630849c7de7a6a24463ad41d0c18dc5d254a3d14328f07) +// Input contract: 0.1.0 (sha256:6b26856793bfd6dd3d34461b3a7422adfdf0fab74c0ebab5ef6ab7fbdf034dfe) // 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 diff --git a/web/workspace/src/lib/generated/runtime-api.ts b/web/workspace/src/lib/generated/runtime-api.ts index ed23ced4c..993c42327 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:550383b600fe2a2e55630849c7de7a6a24463ad41d0c18dc5d254a3d14328f07) +// Input contract: 0.1.0 (sha256:6b26856793bfd6dd3d34461b3a7422adfdf0fab74c0ebab5ef6ab7fbdf034dfe) // 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 diff --git a/web/workspace/src/lib/generated/ticket-api.ts b/web/workspace/src/lib/generated/ticket-api.ts index fac0876fe..19d8e011c 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:550383b600fe2a2e55630849c7de7a6a24463ad41d0c18dc5d254a3d14328f07) +// Input contract: 0.1.0 (sha256:6b26856793bfd6dd3d34461b3a7422adfdf0fab74c0ebab5ef6ab7fbdf034dfe) // 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/workdir-api.ts b/web/workspace/src/lib/generated/workdir-api.ts index ac955be89..7d381c0e5 100644 --- a/web/workspace/src/lib/generated/workdir-api.ts +++ b/web/workspace/src/lib/generated/workdir-api.ts @@ -1,84 +1,20 @@ // @generated by yoi-workdir-openapi-typescript@1. Do not edit. // Input: openapi/server-api.json -// Input contract: 0.1.0 (sha256:550383b600fe2a2e55630849c7de7a6a24463ad41d0c18dc5d254a3d14328f07) +// Input contract: 0.1.0 (sha256:6b26856793bfd6dd3d34461b3a7422adfdf0fab74c0ebab5ef6ab7fbdf034dfe) // Options: workdir-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output // Output: web/workspace/src/lib/generated/workdir-api.ts // Regenerate/check: cargo run -q -p server-api --example generate_workdir_api_types -- --check -export type CommandOutput = { content: string; exit_code?: number | null; next_cursor?: number | null; output_path?: string | null; status: CommandStatus; timed_out: boolean; truncated: boolean; }; - -export type CommandOutputRequest = { cursor: number; handle: string; limit: number; wait: boolean; }; - -export type CommandRequest = { command: string; cwd?: FsPath | null; output_limit: number; spill_dir?: string | null; timeout_secs: number; tool_call_id?: string | null; }; - -export type CommandStatus = "running" | "completed" | "failed" | "timed_out" | "cancelled"; - -export type CurrentWorkerWorkdirAttachRequest = { alias: string; working_directory_id: string; }; - -export type CurrentWorkerWorkdirAttachmentResponse = { alias: string; attached: boolean; capabilities: WorkdirSessionCapabilities; working_directory_id: string; workspace_id: string; }; - -export type CurrentWorkerWorkdirOperationRequest = { operation: WorkdirSessionOperation; target_workdir: string; }; - export type Diagnostic = { code: string; message: string; severity: DiagnosticSeverity; }; export type DiagnosticSeverity = "info" | "warning" | "error"; -export type EditRequest = { expected_hash: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number]; new_string: string; old_string: string; path: FsPath; replace_all: boolean; }; - -export type EditResult = { bytes_written: number; content_hash: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number]; replacements: number; }; - -export type EntryKind = "file" | "directory" | "symlink" | "other"; - export type ExternalWorkdirGrantCreateRequest = { display_name: string; provider_instance_id: string; read_only: boolean; ttl_seconds: number; }; export type ExternalWorkdirGrantResponse = { display_name: string; expires_at: string; generation: number; grant_id: string; permissions: string; provider_instance_id: string; status: string; working_directory_id: string; workspace_id: string; }; -export type FsPath = string; - -export type GlobRequest = { limit: number; path: FsPath; pattern: string; }; - -export type GlobResult = { paths: Array; truncated: boolean; }; - -export type GrepOutputMode = "content" | "files_with_matches" | "count"; - -export type GrepRequest = { after_context: number; before_context: number; case_insensitive: boolean; file_type?: string | null; glob?: string | null; limit: number; multiline: boolean; offset: number; output_mode: GrepOutputMode; path: FsPath; pattern: string; }; - -export type GrepResult = { match_count: number; matched_files: number; output: string; truncated: boolean; }; - -export type ListEntry = { kind: EntryKind; path: FsPath; size: number; }; - -export type ListRequest = { limit: number; path: FsPath; }; - -export type ListResult = { entries: Array; total_bytes: number; total_entries: number; truncated: boolean; }; - -export type ReadRequest = { limit: number; max_bytes: number; offset: number; path: FsPath; }; - -export type ReadResult = { bytes: Array; content_hash: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number]; path: FsPath; start_line: number; total_lines: number; truncated: boolean; }; - export type RepositoryApiError = { diagnostics?: Array; error: string; message: string; }; -export type StatRequest = { path: FsPath; }; - -export type StatResult = { kind: EntryKind; path: FsPath; size: number; }; - -export type WorkdirOperationApiError = { code?: WorkdirTransportErrorCode | null; diagnostics?: Array | null; error?: string | null; message: string; }; - -export type WorkdirScopeAuthorizationRequest = { path: FsPath; permission: WorkdirToolScopePermission; rules: Array; }; - -export type WorkdirScopeOverlapRequest = { left: WorkdirToolScopeRule; right: WorkdirToolScopeRule; }; - -export type WorkdirSessionCapabilities = { bits: number; }; - -export type WorkdirSessionOperation = { operation: "authorize_scope"; request: WorkdirScopeAuthorizationRequest; } | { operation: "scope_rules_overlap"; request: WorkdirScopeOverlapRequest; } | { operation: "stat"; request: StatRequest; } | { operation: "read"; request: ReadRequest; } | { operation: "write"; request: WriteRequest; } | { operation: "edit"; request: EditRequest; } | { operation: "list"; request: ListRequest; } | { operation: "glob"; request: GlobRequest; } | { operation: "grep"; request: GrepRequest; } | { operation: "command_start"; request: CommandRequest; } | { operation: "command_status"; request: string; } | { operation: "command_output"; request: CommandOutputRequest; } | { operation: "command_cancel"; request: string; }; - -export type WorkdirSessionOperationResult = { operation: "authorize_scope"; } | { operation: "scope_rules_overlap"; result: { overlaps: boolean; }; } | { operation: "stat"; result: StatResult; } | { operation: "read"; result: ReadResult; } | { operation: "write"; result: WriteResult; } | { operation: "edit"; result: EditResult; } | { operation: "list"; result: ListResult; } | { operation: "glob"; result: GlobResult; } | { operation: "grep"; result: GrepResult; } | { operation: "command_start"; result: string; } | { operation: "command_status"; result: CommandStatus; } | { operation: "command_output"; result: CommandOutput; } | { operation: "command_cancel"; }; - -export type WorkdirToolScopePermission = "read" | "write"; - -export type WorkdirToolScopeRule = { permission: WorkdirToolScopePermission; recursive: boolean; symlink_policy?: string; target: FsPath; }; - -export type WorkdirTransportErrorCode = "not_found" | "conflict" | "unsupported" | "invalid_request" | "denied" | "out_of_scope" | "symlink_out_of_scope" | "broken_symlink" | "symlink_target_is_directory" | "read_only" | "is_directory" | "symlink_directory_not_traversed" | "unknown_command" | "unavailable" | "io" | "transport" | "internal"; - export type WorkingDirectoryCleanupTarget = { kind: string; repository_key: string; working_directory_id: string; }; export type WorkingDirectoryCreateRequest = { display_name?: string | null; operation_id?: string | null; repository_key: string; runtime_id?: string | null; selector?: string | null; }; @@ -104,7 +40,3 @@ export type WorkingDirectorySource = { kind: "repository"; repository_key: strin export type WorkingDirectoryStatusKind = "active" | "cleanup_pending" | "corrupted" | "not_found" | "unknown"; export type WorkingDirectorySummary = { cleanliness?: string | null; cleanup_target?: WorkingDirectoryCleanupTarget | null; creation_ref?: string | null; creation_selector?: string | null; creation_tree?: string | null; current_ref?: string | null; current_selector?: string | null; current_tree?: string | null; display_name?: string | null; materializer_kind: WorkingDirectoryMaterializerKind; observed_at_epoch_seconds?: number | null; occupied_by?: WorkingDirectoryOccupancy | null; source: WorkingDirectorySource; status: WorkingDirectoryStatusKind; working_directory_id: string; }; - -export type WriteRequest = { content: Array; expected_hash?: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number] | null; path: FsPath; }; - -export type WriteResult = { bytes_written: number; created: boolean; }; diff --git a/web/workspace/src/lib/generated/worker-launch-api.ts b/web/workspace/src/lib/generated/worker-launch-api.ts index 3521b3766..79f151a12 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:550383b600fe2a2e55630849c7de7a6a24463ad41d0c18dc5d254a3d14328f07) +// Input contract: 0.1.0 (sha256:6b26856793bfd6dd3d34461b3a7422adfdf0fab74c0ebab5ef6ab7fbdf034dfe) // 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