mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-31 16:49:34 +00:00
refactor: extract AppEvent to crates/ironclaw_common (#1615)
* refactor: extract AppEvent to crates/ironclaw_common SseEvent was defined in src/channels/web/types.rs but imported by 12+ modules across agent, orchestrator, worker, tools, and extensions — it had become the application-wide event protocol, not a web transport concern. Create crates/ironclaw_common as a shared workspace crate and move the enum there as AppEvent. Also move the truncate_preview utility which was similarly leaked from the web gateway into agent modules. - New crate: crates/ironclaw_common (AppEvent, truncate_preview) - Rename SseEvent → AppEvent, from_sse_event → from_app_event - web/types.rs re-exports AppEvent for internal gateway use - web/util.rs re-exports truncate_preview - Wire format unchanged (serde renames are on variants, not the enum) Aligned with the event bus direction on refactor/architectural-hardening where DomainEvent (≡ AppEvent) is wrapped in a SystemEvent envelope. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor: add AppEvent::event_type() helper, deduplicate match blocks Address Gemini review: extract the variant→string match into a single method on AppEvent, replacing the duplicated 22-arm matches in sse.rs and types.rs. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor: rename leftover sse vars/tests to match AppEvent rename Address Copilot review: rename sse_event vars to app_event in orchestrator/api.rs and ws.rs, rename test functions from test_ws_server_from_sse_* to test_ws_server_from_app_event_*, and update stale SSE comments. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * refactor: add Deserialize to AppEvent, round-trip test, fix stale comments Address zmanian review: - Add Deserialize derive to AppEvent so downstream consumers can deserialize incoming events - Add event_type_matches_serde_type_field test that round-trips every variant through serde and asserts event_type() matches the serialized "type" field — catches drift between serde renames and the manual match - Add round_trip_deserialize test for basic Serialize/Deserialize parity - Update remaining "SSE" references in comments across server.rs, manager.rs, ws_gateway_integration.rs, and worker/job.rs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
656151783c
commit
706c3a1b47
+14
-14
@@ -14,7 +14,6 @@ use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{Mutex, broadcast};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::channels::web::types::SseEvent;
|
||||
use crate::db::Database;
|
||||
use crate::llm::{CompletionRequest, LlmProvider, ToolCompletionRequest};
|
||||
use crate::orchestrator::auth::{TokenStore, worker_auth_middleware};
|
||||
@@ -25,6 +24,7 @@ use crate::worker::api::{
|
||||
CompletionReport, CredentialResponse, JobDescription, ProxyCompletionRequest,
|
||||
ProxyCompletionResponse, ProxyToolCompletionRequest, ProxyToolCompletionResponse, StatusUpdate,
|
||||
};
|
||||
use ironclaw_common::AppEvent;
|
||||
|
||||
/// A follow-up prompt queued for a Claude Code bridge.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -41,7 +41,7 @@ pub struct OrchestratorState {
|
||||
pub token_store: TokenStore,
|
||||
/// Broadcast channel for job events (consumed by the web gateway SSE).
|
||||
/// Tuple: (job_id, user_id, event).
|
||||
pub job_event_tx: Option<broadcast::Sender<(Uuid, String, SseEvent)>>,
|
||||
pub job_event_tx: Option<broadcast::Sender<(Uuid, String, AppEvent)>>,
|
||||
/// Buffered follow-up prompts for sandbox jobs, keyed by job_id.
|
||||
pub prompt_queue: Arc<Mutex<HashMap<Uuid, VecDeque<PendingPrompt>>>>,
|
||||
/// Database handle for persisting job events.
|
||||
@@ -277,10 +277,10 @@ async fn job_event_handler(
|
||||
});
|
||||
}
|
||||
|
||||
// Convert to SSE event and broadcast
|
||||
// Convert to app event and broadcast
|
||||
let job_id_str = job_id.to_string();
|
||||
let sse_event = match payload.event_type.as_str() {
|
||||
"message" => SseEvent::JobMessage {
|
||||
let app_event = match payload.event_type.as_str() {
|
||||
"message" => AppEvent::JobMessage {
|
||||
job_id: job_id_str,
|
||||
role: payload
|
||||
.data
|
||||
@@ -295,7 +295,7 @@ async fn job_event_handler(
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
},
|
||||
"tool_use" => SseEvent::JobToolUse {
|
||||
"tool_use" => AppEvent::JobToolUse {
|
||||
job_id: job_id_str,
|
||||
tool_name: payload
|
||||
.data
|
||||
@@ -309,7 +309,7 @@ async fn job_event_handler(
|
||||
.cloned()
|
||||
.unwrap_or(serde_json::Value::Null),
|
||||
},
|
||||
"tool_result" => SseEvent::JobToolResult {
|
||||
"tool_result" => AppEvent::JobToolResult {
|
||||
job_id: job_id_str,
|
||||
tool_name: payload
|
||||
.data
|
||||
@@ -324,7 +324,7 @@ async fn job_event_handler(
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
},
|
||||
"result" => SseEvent::JobResult {
|
||||
"result" => AppEvent::JobResult {
|
||||
job_id: job_id_str,
|
||||
status: payload
|
||||
.data
|
||||
@@ -344,7 +344,7 @@ async fn job_event_handler(
|
||||
// gain context/memory tracking capabilities.
|
||||
fallback_deliverable: payload.data.get("fallback_deliverable").cloned(),
|
||||
},
|
||||
_ => SseEvent::JobStatus {
|
||||
_ => AppEvent::JobStatus {
|
||||
job_id: job_id_str,
|
||||
message: payload
|
||||
.data
|
||||
@@ -390,9 +390,9 @@ async fn job_event_handler(
|
||||
};
|
||||
|
||||
if user_id.is_empty() {
|
||||
let _ = tx.send((job_id, String::new(), sse_event));
|
||||
let _ = tx.send((job_id, String::new(), app_event));
|
||||
} else {
|
||||
let _ = tx.send((job_id, user_id, sse_event));
|
||||
let _ = tx.send((job_id, user_id, app_event));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,7 +817,7 @@ mod tests {
|
||||
// No store configured, so user_id falls back to empty string.
|
||||
assert_eq!(recv_uid, "");
|
||||
match event {
|
||||
SseEvent::JobMessage {
|
||||
AppEvent::JobMessage {
|
||||
job_id: jid,
|
||||
role,
|
||||
content,
|
||||
@@ -872,7 +872,7 @@ mod tests {
|
||||
|
||||
let (_recv_id, _recv_uid, event) = rx.recv().await.unwrap();
|
||||
match event {
|
||||
SseEvent::JobToolUse { tool_name, .. } => {
|
||||
AppEvent::JobToolUse { tool_name, .. } => {
|
||||
assert_eq!(tool_name, "shell");
|
||||
}
|
||||
other => panic!("Expected JobToolUse, got {:?}", other),
|
||||
@@ -918,7 +918,7 @@ mod tests {
|
||||
|
||||
let (_recv_id, _recv_uid, event) = rx.recv().await.unwrap();
|
||||
// Unknown event types fall through to JobStatus
|
||||
assert!(matches!(event, SseEvent::JobStatus { .. }));
|
||||
assert!(matches!(event, AppEvent::JobStatus { .. }));
|
||||
}
|
||||
|
||||
// -- Status update test --
|
||||
|
||||
@@ -46,10 +46,10 @@ use std::sync::Arc;
|
||||
use tokio::sync::{Mutex, broadcast};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::channels::web::types::SseEvent;
|
||||
use crate::db::Database;
|
||||
use crate::llm::LlmProvider;
|
||||
use crate::secrets::SecretsStore;
|
||||
use ironclaw_common::AppEvent;
|
||||
|
||||
/// Resolve the orchestrator port from the `ORCHESTRATOR_PORT` environment
|
||||
/// variable, falling back to 50051.
|
||||
@@ -63,7 +63,7 @@ fn resolve_orchestrator_port() -> u16 {
|
||||
/// Result of orchestrator setup, containing all handles needed by the agent.
|
||||
pub struct OrchestratorSetup {
|
||||
pub container_job_manager: Option<Arc<ContainerJobManager>>,
|
||||
pub job_event_tx: Option<broadcast::Sender<(Uuid, String, SseEvent)>>,
|
||||
pub job_event_tx: Option<broadcast::Sender<(Uuid, String, AppEvent)>>,
|
||||
pub prompt_queue: Arc<Mutex<HashMap<Uuid, VecDeque<api::PendingPrompt>>>>,
|
||||
pub docker_status: crate::sandbox::DockerStatus,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user