From 2fb7514daa7fa0b4845102f908e1a0a3f8edb3af Mon Sep 17 00:00:00 2001 From: Hare Date: Sun, 28 Jun 2026 03:06:12 +0900 Subject: [PATCH] runtime: remove fake companion responses --- crates/worker-runtime/src/http_server.rs | 4 +- crates/worker-runtime/src/runtime.rs | 34 +-- crates/workspace-server/src/companion.rs | 254 ++---------------- crates/workspace-server/src/hosts.rs | 72 ++--- crates/workspace-server/src/server.rs | 53 ++-- .../worker/web_companion_providerless.md | 3 - 6 files changed, 77 insertions(+), 343 deletions(-) delete mode 100644 resources/prompts/worker/web_companion_providerless.md diff --git a/crates/worker-runtime/src/http_server.rs b/crates/worker-runtime/src/http_server.rs index 94fbd88b..7bd2bdfb 100644 --- a/crates/worker-runtime/src/http_server.rs +++ b/crates/worker-runtime/src/http_server.rs @@ -960,7 +960,7 @@ mod tests { .await; assert_eq!(response.status(), StatusCode::OK); let detail: RuntimeHttpWorkerResponse = read_json(response).await; - assert_eq!(detail.worker.transcript_len, 2); + assert_eq!(detail.worker.transcript_len, 1); let response = empty_request( app.clone(), @@ -999,7 +999,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); let workers: RuntimeHttpWorkersResponse = read_json(response).await; assert_eq!(workers.workers.len(), 1); - assert_eq!(workers.workers[0].transcript_len, 2); + assert_eq!(workers.workers[0].transcript_len, 1); let response = empty_request(app, Method::GET, "/v1/runtime").await; assert_eq!(response.status(), StatusCode::OK); diff --git a/crates/worker-runtime/src/runtime.rs b/crates/worker-runtime/src/runtime.rs index de98953f..cde65c15 100644 --- a/crates/worker-runtime/src/runtime.rs +++ b/crates/worker-runtime/src/runtime.rs @@ -33,10 +33,6 @@ use tokio::sync::broadcast; static NEXT_RUNTIME_SEQUENCE: AtomicU64 = AtomicU64::new(1); -fn providerless_embedded_response_text() -> &'static str { - "Embedded worker-runtime accepted the message. LLM execution is not connected for this worker yet." -} - /// Concrete embedded Runtime domain entity. /// /// The default implementation is memory-backed and tools/provider-less by @@ -319,36 +315,10 @@ impl Runtime { }; state.push_worker_observation_event(worker_ref.clone(), payload); } - let assistant_transcript_sequence = if matches!(role, TranscriptRole::User) { - let assistant_text = providerless_embedded_response_text().to_string(); - let worker = state.worker_mut(worker_ref)?; - let assistant_sequence = worker.next_transcript_sequence; - worker.next_transcript_sequence += 1; - worker.transcript.push(TranscriptEntry { - sequence: assistant_sequence, - worker_ref: worker_ref.clone(), - role: TranscriptRole::Assistant, - content: assistant_text.clone(), - event_id, - }); - #[cfg(feature = "ws-server")] - state.push_worker_observation_event( - worker_ref.clone(), - protocol::Event::TextDone { - text: assistant_text, - }, - ); - Some(assistant_sequence) - } else { - None - }; state.persist_runtime_snapshot()?; state.persist_worker(&worker_ref.worker_id)?; state.persist_event_by_id(event_id)?; state.persist_transcript_entry(&worker_ref.worker_id, transcript_sequence)?; - if let Some(sequence) = assistant_transcript_sequence { - state.persist_transcript_entry(&worker_ref.worker_id, sequence)?; - } Ok(WorkerInteractionAck { worker_ref: worker_ref.clone(), @@ -1412,10 +1382,10 @@ mod tests { let projection = runtime .transcript_projection(&detail.worker_ref, TranscriptQuery::new(0, 2)) .unwrap(); - assert_eq!(projection.total_items, 5); + assert_eq!(projection.total_items, 3); assert_eq!(projection.items.len(), 2); assert_eq!(projection.items[0].content, "hello"); - assert_eq!(projection.items[1].role, TranscriptRole::Assistant); + assert_eq!(projection.items[1].role, TranscriptRole::System); assert_eq!(projection.next_start, Some(2)); let err = runtime diff --git a/crates/workspace-server/src/companion.rs b/crates/workspace-server/src/companion.rs index 662bcb2a..1054cf9a 100644 --- a/crates/workspace-server/src/companion.rs +++ b/crates/workspace-server/src/companion.rs @@ -1,19 +1,15 @@ use std::sync::{Arc, Mutex}; -use chrono::Utc; use serde::{Deserialize, Serialize}; use worker_runtime::catalog::{CapabilityRequest, ProfileSelector}; use crate::hosts::{ - DiagnosticSeverity, RuntimeDiagnostic, RuntimeRegistry, WorkerInputKind, WorkerInputRequest, - WorkerOperationState, WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, WorkerSpawnRequest, - WorkerSummary, + DiagnosticSeverity, RuntimeDiagnostic, RuntimeRegistry, WorkerOperationState, + WorkerSpawnAcceptanceRequirement, WorkerSpawnIntent, WorkerSpawnRequest, WorkerSummary, }; const COMPANION_RUNTIME_ID: &str = "embedded-worker-runtime"; const MAX_MESSAGE_CHARS: usize = 8_000; -const PROVIDERLESS_RESPONSE: &str = - include_str!("../../../resources/prompts/worker/web_companion_providerless.md"); #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] @@ -103,7 +99,6 @@ struct CompanionWorkerState { } pub struct CompanionConsole { - runtime: Arc, worker: Mutex, transcript: Mutex, } @@ -112,7 +107,6 @@ impl CompanionConsole { pub fn new(runtime: Arc) -> Self { let initial = spawn_companion_worker(&runtime); Self { - runtime, worker: Mutex::new(initial), transcript: Mutex::new(CompanionTranscript::default()), } @@ -125,7 +119,7 @@ impl CompanionConsole { return CompanionStatusResponse { state: CompanionState::Error, worker: None, - transport: providerless_transport(), + transport: companion_transport(), diagnostics: vec![diagnostic( "companion_state_unavailable", DiagnosticSeverity::Error, @@ -137,7 +131,7 @@ impl CompanionConsole { CompanionStatusResponse { state: worker.state, worker: worker.worker.clone(), - transport: providerless_transport(), + transport: companion_transport(), diagnostics: worker.diagnostics.clone(), } } @@ -181,125 +175,18 @@ impl CompanionConsole { )); } - let mut transcript = match self.transcript.try_lock() { - Ok(transcript) => transcript, - Err(std::sync::TryLockError::WouldBlock) => { - return self.busy_message_response(); - } - Err(std::sync::TryLockError::Poisoned(_)) => { - return self.error_message_response(diagnostic( - "companion_transcript_unavailable", - DiagnosticSeverity::Error, - "Companion transcript is unavailable", - )); - } - }; - - let (worker, mut diagnostics) = match self.current_worker() { - Ok((Some(worker), diagnostics)) => (worker, diagnostics), - Ok((None, diagnostics)) => { - return response_from_locked_transcript( - &transcript, - CompanionState::Error, - None, - None, - None, - 0, - 200, - diagnostics, - ); - } - Err(diagnostic) => { - return response_from_locked_transcript( - &transcript, - CompanionState::Error, - None, - None, - None, - 0, - 200, - vec![diagnostic], - ); - } - }; - - let user_item = transcript.push("user", content.clone(), "browser_request", "accepted"); - match self.runtime.send_input( - &worker.runtime_id, - &worker.worker_id, - WorkerInputRequest { - kind: WorkerInputKind::User, - content, - }, - ) { - Ok(result) if result.state == WorkerOperationState::Accepted => { - diagnostics.extend(result.diagnostics); - } - Ok(result) => { - diagnostics.extend(result.diagnostics); - diagnostics.push(diagnostic( - "companion_runtime_input_rejected", - DiagnosticSeverity::Error, - "Embedded Companion Worker rejected the browser message", - )); - return response_from_locked_transcript( - &transcript, - CompanionState::Error, - Some(worker), - Some(user_item), - None, - 0, - 200, - diagnostics, - ); - } - Err(error) => { - diagnostics.push(diagnostic( - "companion_runtime_input_failed", - DiagnosticSeverity::Error, - format!("Embedded Companion Worker input failed: {error:?}"), - )); - return response_from_locked_transcript( - &transcript, - CompanionState::Error, - Some(worker), - Some(user_item), - None, - 0, - 200, - diagnostics, - ); - } - } - - diagnostics.push(diagnostic( - "companion_providerless_boundary", - DiagnosticSeverity::Info, - "Real LLM completion is not connected in this MVP; response is the backend provider-less boundary text", - )); - let assistant_item = transcript.push( - "assistant", - providerless_response_text(), - "backend_providerless_boundary", - "complete", - ); - response_from_locked_transcript( - &transcript, - CompanionState::Accepted, - Some(worker), - Some(user_item), - Some(assistant_item), - 0, - 200, - diagnostics, - ) + self.rejected_message_response(diagnostic( + "companion_llm_not_connected", + DiagnosticSeverity::Error, + "Workspace Companion input is disabled until it is connected to actual Worker/LLM execution", + )) } pub fn cancel(&self, _request: CompanionCancelRequest) -> CompanionMessageResponse { let diagnostics = vec![diagnostic( "companion_cancel_no_active_run", DiagnosticSeverity::Info, - "Provider-less Companion Console has no active generation to cancel", + "Workspace Companion has no active generation to cancel", )]; match self.transcript.lock() { Ok(transcript) => response_from_locked_transcript( @@ -335,19 +222,6 @@ impl CompanionConsole { } } - fn current_worker( - &self, - ) -> Result<(Option, Vec), RuntimeDiagnostic> { - let worker = self.worker.lock().map_err(|_| { - diagnostic( - "companion_state_unavailable", - DiagnosticSeverity::Error, - "Companion state is unavailable", - ) - })?; - Ok((worker.worker.clone(), worker.diagnostics.clone())) - } - fn rejected_message_response(&self, diagnostic: RuntimeDiagnostic) -> CompanionMessageResponse { match self.transcript.lock() { Ok(transcript) => response_from_locked_transcript( @@ -378,83 +252,6 @@ impl CompanionConsole { }, } } - - fn busy_message_response(&self) -> CompanionMessageResponse { - let diagnostic = diagnostic( - "companion_busy", - DiagnosticSeverity::Warning, - "Companion Console is already processing a message", - ); - match self.transcript.lock() { - Ok(transcript) => response_from_locked_transcript( - &transcript, - CompanionState::Busy, - self.status().worker, - None, - None, - 0, - 200, - vec![diagnostic], - ), - Err(_) => CompanionMessageResponse { - state: CompanionState::Busy, - worker: self.status().worker, - user_item: None, - assistant_item: None, - transcript: CompanionTranscriptProjection { - state: CompanionState::Busy, - start: 0, - limit: 200, - total_items: 0, - next_start: None, - items: Vec::new(), - diagnostics: vec![diagnostic.clone()], - }, - diagnostics: vec![diagnostic], - }, - } - } - - fn error_message_response(&self, diagnostic: RuntimeDiagnostic) -> CompanionMessageResponse { - CompanionMessageResponse { - state: CompanionState::Error, - worker: self.status().worker, - user_item: None, - assistant_item: None, - transcript: CompanionTranscriptProjection { - state: CompanionState::Error, - start: 0, - limit: 200, - total_items: 0, - next_start: None, - items: Vec::new(), - diagnostics: vec![diagnostic.clone()], - }, - diagnostics: vec![diagnostic], - } - } -} - -impl CompanionTranscript { - fn push( - &mut self, - role: impl Into, - content: impl Into, - source: impl Into, - status: impl Into, - ) -> CompanionTranscriptItem { - self.next_sequence = self.next_sequence.saturating_add(1); - let item = CompanionTranscriptItem { - sequence: self.next_sequence, - role: role.into(), - content: content.into(), - created_at: Utc::now().to_rfc3339(), - source: source.into(), - status: status.into(), - }; - self.items.push(item.clone()); - item - } } fn spawn_companion_worker(runtime: &RuntimeRegistry) -> CompanionWorkerState { @@ -537,15 +334,11 @@ fn project_transcript( } } -fn providerless_response_text() -> String { - PROVIDERLESS_RESPONSE.trim().to_string() -} - -fn providerless_transport() -> CompanionTransportSummary { +fn companion_transport() -> CompanionTransportSummary { CompanionTransportSummary { - kind: "providerless_backend_internal".to_string(), - completion: "synchronous_request_response".to_string(), - limitation: "No provider-backed LLM generation is wired in this MVP; browser messages are recorded by a backend-internal tools-less Companion Worker and receive a resource-defined boundary response.".to_string(), + kind: "embedded_worker_runtime".to_string(), + completion: "not_connected".to_string(), + limitation: "Workspace Companion is visible as an embedded Worker, but browser input is disabled until actual Worker/LLM execution is connected.".to_string(), } } @@ -567,7 +360,7 @@ mod tests { use crate::hosts::{EmbeddedWorkerRuntime, RuntimeRegistry}; #[test] - fn companion_spawns_visible_worker_and_records_providerless_turn() { + fn companion_spawns_visible_worker_without_fake_turn() { let registry = RuntimeRegistry::for_workspace(EmbeddedWorkerRuntime::new_memory("local:test")); let registry = Arc::new(registry); @@ -591,22 +384,19 @@ mod tests { let response = companion.send_message(CompanionMessageRequest { content: "hello".to_string(), }); - assert_eq!(response.state, CompanionState::Accepted); - assert_eq!(response.transcript.items.len(), 2); - assert_eq!(response.transcript.items[0].role, "user"); - assert_eq!(response.transcript.items[1].role, "assistant"); + assert_eq!(response.state, CompanionState::Rejected); + assert!(response.transcript.items.is_empty()); assert!( - response.transcript.items[1] - .content - .contains("provider-less") + response + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "companion_llm_not_connected") ); let runtime_transcript = registry .transcript(COMPANION_RUNTIME_ID, &worker.worker_id, 0, 10) .unwrap(); - assert_eq!(runtime_transcript.items.len(), 2); - assert_eq!(runtime_transcript.items[0].role, "user"); - assert_eq!(runtime_transcript.items[1].role, "assistant"); + assert!(runtime_transcript.items.is_empty()); let browser_payload = serde_json::to_string(&(status, response)).unwrap(); for forbidden in [ diff --git a/crates/workspace-server/src/hosts.rs b/crates/workspace-server/src/hosts.rs index 4728b5bb..f7ee4ea2 100644 --- a/crates/workspace-server/src/hosts.rs +++ b/crates/workspace-server/src/hosts.rs @@ -957,7 +957,7 @@ impl EmbeddedWorkerRuntime { display_hint: "backend-internal worker-runtime Worker".to_string(), }, capabilities: WorkerCapabilitySummary { - can_accept_input: true, + can_accept_input: false, can_stop: false, can_spawn_followup: false, }, @@ -989,7 +989,7 @@ impl EmbeddedWorkerRuntime { display_hint: "backend-internal worker-runtime Worker".to_string(), }, capabilities: WorkerCapabilitySummary { - can_accept_input: true, + can_accept_input: false, can_stop: false, can_spawn_followup: false, }, @@ -1255,40 +1255,17 @@ impl WorkspaceWorkerRuntime for EmbeddedWorkerRuntime { )) } - fn send_input(&self, worker_id: &str, request: WorkerInputRequest) -> WorkerInputResult { - let Some(worker_ref) = self.worker_ref(worker_id) else { - return embedded_input_rejected( - &self.runtime_id, - worker_id, - diagnostic( - "embedded_worker_id_invalid", - DiagnosticSeverity::Warning, - "Worker id was empty and cannot be resolved".to_string(), - ), - ); - }; - let input = EmbeddedWorkerInput { - kind: match request.kind { - WorkerInputKind::User => EmbeddedWorkerInputKind::User, - WorkerInputKind::System => EmbeddedWorkerInputKind::System, - }, - content: request.content, - }; - match self.runtime.send_input(&worker_ref, input) { - Ok(ack) => WorkerInputResult { - state: WorkerOperationState::Accepted, - runtime_id: self.runtime_id.clone(), - worker_id: worker_id.to_string(), - transcript_sequence: Some(ack.transcript_sequence), - event_id: Some(ack.event_id), - diagnostics: Vec::new(), - }, - Err(err) => embedded_input_rejected( - &self.runtime_id, - worker_id, - embedded_runtime_diagnostic(&err), + fn send_input(&self, worker_id: &str, _request: WorkerInputRequest) -> WorkerInputResult { + embedded_input_rejected( + &self.runtime_id, + worker_id, + diagnostic( + "embedded_worker_llm_not_connected", + DiagnosticSeverity::Error, + "Embedded Worker input is disabled until actual Worker/LLM execution is connected" + .to_string(), ), - } + ) } fn transcript( @@ -1878,7 +1855,7 @@ fn embedded_runtime_capabilities(limit: usize, available: bool) -> RuntimeCapabi can_get_worker: available, can_spawn_worker: available, can_stop_worker: false, - can_accept_input: available, + can_accept_input: false, has_workspace_fs: false, has_shell: false, has_git: false, @@ -2611,7 +2588,7 @@ mod tests { ); assert_eq!(embedded_summary.source.status, RuntimeSourceStatus::Active); assert!(embedded_summary.capabilities.can_spawn_worker); - assert!(embedded_summary.capabilities.can_accept_input); + assert!(!embedded_summary.capabilities.can_accept_input); let spawned = registry .spawn_worker( @@ -2644,7 +2621,7 @@ mod tests { assert_eq!(worker.workspace.identity, "runtime_registry_worker"); assert_eq!(worker.implementation.kind, "embedded_worker_runtime"); assert_eq!(worker.profile.as_deref(), Some("builtin:coder")); - assert!(worker.capabilities.can_accept_input); + assert!(!worker.capabilities.can_accept_input); let input = registry .send_input( @@ -2656,24 +2633,21 @@ mod tests { }, ) .unwrap(); - assert_eq!(input.state, WorkerOperationState::Accepted); + assert_eq!(input.state, WorkerOperationState::Rejected); assert_eq!(input.runtime_id, EMBEDDED_RUNTIME_ID); assert_eq!(input.worker_id, worker.worker_id); - assert_eq!(input.transcript_sequence, Some(1)); + assert!( + input + .diagnostics + .iter() + .any(|diagnostic| diagnostic.code == "embedded_worker_llm_not_connected") + ); let transcript = registry .transcript(EMBEDDED_RUNTIME_ID, &worker.worker_id, 0, 10) .unwrap(); assert_eq!(transcript.state, WorkerOperationState::Accepted); - assert_eq!(transcript.items.len(), 2); - assert_eq!(transcript.items[0].role, "user"); - assert_eq!(transcript.items[0].content, "hello embedded runtime"); - assert_eq!(transcript.items[1].role, "assistant"); - assert!( - transcript.items[1] - .content - .contains("LLM execution is not connected") - ); + assert!(transcript.items.is_empty()); let json = serde_json::to_string(&(embedded_summary, worker, transcript)).unwrap(); for forbidden in [ diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index c5ab71d3..cca62427 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -327,16 +327,16 @@ async fn get_workspace(State(api): State) -> ApiResult